arcadeutils/bindiff

115 lines
3.3 KiB
Python
Executable File

#! /usr/bin/env python3
import argparse
import os
import sys
from arcadeutils.binary import BinaryDiff
def main() -> int:
# Create the argument parser
parser = argparse.ArgumentParser(
description="Utilities for diffing or patching binary files.",
)
subparsers = parser.add_subparsers(help='commands', dest='command')
# Parser for diffing two binary files
diff_parser = subparsers.add_parser('diff', help='create a diff of two same-length binary files')
diff_parser.add_argument(
'file1',
metavar='FILE1',
type=str,
help='the base file that we will output diffs relative to',
)
diff_parser.add_argument(
'file2',
metavar='FILE2',
type=str,
help='the file that we will compare against the base file to find diffs',
)
diff_parser.add_argument(
'--patch-file',
metavar='FILE',
type=str,
help='write patches to a file instead of stdout',
)
# Parser for patching a binary file
patch_parser = subparsers.add_parser('patch', help='patch a binary file using a previously created diff')
patch_parser.add_argument(
'bin',
metavar='BIN',
type=str,
help='the binary file we should patch',
)
patch_parser.add_argument(
'out',
metavar='OUT',
type=str,
help='the file we should write the patched binary to',
)
patch_parser.add_argument(
'--patch-file',
metavar='FILE',
type=str,
help='read patches from a file instead of stdin',
)
patch_parser.add_argument(
'--reverse',
action="store_true",
help='perform the patch in reverse (undo the patch)',
)
# Grab what we're doing
args = parser.parse_args()
if args.command == 'diff':
with open(args.file1, "rb") as fp:
file1 = fp.read()
with open(args.file2, "rb") as fp:
file2 = fp.read()
try:
differences = BinaryDiff.diff(file1, file2)
except Exception as e:
print(f"Could not diff {args.file1} against {args.file2}: {str(e)}", file=sys.stderr)
return 1
if not args.patch_file:
for line in differences:
print(line)
else:
with open(args.patch_file, "w") as fp:
fp.write(os.linesep.join(differences))
elif args.command == 'patch':
with open(args.bin, "rb") as fp:
old = fp.read()
if not args.patch_file:
differences = sys.stdin.readlines()
else:
with open(args.patch_file, "r") as fp:
differences = fp.readlines()
differences = [d.strip() for d in differences if d.strip()]
try:
new = BinaryDiff.patch(old, differences, reverse=args.reverse)
except Exception as e:
print(f"Could not patch {args.bin}: {str(e)}", file=sys.stderr)
return 1
with open(args.out, "wb") as fp:
fp.write(new)
print(f"Patched {args.bin} and wrote to {args.out}.")
else:
print(f"Please specify a valid command!{os.linesep}", file=sys.stderr)
parser.print_help()
return 1
return 0
if __name__ == "__main__":
sys.exit(main())