Assets system: reconcile xml versions using implicit offsets

This commit is contained in:
Dragorn421 2025-06-08 09:03:00 +02:00
parent 39de1e0204
commit 3dcba7e70a
No known key found for this signature in database
GPG Key ID: 381AEBAF3D429335
14 changed files with 141 additions and 54 deletions

View File

@ -1,20 +1,51 @@
<Root> <Root>
<File Name="object_mag" Segment="6"> <File Name="object_mag" Segment="6">
<Texture Name="gTitleDiskTex" Format="ia8" Width="48" Height="16" Offset="0x0"/> <Version Pattern="(ntsc|pal)-.*">
<Texture Name="gTitleZeldaShieldLogoTex" Format="rgba32" Width="160" Height="160" Offset="0x300"/> <Texture Name="gTitleDiskTex" Format="ia8" Width="48" Height="16"/>
<Texture Name="gTitleCopyright1998Tex" Format="ia8" Width="128" Height="16" Offset="0x19300"/> </Version>
<Texture Name="gTitleEffectMask00Tex" Format="i4" Width="64" Height="64" Offset="0x19B00"/> <Texture Name="gTitleZeldaShieldLogoTex" Format="rgba32" Width="160" Height="160"/>
<Texture Name="gTitleEffectMask01Tex" Format="i4" Width="64" Height="64" Offset="0x1A300"/> <Version Pattern="(ntsc|pal)-.*">
<Texture Name="gTitleEffectMask02Tex" Format="i4" Width="64" Height="64" Offset="0x1AB00"/> <Texture Name="gTitleCopyright1998Tex" Format="ia8" Width="128" Height="16"/>
<Texture Name="gTitleEffectMask10Tex" Format="i4" Width="64" Height="64" Offset="0x1B300"/> </Version>
<Texture Name="gTitleEffectMask11Tex" Format="i4" Width="64" Height="64" Offset="0x1BB00"/> <Version Pattern="gc-.*">
<Texture Name="gTitleEffectMask12Tex" Format="i4" Width="64" Height="64" Offset="0x1C300"/> <Texture Name="gTitleCopyright19982002Tex" Format="ia8" Width="160" Height="16"/>
<Texture Name="gTitleEffectMask20Tex" Format="i4" Width="64" Height="64" Offset="0x1CB00"/> </Version>
<Texture Name="gTitleEffectMask21Tex" Format="i4" Width="64" Height="64" Offset="0x1D300"/> <Version Pattern="gc-eu.*|gc-jp-ce|gc-us.*">
<Texture Name="gTitleEffectMask22Tex" Format="i4" Width="64" Height="64" Offset="0x1DB00"/> <Texture Name="gTitleCopyright19982003Tex" Format="ia8" Width="160" Height="16"/>
<Texture Name="gTitleFlameEffectTex" Format="i8" Width="32" Height="32" Offset="0x1E300"/> </Version>
<Texture Name="gTitleTheLegendOfTextTex" Format="i8" Width="72" Height="8" Offset="0x1E700"/> <Version Pattern="ique-cn">
<Texture Name="gTitleOcarinaOfTimeTMTextTex" Format="i8" Width="96" Height="8" Offset="0x1E940"/> <Texture Name="gTitleCopyright19982003IQueTex" Format="ia8" Width="128" Height="32"/>
<Texture Name="gTitleTitleJPNTex" Format="i8" Width="128" Height="16" Offset="0x1EC40"/> </Version>
<Version Pattern="gc-jp-mq">
<Texture Name="gTitleUraLogoTex" Format="rgba32" Width="40" Height="40"/>
</Version>
<Version Pattern="gc-jp.*|gc-eu|gc-us|ique-cn">
<Texture Name="gTitleDiskTex" Format="ia8" Width="48" Height="16"/>
</Version>
<Version Pattern="gc-eu-mq(-dbg)?|gc-us-mq">
<Texture Name="gTitleMasterQuestSubtitleTex" Format="rgba32" Width="128" Height="32"/>
</Version>
<Version Pattern="gc-eu-mq(-dbg)?|gc-us-mq">
<Texture Name="gTitleUraLogoTex" Format="rgba32" Width="40" Height="40"/>
<Texture Name="gTitleDiskTex" Format="ia8" Width="48" Height="16"/>
</Version>
<Texture Name="gTitleEffectMask00Tex" Format="i4" Width="64" Height="64"/>
<Texture Name="gTitleEffectMask01Tex" Format="i4" Width="64" Height="64"/>
<Texture Name="gTitleEffectMask02Tex" Format="i4" Width="64" Height="64"/>
<Texture Name="gTitleEffectMask10Tex" Format="i4" Width="64" Height="64"/>
<Texture Name="gTitleEffectMask11Tex" Format="i4" Width="64" Height="64"/>
<Texture Name="gTitleEffectMask12Tex" Format="i4" Width="64" Height="64"/>
<Texture Name="gTitleEffectMask20Tex" Format="i4" Width="64" Height="64"/>
<Texture Name="gTitleEffectMask21Tex" Format="i4" Width="64" Height="64"/>
<Texture Name="gTitleEffectMask22Tex" Format="i4" Width="64" Height="64"/>
<Texture Name="gTitleFlameEffectTex" Format="i8" Width="32" Height="32"/>
<Texture Name="gTitleTheLegendOfTextTex" Format="i8" Width="72" Height="8"/>
<Texture Name="gTitleOcarinaOfTimeTMTextTex" Format="i8" Width="96" Height="8"/>
<Version Pattern="(ntsc|pal)-.*|gc-.*">
<Texture Name="gTitleTitleJPNTex" Format="i8" Width="128" Height="16"/>
</Version>
<Version Pattern="ique-cn">
<Texture Name="gTitleTitleCHNTex" Format="i8" Width="128" Height="16"/>
</Version>
</File> </File>
</Root> </Root>

