# SPDX-FileCopyrightText: © 2025 ZeldaRET # SPDX-License-Identifier: CC0-1.0 import enum import struct from typing import TYPE_CHECKING if TYPE_CHECKING: from ..extase.memorymap import MemoryContext from ..extase import ( File, Resource, RESOURCE_PARSE_SUCCESS, ResourceParseInProgress, ResourceParseWaiting, ) from ..extase.cdata_resources import ( CDataArrayResource, CDataExt_Value, CDataExtWriteContext, ) from .. import oot64_data from . import scene_rooms_resources from . import collision_resources from . import room_shape_resources from . import misc_resources def _SHIFTR(v: int, s: int, w: int): assert isinstance(v, int) assert isinstance(s, int) assert isinstance(w, int) assert v >= 0 assert s >= 0 assert w >= 1 return (v >> s) & ((1 << w) - 1) VERBOSE_NOT_FULLY_PARSED_SCENECMD = False class SceneCmdId(enum.Enum): # keep the SCENE_CMD_ID_ prefix for grepability SCENE_CMD_ID_PLAYER_ENTRY_LIST = 0 SCENE_CMD_ID_ACTOR_LIST = enum.auto() SCENE_CMD_ID_UNUSED_2 = enum.auto() SCENE_CMD_ID_COLLISION_HEADER = enum.auto() SCENE_CMD_ID_ROOM_LIST = enum.auto() SCENE_CMD_ID_WIND_SETTINGS = enum.auto() SCENE_CMD_ID_SPAWN_LIST = enum.auto() SCENE_CMD_ID_SPECIAL_FILES = enum.auto() SCENE_CMD_ID_ROOM_BEHAVIOR = enum.auto() SCENE_CMD_ID_UNDEFINED_9 = enum.auto() SCENE_CMD_ID_ROOM_SHAPE = enum.auto() SCENE_CMD_ID_OBJECT_LIST = enum.auto() SCENE_CMD_ID_LIGHT_LIST = enum.auto() SCENE_CMD_ID_PATH_LIST = enum.auto() SCENE_CMD_ID_TRANSITION_ACTOR_LIST = enum.auto() SCENE_CMD_ID_LIGHT_SETTINGS_LIST = enum.auto() SCENE_CMD_ID_TIME_SETTINGS = enum.auto() SCENE_CMD_ID_SKYBOX_SETTINGS = enum.auto() SCENE_CMD_ID_SKYBOX_DISABLES = enum.auto() SCENE_CMD_ID_EXIT_LIST = enum.auto() SCENE_CMD_ID_END = enum.auto() SCENE_CMD_ID_SOUND_SETTINGS = enum.auto() SCENE_CMD_ID_ECHO_SETTINGS = enum.auto() SCENE_CMD_ID_CUTSCENE_DATA = enum.auto() SCENE_CMD_ID_ALTERNATE_HEADER_LIST = enum.auto() SCENE_CMD_ID_MISC_SETTINGS = enum.auto() scene_cmd_macro_name_by_cmd_id = { SceneCmdId.SCENE_CMD_ID_PLAYER_ENTRY_LIST: "SCENE_CMD_PLAYER_ENTRY_LIST", SceneCmdId.SCENE_CMD_ID_ACTOR_LIST: "SCENE_CMD_ACTOR_LIST", SceneCmdId.SCENE_CMD_ID_UNUSED_2: "SCENE_CMD_UNUSED_02", SceneCmdId.SCENE_CMD_ID_COLLISION_HEADER: "SCENE_CMD_COL_HEADER", SceneCmdId.SCENE_CMD_ID_ROOM_LIST: "SCENE_CMD_ROOM_LIST", SceneCmdId.SCENE_CMD_ID_WIND_SETTINGS: "SCENE_CMD_WIND_SETTINGS", SceneCmdId.SCENE_CMD_ID_SPAWN_LIST: "SCENE_CMD_SPAWN_LIST", SceneCmdId.SCENE_CMD_ID_SPECIAL_FILES: "SCENE_CMD_SPECIAL_FILES", SceneCmdId.SCENE_CMD_ID_ROOM_BEHAVIOR: "SCENE_CMD_ROOM_BEHAVIOR", SceneCmdId.SCENE_CMD_ID_UNDEFINED_9: "SCENE_CMD_UNK_09", SceneCmdId.SCENE_CMD_ID_ROOM_SHAPE: "SCENE_CMD_ROOM_SHAPE", SceneCmdId.SCENE_CMD_ID_OBJECT_LIST: "SCENE_CMD_OBJECT_LIST", SceneCmdId.SCENE_CMD_ID_LIGHT_LIST: "SCENE_CMD_LIGHT_LIST", SceneCmdId.SCENE_CMD_ID_PATH_LIST: "SCENE_CMD_PATH_LIST", SceneCmdId.SCENE_CMD_ID_TRANSITION_ACTOR_LIST: "SCENE_CMD_TRANSITION_ACTOR_LIST", SceneCmdId.SCENE_CMD_ID_LIGHT_SETTINGS_LIST: "SCENE_CMD_ENV_LIGHT_SETTINGS", SceneCmdId.SCENE_CMD_ID_TIME_SETTINGS: "SCENE_CMD_TIME_SETTINGS", SceneCmdId.SCENE_CMD_ID_SKYBOX_SETTINGS: "SCENE_CMD_SKYBOX_SETTINGS", SceneCmdId.SCENE_CMD_ID_SKYBOX_DISABLES: "SCENE_CMD_SKYBOX_DISABLES", SceneCmdId.SCENE_CMD_ID_EXIT_LIST: "SCENE_CMD_EXIT_LIST", SceneCmdId.SCENE_CMD_ID_END: "SCENE_CMD_END", SceneCmdId.SCENE_CMD_ID_SOUND_SETTINGS: "SCENE_CMD_SOUND_SETTINGS", SceneCmdId.SCENE_CMD_ID_ECHO_SETTINGS: "SCENE_CMD_ECHO_SETTINGS", SceneCmdId.SCENE_CMD_ID_CUTSCENE_DATA: "SCENE_CMD_CUTSCENE_DATA", SceneCmdId.SCENE_CMD_ID_ALTERNATE_HEADER_LIST: "SCENE_CMD_ALTERNATE_HEADER_LIST", SceneCmdId.SCENE_CMD_ID_MISC_SETTINGS: "SCENE_CMD_MISC_SETTINGS", } class SceneCommandsResource(Resource, can_size_be_unknown=True): def __init__(self, file: File, range_start: int, name: str): super().__init__(file, range_start, None, name) self.parsed_commands: set[SceneCmdId] = set() self.player_entry_list_length = None self.room_list_length = None self.exit_list_length = None def try_parse_data(self, memory_context: "MemoryContext"): data = self.file.data[self.range_start :] new_progress_done = [] waiting_for = [] offset = 0 cmd_id = None end_offset = None found_commands: set[SceneCmdId] = set() while offset + 8 <= len(data): (cmd_id_int, data1, pad2, data2_I) = struct.unpack_from( ">BBHI", data, offset ) (_, data2_H0, data2_H1) = struct.unpack_from(">IHH", data, offset) (_, data2_B0, data2_B1, data2_B2, data2_B3) = struct.unpack_from( ">IBBBB", data, offset ) offset += 8 cmd_id = SceneCmdId(cmd_id_int) assert pad2 == 0 found_commands.add(cmd_id) if cmd_id == SceneCmdId.SCENE_CMD_ID_END: assert data1 == 0 assert data2_I == 0 end_offset = offset self.parsed_commands.add(cmd_id) break if cmd_id in self.parsed_commands: continue if cmd_id == SceneCmdId.SCENE_CMD_ID_ACTOR_LIST: resource = memory_context.report_resource_at_segmented( self, data2_I, scene_rooms_resources.ActorEntryListResource, lambda file, offset: scene_rooms_resources.ActorEntryListResource( file, offset, f"{self.name}_{data2_I:08X}_ActorEntryList" ), ) resource.set_length(data1) self.parsed_commands.add(cmd_id) new_progress_done.append(("reported ActorEntryListResource", cmd_id)) if cmd_id == SceneCmdId.SCENE_CMD_ID_OBJECT_LIST: resource = memory_context.report_resource_at_segmented( self, data2_I, scene_rooms_resources.ObjectListResource, lambda file, offset: scene_rooms_resources.ObjectListResource( file, offset, f"{self.name}_{data2_I:08X}_ObjectList" ), ) resource.set_length(data1) self.parsed_commands.add(cmd_id) new_progress_done.append(("reported ObjectListResource", cmd_id)) if cmd_id == SceneCmdId.SCENE_CMD_ID_ROOM_SHAPE: room_shape_resources.report_room_shape_at_segmented( self, memory_context, data2_I, self.name ) self.parsed_commands.add(cmd_id) new_progress_done.append(("reported room shape resource", cmd_id)) if cmd_id == SceneCmdId.SCENE_CMD_ID_ROOM_LIST: self.room_list_length = data1 resource = memory_context.report_resource_at_segmented( self, data2_I, scene_rooms_resources.RoomListResource, lambda file, offset: scene_rooms_resources.RoomListResource( file, offset, f"{self.name}_{data2_I:08X}_RoomList" ), ) resource.set_length(data1) self.parsed_commands.add(cmd_id) new_progress_done.append(("reported RoomListResource", cmd_id)) if cmd_id == SceneCmdId.SCENE_CMD_ID_COLLISION_HEADER: assert data1 == 0 resource = memory_context.report_resource_at_segmented( self, data2_I, collision_resources.CollisionResource, lambda file, offset: collision_resources.CollisionResource( file, offset, f"{self.name}_{data2_I:08X}_Col" ), ) new_progress_done.append(("reported CollisionResource", cmd_id)) if resource.is_data_parsed: self.exit_list_length = resource.length_exitList self.parsed_commands.add(cmd_id) new_progress_done.append( ("set self.exit_list_length from CollisionResource", cmd_id) ) else: waiting_for.append( ( "CollisionResource to be parsed to set self.exit_list_length", cmd_id, resource, ) ) if cmd_id == SceneCmdId.SCENE_CMD_ID_SPAWN_LIST: assert data1 == 0 resource = memory_context.report_resource_at_segmented( self, data2_I, scene_rooms_resources.SpawnListResource, lambda file, offset: scene_rooms_resources.SpawnListResource( file, offset, f"{self.name}_{data2_I:08X}_SpawnList" ), ) new_progress_done.append(("reported SpawnListResource", cmd_id)) if ( self.player_entry_list_length is not None and self.room_list_length is not None ): resource.player_entry_list_length = self.player_entry_list_length resource.room_list_length = self.room_list_length self.parsed_commands.add(cmd_id) new_progress_done.append( ("passed lengths to SpawnListResource", cmd_id) ) else: waiting_for.append( ( "self.player_entry_list_length and self.room_list_length" " to pass to SpawnListResource", cmd_id, ) ) if cmd_id == SceneCmdId.SCENE_CMD_ID_PLAYER_ENTRY_LIST: self.player_entry_list_length = data1 resource = memory_context.report_resource_at_segmented( self, data2_I, scene_rooms_resources.ActorEntryListResource, lambda file, offset: scene_rooms_resources.ActorEntryListResource( file, offset, f"{self.name}_{data2_I:08X}_PlayerEntryList" ), ) resource.set_length(data1) self.parsed_commands.add(cmd_id) new_progress_done.append(("reported ActorEntryListResource", cmd_id)) if cmd_id == SceneCmdId.SCENE_CMD_ID_EXIT_LIST: # TODO length from collision assert data1 == 0 resource = memory_context.report_resource_at_segmented( self, data2_I, scene_rooms_resources.ExitListResource, lambda file, offset: scene_rooms_resources.ExitListResource( file, offset, f"{self.name}_{data2_I:08X}_ExitList" ), ) new_progress_done.append(("reported ExitListResource", cmd_id)) if self.exit_list_length is not None: # TODO this doesnt work very well, eg need to trim to avoid overlaps length = self.exit_list_length # blindly align length to 2 (could/should check for zeros) length = max(2, (length + 1) // 2 * 2) # trim based on overlaps while True: _, other_resource = resource.file.get_resource_at( resource.range_start + length * resource.elem_cdata_ext.size - 1 ) if other_resource is resource: break length -= 2 assert length > 0 resource.set_length(length) self.parsed_commands.add(cmd_id) new_progress_done.append( ("passed length to ExitListResource", cmd_id, resource) ) else: waiting_for.append( ( "self.exit_list_length to (guess a length to) pass to ExitListResource", cmd_id, ) ) if cmd_id == SceneCmdId.SCENE_CMD_ID_LIGHT_SETTINGS_LIST: resource = memory_context.report_resource_at_segmented( self, data2_I, scene_rooms_resources.EnvLightSettingsListResource, lambda file, offset: scene_rooms_resources.EnvLightSettingsListResource( file, offset, f"{self.name}_{data2_I:08X}_EnvLightSettingsList" ), ) resource.set_length(data1) self.parsed_commands.add(cmd_id) new_progress_done.append( ("reported EnvLightSettingsListResource", cmd_id) ) if cmd_id == SceneCmdId.SCENE_CMD_ID_TRANSITION_ACTOR_LIST: resource = memory_context.report_resource_at_segmented( self, data2_I, scene_rooms_resources.TransitionActorEntryListResource, lambda file, offset: scene_rooms_resources.TransitionActorEntryListResource( file, offset, f"{self.name}_{data2_I:08X}_TransitionActorEntryList", ), ) resource.set_length(data1) self.parsed_commands.add(cmd_id) new_progress_done.append( ("reported TransitionActorEntryListResource", cmd_id) ) if cmd_id == SceneCmdId.SCENE_CMD_ID_PATH_LIST: assert data1 == 0 memory_context.report_resource_at_segmented( self, data2_I, scene_rooms_resources.PathListResource, lambda file, offset: scene_rooms_resources.PathListResource( file, offset, f"{self.name}_{data2_I:08X}_PathList" ), ) self.parsed_commands.add(cmd_id) new_progress_done.append(("reported PathListResource", cmd_id)) if cmd_id == SceneCmdId.SCENE_CMD_ID_ALTERNATE_HEADER_LIST: assert data1 == 0 memory_context.report_resource_at_segmented( self, data2_I, AltHeadersResource, lambda file, offset: AltHeadersResource( file, offset, f"{self.name}_{data2_I:08X}_AltHeaders" ), ) self.parsed_commands.add(cmd_id) new_progress_done.append(("reported AltHeadersResource", cmd_id)) if cmd_id == SceneCmdId.SCENE_CMD_ID_CUTSCENE_DATA: assert data1 == 0 memory_context.report_resource_at_segmented( self, data2_I, misc_resources.CutsceneResource, lambda file, offset: misc_resources.CutsceneResource( file, offset, f"{self.name}_{data2_I:08X}_Cs" ), ) self.parsed_commands.add(cmd_id) new_progress_done.append(("reported CutsceneResource", cmd_id)) if cmd_id != SceneCmdId.SCENE_CMD_ID_END: raise Exception("reached end of data without encountering end marker") assert end_offset is not None # Nothing to parse for these commands found_commands.discard(SceneCmdId.SCENE_CMD_ID_SOUND_SETTINGS) found_commands.discard(SceneCmdId.SCENE_CMD_ID_MISC_SETTINGS) found_commands.discard(SceneCmdId.SCENE_CMD_ID_SPECIAL_FILES) found_commands.discard(SceneCmdId.SCENE_CMD_ID_SKYBOX_SETTINGS) found_commands.discard(SceneCmdId.SCENE_CMD_ID_TIME_SETTINGS) found_commands.discard(SceneCmdId.SCENE_CMD_ID_ROOM_BEHAVIOR) found_commands.discard(SceneCmdId.SCENE_CMD_ID_ECHO_SETTINGS) found_commands.discard(SceneCmdId.SCENE_CMD_ID_SKYBOX_DISABLES) found_commands.discard(SceneCmdId.SCENE_CMD_ID_UNDEFINED_9) found_commands.discard(SceneCmdId.SCENE_CMD_ID_WIND_SETTINGS) self.range_end = self.range_start + end_offset assert self.parsed_commands.issubset(found_commands) if found_commands - self.parsed_commands: if VERBOSE_NOT_FULLY_PARSED_SCENECMD: print( "NOT FULLY PARSED:", self, "Found commands:", found_commands, "Parsed commands:", self.parsed_commands, "FOUND BUT NOT PARSED:", found_commands - self.parsed_commands, ) if waiting_for: if new_progress_done: raise ResourceParseInProgress( new_progress_done=new_progress_done, waiting_for=waiting_for ) else: raise ResourceParseWaiting(waiting_for=waiting_for) assert self.parsed_commands == found_commands return RESOURCE_PARSE_SUCCESS def get_c_declaration_base(self): return f"SceneCmd {self.symbol_name}[]" def write_extracted(self, memory_context): data = self.file.data[self.range_start : self.range_end] with self.extract_to_path.open("w") as f: if not self.braces_in_source: f.write("{\n") for offset in range(0, len(data), 8): (cmd_id_int, data1, pad2, data2_I) = struct.unpack_from( ">BBHI", data, offset ) (_, data2_H0, data2_H1) = struct.unpack_from(">IHH", data, offset) (_, data2_B0, data2_B1, data2_B2, data2_B3) = struct.unpack_from( ">IBBBB", data, offset ) cmd_id = SceneCmdId(cmd_id_int) f.write(" " * 4) f.write(scene_cmd_macro_name_by_cmd_id[cmd_id]) f.write("(") if cmd_id == SceneCmdId.SCENE_CMD_ID_PLAYER_ENTRY_LIST: address = data2_I f.write( memory_context.get_c_expression_length_at_segmented(address) ) f.write(", ") f.write(memory_context.get_c_reference_at_segmented(address)) if cmd_id == SceneCmdId.SCENE_CMD_ID_ACTOR_LIST: address = data2_I f.write( memory_context.get_c_expression_length_at_segmented(address) ) f.write(", ") f.write(memory_context.get_c_reference_at_segmented(address)) if cmd_id == SceneCmdId.SCENE_CMD_ID_UNUSED_2: raise NotImplementedError(cmd_id) if cmd_id == SceneCmdId.SCENE_CMD_ID_COLLISION_HEADER: assert data1 == 0 address = data2_I f.write(memory_context.get_c_reference_at_segmented(address)) if cmd_id == SceneCmdId.SCENE_CMD_ID_ROOM_LIST: address = data2_I f.write( memory_context.get_c_expression_length_at_segmented(address) ) f.write(", ") f.write(memory_context.get_c_reference_at_segmented(address)) if cmd_id == SceneCmdId.SCENE_CMD_ID_WIND_SETTINGS: assert data1 == 0 # TODO cast x,y,z to s8 xDir = data2_B0 yDir = data2_B1 zDir = data2_B2 strength = data2_B3 f.write(f"{xDir}, {yDir}, {zDir}, {strength}") if cmd_id == SceneCmdId.SCENE_CMD_ID_SPAWN_LIST: assert data1 == 0 address = data2_I f.write(memory_context.get_c_reference_at_segmented(address)) if cmd_id == SceneCmdId.SCENE_CMD_ID_SPECIAL_FILES: naviQuestHintFileId = data1 keepObjectId = data2_I f.write( f"{oot64_data.get_navi_quest_hint_file_id_name(naviQuestHintFileId)}, " f"{oot64_data.get_object_id_name(keepObjectId)}" ) if cmd_id == SceneCmdId.SCENE_CMD_ID_ROOM_BEHAVIOR: gpFlags1 = data1 gpFlags2 = data2_I behaviorType1 = gpFlags1 behaviorType2 = _SHIFTR(gpFlags2, 0, 8) lensMode = _SHIFTR(gpFlags2, 8, 1) disableWarpSongs = _SHIFTR(gpFlags2, 10, 1) behaviorType1_name = oot64_data.get_room_behavior_type1_name( behaviorType1 ) behaviorType2_name = oot64_data.get_room_behavior_type2_name( behaviorType2 ) lensMode_name = oot64_data.get_lens_mode_name(lensMode) disableWarpSongs_name = ( "true /* no warp songs */" if disableWarpSongs else "false /* warp songs enabled */" ) f.write( f"{behaviorType1_name}, {behaviorType2_name}, {lensMode_name}, {disableWarpSongs_name}" ) if cmd_id == SceneCmdId.SCENE_CMD_ID_UNDEFINED_9: assert data1 == 0 assert data2_I == 0 if cmd_id == SceneCmdId.SCENE_CMD_ID_ROOM_SHAPE: assert data1 == 0 # TODO these asserts should be done on parse? address = data2_I f.write(memory_context.get_c_reference_at_segmented(address)) if cmd_id == SceneCmdId.SCENE_CMD_ID_OBJECT_LIST: address = data2_I f.write( memory_context.get_c_expression_length_at_segmented(address) ) f.write(", ") f.write(memory_context.get_c_reference_at_segmented(address)) if cmd_id == SceneCmdId.SCENE_CMD_ID_LIGHT_LIST: raise NotImplementedError(cmd_id) if cmd_id == SceneCmdId.SCENE_CMD_ID_PATH_LIST: assert data1 == 0 address = data2_I f.write(memory_context.get_c_reference_at_segmented(address)) if cmd_id == SceneCmdId.SCENE_CMD_ID_TRANSITION_ACTOR_LIST: address = data2_I f.write( memory_context.get_c_expression_length_at_segmented(address) ) f.write(", ") f.write(memory_context.get_c_reference_at_segmented(address)) if cmd_id == SceneCmdId.SCENE_CMD_ID_LIGHT_SETTINGS_LIST: address = data2_I f.write( memory_context.get_c_expression_length_at_segmented(address) ) f.write(", ") f.write(memory_context.get_c_reference_at_segmented(address)) if cmd_id == SceneCmdId.SCENE_CMD_ID_TIME_SETTINGS: assert data1 == 0 hour = data2_B0 min_ = data2_B1 timeSpeed = data2_B2 assert data2_B3 == 0 hour_str = "0xFF" if hour == 0xFF else str(hour) min_str = "0xFF" if min_ == 0xFF else str(min_) timeSpeed_str = "0xFF" if timeSpeed == 0xFF else str(timeSpeed) if hour == 0xFF or min_ == 0xFF: f.write("/* don't set time */ ") f.write(f"{hour_str}, {min_str}, {timeSpeed_str}") if timeSpeed == 0xFF or timeSpeed == 0: f.write(" /* time doesn't move */") if cmd_id == SceneCmdId.SCENE_CMD_ID_SKYBOX_SETTINGS: assert data1 == 0 skyboxId = data2_B0 skyboxConfig = data2_B1 envLightMode = data2_B2 assert data2_B3 == 0 f.write( f"{oot64_data.get_skybox_id(skyboxId)}, " f"{skyboxConfig}, " f"{oot64_data.get_light_mode(envLightMode)}" ) if cmd_id == SceneCmdId.SCENE_CMD_ID_SKYBOX_DISABLES: assert data1 == 0 skyboxDisabled = data2_B0 sunMoonDisabled = data2_B1 assert data2_B2 == data2_B3 == 0 skyboxDisabled_name = ( "true /* no skybox */" if skyboxDisabled else "false /* skybox enabled */" ) sunMoonDisabled_name = ( "true /* no sun/moon */" if sunMoonDisabled else "false /* sun/moon enabled */" ) f.write(f"{skyboxDisabled_name}, {sunMoonDisabled_name}") if cmd_id == SceneCmdId.SCENE_CMD_ID_EXIT_LIST: assert data1 == 0 address = data2_I f.write(memory_context.get_c_reference_at_segmented(address)) if cmd_id == SceneCmdId.SCENE_CMD_ID_END: assert data1 == 0 assert data2_I == 0 if cmd_id == SceneCmdId.SCENE_CMD_ID_SOUND_SETTINGS: specId = data1 assert data2_B0 == 0 assert data2_B1 == 0 natureAmbienceId = data2_B2 seqId = data2_B3 natureAmbienceId_name = oot64_data.get_nature_ambience_id_name( natureAmbienceId ) seqId_name = oot64_data.get_sequence_id_name(seqId) f.write(f"{specId}, {natureAmbienceId_name}, {seqId_name}") if cmd_id == SceneCmdId.SCENE_CMD_ID_ECHO_SETTINGS: assert data1 == 0 assert data2_B0 == data2_B1 == data2_B2 == 0 echo = data2_B3 f.write(f"{echo}") if cmd_id == SceneCmdId.SCENE_CMD_ID_CUTSCENE_DATA: assert data1 == 0 address = data2_I f.write(memory_context.get_c_reference_at_segmented(address)) if cmd_id == SceneCmdId.SCENE_CMD_ID_ALTERNATE_HEADER_LIST: assert data1 == 0 address = data2_I f.write(memory_context.get_c_reference_at_segmented(address)) if cmd_id == SceneCmdId.SCENE_CMD_ID_MISC_SETTINGS: sceneCamType = data1 worldMapLocation = data2_I sceneCamType_name = oot64_data.get_scene_cam_type_name(sceneCamType) f.write(f"{sceneCamType_name}, {worldMapLocation}") f.write("),\n") if not self.braces_in_source: f.write("}\n") def get_c_reference(self, resource_offset: int): if resource_offset == 0: return self.symbol_name else: raise ValueError def get_c_includes(self): return ( "array_count.h", "object.h", # for OBJECT_* # TODO these are not always needed: "sequence.h", # for NATURE_ID_* and NA_BGM_* "skybox.h", # for SKYBOX_* ) def get_h_includes(self): return ("scene.h",) class AltHeadersResource(CDataArrayResource): def report_elem(resource, memory_context: "MemoryContext", v): assert isinstance(v, int) address = v if address != 0: memory_context.report_resource_at_segmented( resource, address, SceneCommandsResource, lambda file, offset: SceneCommandsResource( file, offset, f"{resource.name}_{address:08X}_Cmds" ), ) def write_elem( resource, memory_context: "MemoryContext", v, wctx: CDataExtWriteContext ): assert isinstance(v, int) address = v wctx.f.write(wctx.line_prefix) if address == 0: wctx.f.write("NULL") else: wctx.f.write(memory_context.get_c_reference_at_segmented(address)) return True elem_cdata_ext = ( CDataExt_Value("I").set_report(report_elem).set_write(write_elem) ) # SceneCmd* def try_parse_data(self, memory_context): length = 0 for i, (v,) in enumerate( struct.iter_unpack(">I", self.file.data[self.range_start :]) ): if v == 0: if i < 3: length = i + 1 continue else: break try: resolve_result = memory_context.resolve_segmented(v) except: break data_may_be_scenecmds = False for j in range( resolve_result.file_offset, len(resolve_result.file.data), 8 ): cmd_id_int = resolve_result.file.data[j] try: cmd_id = SceneCmdId(cmd_id_int) except ValueError: break if cmd_id == SceneCmdId.SCENE_CMD_ID_END: data_may_be_scenecmds = True break if not data_may_be_scenecmds: break length = i + 1 assert length > 0 self.set_length(length) return super().try_parse_data(memory_context) def get_c_declaration_base(self): return f"SceneCmd* {self.symbol_name}[]" def get_h_includes(self): return ("scene.h",)