mirror of
https://github.com/BrianPugh/game-and-watch-patch.git
synced 2025-12-16 07:16:26 +01:00
138 lines
4.8 KiB
Python
138 lines
4.8 KiB
Python
"""
|
|
For usage, run:
|
|
python3 patch.py --help
|
|
"""
|
|
|
|
import sys
|
|
if sys.version_info[0] < 3 or sys.version_info[1] < 6:
|
|
raise Exception("Must be using at least Python 3.6")
|
|
|
|
|
|
from pathlib import Path
|
|
import argparse
|
|
|
|
from patches import Device, IntFirmware, ExtFirmware
|
|
from patches import apply_patches, add_patch_args, validate_patch_args
|
|
from patches.exception import InvalidPatchError
|
|
|
|
import colorama
|
|
from colorama import Fore, Back, Style
|
|
colorama.init()
|
|
|
|
|
|
def parse_args():
|
|
parser = argparse.ArgumentParser(description="Game and Watch Firmware Patcher.")
|
|
|
|
#########################
|
|
# Global configurations #
|
|
#########################
|
|
parser.add_argument('--int-firmware', type=Path, default="internal_flash_backup.bin",
|
|
help="Input stock internal firmware.")
|
|
parser.add_argument('--ext-firmware', type=Path, default="flash_backup.bin",
|
|
help="Input stock external firmware.")
|
|
parser.add_argument('--patch', type=Path, default="build/gw_patch.bin",
|
|
help="Compiled custom code to insert at the end of the internal firmware")
|
|
parser.add_argument('--elf', type=Path, default="build/gw_patch.elf",
|
|
help="ELF file corresponding to the bin provided by --patch")
|
|
parser.add_argument('--int-output', type=Path, default="build/internal_flash_patched.bin",
|
|
help="Patched internal firmware.")
|
|
parser.add_argument('--ext-output', type=Path, default="build/external_flash_patched.bin",
|
|
help="Patched external firmware.")
|
|
|
|
parser.add_argument("--extended", action="store_true", default=False,
|
|
help="256KB internal flash image instead of 128KB.")
|
|
parser.add_argument("--encrypt", action="store_true",
|
|
help="Enable OTFDEC for the main extflash binary.")
|
|
parser.add_argument("--compression-ratio", type=float, default=1.4,
|
|
help="Data targeted for SRAM3 will only be put into "
|
|
"SRAM3 if it's compression ratio is above this value. "
|
|
"Otherwise, will fallback to internal flash, then external "
|
|
"flash."
|
|
)
|
|
|
|
|
|
debugging = parser.add_argument_group("Debugging")
|
|
debugging.add_argument("--show", action="store_true",
|
|
help="Show a picture representation of the external patched binary.")
|
|
debugging.add_argument("--debug", action="store_true",
|
|
help="Install useful debugging fault handlers.")
|
|
|
|
|
|
########################
|
|
# Patch configurations #
|
|
########################
|
|
add_patch_args(parser)
|
|
|
|
# Final Validation
|
|
args = parser.parse_args()
|
|
validate_patch_args(parser, args)
|
|
|
|
return args
|
|
|
|
|
|
def main():
|
|
args = parse_args()
|
|
|
|
device = Device(
|
|
IntFirmware(args.int_firmware, args.elf),
|
|
ExtFirmware(args.ext_firmware)
|
|
)
|
|
|
|
# Decrypt the external firmware
|
|
device.crypt()
|
|
|
|
# Save the decrypted external firmware for debugging/development purposes.
|
|
Path("build/decrypt.bin").write_bytes(device.external)
|
|
|
|
# Copy over novel code
|
|
patch = args.patch.read_bytes()
|
|
if len(device.internal) != len(patch):
|
|
raise InvalidPatchError(f"Expected patch length {len(device.internal)}, got {len(patch)}")
|
|
|
|
novel_code_start = device.internal.address("__do_global_dtors_aux") & 0x00FF_FFF8
|
|
device.internal[novel_code_start:] = patch[novel_code_start:]
|
|
del patch
|
|
|
|
if args.extended:
|
|
device.internal.extend(b"\x00" * 0x20000)
|
|
|
|
|
|
print(Fore.BLUE)
|
|
print("#########################")
|
|
print("# BEGINING BINARY PATCH #")
|
|
print("#########################" + Style.RESET_ALL)
|
|
|
|
# Perform all replacements in stock code.
|
|
internal_remaining_free, sram3_remaining_free = apply_patches(args, device)
|
|
|
|
# Erase the extflash vram region
|
|
if not args.no_save:
|
|
device.external[-8192:] = b"\x00" * 8192
|
|
|
|
if args.show:
|
|
# Debug visualization
|
|
device.show()
|
|
|
|
# Re-encrypt the external firmware
|
|
Path("build/decrypt_flash_patched.bin").write_bytes(device.external)
|
|
if args.encrypt:
|
|
device.external.crypt(device.internal.key, device.internal.nonce)
|
|
|
|
|
|
# Save patched firmware
|
|
args.int_output.write_bytes(device.internal)
|
|
args.ext_output.write_bytes(device.external)
|
|
|
|
print(Fore.GREEN)
|
|
print( "Binary Patching Complete!")
|
|
print(f" Internal Firmware Used: {len(device.internal) - internal_remaining_free} bytes")
|
|
print(f" Free: {internal_remaining_free} bytes")
|
|
print(f" SRAM3 Used: {len(device.sram3) - sram3_remaining_free} bytes")
|
|
print(f" Free: {sram3_remaining_free} bytes")
|
|
print(f" External Firmware Used: {len(device.external)} bytes")
|
|
print(Style.RESET_ALL)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|