tools/assets cleanup pass (mostly typing) (#2705)

* address easy todos

* add type asserts

* format

* add type hints

* more typing fixes

* more asserts for typing

* break long f string
This commit is contained in:
Dragorn421
2026-04-12 23:18:18 +02:00
committed by GitHub
parent 63df17a235
commit ab79222574
15 changed files with 143 additions and 71 deletions
+11 -19
View File
@@ -16,9 +16,6 @@ from .base import (
)
from . import xml_errors
# TODO remove
STATIC_ATTRIB = {"Static"}
class GfxMicroCode(enum.Enum):
F3DEX = enum.auto()
@@ -33,9 +30,7 @@ class DListResourceDesc(ResourceDesc):
def handler_DList(symbol_name, offset, collection, reselem: Element):
xml_errors.check_attrib(
reselem, {"Name", "Offset"}, {"Ucode", "RawPointers"} | STATIC_ATTRIB
)
xml_errors.check_attrib(reselem, {"Name", "Offset"}, {"Ucode", "RawPointers"})
if "Ucode" in reselem.attrib:
ucode = GfxMicroCode[reselem.attrib["Ucode"].upper()]
else:
@@ -54,7 +49,7 @@ class BlobResourceDesc(ResourceDesc):
def handler_Blob(symbol_name, offset, collection, reselem: Element):
xml_errors.check_attrib(reselem, {"Name", "Offset", "Size"}, STATIC_ATTRIB)
xml_errors.check_attrib(reselem, {"Name", "Offset", "Size"})
size = int(reselem.attrib["Size"], 16)
return BlobResourceDesc(symbol_name, offset, collection, reselem, size)
@@ -65,7 +60,7 @@ class MtxResourceDesc(ResourceDesc):
def handler_Mtx(symbol_name, offset, collection, reselem: Element):
xml_errors.check_attrib(reselem, {"Name", "Offset"}, STATIC_ATTRIB)
xml_errors.check_attrib(reselem, {"Name", "Offset"})
return MtxResourceDesc(symbol_name, offset, collection, reselem)
@@ -85,7 +80,7 @@ class VtxArrayResourceDesc(ResourceDesc):
def handler_Array(symbol_name, offset, collection, reselem: Element):
xml_errors.check_attrib(reselem, {"Name", "Offset", "Count"}, STATIC_ATTRIB)
xml_errors.check_attrib(reselem, {"Name", "Offset", "Count"})
count = int(reselem.attrib["Count"])
assert len(reselem) == 1, "Expected exactly one child of Array node"
array_elem = reselem[0]
@@ -138,16 +133,14 @@ def handler_Texture(
xml_errors.check_attrib(
reselem,
{"Name", "Offset", "Format", "Width", "Height"},
# TODO remove OutName, SplitTlut
# TODO remove SplitTlut
{
"OutName",
"SplitTlut",
"TlutOffset",
"ExternalTlut",
"ExternalTlutOffset",
"HackMode",
}
| STATIC_ATTRIB,
},
)
format = TextureFormat[reselem.attrib["Format"].upper()]
width = int(reselem.attrib["Width"])
@@ -170,8 +163,8 @@ def handler_Texture(
xml_errors.check_attrib(
reselem,
{"Name", "Offset", "Format", "Width", "Height", "TlutOffset"},
# TODO remove OutName, SplitTlut
{"OutName", "SplitTlut", "HackMode"} | STATIC_ATTRIB,
# TODO remove SplitTlut
{"SplitTlut", "HackMode"},
)
tlut_offset = int(reselem.attrib["TlutOffset"], 16)
@@ -200,8 +193,8 @@ def handler_Texture(
"ExternalTlut",
"ExternalTlutOffset",
},
# TODO remove OutName, SplitTlut
{"OutName", "SplitTlut", "HackMode"} | STATIC_ATTRIB,
# TODO remove SplitTlut
{"SplitTlut", "HackMode"},
)
external_tlut_file = reselem.attrib["ExternalTlut"]
external_tlut_offset = int(reselem.attrib["ExternalTlutOffset"], 16)
@@ -230,8 +223,7 @@ def handler_Texture(
xml_errors.check_attrib(
reselem,
{"Name", "Offset", "Format", "Width", "Height"},
# TODO remove OutName
{"OutName", "HackMode"} | STATIC_ATTRIB,
{"HackMode"},
)
res = TextureResourceDesc(
symbol_name, offset, collection, reselem, format, width, height
+31 -9
View File
@@ -8,7 +8,7 @@ import enum
import reprlib
import io
from typing import TYPE_CHECKING, Sequence, Optional, Union, Any, Iterable
from typing import TYPE_CHECKING, Sequence, Optional, Union, Any, Iterable, Generator
from pprint import pprint
@@ -398,6 +398,8 @@ class File:
# Ignore markers falling within existing resources
result, resource = self.get_resource_at(file_start)
if result == GetResourceAtResult.DEFINITIVE:
assert resource is not None
assert resource.range_end is not None
if resource.range_start <= file_start < file_end <= resource.range_end:
assert isinstance(resource, resource_type)
resource.reporters.add(reporter)
@@ -491,6 +493,8 @@ class File:
self.add_resource(resource)
else:
assert result == GetResourceAtResult.DEFINITIVE
assert resource is not None
assert resource.range_end is not None
assert (
resource.range_start
<= rbm.file_start
@@ -515,6 +519,7 @@ class File:
unaccounted_resources: list[Resource] = []
def add_unaccounted(range_start, range_end):
assert self.data is not None
if I_D_OMEGALUL:
# IDO aligns every declaration to 4, so declaring zeros
# that is actually padding for that purpose throws off matching.
@@ -577,6 +582,10 @@ class File:
# Add unaccounted if needed at the end of the file
resource_last = self._resources[-1]
assert resource_last.range_end is not None, (
"add_unaccounted_resources should be called once all"
" resources are parsed and have a definitive range"
)
if resource_last.range_end < len(self.data):
add_unaccounted(
resource_last.range_end,
@@ -589,6 +598,10 @@ class File:
for i in range(1, len(self._resources)):
resource_a = self._resources[i - 1]
resource_b = self._resources[i]
assert resource_a.range_end is not None, (
"add_unaccounted_resources should be called once all"
" resources are parsed and have a definitive range"
)
assert resource_a.range_end <= resource_b.range_start
# Add unaccounted if needed between two successive resources
@@ -641,11 +654,12 @@ class File:
self.source_h_path = source_path / f"{file_name}.h"
def write_source(self):
def strip_extracted_prefix(path : Path) -> Path:
def strip_extracted_prefix(path: Path) -> Path:
parts = path.parts
if parts[0] == "extracted":
return Path(*parts[2:]) # Skip first two parts
return path # Return original path if condition not met
assert hasattr(
self, "source_c_path"
), "set_source_path must be called before write_source"
@@ -665,7 +679,9 @@ class File:
referenced_file,
)
assert hasattr(referenced_file, "source_h_path")
file_include_paths_complete.append(strip_extracted_prefix(referenced_file.source_h_path))
file_include_paths_complete.append(
strip_extracted_prefix(referenced_file.source_h_path)
)
# Same as file_include_paths_complete,
# but paths that can be are made relative to the source C.
@@ -838,7 +854,7 @@ class Resource(abc.ABC):
TODO figure out what to do with this, for now thinking debugging"""
@abc.abstractmethod
def try_parse_data(self, memory_context: "MemoryContext"):
def try_parse_data(self, memory_context: "MemoryContext") -> object:
"""Parse this resource's data bytes
This can typically result in finding more resources,
@@ -879,7 +895,7 @@ class Resource(abc.ABC):
"""
...
def get_c_expression_length(self, resource_offset: int):
def get_c_expression_length(self, resource_offset: int) -> str:
"""Get a C expression for referencing the length of data in this resource
The offset `resource_offset` is relative to the resource, as in get_c_reference.
@@ -1049,7 +1065,7 @@ class Resource(abc.ABC):
+ ")"
)
def __rich_repr__(self):
def __rich_repr__(self) -> Generator[str | tuple[str, Any], None, None]:
yield self.name
yield (
f"0x{self.range_start:08X}-"
@@ -1070,13 +1086,15 @@ class ZeroPaddingResource(Resource):
*,
include_in_source=True,
):
# TODO move to try_parse_data ?
assert set(file.data[range_start:range_end]) == {0}
super().__init__(file, range_start, range_end, name)
self.include_in_source = include_in_source
def try_parse_data(self, memory_context):
# Nothing specific to do
# Check the data is 0s
assert self.file.data is not None
if set(self.file.data[self.range_start : self.range_end]) != {0}:
raise ResourceParseImpossible("Not zero padding")
return RESOURCE_PARSE_SUCCESS
def get_c_reference(self, resource_offset):
@@ -1090,6 +1108,7 @@ class ZeroPaddingResource(Resource):
pass
def get_c_declaration_base(self):
assert self.range_end is not None
length_bytes = self.range_end - self.range_start
assert length_bytes > 0
return f"u8 {self.symbol_name}[{length_bytes}]"
@@ -1116,6 +1135,7 @@ class BinaryBlobResource(Resource):
return RESOURCE_PARSE_SUCCESS
def get_as_xml(self):
assert self.range_end is not None
return f"""\
<Blob Name="{self.symbol_name}" Size="0x{self.range_end - self.range_start:X}" Offset="0x{self.range_start:X}"/>"""
@@ -1129,6 +1149,8 @@ class BinaryBlobResource(Resource):
return ("ultra64.h",)
def write_extracted(self, memory_context):
assert self.file.data is not None
assert self.range_end is not None
data = self.file.data[self.range_start : self.range_end]
assert len(data) == self.range_end - self.range_start
self.extract_to_path.write_bytes(data)
@@ -168,6 +168,20 @@ class CDataExt_Value(CData_Value, CDataExt):
else:
return False
s8: "CDataExt_Value"
u8: "CDataExt_Value"
s16: "CDataExt_Value"
u16: "CDataExt_Value"
s32: "CDataExt_Value"
u32: "CDataExt_Value"
f32: "CDataExt_Value"
f64: "CDataExt_Value"
pointer: "CDataExt_Value"
pad8: "CDataExt_Value"
pad16: "CDataExt_Value"
pad32: "CDataExt_Value"
CDataExt_Value.s8 = CDataExt_Value("b").freeze()
CDataExt_Value.u8 = CDataExt_Value("B").freeze()
@@ -277,6 +291,7 @@ class CDataResource(Resource):
self._is_cdata_processed = False
def try_parse_data(self, memory_context: "MemoryContext"):
assert self.file.data is not None
if self.can_size_be_unknown:
assert hasattr(self, "cdata_ext") and self.cdata_ext is not None, (
"Subclasses with can_size_be_unknown=True should redefine try_parse_data"
+33 -19
View File
@@ -38,6 +38,16 @@ class CData_Value(CData):
def unpack_from(self, data: memoryview, offset: int = 0):
return self.unpack_struct.unpack_from(data, offset)[0]
s8: "CData_Value"
u8: "CData_Value"
s16: "CData_Value"
u16: "CData_Value"
s32: "CData_Value"
u32: "CData_Value"
f32: "CData_Value"
f64: "CData_Value"
pointer: "CData_Value"
CData_Value.s8 = CData_Value("b")
CData_Value.u8 = CData_Value("B")
@@ -142,26 +152,30 @@ def try_stuff():
} data = { varLenArray, 3, { 421, 0x01020304 } };
"""
array_bytes = bytes(
[
1,
0,
*(0, 2),
3,
0,
*(0, 4),
]
array_bytes = memoryview(
bytes(
[
1,
0,
*(0, 2),
3,
0,
*(0, 4),
]
)
)
varLenArray_bytes = bytes([1, 2, 3])
data_bytes = bytes(
[
*(0x12, 0x34, 0x56, 0x78),
*(0, 3),
0,
0,
*(0, 0, 421 >> 8, 421 & 0xFF),
*(1, 2, 3, 4),
]
varLenArray_bytes = memoryview(bytes([1, 2, 3]))
data_bytes = memoryview(
bytes(
[
*(0x12, 0x34, 0x56, 0x78),
*(0, 3),
0,
0,
*(0, 0, 421 >> 8, 421 & 0xFF),
*(1, 2, 3, 4),
]
)
)
arrayElem_CData_Struct = CData_Struct(
@@ -30,7 +30,7 @@ class AnimationFrameDataResource(CDataResource, can_size_be_unknown=True):
def __init__(self, file: File, range_start: int, name: str):
super().__init__(file, range_start, name)
self.length = None
self.length: int | None = None
def try_parse_data(self, memory_context):
if self.length is not None:
@@ -64,7 +64,7 @@ class AnimationJointIndicesResource(CDataResource, can_size_be_unknown=True):
def __init__(self, file: File, range_start: int, name: str):
super().__init__(file, range_start, name)
self.length = None
self.length: int | None = None
def try_parse_data(self, memory_context):
if self.length is not None:
@@ -42,6 +42,7 @@ class CollisionVtxListResource(CDataResource):
def get_c_declaration_base(self):
if hasattr(self, "HACK_IS_STATIC_ON"):
assert isinstance(self.cdata_ext, CDataExt_Array)
return f"Vec3s {self.symbol_name}[{self.cdata_ext.length}]"
return f"Vec3s {self.symbol_name}[]"
@@ -161,6 +162,7 @@ class CollisionPolyListResource(CDataResource):
def get_c_declaration_base(self):
if hasattr(self, "HACK_IS_STATIC_ON"):
assert isinstance(self.cdata_ext, CDataExt_Array)
return f"CollisionPoly {self.symbol_name}[{self.cdata_ext.length}]"
return f"CollisionPoly {self.symbol_name}[]"
@@ -292,6 +294,7 @@ class CollisionSurfaceTypeListResource(CDataResource):
def get_c_declaration_base(self):
if hasattr(self, "HACK_IS_STATIC_ON"):
assert isinstance(self.cdata_ext, CDataExt_Array)
return f"SurfaceType {self.symbol_name}[{self.cdata_ext.length}]"
return f"SurfaceType {self.symbol_name}[]"
@@ -318,6 +321,7 @@ class BgCamFuncDataResource(CDataResource):
def get_c_declaration_base(self):
if hasattr(self, "HACK_IS_STATIC_ON"):
assert isinstance(self.cdata_ext, CDataExt_Array)
return f"Vec3s {self.symbol_name}[{self.cdata_ext.length}]"
return f"Vec3s {self.symbol_name}[]"
@@ -337,7 +341,7 @@ class BgCamFuncDataResource(CDataResource):
class CollisionBgCamListResource(CDataResource):
def write_bgCamFuncData(
resource: "CollisionSurfaceTypeListResource",
resource: "CollisionBgCamListResource",
memory_context: "MemoryContext",
v,
wctx: CDataExtWriteContext,
@@ -419,6 +423,7 @@ class CollisionBgCamListResource(CDataResource):
def get_c_declaration_base(self):
if hasattr(self, "HACK_IS_STATIC_ON"):
assert isinstance(self.cdata_ext, CDataExt_Array)
return f"BgCamInfo {self.symbol_name}[{self.cdata_ext.length}]"
return f"BgCamInfo {self.symbol_name}[]"
@@ -438,11 +443,19 @@ class CollisionBgCamListResource(CDataResource):
class CollisionWaterBoxesResource(CDataResource):
def write_properties(v):
assert isinstance(v, int)
bgCamIndex = (v >> 0) & 0xFF
lightIndex = (v >> 8) & 0x1F
room = (v >> 13) & 0x3F
setFlag19 = (v >> 19) & 1
return f"WATERBOX_PROPERTIES(/* bgCamIndex */ {bgCamIndex}, /* lightIndex */ {lightIndex}, /* room */ {room}, /* setFlag19 */ {'true' if setFlag19 else 'false'})"
return (
"WATERBOX_PROPERTIES("
f"/* bgCamIndex */ {bgCamIndex}, "
f"/* lightIndex */ {lightIndex}, "
f"/* room */ {room}, "
f"/* setFlag19 */ {'true' if setFlag19 else 'false'}"
")"
)
elem_cdata_ext = CDataExt_Struct(
(
@@ -147,6 +147,7 @@ class VtxArrayResource(CDataResource):
super().__init__(file, range_start, name)
def get_as_xml(self):
assert self.range_end is not None
return f"""\
<Array Name="{self.symbol_name}" Count="{(self.range_end - self.range_start) // self.element_cdata_ext.size}" Offset="0x{self.range_start:X}">
<Vtx/>
@@ -154,6 +155,7 @@ class VtxArrayResource(CDataResource):
def get_c_declaration_base(self):
if hasattr(self, "HACK_IS_STATIC_ON"):
assert isinstance(self.cdata_ext, CDataExt_Array)
return f"Vtx {self.symbol_name}[{self.cdata_ext.length}]"
return f"Vtx {self.symbol_name}[]"
@@ -343,10 +345,11 @@ class TextureResource(Resource):
if resource_ci.siz == G_IM_SIZ._4b:
v_max = max(max((b >> 4) & 0xF, b & 0xF) for b in resource_ci_data)
assert v_max < 16
if resource_ci.siz == G_IM_SIZ._8b:
elif resource_ci.siz == G_IM_SIZ._8b:
v_max = max(resource_ci_data)
assert v_max < 256
else:
assert False, resource_ci.siz
new_min_count = v_max + 1
@@ -446,6 +449,9 @@ class TextureResource(Resource):
return f"{self.name}.{format_name}{elem_type_suffix}"
def write_extracted(self, memory_context):
assert self.file.data is not None
assert self.range_end is not None
if self.is_tlut():
# TLUTs are extracted as part of the color-indexed textures using them
@@ -605,10 +611,11 @@ class TLUTResource(TextureResource, can_size_be_unknown=True):
if resource_ci.siz == G_IM_SIZ._4b:
v_max = max(max((b >> 4) & 0xF, b & 0xF) for b in resource_ci_data)
assert v_max < 16
if resource_ci.siz == G_IM_SIZ._8b:
elif resource_ci.siz == G_IM_SIZ._8b:
v_max = max(resource_ci_data)
assert v_max < 256
else:
assert False, resource_ci.siz
new_min_count = v_max + 1
@@ -645,6 +652,7 @@ class TextureSplitTlutResource(TextureResource):
return f"{self.name}.ci8.split_{'lo' if self.lo_half else 'hi'}.tlut_{self.resource_tlut.name}"
def write_extracted(self, memory_context):
assert self.file.data is not None
data = self.file.data[self.range_start : self.range_end]
assert len(data) == self.range_end - self.range_start
@@ -652,7 +660,7 @@ class TextureSplitTlutResource(TextureResource):
assert all(_b < 128 for _b in data)
else:
assert all(_b >= 128 for _b in data)
data = bytes(_b - 128 for _b in data)
data = memoryview(bytes(_b - 128 for _b in data))
tlut_data = self.resource_tlut.file.data[
self.resource_tlut.range_start : self.resource_tlut.range_end
@@ -1024,9 +1032,9 @@ class ColorIndexedTexturesManager:
texs: list["ColorIndexedTexturesManager.Tex"]
def __init__(self, *, HACK_late_SetTextureLUT=False):
self.cur_tlut_mode: G_TT = None
self.cur_tlut_mode: G_TT | None = None
self.cur_tluts_count: int = None
self.cur_tluts_count: int | None = None
self.cur_tluts: dict[int, ColorIndexedTexturesManager.Tlut] = dict()
self.cur_texs: list[ColorIndexedTexturesManager.Tex] = []
@@ -18,6 +18,7 @@ class CutsceneResource(Resource, can_size_be_unknown=True):
super().__init__(file, range_start, None, name)
def try_parse_data(self, memory_context):
assert self.file.data is not None
assert self.range_start % 4 == 0
data = self.file.data[self.range_start :]
num_bytes = len(data)
@@ -56,6 +56,8 @@ def report_room_shape_at_segmented(
def get_room_shape_resource_type(file: File, offset: int):
assert file.data is not None
room_shape_type_int = file.data[offset]
room_shape_type = RoomShapeType(room_shape_type_int)
@@ -269,6 +271,7 @@ class JFIFResource(Resource):
return f"{self.name}.jpg"
def write_extracted(self, memory_context):
assert self.file.data is not None
# TODO trim zeros at the end of the data
self.extract_to_path.write_bytes(
self.file.data[self.range_start : self.range_end]
@@ -111,6 +111,7 @@ class SceneCommandsResource(Resource, can_size_be_unknown=True):
self.exit_list_length = None
def try_parse_data(self, memory_context: "MemoryContext"):
assert self.file.data is not None
data = self.file.data[self.range_start :]
new_progress_done = []
@@ -265,7 +266,6 @@ class SceneCommandsResource(Resource, can_size_be_unknown=True):
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,
@@ -423,6 +423,7 @@ class SceneCommandsResource(Resource, can_size_be_unknown=True):
return f"SceneCmd {self.symbol_name}[]"
def write_extracted(self, memory_context):
assert self.file.data is not None
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:
@@ -431,10 +432,12 @@ class SceneCommandsResource(Resource, can_size_be_unknown=True):
(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
)
(_, 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])
@@ -468,10 +471,9 @@ class SceneCommandsResource(Resource, can_size_be_unknown=True):
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
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:
@@ -676,6 +678,7 @@ class AltHeadersResource(CDataArrayResource):
) # SceneCmd*
def try_parse_data(self, memory_context):
assert self.file.data is not None
length = 0
for i, (v,) in enumerate(
struct.iter_unpack(">I", self.file.data[self.range_start :])
@@ -158,8 +158,8 @@ class SpawnListResource(CDataArrayResource):
)
# (eventually) set by SceneCommandsResource
player_entry_list_length = None
room_list_length = None
player_entry_list_length: int | None = None
room_list_length: int | None = None
def try_parse_data(self, memory_context):
if self.player_entry_list_length is None or self.room_list_length is None:
@@ -27,7 +27,7 @@ class KnotCountsArrayResource(CDataResource, can_size_be_unknown=True):
def __init__(self, file: File, range_start: int, name: str):
super().__init__(file, range_start, name)
self.length = None
self.length: int | None = None
def try_parse_data(self, memory_context: "MemoryContext"):
if self.length is not None:
@@ -63,7 +63,7 @@ class CurveInterpKnotArrayResource(CDataResource, can_size_be_unknown=True):
def __init__(self, file: File, range_start: int, name: str):
super().__init__(file, range_start, name)
self.length = None
self.length: int | None = None
def try_parse_data(self, memory_context: "MemoryContext"):
if self.length is not None:
@@ -91,7 +91,7 @@ class ConstantDataArrayResource(CDataResource, can_size_be_unknown=True):
def __init__(self, file: File, range_start: int, name: str):
super().__init__(file, range_start, name)
self.length = None
self.length: int | None = None
def try_parse_data(self, memory_context: "MemoryContext"):
if self.length is not None:
@@ -216,6 +216,7 @@ class SkeletonResourceBaseABC(CDataResource):
return RESOURCE_PARSE_SUCCESS
def write_c_declaration(self, h):
assert self.limbs_array_resource is not None
h.write(f"typedef enum {self.enum_name} {{\n")
limb_enum_members = (
self.enum_member_name_none,
@@ -231,7 +232,6 @@ class SkeletonResourceBaseABC(CDataResource):
)
h.write(f"}} {self.enum_name};\n")
super().write_c_declaration(h)
return True
@abc.abstractmethod
def get_skeleton_header_cdata_unpacked(self) -> dict: ...
@@ -127,5 +127,6 @@ def get_skybox_id(skybox_id: int) -> str:
def get_light_mode(light_mode: int) -> str:
return misc_ids.LIGHT_MODES[light_mode]
def get_navi_quest_hint_file_id_name(navi_quest_hint_file_id: int) -> str:
return misc_ids.NAVI_QUEST_HINT_FILE_IDS[navi_quest_hint_file_id]
@@ -449,7 +449,7 @@ def register_resource_handlers():
)
RESOURCE_HANDLERS: dict[str, ResourceHandler] = {}
RESOURCE_HANDLERS: dict[type[ResourceDesc], ResourceHandler] = {}
def get_resource_from_desc(