From ded9185216670c684e3653f14657d3703169a33f Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Tue, 11 Feb 2025 18:31:17 -0600 Subject: [PATCH] initial --- .gitignore | 4 + holly.py | 230 ++++++++++++++++++++++++++++++++++++++++++++++++ main.py | 47 ++++++++++ object_list.py | 75 ++++++++++++++++ region_array.py | 57 ++++++++++++ 5 files changed, 413 insertions(+) create mode 100644 .gitignore create mode 100644 holly.py create mode 100644 main.py create mode 100644 object_list.py create mode 100644 region_array.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f759374 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.bin +__pycache__/ +*.pyc +out*.txt diff --git a/holly.py b/holly.py new file mode 100644 index 0000000..c8ab620 --- /dev/null +++ b/holly.py @@ -0,0 +1,230 @@ +from dataclasses import dataclass +import struct + +@dataclass +class Holly: + ID: int + REVISION: int + SOFTRESET: int + STARTRENDER: int + TEST_SELECT: int + PARAM_BASE: int + REGION_BASE: int + SPAN_SORT_CFG: int + VO_BORDER_COL: int + FB_R_CTRL: int + FB_W_CTRL: int + FB_W_LINESTRIDE: int + FB_R_SOF1: int + FB_R_SOF2: int + FB_R_SIZE: int + FB_W_SOF1: int + FB_W_SOF2: int + FB_X_CLIP: int + FB_Y_CLIP: int + FPU_SHAD_SCALE: int + FPU_CULL_VAL: int + FPU_PARAM_CFG: int + HALF_OFFSET: int + FPU_PERP_VAL: int + ISP_BACKGND_D: int + ISP_BACKGND_T: int + ISP_FEED_CFG: int + SDRAM_REFRESH: int + SDRAM_ARB_CFG: int + SDRAM_CFG: int + FOG_COL_RAM: int + FOG_COL_VERT: int + FOG_DENSITY: int + FOG_CLAMP_MAX: int + FOG_CLAMP_MIN: int + SPG_TRIGGER_POS: int + SPG_HBLANK_INT: int + SPG_VBLANK_INT: int + SPG_CONTROL: int + SPG_HBLANK: int + SPG_LOAD: int + SPG_VBLANK: int + SPG_WIDTH: int + TEXT_CONTROL: int + VO_CONTROL: int + VO_STARTX: int + VO_STARTY: int + SCALER_CTL: int + PAL_RAM_CTRL: int + SPG_STATUS: int + FB_BURSTCTRL: int + FB_C_SOF: int + Y_COEFF: int + PT_ALPHA_REF: int + TA_OL_BASE: int + TA_ISP_BASE: int + TA_OL_LIMIT: int + TA_ISP_LIMIT: int + TA_NEXT_OPB: int + TA_ITP_CURRENT: int + TA_GLOB_TILE_CLIP: int + TA_ALLOC_CTRL: int + TA_LIST_INIT: int + TA_YUV_TEX_BASE: int + TA_YUV_TEX_CTRL: int + TA_YUV_TEX_CNT: int + TA_LIST_CONT: int + TA_NEXT_OPB_INIT: int + FOG_TABLE: list[int] + TA_OL_POINTERS: list[int] + PALETTE_RAM: list[int] + + def __init__(self): + pass + + def __repr__(self): + return (f"{self.__class__.__qualname__}(" + + ",\n ".join([ + f"ID={self.ID:08x}", + f"REVISION={self.REVISION:08x}", + f"SOFTRESET={self.SOFTRESET:08x}", + f"STARTRENDER={self.STARTRENDER:08x}", + f"TEST_SELECT={self.TEST_SELECT:08x}", + f"PARAM_BASE={self.PARAM_BASE:08x}", + f"REGION_BASE={self.REGION_BASE:08x}", + f"SPAN_SORT_CFG={self.SPAN_SORT_CFG:08x}", + f"VO_BORDER_COL={self.VO_BORDER_COL:08x}", + f"FB_R_CTRL={self.FB_R_CTRL:08x}", + f"FB_W_CTRL={self.FB_W_CTRL:08x}", + f"FB_W_LINESTRIDE={self.FB_W_LINESTRIDE:08x}", + f"FB_R_SOF1={self.FB_R_SOF1:08x}", + f"FB_R_SOF2={self.FB_R_SOF2:08x}", + f"FB_R_SIZE={self.FB_R_SIZE:08x}", + f"FB_W_SOF1={self.FB_W_SOF1:08x}", + f"FB_W_SOF2={self.FB_W_SOF2:08x}", + f"FB_X_CLIP={self.FB_X_CLIP:08x}", + f"FB_Y_CLIP={self.FB_Y_CLIP:08x}", + f"FPU_SHAD_SCALE={self.FPU_SHAD_SCALE:08x}", + f"FPU_CULL_VAL={self.FPU_CULL_VAL:08x}", + f"FPU_PARAM_CFG={self.FPU_PARAM_CFG:08x}", + f"HALF_OFFSET={self.HALF_OFFSET:08x}", + f"FPU_PERP_VAL={self.FPU_PERP_VAL:08x}", + f"ISP_BACKGND_D={self.ISP_BACKGND_D:08x}", + f"ISP_BACKGND_T={self.ISP_BACKGND_T:08x}", + f"ISP_FEED_CFG={self.ISP_FEED_CFG:08x}", + f"SDRAM_REFRESH={self.SDRAM_REFRESH:08x}", + f"SDRAM_ARB_CFG={self.SDRAM_ARB_CFG:08x}", + f"SDRAM_CFG={self.SDRAM_CFG:08x}", + f"FOG_COL_RAM={self.FOG_COL_RAM:08x}", + f"FOG_COL_VERT={self.FOG_COL_VERT:08x}", + f"FOG_DENSITY={self.FOG_DENSITY:08x}", + f"FOG_CLAMP_MAX={self.FOG_CLAMP_MAX:08x}", + f"FOG_CLAMP_MIN={self.FOG_CLAMP_MIN:08x}", + f"SPG_TRIGGER_POS={self.SPG_TRIGGER_POS:08x}", + f"SPG_HBLANK_INT={self.SPG_HBLANK_INT:08x}", + f"SPG_VBLANK_INT={self.SPG_VBLANK_INT:08x}", + f"SPG_CONTROL={self.SPG_CONTROL:08x}", + f"SPG_HBLANK={self.SPG_HBLANK:08x}", + f"SPG_LOAD={self.SPG_LOAD:08x}", + f"SPG_VBLANK={self.SPG_VBLANK:08x}", + f"SPG_WIDTH={self.SPG_WIDTH:08x}", + f"TEXT_CONTROL={self.TEXT_CONTROL:08x}", + f"VO_CONTROL={self.VO_CONTROL:08x}", + f"VO_STARTX={self.VO_STARTX:08x}", + f"VO_STARTY={self.VO_STARTY:08x}", + f"SCALER_CTL={self.SCALER_CTL:08x}", + f"PAL_RAM_CTRL={self.PAL_RAM_CTRL:08x}", + f"SPG_STATUS={self.SPG_STATUS:08x}", + f"FB_BURSTCTRL={self.FB_BURSTCTRL:08x}", + f"FB_C_SOF={self.FB_C_SOF:08x}", + f"Y_COEFF={self.Y_COEFF:08x}", + f"PT_ALPHA_REF={self.PT_ALPHA_REF:08x}", + f"TA_OL_BASE={self.TA_OL_BASE:08x}", + f"TA_ISP_BASE={self.TA_ISP_BASE:08x}", + f"TA_OL_LIMIT={self.TA_OL_LIMIT:08x}", + f"TA_ISP_LIMIT={self.TA_ISP_LIMIT:08x}", + f"TA_NEXT_OPB={self.TA_NEXT_OPB:08x}", + f"TA_ITP_CURRENT={self.TA_ITP_CURRENT:08x}", + f"TA_GLOB_TILE_CLIP={self.TA_GLOB_TILE_CLIP:08x}", + f"TA_ALLOC_CTRL={self.TA_ALLOC_CTRL:08x}", + f"TA_LIST_INIT={self.TA_LIST_INIT:08x}", + f"TA_YUV_TEX_BASE={self.TA_YUV_TEX_BASE:08x}", + f"TA_YUV_TEX_CTRL={self.TA_YUV_TEX_CTRL:08x}", + f"TA_YUV_TEX_CNT={self.TA_YUV_TEX_CNT:08x}", + f"TA_LIST_CONT={self.TA_LIST_CONT:08x}", + f"TA_NEXT_OPB_INIT={self.TA_NEXT_OPB_INIT:08x}", + ]) + + ")") + +def decode_holly(buf: bytes) -> Holly: + mem = memoryview(buf) + holly = Holly() + holly.ID, = struct.unpack('> 31) & 1 == 0, value + + self.mask = (value >> 25) & 0b111111 + self.shadow = (value >> 24) & 0b1 + self.skip = (value >> 21) & 0b111 + self.triangle_strip_start = (value & 0x1fffff) << 2 + +@dataclass +class triangle_array: + number_of_triangles: int + shadow: int + skip: int + triangle_array_start: int + + def __init__(self, value): + assert (value >> 29) & 0b111 == 0b100, value + + self.number_of_triangles = (value >> 25) & 0b1111 + self.shadow = (value >> 24) & 0b1 + self.skip = (value >> 21) & 0b111 + self.triangle_array_start = (value & 0x1fffff) << 2 + +@dataclass +class quad_array: + number_of_quads: int + shadow: int + skip: int + quad_array_start: int + + def __init__(self, value): + assert (value >> 29) & 0b111 == 0b101, value + + self.number_of_quads = (value >> 25) & 0b1111 + self.shadow = (value >> 24) & 0b1 + self.skip = (value >> 21) & 0b111 + self.quad_array_start = (value & 0x1fffff) << 2 + +@dataclass +class object_pointer_block_link: + end_of_list: int + next_pointer_block: int + + def __init__(self, value): + assert (value >> 29) & 0b111 == 0b111, value + assert (value >> 0) & 0b11 == 0b00, value + + self.end_of_list = (value >> 28) & 1 + self.next_pointer_block = value & 0xffffff + + +def decode_ol_entry(mem, offset): + value, = struct.unpack("> 29) & 0b111 + + if selector == 0b111: + return object_pointer_block_link(value) + elif selector == 0b101: + return quad_array(value) + elif selector == 0b100: + return triangle_array(value) + elif (value >> 31) & 1 == 0: + return triangle_strip(value) + else: + assert False, value diff --git a/region_array.py b/region_array.py new file mode 100644 index 0000000..7fe0bd8 --- /dev/null +++ b/region_array.py @@ -0,0 +1,57 @@ +import struct +from dataclasses import dataclass + +@dataclass +class RegionArrayTile: + last_region: int + z_clear: int + flush_accumulate: int + tile_y_position: int + tile_x_position: int + + def __init__(self, value): + assert (value >> 29) & 1 == 0, value + assert (value >> 14) & 0x3fff == 0, value + assert (value >> 0) & 0b11 == 0, value + self.last_region = (value >> 31) & 0b1 + self.z_clear = (value >> 30) & 0b1 + self.flush_accumulate = (value >> 28) & 0b1 + self.tile_y_position = (value >> 8) & 0b111111 + self.tile_x_position = (value >> 2) & 0b111111 + +@dataclass +class RegionArrayPointer: + empty_ptr: int + pointer_to_object_list: int + + def __init__(self, value): + assert (value >> 24) & 0x7f == 0 + assert (value >> 0) & 0b11 == 0 + self.empty_ptr = (value >> 31) & 1 + self.pointer_to_object_list = value & 0xffffff + +@dataclass +class RegionArrayEntry: + tile: RegionArrayTile + opaque_list_pointer: RegionArrayPointer + opaque_modifier_volume_list_pointer: RegionArrayPointer + translucent_list_pointer: RegionArrayPointer + translucent_modifier_volume_list_pointer: RegionArrayPointer + punch_through_list_pointer: RegionArrayPointer + +def decode_region_array_entry(mem, offset): + next_offset = offset + 6 * 4 + tile, *pointers = struct.unpack("