Files
coven/modules/ufbx/misc/hash_diff.py

151 lines
4.9 KiB
Python

import gzip
import os
import re
import sys
def fetch_dumps(file):
files = { }
cur_file = None
cur_dump = None
for line in file:
line = line.rstrip()
if line.startswith("=="):
name = line[2:].strip()
cur_file = { }
files[name] = cur_file
elif line.startswith("--"):
name = line[2:].strip()
cur_dump = []
cur_file[name] = cur_dump
elif cur_dump is not None:
cur_dump.append(line)
return files
def diff_dump(dump, ref, file, runner, ref_runner):
num_lines = max(len(dump), len(ref))
stack = []
has_diff = False
for ix in range(num_lines):
dline = dump[ix] if ix < len(dump) else ""
rline = ref[ix] if ix < len(ref) else ""
if dline == rline:
if "{" in dline:
stack.append(dline)
elif "}" in dline:
stack.pop()
else:
span = 2
start = max(0, ix - span)
stop = ix + span + 1
has_diff = True
print(f"== {file}")
print()
print(" ".join(s.strip().rstrip("{ ") for s in stack))
print()
print(f"-- {runner}")
print()
for lix in range(start, stop):
dl = dump[lix] if lix < len(dump) else ""
rl = ref[lix] if lix < len(ref) else ""
prefix = "> " if dl != rl else " "
print(prefix + dl)
print()
print(f"-- {ref_runner}")
print()
for lix in range(start, stop):
dl = dump[lix] if lix < len(dump) else ""
rl = ref[lix] if lix < len(ref) else ""
prefix = "> " if dl != rl else " "
print(prefix + rl)
print()
break
return has_diff
def do_compress(argv):
with gzip.open(argv.o, "wt", compresslevel=8) as outf:
for file in os.listdir(argv.directory):
name = file[:-4] if file.endswith(".txt") else file
path = os.path.join(argv.directory, file)
print(f"== {name}", file=outf)
with open(path, "rt") as inf:
outf.writelines(inf)
def do_list(argv):
entries = set()
for file in os.listdir(argv.directory):
path = os.path.join(argv.directory, file)
with gzip.open(path, "rt") as inf:
for line in inf:
line = line.strip()
if not line:
continue
m = re.match(r"--\s*(\d+)\s+(.+)", line)
if m:
frame = m.group(1)
path = m.group(2)
entries.add((frame, path))
with open(argv.o, "wt") as outf:
for frame, path in entries:
print(f"0000000000000000 {frame} {path}", file=outf)
def do_diff(argv):
with gzip.open(argv.ref, "rt") as inf:
ref_dumps = fetch_dumps(inf)
ref_tuple = next(iter(ref_dumps.items()), None)
if not ref_tuple:
return
has_diff = False
ref_runner, ref_file = ref_tuple
for file in os.listdir(argv.directory):
path = os.path.join(argv.directory, file)
with gzip.open(path, "rt") as inf:
dumps = fetch_dumps(inf)
for runner, files in dumps.items():
for file, dump in files.items():
ref = ref_file[file]
if diff_dump(dump, ref, file, runner, ref_runner):
has_diff = True
if has_diff:
sys.exit(1)
if __name__ == "__main__":
from argparse import ArgumentParser
parser = ArgumentParser(prog="hash_diff.py")
parser.add_argument("--verbose", action="store_true", help="Show extra information")
subparsers = parser.add_subparsers(metavar="cmd")
parser_compress = subparsers.add_parser("compress", help="Compress files")
parser_compress.add_argument("directory", help="Directory of hash files to compress")
parser_compress.add_argument("-o", metavar="output.gz", required=True, help="Output .gz filename")
parser_compress.set_defaults(func=do_compress)
parser_dump = subparsers.add_parser("list", help="List all files from other dumps in hash check compatible format")
parser_dump.add_argument("directory", help="Directory of .gz dump files generated by 'compress'")
parser_dump.add_argument("-o", metavar="output.txt", required=True, help="Output .txt filename")
parser_dump.set_defaults(func=do_list)
parser_diff = subparsers.add_parser("diff", help="Compare dumps")
parser_diff.add_argument("directory", help="Directory of .gz dump files generated by 'compress'")
parser_diff.add_argument("--ref", metavar="ref.gz", required=True, help="Reference hash dump .gz")
parser_diff.set_defaults(func=do_diff)
argv = parser.parse_args()
if "func" not in argv:
parser.print_help()
else:
argv.func(argv)