shithub: pokecrystal

ref: 084083add038cf3618803be76282640ca071bbc4
dir: /extras/dump_sections.py/

View raw version
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
Use this tool to dump an asm file for a new source code or disassembly project.

usage:

    from dump_sections import dump_sections

    output = dump_sections("../../butt.gbc")

    file_handler = open("main.asm", "w")
    file_handler.write(output)
    file_handler.close()

You can also use this script from the shell, where it will look for
"baserom.gbc" in the current working path or whatever file path you pass in the
first positional argument.
"""

import os
import sys
import argparse

def upper_hex(input):
    """
    Converts the input to an uppercase hex string.
    """
    if input in [0, "0"]:
        return "0"
    elif input <= 0xF:
        return ("%.x" % (input)).upper()
    else:
        return ("%.2x" % (input)).upper()

def format_bank_number(address, bank_size=0x4000):
    """
    Returns a str of the hex number of the bank based on the address.
    """
    return upper_hex(address / bank_size)

def calculate_bank_quantity(path, bank_size=0x4000):
    """
    Returns the number of 0x4000 banks in the file at path.
    """
    return float(os.lstat(path).st_size) / bank_size

def dump_section(bank_number, separator="\n\n"):
    """
    Returns a str of a section header for the asm file.
    """
    output = "SECTION \""
    if bank_number in [0, "0"]:
        output += "bank0\",HOME"
    else:
        output += "bank"
        output += bank_number
        output += "\",DATA,BANK[$"
        output += bank_number
        output += "]"
    output += separator
    return output

def dump_incbin_for_section(address, bank_size=0x4000, baserom="baserom.gbc", separator="\n\n"):
    """
    Returns a str for an INCBIN line for an entire section.
    """
    output = "INCBIN \""
    output += baserom
    output += "\",$"
    output += upper_hex(address)
    output += ",$"
    output += upper_hex(bank_size)
    output += separator
    return output

def dump_sections(path, bank_size=0x4000, initial_bank=0, last_bank=None, separator="\n\n"):
    """
    Returns a str of assembly source code. The source code delineates each
    SECTION and includes bytes from the file specified by baserom.
    """
    if not last_bank:
        last_bank = calculate_bank_quantity(path, bank_size=bank_size)

    if last_bank < initial_bank:
        raise Exception("last_bank must be greater than or equal to initial_bank")

    baserom_name = os.path.basename(path)

    output = ""

    banks = range(initial_bank, last_bank)

    for bank_number in banks:
        address = bank_number * bank_size

        # get a formatted hex number of the bank based on the address
        formatted_bank_number = format_bank_number(address, bank_size=bank_size)

        # SECTION
        output += dump_section(formatted_bank_number, separator=separator)

        # INCBIN a range of bytes from the ROM
        output += dump_incbin_for_section(address, bank_size=bank_size, baserom=baserom_name, separator=separator)

    # clean up newlines at the end of the output
    if output[-2:] == "\n\n":
        output = output[:-2]
        output += "\n"

    return output

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("rompath", nargs="?", metavar="rompath", type=str)
    args = parser.parse_args()

    # default to "baserom.gbc" in the current working directory
    baserom = "baserom.gbc"

    # but let the user override the default
    if args.rompath:
        baserom = args.rompath

    # generate some asm
    output = dump_sections(baserom)

    # dump everything to stdout
    sys.stdout.write(output)