#!/usr/bin/env python3 """level_dump.py OUT_DIR LEVEL [LEVEL...] [--iso ISO] [--repo DECOMP_REPO] Writes each level's program (the code overlay ParseBin loads over the executable's main segment) to OUT_DIR/level_NN/: one file per record (lit, bss, data, vtbl, camvtbl, sndvtbl, text) and manifest.json with the load addresses and the entry point, the layout of OpenRAC's own dump (tools/overlays.py dump, baserom/overlays/level_NN/). The bytes come from the user's own disc image, read the way the game finds them: the table of contents at LBA 1500, the level's header, the header's "data" sector range, whose first word is the offset of the code overlay (docs/ASSETS.md, "Code overlays"). If the decomp repo already has an OpenRAC dump of the level (baserom/overlays/level_NN/manifest.json), that is used instead. Nothing here is ever committed: it is game data. recomp.py translates the level's assembly-only functions from text.bin. """ import json, os, shutil, struct, sys SECTOR = 2048 TOC_LBA, TOC_SIZE = 1500, 0x2960 LEVEL_TABLE = 0x28C8 # D_0013A548: (header LBA, size) per level LEVEL_HEADER_SIZE = 0x2434 RECORD_NAMES = ("lit", "bss", "data", "vtbl", "camvtbl", "sndvtbl", "text") def read(f, lba, size): f.seek(lba * SECTOR) d = f.read(size) if len(d) != size: sys.exit("level_dump: short read from the disc image at LBA %d" % lba) return d def wad(raw, repo): """the overlay is stored plain on PAL v2.00; a WAD-packed one is unpacked with OpenRAC's decoder (tools/extract/formats.py)""" sys.path.insert(0, os.path.join(repo, "tools", "extract")) from formats import wad as unwad return unwad(raw) def overlay_records(raw): """(entry point, [(address, size, type, bytes)]), as ParseBin (func_0012DA38) walks them""" entry = struct.unpack_from("/rac1_wrapper.cfg)" % lv) dump_from_iso(iso, lv, out, repo or "") print("level %02d: read from %s" % (lv, iso)) if __name__ == "__main__": main()