View File

@ -522,7 +522,7 @@ assets:
- name: objects/object_ma2 - name: objects/object_ma2
xml_path: assets/xml/objects/object_ma2.xml xml_path: assets/xml/objects/object_ma2.xml
- name: objects/object_mag - name: objects/object_mag
xml_path: assets/xml/objects/object_mag_v3_mq.xml xml_path: assets/xml/objects/object_mag.xml
- name: objects/object_mamenoki - name: objects/object_mamenoki
xml_path: assets/xml/objects/object_mamenoki.xml xml_path: assets/xml/objects/object_mamenoki.xml
- name: objects/object_mastergolon - name: objects/object_mastergolon

View File

@ -514,7 +514,7 @@ assets:
- name: objects/object_ma2 - name: objects/object_ma2
xml_path: assets/xml/objects/object_ma2.xml xml_path: assets/xml/objects/object_ma2.xml
- name: objects/object_mag - name: objects/object_mag
xml_path: assets/xml/objects/object_mag_v3_mq.xml xml_path: assets/xml/objects/object_mag.xml
- name: objects/object_mamenoki - name: objects/object_mamenoki
xml_path: assets/xml/objects/object_mamenoki.xml xml_path: assets/xml/objects/object_mamenoki.xml
- name: objects/object_mastergolon - name: objects/object_mastergolon

View File

@ -514,7 +514,7 @@ assets:
- name: objects/object_ma2 - name: objects/object_ma2
xml_path: assets/xml/objects/object_ma2.xml xml_path: assets/xml/objects/object_ma2.xml
- name: objects/object_mag - name: objects/object_mag
xml_path: assets/xml/objects/object_mag_v3.xml xml_path: assets/xml/objects/object_mag.xml
- name: objects/object_mamenoki - name: objects/object_mamenoki
xml_path: assets/xml/objects/object_mamenoki.xml xml_path: assets/xml/objects/object_mamenoki.xml
- name: objects/object_mastergolon - name: objects/object_mastergolon

