You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
114 lines
3.3 KiB
114 lines
3.3 KiB
#! /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())
|
|
|