oot/tools/assets/extract/extase_oot64/scene_commands_resource.py

718 lines
30 KiB
Python

# 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",)