View File

@ -513,7 +513,7 @@ assets:
- name: objects/object_ma2 - name: objects/object_ma2
xml_path: assets/xml/objects/object_ma2.xml xml_path: assets/xml/objects/object_ma2.xml
- name: objects/object_mag - name: objects/object_mag
xml_path: assets/xml/objects/object_mag_v3.xml xml_path: assets/xml/objects/object_mag.xml
- name: objects/object_mamenoki - name: objects/object_mamenoki
xml_path: assets/xml/objects/object_mamenoki.xml xml_path: assets/xml/objects/object_mamenoki.xml
- name: objects/object_mastergolon - name: objects/object_mastergolon

View File

@ -513,7 +513,7 @@ assets:
- name: objects/object_ma2 - name: objects/object_ma2
xml_path: assets/xml/objects/object_ma2.xml xml_path: assets/xml/objects/object_ma2.xml
- name: objects/object_mag - name: objects/object_mag
xml_path: assets/xml/objects/object_mag_v2_mq.xml xml_path: assets/xml/objects/object_mag.xml
- name: objects/object_mamenoki - name: objects/object_mamenoki
xml_path: assets/xml/objects/object_mamenoki.xml xml_path: assets/xml/objects/object_mamenoki.xml
- name: objects/object_mastergolon - name: objects/object_mastergolon

View File

@ -513,7 +513,7 @@ assets:
- name: objects/object_ma2 - name: objects/object_ma2
xml_path: assets/xml/objects/object_ma2.xml xml_path: assets/xml/objects/object_ma2.xml
- name: objects/object_mag - name: objects/object_mag
xml_path: assets/xml/objects/object_mag_v2.xml xml_path: assets/xml/objects/object_mag.xml
- name: objects/object_mamenoki - name: objects/object_mamenoki
xml_path: assets/xml/objects/object_mamenoki.xml xml_path: assets/xml/objects/object_mamenoki.xml
- name: objects/object_mastergolon - name: objects/object_mastergolon

View File

@ -513,7 +513,7 @@ assets:
- name: objects/object_ma2 - name: objects/object_ma2
xml_path: assets/xml/objects/object_ma2.xml xml_path: assets/xml/objects/object_ma2.xml
- name: objects/object_mag - name: objects/object_mag
xml_path: assets/xml/objects/object_mag_v3_mq.xml xml_path: assets/xml/objects/object_mag.xml
- name: objects/object_mamenoki - name: objects/object_mamenoki
xml_path: assets/xml/objects/object_mamenoki.xml xml_path: assets/xml/objects/object_mamenoki.xml
- name: objects/object_mastergolon - name: objects/object_mastergolon

View File

@ -513,7 +513,7 @@ assets:
- name: objects/object_ma2 - name: objects/object_ma2
xml_path: assets/xml/objects/object_ma2.xml xml_path: assets/xml/objects/object_ma2.xml
- name: objects/object_mag - name: objects/object_mag
xml_path: assets/xml/objects/object_mag_v3.xml xml_path: assets/xml/objects/object_mag.xml
- name: objects/object_mamenoki - name: objects/object_mamenoki
xml_path: assets/xml/objects/object_mamenoki.xml xml_path: assets/xml/objects/object_mamenoki.xml
- name: objects/object_mastergolon - name: objects/object_mastergolon

View File

@ -513,7 +513,7 @@ assets:
- name: objects/object_ma2 - name: objects/object_ma2
xml_path: assets/xml/objects/object_ma2.xml xml_path: assets/xml/objects/object_ma2.xml
- name: objects/object_mag - name: objects/object_mag
xml_path: assets/xml/objects/object_mag_ique.xml xml_path: assets/xml/objects/object_mag.xml
- name: objects/object_mamenoki - name: objects/object_mamenoki
xml_path: assets/xml/objects/object_mamenoki.xml xml_path: assets/xml/objects/object_mamenoki.xml
- name: objects/object_mastergolon - name: objects/object_mastergolon

