| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| # |
| # Copyright (C) 2017-2020 The Project X-Ray Authors. |
| # |
| # Use of this source code is governed by a ISC-style |
| # license that can be found in the LICENSE file or at |
| # https://opensource.org/licenses/ISC |
| # |
| # SPDX-License-Identifier: ISC |
| """ |
| This tool allows to view segbits using a 2D frame vs. bit plot. It can read |
| either mask.db files or segbits.db files. Multiple files can be read at once |
| allowing to identify where a bit comes from. |
| |
| When a single file is read, a bit is marked as "O". When multiple files are |
| read, a bit is denoted with a letter "A", "B" and so on depending on index |
| of file that it belongs to. |
| |
| Duplicate bits (present in more than one files) are marked with "#" |
| """ |
| |
| import sys |
| import argparse |
| import re |
| |
| from prjxray.util import OpenSafeFile |
| |
| # ============================================================================= |
| |
| |
| def load_just_bits(file_name): |
| """ |
| Read bits from a .db or .rdb file. Ignores tags and bit values. |
| """ |
| |
| with OpenSafeFile(file_name, "r") as fp: |
| lines = fp.readlines() |
| |
| bits = set() |
| for line in lines: |
| for word in line.split(" "): |
| match = re.match("^(!?)([0-9]+)_([0-9]+)$", word) |
| if match is not None: |
| frm = int(match.group(2)) |
| bit = int(match.group(3)) |
| |
| bits.add(( |
| frm, |
| bit, |
| )) |
| |
| return bits |
| |
| |
| # ============================================================================= |
| |
| |
| def main(): |
| |
| # Colors for TTY |
| if sys.stdout.isatty(): |
| |
| bit_colors = [ |
| "\033[39m", |
| "\033[91m", |
| "\033[92m", |
| "\033[93m", |
| "\033[94m", |
| "\033[95m", |
| "\033[96m", |
| "\033[31m", |
| "\033[32m", |
| "\033[33m", |
| "\033[34m", |
| "\033[35m", |
| "\033[36m", |
| ] |
| |
| colors = { |
| "NONE": "\033[0m", |
| "DUPLICATE": "\033[101;97m", |
| } |
| |
| # Colors for pipe |
| else: |
| |
| bit_colors = [""] |
| |
| colors = { |
| "NONE": "", |
| "DUPLICATE": "", |
| } |
| |
| # ......................................................................... |
| |
| parser = argparse.ArgumentParser( |
| description=__doc__, |
| formatter_class=argparse.RawDescriptionHelpFormatter) |
| |
| parser.add_argument("files", nargs="*", type=str, help="Input files") |
| |
| args = parser.parse_args() |
| |
| # Load bits |
| all_bits = [] |
| for i, f in enumerate(args.files): |
| bits = load_just_bits(f) |
| all_bits.append(bits) |
| |
| cstr = bit_colors[i % len(bit_colors)] |
| bstr = "O" if len(args.files) == 1 else chr(65 + i) |
| print(cstr + bstr + colors["NONE"] + ": %s #%d" % (f, len(bits))) |
| |
| print("") |
| |
| max_frames = max([bit[0] for bits in all_bits for bit in bits]) + 1 |
| max_bits = max([bit[1] for bits in all_bits for bit in bits]) + 1 |
| |
| # Header |
| for r in range(3): |
| line = " " * 3 |
| for c in range(max_bits): |
| bstr = "%03d" % c |
| line += bstr[r] |
| print(line) |
| |
| print("") |
| |
| # Display bits |
| for r in range(max_frames): |
| line = "%2d " % r |
| for c in range(max_bits): |
| got_bit = False |
| bit_str = colors["NONE"] + "-" |
| for i, bits in enumerate(all_bits): |
| cstr = bit_colors[i % len(bit_colors)] |
| bstr = "O" if len(args.files) == 1 else chr(65 + i) |
| if (r, c) in bits: |
| if not got_bit: |
| bit_str = cstr + bstr |
| else: |
| bit_str = colors["DUPLICATE"] + "#" + colors["NONE"] |
| got_bit = True |
| |
| line += bit_str |
| |
| line += colors["NONE"] |
| print(line) |
| |
| |
| # ============================================================================= |
| |
| if __name__ == "__main__": |
| main() |