diff --git a/assets/xml/objects/object_mag.xml b/assets/xml/objects/object_mag.xml index 59a0613548..b193c3d871 100644 --- a/assets/xml/objects/object_mag.xml +++ b/assets/xml/objects/object_mag.xml @@ -1,20 +1,51 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/baseroms/gc-eu-mq-dbg/config.yml b/baseroms/gc-eu-mq-dbg/config.yml index e457028060..0d1fb268a8 100644 --- a/baseroms/gc-eu-mq-dbg/config.yml +++ b/baseroms/gc-eu-mq-dbg/config.yml @@ -522,7 +522,7 @@ assets: - name: objects/object_ma2 xml_path: assets/xml/objects/object_ma2.xml - 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 xml_path: assets/xml/objects/object_mamenoki.xml - name: objects/object_mastergolon diff --git a/baseroms/gc-eu-mq/config.yml b/baseroms/gc-eu-mq/config.yml index 0093015d7e..3708686934 100644 --- a/baseroms/gc-eu-mq/config.yml +++ b/baseroms/gc-eu-mq/config.yml @@ -514,7 +514,7 @@ assets: - name: objects/object_ma2 xml_path: assets/xml/objects/object_ma2.xml - 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 xml_path: assets/xml/objects/object_mamenoki.xml - name: objects/object_mastergolon diff --git a/baseroms/gc-eu/config.yml b/baseroms/gc-eu/config.yml index 9aef9891f9..cb55d98bb0 100644 --- a/baseroms/gc-eu/config.yml +++ b/baseroms/gc-eu/config.yml @@ -514,7 +514,7 @@ assets: - name: objects/object_ma2 xml_path: assets/xml/objects/object_ma2.xml - 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 xml_path: assets/xml/objects/object_mamenoki.xml - name: objects/object_mastergolon diff --git a/baseroms/gc-jp-ce/config.yml b/baseroms/gc-jp-ce/config.yml index f3c0541a62..f2c1cb58bc 100644 --- a/baseroms/gc-jp-ce/config.yml +++ b/baseroms/gc-jp-ce/config.yml @@ -513,7 +513,7 @@ assets: - name: objects/object_ma2 xml_path: assets/xml/objects/object_ma2.xml - 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 xml_path: assets/xml/objects/object_mamenoki.xml - name: objects/object_mastergolon diff --git a/baseroms/gc-jp-mq/config.yml b/baseroms/gc-jp-mq/config.yml index 41d1721f3d..f50e46293d 100644 --- a/baseroms/gc-jp-mq/config.yml +++ b/baseroms/gc-jp-mq/config.yml @@ -513,7 +513,7 @@ assets: - name: objects/object_ma2 xml_path: assets/xml/objects/object_ma2.xml - 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 xml_path: assets/xml/objects/object_mamenoki.xml - name: objects/object_mastergolon diff --git a/baseroms/gc-jp/config.yml b/baseroms/gc-jp/config.yml index 73285664c6..5460cca12d 100644 --- a/baseroms/gc-jp/config.yml +++ b/baseroms/gc-jp/config.yml @@ -513,7 +513,7 @@ assets: - name: objects/object_ma2 xml_path: assets/xml/objects/object_ma2.xml - 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 xml_path: assets/xml/objects/object_mamenoki.xml - name: objects/object_mastergolon diff --git a/baseroms/gc-us-mq/config.yml b/baseroms/gc-us-mq/config.yml index 1057e49b2e..9f43ab56fa 100644 --- a/baseroms/gc-us-mq/config.yml +++ b/baseroms/gc-us-mq/config.yml @@ -513,7 +513,7 @@ assets: - name: objects/object_ma2 xml_path: assets/xml/objects/object_ma2.xml - 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 xml_path: assets/xml/objects/object_mamenoki.xml - name: objects/object_mastergolon diff --git a/baseroms/gc-us/config.yml b/baseroms/gc-us/config.yml index 620d550631..25e0c52604 100644 --- a/baseroms/gc-us/config.yml +++ b/baseroms/gc-us/config.yml @@ -513,7 +513,7 @@ assets: - name: objects/object_ma2 xml_path: assets/xml/objects/object_ma2.xml - 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 xml_path: assets/xml/objects/object_mamenoki.xml - name: objects/object_mastergolon diff --git a/baseroms/ique-cn/config.yml b/baseroms/ique-cn/config.yml index 6236b0cf51..594da76ad0 100644 --- a/baseroms/ique-cn/config.yml +++ b/baseroms/ique-cn/config.yml @@ -513,7 +513,7 @@ assets: - name: objects/object_ma2 xml_path: assets/xml/objects/object_ma2.xml - 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 xml_path: assets/xml/objects/object_mamenoki.xml - name: objects/object_mastergolon diff --git a/tools/assets/descriptor/__main__.py b/tools/assets/descriptor/__main__.py index 0d3f4e6dc3..e8698b4b61 100644 --- a/tools/assets/descriptor/__main__.py +++ b/tools/assets/descriptor/__main__.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: © 2025 ZeldaRET # SPDX-License-Identifier: CC0-1.0 +import argparse from pprint import pprint as vanilla_pprint try: @@ -14,17 +15,37 @@ from . import base 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) try: 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): vanilla_pprint(pool) else: 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: print() diff --git a/tools/assets/descriptor/base.py b/tools/assets/descriptor/base.py index a0833ec093..47164e0f8e 100644 --- a/tools/assets/descriptor/base.py +++ b/tools/assets/descriptor/base.py @@ -5,6 +5,7 @@ import abc import dataclasses from functools import cache from pathlib import Path +import re from typing import Callable, Optional from xml.etree import ElementTree @@ -27,6 +28,10 @@ class NoBackingMemory(BackingMemory): pass +class ResourceHasNoSizeError(Exception): + pass + + # eq=False so this uses id-based equality and hashing # Subclasses must also be made to use id-based equality and hashing @dataclasses.dataclass(eq=False) @@ -43,6 +48,9 @@ class ResourceDesc(abc.ABC): hack_modes: set[str] = dataclasses.field(init=False, default_factory=set) + def get_size(self) -> int: + raise ResourceHasNoSizeError() + class StartAddress(abc.ABC): pass @@ -197,6 +205,15 @@ def get_resources_desc(vc: version_config.VersionConfig): 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( vc: version_config.VersionConfig, pool: list[AssetConfigPiece], @@ -257,10 +274,21 @@ def _get_resources_fileelem_to_resourcescollection_pass1( [], ) needs_pass2_exceptions: list[ResourceHandlerNeedsPass2Exception] = [] - for reselem in fileelem: + + prev_resource_end_offset = 0 + + for reselem in _get_version_resources(fileelem, vc.version): try: 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) try: 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) assert isinstance(res, ResourceDesc) resources.append(res) + try: + prev_resource_end_offset = res.offset + res.get_size() + except ResourceHasNoSizeError: + prev_resource_end_offset = None except Exception as e: raise Exception( "Error with resource element:\n" diff --git a/tools/assets/descriptor/n64resources.py b/tools/assets/descriptor/n64resources.py index fe7c7172da..ed0a7615a2 100644 --- a/tools/assets/descriptor/n64resources.py +++ b/tools/assets/descriptor/n64resources.py @@ -34,7 +34,7 @@ class DListResourceDesc(ResourceDesc): def handler_DList(symbol_name, offset, collection, reselem: Element): xml_errors.check_attrib( - reselem, {"Name", "Offset"}, {"Ucode", "RawPointers"} | STATIC_ATTRIB + reselem, {"Name"}, {"Offset", "Ucode", "RawPointers"} | STATIC_ATTRIB ) if "Ucode" in reselem.attrib: ucode = GfxMicroCode[reselem.attrib["Ucode"].upper()] @@ -54,7 +54,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", "Size"}, {"Offset"} | STATIC_ATTRIB) size = int(reselem.attrib["Size"], 16) return BlobResourceDesc(symbol_name, offset, collection, reselem, size) @@ -65,7 +65,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"} | STATIC_ATTRIB) return MtxResourceDesc(symbol_name, offset, collection, reselem) @@ -85,7 +85,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", "Count"}, {"Offset"} | STATIC_ATTRIB) count = int(reselem.attrib["Count"]) assert len(reselem) == 1, "Expected exactly one child of Array node" array_elem = reselem[0] @@ -126,6 +126,9 @@ class TextureResourceDesc(ResourceDesc): width: int height: int + def get_size(self): + return self.width * self.height * self.format.siz.bpp // 8 + @dataclasses.dataclass(eq=False) class CITextureResourceDesc(TextureResourceDesc): @@ -137,9 +140,10 @@ def handler_Texture( ): xml_errors.check_attrib( reselem, - {"Name", "Offset", "Format", "Width", "Height"}, + {"Name", "Format", "Width", "Height"}, # TODO remove OutName, SplitTlut { + "Offset", "OutName", "SplitTlut", "TlutOffset", @@ -169,9 +173,9 @@ def handler_Texture( if "TlutOffset" in reselem.attrib: xml_errors.check_attrib( reselem, - {"Name", "Offset", "Format", "Width", "Height", "TlutOffset"}, + {"Name", "Format", "Width", "Height", "TlutOffset"}, # TODO remove OutName, SplitTlut - {"OutName", "SplitTlut", "HackMode"} | STATIC_ATTRIB, + {"Offset", "OutName", "SplitTlut", "HackMode"} | STATIC_ATTRIB, ) tlut_offset = int(reselem.attrib["TlutOffset"], 16) @@ -193,7 +197,6 @@ def handler_Texture( reselem, { "Name", - "Offset", "Format", "Width", "Height", @@ -201,7 +204,7 @@ def handler_Texture( "ExternalTlutOffset", }, # TODO remove OutName, SplitTlut - {"OutName", "SplitTlut", "HackMode"} | STATIC_ATTRIB, + {"Offset", "OutName", "SplitTlut", "HackMode"} | STATIC_ATTRIB, ) external_tlut_file = reselem.attrib["ExternalTlut"] external_tlut_offset = int(reselem.attrib["ExternalTlutOffset"], 16) @@ -229,9 +232,9 @@ def handler_Texture( else: xml_errors.check_attrib( reselem, - {"Name", "Offset", "Format", "Width", "Height"}, + {"Name", "Format", "Width", "Height"}, # TODO remove OutName - {"OutName", "HackMode"} | STATIC_ATTRIB, + {"Offset", "OutName", "HackMode"} | STATIC_ATTRIB, ) res = TextureResourceDesc( symbol_name, offset, collection, reselem, format, width, height diff --git a/tools/assets/descriptor/z64resources.py b/tools/assets/descriptor/z64resources.py index fba264151f..d5e6cf032c 100644 --- a/tools/assets/descriptor/z64resources.py +++ b/tools/assets/descriptor/z64resources.py @@ -20,7 +20,7 @@ class CollisionResourceDesc(ResourceDesc): 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) @@ -30,7 +30,7 @@ class AnimationResourceDesc(ResourceDesc): 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) @@ -40,7 +40,7 @@ class PlayerAnimationResourceDesc(ResourceDesc): 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) @@ -50,7 +50,7 @@ class LegacyAnimationResourceDesc(ResourceDesc): 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) @@ -60,7 +60,7 @@ class CutsceneResourceDesc(ResourceDesc): 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) @@ -70,7 +70,7 @@ class SceneResourceDesc(ResourceDesc): 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) @@ -80,7 +80,7 @@ class RoomResourceDesc(ResourceDesc): 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) if reselem.attrib.get("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): - xml_errors.check_attrib(reselem, {"Name", "Offset", "FrameCount"}) + xml_errors.check_attrib(reselem, {"Name", "FrameCount"}, {"Offset"}) frame_count = int(reselem.attrib["FrameCount"]) return PlayerAnimationDataResourceDesc( symbol_name, offset, collection, reselem, frame_count @@ -106,7 +106,7 @@ class PathListResourceDesc(ResourceDesc): 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"]) 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): xml_errors.check_attrib( reselem, - {"Name", "Offset", "Type", "LimbType"}, - {"EnumName", "LimbNone", "LimbMax"}, + {"Name", "Type", "LimbType"}, + {"Offset", "EnumName", "LimbNone", "LimbMax"}, ) skel_type = SkeletonType[reselem.attrib["Type"].upper()] limb_type = LimbType[reselem.attrib["LimbType"].upper()] @@ -162,7 +162,7 @@ class LimbResourceDesc(ResourceDesc): 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()] return LimbResourceDesc( symbol_name, @@ -181,7 +181,7 @@ class LimbTableResourceDesc(ResourceDesc): 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()] count = int(reselem.attrib["Count"]) return LimbTableResourceDesc( @@ -197,7 +197,7 @@ class CurveAnimationResourceDesc(ResourceDesc): def handler_CurveAnimation( 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) skel_offset = int(reselem.attrib["SkelOffset"], 16)