View File

@ -1,6 +1,7 @@
# SPDX-FileCopyrightText: © 2025 ZeldaRET # SPDX-FileCopyrightText: © 2025 ZeldaRET
# SPDX-License-Identifier: CC0-1.0 # SPDX-License-Identifier: CC0-1.0
import argparse
from pprint import pprint as vanilla_pprint from pprint import pprint as vanilla_pprint
try: try:
@ -14,17 +15,37 @@ from . import base
def main(): def main():
vc = version_config.load_version_config("gc-eu-mq-dbg") parser = argparse.ArgumentParser()
parser.add_argument("--version", "-v", default="gc-eu-mq-dbg")
parser.add_argument("--all", action="store_true")
parser.add_argument("-s", dest="single", default=None)
args = parser.parse_args()
if not args.all and args.single is None:
parser.error("Must specify --all or -s")
vc = version_config.load_version_config(args.version)
pools = base.get_resources_desc(vc) pools = base.get_resources_desc(vc)
try: try:
for pool in pools: for pool in pools:
if not args.all and not any(
coll.backing_memory.name == args.single
for coll in pool.collections
if isinstance(coll.backing_memory, base.BaseromFileBackingMemory)
):
continue
if any(coll.out_path.name == "gameplay_keep" for coll in pool.collections): if any(coll.out_path.name == "gameplay_keep" for coll in pool.collections):
vanilla_pprint(pool) vanilla_pprint(pool)
else: else:
pprint(pool) pprint(pool)
input("Press enter for next pool") for coll in pool.collections:
print(coll.out_path)
for res in coll.resources:
print(f"0x{res.offset:06X}", res.symbol_name)
if args.all:
input("Press enter for next pool")
except KeyboardInterrupt: except KeyboardInterrupt:
print() print()

View File

