diff --git a/create_config.py b/create_config.py index 92898589..cad21226 100755 --- a/create_config.py +++ b/create_config.py @@ -281,7 +281,7 @@ def create_psx_config(exe_path: Path, exe_bytes: bytes): type: code start: 0x800 vram: 0x{exe.destination_vram:X} - bss_size: 0x{exe.bss_size:X} + # bss_size: Please fill out this value when you figure out the bss size subsegments: """ text_offset = exe.text_offset @@ -290,18 +290,13 @@ def create_psx_config(exe_path: Path, exe_bytes: bytes): - [0x800, rodata, 800] """ segments += f"""\ - - [0x{text_offset:X}, asm, {text_offset:X}] + - [0x{text_offset:X}, asm, {text_offset:X}] # estimated """ - if exe.data_vram != 0 and exe.data_size != 0: + if exe.data_offset != 0: data_offset = exe.data_offset segments += f"""\ - - [0x{data_offset:X}, data, {data_offset:X}] -""" - - if exe.bss_size != 0: - segments += f"""\ - - {{ start: 0x{exe.size:X}, type: bss, name: {exe.bss_vram:X}, vram: 0x{exe.bss_vram:X} }} + - [0x{data_offset:X}, data, {data_offset:X}] # estimated """ segments += f"""\ diff --git a/util/psx/psxexeinfo.py b/util/psx/psxexeinfo.py index 490bb6d8..39812f3b 100755 --- a/util/psx/psxexeinfo.py +++ b/util/psx/psxexeinfo.py @@ -11,60 +11,193 @@ from pathlib import Path +import rabbitizer +import spimdisasm + +# PSX EXE has the following layout +# header ; 0x80 bytes +# padding ; 0x780 bytes +# .rodata ; variable length +# .text ; variable length +# .data ; variable length +# .sdata ; variable length +# .bss ; variable length, all zeroes +# .sbss ; variable length, all zeroes + +PAYLOAD_OFFSET = 0x800 # 0x80 byte header followed by 0x780 bytes of zeroes +WORD_SIZE_BYTES = 4 + +UNSUPPORTED_OPS = { + # MIPS II + "beql", + "bgtzl", + "blezl", + "bnel", + "ldc1", + "ldc2", + "ll", + "sc", + "sdc1", + "sdc2", + "sync", + "teq", + "tge", + "tgei", + "tgeiu", + "tgeu", + "tlt", + "tltu", + "tne", + "tnei", + # MIPS III + "dadd", + "daddi", + "daddiu", + "daddu", + "dsub", + "dsubu", + "ld", + "ldl", + "ldr", + "lld", + "lwu", + "scd", + "sd", + "sdl", + "sdr", + # MIPS IV + "movn", + "movz", + "pref", + "prefx", +} + + +def is_valid(insn) -> bool: + if not insn.isValid(): + if insn.instrIdType.name in ("CPU_SPECIAL", "CPU_COP2"): + return True + else: + return False + + opcode = insn.getOpcodeName() + if opcode in UNSUPPORTED_OPS: + return False + + return True + + +def try_find_text( + rom_bytes, start_offset=PAYLOAD_OFFSET, valid_threshold=32 +) -> tuple[int, int]: + start = end = 0 + good_count = valid_count = 0 + + in_text = False + last_opcode = None + + words = struct.iter_unpack(" valid_threshold: + in_text = True + start = start_offset + ((i + 1 - valid_count) * WORD_SIZE_BYTES) + + last_opcode = insn.getOpcodeName() + + return (start, end) + + +def try_get_gp(rom_bytes, start_offset, max_instructions=50) -> int: + # $gp is set like this: + # /* A7738 800B7138 0E801C3C */ lui $gp, (0x800E0000 >> 16) + # /* A773C 800B713C 90409C27 */ addiu $gp, $gp, 0x4090 + gp = 0 + words = struct.iter_unpack(" max_instructions: + # give up + break + insn = rabbitizer.Instruction(word) + if insn.getOpcodeName() == "lui" and insn.rt.name == "gp": + gp = insn.getImmediate() << 16 + elif insn.getOpcodeName() == "addiu" and insn.rt.name == "gp": + gp += insn.getImmediate() + break + return gp + + +def read_word(exe_bytes, offset) -> int: + return struct.unpack(" int: - return self.initial_pc - self.destination_vram + 0x800 + return self.text_start @property def data_offset(self) -> int: - if self.data_vram == 0 or self.data_size == 0: - return 0 - return self.data_vram - self.destination_vram + 0x800 + return self.data_start @staticmethod def get_info(exe_path: Path, exe_bytes: bytes) -> PsxExe: - initial_pc = struct.unpack(" PsxExe: def main(): parser = argparse.ArgumentParser(description="Gives information on PSX EXEs") - parser.add_argument("exe", help="path to an PSX EXE") + parser.add_argument("exe", help="Path to an PSX EXE") args = parser.parse_args() @@ -80,7 +213,7 @@ def main(): exe_bytes = exe_path.read_bytes() exe = PsxExe.get_info(exe_path, exe_bytes) - print(f"Initial PC: 0x{exe.initial_pc:08X}") + print(f"Entrypoint: 0x{exe.entrypoint:08X}") print(f"Initial GP: ", end="") if exe.initial_gp != 0: @@ -90,23 +223,12 @@ def main(): print() print(f"Destination VRAM: 0x{exe.destination_vram:08X}") - print(f"File size (without header): 0x{exe.file_size:X}") - print(f"Text binary offset: 0x{exe.text_offset:08X}") - - if exe.data_vram != 0 and exe.data_size != 0: - print() - print(f"Data VRAM: 0x{exe.data_vram:08X}") - print(f"Data size: 0x{exe.data_size:08X}") - print(f"Data binary offset: 0x{exe.data_offset:08X}") - - if exe.bss_vram != 0 and exe.bss_size != 0: - print() - print(f"bss VRAM: 0x{exe.bss_vram:08X}") - print(f"bss size: 0x{exe.bss_size:08X}") + print(f"Payload size (without header): 0x{exe.payload_size:X}") print() - print(f"Initial SP base: 0x{exe.initial_sp_base:08X}") - print(f"Initial SP offset: 0x{exe.initial_sp_offset:08X}") + print(f"Text binary offset (estimate): 0x{exe.text_offset:X}") + if exe.data_offset != 0: + print(f"Data binary offset (estimate): 0x{exe.data_offset:X}") print() print(f"File size: 0x{exe.size:X}")