mirror of
https://github.com/encounter/ogws.git
synced 2026-03-30 11:33:37 -07:00
261 lines
8.4 KiB
Python
261 lines
8.4 KiB
Python
#!/usr/bin/env python3
|
|
|
|
################################################################################
|
|
# Description #
|
|
################################################################################
|
|
# extab.py: Disassembles the extab/extabindex sections of the OGWS DOL. #
|
|
# The user can optionally supply an output path for both assembly files. #
|
|
# Syntax: extab.py {EXTAB_OUT_PATH} {EXTABINDEX_OUT_PATH} #
|
|
# #
|
|
# Requirements: #
|
|
# - extab.py must be in the repo's \tools\ directory. #
|
|
# - The DOL must have been built at least once, so that the build map can #
|
|
# be referenced. #
|
|
################################################################################
|
|
|
|
|
|
|
|
|
|
###############################################
|
|
# #
|
|
# Imports #
|
|
# #
|
|
###############################################
|
|
|
|
import os
|
|
from sys import argv
|
|
import struct
|
|
|
|
###############################################
|
|
# #
|
|
# Constants #
|
|
# #
|
|
###############################################
|
|
|
|
UNK_VALUE = 0xD58AC
|
|
|
|
# From postprocess
|
|
substitutions = (
|
|
('<', '$$0'),
|
|
('>', '$$1'),
|
|
('@', '$$2'),
|
|
('\\', '$$3'),
|
|
(',', '$$4'),
|
|
('-', '$$5'),
|
|
)
|
|
|
|
# Section info
|
|
EXTAB_SECTION = {
|
|
"START" : 0x800064e0, # Virtual address of section
|
|
"OFFSET" : 0x34ff80, # Local offset (in file)
|
|
"SIZE" : 0x740 # Size (B)
|
|
}
|
|
EXTABINDEX_SECTION = {
|
|
"START" : 0x80006c20, # Virtual address of section
|
|
"OFFSET" : 0x3506c0, # Local offset (in file)
|
|
"SIZE" : 0xac0 # Size (B)
|
|
}
|
|
|
|
PPC_INST_SIZE = 4
|
|
EXTABINDEX_ENTRY_SIZE = PPC_INST_SIZE * 3
|
|
|
|
# Table placed at the end of extabindex,
|
|
# used by __init_cpp_exceptions/__register_fragment
|
|
"""
|
|
struct _eti_init_info_ {
|
|
void *pExtabindex; // aka ptr to first entry
|
|
void *p_eti_init_info_;
|
|
void *pFirstFunc;
|
|
u32 totalCodeSize;
|
|
u32 unk[4];
|
|
}
|
|
"""
|
|
INIT_TABLE_LEN = 10 * PPC_INST_SIZE
|
|
|
|
EXTABINDEX_HEADER = \
|
|
".include \"macros.inc\"\n\n" \
|
|
".section extabindex_, \"wa\"\n\n"
|
|
|
|
EXTAB_HEADER = \
|
|
".include \"macros.inc\"\n\n" \
|
|
".section extab_, \"wa\"\n\n"
|
|
|
|
###############################################
|
|
# #
|
|
# Classes #
|
|
# #
|
|
###############################################
|
|
|
|
class ExtabIndexEntry:
|
|
def __init__(self, funcPtr : str, funcSize : str, handlerPtr : int):
|
|
self.funcPtr = funcPtr
|
|
self.funcSize = funcSize
|
|
self.handlerPtr = handlerPtr
|
|
self.symbol = proc(getSymbolByAddr(self.funcPtr))
|
|
|
|
def ToString(self) -> str:
|
|
return "\n".join([f".global _index_{self.symbol}",
|
|
f"_index_{self.symbol}:",
|
|
f" .long {self.symbol}",
|
|
f" .long 0x{self.funcSize}",
|
|
f" .long _unwind_{self.symbol}",
|
|
"\n"])
|
|
|
|
class ExtabEntry:
|
|
def __init__(self, parent : ExtabIndexEntry, code : list):
|
|
self.parent = parent
|
|
self.code = code
|
|
|
|
def ToString(self) -> str:
|
|
code = ""
|
|
for i in range(len(self.code) // PPC_INST_SIZE):
|
|
currLong = self.code[i * 4 : (i + 1) * 4].hex()
|
|
# Sometimes a destructor is inserted after the stack-unwinding instructions.
|
|
# This will catch those occurrences and label them appropriately.
|
|
currSymb = getSymbolByAddr(currLong)
|
|
code = "".join([code, f" .long {currSymb}", "\n"]); i += 4
|
|
|
|
return "\n".join([f".global _unwind_{self.parent.symbol}",
|
|
f"_unwind_{self.parent.symbol}:",
|
|
code]) + "\n"
|
|
|
|
###############################################
|
|
# #
|
|
# Functions #
|
|
# #
|
|
###############################################
|
|
|
|
def proc(symbol):
|
|
for sub in substitutions:
|
|
symbol = symbol.replace(sub[0], sub[1])
|
|
return symbol
|
|
|
|
def toHex32(val) -> str:
|
|
return "{:08x}".format(val)
|
|
|
|
def getU32(data, ofs) -> int:
|
|
return ((data[ofs + 0] << 24) |
|
|
(data[ofs + 1] << 16) |
|
|
(data[ofs + 2] << 8) |
|
|
(data[ofs + 3] << 0))
|
|
|
|
def getU32BE(data, ofs) -> int:
|
|
return ((data[ofs + 3] << 24) |
|
|
(data[ofs + 2] << 16) |
|
|
(data[ofs + 1] << 8) |
|
|
(data[ofs + 0] << 0))
|
|
|
|
def getSymbolByAddr(addr) -> str:
|
|
for key in symbols:
|
|
if key == addr:
|
|
return format(symbols[key])
|
|
return "0x" + str(addr)
|
|
|
|
###############################################
|
|
# #
|
|
# Entrypoint #
|
|
# #
|
|
###############################################
|
|
|
|
if __name__ == "__main__":
|
|
|
|
# Symbol dictionary
|
|
symbols = dict()
|
|
|
|
# Args (optional)
|
|
customOut = True
|
|
try:
|
|
EXTAB_OUT = argv[1]
|
|
EXTABINDEX_OUT = argv[2]
|
|
except IndexError:
|
|
print("No output path specified. Writing to extab.s and extabindex.s, respectively.")
|
|
customOut = False
|
|
|
|
# Build symbol dictionary
|
|
with open("build/ogws_us_r1.map", "r") as f:
|
|
map = f.readlines()
|
|
|
|
assert len(map) > 1, "Are you sure the map file is not empty from a failed build?"
|
|
|
|
for i in map:
|
|
# Get symbol virtual address
|
|
addr = i[18:26]
|
|
# Get symbol name
|
|
symbEnd = i[39:].find(' ')
|
|
symb = i[39:symbEnd+39]
|
|
# Add to dict
|
|
symbols[addr] = symb
|
|
|
|
# Read baserom data
|
|
with open("include/baserom.dol", "rb") as f:
|
|
baserom = bytearray(f.read())
|
|
|
|
# Split section data
|
|
# baserom[offset : offset + size]
|
|
extabindex_ = baserom[
|
|
EXTABINDEX_SECTION["OFFSET"] :
|
|
EXTABINDEX_SECTION["OFFSET"] + EXTABINDEX_SECTION["SIZE"]]
|
|
|
|
extab_ = baserom[
|
|
EXTAB_SECTION["OFFSET"] :
|
|
EXTAB_SECTION["OFFSET"] + EXTAB_SECTION["SIZE"]]
|
|
|
|
# Build extabindex entries
|
|
extabindex = list()
|
|
i = 0
|
|
totalCodeSize = 0
|
|
while i < (EXTABINDEX_SECTION["SIZE"] - INIT_TABLE_LEN):
|
|
func = toHex32(getU32(extabindex_, i)); i+= PPC_INST_SIZE
|
|
size = getU32(extabindex_, i); i += PPC_INST_SIZE
|
|
totalCodeSize += size
|
|
handler = getU32(extabindex_, i); i += PPC_INST_SIZE
|
|
extabindex.append(ExtabIndexEntry(func, toHex32(size), handler))
|
|
|
|
# Write extabindex
|
|
if (customOut):
|
|
f = open(EXTABINDEX_OUT, "w+")
|
|
else:
|
|
f = open("extabindex.s", "w+")
|
|
|
|
f.write(EXTABINDEX_HEADER)
|
|
for i in extabindex:
|
|
f.writelines(i.ToString())
|
|
|
|
# Write eti info
|
|
f.write("# For __init_cpp_exceptions/__register_fragment\n")
|
|
f.write(".global _eti_init_info_\n")
|
|
f.write("_eti_init_info_:\n")
|
|
f.write(f" .long _index_{extabindex[0].symbol} # &extabindex[0]\n")
|
|
f.write(f" .long _eti_init_info_ # &_eti_init_info_\n")
|
|
f.write(f" .long {extabindex[0].symbol} # &extabindex[0].func\n")
|
|
f.write(f" .long 0x{toHex32(UNK_VALUE)} # unkValue\n")
|
|
f.writelines([" .long 0x00000000 # Padding\n"] * 4)
|
|
f.write(".balign 0x8\n")
|
|
f.close()
|
|
|
|
# Parse extab
|
|
extab = list()
|
|
for j in range(len(extabindex)):
|
|
currentHandler = extabindex[j].handlerPtr - EXTAB_SECTION["START"]
|
|
|
|
# The last extab entry will end at the end of the extab section
|
|
if (j == len(extabindex) - 1):
|
|
extab.append(ExtabEntry(extabindex[j], extab_[currentHandler : ]))
|
|
# All other extab entries end at the start of the next entry (the next extabindex entry's handler ptr)
|
|
else:
|
|
nextHandler = extabindex[j + 1].handlerPtr - EXTAB_SECTION["START"]
|
|
extab.append(ExtabEntry(extabindex[j], extab_[currentHandler : nextHandler]))
|
|
|
|
# Write extab
|
|
if (customOut):
|
|
f = open(EXTAB_OUT, "w+")
|
|
else:
|
|
f = open("extab.s", "w+")
|
|
|
|
f.write(EXTAB_HEADER)
|
|
for i in extab:
|
|
f.writelines(i.ToString())
|
|
f.close()
|
|
|
|
print("Done!")
|