1
0
Fork 0
mirror of https://github.com/zeldaret/oot.git synced 2024-11-11 11:49:24 +00:00
oot/tools/disasm/disasm.py
2024-01-24 14:13:07 -08:00

135 lines
5.2 KiB
Python
Executable file

#!/usr/bin/env python3
import argparse
import collections
from pathlib import Path
from typing import BinaryIO
import spimdisasm
from spimdisasm import frontendCommon as fec
from file_addresses import DmaFile, parse_file_addresses, get_z_name_for_overlay
def load_file_splits(
context: spimdisasm.common.Context,
config_dir: Path,
dma_file: DmaFile,
f: BinaryIO,
) -> spimdisasm.mips.FileSplits:
# Assume that we're reading from a decompressed ROM where the DMA file is
# now located at the same ROM offset as the VROM start
f.seek(dma_file.vrom_start)
data = bytearray(f.read(dma_file.vrom_end - dma_file.vrom_start))
file_splits_path = config_dir / f"files_{dma_file.name}.csv"
if file_splits_path.exists():
default_filename = ""
splits_data = spimdisasm.common.FileSplitFormat()
splits_data.readCsvFile(file_splits_path)
reloc_section = None
elif dma_file.overlay_dir is not None:
z_name = get_z_name_for_overlay(dma_file.name)
default_filename = f"src/overlays/{dma_file.overlay_dir}/{dma_file.name}/{z_name}.s"
splits_data = None
reloc_section = spimdisasm.mips.sections.SectionRelocZ64(
context,
vromStart=0,
vromEnd=len(data),
vram=dma_file.overlay_vram_start,
filename=default_filename,
array_of_bytes=data,
segmentVromStart=0,
overlayCategory=None)
else:
raise Exception(f"DMA file {dma_file.name} is not an overlay but has no file splits")
return spimdisasm.mips.FileSplits(
context,
vromStart=0,
vromEnd=len(data),
vram=dma_file.overlay_vram_start,
filename=default_filename,
array_of_bytes=data,
segmentVromStart=0,
overlayCategory=None,
splitsData=splits_data,
relocSection=reloc_section)
def main():
parser = argparse.ArgumentParser(description="Disassemble a ROM.")
parser.add_argument("rom", type=Path, help="Input ROM")
parser.add_argument("-o", "--output-dir", help="Output directory", type=Path, required=True)
parser.add_argument("--config-dir", help="Config directory", type=Path, required=True)
parser.add_argument("--split-functions", help="Write functions into separate files", type=Path)
spimdisasm.common.Context.addParametersToArgParse(parser)
spimdisasm.common.GlobalConfig.addParametersToArgParse(parser)
spimdisasm.mips.InstructionConfig.addParametersToArgParse(parser)
args = parser.parse_args()
context = spimdisasm.common.Context()
context.parseArgs(args)
context.changeGlobalSegmentRanges(0x00000000, 0x01000000, 0x8000000, 0x81000000)
context.addBannedSymbolRange(0x80000000, 0x80000400)
spimdisasm.mips.InstructionConfig.parseArgs(args)
spimdisasm.common.GlobalConfig.parseArgs(args)
spimdisasm.common.GlobalConfig.ASM_USE_PRELUDE = False
spimdisasm.common.GlobalConfig.PRODUCE_SYMBOLS_PLUS_OFFSET = True
spimdisasm.common.GlobalConfig.TRUST_USER_FUNCTIONS = True
dma_files = parse_file_addresses(args.config_dir / "file_addresses.csv")
output_files = collections.defaultdict(list)
with open(args.rom, "rb") as f:
for dma_file in dma_files:
file_splits = load_file_splits(context, args.config_dir, dma_file, f)
for section_type, files in file_splits.sectionsDict.items():
# TODO: disassemble overlay reloc sections?
if section_type == spimdisasm.common.FileSectionType.Reloc:
continue
for path, section in files.items():
output_files[path].append(section)
for path, sections in sorted(output_files.items()):
spimdisasm.common.Utils.printQuietless(f"Analyzing {path} ...")
for section in sections:
section.analyze()
for path, sections in sorted(output_files.items()):
spimdisasm.common.Utils.printQuietless(f"Writing {path} ...")
output_path = args.output_dir / path
output_path.parent.mkdir(parents=True, exist_ok=True)
with open(output_path, "w") as f:
f.write(".include \"macro.inc\"\n")
f.write("\n")
f.write(".set noat\n")
f.write(".set noreorder\n")
f.write(".set gp=64\n")
for section in sections:
f.write("\n")
f.write(f".section {section.sectionType.toStr()}\n")
f.write("\n")
f.write(f".align 4\n")
f.write("\n")
section.disassembleToFile(f)
if args.split_functions is not None:
rodata_list = []
for section in sections:
if section.sectionType == spimdisasm.common.FileSectionType.Rodata:
rodata_list.append(section)
for section in sections:
if section.sectionType != spimdisasm.common.FileSectionType.Text:
continue
output_dir = (args.split_functions / section.name).with_suffix("")
for func in section.symbolList:
spimdisasm.mips.FilesHandlers.writeSplitedFunction(output_dir, func, rodata_list)
if __name__ == "__main__":
main()