diff --git a/Cutscenes.py b/Cutscenes.py index 3aa24d8f4..ad3008b54 100644 --- a/Cutscenes.py +++ b/Cutscenes.py @@ -759,3 +759,10 @@ class CutsceneCommandID(IntEnum): CutsceneCommandID.CS_CMD_CAM_EYE, CutsceneCommandID.CS_CMD_CAM_AT, ] + +# z_demo tables +ENTRANCE_CUTSCENE_TABLE_ADDRESS = 0xB65C64 +UNKNOWN_LIST_CUTSCENES = 0xB65D74 # does not include NULL first entry + +# z_demo_kekkai list +SAGE_CUTSCENES = 0xECF8EC diff --git a/FileDataRelocator.py b/FileDataRelocator.py index 3c7ae95b9..11fc6d741 100644 --- a/FileDataRelocator.py +++ b/FileDataRelocator.py @@ -6,6 +6,7 @@ from Rom import Rom from MQ import align4, align8, align16, align_file +from SceneList import RecordType, SCENE_EXTERNAL_REFERENCES, SCENE_TABLE_ADDRESS def segment_address_offset(segment_address: int) -> int: @@ -61,108 +62,6 @@ def __str__(self) -> str: return f'({self.x}, {self.y}, {self.z})' -class RecordType(str, Enum): - # Scene header record types - SceneHeader = 'SceneHeader' - AlternateHeaders = 'AlternateHeaders' - RoomList = 'RoomList' - TransitionActorList = 'TransitionActorList' - CollisionHeader = 'CollisionHeader' - EntranceList = 'EntranceList' - Points = 'Points' - PathList = 'PathList' - SpawnList = 'SpawnList' - ExitList = 'ExitList' - LightSettings = 'LightSettings' - CutsceneData = 'CutsceneData' - - # Collision header record types - Vertices = 'Vertices' - Polys = 'Polys' - SurfaceTypes = 'SurfaceTypes' - Cams = 'Cams' - Waterboxes = 'Waterboxes' - CamPosData = 'CamPosData' - - # Room header record types - RoomHeader = 'RoomHeader' - ObjectList = 'ObjectList' - ActorList = 'ActorList' - - # Mesh header record types - MeshHeader = 'MeshHeader' - MeshHeaderImageSingle = 'MeshHeaderImage' - MeshHeaderImageMulti = 'MeshHeaderImage' - MeshHeaderCullable = 'MeshHeaderCullable' - DlistEntries = 'DlistEntries' - DlistEntry = 'DlistEntry' - DlistCullableEntries = 'DlistCullableEntries' - Dlist = 'Dlist' - Vtx = 'Vtx' - Mtx = 'Mtx' - SetTImg = 'SetTImg' - SetZImg = 'SetZImg' - SetCImg = 'SetCImg' - Backgrounds = 'Backgrounds' - Background = 'Background' - BackgroundImage = 'BackgroundImage' - BackgroundTlut = 'BackgroundTlut' - CullableEntries = 'CullableEntries' - CullableEntry = 'CullableEntry' - Cullable = 'Cullable' - - # Other unparsed data blob record types - Texture = 'Texture' - TextureAnimation = 'TextureAnimation' - Symbol = 'Symbol' - Scalar = 'Scalar' - Vector = 'Vector' - Array = 'Array' - Pointer = 'Pointer' - Blob = 'Blob' - - # Model-related record types - Skeleton = 'Skeleton' - LimbTable = 'LimbTable' - Limb = 'Limb' - Animation = 'Animation' - PlayerAnimation = 'PlayerAnimation' - PlayerAnimationData = 'PlayerAnimationData' - CurveAnimation = 'CurveAnimation' - LegacyAnimation = 'LegacyAnimation' - - # Unknown data not referenced in scene and room files - Unknown = 'Unknown' - - # Record types referenced by header commands without count - def has_unknown_count(self) -> bool: - if self == RecordType.CollisionHeader: # 0x03 - return True - if self == RecordType.EntranceList: # 0x06 - return True - if self == RecordType.MeshHeader: # 0x0A - return True - if self == RecordType.PathList: # 0x0D - return True - if self == RecordType.ExitList: # 0x13 - return True - if self == RecordType.CutsceneData: # 0x17 - return True - if self == RecordType.AlternateHeaders: # 0x18 - return True - if self == RecordType.SurfaceTypes: - return True - if self == RecordType.Cams: - return True - if self == RecordType.SetTImg: - return True - if self == RecordType.SetZImg: - return True - if self == RecordType.SetCImg: - return True - return False - - # File type values correspond to segment number from the segment address table # https://wiki.cloudmodding.com/oot/Addresses#Segment_Addresses # ----------------------------------- @@ -197,6 +96,7 @@ def __init__(self, file: FileDataRelocator, type: RecordType, start: int, offset self.type: RecordType = type self.start: int = start self.offset: int = offset + self.vanilla_offset: int = offset self.length: int = length self.delay_parsing: bool = delay_parsing self.align: int = 4 @@ -289,6 +189,7 @@ def __init__(self, rom: Rom, name: str, start: int, end: int, type: FileType) -> self.name: str = name self.start: int = start self.end: int = end + self.rom_start: int = start self.type: FileType = type self.parsed: bool = False @@ -458,6 +359,15 @@ def get_existing_record_by_offset(self, offset: int, record_type: RecordType) -> f'Existing {existing_record.type.value} at 0x{existing_record.offset:08X} does not match requested type {record_type} in {self.name}') return existing_record + # Return the existing data record matching the given file offset or None + def get_existing_record_by_vanilla_offset(self, offset: int, record_type: RecordType) -> Optional[DataRecord]: + existing_record: Optional[DataRecord] = next( + (x for x in self.data_records if x.vanilla_offset == offset), None) + if existing_record is not None and existing_record.type != record_type: + raise Exception( + f'Existing {existing_record.type.value} at 0x{existing_record.vanilla_offset:08X} does not match requested type {record_type} in {self.name}') + return existing_record + def encode(self) -> bytearray: # Resize data records. Assumes records are sorted by offset. # Don't build full encode at first as pointers may shift. @@ -505,20 +415,52 @@ def encode(self) -> bytearray: bytes.extend(record_bytes) return bytes - def write(self, rom: Rom, start_address: Optional[int] = None) -> int: + # Two-stage process to write to rom. + # Start/end of all files must be determined prior to + # final encode as other files may reference this file's + # location, such as scene files referencing their room files. + def update_start_and_end(self, start_address: Optional[int] = None) -> int: raw_file = self.encode() if start_address is None: new_start = align_file(self.start) else: new_start = align_file(start_address) + new_end = new_start + len(raw_file) + self.start = new_start + self.end = new_end + return align_file(new_end) + + def write(self, rom: Rom) -> int: + raw_file = self.encode() + if self.name in SCENE_EXTERNAL_REFERENCES.keys(): + for record_type, offset, data_references, code_references in SCENE_EXTERNAL_REFERENCES[self.name]: + record = self.get_existing_record_by_vanilla_offset(offset, record_type) + if record is None: + raise Exception(f'Offset {offset:0>6x} does not match any records in {self.name}') + record_address = record.get_segment_address_bytes() + for external_pointer_address in data_references: + self.write_external_data_pointer(rom, record_address, external_pointer_address) + for external_pointer_addresses in code_references: + self.write_external_code_pointer(rom, record_address, external_pointer_addresses) file_length = len(raw_file) - while file_length < align16(file_length): + while file_length < align_file(file_length): raw_file.extend(int.to_bytes(0, 1, 'big')) file_length += 1 - rom.write_bytes(new_start, raw_file) - new_end = new_start + len(raw_file) - rom.update_dmadata_record_by_key(self.start, new_start, new_end) - return align_file(new_end) + rom.write_bytes(self.start, raw_file) + rom.update_dmadata_record_by_key(self.rom_start, self.start, self.end) + self.rom_start = self.start + return align_file(self.end) + + def write_external_data_pointer(self, rom: Rom, record_address: bytearray, external_pointer_address: int) -> None: + rom.write_bytes(external_pointer_address, record_address) + + def write_external_code_pointer(self, rom: Rom, record_address: bytearray, external_pointer_addresses: tuple[int, int]) -> None: + address = int.from_bytes(record_address) + address_low = address & 0xFFFF + address_high = (address >> 16) + (1 if address_low > 0x7FFF else 0) + external_high, external_low = external_pointer_addresses + rom.write_bytes(external_high, address_high.to_bytes(2, 'big')) + rom.write_bytes(external_low, address_low.to_bytes(2, 'big')) # Return the file data as a serializable dict diff --git a/FileList.py b/FileList.py index 20d152a14..3b608cc3b 100644 --- a/FileList.py +++ b/FileList.py @@ -1,7 +1,7 @@ # Keyed on filename # Values: file number, VROM start, VROM end -scene_and_room_files = { +SCENE_AND_ROOM_FILES = { "ddan_scene": (0x03EF, 0x01F12000, 0x01F27140), "ddan_room_0": (0x03F0, 0x01F28000, 0x01F438A0), "ddan_room_1": (0x03F1, 0x01F44000, 0x01F4DB00), diff --git a/MQ.py b/MQ.py index 90101223b..ddec28828 100644 --- a/MQ.py +++ b/MQ.py @@ -554,7 +554,7 @@ def align16(value: int) -> int: def align_file(value: int) -> int: - return ((value + 0xFFF) // 0x1000) * 0x1000 + return align16(value) # This function inserts space in a ovl section at the section's offset # The section size is expanded diff --git a/Notes/scene-and-room-patching.md b/Notes/scene-and-room-patching.md index f61ca2de8..ba0f336c4 100644 --- a/Notes/scene-and-room-patching.md +++ b/Notes/scene-and-room-patching.md @@ -549,3 +549,462 @@ Null pointers does not appear to be in any 1.0 assets, MM only? total size = 0x08 total size = 0x08 + 0x08 * entries + +Externally referenced cutscenes: + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/dungeons/bdan.xml + 3,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/dungeons/ddan.xml + 16,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/dungeons/ganontika.xml + 3,10: + 4,10: + 5,10: + 6,10: + 7,10: + 8,10: + 10,10: + 12,10: + 13,10: + 14,10: + 15,10: + 16,10: + 17,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/dungeons/ice_doukutu.xml + 3,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/dungeons/jyasinboss.xml + 3,10: + 4,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/dungeons/ydan.xml + 3,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/indoors/daiyousei_izumi.xml + 3,10: + 4,10: + 5,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/indoors/hakasitarelay.xml + 4,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/indoors/nakaniwa.xml + 3,10: + 4,10: + 5,10: + 6,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/indoors/tokinoma.xml + 3,10: + 4,10: + 5,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/indoors/yousei_izumi_yoko.xml + 3,10: + 4,10: + 5,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/misc/hakaana_ouke.xml + 3,10: + 4,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/overworld/ganon_tou.xml + 3,10: + 4,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/overworld/spot00.xml + 3,10: + 4,10: + 5,10: + 6,10: + 7,10: + 8,10: + 9,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/overworld/spot01.xml + 4,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/overworld/spot02.xml + 5,10: + 7,10: + 8,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/overworld/spot04.xml + 5,10: + 6,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/overworld/spot05.xml + 3,10: + 5,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/overworld/spot06.xml + 3,10: + 4,10: + 6,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/overworld/spot07.xml + 4,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/overworld/spot08.xml + 3,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/overworld/spot09.xml + 3,10: + 4,10: + 5,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/overworld/spot11.xml + 3,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/overworld/spot12.xml + 3,10: + 4,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/overworld/spot15.xml + 3,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/overworld/spot16.xml + 3,10: + 4,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/overworld/spot17.xml + 3,10: + 4,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/overworld/spot18.xml + 3,10: + 4,10: + 5,10: + 6,10: + + /Users/mracsys/git/OoT-Randomizer-Fork/data/scenes/overworld/spot20.xml + 3,10: + + +Addresses: +```python +# z_demo tables +ENTRANCE_CUTSCENE_TABLE_ADDRESS = 0xB65C64 +UNKNOWN_LIST_CUTSCENES = 0xB65D74 # does not include NULL first entry + +# z_demo_kekkai list +SAGE_CUTSCENES = 0xECF8EC + +external_cutscene_pointer_locations = { + # z_demo + 'bdan': [ + (1, [ # gJabuJabuIntroCs + ENTRANCE_CUTSCENE_TABLE_ADDRESS + (17 * 0x8) + 0x4, + UNKNOWN_LIST_CUTSCENES + 1 * 0x4, + ], [], []), + ], + # z_demo, z_bg_breakwall + 'ddan': [ + (0, [ # gDcOpeningCs + UNKNOWN_LIST_CUTSCENES + 2 * 0x4, + ], [ + 0xC9EDAE # lui a1, 0x0201 + ], [ + 0xC9EDB2 # addiu a1, a1, 0x4F80 + ]), + ], + # z_demo, z_demo_kekkai + 'ganontika': [ + (0, [SAGE_CUTSCENES + 6 * 0x4], [], []), # gForestTrialSageCs + (1, [SAGE_CUTSCENES + 1 * 0x4], [], []), # gWaterTrialSageCs + (2, [SAGE_CUTSCENES + 4 * 0x4], [], []), # gShadowTrialSageCs + (3, [SAGE_CUTSCENES + 3 * 0x4], [], []), # gFireTrialSageCs + (4, [SAGE_CUTSCENES + 2 * 0x4], [], []), # gLightTrialSageCs + (5, [SAGE_CUTSCENES + 5 * 0x4], [], []), # gSpiritTrialSageCs + (6, [ # gTowerBarrierCs + UNKNOWN_LIST_CUTSCENES + 5 * 0x4, + ], [ + 0xACA97E # lui v0, 0x0202 + ], [ + 0xACA982 # addiu v0, v0, 0xDA40 + ]), + (7, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (28 * 0x8) + 0x4], [], []), # gLightBarrierCs + (8, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (27 * 0x8) + 0x4], [], []), # gFireBarrierCs + (9, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (24 * 0x8) + 0x4], [], []), # gForestBarrierCs + (10, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (29 * 0x8) + 0x4], [], []), # gSpiritBarrierCs + (11, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (25 * 0x8) + 0x4], [], []), # gWaterBarrierCs + (12, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (26 * 0x8) + 0x4], [], []), # gShadowBarrierCs + ], + # z_demo, z_en_xc + 'ice_doukutu': [ + (0, [ # gIceCavernSerenadeCs + UNKNOWN_LIST_CUTSCENES + 4 * 0x4, + ], [ + 0xC7BD46 # lui a1, 0x0200 + ], [ + 0xC7BD4E # addiu a1, a1, 0x0250 + ]), + ], + # z_demo, z_en_ik + 'jyasinboss': [ + (0, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (30 * 0x8) + 0x4], [], []), # gSpiritBossNabooruKnuckleIntroCs + (1, [ # gSpiritBossNabooruKnuckleDefeatCs + UNKNOWN_LIST_CUTSCENES + 3 * 0x4, + ], [ + 0xDED5C2 # lui a1, 0x0200 + ], [ + 0xDED5D6 # addiu a1, a1, 0x3F80 + ]), + ], + # z_demo, z_bg_breakwall + 'ydan': [ + (1, [ # gDekuTreeIntroCs + ENTRANCE_CUTSCENE_TABLE_ADDRESS + (7 * 0x8) + 0x4, + UNKNOWN_LIST_CUTSCENES + 0 * 0x4, + ], [], []), + ], + # z_bg_dy_yoseizo + 'daiyousei_izumi': [ + (0, [], [ # gGreatFairyMagicCs + 0xC89A0A # lui v0, 0x0200 + ], [ + 0xC89A0E # addiu v0, v0, 0x0130 + ]), + (1, [], [ # gGreatFairyDoubleMagicCs + 0xC89A4E # lui v0, 0x0200 + ], [ + 0xC89A52 # addiu v0, v0, 0x13E0 + ]), + (2, [], [ # gGreatFairyDoubleDefenseCs + 0xC89A92 # lui v0, 0x0200 + ], [ + 0xC89A96 # addiu v0, v0, 0x25D0 + ]), + ], + # z_en_fu + 'hakasitarelay': [ + (0, [], [ # gSongOfStormsCs + 0xE42A4E # lui v0, 0x0201 + ], [ + 0xE42A5A # addiu v0, v0, 0xE080 + ]), + ], + # z_en_zl4, z_demo_im + 'nakaniwa': [ + (0, [], [ # gZeldasCourtyardGanonCs + 0xEFE0FA # lui v0, 0x0200 + ], [ + 0xEFE0FE # addiu v0, v0, 0x0104 + ]), + (1, [], [ # gZeldasCourtyardWindowCs + 0xEFE09A # lui v0, 0x0200 + ], [ + 0xEFE09E # addiu v0, v0, 0x0444 + ]), + (3, [], [ # gZeldasCourtyardLullabyCs + 0xD12E72 # lui v0, 0x0200 + ], [ + 0xD12E76 # addiu v0, v0, 0x2524 + ]), + (5, [], [ # gZeldasCourtyardMeetCs + 0xEFCEF2 # lui v0, 0x0200 + ], [ + 0xEFCEF6 # addiu v0, v0, 0x3994 + ]), + ], + # z_demo, z_en_xc + 'tokinoma': [ + (4, [], [ # gTempleOfTimeFirstAdultCs + 0xC8056E # lui v0, 0x0200 + ], [ + 0xC80572 # addiu v0, v0, 0x46F0 + ]), + (6, [], [ # gTempleOfTimePreludeCs + 0xC8060E # lui v0, 0x0200 + ], [ + 0xC80612 # addiu v0, v0, 0x13E0 + ]), + (11, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (6 * 0x8) + 0x4], [], []), # gTempleOfTimeIntroCs + ], + # z_bg_dy_yoseizo + 'yousei_izumi_yoko': [ + (0, [], [ # gGreatFairyFaroresWindCs + 0xC8991A # lui v0, 0x0200 + ], [ + 0xC8991E # addiu v0, v0, 0x0160 + ]), + (1, [], [ # gGreatFairyDinsFireCs + 0xC8995E # lui v0, 0x0200 + ], [ + 0xC89962 # addiu v0, v0, 0x1020 + ]), + (2, [], [ # gGreatFairyNayrusLoveCs + 0xC899A2 # lui v0, 0x0200 + ], [ + 0xC899A6 # addiu v0, v0, 0x1F40 + ]), + ], + # z_demo, z_en_okarina_tag + 'hakaana_ouke': [ + (0, [], [ # gSunSongGraveSunSongTeachCs + 0xE09F6E # lui v0, 0x0200 + ], [ + 0xE09F72 # addiu v0, v0, 0x24A0 + ]), + (1, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (23 * 0x8) + 0x4], [], []), # gSunSongGraveSunSongTeachPart2Cs + ], + # z_demo, z_bg_giyo_bridge + 'ganon_tou': [ + (0, [], [ # gRainbowBridgeCs + 0xE2B502 # lui v0, 0x0200 + ], [ + 0xE2B506 # addiu v0, v0, 0x2640 + ]), + (1, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (22 * 0x8) + 0x4], [], []), # gGanonsCastleIntroCs + ], + # z_demo, z_item_ocarina + 'spot00': [ + (3, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (12 * 0x8) + 0x4], [], []), # gHyruleFieldGetOoTCs + (9, [], [ # gHyruleFieldZeldaSongOfTimeCs + 0xDB529A # lui v0, 0x0201 + ], [ + 0xDB529E # addiu v0, v0, 0xF870 + ]), + (11, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (9 * 0x8) + 0x4], [], []), # gHyruleFieldEastEponaJumpCs + (15, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (0 * 0x8) + 0x4], [], []), # gHyruleFieldIntroCs + (10, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (8 * 0x8) + 0x4], [], []), # gHyruleFieldSouthEponaJumpCs + (12, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (10 * 0x8) + 0x4], [], []), # gHyruleFieldWestEponaJumpCs + (13, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (11 * 0x8) + 0x4], [], []), # gHyruleFieldGateEponaJumpCs + ], + # z_demo + 'spot01': [ + (5, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (2 * 0x8) + 0x4], [], []), # gKakarikoVillageIntroCs + ], + # z_demo, z_en_okarina_tag + 'spot02': [ + (0, [], [ # spot02_scene_Cs_003C80 + 0xE09D42 # lui v0, 0x0200 + ], [ + 0xE09D46 # addiu v0, v0, 0x3C80 + ]), + (2, [], [ # spot02_scene_Cs_005020 + 0xE09D32 # lui v0, 0x0200 + ], [ + 0xE09D36 # addiu v0, v0, 0x5020 + ]), + (3, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (18 * 0x8) + 0x4], [], []), # gGraveyardIntroCs + ], + # z_demo, z_en_sa + 'spot04': [ + (9, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (33 * 0x8) + 0x4], [], []), # gKokiriForestDekuSproutCs + (10, [], [ # gSpot04Cs_10E20 + 0xE297AE # lui v0, 0x0201 + ], [ + 0xE297B6 # addiu v0, v0, 0x0E20 + ]), + ], + # z_en_xc, z_en_sa + 'spot05': [ + (0, [], [ # gMinuetCs + 0xC7BA4A # lui v0, 0x0200 + ], [ + 0xC7BA4E # addiu v0, v0, 0x3F80 + ]), + (1, [], [ # spot05_scene_Cs_005730 + 0xE29D5E # lui v0, 0x0200 + ], [ + 0xE29D62 # addiu v0, v0, 0x5730 + ]), + ], + # z_demo, z_shot_sun, z_en_owl + 'spot06': [ + (2, [], [ # gLakeHyliaFireArrowsCS + 0xE9E222 # lui v1, 0x0200 + ], [ + 0xE9E226 # addiu v1, v1, 0x7020 + ]), + (3, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (13 * 0x8) + 0x4], [], []), # gLakeHyliaIntroCs + (4, [], [ # gLakeHyliaOwlCs + 0xE31302 # lui v0, 0x0202 + ], [ + 0xE31306 # addiu v0, v0, 0xB0C0 + ]), + ], + # z_demo + 'spot07': [ + (1, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (3 * 0x8) + 0x4], [], []), # gZorasDomainIntroCs + ], + # z_demo + 'spot08': [ + (3, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (19 * 0x8) + 0x4], [], []), # gZorasFountainIntroCs + ], + # z_demo, z_en_horse + 'spot09': [ + (4, [], [ # gGerudoValleyBridgeJumpFieldFortressCs + 0xC1CCC6 # lui v0, 0x0200 + ], [ + 0xC1CCCA # addiu v0, v0, 0x2AC0 + ]), + (0, [], [ # gGerudoValleyBridgeJumpFortressToFieldCs + 0xC1CD12 # lui v0, 0x0200 + ], [ + 0xC1CD16 # addiu v0, v0, 0x0230 + ]), + (5, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (14 * 0x8) + 0x4], [], []), # gGerudoValleyIntroCs + ], + # z_demo + 'spot11': [ + (2, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (20 * 0x8) + 0x4], [], []), # gDesertColossusIntroCs + ], + # z_demo + 'spot12': [ + (2, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (31 * 0x8) + 0x4], [], []), # gGerudoFortressFirstCaptureCs + (3, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (15 * 0x8) + 0x4], [], []), # gGerudoFortressIntroCs + ], + # z_demo + 'spot15': [ + (0, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (4 * 0x8) + 0x4], [], []), # gHyruleCastleIntroCs + ], + # z_demo, z_en_owl + 'spot16': [ + (6, [], [ # gDMTOwlCs + 0xE31342 # lui v0, 0x0202 + ], [ + 0xE31346 # addiu v0, v0, 0xE6A0 + ]), + (5, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (1 * 0x8) + 0x4], [], []), # gDMTIntroCs + ], + # z_demo, z_en_xc + 'spot17': [ + (0, [], [ # gDeathMountainCraterBoleroCs + 0xC7BBFE # lui v0, 0x0200 + ], [ + 0xC7BC02 # addiu v0, v0, 0x45D0 + ]), + (2, [ # gDeathMountainCraterIntroCs + ENTRANCE_CUTSCENE_TABLE_ADDRESS + (21 * 0x8) + 0x4, + ENTRANCE_CUTSCENE_TABLE_ADDRESS + (32 * 0x8) + 0x4, + ], [], []), + ], + # z_demo, z_en_du + 'spot18': [ + (0, [], [ # gGoronCityDaruniaCorrectCs + 0xCF14DA # lui v0, 0x0200 + ], [ + 0xCF14DE # addiu v0, v0, 0x59E0 + ]), + (1, [], [ # gGoronCityDarunia01Cs + 0xCF11C6 # lui v0, 0x0200 + ], [ + 0xCF11D6 # addiu v0, v0, 0x6930 + ]), + (3, [], [ # gGoronCityDaruniaWrongCs + 0xCF1436 # lui v0, 0x0200 + ], [ + 0xCF143A # addiu v0, v0, 0x7DE0 + ]), + (4, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (21 * 0x8) + 0x4,], [], []), # gGoronCityIntroCs + ], + # z_demo + 'spot20': [ + (8, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (16 * 0x8) + 0x4], [], []), # gLonLonRanchIntroCs + ], +} +``` diff --git a/Scene.py b/Scene.py index ee492bd76..5797ab4ae 100644 --- a/Scene.py +++ b/Scene.py @@ -4,13 +4,12 @@ from typing import Any, Optional import xml.etree.ElementTree as ET -from FileDataRelocator import Vec3s, segment_address_segment, segment_address_offset, create_segment_address, DataRecord, FileDataRelocator, FileType, RecordType -from MQ import align4 +from FileDataRelocator import Vec3s, segment_address_offset, create_segment_address, DataRecord, FileDataRelocator, FileType from Rom import Rom from Utils import data_path -from SceneList import scene_table +from SceneList import SCENE_TABLE, RecordType, SCENE_EXTERNAL_REFERENCES, SCENE_TABLE_ADDRESS from Cutscenes import CutsceneCommandID, ACTOR_CUE_COMMANDS, GENERIC_COMMANDS, CAMERA_COMMANDS, NULL_COMMANDS -from FileList import scene_and_room_files +from FileList import SCENE_AND_ROOM_FILES class SceneFileAddressException(Exception): def __init__(self, file: FileDataRelocator, segment: int, cursor: int, resource_name: str) -> None: @@ -26,6 +25,15 @@ class SceneDataRelocator(FileDataRelocator): def __init__(self, rom: Rom, name: str, start: int, end: int) -> None: self.rooms: list[RoomDataRelocator] = [] self.headers: list[Optional[SceneHeader]] = [None] + self.id: int = -1 + self.description: str = '' + for scene_id, scene_name, scene_description, _, _, _, _, _, _, _, _ in SCENE_TABLE.values(): + if scene_name == name: + self.id = scene_id + self.description = scene_description + break + if scene_id == -1: + raise Exception(f'Could not locate scene file {name} in vanilla scene table') super().__init__(rom, name, start, end, FileType.Scene) def parse_file_header(self, alternate: Optional[int] = None) -> DataRecord: @@ -41,6 +49,15 @@ def get_offset(self, cursor: int) -> tuple[int, Optional[FileDataRelocator]]: return (offset, self) # scene return (-1, None) # unknown + def write(self, rom: Rom) -> int: + aligned_file_end = super().write(rom) + addresses = bytearray() + addresses.extend(self.start.to_bytes(4, 'big')) + addresses.extend(self.end.to_bytes(4, 'big')) + rom.write_bytes(SCENE_TABLE_ADDRESS + self.id * 0x14, addresses) + return aligned_file_end + + def to_json(self) -> dict[str, Any]: return { **super().to_json(), @@ -2102,8 +2119,6 @@ def scene_resource_factory(file: FileDataRelocator, offset: int, type: str, attr else: raise Exception(f'Unrecognized resource type when parsing scenes: {type}') -SCENE_TABLE_ADDRESS = 0x00B71440 - def parse_scene_data(rom: Rom) -> list[SceneDataRelocator]: xml_dir = data_path('scenes') # XML files may not be read in the same order as the scene IDs @@ -2118,7 +2133,7 @@ def parse_scene_data(rom: Rom) -> list[SceneDataRelocator]: segment = int(file.attrib['Segment']) current_file: FileDataRelocator = None if segment == 0x02: - ids = list(filter(lambda row: row[1] == filename, scene_table.values())) + ids = list(filter(lambda row: row[1] == filename, SCENE_TABLE.values())) if len(ids) == 0: raise Exception(f'Unknown filename from ZAPD XML files: {filename}') if len(ids) > 1: @@ -2160,6 +2175,40 @@ def parse_scene_data(rom: Rom) -> list[SceneDataRelocator]: return scenes +def check_references_for_file(rom: Rom, file: FileDataRelocator) -> int: + checked_refs = 0 + if file.name in SCENE_EXTERNAL_REFERENCES.keys(): + for record_type, offset, data_references, code_references in SCENE_EXTERNAL_REFERENCES[file.name]: + record = file.get_existing_record_by_offset(offset, record_type) + if record is None: + raise Exception(f'Offset {offset:0>6x} does not match any records in {file.name}') + address = create_segment_address(file.type.value, offset) + address_low = address & 0xFFFF + address_high = (address >> 16) + (1 if address_low > 0x7FFF else 0) + for data_address in data_references: + if rom.read_int32(data_address) != address: + raise Exception(f'Data reference address {data_address:0>8x} does not match record address {address:0>8x} for {file.name}') + checked_refs += 1 + for high_ref, low_ref in code_references: + if rom.read_int16(high_ref) != address_high: + raise Exception(f'Data reference address {high_ref:0>8x} does not match record address top half {address_high:0>4x} for {file.name}') + checked_refs += 1 + if rom.read_int16(low_ref) != address_low: + raise Exception(f'Data reference address {low_ref:0>8x} does not match record address bottom half {address_low:0>4x} for {file.name}') + checked_refs += 1 + return checked_refs + + +def check_external_reference_locations(rom: Rom): + scene_list = parse_scene_data(rom) + checked_refs = 0 + for scene in scene_list: + checked_refs += check_references_for_file(rom, scene) + for room in scene.rooms: + checked_refs += check_references_for_file(rom, room) + print (f'Checked {checked_refs} rom addresses. All passed :)') + + def compare_parsed_data_to_rom(rom: Rom, save_files: bool = False): scene_list = parse_scene_data(rom) print('Done parsing') @@ -2168,7 +2217,7 @@ def compare_parsed_data_to_rom(rom: Rom, save_files: bool = False): if save_files: with open('scene_out', 'wb') as f: f.write(scene_bytes) - _, vrom_start, vrom_end = scene_and_room_files[scene.name] + _, vrom_start, vrom_end = SCENE_AND_ROOM_FILES[scene.name] rom_scene = rom.read_bytes(vrom_start, vrom_end - vrom_start) if len(rom_scene) != len(scene_bytes): print(f'Length mismatch for {scene.name}. Vanilla: {len(rom_scene):0>8x} Encode: {len(scene_bytes):0>8x}') @@ -2184,7 +2233,7 @@ def compare_parsed_data_to_rom(rom: Rom, save_files: bool = False): if save_files: with open('room_out', 'wb') as f: f.write(room_bytes) - _, vrom_start, vrom_end = scene_and_room_files[room.name] + _, vrom_start, vrom_end = SCENE_AND_ROOM_FILES[room.name] rom_room = rom.read_bytes(vrom_start, vrom_end - vrom_start) if len(rom_room) != len(room_bytes): print(f'Length mismatch for {room.name}. Vanilla: {len(rom_room):0>8x} Encode: {len(room_bytes):0>8x}') @@ -2215,8 +2264,16 @@ def write_scenes_to_rom(rom: Rom, scenes: list[SceneDataRelocator]) -> None: files = [scene for scene in scenes] files.extend([room for scene in scenes for room in scene.rooms]) files.sort(key=lambda f: f.start) + files_end: int = max([file.end for file in files]) for file in files: - file_start = file.write(rom, file_start) + file_start = file.update_start_and_end(file_start) + new_files_end: int = max([file.end for file in files]) + for file in files: + file.write(rom) + if files_end > new_files_end: + print(f'Wrote scene files to rom. You saved 0x{files_end - new_files_end:x} bytes! Hooray! 🎉') + else: + print(f'Wrote scene files to rom. You used 0x{new_files_end - files_end:x} additional bytes! 🫤') def make_kak_skulltulas_ignore_tod(rom: Rom): diff --git a/SceneList.py b/SceneList.py index 99a8b9956..3c4040302 100644 --- a/SceneList.py +++ b/SceneList.py @@ -1,110 +1,444 @@ +from enum import Enum + +SCENE_TABLE_ADDRESS = 0x00B71440 + # Duplicate of vanilla scene table for non-debug ROM, # plus additional metadata used for parsing scene files. # Indexed on scene ID # -# | | | | VROM | VROM (Title Card) | | | | | Alt | -# | ID | Filename | Description | Start | End | Start | End | ? | Rn | # | * | Hdr | -# | | | | | | | | | | | | Cnt | -scene_table = { -0x0000: (0x0000, "ydan_scene", "Inside the Deku Tree", 0x02499000, 0x024A6A10, 0x01994000, 0x01995B00, 0x01, 0x13, 0x02, 0x00, 0 ), -0x0001: (0x0001, "ddan_scene", "Dodongo's Cavern", 0x01F12000, 0x01F27140, 0x01998000, 0x01999B00, 0x01, 0x14, 0x03, 0x00, 0 ), -0x0002: (0x0002, "bdan_scene", "Inside Jabu-Jabu's Belly", 0x0273E000, 0x027537C0, 0x01996000, 0x01997B00, 0x01, 0x15, 0x04, 0x00, 4 ), -0x0003: (0x0003, "Bmori1_scene", "Forest Temple", 0x023CF000, 0x023E4F90, 0x0198A000, 0x0198BB00, 0x02, 0x16, 0x05, 0x00, 0 ), -0x0004: (0x0004, "HIDAN_scene", "Fire Temple", 0x022D8000, 0x022F2970, 0x0198E000, 0x0198FB00, 0x02, 0x12, 0x06, 0x00, 0 ), -0x0005: (0x0005, "MIZUsin_scene", "Water Temple", 0x025B8000, 0x025CDCF0, 0x01990000, 0x01991B00, 0x01, 0x17, 0x07, 0x00, 0 ), -0x0006: (0x0006, "jyasinzou_scene", "Spirit Temple", 0x02ADE000, 0x02AF7B40, 0x01992000, 0x01993B00, 0x01, 0x19, 0x08, 0x00, 0 ), -0x0007: (0x0007, "HAKAdan_scene", "Shadow Temple", 0x027A7000, 0x027BF3C0, 0x0198C000, 0x0198DB00, 0x02, 0x18, 0x09, 0x00, 0 ), -0x0008: (0x0008, "HAKAdanCH_scene", "Bottom of the Well", 0x032C6000, 0x032D2560, 0x019F4000, 0x019F5B00, 0x02, 0x18, 0x0A, 0x00, 0 ), -0x0009: (0x0009, "ice_doukutu_scene", "Ice Cavern", 0x02BEB000, 0x02BFC610, 0x0199C000, 0x0199DB00, 0x00, 0x25, 0x00, 0x00, 4 ), -0x000A: (0x000A, "ganon_scene", "Ganon's Tower", 0x02EE3000, 0x02EF37B0, 0x00000000, 0x00000000, 0x02, 0x00, 0x00, 0x00, 0 ), -0x000B: (0x000B, "men_scene", "Gerudo Training Ground", 0x02875000, 0x02886530, 0x0199E000, 0x0199FB00, 0x00, 0x1B, 0x00, 0x00, 0 ), -0x000C: (0x000C, "gerudoway_scene", "Thieves' Hideout", 0x03292000, 0x0329F920, 0x019EA000, 0x019EBB00, 0x00, 0x28, 0x00, 0x00, 0 ), -0x000D: (0x000D, "ganontika_scene", "Inside Ganon's Castle", 0x0318E000, 0x031AF7C0, 0x0199A000, 0x0199BB00, 0x00, 0x1A, 0x00, 0x00, 0 ), -0x000E: (0x000E, "ganon_sonogo_scene", "Ganon's Tower (Collapsing)", 0x033CA000, 0x033D1F10, 0x00000000, 0x00000000, 0x00, 0x33, 0x00, 0x00, 0 ), -0x000F: (0x000F, "ganontikasonogo_scene", "Inside Ganon's Castle (Collapsing)", 0x0344A000, 0x0344EB00, 0x00000000, 0x00000000, 0x00, 0x34, 0x00, 0x00, 0 ), -0x0010: (0x0010, "takaraya_scene", "Treasure Box Shop", 0x033A6000, 0x033AD1B0, 0x019EE000, 0x019EFB00, 0x00, 0x00, 0x00, 0x00, 0 ), -0x0011: (0x0011, "ydan_boss_scene", "Gohma's Lair", 0x02EB4000, 0x02EB5740, 0x00000000, 0x00000000, 0x00, 0x1C, 0x00, 0x00, 0 ), -0x0012: (0x0012, "ddan_boss_scene", "King Dodongo's Lair", 0x02EA8000, 0x02EAA860, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00, 0 ), -0x0013: (0x0013, "bdan_boss_scene", "Barinade's Lair", 0x02CB0000, 0x02CB0E40, 0x00000000, 0x00000000, 0x00, 0x15, 0x00, 0x00, 0 ), -0x0014: (0x0014, "moribossroom_scene", "Phantom Ganon's Lair", 0x0284A000, 0x0284B900, 0x00000000, 0x00000000, 0x01, 0x00, 0x00, 0x00, 0 ), -0x0015: (0x0015, "FIRE_bs_scene", "Volvagia's Lair", 0x02CBB000, 0x02CBFC00, 0x00000000, 0x00000000, 0x00, 0x12, 0x00, 0x00, 0 ), -0x0016: (0x0016, "MIZUsin_bs_scene", "Morpha's Lair", 0x02ED0000, 0x02ED1A60, 0x00000000, 0x00000000, 0x00, 0x1D, 0x00, 0x00, 0 ), -0x0017: (0x0017, "jyasinboss_scene", "Twinrova's Lair & Nabooru's Mini-Boss Room", 0x02F57000, 0x02F5FCF0, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00, 6 ), -0x0018: (0x0018, "HAKAdan_bs_scene", "Bongo Bongo's Lair", 0x02EC4000, 0x02EC6780, 0x00000000, 0x00000000, 0x00, 0x18, 0x00, 0x00, 0 ), -0x0019: (0x0019, "ganon_boss_scene", "Ganondorf's Lair", 0x02F49000, 0x02F50C20, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00, 0 ), -0x001A: (0x001A, "ganon_final_scene", "Tower Collapse Exterior", 0x02FE7000, 0x02FF9180, 0x00000000, 0x00000000, 0x00, 0x26, 0x00, 0x00, 0 ), -0x001B: (0x001B, "entra_scene", "Market Entrance (Child - Day)", 0x02717000, 0x027173E0, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00, 0 ), -0x001C: (0x001C, "entra_n_scene", "Market Entrance (Child - Night)", 0x029DA000, 0x029DA430, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00, 0 ), -0x001D: (0x001D, "enrui_scene", "Market Entrance (Ruins)", 0x02A01000, 0x02A013E0, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00, 0 ), -0x001E: (0x001E, "market_alley_scene", "Back Alley (Child - Day)", 0x02944000, 0x02945250, 0x019AC000, 0x019ADB00, 0x00, 0x00, 0x00, 0x00, 0 ), -0x001F: (0x001F, "market_alley_n_scene", "Back Alley (Child - Night)", 0x02A28000, 0x02A292F0, 0x019AC000, 0x019ADB00, 0x00, 0x00, 0x00, 0x00, 0 ), -0x0020: (0x0020, "market_day_scene", "Market (Child - Day)", 0x022C6000, 0x022C8670, 0x019AA000, 0x019ABB00, 0x00, 0x00, 0x00, 0x00, 0 ), -0x0021: (0x0021, "market_night_scene", "Market (Child - Night)", 0x022CF000, 0x022D1630, 0x019AA000, 0x019ABB00, 0x00, 0x00, 0x00, 0x00, 0 ), -0x0022: (0x0022, "market_ruins_scene", "Market (Ruins)", 0x029D5000, 0x029D6630, 0x019AA000, 0x019ABB00, 0x00, 0x00, 0x00, 0x00, 0 ), -0x0023: (0x0023, "shrine_scene", "Temple of Time Exterior (Child - Day)", 0x03075000, 0x030764E0, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00, 0 ), -0x0024: (0x0024, "shrine_n_scene", "Temple of Time Exterior (Child - Night)", 0x030E4000, 0x030E5500, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00, 0 ), -0x0025: (0x0025, "shrine_r_scene", "Temple of Time Exterior (Ruins)", 0x03139000, 0x0313A490, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00, 0 ), -0x0026: (0x0026, "kokiri_home_scene", "Know-It-All Brothers' House", 0x02686000, 0x02686CC0, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00, 0 ), -0x0027: (0x0027, "kokiri_home3_scene", "House of Twins", 0x02AA5000, 0x02AA67A0, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00, 0 ), -0x0028: (0x0028, "kokiri_home4_scene", "Mido's House", 0x02F79000, 0x02F7AAB0, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00, 0 ), -0x0029: (0x0029, "kokiri_home5_scene", "Saria's House", 0x02FB4000, 0x02FB5410, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00, 0 ), -0x002A: (0x002A, "kakariko_scene", "Carpenter Boss's House", 0x02C84000, 0x02C84EA0, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00, 0 ), -0x002B: (0x002B, "kakariko3_scene", "Back Alley House (Man in Green)", 0x03007000, 0x03007840, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00, 0 ), -0x002C: (0x002C, "shop1_scene", "Bazaar", 0x028E3000, 0x028E32F0, 0x019B6000, 0x019B7B00, 0x00, 0x00, 0x00, 0x00, 0 ), -0x002D: (0x002D, "kokiri_shop_scene", "Kokiri Shop", 0x02586000, 0x02586980, 0x019AE000, 0x019AFB00, 0x00, 0x00, 0x00, 0x00, 0 ), -0x002E: (0x002E, "golon_scene", "Goron Shop", 0x02D32000, 0x02D323A0, 0x019B0000, 0x019B1B00, 0x00, 0x00, 0x00, 0x00, 0 ), -0x002F: (0x002F, "zoora_scene", "Zora Shop", 0x02D5A000, 0x02D5A390, 0x019B2000, 0x019B3B00, 0x00, 0x00, 0x00, 0x00, 0 ), -0x0030: (0x0030, "drag_scene", "Kakariko Potion Shop", 0x02D82000, 0x02D823F0, 0x019B8000, 0x019B9B00, 0x00, 0x00, 0x00, 0x00, 0 ), -0x0031: (0x0031, "alley_shop_scene", "Market Potion Shop", 0x02DAF000, 0x02DAF5B0, 0x019B8000, 0x019B9B00, 0x00, 0x00, 0x00, 0x00, 0 ), -0x0032: (0x0032, "night_shop_scene", "Bombchu Shop", 0x02DD7000, 0x02DD7670, 0x019F8000, 0x019F9B00, 0x00, 0x00, 0x00, 0x00, 0 ), -0x0033: (0x0033, "face_shop_scene", "Happy Mask Shop", 0x03409000, 0x03409370, 0x019EC000, 0x019EDB00, 0x00, 0x00, 0x00, 0x00, 0 ), -0x0034: (0x0034, "link_home_scene", "Link's House", 0x0255C000, 0x0255DB60, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00, 5 ), -0x0035: (0x0035, "impa_scene", "Back Alley House (Dog Lady)", 0x02E01000, 0x02E01D10, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00, 0 ), -0x0036: (0x0036, "malon_stable_scene", "Stable", 0x02C54000, 0x02C54670, 0x019E8000, 0x019E9B00, 0x00, 0x00, 0x00, 0x00, 0 ), -0x0037: (0x0037, "labo_scene", "Impa's House", 0x02E29000, 0x02E29EF0, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00, 0 ), -0x0038: (0x0038, "hylia_labo_scene", "Lakeside Laboratory", 0x03344000, 0x03355090, 0x019BC000, 0x019BDB00, 0x00, 0x2B, 0x00, 0x00, 0 ), -0x0039: (0x0039, "tent_scene", "Carpenters' Tent", 0x02E57000, 0x02E57680, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00, 0 ), -0x003A: (0x003A, "hut_scene", "Gravekeeper's Hut", 0x02CCC000, 0x02CCC510, 0x019BA000, 0x019BBB00, 0x00, 0x00, 0x00, 0x00, 0 ), -0x003B: (0x003B, "daiyousei_izumi_scene", "Great Fairy's Fountain (Upgrades)", 0x02CF7000, 0x02D05000, 0x019A2000, 0x019A3B00, 0x00, 0x21, 0x00, 0x00, 6 ), -0x003C: (0x003C, "yousei_izumi_tate_scene", "Fairy's Fountain", 0x02D12000, 0x02D1A810, 0x019E2000, 0x019E3B00, 0x00, 0x27, 0x00, 0x00, 0 ), -0x003D: (0x003D, "yousei_izumi_yoko_scene", "Great Fairy's Fountain (Spells)", 0x02D20000, 0x02D2CDA0, 0x019A2000, 0x019A3B00, 0x00, 0x21, 0x00, 0x00, 6 ), -0x003E: (0x003E, "kakusiana_scene", "Grottos", 0x026B3000, 0x026C0820, 0x00000000, 0x00000000, 0x00, 0x1F, 0x00, 0x00, 0 ), -0x003F: (0x003F, "hakaana_scene", "Grave (Redead)", 0x02D09000, 0x02D09A90, 0x00000000, 0x00000000, 0x00, 0x30, 0x00, 0x00, 5 ), -0x0040: (0x0040, "hakaana2_scene", "Grave (Fairy's Fountain)", 0x0327D000, 0x0328A090, 0x00000000, 0x00000000, 0x00, 0x27, 0x00, 0x00, 0 ), -0x0041: (0x0041, "hakaana_ouke_scene", "Royal Family's Tomb", 0x03328000, 0x0332CAE0, 0x019E0000, 0x019E1B00, 0x00, 0x2A, 0x00, 0x00, 0 ), -0x0042: (0x0042, "syatekijyou_scene", "Shooting Gallery", 0x02860000, 0x0286C2C0, 0x019A6000, 0x019A7B00, 0x00, 0x22, 0x00, 0x00, 6 ), -0x0043: (0x0043, "tokinoma_scene", "Temple of Time", 0x02529000, 0x0253B7A0, 0x019A8000, 0x019A9B00, 0x00, 0x1E, 0x00, 0x00, 14 ), -0x0044: (0x0044, "kenjyanoma_scene", "Chamber of the Sages", 0x02511000, 0x025148F0, 0x019A4000, 0x019A5B00, 0x00, 0x20, 0x00, 0x00, 6 ), -0x0045: (0x0045, "hairal_niwa_scene", "Castle Hedge Maze (Day)", 0x0290E000, 0x0291F390, 0x019A0000, 0x019A1B00, 0x00, 0x23, 0x00, 0x00, 0 ), -0x0046: (0x0046, "hairal_niwa_n_scene", "Castle Hedge Maze (Night)", 0x03304000, 0x0330D0F0, 0x019A0000, 0x019A1B00, 0x00, 0x23, 0x00, 0x00, 0 ), -0x0047: (0x0047, "hiral_demo_scene", "Cutscene Map", 0x02AA0000, 0x02AA3D50, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00, 12 ), -0x0048: (0x0048, "hakasitarelay_scene", "Dampé's Grave & Windmill", 0x03033000, 0x03041270, 0x019FA000, 0x019FBB00, 0x00, 0x30, 0x00, 0x00, 0 ), -0x0049: (0x0049, "turibori_scene", "Fishing Pond", 0x030CA000, 0x030DC6E0, 0x019E4000, 0x019E5B00, 0x00, 0x32, 0x00, 0x00, 0 ), -0x004A: (0x004A, "nakaniwa_scene", "Castle Courtyard", 0x02E8C000, 0x02E8FA20, 0x019A0000, 0x019A1B00, 0x00, 0x2F, 0x00, 0x00, 6 ), -0x004B: (0x004B, "bowling_scene", "Bombchu Bowling Alley", 0x03312000, 0x03320B20, 0x019E6000, 0x019E7B00, 0x00, 0x29, 0x00, 0x00, 0 ), -0x004C: (0x004C, "souko_scene", "Ranch House & Silo", 0x0335E000, 0x03364C10, 0x00000000, 0x00000000, 0x00, 0x2C, 0x00, 0x00, 0 ), -0x004D: (0x004D, "miharigoya_scene", "Guard House", 0x03383000, 0x0338F550, 0x00000000, 0x00000000, 0x00, 0x2D, 0x00, 0x00, 3 ), -0x004E: (0x004E, "mahouya_scene", "Granny's Potion Shop", 0x03394000, 0x0339EA20, 0x019B8000, 0x019B9B00, 0x00, 0x2E, 0x00, 0x00, 0 ), -0x004F: (0x004F, "ganon_demo_scene", "Ganon's Tower Collapse & Battle Arena", 0x033FA000, 0x03403070, 0x00000000, 0x00000000, 0x00, 0x24, 0x00, 0x00, 4 ), -0x0050: (0x0050, "kinsuta_scene", "House of Skulltula", 0x03439000, 0x0343A610, 0x019B4000, 0x019B5B00, 0x00, 0x00, 0x00, 0x00, 0 ), -0x0051: (0x0051, "spot00_scene", "Spot 0x00 - Hyrule Field", 0x01FB8000, 0x01FE2220, 0x019BE000, 0x019BFB00, 0x00, 0x01, 0x00, 0x00, 12 ), -0x0052: (0x0052, "spot01_scene", "Spot 0x01 - Kakariko Village", 0x01FF9000, 0x02015150, 0x019C0000, 0x019C1B00, 0x00, 0x02, 0x00, 0x00, 8 ), -0x0053: (0x0053, "spot02_scene", "Spot 0x02 - Graveyard", 0x02020000, 0x0202BC80, 0x019C2000, 0x019C3B00, 0x00, 0x00, 0x00, 0x00, 5 ), -0x0054: (0x0054, "spot03_scene", "Spot 0x03 - Zora's River", 0x0204D000, 0x02058560, 0x019C4000, 0x019C5B00, 0x00, 0x03, 0x00, 0x00, 3 ), -0x0055: (0x0055, "spot04_scene", "Spot 0x04 - Kokiri Forest", 0x0206F000, 0x02080240, 0x019C6000, 0x019C7B00, 0x00, 0x04, 0x00, 0x00, 13 ), -0x0056: (0x0056, "spot05_scene", "Spot 0x05 - Sacred Forest Meadow", 0x020AC000, 0x020C0D60, 0x019F0000, 0x019F1B00, 0x00, 0x2F, 0x00, 0x00, 5 ), -0x0057: (0x0057, "spot06_scene", "Spot 0x06 - Lake Hylia", 0x020CB000, 0x020E6430, 0x019C8000, 0x019C9B00, 0x00, 0x05, 0x00, 0x00, 5 ), -0x0058: (0x0058, "spot07_scene", "Spot 0x07 - Zora's Domain", 0x020F2000, 0x020FB820, 0x019CA000, 0x019CBB00, 0x00, 0x06, 0x00, 0x00, 4 ), -0x0059: (0x0059, "spot08_scene", "Spot 0x08 - Zora's Fountain", 0x02110000, 0x021216A0, 0x019CC000, 0x019CDB00, 0x00, 0x07, 0x00, 0x00, 6 ), -0x005A: (0x005A, "spot09_scene", "Spot 0x09 - Gerudo Valley", 0x0212B000, 0x0213C160, 0x019CE000, 0x019CFB00, 0x00, 0x08, 0x00, 0x00, 6 ), -0x005B: (0x005B, "spot10_scene", "Spot 0x10 - Lost Woods", 0x02146000, 0x02156430, 0x019D0000, 0x019D1B00, 0x00, 0x09, 0x00, 0x00, 4 ), -0x005C: (0x005C, "spot11_scene", "Spot 0x11 - Desert Colossus", 0x02186000, 0x0219F4E0, 0x019F6000, 0x019F7B00, 0x00, 0x0A, 0x00, 0x00, 5 ), -0x005D: (0x005D, "spot12_scene", "Spot 0x12 - Gerudo's Fortress", 0x021AD000, 0x021BCE80, 0x019F2000, 0x019F3B00, 0x00, 0x0B, 0x00, 0x00, 6 ), -0x005E: (0x005E, "spot13_scene", "Spot 0x13 - Haunted Wasteland", 0x021DC000, 0x021E1E30, 0x019D2000, 0x019D3B00, 0x00, 0x0C, 0x00, 0x00, 0 ), -0x005F: (0x005F, "spot15_scene", "Spot 0x15 - Hyrule Castle", 0x021F6000, 0x0220E500, 0x019D4000, 0x019D5B00, 0x00, 0x0D, 0x00, 0x00, 0 ), -0x0060: (0x0060, "spot16_scene", "Spot 0x16 - Death Mountain Trail", 0x0221D000, 0x0223BA90, 0x019D6000, 0x019D7B00, 0x00, 0x0E, 0x00, 0x00, 8 ), -0x0061: (0x0061, "spot17_scene", "Spot 0x17 - Death Mountain Crater", 0x02247000, 0x02256EE0, 0x019D8000, 0x019D9B00, 0x00, 0x0F, 0x00, 0x00, 5 ), -0x0062: (0x0062, "spot18_scene", "Spot 0x18 - Goron City", 0x02271000, 0x0227B450, 0x019DA000, 0x019DBB00, 0x00, 0x10, 0x00, 0x00, 5 ), -0x0063: (0x0063, "spot20_scene", "Spot 0x20 - Lon Lon Ranch", 0x029BC000, 0x029CDDC0, 0x019DC000, 0x019DDB00, 0x00, 0x11, 0x00, 0x00, 11 ), -0x0064: (0x0064, "ganon_tou_scene", "Ganon's Castle Exterior", 0x0292B000, 0x0292FB70, 0x019DE000, 0x019DFB00, 0x00, 0x24, 0x00, 0x00, 4 ), +# | | | | VROM | VROM (Title Card) | | | | | +# | ID | Filename | Description | Start | End | Start | End | ? | Rn | # | * | +# | | | | | | | | | | | | +SCENE_TABLE = { +0x0000: (0x0000, "ydan_scene", "Inside the Deku Tree", 0x02499000, 0x024A6A10, 0x01994000, 0x01995B00, 0x01, 0x13, 0x02, 0x00 ), +0x0001: (0x0001, "ddan_scene", "Dodongo's Cavern", 0x01F12000, 0x01F27140, 0x01998000, 0x01999B00, 0x01, 0x14, 0x03, 0x00 ), +0x0002: (0x0002, "bdan_scene", "Inside Jabu-Jabu's Belly", 0x0273E000, 0x027537C0, 0x01996000, 0x01997B00, 0x01, 0x15, 0x04, 0x00 ), +0x0003: (0x0003, "Bmori1_scene", "Forest Temple", 0x023CF000, 0x023E4F90, 0x0198A000, 0x0198BB00, 0x02, 0x16, 0x05, 0x00 ), +0x0004: (0x0004, "HIDAN_scene", "Fire Temple", 0x022D8000, 0x022F2970, 0x0198E000, 0x0198FB00, 0x02, 0x12, 0x06, 0x00 ), +0x0005: (0x0005, "MIZUsin_scene", "Water Temple", 0x025B8000, 0x025CDCF0, 0x01990000, 0x01991B00, 0x01, 0x17, 0x07, 0x00 ), +0x0006: (0x0006, "jyasinzou_scene", "Spirit Temple", 0x02ADE000, 0x02AF7B40, 0x01992000, 0x01993B00, 0x01, 0x19, 0x08, 0x00 ), +0x0007: (0x0007, "HAKAdan_scene", "Shadow Temple", 0x027A7000, 0x027BF3C0, 0x0198C000, 0x0198DB00, 0x02, 0x18, 0x09, 0x00 ), +0x0008: (0x0008, "HAKAdanCH_scene", "Bottom of the Well", 0x032C6000, 0x032D2560, 0x019F4000, 0x019F5B00, 0x02, 0x18, 0x0A, 0x00 ), +0x0009: (0x0009, "ice_doukutu_scene", "Ice Cavern", 0x02BEB000, 0x02BFC610, 0x0199C000, 0x0199DB00, 0x00, 0x25, 0x00, 0x00 ), +0x000A: (0x000A, "ganon_scene", "Ganon's Tower", 0x02EE3000, 0x02EF37B0, 0x00000000, 0x00000000, 0x02, 0x00, 0x00, 0x00 ), +0x000B: (0x000B, "men_scene", "Gerudo Training Ground", 0x02875000, 0x02886530, 0x0199E000, 0x0199FB00, 0x00, 0x1B, 0x00, 0x00 ), +0x000C: (0x000C, "gerudoway_scene", "Thieves' Hideout", 0x03292000, 0x0329F920, 0x019EA000, 0x019EBB00, 0x00, 0x28, 0x00, 0x00 ), +0x000D: (0x000D, "ganontika_scene", "Inside Ganon's Castle", 0x0318E000, 0x031AF7C0, 0x0199A000, 0x0199BB00, 0x00, 0x1A, 0x00, 0x00 ), +0x000E: (0x000E, "ganon_sonogo_scene", "Ganon's Tower (Collapsing)", 0x033CA000, 0x033D1F10, 0x00000000, 0x00000000, 0x00, 0x33, 0x00, 0x00 ), +0x000F: (0x000F, "ganontikasonogo_scene", "Inside Ganon's Castle (Collapsing)", 0x0344A000, 0x0344EB00, 0x00000000, 0x00000000, 0x00, 0x34, 0x00, 0x00 ), +0x0010: (0x0010, "takaraya_scene", "Treasure Box Shop", 0x033A6000, 0x033AD1B0, 0x019EE000, 0x019EFB00, 0x00, 0x00, 0x00, 0x00 ), +0x0011: (0x0011, "ydan_boss_scene", "Gohma's Lair", 0x02EB4000, 0x02EB5740, 0x00000000, 0x00000000, 0x00, 0x1C, 0x00, 0x00 ), +0x0012: (0x0012, "ddan_boss_scene", "King Dodongo's Lair", 0x02EA8000, 0x02EAA860, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00 ), +0x0013: (0x0013, "bdan_boss_scene", "Barinade's Lair", 0x02CB0000, 0x02CB0E40, 0x00000000, 0x00000000, 0x00, 0x15, 0x00, 0x00 ), +0x0014: (0x0014, "moribossroom_scene", "Phantom Ganon's Lair", 0x0284A000, 0x0284B900, 0x00000000, 0x00000000, 0x01, 0x00, 0x00, 0x00 ), +0x0015: (0x0015, "FIRE_bs_scene", "Volvagia's Lair", 0x02CBB000, 0x02CBFC00, 0x00000000, 0x00000000, 0x00, 0x12, 0x00, 0x00 ), +0x0016: (0x0016, "MIZUsin_bs_scene", "Morpha's Lair", 0x02ED0000, 0x02ED1A60, 0x00000000, 0x00000000, 0x00, 0x1D, 0x00, 0x00 ), +0x0017: (0x0017, "jyasinboss_scene", "Twinrova's Lair & Nabooru's Mini-Boss Room", 0x02F57000, 0x02F5FCF0, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00 ), +0x0018: (0x0018, "HAKAdan_bs_scene", "Bongo Bongo's Lair", 0x02EC4000, 0x02EC6780, 0x00000000, 0x00000000, 0x00, 0x18, 0x00, 0x00 ), +0x0019: (0x0019, "ganon_boss_scene", "Ganondorf's Lair", 0x02F49000, 0x02F50C20, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00 ), +0x001A: (0x001A, "ganon_final_scene", "Tower Collapse Exterior", 0x02FE7000, 0x02FF9180, 0x00000000, 0x00000000, 0x00, 0x26, 0x00, 0x00 ), +0x001B: (0x001B, "entra_scene", "Market Entrance (Child - Day)", 0x02717000, 0x027173E0, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00 ), +0x001C: (0x001C, "entra_n_scene", "Market Entrance (Child - Night)", 0x029DA000, 0x029DA430, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00 ), +0x001D: (0x001D, "enrui_scene", "Market Entrance (Ruins)", 0x02A01000, 0x02A013E0, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00 ), +0x001E: (0x001E, "market_alley_scene", "Back Alley (Child - Day)", 0x02944000, 0x02945250, 0x019AC000, 0x019ADB00, 0x00, 0x00, 0x00, 0x00 ), +0x001F: (0x001F, "market_alley_n_scene", "Back Alley (Child - Night)", 0x02A28000, 0x02A292F0, 0x019AC000, 0x019ADB00, 0x00, 0x00, 0x00, 0x00 ), +0x0020: (0x0020, "market_day_scene", "Market (Child - Day)", 0x022C6000, 0x022C8670, 0x019AA000, 0x019ABB00, 0x00, 0x00, 0x00, 0x00 ), +0x0021: (0x0021, "market_night_scene", "Market (Child - Night)", 0x022CF000, 0x022D1630, 0x019AA000, 0x019ABB00, 0x00, 0x00, 0x00, 0x00 ), +0x0022: (0x0022, "market_ruins_scene", "Market (Ruins)", 0x029D5000, 0x029D6630, 0x019AA000, 0x019ABB00, 0x00, 0x00, 0x00, 0x00 ), +0x0023: (0x0023, "shrine_scene", "Temple of Time Exterior (Child - Day)", 0x03075000, 0x030764E0, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00 ), +0x0024: (0x0024, "shrine_n_scene", "Temple of Time Exterior (Child - Night)", 0x030E4000, 0x030E5500, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00 ), +0x0025: (0x0025, "shrine_r_scene", "Temple of Time Exterior (Ruins)", 0x03139000, 0x0313A490, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00 ), +0x0026: (0x0026, "kokiri_home_scene", "Know-It-All Brothers' House", 0x02686000, 0x02686CC0, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00 ), +0x0027: (0x0027, "kokiri_home3_scene", "House of Twins", 0x02AA5000, 0x02AA67A0, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00 ), +0x0028: (0x0028, "kokiri_home4_scene", "Mido's House", 0x02F79000, 0x02F7AAB0, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00 ), +0x0029: (0x0029, "kokiri_home5_scene", "Saria's House", 0x02FB4000, 0x02FB5410, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00 ), +0x002A: (0x002A, "kakariko_scene", "Carpenter Boss's House", 0x02C84000, 0x02C84EA0, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00 ), +0x002B: (0x002B, "kakariko3_scene", "Back Alley House (Man in Green)", 0x03007000, 0x03007840, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00 ), +0x002C: (0x002C, "shop1_scene", "Bazaar", 0x028E3000, 0x028E32F0, 0x019B6000, 0x019B7B00, 0x00, 0x00, 0x00, 0x00 ), +0x002D: (0x002D, "kokiri_shop_scene", "Kokiri Shop", 0x02586000, 0x02586980, 0x019AE000, 0x019AFB00, 0x00, 0x00, 0x00, 0x00 ), +0x002E: (0x002E, "golon_scene", "Goron Shop", 0x02D32000, 0x02D323A0, 0x019B0000, 0x019B1B00, 0x00, 0x00, 0x00, 0x00 ), +0x002F: (0x002F, "zoora_scene", "Zora Shop", 0x02D5A000, 0x02D5A390, 0x019B2000, 0x019B3B00, 0x00, 0x00, 0x00, 0x00 ), +0x0030: (0x0030, "drag_scene", "Kakariko Potion Shop", 0x02D82000, 0x02D823F0, 0x019B8000, 0x019B9B00, 0x00, 0x00, 0x00, 0x00 ), +0x0031: (0x0031, "alley_shop_scene", "Market Potion Shop", 0x02DAF000, 0x02DAF5B0, 0x019B8000, 0x019B9B00, 0x00, 0x00, 0x00, 0x00 ), +0x0032: (0x0032, "night_shop_scene", "Bombchu Shop", 0x02DD7000, 0x02DD7670, 0x019F8000, 0x019F9B00, 0x00, 0x00, 0x00, 0x00 ), +0x0033: (0x0033, "face_shop_scene", "Happy Mask Shop", 0x03409000, 0x03409370, 0x019EC000, 0x019EDB00, 0x00, 0x00, 0x00, 0x00 ), +0x0034: (0x0034, "link_home_scene", "Link's House", 0x0255C000, 0x0255DB60, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00 ), +0x0035: (0x0035, "impa_scene", "Back Alley House (Dog Lady)", 0x02E01000, 0x02E01D10, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00 ), +0x0036: (0x0036, "malon_stable_scene", "Stable", 0x02C54000, 0x02C54670, 0x019E8000, 0x019E9B00, 0x00, 0x00, 0x00, 0x00 ), +0x0037: (0x0037, "labo_scene", "Impa's House", 0x02E29000, 0x02E29EF0, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00 ), +0x0038: (0x0038, "hylia_labo_scene", "Lakeside Laboratory", 0x03344000, 0x03355090, 0x019BC000, 0x019BDB00, 0x00, 0x2B, 0x00, 0x00 ), +0x0039: (0x0039, "tent_scene", "Carpenters' Tent", 0x02E57000, 0x02E57680, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00 ), +0x003A: (0x003A, "hut_scene", "Gravekeeper's Hut", 0x02CCC000, 0x02CCC510, 0x019BA000, 0x019BBB00, 0x00, 0x00, 0x00, 0x00 ), +0x003B: (0x003B, "daiyousei_izumi_scene", "Great Fairy's Fountain (Upgrades)", 0x02CF7000, 0x02D05000, 0x019A2000, 0x019A3B00, 0x00, 0x21, 0x00, 0x00 ), +0x003C: (0x003C, "yousei_izumi_tate_scene", "Fairy's Fountain", 0x02D12000, 0x02D1A810, 0x019E2000, 0x019E3B00, 0x00, 0x27, 0x00, 0x00 ), +0x003D: (0x003D, "yousei_izumi_yoko_scene", "Great Fairy's Fountain (Spells)", 0x02D20000, 0x02D2CDA0, 0x019A2000, 0x019A3B00, 0x00, 0x21, 0x00, 0x00 ), +0x003E: (0x003E, "kakusiana_scene", "Grottos", 0x026B3000, 0x026C0820, 0x00000000, 0x00000000, 0x00, 0x1F, 0x00, 0x00 ), +0x003F: (0x003F, "hakaana_scene", "Grave (Redead)", 0x02D09000, 0x02D09A90, 0x00000000, 0x00000000, 0x00, 0x30, 0x00, 0x00 ), +0x0040: (0x0040, "hakaana2_scene", "Grave (Fairy's Fountain)", 0x0327D000, 0x0328A090, 0x00000000, 0x00000000, 0x00, 0x27, 0x00, 0x00 ), +0x0041: (0x0041, "hakaana_ouke_scene", "Royal Family's Tomb", 0x03328000, 0x0332CAE0, 0x019E0000, 0x019E1B00, 0x00, 0x2A, 0x00, 0x00 ), +0x0042: (0x0042, "syatekijyou_scene", "Shooting Gallery", 0x02860000, 0x0286C2C0, 0x019A6000, 0x019A7B00, 0x00, 0x22, 0x00, 0x00 ), +0x0043: (0x0043, "tokinoma_scene", "Temple of Time", 0x02529000, 0x0253B7A0, 0x019A8000, 0x019A9B00, 0x00, 0x1E, 0x00, 0x00 ), +0x0044: (0x0044, "kenjyanoma_scene", "Chamber of the Sages", 0x02511000, 0x025148F0, 0x019A4000, 0x019A5B00, 0x00, 0x20, 0x00, 0x00 ), +0x0045: (0x0045, "hairal_niwa_scene", "Castle Hedge Maze (Day)", 0x0290E000, 0x0291F390, 0x019A0000, 0x019A1B00, 0x00, 0x23, 0x00, 0x00 ), +0x0046: (0x0046, "hairal_niwa_n_scene", "Castle Hedge Maze (Night)", 0x03304000, 0x0330D0F0, 0x019A0000, 0x019A1B00, 0x00, 0x23, 0x00, 0x00 ), +0x0047: (0x0047, "hiral_demo_scene", "Cutscene Map", 0x02AA0000, 0x02AA3D50, 0x00000000, 0x00000000, 0x00, 0x00, 0x00, 0x00 ), +0x0048: (0x0048, "hakasitarelay_scene", "Dampé's Grave & Windmill", 0x03033000, 0x03041270, 0x019FA000, 0x019FBB00, 0x00, 0x30, 0x00, 0x00 ), +0x0049: (0x0049, "turibori_scene", "Fishing Pond", 0x030CA000, 0x030DC6E0, 0x019E4000, 0x019E5B00, 0x00, 0x32, 0x00, 0x00 ), +0x004A: (0x004A, "nakaniwa_scene", "Castle Courtyard", 0x02E8C000, 0x02E8FA20, 0x019A0000, 0x019A1B00, 0x00, 0x2F, 0x00, 0x00 ), +0x004B: (0x004B, "bowling_scene", "Bombchu Bowling Alley", 0x03312000, 0x03320B20, 0x019E6000, 0x019E7B00, 0x00, 0x29, 0x00, 0x00 ), +0x004C: (0x004C, "souko_scene", "Ranch House & Silo", 0x0335E000, 0x03364C10, 0x00000000, 0x00000000, 0x00, 0x2C, 0x00, 0x00 ), +0x004D: (0x004D, "miharigoya_scene", "Guard House", 0x03383000, 0x0338F550, 0x00000000, 0x00000000, 0x00, 0x2D, 0x00, 0x00 ), +0x004E: (0x004E, "mahouya_scene", "Granny's Potion Shop", 0x03394000, 0x0339EA20, 0x019B8000, 0x019B9B00, 0x00, 0x2E, 0x00, 0x00 ), +0x004F: (0x004F, "ganon_demo_scene", "Ganon's Tower Collapse & Battle Arena", 0x033FA000, 0x03403070, 0x00000000, 0x00000000, 0x00, 0x24, 0x00, 0x00 ), +0x0050: (0x0050, "kinsuta_scene", "House of Skulltula", 0x03439000, 0x0343A610, 0x019B4000, 0x019B5B00, 0x00, 0x00, 0x00, 0x00 ), +0x0051: (0x0051, "spot00_scene", "Spot 0x00 - Hyrule Field", 0x01FB8000, 0x01FE2220, 0x019BE000, 0x019BFB00, 0x00, 0x01, 0x00, 0x00 ), +0x0052: (0x0052, "spot01_scene", "Spot 0x01 - Kakariko Village", 0x01FF9000, 0x02015150, 0x019C0000, 0x019C1B00, 0x00, 0x02, 0x00, 0x00 ), +0x0053: (0x0053, "spot02_scene", "Spot 0x02 - Graveyard", 0x02020000, 0x0202BC80, 0x019C2000, 0x019C3B00, 0x00, 0x00, 0x00, 0x00 ), +0x0054: (0x0054, "spot03_scene", "Spot 0x03 - Zora's River", 0x0204D000, 0x02058560, 0x019C4000, 0x019C5B00, 0x00, 0x03, 0x00, 0x00 ), +0x0055: (0x0055, "spot04_scene", "Spot 0x04 - Kokiri Forest", 0x0206F000, 0x02080240, 0x019C6000, 0x019C7B00, 0x00, 0x04, 0x00, 0x00 ), +0x0056: (0x0056, "spot05_scene", "Spot 0x05 - Sacred Forest Meadow", 0x020AC000, 0x020C0D60, 0x019F0000, 0x019F1B00, 0x00, 0x2F, 0x00, 0x00 ), +0x0057: (0x0057, "spot06_scene", "Spot 0x06 - Lake Hylia", 0x020CB000, 0x020E6430, 0x019C8000, 0x019C9B00, 0x00, 0x05, 0x00, 0x00 ), +0x0058: (0x0058, "spot07_scene", "Spot 0x07 - Zora's Domain", 0x020F2000, 0x020FB820, 0x019CA000, 0x019CBB00, 0x00, 0x06, 0x00, 0x00 ), +0x0059: (0x0059, "spot08_scene", "Spot 0x08 - Zora's Fountain", 0x02110000, 0x021216A0, 0x019CC000, 0x019CDB00, 0x00, 0x07, 0x00, 0x00 ), +0x005A: (0x005A, "spot09_scene", "Spot 0x09 - Gerudo Valley", 0x0212B000, 0x0213C160, 0x019CE000, 0x019CFB00, 0x00, 0x08, 0x00, 0x00 ), +0x005B: (0x005B, "spot10_scene", "Spot 0x10 - Lost Woods", 0x02146000, 0x02156430, 0x019D0000, 0x019D1B00, 0x00, 0x09, 0x00, 0x00 ), +0x005C: (0x005C, "spot11_scene", "Spot 0x11 - Desert Colossus", 0x02186000, 0x0219F4E0, 0x019F6000, 0x019F7B00, 0x00, 0x0A, 0x00, 0x00 ), +0x005D: (0x005D, "spot12_scene", "Spot 0x12 - Gerudo's Fortress", 0x021AD000, 0x021BCE80, 0x019F2000, 0x019F3B00, 0x00, 0x0B, 0x00, 0x00 ), +0x005E: (0x005E, "spot13_scene", "Spot 0x13 - Haunted Wasteland", 0x021DC000, 0x021E1E30, 0x019D2000, 0x019D3B00, 0x00, 0x0C, 0x00, 0x00 ), +0x005F: (0x005F, "spot15_scene", "Spot 0x15 - Hyrule Castle", 0x021F6000, 0x0220E500, 0x019D4000, 0x019D5B00, 0x00, 0x0D, 0x00, 0x00 ), +0x0060: (0x0060, "spot16_scene", "Spot 0x16 - Death Mountain Trail", 0x0221D000, 0x0223BA90, 0x019D6000, 0x019D7B00, 0x00, 0x0E, 0x00, 0x00 ), +0x0061: (0x0061, "spot17_scene", "Spot 0x17 - Death Mountain Crater", 0x02247000, 0x02256EE0, 0x019D8000, 0x019D9B00, 0x00, 0x0F, 0x00, 0x00 ), +0x0062: (0x0062, "spot18_scene", "Spot 0x18 - Goron City", 0x02271000, 0x0227B450, 0x019DA000, 0x019DBB00, 0x00, 0x10, 0x00, 0x00 ), +0x0063: (0x0063, "spot20_scene", "Spot 0x20 - Lon Lon Ranch", 0x029BC000, 0x029CDDC0, 0x019DC000, 0x019DDB00, 0x00, 0x11, 0x00, 0x00 ), +0x0064: (0x0064, "ganon_tou_scene", "Ganon's Castle Exterior", 0x0292B000, 0x0292FB70, 0x019DE000, 0x019DFB00, 0x00, 0x24, 0x00, 0x00 ), +} + + +class RecordType(str, Enum): + # Scene header record types + SceneHeader = 'SceneHeader' + AlternateHeaders = 'AlternateHeaders' + RoomList = 'RoomList' + TransitionActorList = 'TransitionActorList' + CollisionHeader = 'CollisionHeader' + EntranceList = 'EntranceList' + Points = 'Points' + PathList = 'PathList' + SpawnList = 'SpawnList' + ExitList = 'ExitList' + LightSettings = 'LightSettings' + CutsceneData = 'CutsceneData' + + # Collision header record types + Vertices = 'Vertices' + Polys = 'Polys' + SurfaceTypes = 'SurfaceTypes' + Cams = 'Cams' + Waterboxes = 'Waterboxes' + CamPosData = 'CamPosData' + + # Room header record types + RoomHeader = 'RoomHeader' + ObjectList = 'ObjectList' + ActorList = 'ActorList' + + # Mesh header record types + MeshHeader = 'MeshHeader' + MeshHeaderImageSingle = 'MeshHeaderImage' + MeshHeaderImageMulti = 'MeshHeaderImage' + MeshHeaderCullable = 'MeshHeaderCullable' + DlistEntries = 'DlistEntries' + DlistEntry = 'DlistEntry' + DlistCullableEntries = 'DlistCullableEntries' + Dlist = 'Dlist' + Vtx = 'Vtx' + Mtx = 'Mtx' + SetTImg = 'SetTImg' + SetZImg = 'SetZImg' + SetCImg = 'SetCImg' + Backgrounds = 'Backgrounds' + Background = 'Background' + BackgroundImage = 'BackgroundImage' + BackgroundTlut = 'BackgroundTlut' + CullableEntries = 'CullableEntries' + CullableEntry = 'CullableEntry' + Cullable = 'Cullable' + + # Other unparsed data blob record types + Texture = 'Texture' + TextureAnimation = 'TextureAnimation' + Symbol = 'Symbol' + Scalar = 'Scalar' + Vector = 'Vector' + Array = 'Array' + Pointer = 'Pointer' + Blob = 'Blob' + + # Model-related record types + Skeleton = 'Skeleton' + LimbTable = 'LimbTable' + Limb = 'Limb' + Animation = 'Animation' + PlayerAnimation = 'PlayerAnimation' + PlayerAnimationData = 'PlayerAnimationData' + CurveAnimation = 'CurveAnimation' + LegacyAnimation = 'LegacyAnimation' + + # Unknown data not referenced in scene and room files + Unknown = 'Unknown' + + # Record types referenced by header commands without count + def has_unknown_count(self) -> bool: + if self == RecordType.CollisionHeader: # 0x03 + return True + if self == RecordType.EntranceList: # 0x06 + return True + if self == RecordType.MeshHeader: # 0x0A + return True + if self == RecordType.PathList: # 0x0D + return True + if self == RecordType.ExitList: # 0x13 + return True + if self == RecordType.CutsceneData: # 0x17 + return True + if self == RecordType.AlternateHeaders: # 0x18 + return True + if self == RecordType.SurfaceTypes: + return True + if self == RecordType.Cams: + return True + if self == RecordType.SetTImg: + return True + if self == RecordType.SetZImg: + return True + if self == RecordType.SetCImg: + return True + return False + + +# z_demo tables +ENTRANCE_CUTSCENE_TABLE_ADDRESS = 0xB65C64 +UNKNOWN_LIST_CUTSCENES = 0xB65D74 # does not include NULL first entry + +# z_demo_kekkai list +SAGE_CUTSCENES = 0xECF8EC + +# z_scene_table day/night texture list +SCENE_TEXTURE_LIST = 0xB71D4C + +# Indexed by scene/room file name +# Data is the vanilla offset from the start of the file and +# a list of every VROM address where the segment address +# for that cutscene is referenced. Some addresses are +# used directly in the code, split into upper and lower +# halves of the full address. These are supplied as tuples +# of the addresses referencing the upper and lower segment +# address halves. + +# Tuple data: +# scene file record type, +# record offset from file start, +# data reference addresses, +# code reference addresses + +SCENE_EXTERNAL_REFERENCES: dict[str, list[tuple[RecordType, int, list[int], list[tuple[int, int]]]]] = { + 'bdan_scene': [ + ( RecordType.CutsceneData, 0x155E0, [ + ENTRANCE_CUTSCENE_TABLE_ADDRESS + (17 * 0x8) + 0x4, + UNKNOWN_LIST_CUTSCENES + 1 * 0x4, + ], [] ), + ], + "Bmori1_scene": [ + ( RecordType.Texture, 0x14D90, [SCENE_TEXTURE_LIST + 26 * 0x4], [] ), + ( RecordType.Texture, 0x14590, [SCENE_TEXTURE_LIST + 27 * 0x4], [] ), + ], + "ddan_boss_room_1": [ + ( RecordType.Texture, 0x021C8, [], [(0xC3BA9A, 0xC3BAA2), (0xC3E61A, 0xC3E626)] ), + ], + "ddan_scene": [ + ( RecordType.Texture, 0x12378, [SCENE_TEXTURE_LIST + 2 * 0x4], [] ), + ( RecordType.Texture, 0x13378, [SCENE_TEXTURE_LIST + 3 * 0x4], [] ), + ( RecordType.Texture, 0x11F78, [SCENE_TEXTURE_LIST + 4 * 0x4], [] ), + ( RecordType.Texture, 0x14778, [SCENE_TEXTURE_LIST + 5 * 0x4], [] ), + ( RecordType.Texture, 0x14378, [SCENE_TEXTURE_LIST + 6 * 0x4], [] ), + ( RecordType.Texture, 0x13F78, [SCENE_TEXTURE_LIST + 7 * 0x4], [] ), + ( RecordType.Texture, 0x14B78, [SCENE_TEXTURE_LIST + 8 * 0x4], [] ), + ( RecordType.Texture, 0x13B78, [SCENE_TEXTURE_LIST + 9 * 0x4], [] ), + ( RecordType.Texture, 0x12F78, [SCENE_TEXTURE_LIST + 10 * 0x4], [] ), + ( RecordType.Texture, 0x12B78, [SCENE_TEXTURE_LIST + 11 * 0x4], [] ), + ( RecordType.CutsceneData, 0x14F80, [UNKNOWN_LIST_CUTSCENES + 2 * 0x4], [(0xC9EDAE, 0xC9EDB2)] ), + ], + 'ganontika': [ + ( RecordType.CutsceneData, 0x19ED0, [SAGE_CUTSCENES + 6 * 0x4], [] ), + ( RecordType.CutsceneData, 0x1A8D0, [SAGE_CUTSCENES + 1 * 0x4], [] ), + ( RecordType.CutsceneData, 0x1B2A0, [SAGE_CUTSCENES + 4 * 0x4], [] ), + ( RecordType.CutsceneData, 0x1BC70, [SAGE_CUTSCENES + 3 * 0x4], [] ), + ( RecordType.CutsceneData, 0x1C6A0, [SAGE_CUTSCENES + 2 * 0x4], [] ), + ( RecordType.CutsceneData, 0x1D070, [SAGE_CUTSCENES + 5 * 0x4], [] ), + ( RecordType.CutsceneData, 0x1DA40, [UNKNOWN_LIST_CUTSCENES + 5 * 0x4], [(0xACA97E, 0xACA982)] ), + ( RecordType.CutsceneData, 0x1DF80, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (28 * 0x8) + 0x4], [] ), + ( RecordType.CutsceneData, 0x1E3D0, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (27 * 0x8) + 0x4], [] ), + ( RecordType.CutsceneData, 0x1E780, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (24 * 0x8) + 0x4], [] ), + ( RecordType.CutsceneData, 0x1EB30, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (29 * 0x8) + 0x4], [] ), + ( RecordType.CutsceneData, 0x1EF60, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (25 * 0x8) + 0x4], [] ), + ( RecordType.CutsceneData, 0x21370, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (26 * 0x8) + 0x4], [] ), + ], + "gerudoway_scene": [ + ( RecordType.Texture, 0x0B920, [SCENE_TEXTURE_LIST + 13 * 0x4], [] ), + ( RecordType.Texture, 0x0BD20, [SCENE_TEXTURE_LIST + 12 * 0x4], [] ), + ], + "ice_doukutu_scene": [ + ( RecordType.Texture, 0x0F810, [SCENE_TEXTURE_LIST + 17 * 0x4], [] ), + ( RecordType.Texture, 0x0FA10, [SCENE_TEXTURE_LIST + 16 * 0x4], [] ), + ( RecordType.CutsceneData, 0x00250, [UNKNOWN_LIST_CUTSCENES + 4 * 0x4], [(0xC7BD46, 0xC7BD4E)] ), + ], + "jyasinboss_scene": [ + ( RecordType.CutsceneData, 0x02BB0, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (30 * 0x8) + 0x4], [] ), + ( RecordType.CutsceneData, 0x03F80, [UNKNOWN_LIST_CUTSCENES + 3 * 0x4], [(0xDED5C2, 0xDED5D6)] ), + ], + "jyasinzou_scene": [ + ( RecordType.Texture, 0x18940, [SCENE_TEXTURE_LIST + 28 * 0x4], [] ), + ( RecordType.Texture, 0x18040, [SCENE_TEXTURE_LIST + 29 * 0x4], [] ), + ], + "men_scene": [ + ( RecordType.Texture, 0x0F930, [SCENE_TEXTURE_LIST + 18 * 0x4], [] ), + ( RecordType.Texture, 0x10130, [SCENE_TEXTURE_LIST + 19 * 0x4], [] ), + ], + "MIZUsin_scene": [ + ( RecordType.Texture, 0x14CF0, [SCENE_TEXTURE_LIST + 14 * 0x4], [] ), + ( RecordType.Texture, 0x158F0, [SCENE_TEXTURE_LIST + 15 * 0x4], [] ), + ], + "ydan_scene": [ + ( RecordType.Texture, 0x0BA08, [SCENE_TEXTURE_LIST + 0 * 0x4], [] ), + ( RecordType.Texture, 0x0CA08, [SCENE_TEXTURE_LIST + 1 * 0x4], [] ), + ( RecordType.CutsceneData, 0x0B640, [ + ENTRANCE_CUTSCENE_TABLE_ADDRESS + (7 * 0x8) + 0x4, + UNKNOWN_LIST_CUTSCENES + 0 * 0x4, + ], [] ), + ], + "daiyousei_izumi_scene": [ + ( RecordType.CutsceneData, 0x00130, [], [(0xC89A0A, 0xC89A0E)] ), + ( RecordType.CutsceneData, 0x013E0, [], [(0xC89A4E, 0xC89A52)] ), + ( RecordType.CutsceneData, 0x025D0, [], [(0xC89A92, 0xC89A96)] ), + ], + "hakasitarelay_scene": [ + ( RecordType.CutsceneData, 0x0E080, [], [(0xE42A4E, 0xE42A5A)] ), + ], + "miharigoya_scene": [ + ( RecordType.Texture, 0x01350, [SCENE_TEXTURE_LIST + 25 * 0x4], [] ), + ( RecordType.Texture, 0x02350, [SCENE_TEXTURE_LIST + 24 * 0x4], [] ), + ( RecordType.Texture, 0x03550, [SCENE_TEXTURE_LIST + 23 * 0x4], [] ), + ( RecordType.Texture, 0x06550, [SCENE_TEXTURE_LIST + 22 * 0x4], [] ), + ], + "nakaniwa_scene": [ + ( RecordType.CutsceneData, 0x00104, [], [(0xEFE0FA, 0xEFE0FE)] ), + ( RecordType.CutsceneData, 0x00444, [], [(0xEFE09A, 0xEFE09E)] ), + ( RecordType.CutsceneData, 0x03994, [], [(0xEFCEF2, 0xEFCEF6)] ), + ( RecordType.CutsceneData, 0x02524, [], [(0xD12E72, 0xD12E76)] ), + ], + "souko_scene": [ + ( RecordType.Texture, 0x05210, [SCENE_TEXTURE_LIST + 20 * 0x4], [] ), + ( RecordType.Texture, 0x05010, [SCENE_TEXTURE_LIST + 21 * 0x4], [] ), + ], + "tokinoma_scene": [ + ( RecordType.CutsceneData, 0x046F0, [], [(0xC8056E, 0xC80572)] ), + ( RecordType.CutsceneData, 0x06D20, [], [(0xC8060E, 0xC80612)] ), + ( RecordType.CutsceneData, 0x0CE00, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (6 * 0x8) + 0x4], [] ), + ], + "yousei_izumi_yoko_scene": [ + ( RecordType.CutsceneData, 0x00160, [], [(0xC8991A, 0xC8991E)] ), + ( RecordType.CutsceneData, 0x01020, [], [(0xC8995E, 0xC89962)] ), + ( RecordType.CutsceneData, 0x01F40, [], [(0xC899A2, 0xC899A6)] ), + ], + "hakaana_ouke_scene": [ + ( RecordType.CutsceneData, 0x024A0, [], [(0xE09F6E, 0xE09F72)] ), + ( RecordType.CutsceneData, 0x028E0, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (23 * 0x8) + 0x4], [] ), + ], + "ganon_tou_scene": [ + ( RecordType.CutsceneData, 0x02640, [], [(0xE2B502, 0xE2B506)] ), + ( RecordType.CutsceneData, 0x04280, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (22 * 0x8) + 0x4], [] ), + ], + "spot00_scene": [ + ( RecordType.CutsceneData, 0x0BB80, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (12 * 0x8) + 0x4], [] ), + ( RecordType.CutsceneData, 0x0F870, [], [(0xDB529A, 0xDB529E)] ), + ( RecordType.CutsceneData, 0x0FF00, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (9 * 0x8) + 0x4], [] ), + ( RecordType.CutsceneData, 0x13AA0, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (0 * 0x8) + 0x4], [] ), + ( RecordType.CutsceneData, 0x0F9E0, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (8 * 0x8) + 0x4], [] ), + ( RecordType.CutsceneData, 0x10550, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (10 * 0x8) + 0x4], [] ), + ( RecordType.CutsceneData, 0x10B30, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (11 * 0x8) + 0x4], [] ), + ], + "spot00_room_0": [ + ( RecordType.Dlist, 0x12B20, [], [(0xAFC812, 0xAFC816)] ), + ], + "spot01_scene": [ + ( RecordType.Texture, 0x15B50, [SCENE_TEXTURE_LIST + 30 * 0x4], [] ), + ( RecordType.Texture, 0x16B50, [SCENE_TEXTURE_LIST + 31 * 0x4], [] ), + ( RecordType.CutsceneData, 0x0A540, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (2 * 0x8) + 0x4], [] ), + ], + "spot02_scene": [ + ( RecordType.CutsceneData, 0x03C80, [], [(0xE09D42, 0xE09D46)] ), + ( RecordType.CutsceneData, 0x05020, [], [(0xE09D32, 0xE09D36)] ), + ( RecordType.CutsceneData, 0x070C0, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (18 * 0x8) + 0x4], [] ), + ], + "spot04_scene": [ + ( RecordType.CutsceneData, 0xC9D0, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (33 * 0x8) + 0x4], [] ), + ( RecordType.CutsceneData, 0x10E20, [], [(0xE297AE, 0xE297B6)] ), + ], + "spot06_scene": [ + ( RecordType.CutsceneData, 0x07020, [], [(0xE9E222, 0xE9E226)] ), + ( RecordType.CutsceneData, 0x07A30, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (13 * 0x8) + 0x4], [] ), + ( RecordType.CutsceneData, 0x1B0C0, [], [(0xE31302, 0xE31306)] ), + ], + "spot07_scene": [ + ( RecordType.Texture, 0x08F98, [SCENE_TEXTURE_LIST + 32 * 0x4], [] ), + ( RecordType.Texture, 0x08FD8, [SCENE_TEXTURE_LIST + 33 * 0x4], [] ), + ( RecordType.CutsceneData, 0x03D70, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (3 * 0x8) + 0x4], [] ), + ], + "spot08_scene": [ + ( RecordType.CutsceneData, 0x04A80, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (19 * 0x8) + 0x4], [] ), + ], + "spot09_scene": [ + ( RecordType.CutsceneData, 0x02AC0, [], [(0xC1CCC6, 0xC1CCCA)] ), + ( RecordType.CutsceneData, 0x00230, [], [(0xC1CD12, 0xC1CD16)] ), + ( RecordType.CutsceneData, 0x031E0, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (14 * 0x8) + 0x4], [] ), + ], + "spot11_scene": [ + ( RecordType.CutsceneData, 0x07990, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (20 * 0x8) + 0x4], [] ), + ], + "spot12_scene": [ + ( RecordType.Texture, 0x09678, [SCENE_TEXTURE_LIST + 34 * 0x4], [] ), + ( RecordType.Texture, 0x0DE78, [SCENE_TEXTURE_LIST + 35 * 0x4], [] ), + ( RecordType.CutsceneData, 0x055C0, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (31 * 0x8) + 0x4], [] ), + ( RecordType.CutsceneData, 0x06490, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (15 * 0x8) + 0x4], [] ), + ], + "spot15_scene": [ + ( RecordType.CutsceneData, 0x03F40, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (4 * 0x8) + 0x4], [] ), + ], + "spot16_scene": [ + ( RecordType.CutsceneData, 0x1E6A0, [], [(0xE31342, 0xE31346)] ), + ( RecordType.CutsceneData, 0x07EA0, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (1 * 0x8) + 0x4], [] ), + ], + "spot16_room_0": [ + ( RecordType.Dlist, 0x0A9C8, [], [(0xAFDF2E, 0xAFDF32)] ), + ], + "spot17_scene": [ + ( RecordType.CutsceneData, 0x045D0, [], [(0xC7BBFE, 0xC7BC02)] ), + ( RecordType.CutsceneData, 0x076D0, [ + ENTRANCE_CUTSCENE_TABLE_ADDRESS + (21 * 0x8) + 0x4, + ENTRANCE_CUTSCENE_TABLE_ADDRESS + (32 * 0x8) + 0x4, + ], [] ), + ], + "spot18_scene": [ + ( RecordType.Texture, 0x08FC8, [SCENE_TEXTURE_LIST + 37 * 0x4], [] ), + ( RecordType.Texture, 0x09808, [SCENE_TEXTURE_LIST + 36 * 0x4], [] ), + ( RecordType.CutsceneData, 0x059E0, [], [(0xCF14DA, 0xCF14DE)] ), + ( RecordType.CutsceneData, 0x06930, [], [(0xCF11C6, 0xCF11D6)] ), + ( RecordType.CutsceneData, 0x07DE0, [], [(0xCF1436, 0xCF143A)] ), + ( RecordType.CutsceneData, 0x08400, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (5 * 0x8) + 0x4], [] ), + ], + "spot20_scene": [ + ( RecordType.Texture, 0x08180, [SCENE_TEXTURE_LIST + 38 * 0x4], [] ), + ( RecordType.Texture, 0x0FB80, [SCENE_TEXTURE_LIST + 39 * 0x4], [] ), + ( RecordType.CutsceneData, 0x05B70, [ENTRANCE_CUTSCENE_TABLE_ADDRESS + (16 * 0x8) + 0x4], [] ), + ], }