initial
This commit is contained in:
commit
ded9185216
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
*.bin
|
||||
__pycache__/
|
||||
*.pyc
|
||||
out*.txt
|
230
holly.py
Normal file
230
holly.py
Normal file
@ -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('<I', mem[0x0:0x4])
|
||||
holly.REVISION, = struct.unpack('<I', mem[0x4:0x8])
|
||||
holly.SOFTRESET, = struct.unpack('<I', mem[0x8:0xc])
|
||||
holly.STARTRENDER, = struct.unpack('<I', mem[0x14:0x18])
|
||||
holly.TEST_SELECT, = struct.unpack('<I', mem[0x18:0x1c])
|
||||
holly.PARAM_BASE, = struct.unpack('<I', mem[0x20:0x24])
|
||||
holly.REGION_BASE, = struct.unpack('<I', mem[0x2c:0x30])
|
||||
holly.SPAN_SORT_CFG, = struct.unpack('<I', mem[0x30:0x34])
|
||||
holly.VO_BORDER_COL, = struct.unpack('<I', mem[0x40:0x44])
|
||||
holly.FB_R_CTRL, = struct.unpack('<I', mem[0x44:0x48])
|
||||
holly.FB_W_CTRL, = struct.unpack('<I', mem[0x48:0x4c])
|
||||
holly.FB_W_LINESTRIDE, = struct.unpack('<I', mem[0x4c:0x50])
|
||||
holly.FB_R_SOF1, = struct.unpack('<I', mem[0x50:0x54])
|
||||
holly.FB_R_SOF2, = struct.unpack('<I', mem[0x54:0x58])
|
||||
holly.FB_R_SIZE, = struct.unpack('<I', mem[0x5c:0x60])
|
||||
holly.FB_W_SOF1, = struct.unpack('<I', mem[0x60:0x64])
|
||||
holly.FB_W_SOF2, = struct.unpack('<I', mem[0x64:0x68])
|
||||
holly.FB_X_CLIP, = struct.unpack('<I', mem[0x68:0x6c])
|
||||
holly.FB_Y_CLIP, = struct.unpack('<I', mem[0x6c:0x70])
|
||||
holly.FPU_SHAD_SCALE, = struct.unpack('<I', mem[0x74:0x78])
|
||||
holly.FPU_CULL_VAL, = struct.unpack('<I', mem[0x78:0x7c])
|
||||
holly.FPU_PARAM_CFG, = struct.unpack('<I', mem[0x7c:0x80])
|
||||
holly.HALF_OFFSET, = struct.unpack('<I', mem[0x80:0x84])
|
||||
holly.FPU_PERP_VAL, = struct.unpack('<I', mem[0x84:0x88])
|
||||
holly.ISP_BACKGND_D, = struct.unpack('<I', mem[0x88:0x8c])
|
||||
holly.ISP_BACKGND_T, = struct.unpack('<I', mem[0x8c:0x90])
|
||||
holly.ISP_FEED_CFG, = struct.unpack('<I', mem[0x98:0x9c])
|
||||
holly.SDRAM_REFRESH, = struct.unpack('<I', mem[0xa0:0xa4])
|
||||
holly.SDRAM_ARB_CFG, = struct.unpack('<I', mem[0xa4:0xa8])
|
||||
holly.SDRAM_CFG, = struct.unpack('<I', mem[0xa8:0xac])
|
||||
holly.FOG_COL_RAM, = struct.unpack('<I', mem[0xb0:0xb4])
|
||||
holly.FOG_COL_VERT, = struct.unpack('<I', mem[0xb4:0xb8])
|
||||
holly.FOG_DENSITY, = struct.unpack('<I', mem[0xb8:0xbc])
|
||||
holly.FOG_CLAMP_MAX, = struct.unpack('<I', mem[0xbc:0xc0])
|
||||
holly.FOG_CLAMP_MIN, = struct.unpack('<I', mem[0xc0:0xc4])
|
||||
holly.SPG_TRIGGER_POS, = struct.unpack('<I', mem[0xc4:0xc8])
|
||||
holly.SPG_HBLANK_INT, = struct.unpack('<I', mem[0xc8:0xcc])
|
||||
holly.SPG_VBLANK_INT, = struct.unpack('<I', mem[0xcc:0xd0])
|
||||
holly.SPG_CONTROL, = struct.unpack('<I', mem[0xd0:0xd4])
|
||||
holly.SPG_HBLANK, = struct.unpack('<I', mem[0xd4:0xd8])
|
||||
holly.SPG_LOAD, = struct.unpack('<I', mem[0xd8:0xdc])
|
||||
holly.SPG_VBLANK, = struct.unpack('<I', mem[0xdc:0xe0])
|
||||
holly.SPG_WIDTH, = struct.unpack('<I', mem[0xe0:0xe4])
|
||||
holly.TEXT_CONTROL, = struct.unpack('<I', mem[0xe4:0xe8])
|
||||
holly.VO_CONTROL, = struct.unpack('<I', mem[0xe8:0xec])
|
||||
holly.VO_STARTX, = struct.unpack('<I', mem[0xec:0xf0])
|
||||
holly.VO_STARTY, = struct.unpack('<I', mem[0xf0:0xf4])
|
||||
holly.SCALER_CTL, = struct.unpack('<I', mem[0xf4:0xf8])
|
||||
holly.PAL_RAM_CTRL, = struct.unpack('<I', mem[0x108:0x10c])
|
||||
holly.SPG_STATUS, = struct.unpack('<I', mem[0x10c:0x110])
|
||||
holly.FB_BURSTCTRL, = struct.unpack('<I', mem[0x110:0x114])
|
||||
holly.FB_C_SOF, = struct.unpack('<I', mem[0x114:0x118])
|
||||
holly.Y_COEFF, = struct.unpack('<I', mem[0x118:0x11c])
|
||||
holly.PT_ALPHA_REF, = struct.unpack('<I', mem[0x11c:0x120])
|
||||
holly.TA_OL_BASE, = struct.unpack('<I', mem[0x124:0x128])
|
||||
holly.TA_ISP_BASE, = struct.unpack('<I', mem[0x128:0x12c])
|
||||
holly.TA_OL_LIMIT, = struct.unpack('<I', mem[0x12c:0x130])
|
||||
holly.TA_ISP_LIMIT, = struct.unpack('<I', mem[0x130:0x134])
|
||||
holly.TA_NEXT_OPB, = struct.unpack('<I', mem[0x134:0x138])
|
||||
holly.TA_ITP_CURRENT, = struct.unpack('<I', mem[0x138:0x13c])
|
||||
holly.TA_GLOB_TILE_CLIP, = struct.unpack('<I', mem[0x13c:0x140])
|
||||
holly.TA_ALLOC_CTRL, = struct.unpack('<I', mem[0x140:0x144])
|
||||
holly.TA_LIST_INIT, = struct.unpack('<I', mem[0x144:0x148])
|
||||
holly.TA_YUV_TEX_BASE, = struct.unpack('<I', mem[0x148:0x14c])
|
||||
holly.TA_YUV_TEX_CTRL, = struct.unpack('<I', mem[0x14c:0x150])
|
||||
holly.TA_YUV_TEX_CNT, = struct.unpack('<I', mem[0x150:0x154])
|
||||
holly.TA_LIST_CONT, = struct.unpack('<I', mem[0x160:0x164])
|
||||
holly.TA_NEXT_OPB_INIT, = struct.unpack('<I', mem[0x164:0x168])
|
||||
holly.FOG_TABLE = struct.unpack('<' + ('I' * 128), mem[0x200:0x400])
|
||||
holly.TA_OL_POINTERS = struct.unpack('<' + ('I' * 600), mem[0x600:0xf60])
|
||||
holly.PALETTE_RAM = struct.unpack('<' + ('I' * 1024), mem[0x1000:0x2000])
|
||||
return holly
|
||||
|
47
main.py
Normal file
47
main.py
Normal file
@ -0,0 +1,47 @@
|
||||
from pprint import pprint
|
||||
import sys
|
||||
|
||||
from holly import decode_holly
|
||||
from region_array import decode_region_array_entries
|
||||
from object_list import *
|
||||
|
||||
texture_memory_filename = sys.argv[1]
|
||||
holly_registers_filename = sys.argv[2]
|
||||
|
||||
with open(texture_memory_filename, "rb") as f:
|
||||
texture_memory = memoryview(f.read())
|
||||
|
||||
with open(holly_registers_filename, "rb") as f:
|
||||
holly_registers = memoryview(f.read())
|
||||
|
||||
holly = decode_holly(holly_registers)
|
||||
|
||||
assert (holly.REGION_BASE & 0b11) == 0, holly.REGION_BASE
|
||||
|
||||
region_array_entries = decode_region_array_entries(texture_memory, holly.REGION_BASE)
|
||||
|
||||
for entry in region_array_entries:
|
||||
lists = [
|
||||
("opaque_list_pointer", entry.opaque_list_pointer),
|
||||
("opaque_modifier_volume_list_pointer", entry.opaque_modifier_volume_list_pointer),
|
||||
("translucent_list_pointer", entry.translucent_list_pointer),
|
||||
("translucent_modifier_volume_list_pointer", entry.translucent_modifier_volume_list_pointer),
|
||||
("punch_through_list_pointer", entry.punch_through_list_pointer),
|
||||
]
|
||||
print(entry.tile)
|
||||
for list_type_name, list_pointer in lists:
|
||||
print(" " * 4, end='')
|
||||
print(list_type_name, list_pointer)
|
||||
if list_pointer.empty_ptr:
|
||||
continue
|
||||
offset = list_pointer.pointer_to_object_list
|
||||
while True:
|
||||
ol_entry = decode_ol_entry(texture_memory, offset)
|
||||
print(" " * 8, end='')
|
||||
print(ol_entry)
|
||||
offset += 4
|
||||
if type(ol_entry) == object_pointer_block_link:
|
||||
if ol_entry.end_of_list:
|
||||
break
|
||||
else:
|
||||
offset = ol_entry.next_pointer_block
|
75
object_list.py
Normal file
75
object_list.py
Normal file
@ -0,0 +1,75 @@
|
||||
from dataclasses import dataclass
|
||||
import struct
|
||||
|
||||
@dataclass
|
||||
class triangle_strip:
|
||||
mask: int
|
||||
shadow: int
|
||||
skip: int
|
||||
triangle_strip_start: int
|
||||
|
||||
def __init__(self, value):
|
||||
assert (value >> 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("<I", mem[offset:offset+4])
|
||||
selector = (value >> 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
|
57
region_array.py
Normal file
57
region_array.py
Normal file
@ -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("<IIIIII", mem[offset:next_offset])
|
||||
return next_offset, RegionArrayEntry(
|
||||
RegionArrayTile(tile),
|
||||
*map(RegionArrayPointer, pointers),
|
||||
)
|
||||
|
||||
def decode_region_array_entries(mem, offset):
|
||||
entries = []
|
||||
while True:
|
||||
offset, entry = decode_region_array_entry(mem, offset)
|
||||
entries.append(entry)
|
||||
if entry.tile.last_region:
|
||||
break
|
||||
return entries
|
Loading…
x
Reference in New Issue
Block a user