@ -5,6 +5,7 @@ import abc
import dataclasses import dataclasses
from functools import cache from functools import cache
from pathlib import Path from pathlib import Path
import re
from typing import Callable, Optional from typing import Callable, Optional
from xml.etree import ElementTree from xml.etree import ElementTree
@ -27,6 +28,10 @@ class NoBackingMemory(BackingMemory):
pass pass
class ResourceHasNoSizeError(Exception):
pass
# eq=False so this uses id-based equality and hashing # eq=False so this uses id-based equality and hashing
# Subclasses must also be made to use id-based equality and hashing # Subclasses must also be made to use id-based equality and hashing
@dataclasses.dataclass(eq=False) @dataclasses.dataclass(eq=False)
@ -43,6 +48,9 @@ class ResourceDesc(abc.ABC):
hack_modes: set[str] = dataclasses.field(init=False, default_factory=set) hack_modes: set[str] = dataclasses.field(init=False, default_factory=set)
def get_size(self) -> int:
raise ResourceHasNoSizeError()
class StartAddress(abc.ABC): class StartAddress(abc.ABC):
pass pass
@ -197,6 +205,15 @@ def get_resources_desc(vc: version_config.VersionConfig):
return pools return pools
def _get_version_resources(fileelem: ElementTree.Element, version: str):
for reselem in fileelem:
if reselem.tag == "Version":
if re.fullmatch(reselem.attrib["Pattern"], version):
yield from reselem
else:
yield reselem
def _get_resources_fileelem_to_resourcescollection_pass1( def _get_resources_fileelem_to_resourcescollection_pass1(
vc: version_config.VersionConfig, vc: version_config.VersionConfig,
pool: list[AssetConfigPiece], pool: list[AssetConfigPiece],
@ -257,10 +274,21 @@ def _get_resources_fileelem_to_resourcescollection_pass1(
[], [],
) )
needs_pass2_exceptions: list[ResourceHandlerNeedsPass2Exception] = [] needs_pass2_exceptions: list[ResourceHandlerNeedsPass2Exception] = []
for reselem in fileelem:
prev_resource_end_offset = 0
for reselem in _get_version_resources(fileelem, vc.version):
try: try:
symbol_name = reselem.attrib["Name"] symbol_name = reselem.attrib["Name"]
offset = int(reselem.attrib["Offset"], 16) if "Offset" in reselem.attrib:
offset = int(reselem.attrib["Offset"], 16)
else:
if prev_resource_end_offset is None:
raise Exception(
f"Resource {symbol_name} has no Offset"
" and previous resource has no known end offset"
)
offset = prev_resource_end_offset
res_handler = _get_resource_handler(reselem.tag) res_handler = _get_resource_handler(reselem.tag)
try: try:
res = res_handler(symbol_name, offset, collection, reselem) res = res_handler(symbol_name, offset, collection, reselem)
@ -269,6 +297,10 @@ def _get_resources_fileelem_to_resourcescollection_pass1(
needs_pass2_exceptions.append(needs_pass2_exc) needs_pass2_exceptions.append(needs_pass2_exc)
assert isinstance(res, ResourceDesc) assert isinstance(res, ResourceDesc)
resources.append(res) resources.append(res)
try:
prev_resource_end_offset = res.offset + res.get_size()
except ResourceHasNoSizeError:
prev_resource_end_offset = None
except Exception as e: except Exception as e:
raise Exception( raise Exception(
"Error with resource element:\n" "Error with resource element:\n"

View File

@ -34,7 +34,7 @@ class DListResourceDesc(ResourceDesc):
def handler_DList(symbol_name, offset, collection, reselem: Element): def handler_DList(symbol_name, offset, collection, reselem: Element):
xml_errors.check_attrib( xml_errors.check_attrib(
reselem, {"Name", "Offset"}, {"Ucode", "RawPointers"} | STATIC_ATTRIB reselem, {"Name"}, {"Offset", "Ucode", "RawPointers"} | STATIC_ATTRIB
) )
if "Ucode" in reselem.attrib: if "Ucode" in reselem.attrib:
ucode = GfxMicroCode[reselem.attrib["Ucode"].upper()] ucode = GfxMicroCode[reselem.attrib["Ucode"].upper()]
@ -54,7 +54,7 @@ class BlobResourceDesc(ResourceDesc):
def handler_Blob(symbol_name, offset, collection, reselem: Element): 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", "Size"}, {"Offset"} | STATIC_ATTRIB)
size = int(reselem.attrib["Size"], 16) size = int(reselem.attrib["Size"], 16)
return BlobResourceDesc(symbol_name, offset, collection, reselem, size) return BlobResourceDesc(symbol_name, offset, collection, reselem, size)
@ -65,7 +65,7 @@ class MtxResourceDesc(ResourceDesc):
def handler_Mtx(symbol_name, offset, collection, reselem: Element): 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"} | STATIC_ATTRIB)
return MtxResourceDesc(symbol_name, offset, collection, reselem) return MtxResourceDesc(symbol_name, offset, collection, reselem)
@ -85,7 +85,7 @@ class VtxArrayResourceDesc(ResourceDesc):
def handler_Array(symbol_name, offset, collection, reselem: Element): 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", "Count"}, {"Offset"} | STATIC_ATTRIB)
count = int(reselem.attrib["Count"]) count = int(reselem.attrib["Count"])
assert len(reselem) == 1, "Expected exactly one child of Array node" assert len(reselem) == 1, "Expected exactly one child of Array node"
array_elem = reselem[0] array_elem = reselem[0]
@ -126,6 +126,9 @@ class TextureResourceDesc(ResourceDesc):
width: int width: int
height: int height: int
def get_size(self):
return self.width * self.height * self.format.siz.bpp // 8
@dataclasses.dataclass(eq=False) @dataclasses.dataclass(eq=False)
class CITextureResourceDesc(TextureResourceDesc): class CITextureResourceDesc(TextureResourceDesc):
@ -137,9 +140,10 @@ def handler_Texture(
): ):
xml_errors.check_attrib( xml_errors.check_attrib(
reselem, reselem,
{"Name", "Offset", "Format", "Width", "Height"}, {"Name", "Format", "Width", "Height"},
# TODO remove OutName, SplitTlut # TODO remove OutName, SplitTlut
{ {
"Offset",
"OutName", "OutName",
"SplitTlut", "SplitTlut",
"TlutOffset", "TlutOffset",
@ -169,9 +173,9 @@ def handler_Texture(
if "TlutOffset" in reselem.attrib: if "TlutOffset" in reselem.attrib:
xml_errors.check_attrib( xml_errors.check_attrib(
reselem, reselem,
{"Name", "Offset", "Format", "Width", "Height", "TlutOffset"}, {"Name", "Format", "Width", "Height", "TlutOffset"},
# TODO remove OutName, SplitTlut # TODO remove OutName, SplitTlut
{"OutName", "SplitTlut", "HackMode"} | STATIC_ATTRIB, {"Offset", "OutName", "SplitTlut", "HackMode"} | STATIC_ATTRIB,
) )
tlut_offset = int(reselem.attrib["TlutOffset"], 16) tlut_offset = int(reselem.attrib["TlutOffset"], 16)
@ -193,7 +197,6 @@ def handler_Texture(
reselem, reselem,
{ {
"Name", "Name",
"Offset",
"Format", "Format",
"Width", "Width",
"Height", "Height",
@ -201,7 +204,7 @@ def handler_Texture(
"ExternalTlutOffset", "ExternalTlutOffset",
}, },
# TODO remove OutName, SplitTlut # TODO remove OutName, SplitTlut
{"OutName", "SplitTlut", "HackMode"} | STATIC_ATTRIB, {"Offset", "OutName", "SplitTlut", "HackMode"} | STATIC_ATTRIB,
) )
external_tlut_file = reselem.attrib["ExternalTlut"] external_tlut_file = reselem.attrib["ExternalTlut"]
external_tlut_offset = int(reselem.attrib["ExternalTlutOffset"], 16) external_tlut_offset = int(reselem.attrib["ExternalTlutOffset"], 16)
@ -229,9 +232,9 @@ def handler_Texture(
else: else:
xml_errors.check_attrib( xml_errors.check_attrib(
reselem, reselem,
{"Name", "Offset", "Format", "Width", "Height"}, {"Name", "Format", "Width", "Height"},
# TODO remove OutName # TODO remove OutName
{"OutName", "HackMode"} | STATIC_ATTRIB, {"Offset", "OutName", "HackMode"} | STATIC_ATTRIB,
) )
res = TextureResourceDesc( res = TextureResourceDesc(
symbol_name, offset, collection, reselem, format, width, height symbol_name, offset, collection, reselem, format, width, height

View File

@ -20,7 +20,7 @@ class CollisionResourceDesc(ResourceDesc):
def handler_Collision(symbol_name, offset, collection, reselem: Element): def handler_Collision(symbol_name, offset, collection, reselem: Element):
xml_errors.check_attrib(reselem, {"Name", "Offset"}) xml_errors.check_attrib(reselem, {"Name"}, {"Offset"})
return CollisionResourceDesc(symbol_name, offset, collection, reselem) return CollisionResourceDesc(symbol_name, offset, collection, reselem)
@ -30,7 +30,7 @@ class AnimationResourceDesc(ResourceDesc):
def handler_Animation(symbol_name, offset, collection, reselem: Element): def handler_Animation(symbol_name, offset, collection, reselem: Element):
xml_errors.check_attrib(reselem, {"Name", "Offset"}) xml_errors.check_attrib(reselem, {"Name"}, {"Offset"})
return AnimationResourceDesc(symbol_name, offset, collection, reselem) return AnimationResourceDesc(symbol_name, offset, collection, reselem)
@ -40,7 +40,7 @@ class PlayerAnimationResourceDesc(ResourceDesc):
def handler_PlayerAnimation(symbol_name, offset, collection, reselem: Element): def handler_PlayerAnimation(symbol_name, offset, collection, reselem: Element):
xml_errors.check_attrib(reselem, {"Name", "Offset"}) xml_errors.check_attrib(reselem, {"Name"}, {"Offset"})
return PlayerAnimationResourceDesc(symbol_name, offset, collection, reselem) return PlayerAnimationResourceDesc(symbol_name, offset, collection, reselem)
@ -50,7 +50,7 @@ class LegacyAnimationResourceDesc(ResourceDesc):
def handler_LegacyAnimation(symbol_name, offset, collection, reselem: Element): def handler_LegacyAnimation(symbol_name, offset, collection, reselem: Element):
xml_errors.check_attrib(reselem, {"Name", "Offset"}) xml_errors.check_attrib(reselem, {"Name"}, {"Offset"})
return LegacyAnimationResourceDesc(symbol_name, offset, collection, reselem) return LegacyAnimationResourceDesc(symbol_name, offset, collection, reselem)
@ -60,7 +60,7 @@ class CutsceneResourceDesc(ResourceDesc):
def handler_Cutscene(symbol_name, offset, collection, reselem: Element): def handler_Cutscene(symbol_name, offset, collection, reselem: Element):
xml_errors.check_attrib(reselem, {"Name", "Offset"}) xml_errors.check_attrib(reselem, {"Name"}, {"Offset"})
return CutsceneResourceDesc(symbol_name, offset, collection, reselem) return CutsceneResourceDesc(symbol_name, offset, collection, reselem)
@ -70,7 +70,7 @@ class SceneResourceDesc(ResourceDesc):
def handler_Scene(symbol_name, offset, collection, reselem: Element): def handler_Scene(symbol_name, offset, collection, reselem: Element):
xml_errors.check_attrib(reselem, {"Name", "Offset"}) xml_errors.check_attrib(reselem, {"Name"}, {"Offset"})
return SceneResourceDesc(symbol_name, offset, collection, reselem) return SceneResourceDesc(symbol_name, offset, collection, reselem)
@ -80,7 +80,7 @@ class RoomResourceDesc(ResourceDesc):
def handler_Room(symbol_name, offset, collection, reselem: Element): def handler_Room(symbol_name, offset, collection, reselem: Element):
xml_errors.check_attrib(reselem, {"Name", "Offset"}, {"HackMode"}) xml_errors.check_attrib(reselem, {"Name"}, {"Offset", "HackMode"})
res = RoomResourceDesc(symbol_name, offset, collection, reselem) res = RoomResourceDesc(symbol_name, offset, collection, reselem)
if reselem.attrib.get("HackMode") == "syotes_room": if reselem.attrib.get("HackMode") == "syotes_room":
res.hack_modes.add("hackmode_syotes_room") res.hack_modes.add("hackmode_syotes_room")
@ -93,7 +93,7 @@ class PlayerAnimationDataResourceDesc(ResourceDesc):
def handler_PlayerAnimationData(symbol_name, offset, collection, reselem: Element): def handler_PlayerAnimationData(symbol_name, offset, collection, reselem: Element):
xml_errors.check_attrib(reselem, {"Name", "Offset", "FrameCount"}) xml_errors.check_attrib(reselem, {"Name", "FrameCount"}, {"Offset"})
frame_count = int(reselem.attrib["FrameCount"]) frame_count = int(reselem.attrib["FrameCount"])
return PlayerAnimationDataResourceDesc( return PlayerAnimationDataResourceDesc(
symbol_name, offset, collection, reselem, frame_count symbol_name, offset, collection, reselem, frame_count
@ -106,7 +106,7 @@ class PathListResourceDesc(ResourceDesc):
def handler_PathList(symbol_name, offset, collection, reselem: Element): def handler_PathList(symbol_name, offset, collection, reselem: Element):
xml_errors.check_attrib(reselem, {"Name", "Offset", "NumPaths"}) xml_errors.check_attrib(reselem, {"Name", "NumPaths"}, {"Offset"})
num_paths = int(reselem.attrib["NumPaths"]) num_paths = int(reselem.attrib["NumPaths"])
return PathListResourceDesc(symbol_name, offset, collection, reselem, num_paths) return PathListResourceDesc(symbol_name, offset, collection, reselem, num_paths)
@ -137,8 +137,8 @@ class SkeletonResourceDesc(ResourceDesc):
def handler_Skeleton(symbol_name, offset, collection, reselem: Element): def handler_Skeleton(symbol_name, offset, collection, reselem: Element):
xml_errors.check_attrib( xml_errors.check_attrib(
reselem, reselem,
{"Name", "Offset", "Type", "LimbType"}, {"Name", "Type", "LimbType"},
{"EnumName", "LimbNone", "LimbMax"}, {"Offset", "EnumName", "LimbNone", "LimbMax"},
) )
skel_type = SkeletonType[reselem.attrib["Type"].upper()] skel_type = SkeletonType[reselem.attrib["Type"].upper()]
limb_type = LimbType[reselem.attrib["LimbType"].upper()] limb_type = LimbType[reselem.attrib["LimbType"].upper()]
@ -162,7 +162,7 @@ class LimbResourceDesc(ResourceDesc):
def handler_Limb(symbol_name, offset, collection, reselem: Element): def handler_Limb(symbol_name, offset, collection, reselem: Element):
xml_errors.check_attrib(reselem, {"Name", "Offset", "LimbType"}, {"EnumName"}) xml_errors.check_attrib(reselem, {"Name", "LimbType"}, {"Offset", "EnumName"})
limb_type = LimbType[reselem.attrib["LimbType"].upper()] limb_type = LimbType[reselem.attrib["LimbType"].upper()]
return LimbResourceDesc( return LimbResourceDesc(
symbol_name, symbol_name,
@ -181,7 +181,7 @@ class LimbTableResourceDesc(ResourceDesc):
def handler_LimbTable(symbol_name, offset, collection, reselem: Element): def handler_LimbTable(symbol_name, offset, collection, reselem: Element):
xml_errors.check_attrib(reselem, {"Name", "Offset", "LimbType", "Count"}) xml_errors.check_attrib(reselem, {"Name", "LimbType", "Count"}, {"Offset"})
limb_type = LimbType[reselem.attrib["LimbType"].upper()] limb_type = LimbType[reselem.attrib["LimbType"].upper()]
count = int(reselem.attrib["Count"]) count = int(reselem.attrib["Count"])
return LimbTableResourceDesc( return LimbTableResourceDesc(
@ -197,7 +197,7 @@ class CurveAnimationResourceDesc(ResourceDesc):
def handler_CurveAnimation( def handler_CurveAnimation(
symbol_name, offset, collection: ResourcesDescCollection, reselem: Element symbol_name, offset, collection: ResourcesDescCollection, reselem: Element
): ):
xml_errors.check_attrib(reselem, {"Name", "Offset", "SkelOffset"}) xml_errors.check_attrib(reselem, {"Name", "SkelOffset"}, {"Offset"})
res = CurveAnimationResourceDesc(symbol_name, offset, collection, reselem, None) res = CurveAnimationResourceDesc(symbol_name, offset, collection, reselem, None)
skel_offset = int(reselem.attrib["SkelOffset"], 16) skel_offset = int(reselem.attrib["SkelOffset"], 16)