| #!/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() |