pokecrystal/extras/dump_sections.py

131 lines
3.5 KiB
Python
Executable File

#!/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)