Example #1
0
def get_prg_addresses(fileInfo, args, handle):
    # get PRG ROM addresses affected by code in file1

    (cpuAddr, repl, comp) = qneslib.game_genie_decode(args.code)

    if comp is None and qneslib.is_prg_bankswitched(fileInfo["prgSize"],
                                                    fileInfo["mapper"]):
        sys.exit(
            "Six-letter codes not supported because file1 uses PRG ROM bankswitching."
        )

    prgBankSize = qneslib.min_prg_bank_size(fileInfo["prgSize"],
                                            fileInfo["mapper"])
    prgAddresses = set()
    if comp is None or comp != repl:
        if comp is None:
            # 6-letter code (old value must not equal replace value)
            validValues = set(range(0x100)) - {repl}
        else:
            # 8-letter code (old value must equal compare value)
            validValues = {comp}
        for prgAddr in qneslib.address_cpu_to_prg(cpuAddr, prgBankSize,
                                                  fileInfo["prgSize"]):
            handle.seek(fileInfo["prgStart"] + prgAddr)
            if handle.read(1)[0] in validValues:
                prgAddresses.add(prgAddr)
    return prgAddresses
Example #2
0
def get_fake_compare_value(handle, args):
    # get a fake compare value from file1 for user's six-letter code
    cpuAddr = qneslib.game_genie_decode(args.code)[0]
    fileInfo = qneslib.ines_header_decode(handle)
    prgAddr = cpuAddr & (fileInfo["prgSize"] - 1)
    handle.seek(fileInfo["prgStart"] + prgAddr)
    return handle.read(1)[0]
Example #3
0
def print_results(cpuAddresses, compareValue, args):
    # print codes with new addresses (sort by difference from original address)
    (origCpuAddr, replaceValue) = qneslib.game_genie_decode(args.code)[:2]
    cpuAddresses = sorted(cpuAddresses)
    cpuAddresses.sort(key=lambda addr: abs(addr - origCpuAddr))
    print(
        "Game Genie codes for file2 (try the first one first):", ", ".join(
            qneslib.game_genie_encode(a, replaceValue, compareValue)
            for a in cpuAddresses))
Example #4
0
def print_decoded_code(args):
    (addr, repl, comp) = qneslib.game_genie_decode(args.code)
    comp = "none" if comp is None else f"{comp:02x}"
    print(
        f"Code: CPU address={addr:04x}, replace value={repl:02x}, compare value={comp}"
    )
Example #5
0
def parse_arguments():
    # parse command line arguments using argparse

    parser = argparse.ArgumentParser(
        description=
        "Convert an NES Game Genie code from one version of a game to another using "
        "both iNES ROM files (.nes). Technical explanation: decode the code; find out PRG ROM "
        "addresses affected in file1; see what's in and around them; look for similar bytestrings "
        "in file2's PRG ROM; convert the addresses back into CPU addresses; encode them into "
        "codes.")
    parser.add_argument(
        "-s",
        "--slice-length",
        type=int,
        default=4,
        help=
        "How many PRG ROM bytes to compare both before and after the relevant byte (that is, "
        "total number of bytes compared is twice this value, plus one). Fewer bytes will be "
        "compared if the relevant byte is too close to start or end of PRG ROM. 1 to 20, "
        "default=4. Decrease to get more results.")
    parser.add_argument(
        "-d",
        "--max-different-bytes",
        type=int,
        default=1,
        help=
        "Maximum number of non-matching bytes allowed in each pair of PRG ROM slices to "
        "compare. (The relevant byte must always match.) Minimum=0, default=1, maximum=twice "
        "--slice-length, minus one. Increase to get more results.")
    parser.add_argument(
        "-v",
        "--verbose",
        action="store_true",
        help=
        "Print more information. Note: all printed numbers are hexadecimal.")
    parser.add_argument(
        "code",
        help=
        "An NES Game Genie code that is known to work with file1. Six-letter codes are not "
        "allowed if file1 uses PRG ROM bankswitching.")
    parser.add_argument(
        "file1",
        help=
        "An iNES ROM file (.nes) to read. The game your code is known to work with."
    )
    parser.add_argument(
        "file2",
        help=
        "Another iNES ROM file (.nes) to read. The equivalent code for this game will be "
        "searched for.")
    args = parser.parse_args()

    if not 1 <= args.slice_length <= 20:
        sys.exit("Invalid --slice-length.")
    if not 0 <= args.max_different_bytes < 2 * args.slice_length:
        sys.exit("Invalid --max-different-bytes.")
    if qneslib.game_genie_decode(args.code) is None:
        sys.exit("Invalid code.")
    if not os.path.isfile(args.file1):
        sys.exit("file1 not found.")
    if not os.path.isfile(args.file2):
        sys.exit("file2 not found.")

    return args
Example #6
0
def main():
    args = parse_arguments()

    if args.verbose:
        print_decoded_code(args)

    compareValue = qneslib.game_genie_decode(args.code)[2]

    # get PRG addresses, PRG slices and optionally a fake compare value from file1
    try:
        with open(args.file1, "rb") as handle:
            fileInfo = qneslib.ines_header_decode(handle)
            if fileInfo is None:
                sys.exit("file1 is not a valid iNES ROM file.")

            prgAddresses = get_prg_addresses(fileInfo, args, handle)
            if not prgAddresses:
                sys.exit("Your code seems to affect file1 in no way.")
            slices = set(get_prg_slices(prgAddresses, fileInfo, args, handle))

            if compareValue is None:
                compareValue = get_fake_compare_value(handle, args)
                if args.verbose:
                    print(f"Using fake compare value: {compareValue:02x}")
    except OSError:
        sys.exit("Error reading file1.")

    if args.verbose:
        print("PRG addresses in file1:",
              ", ".join(f"{addr:04x}" for addr in sorted(prgAddresses)))
        print_slices(slices, compareValue)

    # find PRG addresses in file2
    try:
        with open(args.file2, "rb") as handle:
            fileInfo = qneslib.ines_header_decode(handle)
            if fileInfo is None:
                sys.exit("file2 is not a valid iNES ROM file.")
            prgAddresses = set(
                find_slices_in_prg(handle, slices, compareValue, args))
    except OSError:
        sys.exit("Error reading file2.")
    if not prgAddresses:
        sys.exit(
            "file2 contains nothing similar to what your code affects in file1."
        )
    if args.verbose:
        print("PRG address matches in file2:",
              ", ".join(f"{a:04x}" for a in sorted(prgAddresses)))

    # convert PRG addresses into CPU addresses
    cpuAddresses = set()
    prgBankSize = qneslib.min_prg_bank_size(fileInfo["prgSize"],
                                            fileInfo["mapper"])
    for prgAddr in prgAddresses:
        cpuAddresses.update(qneslib.address_prg_to_cpu(prgAddr, prgBankSize))
    if args.verbose:
        print("CPU address matches in file2:",
              ", ".join(f"{a:04x}" for a in sorted(cpuAddresses)))

    # if file2 not bankswitched, discard compare value to output six-letter codes
    if not qneslib.is_prg_bankswitched(fileInfo["prgSize"],
                                       fileInfo["mapper"]):
        compareValue = None

    print_results(cpuAddresses, compareValue, args)
Example #7
0
import sys
import qneslib  # qalle's NES library, https://github.com/qalle2/nes-util

if len(sys.argv) == 2:
    # decode Game Genie code
    values = qneslib.game_genie_decode(sys.argv[1])
    if values is None:
        sys.exit("Invalid Game Genie code.")
    # reencode to get canonical form
    code = qneslib.game_genie_encode(*values)
elif 3 <= len(sys.argv) <= 4:
    # encode Game Genie code
    try:
        values = [int(n, 16) for n in sys.argv[1:]]
    except ValueError:
        sys.exit("Invalid hexadecimal integers.")
    code = qneslib.game_genie_encode(*values)
    if code is None:
        sys.exit("Hexadecimal integers out of range.")
    # redecode to get canonical form
    values = qneslib.game_genie_decode(code)
else:
    sys.exit(
        "Encode and decode NES Game Genie codes. Argument: six-letter code, eight-letter code, "
        "AAAA RR or AAAA RR CC (AAAA = address in hexadecimal, RR = replacement value in "
        "hexadecimal, CC = compare value in hexadecimal)."
    )

comp = "none" if values[2] is None else f"0x{values[2]:02x}"
print(
    f"{code}: CPU address = 0x{values[0]:04x}, replace value = 0x{values[1]:02x}, compare value = "
Example #8
0
import os, sys
import qneslib  # qalle's NES library, https://github.com/qalle2/nes-util

# read args
if len(sys.argv) != 3:
    sys.exit(
        "Find the PRG ROM addresses affected by an NES Game Genie code in an iNES ROM file (.nes). "
        "Args: file code")
(filename, code) = sys.argv[1:]

# decode the code
values = qneslib.game_genie_decode(code)
if values is None:
    sys.exit("Invalid code.")
(cpuAddr, replaceValue, compareValue) = values

if not os.path.isfile(filename):
    sys.exit("File not found.")

try:
    with open(filename, "rb") as handle:
        # get file info
        fileInfo = qneslib.ines_header_decode(handle)
        if fileInfo is None:
            sys.exit("Invalid iNES ROM file.")
        # get PRG ROM addresses
        prgAddresses = []
        if compareValue is None or compareValue != replaceValue:
            if compareValue is None:
                # 6-letter code (old value must not equal replace value)
                validValues = set(range(0x100)) - {