diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fbc03d1..1c2a11a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,3 +11,8 @@ repos: hooks: - id: ruff args: [ --fix, --exit-non-zero-on-fix ] + +- repo: https://github.com/psf/black + rev: 23.7.0 + hooks: + - id: black \ No newline at end of file diff --git a/parse_pwe_templates.py b/parse_pwe_templates.py index 73c3acd..7cc1d34 100644 --- a/parse_pwe_templates.py +++ b/parse_pwe_templates.py @@ -37,12 +37,12 @@ } _CODE_PARSE_UINT16 = { - '>': 'struct.unpack(">H", data.read(2))[0]', - '<': 'struct.unpack("": 'struct.unpack(">H", data.read(2))[0]', + "<": 'struct.unpack("': 'struct.unpack(">L", data.read(4))[0]', - '<': 'struct.unpack("": 'struct.unpack(">L", data.read(4))[0]', + "<": 'struct.unpack(" dict: def _prop_struct(element: Element, game_id: str, path: Path) -> dict: return { "archetype": element.attrib.get("Archetype"), - "properties": _parse_properties(element, game_id, path)["properties"] + "properties": _parse_properties(element, game_id, path)["properties"], } @@ -150,8 +150,7 @@ def _prop_flags(element: Element, game_id: str, path: Path) -> dict: extras = _prop_default_value(element, game_id, path) if (flags_element := element.find("Flags")) is not None: extras["flags"] = { - element.attrib["Name"]: int(element.attrib["Mask"], 16) - for element in flags_element.findall("Element") + element.attrib["Name"]: int(element.attrib["Mask"], 16) for element in flags_element.findall("Element") } name = None @@ -180,11 +179,13 @@ def _parse_single_property(element: Element, game_id: str, path: Path, include_i cook = element.find("CookPreference") - parsed.update({ - "type": element.attrib["Type"], - "name": name, - "cook_preference": cook.text if cook is not None and cook.text is not None else "Always" - }) + parsed.update( + { + "type": element.attrib["Type"], + "name": name, + "cook_preference": cook.text if cook is not None and cook.text is not None else "Always", + } + ) ignore_dependencies_mlvl = element.attrib.get("IgnoreDependenciesMlvl", "False") ignore_dependencies_mlvl = ignore_dependencies_mlvl.lower() != "false" @@ -258,10 +259,7 @@ def parse_script_object_file(path: Path, game_id: str) -> dict: result = _parse_properties(root.find("Properties"), game_id, path) if modules := root.find("Modules"): - result["modules"] = [ - item.text - for item in modules.findall("Element") - ] + result["modules"] = [item.text for item in modules.findall("Element")] return result @@ -298,27 +296,24 @@ def read_property_names(map_path: Path): def get_paths(elements: typing.Iterable[Element]) -> dict[str, str]: - return { - item.find("Key").text: item.find("Value").attrib["Path"] - for item in elements - } + return {item.find("Key").text: item.find("Value").attrib["Path"] for item in elements} def get_key_map(elements: typing.Iterable[Element]) -> dict[str, str]: - return { - item.find("Key").text: item.find("Value").text - for item in elements - } + return {item.find("Key").text: item.find("Value").text for item in elements} -_to_snake_case_re = re.compile(r'(? str: - result = inflection.underscore(n.translate(_to_underscore_table).replace("#", "Number") - ).translate(_invalid_chars_table).lower() + result = ( + inflection.underscore(n.translate(_to_underscore_table).replace("#", "Number")) + .translate(_invalid_chars_table) + .lower() + ) if keyword.iskeyword(result): result += "_" return result @@ -373,7 +368,7 @@ def _get_default(meta: dict) -> str: else: default_value: str = meta["default_factory"] if default_value.startswith("lambda: "): - default_value = default_value[len("lambda: "):] + default_value = default_value[len("lambda: ") :] else: default_value += "()" return default_value @@ -418,10 +413,7 @@ def add_prop(self, prop: PropDetails, prop_name: str): self.class_code += f" {prop_name}: {prop.prop_type}" if prop.meta: self.class_code += " = dataclasses.field({})".format( - ", ".join( - f"{key}={value}" - for key, value in prop.meta.items() - ) + ", ".join(f"{key}={value}" for key, value in prop.meta.items()) ) if prop.comment is not None: @@ -435,44 +427,44 @@ def add_prop(self, prop: PropDetails, prop_name: str): # build prop_id_bytes = struct.pack(endianness + "L", prop.id) self.properties_builder += "\n" - build_prop = [ - f'data.write({repr(prop_id_bytes)}) # {hex(prop.id)}' - ] + build_prop = [f"data.write({repr(prop_id_bytes)}) # {hex(prop.id)}"] if prop.known_size is not None: placeholder = repr(struct.pack(endianness + "H", prop.known_size)) build_prop.append(f"data.write({placeholder}) # size") else: - placeholder = repr(b'\x00\x00') + placeholder = repr(b"\x00\x00") build_prop.append("before = data.tell()") build_prop.append(f"data.write({placeholder}) # size placeholder") for build in prop.build_code: - build_prop.append(build.replace("{obj}", f'self.{prop_name}')) + build_prop.append(build.replace("{obj}", f"self.{prop_name}")) if prop.known_size is None: build_prop.append("after = data.tell()") build_prop.append("data.seek(before)") build_prop.append(f'data.write(struct.pack("{endianness}H", after - before - 2))') - build_prop.append('data.seek(after)') + build_prop.append("data.seek(after)") if not prop.custom_cook_pref: build_prop = [f" {text}" for text in build_prop] self.property_count += 1 - elif prop.raw['cook_preference'] == "Never": + elif prop.raw["cook_preference"] == "Never": build_prop = [] else: default_value = _get_default(prop.meta) - if prop.raw['cook_preference'] == "Default": + if prop.raw["cook_preference"] == "Default": self.properties_builder += f" self.{prop_name} = default_override.get({repr(prop_name)}, {default_value}) # Cooks with Default\n" build_prop = [f" {text}" for text in build_prop] self.property_count += 1 - elif prop.raw['cook_preference'] == "OnlyIfModified": - self.properties_builder += f" if self.{prop_name} != default_override.get({repr(prop_name)}, {default_value}):\n" + elif prop.raw["cook_preference"] == "OnlyIfModified": + self.properties_builder += ( + f" if self.{prop_name} != default_override.get({repr(prop_name)}, {default_value}):\n" + ) self.properties_builder += " num_properties_written += 1\n" build_prop = [f" {text}" for text in build_prop] @@ -491,10 +483,7 @@ def keep_unknown_properties(self): return self.is_incomplete def _can_fast_decode(self) -> bool: - return all( - prop.format_specifier is not None - for prop in self.all_props.values() - ) + return all(prop.format_specifier is not None for prop in self.all_props.values()) def _create_fast_decode_body(self): num_props = len(self.all_props) @@ -555,14 +544,18 @@ def from_stream(cls, data: typing.BinaryIO, size: typing.Optional[int] = None, d return if self.is_struct: - self.class_code += ' struct_id, size, property_count = struct.unpack("' + endianness + 'LHH", data.read(8))\n' + self.class_code += ( + ' struct_id, size, property_count = struct.unpack("' + endianness + 'LHH", data.read(8))\n' + ) self.class_code += " assert struct_id == 0xFFFFFFFF\n" self.class_code += " root_size_start = data.tell() - 2\n\n" else: - self.class_code += f' property_count = {_CODE_PARSE_UINT16[endianness]}\n' + self.class_code += f" property_count = {_CODE_PARSE_UINT16[endianness]}\n" if self._can_fast_decode(): - self.class_code += " if default_override is None and (result := _fast_decode(data, property_count)) is not None:\n" + self.class_code += ( + " if default_override is None and (result := _fast_decode(data, property_count)) is not None:\n" + ) self.class_code += " return result\n\n" for fast_decode in self._create_fast_decode_body(): @@ -632,7 +625,7 @@ def to_stream(self, data: typing.BinaryIO, default_override: typing.Optional[dic if self.is_struct: null_bytes = repr(b"\xFF\xFF\xFF\xFF") self.class_code += f" data.write({null_bytes}) # struct object id\n" - placeholder = repr(b'\x00\x00') + placeholder = repr(b"\x00\x00") self.class_code += " root_size_offset = data.tell()\n" self.class_code += f" data.write({placeholder}) # placeholder for root struct size\n" has_root_size_offset = True @@ -652,8 +645,10 @@ def to_stream(self, data: typing.BinaryIO, default_override: typing.Optional[dic self.class_code += self.properties_builder if self.keep_unknown_properties(): self.class_code += "\n for property_id, property_data in self.unknown_properties.items():\n" - self.class_code += f' data.write(struct.pack("{endianness}LH", property_id, len(property_data)))\n' - self.class_code += ' data.write(property_data)\n' + self.class_code += ( + f' data.write(struct.pack("{endianness}LH", property_id, len(property_data)))\n' + ) + self.class_code += " data.write(property_data)\n" num_props_variable = "num_properties_written + len(self.unknown_properties)" else: num_props_variable = "num_properties_written" @@ -661,7 +656,9 @@ def to_stream(self, data: typing.BinaryIO, default_override: typing.Optional[dic if has_root_size_offset: self.class_code += "\n struct_end_offset = data.tell()\n" self.class_code += " data.seek(root_size_offset)\n" - self.class_code += ' data.write(struct.pack("' + endianness + 'H", struct_end_offset - root_size_offset - 2))\n' + self.class_code += ( + ' data.write(struct.pack("' + endianness + 'H", struct_end_offset - root_size_offset - 2))\n' + ) if self.has_custom_cook_pref: self.class_code += f' data.write(struct.pack("{endianness}H", {num_props_variable}))\n' self.class_code += " data.seek(struct_end_offset)\n" @@ -726,7 +723,7 @@ def write_dependencies(self): continue has_dep = True - prop_code = prop.dependency_code.format(obj=f'self.{prop_name}') + prop_code = prop.dependency_code.format(obj=f"self.{prop_name}") if prop.raw.get("ignore_dependencies_mlvl"): self.needed_imports["retro_data_structures.base_resource"] = "Dependency" prop_code = f"for it in {prop_code}:\n yield Dependency(it.type, it.id, True)" @@ -772,7 +769,8 @@ def game(cls) -> Game: endianness = get_endianness(game_id) - core_path.joinpath("Color.py").write_text(f"""# Generated file + core_path.joinpath("Color.py").write_text( + f"""# Generated file import dataclasses import struct import typing @@ -806,8 +804,11 @@ def to_json(self) -> dict: "b": self.b, "a": self.a, }} -""" + game_code) - core_path.joinpath("Vector.py").write_text(f"""# Generated file +""" + + game_code + ) + core_path.joinpath("Vector.py").write_text( + f"""# Generated file import dataclasses import struct import typing @@ -842,7 +843,9 @@ def to_json(self) -> dict: def dependencies_for(self, asset_manager): yield from [] -""" + game_code) +""" + + game_code + ) if game_id == "PrimeRemastered": asset_code = "import uuid\n\nAssetId = uuid.UUID\ndefault_asset_id = uuid.UUID(int=0)\n" else: @@ -854,7 +857,8 @@ def dependencies_for(self, asset_manager): core_path.joinpath("AssetId.py").write_text(asset_code) if game_id == "PrimeRemastered": - core_path.joinpath("PooledString.py").write_text(f"""# Generated file + core_path.joinpath("PooledString.py").write_text( + f"""# Generated file import dataclasses import struct import typing @@ -893,7 +897,9 @@ def to_json(self) -> dict: "index": self.index, "size_or_str": self.size_or_str, }} -""" + game_code) +""" + + game_code + ) return if game_id in ["Prime", "Echoes"]: @@ -903,7 +909,8 @@ def to_json(self) -> dict: format_specifier = "Q" known_size = 16 - core_path.joinpath("AnimationParameters.py").write_text(f"""# Generated file + core_path.joinpath("AnimationParameters.py").write_text( + f"""# Generated file import dataclasses import struct import typing @@ -939,8 +946,11 @@ def to_json(self) -> dict: def dependencies_for(self, asset_manager): yield from asset_manager.get_dependencies_for_ancs(self.ancs, self.character_index) -""" + game_code) - core_path.joinpath("Spline.py").write_text("""# Generated file +""" + + game_code + ) + core_path.joinpath("Spline.py").write_text( + """# Generated file import dataclasses import typing import base64 @@ -969,7 +979,9 @@ def from_json(cls, data): def to_json(self) -> str: return base64.b64encode(self.data).decode("ascii") -""" + game_code) +""" + + game_code + ) def parse_game(templates_path: Path, game_xml: Path, game_id: str) -> dict: @@ -994,29 +1006,26 @@ def parse_game(templates_path: Path, game_xml: Path, game_id: str) -> dict: enum_base = "Enum" if states: - game_enums.append(EnumDefinition( - "State", - { - value: enum_value_repr(key) - for key, value in states.items() - }, - enum_base=enum_base, - )) + game_enums.append( + EnumDefinition( + "State", + {value: enum_value_repr(key) for key, value in states.items()}, + enum_base=enum_base, + ) + ) if messages: - game_enums.append(EnumDefinition( - "Message", - { - value: enum_value_repr(key) - for key, value in messages.items() - }, - enum_base=enum_base, - )) + game_enums.append( + EnumDefinition( + "Message", + {value: enum_value_repr(key) for key, value in messages.items()}, + enum_base=enum_base, + ) + ) script_objects_paths = dict(get_paths(root.find("ScriptObjects")).items()) script_objects = { - four_cc: parse_script_object_file(base_path / path, game_id) - for four_cc, path in script_objects_paths.items() + four_cc: parse_script_object_file(base_path / path, game_id) for four_cc, path in script_objects_paths.items() } property_archetypes = { name: parse_property_archetypes(base_path / path, game_id) @@ -1104,13 +1113,13 @@ def get_prop_details(prop) -> PropDetails: parse_code = f"{prop_type}.from_stream(data, property_size)" build_code.append("{obj}.to_stream(data)") - elif prop['type'] in ['Choice', 'Enum']: - default_value = prop["default_value"] if prop['has_default'] else 0 + elif prop["type"] in ["Choice", "Enum"]: + default_value = prop["default_value"] if prop["has_default"] else 0 enum_name = _scrub_enum(prop["archetype"] or prop["name"] or property_names.get(prop["id"]) or "") format_specifier = "L" uses_known_enum = enum_name in known_enums and ( - default_value in list(known_enums[enum_name].values.values()) + default_value in list(known_enums[enum_name].values.values()) ) if uses_known_enum: prop_type = f"enums.{enum_name}" @@ -1134,7 +1143,7 @@ def get_prop_details(prop) -> PropDetails: to_json_code = "{obj}" elif raw_type == "Flags": - default_value = repr(prop["default_value"] if prop['has_default'] else 0) + default_value = repr(prop["default_value"] if prop["has_default"] else 0) format_specifier = "L" if "flagset_name" in prop: @@ -1172,8 +1181,8 @@ def get_prop_details(prop) -> PropDetails: if game_id in ["PrimeRemastered"]: needed_imports["uuid"] = True known_size = 16 - parse_code = 'uuid.UUID(bytes_le=data.read(16))' - build_code.append('data.write({obj}.bytes_le)') + parse_code = "uuid.UUID(bytes_le=data.read(16))" + build_code.append("data.write({obj}.bytes_le)") from_json_code = "uuid.UUID({obj})" to_json_code = "str({obj})" @@ -1186,8 +1195,8 @@ def get_prop_details(prop) -> PropDetails: known_size = 8 format_with_prefix = f'"{endianness}{format_specifier}"' - parse_code = f'struct.unpack({format_with_prefix}, data.read({known_size}))[0]' - build_code.append(f'data.write(struct.pack({format_with_prefix}, {{obj}}))') + parse_code = f"struct.unpack({format_with_prefix}, data.read({known_size}))[0]" + build_code.append(f"data.write(struct.pack({format_with_prefix}, {{obj}}))") from_json_code = "{obj}" to_json_code = "{obj}" @@ -1212,29 +1221,29 @@ def get_prop_details(prop) -> PropDetails: comment = inner_prop.comment meta["default_factory"] = "list" parse_code = f"[{inner_prop.parse_code} for _ in range({_CODE_PARSE_UINT32[endianness]})]" - build_code.extend([ - 'array = {obj}', - 'data.write(struct.pack("' + endianness + 'L", len(array)))', - 'for item in array:', - *[' ' + inner.format(obj="item") for inner in inner_prop.build_code] - ]) - from_json_code = "[{inner} for item in {{obj}}]".format( - inner=inner_prop.from_json_code.format(obj="item") - ) - to_json_code = "[{inner} for item in {{obj}}]".format( - inner=inner_prop.to_json_code.format(obj="item") + build_code.extend( + [ + "array = {obj}", + 'data.write(struct.pack("' + endianness + 'L", len(array)))', + "for item in array:", + *[" " + inner.format(obj="item") for inner in inner_prop.build_code], + ] ) + from_json_code = "[{inner} for item in {{obj}}]".format(inner=inner_prop.from_json_code.format(obj="item")) + to_json_code = "[{inner} for item in {{obj}}]".format(inner=inner_prop.to_json_code.format(obj="item")) needed_imports.update(inner_prop.needed_imports) elif raw_type == "String": prop_type = "str" - meta["default"] = repr(prop["default_value"] if prop['has_default'] else "") + meta["default"] = repr(prop["default_value"] if prop["has_default"] else "") null_byte = repr(b"\x00") parse_code = f'b"".join(iter(lambda: data.read(1), {null_byte})).decode("utf-8")' - build_code.extend([ - 'data.write({obj}.encode("utf-8"))', - f'data.write({null_byte})', - ]) + build_code.extend( + [ + 'data.write({obj}.encode("utf-8"))', + f"data.write({null_byte})", + ] + ) from_json_code = "{obj}" to_json_code = "{obj}" @@ -1253,11 +1262,8 @@ def get_prop_details(prop) -> PropDetails: s = struct.Struct(f"{endianness}f") - if prop['has_default']: - default_value = { - k: s.unpack(s.pack(v))[0] - for k, v in prop["default_value"].items() - } + if prop["has_default"]: + default_value = {k: s.unpack(s.pack(v))[0] for k, v in prop["default_value"].items()} if raw_type == "Color": value = {"A": 0.0, **default_value} meta["default_factory"] = "lambda: Color(r={R}, g={G}, b={B}, a={A})".format(**value) @@ -1277,7 +1283,7 @@ def get_prop_details(prop) -> PropDetails: from_json_code = "{obj}" to_json_code = "{obj}" - default_value = prop["default_value"] if prop['has_default'] else literal_prop.default + default_value = prop["default_value"] if prop["has_default"] else literal_prop.default try: s = struct.Struct(struct_format) default_value = s.unpack(s.pack(default_value))[0] @@ -1296,10 +1302,22 @@ def get_prop_details(prop) -> PropDetails: if known_size is None and format_specifier is not None: known_size = struct.calcsize(endianness + format_specifier) - return PropDetails(prop, prop_type, need_enums, comment, parse_code, build_code, from_json_code, to_json_code, - custom_cook_pref=prop['cook_preference'] != "Always", known_size=known_size, - meta=meta, needed_imports=needed_imports, format_specifier=format_specifier, - dependency_code=dependency_code) + return PropDetails( + prop, + prop_type, + need_enums, + comment, + parse_code, + build_code, + from_json_code, + to_json_code, + custom_cook_pref=prop["cook_preference"] != "Always", + known_size=known_size, + meta=meta, + needed_imports=needed_imports, + format_specifier=format_specifier, + dependency_code=dependency_code, + ) def parse_struct(name: str, this, output_path: Path, struct_fourcc: str | None = None): is_struct = struct_fourcc is not None and game_id != "PrimeRemastered" @@ -1358,7 +1376,7 @@ def parse_struct(name: str, this, output_path: Path, struct_fourcc: str | None = if name_field == "None": cls.class_code += ' raise RuntimeError(f"{self.__class__.__name__} does not have name")\n' else: - cls.class_code += f' {name_field} = name\n' + cls.class_code += f" {name_field} = name\n" cls.class_code += "\n @classmethod\n" if game_id in ["Prime", "PrimeRemastered"]: @@ -1418,9 +1436,11 @@ def parse_struct(name: str, this, output_path: Path, struct_fourcc: str | None = path = code_path.joinpath("objects") _ensure_is_generated_dir(path) - if game_id in ['Prime', 'PrimeRemastered']: + if game_id in ["Prime", "PrimeRemastered"]: + def four_cc_wrap(it): return it + four_cc_type = "int" else: four_cc_wrap = repr @@ -1464,14 +1484,12 @@ def four_cc_wrap(it): def parse_game_list(templates_path: Path) -> dict: t = ElementTree.parse(templates_path / "GameList.xml") root = t.getroot() - return { - game.attrib["ID"]: Path(game.find("GameTemplate").text) - for game in root - } + return {game.attrib["ID"]: Path(game.find("GameTemplate").text) for game in root} -def write_shared_type_with_common_import(output_file: Path, kind: str, import_path: str, - all_objects: dict[str, list[str]]): +def write_shared_type_with_common_import( + output_file: Path, kind: str, import_path: str, all_objects: dict[str, list[str]] +): declarations = [] base = f"import retro_data_structures.{import_path}." @@ -1484,23 +1502,26 @@ def write_shared_type_with_common_import(output_file: Path, kind: str, import_pa type_name = object_name.split("_")[-1] used_games.update(games) - declarations.append("{} = typing.Union[\n{}\n]".format( - object_name, - ",\n".join(f" _{_game_id_to_file[game]}_{kind}.{type_name}" for game in games) - )) + declarations.append( + "{} = typing.Union[\n{}\n]".format( + object_name, ",\n".join(f" _{_game_id_to_file[game]}_{kind}.{type_name}" for game in games) + ) + ) if kind == "enums": left_kind = "" else: left_kind = f".{kind}" - output_file.write_text("# Generated File\nimport typing\n\n{imports}\n\n{declarations}\n".format( - imports="\n".join( - f"{base}{_game_id_to_file[game]}{left_kind} as _{_game_id_to_file[game]}_{kind}" - for game in sorted(used_games) - ), - declarations="\n".join(declarations), - )) + output_file.write_text( + "# Generated File\nimport typing\n\n{imports}\n\n{declarations}\n".format( + imports="\n".join( + f"{base}{_game_id_to_file[game]}{left_kind} as _{_game_id_to_file[game]}_{kind}" + for game in sorted(used_games) + ), + declarations="\n".join(declarations), + ) + ) def write_shared_type(output_file: Path, kind: str, all_objects: dict[str, list[str]]): @@ -1516,18 +1537,19 @@ def write_shared_type(output_file: Path, kind: str, all_objects: dict[str, list[ type_name = object_name.split("_")[-1] for game in games: - imports.append( - f"{base}{_game_id_to_file[game]}.{kind}.{import_name} as _{object_name}_{game}" + imports.append(f"{base}{_game_id_to_file[game]}.{kind}.{import_name} as _{object_name}_{game}") + declarations.append( + "{} = typing.Union[\n{}\n]".format( + object_name, ",\n".join(f" _{object_name}_{game}.{type_name}" for game in games) ) - declarations.append("{} = typing.Union[\n{}\n]".format( - object_name, - ",\n".join(f" _{object_name}_{game}.{type_name}" for game in games) - )) + ) - output_file.write_text("# Generated File\nimport typing\n\n{imports}\n\n{declarations}\n".format( - imports="\n".join(imports), - declarations="\n".join(declarations), - )) + output_file.write_text( + "# Generated File\nimport typing\n\n{imports}\n\n{declarations}\n".format( + imports="\n".join(imports), + declarations="\n".join(declarations), + ) + ) def write_shared_types_helpers(all_games: dict): @@ -1611,7 +1633,7 @@ def persist_data(parse_result): ) -if __name__ == '__main__': +if __name__ == "__main__": logging.basicConfig(level=logging.INFO) # persist_data(parse(["PrimeRemastered"])) persist_data(parse(_game_id_to_file.keys())) diff --git a/setup.py b/setup.py index 6d95dfe..1c12d75 100644 --- a/setup.py +++ b/setup.py @@ -10,10 +10,7 @@ def generate_property_templates(): - subprocess.run([ - sys.executable, - os.fspath(Path(__file__).parent.joinpath("parse_pwe_templates.py")) - ], check=True) + subprocess.run([sys.executable, os.fspath(Path(__file__).parent.joinpath("parse_pwe_templates.py"))], check=True) class GenerateTemplateCommand(egg_info): @@ -29,6 +26,6 @@ def run(self): setup( cmdclass={ - 'egg_info': GenerateTemplateCommand, + "egg_info": GenerateTemplateCommand, }, ) diff --git a/src/retro_data_structures/asset_manager.py b/src/retro_data_structures/asset_manager.py index 549759c..44d3ac5 100644 --- a/src/retro_data_structures/asset_manager.py +++ b/src/retro_data_structures/asset_manager.py @@ -26,7 +26,6 @@ from retro_data_structures.game_check import Game if typing.TYPE_CHECKING: - import uuid from collections.abc import Iterator from pathlib import Path @@ -116,6 +115,7 @@ class AssetManager: _ensured_asset_ids: mapping of pak name to assets we'll copy into it when saving _modified_resources: mapping of asset id to raw resources. When saving, these asset ids are replaced """ + headers: dict[str, construct.Container] _paks_for_asset_id: dict[AssetId, set[str]] _types_for_asset_id: dict[AssetId, AssetType] @@ -165,9 +165,7 @@ def _update_headers(self): self._custom_asset_ids.update(dict(json.loads(custom_names_text).items())) - self.all_paks = list( - self.provider.rglob("*.pak") - ) + self.all_paks = list(self.provider.rglob("*.pak")) for name in self.all_paks: with self.provider.open_binary(name) as f: @@ -252,8 +250,7 @@ def get_asset_format(self, asset_id: NameOrAssetId) -> type[BaseResource]: asset_type = self.get_asset_type(asset_id) return formats.resource_type_for(asset_type) - def get_parsed_asset(self, asset_id: NameOrAssetId, *, - type_hint: type[T] = BaseResource) -> T: + def get_parsed_asset(self, asset_id: NameOrAssetId, *, type_hint: type[T] = BaseResource) -> T: """ Gets the resource with the given name and decodes it based on the extension. """ @@ -261,8 +258,7 @@ def get_parsed_asset(self, asset_id: NameOrAssetId, *, if type_hint is not BaseResource and type_hint != format_class: raise ValueError(f"type_hint was {type_hint}, pak listed {format_class}") - return format_class.parse(self.get_raw_asset(asset_id).data, target_game=self.target_game, - asset_manager=self) + return format_class.parse(self.get_raw_asset(asset_id).data, target_game=self.target_game, asset_manager=self) def get_file(self, path: NameOrAssetId, type_hint: type[T] = BaseResource) -> T: """ @@ -290,8 +286,7 @@ def register_custom_asset_name(self, name: str, asset_id: AssetId): def get_custom_asset(self, name: str) -> AssetId | None: return self._custom_asset_ids.get(name) - def add_new_asset(self, name: str, new_data: Resource, - in_paks: typing.Iterable[str] = ()) -> AssetId: + def add_new_asset(self, name: str, new_data: Resource, in_paks: typing.Iterable[str] = ()) -> AssetId: """ Adds an asset that doesn't already exist. :return: Asset id of the new asset. @@ -398,8 +393,11 @@ def get_pak(self, pak_name: str) -> Pak: return self._in_memory_paks[pak_name] - def _get_dependencies_for_asset(self, asset_id: NameOrAssetId, must_exist: bool, - ) -> Iterator[Dependency]: + def _get_dependencies_for_asset( + self, + asset_id: NameOrAssetId, + must_exist: bool, + ) -> Iterator[Dependency]: if not self.target_game.is_valid_asset_id(asset_id): return @@ -500,15 +498,11 @@ def get_audio_group_dependency(self, sound_id: int) -> Iterator[Dependency]: if self._audio_group_dependency is None: self._audio_group_dependency = tuple( - self.get_file(asset, Dgrp) - for asset in self.target_game.audio_group_dependencies() + self.get_file(asset, Dgrp) for asset in self.target_game.audio_group_dependencies() ) dep = Dependency("AGSC", agsc, False) - if any( - (dep in deps.direct_dependencies) - for deps in self._audio_group_dependency - ): + if any((dep in deps.direct_dependencies) for deps in self._audio_group_dependency): return else: yield dep diff --git a/src/retro_data_structures/base_resource.py b/src/retro_data_structures/base_resource.py index 561f711..005c53f 100644 --- a/src/retro_data_structures/base_resource.py +++ b/src/retro_data_structures/base_resource.py @@ -51,10 +51,8 @@ def resource_type(cls) -> AssetType: raise NotImplementedError @classmethod - def parse(cls, data: bytes, target_game: Game, - asset_manager: AssetManager | None = None) -> Self: - return cls(cls.construct_class(target_game).parse(data, target_game=target_game), - target_game, asset_manager) + def parse(cls, data: bytes, target_game: Game, asset_manager: AssetManager | None = None) -> Self: + return cls(cls.construct_class(target_game).parse(data, target_game=target_game), target_game, asset_manager) def build(self) -> bytes: return self.construct_class(self.target_game).build(self._raw, target_game=self.target_game) @@ -115,4 +113,5 @@ class RawResource(typing.NamedTuple): data: bytes compressed: bool = False + Resource = RawResource | BaseResource diff --git a/src/retro_data_structures/cli.py b/src/retro_data_structures/cli.py index b5ed4d3..9a6f581 100644 --- a/src/retro_data_structures/cli.py +++ b/src/retro_data_structures/cli.py @@ -181,7 +181,6 @@ def do_decode_from_pak(args): print(asset_manager.get_parsed_asset(asset_id).raw) - def do_find_in_paks(args): game: Game = args.game asset_id: int = args.asset_id @@ -199,9 +198,7 @@ def do_extract(args): target_asset = asset_manager.get_raw_asset(asset_id) destination.mkdir(parents=True, exist_ok=True) - destination.joinpath(f"{asset_id}.{target_asset.type.lower()}").write_bytes( - target_asset.data - ) + destination.joinpath(f"{asset_id}.{target_asset.type.lower()}").write_bytes(target_asset.data) def list_dependencies(args): @@ -264,9 +261,7 @@ def id_generator(asset_type): print(f"* Dependency: {dependency[1]:08x} ({dependency[0]})") print("==================\n>> All converted assets") - reverse_converted_ids: dict[AssetId, tuple[Game, AssetId]] = { - v: k for k, v in converter.converted_ids.items() - } + reverse_converted_ids: dict[AssetId, tuple[Game, AssetId]] = {v: k for k, v in converter.converted_ids.items()} for converted_asset in converter.converted_assets.values(): print( diff --git a/src/retro_data_structures/compression.py b/src/retro_data_structures/compression.py index 4ab6695..ef2dc19 100644 --- a/src/retro_data_structures/compression.py +++ b/src/retro_data_structures/compression.py @@ -83,14 +83,15 @@ def _decode(self, segments, context, path): return b"".join(segments) def _encode(self, uncompressed, context, path): - decompressed_size = construct.evaluate(self.decompressed_size, context) if decompressed_size != len(uncompressed): - raise ValueError("Decompressed size {} doesn't match size of data to compress ({}) at {}".format( - decompressed_size, - len(uncompressed), - path, - )) + raise ValueError( + "Decompressed size {} doesn't match size of data to compress ({}) at {}".format( + decompressed_size, + len(uncompressed), + path, + ) + ) segment_size = self.segment_size return [ diff --git a/src/retro_data_structures/construct_extensions/misc.py b/src/retro_data_structures/construct_extensions/misc.py index 314b9f5..197d34a 100644 --- a/src/retro_data_structures/construct_extensions/misc.py +++ b/src/retro_data_structures/construct_extensions/misc.py @@ -40,8 +40,12 @@ def _emitseq(ksy, bitwise): return [ {"id": "countfield", "type": countfield._compileprimitivetype(ksy, bitwise)}, {"id": "extra", "type": extrafield._compileprimitivetype(ksy, bitwise)}, - {"id": "data", "type": subcon._compileprimitivetype(ksy, bitwise), - "repeat": "expr", "repeat_expr": "countfield"}, + { + "id": "data", + "type": subcon._compileprimitivetype(ksy, bitwise), + "repeat": "expr", + "repeat_expr": "countfield", + }, ] macro._emitseq = _emitseq diff --git a/src/retro_data_structures/conversion/anim.py b/src/retro_data_structures/conversion/anim.py index 6462b4e..7400046 100644 --- a/src/retro_data_structures/conversion/anim.py +++ b/src/retro_data_structures/conversion/anim.py @@ -78,8 +78,10 @@ def convert_from_prime(data: Resource, details: AssetDetails, converter: AssetCo def convert_from_echoes( # noqa: PLR0912 Too many branches - data: Resource, details: AssetDetails, converter: AssetConverter, - ): + data: Resource, + details: AssetDetails, + converter: AssetConverter, +): if converter.target_game != Game.PRIME: raise UnsupportedTargetGame(Game.ECHOES, converter.target_game) diff --git a/src/retro_data_structures/conversion/asset_converter.py b/src/retro_data_structures/conversion/asset_converter.py index c7e5c9d..03924b0 100644 --- a/src/retro_data_structures/conversion/asset_converter.py +++ b/src/retro_data_structures/conversion/asset_converter.py @@ -48,11 +48,11 @@ class AssetConverter: converted_assets: dict[AssetId, ConvertedAsset] def __init__( - self, - target_game: Game, - asset_providers: dict[Game, AssetManager], - id_generator: IdGenerator, - converters: Callable[[AssetDetails], ResourceConverter], + self, + target_game: Game, + asset_providers: dict[Game, AssetManager], + id_generator: IdGenerator, + converters: Callable[[AssetDetails], ResourceConverter], ): self.target_game = target_game self.asset_providers = asset_providers @@ -63,7 +63,7 @@ def __init__( self._being_converted = set() def convert_id( - self, asset_id: AssetId | None, source_game: Game, *, missing_assets_as_invalid: bool = True + self, asset_id: AssetId | None, source_game: Game, *, missing_assets_as_invalid: bool = True ) -> AssetId: if asset_id is not None and source_game.is_valid_asset_id(asset_id): try: diff --git a/src/retro_data_structures/conversion/part.py b/src/retro_data_structures/conversion/part.py index 08d8e35..88a127e 100644 --- a/src/retro_data_structures/conversion/part.py +++ b/src/retro_data_structures/conversion/part.py @@ -19,10 +19,7 @@ def upgrade(data, converter: AssetConverter, source_game: Game): def _downgrade_color_mdao(element): - if ( - element["body"]["body"]["a"]["type"] == "KEYE" - and element["body"]["body"]["b"]["type"] == "KEYP" - ): + if element["body"]["body"]["a"]["type"] == "KEYE" and element["body"]["body"]["b"]["type"] == "KEYP": org_colr_mado_a_keye = element["body"]["body"]["a"]["body"]["keys"] new_colr_cnst_a_keyp_a = copy.deepcopy(element["body"]["body"]["a"]) new_colr_cnst_a_keyp_b = copy.deepcopy(element["body"]["body"]["a"]) @@ -54,10 +51,7 @@ def _downgrade_color_mdao(element): def _downgrade_color_mult(element): # noqa: PLR0915 Too many statements - if ( - element["body"]["body"]["a"]["type"] == "PULS" - and element["body"]["body"]["b"]["type"] == "KEYP" - ): + if element["body"]["body"]["a"]["type"] == "PULS" and element["body"]["body"]["b"]["type"] == "KEYP": org_colr_mult_b_keyp = element["body"]["body"]["b"]["body"]["keys"] new_colr_a_c_mult_b_keyp_a = copy.deepcopy(element["body"]["body"]["b"]) new_colr_a_c_mult_b_keyp_b = copy.deepcopy(element["body"]["body"]["b"]) @@ -75,8 +69,8 @@ def _downgrade_color_mult(element): # noqa: PLR0915 Too many statements new_colr_a_c_mult_b_keyp_d["body"]["keys"][i] = key[3] if ( - element["body"]["body"]["a"]["body"]["c"]["type"] == "KEYP" - and element["body"]["body"]["a"]["body"]["d"]["type"] == "KEYP" + element["body"]["body"]["a"]["body"]["c"]["type"] == "KEYP" + and element["body"]["body"]["a"]["body"]["d"]["type"] == "KEYP" ): org_colr_mult_a_c_keyp = element["body"]["body"]["a"]["body"]["c"]["body"]["keys"] new_colr_a_c_mult_a_keyp_c_a = copy.deepcopy(element["body"]["body"]["a"]["body"]["c"]) @@ -294,8 +288,8 @@ def downgrade(data, converter: AssetConverter, source_game: Game): # noqa: PLR0 if element["type"] == "EMTR": if element["body"]["type"] == "SEMR": if ( - element["body"]["body"]["a"]["type"] == "RNDV" - and element["body"]["body"]["b"]["type"] == "RNDV" + element["body"]["body"]["a"]["type"] == "RNDV" + and element["body"]["body"]["b"]["type"] == "RNDV" ): element["body"]["type"] = "SPHE" element["body"]["body"] = { @@ -319,29 +313,20 @@ def downgrade(data, converter: AssetConverter, source_game: Game): # noqa: PLR0 }, } if ( - element["body"]["body"]["a"]["type"] == "RNDV" - and element["body"]["body"]["b"]["type"] == "CNST" + element["body"]["body"]["a"]["type"] == "RNDV" + and element["body"]["body"]["b"]["type"] == "CNST" ): element["body"]["type"] = "SPHE" element["body"]["body"] = { - "a": { - "type": "RTOV", - "body": { - "type": "CNST", - "body": 0 - } - }, + "a": {"type": "RTOV", "body": {"type": "CNST", "body": 0}}, "b": element["body"]["body"]["a"]["body"], "c": { "type": "RAND", "body": { - "a": { - "type": "CNST", - "body": 0 - }, - "b": element["body"]["body"]["b"]["body"]["a"] - } - } + "a": {"type": "CNST", "body": 0}, + "b": element["body"]["body"]["b"]["body"]["a"], + }, + }, } if element["body"]["type"] == "ELPS": element["body"]["type"] = "SPHE" diff --git a/src/retro_data_structures/crc.py b/src/retro_data_structures/crc.py index e5f2ab0..8699ba4 100644 --- a/src/retro_data_structures/crc.py +++ b/src/retro_data_structures/crc.py @@ -4,89 +4,520 @@ from __future__ import annotations _crc32_constants = [ - 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, - 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, - 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, - 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, - 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, - 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, - 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, - 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, - 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, - 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, - 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, - 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, - 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, - 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, - 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, - 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, - 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, - 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, - 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, - 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, - 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, - 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, - 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, - 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, - 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, - 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, - 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, - 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, - 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D, + 0x00000000, + 0x77073096, + 0xEE0E612C, + 0x990951BA, + 0x076DC419, + 0x706AF48F, + 0xE963A535, + 0x9E6495A3, + 0x0EDB8832, + 0x79DCB8A4, + 0xE0D5E91E, + 0x97D2D988, + 0x09B64C2B, + 0x7EB17CBD, + 0xE7B82D07, + 0x90BF1D91, + 0x1DB71064, + 0x6AB020F2, + 0xF3B97148, + 0x84BE41DE, + 0x1ADAD47D, + 0x6DDDE4EB, + 0xF4D4B551, + 0x83D385C7, + 0x136C9856, + 0x646BA8C0, + 0xFD62F97A, + 0x8A65C9EC, + 0x14015C4F, + 0x63066CD9, + 0xFA0F3D63, + 0x8D080DF5, + 0x3B6E20C8, + 0x4C69105E, + 0xD56041E4, + 0xA2677172, + 0x3C03E4D1, + 0x4B04D447, + 0xD20D85FD, + 0xA50AB56B, + 0x35B5A8FA, + 0x42B2986C, + 0xDBBBC9D6, + 0xACBCF940, + 0x32D86CE3, + 0x45DF5C75, + 0xDCD60DCF, + 0xABD13D59, + 0x26D930AC, + 0x51DE003A, + 0xC8D75180, + 0xBFD06116, + 0x21B4F4B5, + 0x56B3C423, + 0xCFBA9599, + 0xB8BDA50F, + 0x2802B89E, + 0x5F058808, + 0xC60CD9B2, + 0xB10BE924, + 0x2F6F7C87, + 0x58684C11, + 0xC1611DAB, + 0xB6662D3D, + 0x76DC4190, + 0x01DB7106, + 0x98D220BC, + 0xEFD5102A, + 0x71B18589, + 0x06B6B51F, + 0x9FBFE4A5, + 0xE8B8D433, + 0x7807C9A2, + 0x0F00F934, + 0x9609A88E, + 0xE10E9818, + 0x7F6A0DBB, + 0x086D3D2D, + 0x91646C97, + 0xE6635C01, + 0x6B6B51F4, + 0x1C6C6162, + 0x856530D8, + 0xF262004E, + 0x6C0695ED, + 0x1B01A57B, + 0x8208F4C1, + 0xF50FC457, + 0x65B0D9C6, + 0x12B7E950, + 0x8BBEB8EA, + 0xFCB9887C, + 0x62DD1DDF, + 0x15DA2D49, + 0x8CD37CF3, + 0xFBD44C65, + 0x4DB26158, + 0x3AB551CE, + 0xA3BC0074, + 0xD4BB30E2, + 0x4ADFA541, + 0x3DD895D7, + 0xA4D1C46D, + 0xD3D6F4FB, + 0x4369E96A, + 0x346ED9FC, + 0xAD678846, + 0xDA60B8D0, + 0x44042D73, + 0x33031DE5, + 0xAA0A4C5F, + 0xDD0D7CC9, + 0x5005713C, + 0x270241AA, + 0xBE0B1010, + 0xC90C2086, + 0x5768B525, + 0x206F85B3, + 0xB966D409, + 0xCE61E49F, + 0x5EDEF90E, + 0x29D9C998, + 0xB0D09822, + 0xC7D7A8B4, + 0x59B33D17, + 0x2EB40D81, + 0xB7BD5C3B, + 0xC0BA6CAD, + 0xEDB88320, + 0x9ABFB3B6, + 0x03B6E20C, + 0x74B1D29A, + 0xEAD54739, + 0x9DD277AF, + 0x04DB2615, + 0x73DC1683, + 0xE3630B12, + 0x94643B84, + 0x0D6D6A3E, + 0x7A6A5AA8, + 0xE40ECF0B, + 0x9309FF9D, + 0x0A00AE27, + 0x7D079EB1, + 0xF00F9344, + 0x8708A3D2, + 0x1E01F268, + 0x6906C2FE, + 0xF762575D, + 0x806567CB, + 0x196C3671, + 0x6E6B06E7, + 0xFED41B76, + 0x89D32BE0, + 0x10DA7A5A, + 0x67DD4ACC, + 0xF9B9DF6F, + 0x8EBEEFF9, + 0x17B7BE43, + 0x60B08ED5, + 0xD6D6A3E8, + 0xA1D1937E, + 0x38D8C2C4, + 0x4FDFF252, + 0xD1BB67F1, + 0xA6BC5767, + 0x3FB506DD, + 0x48B2364B, + 0xD80D2BDA, + 0xAF0A1B4C, + 0x36034AF6, + 0x41047A60, + 0xDF60EFC3, + 0xA867DF55, + 0x316E8EEF, + 0x4669BE79, + 0xCB61B38C, + 0xBC66831A, + 0x256FD2A0, + 0x5268E236, + 0xCC0C7795, + 0xBB0B4703, + 0x220216B9, + 0x5505262F, + 0xC5BA3BBE, + 0xB2BD0B28, + 0x2BB45A92, + 0x5CB36A04, + 0xC2D7FFA7, + 0xB5D0CF31, + 0x2CD99E8B, + 0x5BDEAE1D, + 0x9B64C2B0, + 0xEC63F226, + 0x756AA39C, + 0x026D930A, + 0x9C0906A9, + 0xEB0E363F, + 0x72076785, + 0x05005713, + 0x95BF4A82, + 0xE2B87A14, + 0x7BB12BAE, + 0x0CB61B38, + 0x92D28E9B, + 0xE5D5BE0D, + 0x7CDCEFB7, + 0x0BDBDF21, + 0x86D3D2D4, + 0xF1D4E242, + 0x68DDB3F8, + 0x1FDA836E, + 0x81BE16CD, + 0xF6B9265B, + 0x6FB077E1, + 0x18B74777, + 0x88085AE6, + 0xFF0F6A70, + 0x66063BCA, + 0x11010B5C, + 0x8F659EFF, + 0xF862AE69, + 0x616BFFD3, + 0x166CCF45, + 0xA00AE278, + 0xD70DD2EE, + 0x4E048354, + 0x3903B3C2, + 0xA7672661, + 0xD06016F7, + 0x4969474D, + 0x3E6E77DB, + 0xAED16A4A, + 0xD9D65ADC, + 0x40DF0B66, + 0x37D83BF0, + 0xA9BCAE53, + 0xDEBB9EC5, + 0x47B2CF7F, + 0x30B5FFE9, + 0xBDBDF21C, + 0xCABAC28A, + 0x53B39330, + 0x24B4A3A6, + 0xBAD03605, + 0xCDD70693, + 0x54DE5729, + 0x23D967BF, + 0xB3667A2E, + 0xC4614AB8, + 0x5D681B02, + 0x2A6F2B94, + 0xB40BBE37, + 0xC30C8EA1, + 0x5A05DF1B, + 0x2D02EF8D, ] _crc64_constants = [ - 0x0000000000000000, 0xB32E4CBE03A75F6F, 0xF4843657A840A05B, 0x47AA7AE9ABE7FF34, 0x7BD0C384FF8F5E33, - 0xC8FE8F3AFC28015C, 0x8F54F5D357CFFE68, 0x3C7AB96D5468A107, 0xF7A18709FF1EBC66, 0x448FCBB7FCB9E309, - 0x0325B15E575E1C3D, 0xB00BFDE054F94352, 0x8C71448D0091E255, 0x3F5F08330336BD3A, 0x78F572DAA8D1420E, - 0xCBDB3E64AB761D61, 0x7D9BA13851336649, 0xCEB5ED8652943926, 0x891F976FF973C612, 0x3A31DBD1FAD4997D, - 0x064B62BCAEBC387A, 0xB5652E02AD1B6715, 0xF2CF54EB06FC9821, 0x41E11855055BC74E, 0x8A3A2631AE2DDA2F, - 0x39146A8FAD8A8540, 0x7EBE1066066D7A74, 0xCD905CD805CA251B, 0xF1EAE5B551A2841C, 0x42C4A90B5205DB73, - 0x056ED3E2F9E22447, 0xB6409F5CFA457B28, 0xFB374270A266CC92, 0x48190ECEA1C193FD, 0x0FB374270A266CC9, - 0xBC9D3899098133A6, 0x80E781F45DE992A1, 0x33C9CD4A5E4ECDCE, 0x7463B7A3F5A932FA, 0xC74DFB1DF60E6D95, - 0x0C96C5795D7870F4, 0xBFB889C75EDF2F9B, 0xF812F32EF538D0AF, 0x4B3CBF90F69F8FC0, 0x774606FDA2F72EC7, - 0xC4684A43A15071A8, 0x83C230AA0AB78E9C, 0x30EC7C140910D1F3, 0x86ACE348F355AADB, 0x3582AFF6F0F2F5B4, - 0x7228D51F5B150A80, 0xC10699A158B255EF, 0xFD7C20CC0CDAF4E8, 0x4E526C720F7DAB87, 0x09F8169BA49A54B3, - 0xBAD65A25A73D0BDC, 0x710D64410C4B16BD, 0xC22328FF0FEC49D2, 0x85895216A40BB6E6, 0x36A71EA8A7ACE989, - 0x0ADDA7C5F3C4488E, 0xB9F3EB7BF06317E1, 0xFE5991925B84E8D5, 0x4D77DD2C5823B7BA, 0x64B62BCAEBC387A1, - 0xD7986774E864D8CE, 0x90321D9D438327FA, 0x231C512340247895, 0x1F66E84E144CD992, 0xAC48A4F017EB86FD, - 0xEBE2DE19BC0C79C9, 0x58CC92A7BFAB26A6, 0x9317ACC314DD3BC7, 0x2039E07D177A64A8, 0x67939A94BC9D9B9C, - 0xD4BDD62ABF3AC4F3, 0xE8C76F47EB5265F4, 0x5BE923F9E8F53A9B, 0x1C4359104312C5AF, 0xAF6D15AE40B59AC0, - 0x192D8AF2BAF0E1E8, 0xAA03C64CB957BE87, 0xEDA9BCA512B041B3, 0x5E87F01B11171EDC, 0x62FD4976457FBFDB, - 0xD1D305C846D8E0B4, 0x96797F21ED3F1F80, 0x2557339FEE9840EF, 0xEE8C0DFB45EE5D8E, 0x5DA24145464902E1, - 0x1A083BACEDAEFDD5, 0xA9267712EE09A2BA, 0x955CCE7FBA6103BD, 0x267282C1B9C65CD2, 0x61D8F8281221A3E6, - 0xD2F6B4961186FC89, 0x9F8169BA49A54B33, 0x2CAF25044A02145C, 0x6B055FEDE1E5EB68, 0xD82B1353E242B407, - 0xE451AA3EB62A1500, 0x577FE680B58D4A6F, 0x10D59C691E6AB55B, 0xA3FBD0D71DCDEA34, 0x6820EEB3B6BBF755, - 0xDB0EA20DB51CA83A, 0x9CA4D8E41EFB570E, 0x2F8A945A1D5C0861, 0x13F02D374934A966, 0xA0DE61894A93F609, - 0xE7741B60E174093D, 0x545A57DEE2D35652, 0xE21AC88218962D7A, 0x5134843C1B317215, 0x169EFED5B0D68D21, - 0xA5B0B26BB371D24E, 0x99CA0B06E7197349, 0x2AE447B8E4BE2C26, 0x6D4E3D514F59D312, 0xDE6071EF4CFE8C7D, - 0x15BB4F8BE788911C, 0xA6950335E42FCE73, 0xE13F79DC4FC83147, 0x521135624C6F6E28, 0x6E6B8C0F1807CF2F, - 0xDD45C0B11BA09040, 0x9AEFBA58B0476F74, 0x29C1F6E6B3E0301B, 0xC96C5795D7870F42, 0x7A421B2BD420502D, - 0x3DE861C27FC7AF19, 0x8EC62D7C7C60F076, 0xB2BC941128085171, 0x0192D8AF2BAF0E1E, 0x4638A2468048F12A, - 0xF516EEF883EFAE45, 0x3ECDD09C2899B324, 0x8DE39C222B3EEC4B, 0xCA49E6CB80D9137F, 0x7967AA75837E4C10, - 0x451D1318D716ED17, 0xF6335FA6D4B1B278, 0xB199254F7F564D4C, 0x02B769F17CF11223, 0xB4F7F6AD86B4690B, - 0x07D9BA1385133664, 0x4073C0FA2EF4C950, 0xF35D8C442D53963F, 0xCF273529793B3738, 0x7C0979977A9C6857, - 0x3BA3037ED17B9763, 0x888D4FC0D2DCC80C, 0x435671A479AAD56D, 0xF0783D1A7A0D8A02, 0xB7D247F3D1EA7536, - 0x04FC0B4DD24D2A59, 0x3886B22086258B5E, 0x8BA8FE9E8582D431, 0xCC0284772E652B05, 0x7F2CC8C92DC2746A, - 0x325B15E575E1C3D0, 0x8175595B76469CBF, 0xC6DF23B2DDA1638B, 0x75F16F0CDE063CE4, 0x498BD6618A6E9DE3, - 0xFAA59ADF89C9C28C, 0xBD0FE036222E3DB8, 0x0E21AC88218962D7, 0xC5FA92EC8AFF7FB6, 0x76D4DE52895820D9, - 0x317EA4BB22BFDFED, 0x8250E80521188082, 0xBE2A516875702185, 0x0D041DD676D77EEA, 0x4AAE673FDD3081DE, - 0xF9802B81DE97DEB1, 0x4FC0B4DD24D2A599, 0xFCEEF8632775FAF6, 0xBB44828A8C9205C2, 0x086ACE348F355AAD, - 0x34107759DB5DFBAA, 0x873E3BE7D8FAA4C5, 0xC094410E731D5BF1, 0x73BA0DB070BA049E, 0xB86133D4DBCC19FF, - 0x0B4F7F6AD86B4690, 0x4CE50583738CB9A4, 0xFFCB493D702BE6CB, 0xC3B1F050244347CC, 0x709FBCEE27E418A3, - 0x3735C6078C03E797, 0x841B8AB98FA4B8F8, 0xADDA7C5F3C4488E3, 0x1EF430E13FE3D78C, 0x595E4A08940428B8, - 0xEA7006B697A377D7, 0xD60ABFDBC3CBD6D0, 0x6524F365C06C89BF, 0x228E898C6B8B768B, 0x91A0C532682C29E4, - 0x5A7BFB56C35A3485, 0xE955B7E8C0FD6BEA, 0xAEFFCD016B1A94DE, 0x1DD181BF68BDCBB1, 0x21AB38D23CD56AB6, - 0x9285746C3F7235D9, 0xD52F0E859495CAED, 0x6601423B97329582, 0xD041DD676D77EEAA, 0x636F91D96ED0B1C5, - 0x24C5EB30C5374EF1, 0x97EBA78EC690119E, 0xAB911EE392F8B099, 0x18BF525D915FEFF6, 0x5F1528B43AB810C2, - 0xEC3B640A391F4FAD, 0x27E05A6E926952CC, 0x94CE16D091CE0DA3, 0xD3646C393A29F297, 0x604A2087398EADF8, - 0x5C3099EA6DE60CFF, 0xEF1ED5546E415390, 0xA8B4AFBDC5A6ACA4, 0x1B9AE303C601F3CB, 0x56ED3E2F9E224471, - 0xE5C372919D851B1E, 0xA26908783662E42A, 0x114744C635C5BB45, 0x2D3DFDAB61AD1A42, 0x9E13B115620A452D, - 0xD9B9CBFCC9EDBA19, 0x6A978742CA4AE576, 0xA14CB926613CF817, 0x1262F598629BA778, 0x55C88F71C97C584C, - 0xE6E6C3CFCADB0723, 0xDA9C7AA29EB3A624, 0x69B2361C9D14F94B, 0x2E184CF536F3067F, 0x9D36004B35545910, - 0x2B769F17CF112238, 0x9858D3A9CCB67D57, 0xDFF2A94067518263, 0x6CDCE5FE64F6DD0C, 0x50A65C93309E7C0B, - 0xE388102D33392364, 0xA4226AC498DEDC50, 0x170C267A9B79833F, 0xDCD7181E300F9E5E, 0x6FF954A033A8C131, - 0x28532E49984F3E05, 0x9B7D62F79BE8616A, 0xA707DB9ACF80C06D, 0x14299724CC279F02, 0x5383EDCD67C06036, + 0x0000000000000000, + 0xB32E4CBE03A75F6F, + 0xF4843657A840A05B, + 0x47AA7AE9ABE7FF34, + 0x7BD0C384FF8F5E33, + 0xC8FE8F3AFC28015C, + 0x8F54F5D357CFFE68, + 0x3C7AB96D5468A107, + 0xF7A18709FF1EBC66, + 0x448FCBB7FCB9E309, + 0x0325B15E575E1C3D, + 0xB00BFDE054F94352, + 0x8C71448D0091E255, + 0x3F5F08330336BD3A, + 0x78F572DAA8D1420E, + 0xCBDB3E64AB761D61, + 0x7D9BA13851336649, + 0xCEB5ED8652943926, + 0x891F976FF973C612, + 0x3A31DBD1FAD4997D, + 0x064B62BCAEBC387A, + 0xB5652E02AD1B6715, + 0xF2CF54EB06FC9821, + 0x41E11855055BC74E, + 0x8A3A2631AE2DDA2F, + 0x39146A8FAD8A8540, + 0x7EBE1066066D7A74, + 0xCD905CD805CA251B, + 0xF1EAE5B551A2841C, + 0x42C4A90B5205DB73, + 0x056ED3E2F9E22447, + 0xB6409F5CFA457B28, + 0xFB374270A266CC92, + 0x48190ECEA1C193FD, + 0x0FB374270A266CC9, + 0xBC9D3899098133A6, + 0x80E781F45DE992A1, + 0x33C9CD4A5E4ECDCE, + 0x7463B7A3F5A932FA, + 0xC74DFB1DF60E6D95, + 0x0C96C5795D7870F4, + 0xBFB889C75EDF2F9B, + 0xF812F32EF538D0AF, + 0x4B3CBF90F69F8FC0, + 0x774606FDA2F72EC7, + 0xC4684A43A15071A8, + 0x83C230AA0AB78E9C, + 0x30EC7C140910D1F3, + 0x86ACE348F355AADB, + 0x3582AFF6F0F2F5B4, + 0x7228D51F5B150A80, + 0xC10699A158B255EF, + 0xFD7C20CC0CDAF4E8, + 0x4E526C720F7DAB87, + 0x09F8169BA49A54B3, + 0xBAD65A25A73D0BDC, + 0x710D64410C4B16BD, + 0xC22328FF0FEC49D2, + 0x85895216A40BB6E6, + 0x36A71EA8A7ACE989, + 0x0ADDA7C5F3C4488E, + 0xB9F3EB7BF06317E1, + 0xFE5991925B84E8D5, + 0x4D77DD2C5823B7BA, + 0x64B62BCAEBC387A1, + 0xD7986774E864D8CE, + 0x90321D9D438327FA, + 0x231C512340247895, + 0x1F66E84E144CD992, + 0xAC48A4F017EB86FD, + 0xEBE2DE19BC0C79C9, + 0x58CC92A7BFAB26A6, + 0x9317ACC314DD3BC7, + 0x2039E07D177A64A8, + 0x67939A94BC9D9B9C, + 0xD4BDD62ABF3AC4F3, + 0xE8C76F47EB5265F4, + 0x5BE923F9E8F53A9B, + 0x1C4359104312C5AF, + 0xAF6D15AE40B59AC0, + 0x192D8AF2BAF0E1E8, + 0xAA03C64CB957BE87, + 0xEDA9BCA512B041B3, + 0x5E87F01B11171EDC, + 0x62FD4976457FBFDB, + 0xD1D305C846D8E0B4, + 0x96797F21ED3F1F80, + 0x2557339FEE9840EF, + 0xEE8C0DFB45EE5D8E, + 0x5DA24145464902E1, + 0x1A083BACEDAEFDD5, + 0xA9267712EE09A2BA, + 0x955CCE7FBA6103BD, + 0x267282C1B9C65CD2, + 0x61D8F8281221A3E6, + 0xD2F6B4961186FC89, + 0x9F8169BA49A54B33, + 0x2CAF25044A02145C, + 0x6B055FEDE1E5EB68, + 0xD82B1353E242B407, + 0xE451AA3EB62A1500, + 0x577FE680B58D4A6F, + 0x10D59C691E6AB55B, + 0xA3FBD0D71DCDEA34, + 0x6820EEB3B6BBF755, + 0xDB0EA20DB51CA83A, + 0x9CA4D8E41EFB570E, + 0x2F8A945A1D5C0861, + 0x13F02D374934A966, + 0xA0DE61894A93F609, + 0xE7741B60E174093D, + 0x545A57DEE2D35652, + 0xE21AC88218962D7A, + 0x5134843C1B317215, + 0x169EFED5B0D68D21, + 0xA5B0B26BB371D24E, + 0x99CA0B06E7197349, + 0x2AE447B8E4BE2C26, + 0x6D4E3D514F59D312, + 0xDE6071EF4CFE8C7D, + 0x15BB4F8BE788911C, + 0xA6950335E42FCE73, + 0xE13F79DC4FC83147, + 0x521135624C6F6E28, + 0x6E6B8C0F1807CF2F, + 0xDD45C0B11BA09040, + 0x9AEFBA58B0476F74, + 0x29C1F6E6B3E0301B, + 0xC96C5795D7870F42, + 0x7A421B2BD420502D, + 0x3DE861C27FC7AF19, + 0x8EC62D7C7C60F076, + 0xB2BC941128085171, + 0x0192D8AF2BAF0E1E, + 0x4638A2468048F12A, + 0xF516EEF883EFAE45, + 0x3ECDD09C2899B324, + 0x8DE39C222B3EEC4B, + 0xCA49E6CB80D9137F, + 0x7967AA75837E4C10, + 0x451D1318D716ED17, + 0xF6335FA6D4B1B278, + 0xB199254F7F564D4C, + 0x02B769F17CF11223, + 0xB4F7F6AD86B4690B, + 0x07D9BA1385133664, + 0x4073C0FA2EF4C950, + 0xF35D8C442D53963F, + 0xCF273529793B3738, + 0x7C0979977A9C6857, + 0x3BA3037ED17B9763, + 0x888D4FC0D2DCC80C, + 0x435671A479AAD56D, + 0xF0783D1A7A0D8A02, + 0xB7D247F3D1EA7536, + 0x04FC0B4DD24D2A59, + 0x3886B22086258B5E, + 0x8BA8FE9E8582D431, + 0xCC0284772E652B05, + 0x7F2CC8C92DC2746A, + 0x325B15E575E1C3D0, + 0x8175595B76469CBF, + 0xC6DF23B2DDA1638B, + 0x75F16F0CDE063CE4, + 0x498BD6618A6E9DE3, + 0xFAA59ADF89C9C28C, + 0xBD0FE036222E3DB8, + 0x0E21AC88218962D7, + 0xC5FA92EC8AFF7FB6, + 0x76D4DE52895820D9, + 0x317EA4BB22BFDFED, + 0x8250E80521188082, + 0xBE2A516875702185, + 0x0D041DD676D77EEA, + 0x4AAE673FDD3081DE, + 0xF9802B81DE97DEB1, + 0x4FC0B4DD24D2A599, + 0xFCEEF8632775FAF6, + 0xBB44828A8C9205C2, + 0x086ACE348F355AAD, + 0x34107759DB5DFBAA, + 0x873E3BE7D8FAA4C5, + 0xC094410E731D5BF1, + 0x73BA0DB070BA049E, + 0xB86133D4DBCC19FF, + 0x0B4F7F6AD86B4690, + 0x4CE50583738CB9A4, + 0xFFCB493D702BE6CB, + 0xC3B1F050244347CC, + 0x709FBCEE27E418A3, + 0x3735C6078C03E797, + 0x841B8AB98FA4B8F8, + 0xADDA7C5F3C4488E3, + 0x1EF430E13FE3D78C, + 0x595E4A08940428B8, + 0xEA7006B697A377D7, + 0xD60ABFDBC3CBD6D0, + 0x6524F365C06C89BF, + 0x228E898C6B8B768B, + 0x91A0C532682C29E4, + 0x5A7BFB56C35A3485, + 0xE955B7E8C0FD6BEA, + 0xAEFFCD016B1A94DE, + 0x1DD181BF68BDCBB1, + 0x21AB38D23CD56AB6, + 0x9285746C3F7235D9, + 0xD52F0E859495CAED, + 0x6601423B97329582, + 0xD041DD676D77EEAA, + 0x636F91D96ED0B1C5, + 0x24C5EB30C5374EF1, + 0x97EBA78EC690119E, + 0xAB911EE392F8B099, + 0x18BF525D915FEFF6, + 0x5F1528B43AB810C2, + 0xEC3B640A391F4FAD, + 0x27E05A6E926952CC, + 0x94CE16D091CE0DA3, + 0xD3646C393A29F297, + 0x604A2087398EADF8, + 0x5C3099EA6DE60CFF, + 0xEF1ED5546E415390, + 0xA8B4AFBDC5A6ACA4, + 0x1B9AE303C601F3CB, + 0x56ED3E2F9E224471, + 0xE5C372919D851B1E, + 0xA26908783662E42A, + 0x114744C635C5BB45, + 0x2D3DFDAB61AD1A42, + 0x9E13B115620A452D, + 0xD9B9CBFCC9EDBA19, + 0x6A978742CA4AE576, + 0xA14CB926613CF817, + 0x1262F598629BA778, + 0x55C88F71C97C584C, + 0xE6E6C3CFCADB0723, + 0xDA9C7AA29EB3A624, + 0x69B2361C9D14F94B, + 0x2E184CF536F3067F, + 0x9D36004B35545910, + 0x2B769F17CF112238, + 0x9858D3A9CCB67D57, + 0xDFF2A94067518263, + 0x6CDCE5FE64F6DD0C, + 0x50A65C93309E7C0B, + 0xE388102D33392364, + 0xA4226AC498DEDC50, + 0x170C267A9B79833F, + 0xDCD7181E300F9E5E, + 0x6FF954A033A8C131, + 0x28532E49984F3E05, + 0x9B7D62F79BE8616A, + 0xA707DB9ACF80C06D, + 0x14299724CC279F02, + 0x5383EDCD67C06036, 0xE0ADA17364673F59, ] @@ -98,7 +529,7 @@ def _run_crc(table: list[int], initial: int, data: bytes | str) -> int: checksum = initial for it in data: - checksum = table[(checksum & 0xff) ^ it] ^ (checksum >> 8) + checksum = table[(checksum & 0xFF) ^ it] ^ (checksum >> 8) return checksum diff --git a/src/retro_data_structures/dependencies.py b/src/retro_data_structures/dependencies.py index 82ea0fb..74b9494 100644 --- a/src/retro_data_structures/dependencies.py +++ b/src/retro_data_structures/dependencies.py @@ -53,11 +53,11 @@ def direct_dependencies_for(obj, obj_type: AssetType, target_game: Game) -> Iter def _internal_dependencies_for( - get_asset: Callable[[AssetId], Any], - target_game: Game, - asset_id: AssetId, - obj_type: AssetType, - deps_by_asset_id: dict[AssetId, set[Dependency]], + get_asset: Callable[[AssetId], Any], + target_game: Game, + asset_id: AssetId, + obj_type: AssetType, + deps_by_asset_id: dict[AssetId, set[Dependency]], ): if asset_id in deps_by_asset_id: return @@ -79,9 +79,10 @@ def _internal_dependencies_for( raise InvalidDependency(asset_id, new_asset_id, new_type) -def recursive_dependencies_for(asset_manager: AssetManager, - asset_ids: list[AssetId], - ) -> set[Dependency]: +def recursive_dependencies_for( + asset_manager: AssetManager, + asset_ids: list[AssetId], +) -> set[Dependency]: deps_by_asset_id: dict[AssetId, set[Dependency]] = {} def get_asset(aid: AssetId): @@ -116,9 +117,10 @@ def get_asset(asset_id: AssetId): return deps_by_asset_id -def recursive_dependencies_for_editor(editor: AssetManager, - asset_ids: list[AssetId], - ) -> set[Dependency]: +def recursive_dependencies_for_editor( + editor: AssetManager, + asset_ids: list[AssetId], +) -> set[Dependency]: deps_by_asset_id: dict[AssetId, set[Dependency]] = {} def _recursive(asset_id: AssetId): diff --git a/src/retro_data_structures/formats/__init__.py b/src/retro_data_structures/formats/__init__.py index 1c01a10..a58b6d9 100644 --- a/src/retro_data_structures/formats/__init__.py +++ b/src/retro_data_structures/formats/__init__.py @@ -132,9 +132,5 @@ def has_format(type_name: AssetType) -> bool: def has_resource_type(type_name: AssetType) -> bool: return type_name in ALL_RESOURCE_TYPES -__all__ = [ - "format_for", - "resource_type_for", - "has_format", - "has_resource_type" -] + +__all__ = ["format_for", "resource_type_for", "has_format", "has_resource_type"] diff --git a/src/retro_data_structures/formats/ancs.py b/src/retro_data_structures/formats/ancs.py index 87cb407..81c8a37 100644 --- a/src/retro_data_structures/formats/ancs.py +++ b/src/retro_data_structures/formats/ancs.py @@ -158,14 +158,16 @@ def _array(asset_ids: Iterable[int] | None): for asset_id in asset_ids: yield from asset_manager.get_dependencies_for_asset(asset_id, must_exist=False) - yield from _array(( - character.model_id, - character.skin_id, - character.skeleton_id, - character.frozen_model, - character.frozen_skin, - character.spatial_primitives_id - )) + yield from _array( + ( + character.model_id, + character.skin_id, + character.skeleton_id, + character.frozen_model, + character.frozen_skin, + character.spatial_primitives_id, + ) + ) # ParticleResourceData psd = character.particle_resource_data @@ -216,8 +218,8 @@ def construct_class(cls, target_game: Game) -> construct.Construct: def ancs_dependencies_for(self, char_index: int | None) -> typing.Iterator[Dependency]: def char_anims(char) -> typing.Iterator[tuple[int, str]]: for anim_name in char.animation_names: - yield next((i, a) for i, a in enumerate(self.raw.animation_set.animations) - if a.name == anim_name.name) + yield next((i, a) for i, a in enumerate(self.raw.animation_set.animations) if a.name == anim_name.name) + def char_deps(char): yield from char_dependencies_for(char, self.asset_manager) @@ -235,8 +237,9 @@ def char_deps(char): yield from evnt.dependencies_for(evnt_file.raw, self.asset_manager, char_index) elif self.raw.animation_set.event_sets is not None: - yield from evnt.dependencies_for(self.raw.animation_set.event_sets[anim_index], self.asset_manager, - char_index) + yield from evnt.dependencies_for( + self.raw.animation_set.event_sets[anim_index], self.asset_manager, char_index + ) if char_index is not None: chars = [self.raw.character_set.characters[char_index]] diff --git a/src/retro_data_structures/formats/area_collision.py b/src/retro_data_structures/formats/area_collision.py index 3da40b4..18c8ff1 100644 --- a/src/retro_data_structures/formats/area_collision.py +++ b/src/retro_data_structures/formats/area_collision.py @@ -37,6 +37,7 @@ class AreaCollisionVersion(enum.IntEnum): prime23 = 4 dkcr = 5 + VersionEnum = Enum(Int32ub, AreaCollisionVersion) _shared_materials = { @@ -77,7 +78,7 @@ class AreaCollisionVersion(enum.IntEnum): "unknown_2": 0x00100000, "unknown_3": 0x01000000, "Redundant Edge/Flipped Tri": 0x02000000, - } + }, ) _prime23_materials = dict( @@ -96,7 +97,7 @@ class AreaCollisionVersion(enum.IntEnum): "Jump Not Allowed": 0x0400000000000000, "Spider Ball": 0x2000000000000000, "Screw Attack Wall Jump": 0x4000000000000000, - } + }, ) _internal_materials = { @@ -178,8 +179,10 @@ def _encode(self, triangles, context, path): "triangle_indices" / PrefixedArray(Int32ub, Int8ub), "edges" / PrefixedArray(Int32ub, Struct(vertexA=Int16ub, vertexB=Int16ub)), "triangles" / TriangleAdapter(PrefixedArray(Int32ub, Int16ub)), - "unknowns" / If(lambda this: AreaCollisionVersion[this._.version] > AreaCollisionVersion.prime1, - PrefixedArray(Int32ub, Int16ub)), + "unknowns" + / If( + lambda this: AreaCollisionVersion[this._.version] > AreaCollisionVersion.prime1, PrefixedArray(Int32ub, Int16ub) + ), "vertices" / PrefixedArray(Int32ub, Vector3), ) @@ -192,9 +195,18 @@ def _encode(self, triangles, context, path): "version" / VersionEnum, "bounding_box" / AABox, "root_node_type" / NodeTypeEnum(Int32ub), - "octree" / Prefixed(Int32ub, Switch(this.root_node_type, { - "none": Pass, "branch": CollisionBranch, "leaf": CollisionLeaf, - })), + "octree" + / Prefixed( + Int32ub, + Switch( + this.root_node_type, + { + "none": Pass, + "branch": CollisionBranch, + "leaf": CollisionLeaf, + }, + ), + ), "collision_indices" / CollisionIndex, "_size_end" / Tell, "size" / Pointer(this._size_addr, Rebuild(Int32ub, this._size_end - this._size_start)), diff --git a/src/retro_data_structures/formats/arot.py b/src/retro_data_structures/formats/arot.py index fdb7b70..c2c6810 100644 --- a/src/retro_data_structures/formats/arot.py +++ b/src/retro_data_structures/formats/arot.py @@ -32,7 +32,7 @@ "bitmap_index" / Int16ub, "subdivision_flags" / FlagsEnum(Int16ub, x=1, y=2, z=4), "subdivisions" / Computed(this.subdivision_flags.z + this.subdivision_flags.y + this.subdivision_flags.x), - "children" / If(this.subdivisions, Array(2 ** this.subdivisions, Int16ub)), + "children" / If(this.subdivisions, Array(2**this.subdivisions, Int16ub)), ), ), ) diff --git a/src/retro_data_structures/formats/audio_group.py b/src/retro_data_structures/formats/audio_group.py index 73fd4a1..88ff787 100644 --- a/src/retro_data_structures/formats/audio_group.py +++ b/src/retro_data_structures/formats/audio_group.py @@ -37,27 +37,20 @@ def project_table(offset: int, subcon, group_type: int | None = None): - table = FocusedSeq("table", + table = FocusedSeq( + "table", "pos" / Tell, - "offset" / Pointer( - lambda ctx: ctx._._offsets + offset, - Rebuild(Int32ub, lambda ctx: ctx.pos - ctx._._start) - ), + "offset" / Pointer(lambda ctx: ctx._._offsets + offset, Rebuild(Int32ub, lambda ctx: ctx.pos - ctx._._start)), Seek(lambda ctx: ctx._._start + ctx.offset), # Check(lambda ctx: (ctx.pos - ctx._._start) == ctx.offset), - "table" / subcon + "table" / subcon, ) if group_type is None: return table - return If( - lambda ctx: ctx.group_type == group_type, - table - ) + return If(lambda ctx: ctx.group_type == group_type, table) -StandardTable = RepeatUntil( - lambda obj, lst, ctx: obj == 0xFFFF, - Int16ub -) + +StandardTable = RepeatUntil(lambda obj, lst, ctx: obj == 0xFFFF, Int16ub) Project = Struct( "_start" / Tell, @@ -73,22 +66,26 @@ def project_table(offset: int, subcon, group_type: int | None = None): "tables_table" / project_table(0x8, StandardTable), "keymaps_table" / project_table(0xC, StandardTable), "layers_table" / project_table(0x10, StandardTable), - "song_group_stuff" / If( - lambda ctx: ctx.group_type == 1, - GreedyBytes + "song_group_stuff" / If(lambda ctx: ctx.group_type == 1, GreedyBytes), + "sfx_table" + / project_table( + 0x14, + PrefixedArray( + Padded(4, Int16ub), + Padded( + 10, + Struct( + "define_id" / Int16ub, + "object_id" / Int16ub, + "priority" / Int8ub, + "max_voices" / Int8ub, + "definite_velocity" / Int8ub, + "panning" / Int8sb, + "definite_key" / Int8ub, + ), + ), + ), ), - "sfx_table" / project_table(0x14, PrefixedArray( - Padded(4, Int16ub), - Padded(10, Struct( - "define_id" / Int16ub, - "object_id" / Int16ub, - "priority" / Int8ub, - "max_voices" / Int8ub, - "definite_velocity" / Int8ub, - "panning" / Int8sb, - "definite_key" / Int8ub, - )) - )) ) Sample = GreedyBytes @@ -99,13 +96,10 @@ def project_table(offset: int, subcon, group_type: int | None = None): def mp1_sized_chunk(subcon): return Prefixed(Int32ub, subcon) + def mp2_sized_chunk(offset: int, subcon): - return Prefixed( - Pointer( - lambda ctx: ctx._size_offsets + offset, - Int32ub - ), subcon - ) + return Prefixed(Pointer(lambda ctx: ctx._size_offsets + offset, Int32ub), subcon) + DataMP1 = Struct( "audio_directory" / String, @@ -113,7 +107,7 @@ def mp2_sized_chunk(offset: int, subcon): "pool" / mp1_sized_chunk(Pool), "project" / mp1_sized_chunk(Project), "sample" / mp1_sized_chunk(Sample), - "sample_directory" / mp1_sized_chunk(SampleDirectory) + "sample_directory" / mp1_sized_chunk(SampleDirectory), ) DataMP2 = Struct( @@ -128,14 +122,11 @@ def mp2_sized_chunk(offset: int, subcon): "sample" / mp2_sized_chunk(12, Sample), ) -AGSC = game_check.current_game_at_least_else( - game_check.Game.ECHOES, - DataMP2, - DataMP1 -) +AGSC = game_check.current_game_at_least_else(game_check.Game.ECHOES, DataMP2, DataMP1) ATBL = PrefixedArray(Int32ub, Int16ub) + class Atbl(BaseResource): @classmethod def construct_class(cls, target_game: Game) -> Construct: diff --git a/src/retro_data_structures/formats/chunk_descriptor.py b/src/retro_data_structures/formats/chunk_descriptor.py index 38ef090..c015fba 100644 --- a/src/retro_data_structures/formats/chunk_descriptor.py +++ b/src/retro_data_structures/formats/chunk_descriptor.py @@ -25,8 +25,9 @@ def ChunkDescriptor(data_types: dict[str, construct.Construct]): ErrorWithMessage(lambda ctx: f"Unknown type: {ctx.id}"), ), terminate=construct.Terminated, - )), - ) + ), + ), + ) def SingleTypeChunkDescriptor(type_name: str, contents: construct.Construct, *, add_terminated: bool = True): diff --git a/src/retro_data_structures/formats/cmdl.py b/src/retro_data_structures/formats/cmdl.py index 4471b44..0339ce1 100644 --- a/src/retro_data_structures/formats/cmdl.py +++ b/src/retro_data_structures/formats/cmdl.py @@ -45,6 +45,7 @@ UnknownType = Sequence(Probe(into=lambda ctx: ctx["_"]), ErrorWithMessage("Unknown type")) + def FourCCSwitch(element_types): return Struct(type=FourCC, body=Switch(construct.this.type, element_types, UnknownType)) @@ -56,10 +57,11 @@ def FourCCSwitch(element_types): flags=Int32ub, id=AssetId64 * "TXTR", uv_source=Int32ub, - uv_animation=PrefixedArray(Int32ub,Byte), + uv_animation=PrefixedArray(Int32ub, Byte), _end=Tell, - _update_pass_size=Pointer(construct.this._start - Int32ub.length, - Rebuild(Int32ub, construct.this._end - construct.this._start)), + _update_pass_size=Pointer( + construct.this._start - Int32ub.length, Rebuild(Int32ub, construct.this._end - construct.this._start) + ), ) GetClr = Struct(subtype=FourCC, value=Int32ub) @@ -109,7 +111,20 @@ def FourCCSwitch(element_types): } PASS_TYPES = { - "DIFF", "RIML", "BLOL", "BLOD", "CLR ", "TRAN", "INCA", "RFLV", "RFLD", "LRLD", "LURD", "BLOI", "XRAY", "TOON" + "DIFF", + "RIML", + "BLOL", + "BLOD", + "CLR ", + "TRAN", + "INCA", + "RFLV", + "RFLD", + "LRLD", + "LURD", + "BLOI", + "XRAY", + "TOON", } MATERIAL_PARAMETERS = { @@ -167,11 +182,12 @@ def FourCCSwitch(element_types): ) MaterialSet = Struct( - texture_file_ids=If(game_check.current_game_at_most(Game.ECHOES),PrefixedArray(Int32ub, AssetId32)), + texture_file_ids=If(game_check.current_game_at_most(Game.ECHOES), PrefixedArray(Int32ub, AssetId32)), _material_count=Rebuild(Int32ub, construct.len_(construct.this.materials)), _material_end_offsets_address=Tell, - _material_end_offsets=If(game_check.current_game_at_most(Game.ECHOES), - Seek(construct.this["_material_count"] * Int32ub.length, 1)), + _material_end_offsets=If( + game_check.current_game_at_most(Game.ECHOES), Seek(construct.this["_material_count"] * Int32ub.length, 1) + ), _materials_start=Tell, materials=Array( construct.this["_material_count"], @@ -179,7 +195,8 @@ def FourCCSwitch(element_types): "material", material=Material, _end=Tell, - update_end_offset=If(game_check.current_game_at_most(Game.ECHOES), + update_end_offset=If( + game_check.current_game_at_most(Game.ECHOES), Pointer( lambda ctx: ctx["_"]["_material_end_offsets_address"] + Int32ub.length * ctx["_index"], Rebuild(Int32ub, lambda ctx: ctx["_end"] - ctx["_"]["_materials_start"]), @@ -292,10 +309,10 @@ def VertexAttrib(flag): _data_section_count=Rebuild( Int32ub, lambda context: ( - len(context.material_sets) - + sum(1 for k, v in context.attrib_arrays.items() if not k.startswith("_") and v is not None) - + 1 - + len(context.surfaces) + len(context.material_sets) + + sum(1 for k, v in context.attrib_arrays.items() if not k.startswith("_") and v is not None) + + 1 + + len(context.surfaces) ), ), _material_set_count=Rebuild(Int32ub, construct.len_(construct.this.material_sets)), @@ -365,6 +382,7 @@ def legacy_dependencies(obj, target_game: Game): if element.type == "PASS": yield "TXTR", element.body.id + class Cmdl(BaseResource): @classmethod def resource_type(cls) -> AssetType: diff --git a/src/retro_data_structures/formats/cskr.py b/src/retro_data_structures/formats/cskr.py index ba4ac28..c3bc8dc 100644 --- a/src/retro_data_structures/formats/cskr.py +++ b/src/retro_data_structures/formats/cskr.py @@ -54,8 +54,8 @@ ) CSKR = Struct( - _magic=If(game_check.current_game_at_least(Game.CORRUPTION),Const(0x534B494E, Int32ub)), - unk=If(game_check.current_game_at_least(Game.CORRUPTION),Int32ub), # Version ? + _magic=If(game_check.current_game_at_least(Game.CORRUPTION), Const(0x534B494E, Int32ub)), + unk=If(game_check.current_game_at_least(Game.CORRUPTION), Int32ub), # Version ? vertex_groups=PrefixedArray(Int32ub, VertexGroup), footer=IfThenElse( game_check.is_prime1, diff --git a/src/retro_data_structures/formats/dependency_cheating.py b/src/retro_data_structures/formats/dependency_cheating.py index 9859bef..8ee46ff 100644 --- a/src/retro_data_structures/formats/dependency_cheating.py +++ b/src/retro_data_structures/formats/dependency_cheating.py @@ -35,10 +35,7 @@ def _cheat(stream: bytes, asset_manager: AssetManager) -> typing.Iterator[Depend _csng = construct.FocusedSeq( - "agsc_id", - construct.Const(2, construct.Int32ub), - construct.Seek(0xC), - "agsc_id" / construct.Int32ub + "agsc_id", construct.Const(2, construct.Int32ub), construct.Seek(0xC), "agsc_id" / construct.Int32ub ) @@ -65,13 +62,7 @@ def dumb_dependencies(asset: RawResource, asset_manager: AssetManager) -> typing _frme = construct.FocusedSeq( "deps", construct.Int32ub, - "deps" / construct.PrefixedArray( - construct.Int32ub, - construct.Struct( - type=FourCC, - id=construct.Int32ub - ) - ) + "deps" / construct.PrefixedArray(construct.Int32ub, construct.Struct(type=FourCC, id=construct.Int32ub)), ) @@ -90,37 +81,45 @@ def frme_dependencies(asset: RawResource, asset_manager: AssetManager) -> typing "num_unk1" / construct.Int32ub, "num_unk2" / construct.Int32ub, "num_unk3" / construct.Int32ub, - "states" / construct.Array(lambda ctx: ctx.num_states, construct.Sequence( - String, - construct.If(lambda ctx: ctx._.version >= 2, construct.Seek(0x10, 1)), - construct.PrefixedArray(construct.Int32ub, construct.Sequence( - String, construct.Seek(0x4, 1) - )) - )), - "unk1" / construct.Array(lambda ctx: ctx.num_unk1, construct.Sequence( - String, - construct.If(lambda ctx: ctx._.version >= 2, construct.Seek(0x10, 1)), - construct.Seek(0x4, 1), - construct.PrefixedArray(construct.Int32ub, construct.Sequence( - String, construct.Seek(0x4, 1) - )), - construct.Seek(0x1, 1) - )), - "unk2" / construct.Array(lambda ctx: ctx.num_unk2, construct.Sequence( - String, - construct.If(lambda ctx: ctx._.version >= 2, construct.Seek(0x10, 1)), - construct.PrefixedArray(construct.Int32ub, construct.Sequence( - String, construct.Seek(0x4, 1) - )) - )), - "unk3" / construct.Array(lambda ctx: ctx.num_unk3, construct.Struct( - "str" / String, - construct.If(lambda ctx: ctx._.version >= 2, construct.Seek(0x10, 1)), - "unk" / construct.PrefixedArray(construct.Int32ub, construct.Sequence( - String, construct.Seek(0x4, 1) - )), - "dep" / construct.Int32ub, - )) + "states" + / construct.Array( + lambda ctx: ctx.num_states, + construct.Sequence( + String, + construct.If(lambda ctx: ctx._.version >= 2, construct.Seek(0x10, 1)), + construct.PrefixedArray(construct.Int32ub, construct.Sequence(String, construct.Seek(0x4, 1))), + ), + ), + "unk1" + / construct.Array( + lambda ctx: ctx.num_unk1, + construct.Sequence( + String, + construct.If(lambda ctx: ctx._.version >= 2, construct.Seek(0x10, 1)), + construct.Seek(0x4, 1), + construct.PrefixedArray(construct.Int32ub, construct.Sequence(String, construct.Seek(0x4, 1))), + construct.Seek(0x1, 1), + ), + ), + "unk2" + / construct.Array( + lambda ctx: ctx.num_unk2, + construct.Sequence( + String, + construct.If(lambda ctx: ctx._.version >= 2, construct.Seek(0x10, 1)), + construct.PrefixedArray(construct.Int32ub, construct.Sequence(String, construct.Seek(0x4, 1))), + ), + ), + "unk3" + / construct.Array( + lambda ctx: ctx.num_unk3, + construct.Struct( + "str" / String, + construct.If(lambda ctx: ctx._.version >= 2, construct.Seek(0x10, 1)), + "unk" / construct.PrefixedArray(construct.Int32ub, construct.Sequence(String, construct.Seek(0x4, 1))), + "dep" / construct.Int32ub, + ), + ), ) @@ -133,19 +132,27 @@ def fsm2_dependencies(asset: RawResource, asset_manager: AssetManager) -> typing _hint = construct.Struct( construct.Const(0x00BADBAD, construct.Int32ub), "version" / construct.Int32ub, - "hints" / construct.PrefixedArray(construct.Int32ub, construct.Struct( - "name" / String, - "immediate_time" / construct.Float32b, - "normal_time" / construct.Float32b, - "popup_strg" / construct.Int32ub, - "text_time" / construct.Float32b, - "locations" / construct.PrefixedArray(construct.Int32ub, construct.Struct( - "mlvl" / construct.Int32ub, - "mrea" / construct.Int32ub, - "index" / construct.Int32ub, - "map_text_strg" / construct.Int32ub, - )) - )) + "hints" + / construct.PrefixedArray( + construct.Int32ub, + construct.Struct( + "name" / String, + "immediate_time" / construct.Float32b, + "normal_time" / construct.Float32b, + "popup_strg" / construct.Int32ub, + "text_time" / construct.Float32b, + "locations" + / construct.PrefixedArray( + construct.Int32ub, + construct.Struct( + "mlvl" / construct.Int32ub, + "mrea" / construct.Int32ub, + "index" / construct.Int32ub, + "map_text_strg" / construct.Int32ub, + ), + ), + ), + ), ) @@ -168,12 +175,7 @@ def rule_dependencies(asset: RawResource, asset_manager: AssetManager) -> typing yield from asset_manager.get_dependencies_for_asset(_rule.parse(asset.data)) -_font = construct.FocusedSeq( - "txtr", - construct.Seek(0x22), - String, - "txtr" / construct.Int32ub -) +_font = construct.FocusedSeq("txtr", construct.Seek(0x22), String, "txtr" / construct.Int32ub) def font_dependencies(asset: RawResource, asset_manager: AssetManager) -> typing.Iterator[Dependency]: @@ -189,11 +191,14 @@ def font_dependencies(asset: RawResource, asset_manager: AssetManager) -> typing material_set_count=construct.Int32ub, data_section_sizes=construct.Array(construct.this.data_section_count, construct.Int32ub), _=AlignTo(32), - material_sets=construct.Array(construct.this.material_set_count, DataSection( - # Assumes Prime 1/2 - construct.PrefixedArray(construct.Int32ub, AssetId32), - size=lambda: construct.Computed(lambda ctx: ctx.data_section_sizes[ctx._index]) - )), + material_sets=construct.Array( + construct.this.material_set_count, + DataSection( + # Assumes Prime 1/2 + construct.PrefixedArray(construct.Int32ub, AssetId32), + size=lambda: construct.Computed(lambda ctx: ctx.data_section_sizes[ctx._index]), + ), + ), ) @@ -219,9 +224,9 @@ def effect_dependencies(asset: RawResource, asset_manager: AssetManager) -> typi effect_type = next(c for c in ALL_EFFECTS if c.resource_type() == asset.type) for match in _make_re(effect_type.texture_keys()).finditer(asset.data): - k = asset.data[match.end():match.end() + 4] + k = asset.data[match.end() : match.end() + 4] element = effect_script.TEXTURE_ELEMENT_TYPES[k.decode()].parse( - asset.data[match.end() + 4:], + asset.data[match.end() + 4 :], target_game=asset_manager.target_game, ) if element is not None: @@ -229,7 +234,7 @@ def effect_dependencies(asset: RawResource, asset_manager: AssetManager) -> typi for match in _make_re(effect_type.spawn_system_keys()).finditer(asset.data): element = effect_script.SpawnSystemKeyframeData.parse( - asset.data[match.end():], + asset.data[match.end() :], target_game=asset_manager.target_game, ) if element.magic != "NONE": @@ -239,7 +244,7 @@ def effect_dependencies(asset: RawResource, asset_manager: AssetManager) -> typi for match in _make_re(effect_type.asset_id_keys()).finditer(asset.data): element = effect_script.GetAssetId.parse( - asset.data[match.end():], + asset.data[match.end() :], target_game=asset_manager.target_game, ) if element is not None and element.body is not None: diff --git a/src/retro_data_structures/formats/dgrp.py b/src/retro_data_structures/formats/dgrp.py index eedd569..482d435 100644 --- a/src/retro_data_structures/formats/dgrp.py +++ b/src/retro_data_structures/formats/dgrp.py @@ -17,7 +17,7 @@ def construct_dep(asset_id_format): DGRP = CurrentGameCheck( Game.CORRUPTION, PrefixedArray(Int32ub, construct_dep(common_types.AssetId64)), - PrefixedArray(Int32ub, construct_dep(common_types.AssetId32)) + PrefixedArray(Int32ub, construct_dep(common_types.AssetId32)), ).compile() diff --git a/src/retro_data_structures/formats/effect_script.py b/src/retro_data_structures/formats/effect_script.py index 19a38ee..e671248 100644 --- a/src/retro_data_structures/formats/effect_script.py +++ b/src/retro_data_structures/formats/effect_script.py @@ -898,414 +898,425 @@ def create_keyf_emitter(keys_type): } DECAL_TYPES = dict(PARTICLE_TYPES) -DECAL_TYPES.update({ - "DMOO": GetBool, - "DMAB": GetBool, - "DMDL": GetModel, - "DMCL": GetColorElement, - "DMOP": GetVectorElement, - "DMRT": GetVectorElement, - "DMSC": GetVectorElement, - "DLFT": GetIntElement, - "1ADD": GetBool, - "1TEX": GetTextureElement, - "1CLR": GetColorElement, - "1OFF": GetVectorElement, - "1ROT": GetRealElement, - "1SZE": GetRealElement, - "1LFT": GetRealElement, - "2ADD": GetBool, - "2TEX": GetTextureElement, - "2CLR": GetColorElement, - "2OFF": GetVectorElement, - "2ROT": GetRealElement, - "2SZE": GetRealElement, - "2LFT": GetRealElement, - # End - "_END": Pass, -}) +DECAL_TYPES.update( + { + "DMOO": GetBool, + "DMAB": GetBool, + "DMDL": GetModel, + "DMCL": GetColorElement, + "DMOP": GetVectorElement, + "DMRT": GetVectorElement, + "DMSC": GetVectorElement, + "DLFT": GetIntElement, + "1ADD": GetBool, + "1TEX": GetTextureElement, + "1CLR": GetColorElement, + "1OFF": GetVectorElement, + "1ROT": GetRealElement, + "1SZE": GetRealElement, + "1LFT": GetRealElement, + "2ADD": GetBool, + "2TEX": GetTextureElement, + "2CLR": GetColorElement, + "2OFF": GetVectorElement, + "2ROT": GetRealElement, + "2SZE": GetRealElement, + "2LFT": GetRealElement, + # End + "_END": Pass, + } +) WEAPON_TYPES = dict(PARTICLE_TYPES) -WEAPON_TYPES.update({ - "LWTR": GetBool, - "EWTR": GetBool, - "SWTR": GetBool, - "PJFX": GetAudioTable, - "TRAT": GetRealElement, - "HOMG": GetBool, - "OFST": GetVectorElement, - "COLR": GetCollisionResponseGeneratorDesc, - "PCOL": GetColorElement, - "POFS": GetVectorElement, - "PSCL": GetVectorElement, - "PSLT": GetIntElement, - "OHEF": GetModel, - "APSM": GetParticleGeneratorDesc, - "APSO": GetBool, - "APS1": GetParticleGeneratorDesc, - "AP11": GetBool, - "APS2": GetParticleGeneratorDesc, - "AP21": GetBool, - "ASW1": GetSwooshGeneratorDesc, - "AS11": GetBool, - "ASW2": GetSwooshGeneratorDesc, - "AS12": GetBool, - "ASW3": GetSwooshGeneratorDesc, - "AS13": GetBool, - "PSOV": GetVectorElement, - "IORN": GetVectorElement, - "VMD2": GetBool, - "PSVM": GetModVectorElement, - "IVEC": GetVectorElement, - "RNGE": GetRealElement, - "FC60": GetBool, - "SPS1": GetBool, - "SPS2": GetBool, - # Echoes? - "EELT": GetBool, - "DP2C": GetBool, - "DP1C": GetBool, - "RTLA": GetBool, - "RB1A": GetBool, - "RB2A": GetBool, - "RWPE": GetBool, - "TECL": GetColorElement, - "FOFF": GetRealElement, - "TSCL": GetColorElement, - "B2CL": GetColorElement, - "B1CL": GetColorElement, - "TLEN": GetRealElement, - "TSZE": GetRealElement, - "B2SE": GetRealElement, - "B1SE": GetRealElement, - "B2TX": GetTextureElement, - "B1TX": GetTextureElement, - "TLPO": GetVectorElement, - "TTEX": GetTextureElement, - "B2PO": GetVectorElement, - "B1PO": GetVectorElement, - "B2RT": GetRealElement, - "B1RT": GetRealElement, - # End - "_END": Pass, -}) +WEAPON_TYPES.update( + { + "LWTR": GetBool, + "EWTR": GetBool, + "SWTR": GetBool, + "PJFX": GetAudioTable, + "TRAT": GetRealElement, + "HOMG": GetBool, + "OFST": GetVectorElement, + "COLR": GetCollisionResponseGeneratorDesc, + "PCOL": GetColorElement, + "POFS": GetVectorElement, + "PSCL": GetVectorElement, + "PSLT": GetIntElement, + "OHEF": GetModel, + "APSM": GetParticleGeneratorDesc, + "APSO": GetBool, + "APS1": GetParticleGeneratorDesc, + "AP11": GetBool, + "APS2": GetParticleGeneratorDesc, + "AP21": GetBool, + "ASW1": GetSwooshGeneratorDesc, + "AS11": GetBool, + "ASW2": GetSwooshGeneratorDesc, + "AS12": GetBool, + "ASW3": GetSwooshGeneratorDesc, + "AS13": GetBool, + "PSOV": GetVectorElement, + "IORN": GetVectorElement, + "VMD2": GetBool, + "PSVM": GetModVectorElement, + "IVEC": GetVectorElement, + "RNGE": GetRealElement, + "FC60": GetBool, + "SPS1": GetBool, + "SPS2": GetBool, + # Echoes? + "EELT": GetBool, + "DP2C": GetBool, + "DP1C": GetBool, + "RTLA": GetBool, + "RB1A": GetBool, + "RB2A": GetBool, + "RWPE": GetBool, + "TECL": GetColorElement, + "FOFF": GetRealElement, + "TSCL": GetColorElement, + "B2CL": GetColorElement, + "B1CL": GetColorElement, + "TLEN": GetRealElement, + "TSZE": GetRealElement, + "B2SE": GetRealElement, + "B1SE": GetRealElement, + "B2TX": GetTextureElement, + "B1TX": GetTextureElement, + "TLPO": GetVectorElement, + "TTEX": GetTextureElement, + "B2PO": GetVectorElement, + "B1PO": GetVectorElement, + "B2RT": GetRealElement, + "B1RT": GetRealElement, + # End + "_END": Pass, + } +) COLLISION_RESPONSE_TYPES = dict(PARTICLE_TYPES) -COLLISION_RESPONSE_TYPES.update({ - "DCHR": GetParticleGeneratorDesc, - "DEFS": GetParticleGeneratorDesc, - "TALP": GetParticleGeneratorDesc, - "DESH": GetParticleGeneratorDesc, - "DENM": GetParticleGeneratorDesc, - "DDCL": GetDecalGeneratorDesc, - "ENDL": GetDecalGeneratorDesc, - "CHDL": GetDecalGeneratorDesc, - "WTDL": GetDecalGeneratorDesc, - "GODL": GetDecalGeneratorDesc, - "ICDL": GetDecalGeneratorDesc, - "GRDL": GetDecalGeneratorDesc, - "MEDL": GetDecalGeneratorDesc, - "CODL": GetDecalGeneratorDesc, - "WODL": GetDecalGeneratorDesc, - "FOFF": GetRealElement, - "RNGE": GetRealElement, - "MSFX": GetIntElement, - "DSHX": GetIntElement, - "DSFX": GetIntElement, - "GOFX": GetIntElement, - "GOOO": GetIntElement, - "ICFX": GetIntElement, - "ICEE": GetIntElement, - "GRFX": GetIntElement, - "GRAS": GetIntElement, - "WTFX": GetIntElement, - "WATR": GetIntElement, - "CHFX": GetIntElement, - "CHSH": GetIntElement, - "CHSP": GetIntElement, - "CZFX": GetIntElement, - "CHOZ": GetIntElement, - "IBHX": GetIntElement, - "IBSH": GetIntElement, - "IBSX": GetIntElement, - "IBSP": GetIntElement, - "IBFX": GetIntElement, - "IBOS": GetIntElement, - "PBHX": GetIntElement, - "PBSH": GetIntElement, - "PBSX": GetIntElement, - "PBSP": GetIntElement, - "PBFX": GetIntElement, - "PBOS": GetIntElement, - "HBFX": GetIntElement, - "BFSH": GetIntElement, - "SBFX": GetIntElement, - "BFSP": GetIntElement, - "BFFX": GetIntElement, - "BFLR": GetIntElement, - "MHFX": GetIntElement, - "BMSH": GetIntElement, - "BMSP": GetIntElement, - "BMFX": GetIntElement, - "BMON": GetIntElement, - "PHFX": GetIntElement, - "PSSH": GetIntElement, - "PSFX": GetIntElement, - "PSSP": GetIntElement, - "PAFX": GetIntElement, - "PARA": GetIntElement, - "HFFX": GetIntElement, - "FFSH": GetIntElement, - "SFFX": GetIntElement, - "FFSP": GetIntElement, - "FFFX": GetIntElement, - "FFLE": GetIntElement, - "FHFX": GetIntElement, - "FPSH": GetIntElement, - "FSFX": GetIntElement, - "FPSP": GetIntElement, - "FPFX": GetIntElement, - "FPIR": GetIntElement, - "SPSH": GetIntElement, - "SSFX": GetIntElement, - "SPSP": GetIntElement, - "SPFX": GetIntElement, - "SPIR": GetIntElement, - "GHFX": GetIntElement, - "TGSH": GetIntElement, - "GSFX": GetIntElement, - "TGSP": GetIntElement, - "GTFX": GetIntElement, - "PTGM": GetIntElement, - "THFX": GetIntElement, - "TASH": GetIntElement, - "TSFX": GetIntElement, - "TASP": GetIntElement, - "TAFX": GetIntElement, - "WHFX": GetIntElement, - "WWSH": GetIntElement, - "WWSP": GetIntElement, - "WWFX": GetIntElement, - "WASP": GetIntElement, - "BHFX": GetIntElement, - "BTSH": GetIntElement, - "BSFX": GetIntElement, - "BTSP": GetIntElement, - "BEFX": GetIntElement, - "BTLE": GetIntElement, - "SHFX": GetIntElement, - "ESFX": GetIntElement, - "DESP": GetIntElement, - "DEFX": GetIntElement, - "DCHS": GetIntElement, - "DCFX": GetIntElement, - "CSFX": GetIntElement, - "CRTS": GetIntElement, - "MTLS": GetIntElement, - "WSFX": GetIntElement, - "WODS": GetIntElement, - "6ISE": GetIntElement, - "5ISE": GetIntElement, - "4ISE": GetIntElement, - "3ISE": GetIntElement, - "2ISE": GetIntElement, - "1ISE": GetIntElement, - "JZHS": GetIntElement, - "JZSH": GetIntElement, - "JZPS": GetIntElement, - "JZSP": GetIntElement, - "JZAS": GetIntElement, - "JZAP": GetIntElement, - "6MRE": GetIntElement, - "5MRE": GetIntElement, - "4MRE": GetIntElement, - "3MRE": GetIntElement, - "2MRE": GetIntElement, - "1MRE": GetIntElement, - "6DRN": GetIntElement, - "5DRN": GetIntElement, - "4DRN": GetIntElement, - "3DRN": GetIntElement, - "2DRN": GetIntElement, - "1DRN": GetIntElement, - "6FLB": GetIntElement, - "5FLB": GetIntElement, - "4FLB": GetIntElement, - "3FLB": GetIntElement, - "2FLB": GetIntElement, - "1FLB": GetIntElement, - "6PDS": GetIntElement, - "5PDS": GetIntElement, - "4PDS": GetIntElement, - "3PDS": GetIntElement, - "2PDS": GetIntElement, - "1PDS": GetIntElement, - "6MTR": GetIntElement, - "5MTR": GetIntElement, - "4MTR": GetIntElement, - "3MTR": GetIntElement, - "2MTR": GetIntElement, - "1MTR": GetIntElement, - "6RPR": GetIntElement, - "5RPR": GetIntElement, - "4RPR": GetIntElement, - "3RPR": GetIntElement, - "2RPR": GetIntElement, - "1RPR": GetIntElement, - "6SVA": GetIntElement, - "5SVA": GetIntElement, - "4SVA": GetIntElement, - "3SVA": GetIntElement, - "2SVA": GetIntElement, - "1SVA": GetIntElement, - "6ATA": GetIntElement, - "5ATA": GetIntElement, - "4ATA": GetIntElement, - "3ATA": GetIntElement, - "2ATA": GetIntElement, - "1ATA": GetIntElement, - "6ATB": GetIntElement, - "5ATB": GetIntElement, - "4ATB": GetIntElement, - "3ATB": GetIntElement, - "2ATB": GetIntElement, - "1ATB": GetIntElement, - "6BSE": GetIntElement, - "5BSE": GetIntElement, - "4BSE": GetIntElement, - "3BSE": GetIntElement, - "2BSE": GetIntElement, - "1BSE": GetIntElement, - "6SAN": GetIntElement, - "5SAN": GetIntElement, - "4SAN": GetIntElement, - "3SAN": GetIntElement, - "2SAN": GetIntElement, - "1SAN": GetIntElement, - "6MUD": GetIntElement, - "5MUD": GetIntElement, - "4MUD": GetIntElement, - "3MUD": GetIntElement, - "2MUD": GetIntElement, - "1MUD": GetIntElement, - "6GRN": GetIntElement, - "5GRN": GetIntElement, - "4GRN": GetIntElement, - "3GRN": GetIntElement, - "2GRN": GetIntElement, - "1GRN": GetIntElement, - "6LAV": GetDecalGeneratorDesc, - "5LAV": GetDecalGeneratorDesc, - "4LAV": GetDecalGeneratorDesc, - "3LAV": GetDecalGeneratorDesc, - "2LAV": GetDecalGeneratorDesc, - "1LAV": GetDecalGeneratorDesc, - "DCSH": GetIntElement, - # End - "_END": Pass, -}) +COLLISION_RESPONSE_TYPES.update( + { + "DCHR": GetParticleGeneratorDesc, + "DEFS": GetParticleGeneratorDesc, + "TALP": GetParticleGeneratorDesc, + "DESH": GetParticleGeneratorDesc, + "DENM": GetParticleGeneratorDesc, + "DDCL": GetDecalGeneratorDesc, + "ENDL": GetDecalGeneratorDesc, + "CHDL": GetDecalGeneratorDesc, + "WTDL": GetDecalGeneratorDesc, + "GODL": GetDecalGeneratorDesc, + "ICDL": GetDecalGeneratorDesc, + "GRDL": GetDecalGeneratorDesc, + "MEDL": GetDecalGeneratorDesc, + "CODL": GetDecalGeneratorDesc, + "WODL": GetDecalGeneratorDesc, + "FOFF": GetRealElement, + "RNGE": GetRealElement, + "MSFX": GetIntElement, + "DSHX": GetIntElement, + "DSFX": GetIntElement, + "GOFX": GetIntElement, + "GOOO": GetIntElement, + "ICFX": GetIntElement, + "ICEE": GetIntElement, + "GRFX": GetIntElement, + "GRAS": GetIntElement, + "WTFX": GetIntElement, + "WATR": GetIntElement, + "CHFX": GetIntElement, + "CHSH": GetIntElement, + "CHSP": GetIntElement, + "CZFX": GetIntElement, + "CHOZ": GetIntElement, + "IBHX": GetIntElement, + "IBSH": GetIntElement, + "IBSX": GetIntElement, + "IBSP": GetIntElement, + "IBFX": GetIntElement, + "IBOS": GetIntElement, + "PBHX": GetIntElement, + "PBSH": GetIntElement, + "PBSX": GetIntElement, + "PBSP": GetIntElement, + "PBFX": GetIntElement, + "PBOS": GetIntElement, + "HBFX": GetIntElement, + "BFSH": GetIntElement, + "SBFX": GetIntElement, + "BFSP": GetIntElement, + "BFFX": GetIntElement, + "BFLR": GetIntElement, + "MHFX": GetIntElement, + "BMSH": GetIntElement, + "BMSP": GetIntElement, + "BMFX": GetIntElement, + "BMON": GetIntElement, + "PHFX": GetIntElement, + "PSSH": GetIntElement, + "PSFX": GetIntElement, + "PSSP": GetIntElement, + "PAFX": GetIntElement, + "PARA": GetIntElement, + "HFFX": GetIntElement, + "FFSH": GetIntElement, + "SFFX": GetIntElement, + "FFSP": GetIntElement, + "FFFX": GetIntElement, + "FFLE": GetIntElement, + "FHFX": GetIntElement, + "FPSH": GetIntElement, + "FSFX": GetIntElement, + "FPSP": GetIntElement, + "FPFX": GetIntElement, + "FPIR": GetIntElement, + "SPSH": GetIntElement, + "SSFX": GetIntElement, + "SPSP": GetIntElement, + "SPFX": GetIntElement, + "SPIR": GetIntElement, + "GHFX": GetIntElement, + "TGSH": GetIntElement, + "GSFX": GetIntElement, + "TGSP": GetIntElement, + "GTFX": GetIntElement, + "PTGM": GetIntElement, + "THFX": GetIntElement, + "TASH": GetIntElement, + "TSFX": GetIntElement, + "TASP": GetIntElement, + "TAFX": GetIntElement, + "WHFX": GetIntElement, + "WWSH": GetIntElement, + "WWSP": GetIntElement, + "WWFX": GetIntElement, + "WASP": GetIntElement, + "BHFX": GetIntElement, + "BTSH": GetIntElement, + "BSFX": GetIntElement, + "BTSP": GetIntElement, + "BEFX": GetIntElement, + "BTLE": GetIntElement, + "SHFX": GetIntElement, + "ESFX": GetIntElement, + "DESP": GetIntElement, + "DEFX": GetIntElement, + "DCHS": GetIntElement, + "DCFX": GetIntElement, + "CSFX": GetIntElement, + "CRTS": GetIntElement, + "MTLS": GetIntElement, + "WSFX": GetIntElement, + "WODS": GetIntElement, + "6ISE": GetIntElement, + "5ISE": GetIntElement, + "4ISE": GetIntElement, + "3ISE": GetIntElement, + "2ISE": GetIntElement, + "1ISE": GetIntElement, + "JZHS": GetIntElement, + "JZSH": GetIntElement, + "JZPS": GetIntElement, + "JZSP": GetIntElement, + "JZAS": GetIntElement, + "JZAP": GetIntElement, + "6MRE": GetIntElement, + "5MRE": GetIntElement, + "4MRE": GetIntElement, + "3MRE": GetIntElement, + "2MRE": GetIntElement, + "1MRE": GetIntElement, + "6DRN": GetIntElement, + "5DRN": GetIntElement, + "4DRN": GetIntElement, + "3DRN": GetIntElement, + "2DRN": GetIntElement, + "1DRN": GetIntElement, + "6FLB": GetIntElement, + "5FLB": GetIntElement, + "4FLB": GetIntElement, + "3FLB": GetIntElement, + "2FLB": GetIntElement, + "1FLB": GetIntElement, + "6PDS": GetIntElement, + "5PDS": GetIntElement, + "4PDS": GetIntElement, + "3PDS": GetIntElement, + "2PDS": GetIntElement, + "1PDS": GetIntElement, + "6MTR": GetIntElement, + "5MTR": GetIntElement, + "4MTR": GetIntElement, + "3MTR": GetIntElement, + "2MTR": GetIntElement, + "1MTR": GetIntElement, + "6RPR": GetIntElement, + "5RPR": GetIntElement, + "4RPR": GetIntElement, + "3RPR": GetIntElement, + "2RPR": GetIntElement, + "1RPR": GetIntElement, + "6SVA": GetIntElement, + "5SVA": GetIntElement, + "4SVA": GetIntElement, + "3SVA": GetIntElement, + "2SVA": GetIntElement, + "1SVA": GetIntElement, + "6ATA": GetIntElement, + "5ATA": GetIntElement, + "4ATA": GetIntElement, + "3ATA": GetIntElement, + "2ATA": GetIntElement, + "1ATA": GetIntElement, + "6ATB": GetIntElement, + "5ATB": GetIntElement, + "4ATB": GetIntElement, + "3ATB": GetIntElement, + "2ATB": GetIntElement, + "1ATB": GetIntElement, + "6BSE": GetIntElement, + "5BSE": GetIntElement, + "4BSE": GetIntElement, + "3BSE": GetIntElement, + "2BSE": GetIntElement, + "1BSE": GetIntElement, + "6SAN": GetIntElement, + "5SAN": GetIntElement, + "4SAN": GetIntElement, + "3SAN": GetIntElement, + "2SAN": GetIntElement, + "1SAN": GetIntElement, + "6MUD": GetIntElement, + "5MUD": GetIntElement, + "4MUD": GetIntElement, + "3MUD": GetIntElement, + "2MUD": GetIntElement, + "1MUD": GetIntElement, + "6GRN": GetIntElement, + "5GRN": GetIntElement, + "4GRN": GetIntElement, + "3GRN": GetIntElement, + "2GRN": GetIntElement, + "1GRN": GetIntElement, + "6LAV": GetDecalGeneratorDesc, + "5LAV": GetDecalGeneratorDesc, + "4LAV": GetDecalGeneratorDesc, + "3LAV": GetDecalGeneratorDesc, + "2LAV": GetDecalGeneratorDesc, + "1LAV": GetDecalGeneratorDesc, + "DCSH": GetIntElement, + # End + "_END": Pass, + } +) ELECTRIC_TYPES = dict(PARTICLE_TYPES) -ELECTRIC_TYPES.update({ - "LIFE": GetIntElement, - "SLIF": GetIntElement, - "GRAT": GetRealElement, - "SCNT": GetIntElement, - "SSEG": GetIntElement, - "COLR": GetColorElement, - "IEMT": GetEmitterElement, - "FEMT": GetEmitterElement, - "AMPL": GetRealElement, - "AMPD": GetRealElement, - "LWD1": GetRealElement, - "LWD2": GetRealElement, - "LWD3": GetRealElement, - "LCL1": GetColorElement, - "LCL2": GetColorElement, - "LCL3": GetColorElement, - "SSWH": GetSwooshGeneratorDesc, - "GPSM": GetParticleGeneratorDesc, - "EPSM": GetParticleGeneratorDesc, - "ZERY": GetBool, - "TEXR": GetTextureElement, - # End - "_END": Pass -}) +ELECTRIC_TYPES.update( + { + "LIFE": GetIntElement, + "SLIF": GetIntElement, + "GRAT": GetRealElement, + "SCNT": GetIntElement, + "SSEG": GetIntElement, + "COLR": GetColorElement, + "IEMT": GetEmitterElement, + "FEMT": GetEmitterElement, + "AMPL": GetRealElement, + "AMPD": GetRealElement, + "LWD1": GetRealElement, + "LWD2": GetRealElement, + "LWD3": GetRealElement, + "LCL1": GetColorElement, + "LCL2": GetColorElement, + "LCL3": GetColorElement, + "SSWH": GetSwooshGeneratorDesc, + "GPSM": GetParticleGeneratorDesc, + "EPSM": GetParticleGeneratorDesc, + "ZERY": GetBool, + "TEXR": GetTextureElement, + # End + "_END": Pass, + } +) SWOOSH_TYPES = dict(PARTICLE_TYPES) -SWOOSH_TYPES.update({ - "PSLT": GetIntElement, - "TIME": GetRealElement, - "LRAD": GetRealElement, - "RRAD": GetRealElement, - "LEND": GetIntElement, - "COLR": GetColorElement, - "SIDE": GetIntElement, - "IROT": GetRealElement, - "ROTM": GetRealElement, - "POFS": GetVectorElement, - "IVEL": GetVectorElement, - "NPOS": GetVectorElement, - "VELM": GetModVectorElement, - "VLM2": GetModVectorElement, - "SPLN": GetIntElement, - "TEXR": GetTextureElement, - "TSPN": GetIntElement, - "LLRD": GetBool, - "CROS": GetBool, - "VLS1": GetBool, - "VLS2": GetBool, - "SROT": GetBool, - "WIRE": GetBool, - "TEXW": GetBool, - "AALP": GetBool, - "ZBUF": GetBool, - "ORNT": GetBool, - "CRND": GetBool, - "CLTX": GetBool, - "LENG": GetRealElement, - # End - "_END": Pass, -}) +SWOOSH_TYPES.update( + { + "PSLT": GetIntElement, + "TIME": GetRealElement, + "LRAD": GetRealElement, + "RRAD": GetRealElement, + "LEND": GetIntElement, + "COLR": GetColorElement, + "SIDE": GetIntElement, + "IROT": GetRealElement, + "ROTM": GetRealElement, + "POFS": GetVectorElement, + "IVEL": GetVectorElement, + "NPOS": GetVectorElement, + "VELM": GetModVectorElement, + "VLM2": GetModVectorElement, + "SPLN": GetIntElement, + "TEXR": GetTextureElement, + "TSPN": GetIntElement, + "LLRD": GetBool, + "CROS": GetBool, + "VLS1": GetBool, + "VLS2": GetBool, + "SROT": GetBool, + "WIRE": GetBool, + "TEXW": GetBool, + "AALP": GetBool, + "ZBUF": GetBool, + "ORNT": GetBool, + "CRND": GetBool, + "CLTX": GetBool, + "LENG": GetRealElement, + # End + "_END": Pass, + } +) SPAWN_TYPES = dict(PARTICLE_TYPES) -SPAWN_TYPES.update({ - "FRCO": GetBool, - "FROV": GetVectorElement, - "SPWN": SpawnSystemKeyframeData, - "VMD2": GetBool, - "VMD1": GetBool, - "IGLT": GetBool, - "IGGT": GetBool, - "GIVL": GetIntElement, - "DEOL": GetBool, - "PSLT": GetIntElement, - "GORN": GetVectorElement, - "TRNL": GetVectorElement, - "ORNT": GetVectorElement, - "LSCL": GetVectorElement, - "SCLE": GetVectorElement, - "VLM1": GetModVectorElement, - "VLM2": GetModVectorElement, - "PCOL": GetColorElement, - # End - "_END": Pass, -}) +SPAWN_TYPES.update( + { + "FRCO": GetBool, + "FROV": GetVectorElement, + "SPWN": SpawnSystemKeyframeData, + "VMD2": GetBool, + "VMD1": GetBool, + "IGLT": GetBool, + "IGGT": GetBool, + "GIVL": GetIntElement, + "DEOL": GetBool, + "PSLT": GetIntElement, + "GORN": GetVectorElement, + "TRNL": GetVectorElement, + "ORNT": GetVectorElement, + "LSCL": GetVectorElement, + "SCLE": GetVectorElement, + "VLM1": GetModVectorElement, + "VLM2": GetModVectorElement, + "PCOL": GetColorElement, + # End + "_END": Pass, + } +) def effect_script(magic: str, effect_types: dict): return Struct( magic=Const(magic, FourCC), - elements=RepeatUntil( - lambda x, lst, ctx: x.type == "_END", - FourCCSwitch(effect_types) - ) + elements=RepeatUntil(lambda x, lst, ctx: x.type == "_END", FourCCSwitch(effect_types)), ) + def _yield_dependency_if_valid(asset_id: int | None, asset_type: str, game: Game): if asset_id is not None and game.is_valid_asset_id(asset_id): yield asset_type, asset_id + def legacy_dependencies(obj, target_game: Game): # noqa: PLR0912 Too many branches for element in obj.elements: if element.type in ("TEXR", "TIND"): @@ -1394,7 +1405,7 @@ def asset_id_keys(cls) -> Iterable[AssetType]: @classmethod def spawn_system_keys(cls) -> Iterable[AssetType]: - return ("KSSM", ) + return ("KSSM",) @classmethod def texture_keys(cls) -> Iterable[AssetType]: @@ -1412,7 +1423,7 @@ def construct_class(cls, target_game: Game) -> Construct: @classmethod def asset_id_keys(cls) -> typing.Iterable[AssetType]: - return ("DMDL", ) + return ("DMDL",) @classmethod def texture_keys(cls) -> Iterable[AssetType]: @@ -1448,8 +1459,29 @@ def construct_class(cls, target_game: Game) -> Construct: @classmethod def asset_id_keys(cls) -> typing.Iterable[AssetType]: - return ("DCHR", "DEFS", "TALP", "DESH", "DENM", "DDCL", "ENDL", "CHDL", "WTDL", "GODL", - "ICDL", "GRDL", "MEDL", "CODL", "WODL", "6LAV", "5LAV", "4LAV", "3LAV", "2LAV", "1LAV") + return ( + "DCHR", + "DEFS", + "TALP", + "DESH", + "DENM", + "DDCL", + "ENDL", + "CHDL", + "WTDL", + "GODL", + "ICDL", + "GRDL", + "MEDL", + "CODL", + "WODL", + "6LAV", + "5LAV", + "4LAV", + "3LAV", + "2LAV", + "1LAV", + ) class Spsc(BaseEffect): @@ -1463,7 +1495,7 @@ def construct_class(cls, target_game: Game) -> Construct: @classmethod def spawn_system_keys(cls) -> Iterable[AssetType]: - return ("SPWN", ) + return ("SPWN",) class Srsc(BaseEffect): @@ -1477,7 +1509,7 @@ def construct_class(cls, target_game: Game) -> Construct: @classmethod def spawn_system_keys(cls) -> Iterable[AssetType]: - return ("SPWN", ) + return ("SPWN",) class Elsc(BaseEffect): @@ -1495,7 +1527,7 @@ def asset_id_keys(cls) -> Iterable[AssetType]: @classmethod def texture_keys(cls) -> Iterable[AssetType]: - return ("TEXR", ) + return ("TEXR",) class Swhc(BaseEffect): @@ -1509,4 +1541,4 @@ def construct_class(cls, target_game: Game) -> Construct: @classmethod def texture_keys(cls) -> Iterable[AssetType]: - return ("TEXR", ) + return ("TEXR",) diff --git a/src/retro_data_structures/formats/evnt.py b/src/retro_data_structures/formats/evnt.py index c09bea0..5ec0b39 100644 --- a/src/retro_data_structures/formats/evnt.py +++ b/src/retro_data_structures/formats/evnt.py @@ -61,13 +61,14 @@ base=BasePOINode, value=If(game_check.current_game_at_most(Game.ECHOES), Int32sb), locator_name=If(game_check.current_game_at_most(Game.ECHOES), String), - corruption=If(game_check.is_prime3, - Struct( - unk_a=Int8ub, - unk_b=Int16ub, - unk_c=Int16ub, - ), - ), + corruption=If( + game_check.is_prime3, + Struct( + unk_a=Int8ub, + unk_b=Int16ub, + unk_c=Int16ub, + ), + ), ) ParticlePOINode = Struct( @@ -105,17 +106,19 @@ Struct( unk_a=Int32ub, unk_b=Int32ub, - data=Array(2, - Struct( - type=Int32ub, - data=Switch(construct.this.type, - { - 1: Int32ub, - 2: MayaSpline, - } - ), - ), - ), + data=Array( + 2, + Struct( + type=Int32ub, + data=Switch( + construct.this.type, + { + 1: Int32ub, + 2: MayaSpline, + }, + ), + ), + ), ), ), ) diff --git a/src/retro_data_structures/formats/form_descriptor.py b/src/retro_data_structures/formats/form_descriptor.py index 690f33f..ab59af2 100644 --- a/src/retro_data_structures/formats/form_descriptor.py +++ b/src/retro_data_structures/formats/form_descriptor.py @@ -15,8 +15,9 @@ ) -def FormDescriptor(data_type: str, version: int, other_version: int, contents: construct.Construct, - *, add_terminated: bool = True): +def FormDescriptor( + data_type: str, version: int, other_version: int, contents: construct.Construct, *, add_terminated: bool = True +): if add_terminated: contents = construct.FocusedSeq("contents", contents=contents, terminate=construct.Terminated) diff --git a/src/retro_data_structures/formats/mapa.py b/src/retro_data_structures/formats/mapa.py index 50f2bad..7033fd9 100644 --- a/src/retro_data_structures/formats/mapa.py +++ b/src/retro_data_structures/formats/mapa.py @@ -110,7 +110,7 @@ def __str__(self): Game.PRIME: construct.Enum(construct.Int32sb, ObjectTypeMP1), Game.ECHOES: construct.Enum(construct.Int32sb, ObjectTypeMP2), }, - default=construct.Int32sb + default=construct.Int32sb, ), visibility_mode=construct.Enum(construct.Int32ub, ObjectVisibility), editor_id=construct.Int32ub, @@ -119,54 +119,70 @@ def __str__(self): unk2=construct.Int32ub[4], ) -Primitive = construct.Aligned(4, construct.Struct( - type=EnumAdapter(GXPrimitive), - indices=construct.PrefixedArray(construct.Int32ub, construct.Int8ub), -)) - -Border = construct.Aligned(4, construct.Struct( - indices=construct.PrefixedArray(construct.Int32ub, construct.Int8ub), -)) - -MAPA = construct.Aligned(32, construct.Struct( - header=construct.Struct( - _magic=_const(0xDEADD00D), - version=construct.Switch( - get_current_game, - { - Game.PRIME: _const(2), - Game.ECHOES: _const(3), - Game.CORRUPTION: _const(5), - }, - default=ErrorWithMessage("Unknown game"), - ), - type=construct.Int32ub, # Light/Dark world for Echoes - visibility_mode=construct.Enum(construct.Int32ub, AreaVisibilty), - bounding_box=AABox, - map_adjustment=current_game_at_least_else(Game.ECHOES, Vector3, construct.Pass), - mappable_object_count=construct.Int32ub, - vertex_count=construct.Int32ub, - primitive_count=construct.Int32ub, +Primitive = construct.Aligned( + 4, + construct.Struct( + type=EnumAdapter(GXPrimitive), + indices=construct.PrefixedArray(construct.Int32ub, construct.Int8ub), ), - mappable_objects=construct.Array(construct.this.header.mappable_object_count, MappableObject), - vertices=construct.Array(construct.this.header.vertex_count, Vector3), - primitive_headers=construct.Array(construct.this.header.primitive_count, construct.Struct( - normal=Vector3, - center_of_mass=Vector3, - primitive_table_start=construct.Int32ub, - border_table_start=construct.Int32ub, - )), - primitive_tables=construct.Array(construct.this.header.primitive_count, construct.Struct( - primitives=construct.PrefixedArray( - construct.Int32ub, - Primitive, +) + +Border = construct.Aligned( + 4, + construct.Struct( + indices=construct.PrefixedArray(construct.Int32ub, construct.Int8ub), + ), +) + +MAPA = construct.Aligned( + 32, + construct.Struct( + header=construct.Struct( + _magic=_const(0xDEADD00D), + version=construct.Switch( + get_current_game, + { + Game.PRIME: _const(2), + Game.ECHOES: _const(3), + Game.CORRUPTION: _const(5), + }, + default=ErrorWithMessage("Unknown game"), + ), + type=construct.Int32ub, # Light/Dark world for Echoes + visibility_mode=construct.Enum(construct.Int32ub, AreaVisibilty), + bounding_box=AABox, + map_adjustment=current_game_at_least_else(Game.ECHOES, Vector3, construct.Pass), + mappable_object_count=construct.Int32ub, + vertex_count=construct.Int32ub, + primitive_count=construct.Int32ub, + ), + mappable_objects=construct.Array(construct.this.header.mappable_object_count, MappableObject), + vertices=construct.Array(construct.this.header.vertex_count, Vector3), + primitive_headers=construct.Array( + construct.this.header.primitive_count, + construct.Struct( + normal=Vector3, + center_of_mass=Vector3, + primitive_table_start=construct.Int32ub, + border_table_start=construct.Int32ub, + ), ), - borders=construct.PrefixedArray( - construct.Int32ub, - Border, + primitive_tables=construct.Array( + construct.this.header.primitive_count, + construct.Struct( + primitives=construct.PrefixedArray( + construct.Int32ub, + Primitive, + ), + borders=construct.PrefixedArray( + construct.Int32ub, + Border, + ), + ), ), - )), -), b"\xFF") + ), + b"\xFF", +) class Mapa(BaseResource): diff --git a/src/retro_data_structures/formats/mapw.py b/src/retro_data_structures/formats/mapw.py index 1235d8a..faddd43 100644 --- a/src/retro_data_structures/formats/mapw.py +++ b/src/retro_data_structures/formats/mapw.py @@ -10,7 +10,7 @@ MAPW = construct.Struct( _magic=construct.Const(0xDEADF00D, construct.Int32ub), _version=construct.Const(1, construct.Int32ub), - area_map=construct.PrefixedArray(construct.Int32ub, AssetIdCorrect) + area_map=construct.PrefixedArray(construct.Int32ub, AssetIdCorrect), ) diff --git a/src/retro_data_structures/formats/mlvl.py b/src/retro_data_structures/formats/mlvl.py index b2acc42..c55d1f6 100644 --- a/src/retro_data_structures/formats/mlvl.py +++ b/src/retro_data_structures/formats/mlvl.py @@ -45,6 +45,7 @@ if typing.TYPE_CHECKING: from collections.abc import Iterator + pass MLVLConnectingDock = Struct( @@ -67,21 +68,20 @@ class LayerFlags(Adapter): def __init__(self): - super().__init__(Struct( - layer_count=Int32ub, - layer_flags=Bitwise(Array(64, Flag)), - )) + super().__init__( + Struct( + layer_count=Int32ub, + layer_flags=Bitwise(Array(64, Flag)), + ) + ) def _decode(self, obj, context, path): - return ListContainer(reversed(obj.layer_flags))[:obj.layer_count] + return ListContainer(reversed(obj.layer_flags))[: obj.layer_count] def _encode(self, obj, context, path): flags = [True for i in range(64)] - flags[:len(obj)] = obj - return Container({ - "layer_count": len(obj), - "layer_flags": list(reversed(flags)) - }) + flags[: len(obj)] = obj + return Container({"layer_count": len(obj), "layer_flags": list(reversed(flags))}) def create_area(version: int, asset_id): @@ -105,9 +105,7 @@ def create_area(version: int, asset_id): # Echoes if version == 0x17: - area_fields.append( - "module_dependencies" / AreaModuleDependencyAdapter() - ) + area_fields.append("module_dependencies" / AreaModuleDependencyAdapter()) # DKCR if version >= 0x1B: @@ -239,11 +237,7 @@ def dependencies_for(self) -> typing.Iterator[Dependency]: area.build_mlvl_dependencies(False) yield from area.dependencies_for() - mlvl_deps = [ - self._raw.world_name_id, - self._raw.world_save_info_id, - self._raw.default_skybox_id - ] + mlvl_deps = [self._raw.world_name_id, self._raw.world_save_info_id, self._raw.default_skybox_id] if self.asset_manager.target_game == Game.ECHOES: mlvl_deps.append(self._raw.dark_world_name_id) if self.asset_manager.target_game <= Game.CORRUPTION: @@ -265,7 +259,9 @@ def areas(self) -> Iterator[Area]: offsets = self._raw.area_layer_name_offset names = self._raw.layer_names for i, area in enumerate(self._raw.areas): - area_layer_names = names[offsets[i]:] if i == len(self._raw.areas) - 1 else names[offsets[i]:offsets[i+1]] + area_layer_names = ( + names[offsets[i] :] if i == len(self._raw.areas) - 1 else names[offsets[i] : offsets[i + 1]] + ) yield Area(area, self.asset_manager, self._raw.area_layer_flags[i], area_layer_names, i, self) def get_area(self, asset_id: NameOrAssetId) -> Area: diff --git a/src/retro_data_structures/formats/mrea.py b/src/retro_data_structures/formats/mrea.py index 59eb252..000aa9d 100644 --- a/src/retro_data_structures/formats/mrea.py +++ b/src/retro_data_structures/formats/mrea.py @@ -64,26 +64,30 @@ class MREAVersion(IntEnum): class AreaDependencyAdapter(Adapter): def __init__(self, asset_id): - super().__init__(Struct( - Const(0, Int32ub), - "dependencies" / PrefixedArray(Int32ub, Struct( - asset_id=asset_id, - asset_type=construct.Bytes(4), - )), - "offsets" / PrefixedArray(Int32ub, Int32ub), - ).compile()) + super().__init__( + Struct( + Const(0, Int32ub), + "dependencies" + / PrefixedArray( + Int32ub, + Struct( + asset_id=asset_id, + asset_type=construct.Bytes(4), + ), + ), + "offsets" / PrefixedArray(Int32ub, Int32ub), + ).compile() + ) def _decode(self, obj, context, path) -> AreaDependencies: layers = [] for start, finish in zip(obj.offsets, obj.offsets[1:]): - layers.append([ - Dependency(dep.asset_type.decode("ascii"), dep.asset_id) - for dep in obj.dependencies[start:finish] - ]) + layers.append( + [Dependency(dep.asset_type.decode("ascii"), dep.asset_id) for dep in obj.dependencies[start:finish]] + ) non_layer = [ - Dependency(dep.asset_type.decode("ascii"), dep.asset_id) - for dep in obj.dependencies[obj.offsets[-1]:] + Dependency(dep.asset_type.decode("ascii"), dep.asset_id) for dep in obj.dependencies[obj.offsets[-1] :] ] return AreaDependencies(layers, non_layer) @@ -102,25 +106,21 @@ def _encode(self, obj: AreaDependencies, context, path): if not dep.exclude_for_mlvl ) - return { - "dependencies": deps, - "offsets": offsets - } + return {"dependencies": deps, "offsets": offsets} class AreaModuleDependencyAdapter(Adapter): def __init__(self): - super().__init__(Struct( - rel_module=PrefixedArray(Int32ub, String), - rel_offset=PrefixedArray(Int32ub, Int32ub), - )) + super().__init__( + Struct( + rel_module=PrefixedArray(Int32ub, String), + rel_offset=PrefixedArray(Int32ub, Int32ub), + ) + ) def _decode(self, obj, context, path) -> list[list[str]]: offset_iter = iter(obj.rel_offset) - return [ - obj.rel_module[start:finish] - for start, finish in zip(offset_iter, offset_iter) - ] + return [obj.rel_module[start:finish] for start, finish in zip(offset_iter, offset_iter)] def _encode(self, obj: list[list[str]], context, path): offset = 0 @@ -130,10 +130,7 @@ def _encode(self, obj: list[list[str]], context, path): offset += len(layer) offsets.append(offset) - return { - "rel_module": list(itertools.chain(*obj)), - "rel_offset": offsets - } + return {"rel_module": list(itertools.chain(*obj)), "rel_offset": offsets} _all_categories = [ @@ -162,10 +159,7 @@ def _encode(self, obj: list[list[str]], context, path): "path_section": AssetIdCorrect, "portal_area_section": AssetIdCorrect, "static_geometry_map_section": AssetIdCorrect, - "unknown_section_1": Struct( - magic=If(game_check.is_prime3, Const("LLTE", FourCC)), - data=Const(1, Int32ub) - ), + "unknown_section_1": Struct(magic=If(game_check.is_prime3, Const("LLTE", FourCC)), data=Const(1, Int32ub)), "unknown_section_2": Struct( unk1=PrefixedArray(Int32ub, Int32ub), # TODO: rebuild according to surface group count @@ -173,62 +167,48 @@ def _encode(self, obj: list[list[str]], context, path): ), } -MREAHeader = Aligned(32, Struct( - "magic" / Const(0xDEADBEEF, Int32ub), - "version" / Enum(Int32ub, MREAVersion), - - # Matrix that represents the area's transform from the origin. - # Most area data is pre-transformed, so this matrix is only used occasionally. - "area_transform" / Transform4f, - - # Number of world models in this area. - "world_model_count" / Int32ub, - - # Number of script layers in this area. - "script_layer_count" / WithVersion(MREAVersion.Echoes, Int32ub), - - # Number of data sections in the file. - "data_section_count" / Int32ub, - - # Section index for world geometry data. Always 0; starts on materials. - "geometry_section" / Int32ub, - - # Section index for script layer data. - "script_layers_section" / Int32ub, - - # Section index for generated script object data. - "generated_script_objects_section" / WithVersion(MREAVersion.Echoes, Int32ub), - - # Section index for collision data. - "collision_section" / Int32ub, - - # Section index for first unknown section. - "unknown_section_1" / Int32ub, - - # Section index for light data. - "lights_section" / Int32ub, - - # Section index for visibility tree data. - "visibility_tree_section" / Int32ub, - - # Section index for path data. - "path_section" / Int32ub, - - # Section index for area octree data. - "area_octree_section" / BeforeVersion(MREAVersion.EchoesDemo, Int32ub), - - # Section index for second unknown section. - "unknown_section_2" / WithVersion(MREAVersion.Echoes, Int32ub), - - # Section index for portal area data. - "portal_area_section" / WithVersion(MREAVersion.Echoes, Int32ub), - - # Section index for static geometry map data. - "static_geometry_map_section" / WithVersion(MREAVersion.Echoes, Int32ub), - - # Number of compressed data blocks in the file. - "compressed_block_count" / WithVersion(MREAVersion.Echoes, Int32ub), -)) +MREAHeader = Aligned( + 32, + Struct( + "magic" / Const(0xDEADBEEF, Int32ub), + "version" / Enum(Int32ub, MREAVersion), + # Matrix that represents the area's transform from the origin. + # Most area data is pre-transformed, so this matrix is only used occasionally. + "area_transform" / Transform4f, + # Number of world models in this area. + "world_model_count" / Int32ub, + # Number of script layers in this area. + "script_layer_count" / WithVersion(MREAVersion.Echoes, Int32ub), + # Number of data sections in the file. + "data_section_count" / Int32ub, + # Section index for world geometry data. Always 0; starts on materials. + "geometry_section" / Int32ub, + # Section index for script layer data. + "script_layers_section" / Int32ub, + # Section index for generated script object data. + "generated_script_objects_section" / WithVersion(MREAVersion.Echoes, Int32ub), + # Section index for collision data. + "collision_section" / Int32ub, + # Section index for first unknown section. + "unknown_section_1" / Int32ub, + # Section index for light data. + "lights_section" / Int32ub, + # Section index for visibility tree data. + "visibility_tree_section" / Int32ub, + # Section index for path data. + "path_section" / Int32ub, + # Section index for area octree data. + "area_octree_section" / BeforeVersion(MREAVersion.EchoesDemo, Int32ub), + # Section index for second unknown section. + "unknown_section_2" / WithVersion(MREAVersion.Echoes, Int32ub), + # Section index for portal area data. + "portal_area_section" / WithVersion(MREAVersion.Echoes, Int32ub), + # Section index for static geometry map data. + "static_geometry_map_section" / WithVersion(MREAVersion.Echoes, Int32ub), + # Number of compressed data blocks in the file. + "compressed_block_count" / WithVersion(MREAVersion.Echoes, Int32ub), + ), +) CompressedBlockHeader = Struct( buffer_size=Int32ub, @@ -291,15 +271,16 @@ def _aligned_parse(self, conn: construct.Construct, stream, context, path): def _decode_compressed_blocks(self, mrea_header, data_section_sizes, stream, context, path) -> list[bytes]: compressed_block_headers = self._aligned_parse( - Array(mrea_header.compressed_block_count, CompressedBlockHeader), - stream, context, path + Array(mrea_header.compressed_block_count, CompressedBlockHeader), stream, context, path ) # Read compressed blocks from stream compressed_blocks = construct.ListContainer( self._aligned_parse( FixedSized(_get_compressed_block_size(header), GreedyBytes), - stream, context, path, + stream, + context, + path, ) for header in compressed_block_headers ) @@ -307,8 +288,9 @@ def _decode_compressed_blocks(self, mrea_header, data_section_sizes, stream, con # Decompress blocks into the data sections data_sections = ListContainer() for compressed_header, compressed_block in zip(compressed_block_headers, compressed_blocks): - subcon = _get_compressed_block_subcon(compressed_header.compressed_size, - compressed_header.uncompressed_size) + subcon = _get_compressed_block_subcon( + compressed_header.compressed_size, compressed_header.uncompressed_size + ) decompressed_block = subcon._parsereport(io.BytesIO(compressed_block), context, path) if len(decompressed_block) != compressed_header.uncompressed_size: raise construct.ConstructError( @@ -319,7 +301,7 @@ def _decode_compressed_blocks(self, mrea_header, data_section_sizes, stream, con for i in range(compressed_header.data_section_count): section_size = data_section_sizes[len(data_sections)] - data = decompressed_block[offset: offset + section_size] + data = decompressed_block[offset : offset + section_size] data_sections.append(data) offset += section_size @@ -339,9 +321,7 @@ def _parse(self, stream, context, path): # Split data sections into the named sections categories = [ - {"label": label, "value": mrea_header[label]} - for label in _all_categories - if mrea_header[label] is not None + {"label": label, "value": mrea_header[label]} for label in _all_categories if mrea_header[label] is not None ] categories.sort(key=lambda c: c["value"]) @@ -361,9 +341,9 @@ def _parse(self, stream, context, path): sections=construct.Container(), ) - def _encode_compressed_blocks(self, data_sections: list[bytes], - category_starts: dict[str, int | None], - context, path): + def _encode_compressed_blocks( + self, data_sections: list[bytes], category_starts: dict[str, int | None], context, path + ): def _start_new_group(group_size, section_size, curr_label, prev_label): # noqa: PLR0911 if group_size == 0: return False, "" @@ -400,14 +380,13 @@ def add_group(r): # The padding is not included in the block's uncompressed size merged_and_padded_group = b"".join( - item.ljust(len(item) + (-len(item) % 32), b"\x00") - for item in current_group + item.ljust(len(item) + (-len(item) % 32), b"\x00") for item in current_group ) header = Container( buffer_size=current_group_size, uncompressed_size=current_group_size, compressed_size=0, - data_section_count=len(current_group) + data_section_count=len(current_group), ) substream = io.BytesIO() @@ -421,10 +400,12 @@ def add_group(r): else: data = merged_and_padded_group - compressed_blocks.append(Container( - header=header, - data=data, - )) + compressed_blocks.append( + Container( + header=header, + data=data, + ) + ) current_group = ListContainer() current_group_size = 0 @@ -432,10 +413,7 @@ def add_group(r): all_garbage = [cat for cat, start in category_starts.items() if i >= start] cat_label = all_garbage[-1] - start_new, reason = _start_new_group( - current_group_size, len(section), - previous_label, cat_label - ) + start_new, reason = _start_new_group(current_group_size, len(section), previous_label, cat_label) if start_new: add_group(reason) @@ -454,8 +432,9 @@ def _build(self, obj: Container, stream, context, path): # Encode each category for category, values in obj.sections.items(): - raw_sections[category] = _encode_category(values, _CATEGORY_ENCODINGS.get(category), - context, f"{path} -> {category}") + raw_sections[category] = _encode_category( + values, _CATEGORY_ENCODINGS.get(category), context, f"{path} -> {category}" + ) # Combine all sections into the data sections array data_sections = ListContainer() @@ -469,12 +448,7 @@ def _build(self, obj: Container, stream, context, path): # Compress the data sections if int(obj.version) >= MREAVersion.Echoes.value: - compressed_blocks = self._encode_compressed_blocks( - data_sections, - mrea_header, - context, - path - ) + compressed_blocks = self._encode_compressed_blocks(data_sections, mrea_header, context, path) mrea_header.compressed_block_count = len(compressed_blocks) else: compressed_blocks = None @@ -489,12 +463,16 @@ def _build(self, obj: Container, stream, context, path): MREAHeader._build(mrea_header, stream, context, path) Aligned(32, Array(mrea_header.data_section_count, Int32ub))._build( [len(section) for section in data_sections], - stream, context, path, + stream, + context, + path, ) if compressed_blocks is not None: Aligned(32, Array(mrea_header.compressed_block_count, CompressedBlockHeader))._build( [block.header for block in compressed_blocks], - stream, context, path, + stream, + context, + path, ) for compressed_block in compressed_blocks: block_header = compressed_block.header @@ -536,7 +514,8 @@ def _ensure_decoded_section(self, section_name: str, lazy_load: bool = False): self._raw.sections[section_name] = _decode_category( self._raw.raw_sections[section_name], GreedyBytes if lazy_load else _CATEGORY_ENCODINGS[section_name], - context, "", + context, + "", ) def get_section(self, section_name: str, lazy_load: bool = False): @@ -579,24 +558,21 @@ def script_layers(self) -> Iterator[ScriptLayer]: for i, section in enumerate(self._raw.sections.script_layers_section): if i not in self._script_layer_helpers: self._script_layer_helpers[i] = ScriptLayer( - _CATEGORY_ENCODINGS["script_layers_section"].parse( - section, target_game=self.target_game - ), + _CATEGORY_ENCODINGS["script_layers_section"].parse(section, target_game=self.target_game), i, - self.target_game + self.target_game, ) yield from self._script_layer_helpers.values() _generated_objects_layer: ScriptLayer | None = None + @property def generated_objects_layer(self) -> ScriptLayer: assert self.target_game >= Game.ECHOES if self._generated_objects_layer is None: self._generated_objects_layer = ScriptLayer( - self.get_section("generated_script_objects_section")[0], - None, - self.target_game + self.get_section("generated_script_objects_section")[0], None, self.target_game ) return self._generated_objects_layer @@ -631,8 +607,15 @@ class Area: # FIXME: since the whole Mlvl is now being passed, this function can have the other arguments removed # FIXME: also every time i've tried to do this it breaks mlvl dependencies. good luck! - def __init__(self, raw: Container, asset_manager: AssetManager, flags: Container, names: Container, - index: int, parent_mlvl: Mlvl): + def __init__( + self, + raw: Container, + asset_manager: AssetManager, + flags: Container, + names: Container, + index: int, + parent_mlvl: Mlvl, + ): self._raw = raw self.asset_manager = asset_manager self._flags = flags @@ -784,9 +767,10 @@ def build_mlvl_dependencies(self, only_modified: bool = False): layer_deps = [ list( layer.build_mlvl_dependencies(self.asset_manager) - if (not only_modified) or layer.is_modified() else - layer.dependencies - ) for layer in self.layers + if (not only_modified) or layer.is_modified() + else layer.dependencies + ) + for layer in self.layers ] layer_deps = self.build_scgn_dependencies(layer_deps, only_modified) @@ -832,23 +816,18 @@ def build_module_dependencies(self, only_modified: bool = False): layer_rels = [ list( layer.build_module_dependencies() - if (not only_modified) or layer.is_modified() else - layer.module_dependencies - ) for layer in layers + if (not only_modified) or layer.is_modified() + else layer.module_dependencies + ) + for layer in layers ] for instance in self.generated_objects_layer.instances: layer = instance.id.layer - if ( - (not only_modified) - or layers[layer].is_modified() - ): + if (not only_modified) or layers[layer].is_modified(): layer_rels[layer].extend(instance.type.modules()) - layer_rels = [ - list(dict.fromkeys(layer)) - for layer in layer_rels - ] + layer_rels = [list(dict.fromkeys(layer)) for layer in layer_rels] self.module_dependencies = layer_rels @property @@ -861,10 +840,7 @@ def dependencies(self, new: AreaDependencies): @property def dependencies_by_layer(self) -> dict[str, list[Dependency]]: - deps = { - layer.name: [dep for dep in layer.dependencies if not dep.exclude_for_mlvl] - for layer in self.layers - } + deps = {layer.name: [dep for dep in layer.dependencies if not dep.exclude_for_mlvl] for layer in self.layers} deps["!!non_layer!!"] = list(self.dependencies.non_layer) return deps @@ -878,10 +854,7 @@ def module_dependencies(self, new: list[list[str]]): @property def module_dependencies_by_layer(self) -> dict[str, list[str]]: - return { - layer.name: list(layer.module_dependencies) - for layer in self.layers - } + return {layer.name: list(layer.module_dependencies) for layer in self.layers} def update_all_dependencies(self, only_modified: bool = False): self.build_mlvl_dependencies(only_modified) @@ -892,87 +865,87 @@ def update_all_dependencies(self, only_modified: bool = False): _hardcoded_dependencies: dict[int, dict[str, list[Dependency]]] = { 0xD7C3B839: { # Sanctum - "Default": [Dependency("TXTR", 0xd5b9e5d1)], - "Emperor Ing Stage 1": [Dependency("TXTR", 0x52c7d438)], - "Emperor Ing Stage 3": [Dependency("TXTR", 0xd5b9e5d1)], - "Emperor Ing Stage 1 Intro Cine": [Dependency("TXTR", 0x52c7d438)], - "Emperor Ing Stage 3 Death Cine": [Dependency("TXTR", 0xd5b9e5d1)], + "Default": [Dependency("TXTR", 0xD5B9E5D1)], + "Emperor Ing Stage 1": [Dependency("TXTR", 0x52C7D438)], + "Emperor Ing Stage 3": [Dependency("TXTR", 0xD5B9E5D1)], + "Emperor Ing Stage 1 Intro Cine": [Dependency("TXTR", 0x52C7D438)], + "Emperor Ing Stage 3 Death Cine": [Dependency("TXTR", 0xD5B9E5D1)], }, 0xA92F00B3: { # Hive Temple "CliffsideBoss": [ - Dependency("TXTR", 0x24149e16), - Dependency("TXTR", 0xbdb8a88a), - Dependency("FSM2", 0x3d31822b), + Dependency("TXTR", 0x24149E16), + Dependency("TXTR", 0xBDB8A88A), + Dependency("FSM2", 0x3D31822B), ] }, 0xC0113CE8: { # Dynamo Works - "3rd Pass": [Dependency("RULE", 0x393ca543)] + "3rd Pass": [Dependency("RULE", 0x393CA543)] }, 0x5571E89E: { # Hall of Combat Mastery - "2nd Pass Enemies": [Dependency("RULE", 0x393ca543)] + "2nd Pass Enemies": [Dependency("RULE", 0x393CA543)] }, 0x7B94B06B: { # Hive Portal Chamber - "1st Pass": [Dependency("RULE", 0x393ca543)], - "2nd Pass": [Dependency("RULE", 0x393ca543)] + "1st Pass": [Dependency("RULE", 0x393CA543)], + "2nd Pass": [Dependency("RULE", 0x393CA543)], }, 0xF8DBC03D: { # Hive Reactor - "2nd Pass": [Dependency("RULE", 0x393ca543)] + "2nd Pass": [Dependency("RULE", 0x393CA543)] }, 0xB666B655: { # Reactor Access - "2nd Pass": [Dependency("RULE", 0x393ca543)] + "2nd Pass": [Dependency("RULE", 0x393CA543)] }, 0xE79AAFAE: { # Transport A Access - "2nd Pass": [Dependency("RULE", 0x393ca543)] + "2nd Pass": [Dependency("RULE", 0x393CA543)] }, 0xFEB7BD27: { # Transport B Access - "Default": [Dependency("RULE", 0x393ca543)] + "Default": [Dependency("RULE", 0x393CA543)] }, 0x89D246FD: { # Portal Access - "Default": [Dependency("RULE", 0x393ca543)] + "Default": [Dependency("RULE", 0x393CA543)] }, 0x0253782D: { # Dark Forgotten Bridge - "Default": [Dependency("RULE", 0x393ca543)] + "Default": [Dependency("RULE", 0x393CA543)] }, 0x09DECF21: { # Forgotten Bridge - "Default": [Dependency("RULE", 0x393ca543)] + "Default": [Dependency("RULE", 0x393CA543)] }, 0x629790F4: { # Sacrificial Chamber - "1st Pass": [Dependency("RULE", 0x393ca543)] + "1st Pass": [Dependency("RULE", 0x393CA543)] }, 0xBBE4B3AE: { # Dungeon - "Default": [Dependency("TXTR", 0xe252e7f6)] + "Default": [Dependency("TXTR", 0xE252E7F6)] }, 0x2BCD44A7: { # Portal Terminal - "Default": [Dependency("TXTR", 0xb6fa5023)] + "Default": [Dependency("TXTR", 0xB6FA5023)] }, 0xC68B5B51: { # Transport to Sanctuary Fortress - "!!non_layer!!": [Dependency("TXTR", 0x75a219a8)] + "!!non_layer!!": [Dependency("TXTR", 0x75A219A8)] }, 0x625A2692: { # Temple Transport Access - "!!non_layer!!": [Dependency("TXTR", 0x581c56ea)] + "!!non_layer!!": [Dependency("TXTR", 0x581C56EA)] }, 0x96F4CA1E: { # Minigyro Chamber - "Default": [Dependency("TXTR", 0xac080dfb)] + "Default": [Dependency("TXTR", 0xAC080DFB)] }, 0x5BBF334F: { # Staging Area - "!!non_layer!!": [Dependency("TXTR", 0x738feb19)] - } + "!!non_layer!!": [Dependency("TXTR", 0x738FEB19)] + }, } diff --git a/src/retro_data_structures/formats/msbt.py b/src/retro_data_structures/formats/msbt.py index 28eea3a..7143d68 100644 --- a/src/retro_data_structures/formats/msbt.py +++ b/src/retro_data_structures/formats/msbt.py @@ -13,23 +13,29 @@ if typing.TYPE_CHECKING: from retro_data_structures.game_check import Game -MSBTHeader = construct.Aligned(16, Struct( - magic=construct.Const(b"MsgStdBn"), - bom=construct.Const(0xFEFF, construct.Int16ul), - unk1=construct.Int16ul, - maybe_major_version=construct.Int8ul, - maybe_minor_version=construct.Int8ul, - section_count=construct.Int16ul, - unk3=construct.Int16ul, - file_size=construct.Int32ul, -)) +MSBTHeader = construct.Aligned( + 16, + Struct( + magic=construct.Const(b"MsgStdBn"), + bom=construct.Const(0xFEFF, construct.Int16ul), + unk1=construct.Int16ul, + maybe_major_version=construct.Int8ul, + maybe_minor_version=construct.Int8ul, + section_count=construct.Int16ul, + unk3=construct.Int16ul, + file_size=construct.Int32ul, + ), +) def TableHeader(magic: str): - return construct.Aligned(16, Struct( - magic=construct.Const(magic, FourCC), - table_size=construct.Int32ul, - )) + return construct.Aligned( + 16, + Struct( + magic=construct.Const(magic, FourCC), + table_size=construct.Int32ul, + ), + ) class SectionBody(construct.Construct): @@ -61,10 +67,7 @@ def _parse(self, stream, context, path): entry_headers = construct.Array(count, self.entry_header)._parsereport(stream, context, path) - all_offsets = [ - self.get_offset_from_header(entry_header) - for entry_header in entry_headers - ] + all_offsets = [self.get_offset_from_header(entry_header) for entry_header in entry_headers] all_offsets.append(table_size) result = construct.ListContainer() @@ -98,9 +101,12 @@ def _build(self, obj, stream, context, path): stream2 = io.BytesIO() self.entry._build(item, stream2, new_context, path) items.append(stream2.getvalue()) - entry_headers.append(self._header_for_entry( - offset, item, - )) + entry_headers.append( + self._header_for_entry( + offset, + item, + ) + ) offset += len(items[-1]) # Build! @@ -108,7 +114,9 @@ def _build(self, obj, stream, context, path): construct.Container( table_size=offset, ), - stream, context, path, + stream, + context, + path, ) table_start = construct.stream_tell(stream, path) self.int_type._build(count, stream, context, path) @@ -157,9 +165,14 @@ def _header_for_entry(self, offset: int, entry): def _context_for_entry(self, context, entry_or_header): new_context = construct.Container( - _=context, _params=context._params, _root=None, _parsing=context._parsing, - _building=context._building, _sizing=context._sizing, - _io=context._io, _index=context.get("_index", None), + _=context, + _params=context._params, + _root=None, + _parsing=context._parsing, + _building=context._building, + _sizing=context._sizing, + _io=context._io, + _index=context.get("_index", None), ) new_context._root = new_context._.get("_root", context) @@ -173,17 +186,25 @@ def _context_for_entry(self, context, entry_or_header): LabelsSection = construct.Aligned(16, LabelsSectionBody(), b"\xAB") -AttributesSection = construct.Aligned(16, SectionBody( - header_magic="ATR1", - entry=construct.CString("utf_16_le"), - has_entry_size=True, -), b"\xAB") - -TextsSection = construct.Aligned(16, SectionBody( - header_magic="TXT2", - entry=construct.StringEncoded(construct.GreedyBytes, "utf_16_le"), - has_entry_size=False, -), b"\xAB") +AttributesSection = construct.Aligned( + 16, + SectionBody( + header_magic="ATR1", + entry=construct.CString("utf_16_le"), + has_entry_size=True, + ), + b"\xAB", +) + +TextsSection = construct.Aligned( + 16, + SectionBody( + header_magic="TXT2", + entry=construct.StringEncoded(construct.GreedyBytes, "utf_16_le"), + has_entry_size=False, + ), + b"\xAB", +) def Language(language_code: str): @@ -202,21 +223,26 @@ def Language(language_code: str): ) -MSBT = FormDescriptor("MSBT", 10, 10, Struct( - us_english=Language("USEN"), - eu_english=Language("EUEN"), - eu_french=Language("EUFR"), - us_french=Language("USFR"), - eu_spanish=Language("EUSP"), - eu_german=Language("EUGE"), - eu_italian=Language("EUIT"), - eu_dutch=Language("EUDU"), - jp_japanese=Language("JPJP"), - ko_korean=Language("KOKO"), - ch_traditionalchinese=Language("CHTC"), - ch_simplifiedchinese=Language("CHSC"), - us_spanish=Language("USSP"), -)) +MSBT = FormDescriptor( + "MSBT", + 10, + 10, + Struct( + us_english=Language("USEN"), + eu_english=Language("EUEN"), + eu_french=Language("EUFR"), + us_french=Language("USFR"), + eu_spanish=Language("EUSP"), + eu_german=Language("EUGE"), + eu_italian=Language("EUIT"), + eu_dutch=Language("EUDU"), + jp_japanese=Language("JPJP"), + ko_korean=Language("KOKO"), + ch_traditionalchinese=Language("CHTC"), + ch_simplifiedchinese=Language("CHSC"), + us_spanish=Language("USSP"), + ), +) class Msbt(BaseResource): diff --git a/src/retro_data_structures/formats/pak.py b/src/retro_data_structures/formats/pak.py index 28b961a..f771501 100644 --- a/src/retro_data_structures/formats/pak.py +++ b/src/retro_data_structures/formats/pak.py @@ -69,13 +69,15 @@ def replace_asset(self, asset_id: AssetId, asset: RawResource): raise ValueError(f"Unknown asset id: {asset_id}") def add_asset(self, asset_id: AssetId, asset: RawResource): - self._raw.files.append(PakFile( - asset_id=asset_id, - asset_type=asset.type, - should_compress=False, - uncompressed_data=asset.data, - compressed_data=None, - )) + self._raw.files.append( + PakFile( + asset_id=asset_id, + asset_type=asset.type, + should_compress=False, + uncompressed_data=asset.data, + compressed_data=None, + ) + ) def remove_asset(self, asset_id: AssetId): for name, file in self._raw.named_resources.items(): diff --git a/src/retro_data_structures/formats/pak_gc.py b/src/retro_data_structures/formats/pak_gc.py index e8c5d56..effc985 100644 --- a/src/retro_data_structures/formats/pak_gc.py +++ b/src/retro_data_structures/formats/pak_gc.py @@ -106,18 +106,19 @@ def _parse(self, stream, context, path) -> PakBody: uncompressed_data = data compressed_data = None - files.append(PakFile( - resource.asset.id, - resource.asset.type, - resource.compressed > 0, - uncompressed_data, - compressed_data, - )) + files.append( + PakFile( + resource.asset.id, + resource.asset.type, + resource.compressed > 0, + uncompressed_data, + compressed_data, + ) + ) return PakBody( named_resources={ - named.name: Dependency(type=named.asset.type, id=named.asset.id) - for named in header.named_resources + named.name: Dependency(type=named.asset.type, id=named.asset.id) for named in header.named_resources }, files=files, ) diff --git a/src/retro_data_structures/formats/pak_wiiu.py b/src/retro_data_structures/formats/pak_wiiu.py index 07c3324..6dfd031 100644 --- a/src/retro_data_structures/formats/pak_wiiu.py +++ b/src/retro_data_structures/formats/pak_wiiu.py @@ -49,7 +49,10 @@ ) TOCC = FormDescriptor( - "TOCC", 3, 3, construct.ExprAdapter( + "TOCC", + 3, + 3, + construct.ExprAdapter( UntilEof(TOCCChunkDescriptor), lambda obj, ctx: construct.Container((chunk.id, chunk) for chunk in obj), lambda obj, ctx: construct.ListContainer(obj.values()), @@ -57,7 +60,10 @@ ) PakWiiU = FormDescriptor( - "PACK", 1, 0, Struct( + "PACK", + 1, + 0, + Struct( tocc=TOCC, remain=construct.GreedyBytes, ), @@ -85,19 +91,21 @@ def _parse(self, stream, context, path): construct.stream_seek(stream, resource.offset, 0, path) data = construct.stream_read(stream, resource.size, path) - files.append(PakFile( - resource.asset.id, - resource.asset.type, - False, - data, - None, - extra=construct.Container( - version_a=resource.version_a, - version_b=resource.version_b, - offset=resource.offset, - decompressed_size=resource.decompressed_size, - ), - )) + files.append( + PakFile( + resource.asset.id, + resource.asset.type, + False, + data, + None, + extra=construct.Container( + version_a=resource.version_a, + version_b=resource.version_b, + offset=resource.offset, + decompressed_size=resource.decompressed_size, + ), + ) + ) last = max(last, construct.stream_tell(stream, path)) construct.stream_seek(stream, last, 0, path) @@ -129,14 +137,15 @@ def _build(self, obj, stream, context, path): size=len(file.get_decompressed(game)), ) for file in sorted(files, key=lambda it: it.asset_id) - ) + ), ) header_start = construct.stream_tell(stream, path) PakWiiUNoData._build(obj.header, stream, context, f"{path} -> header") - for i, (adir, file) in enumerate(sorted(zip(tocc.ADIR.data, files), - key=lambda it: it[1].extra.offset or math.inf)): + for i, (adir, file) in enumerate( + sorted(zip(tocc.ADIR.data, files), key=lambda it: it[1].extra.offset or math.inf) + ): adir.offset = construct.stream_tell(stream, f"{path} -> file[{i}]") data = file.get_decompressed(game) construct.stream_write(stream, data, len(data), f"{path} -> file[{i}]") diff --git a/src/retro_data_structures/formats/room.py b/src/retro_data_structures/formats/room.py index 3f29285..64b375b 100644 --- a/src/retro_data_structures/formats/room.py +++ b/src/retro_data_structures/formats/room.py @@ -22,18 +22,26 @@ GreedyBytes = typing.cast(construct.Construct, construct.GreedyBytes) -LoadUnit = FormDescriptor("LUNT", 0, 0, Struct( - header=SingleTypeChunkDescriptor("LUHD", Struct( - name=construct.PascalString(Int32ul, "utf8"), - guid=GUID, - idB=GUID, - unk1=construct.Int16ul, - unk2=Int32ul, - rest=GreedyBytes, - )), - load_resources=SingleTypeChunkDescriptor("LRES", construct.PrefixedArray(Int32ul, GUID)), - load_layers=SingleTypeChunkDescriptor("LLYR", construct.PrefixedArray(Int32ul, GUID)), -)) +LoadUnit = FormDescriptor( + "LUNT", + 0, + 0, + Struct( + header=SingleTypeChunkDescriptor( + "LUHD", + Struct( + name=construct.PascalString(Int32ul, "utf8"), + guid=GUID, + idB=GUID, + unk1=construct.Int16ul, + unk2=Int32ul, + rest=GreedyBytes, + ), + ), + load_resources=SingleTypeChunkDescriptor("LRES", construct.PrefixedArray(Int32ul, GUID)), + load_layers=SingleTypeChunkDescriptor("LLYR", construct.PrefixedArray(Int32ul, GUID)), + ), +) PerformanceGroups = construct.PrefixedArray( construct.Int16ul, @@ -50,7 +58,7 @@ Struct( object_id=GUID, layer_id=GUID, - ) + ), ) Docks = construct.PrefixedArray( @@ -83,24 +91,29 @@ id_e=GUID, id_f=GUID, work_stages=Struct( - items=construct.PrefixedArray(construct.Int16ul, Struct( - type=Hex(construct.Int32ul), - data=construct.Prefixed(construct.Int16ul, construct.Switch( - construct.this.type, - { - 0x7B68B3A0: DataEnumValue, - 0xb9187c94: DataEnumValue, - 0xec5d9069: DataEnumValue, - 0x8333a604: DataEnumValue, - 0x5ca42b9d: DataEnumValue, - - # LdrToEnum_ProductionWorkStageEnu - 0x113F9CE1: Hex(Int32ul), - }, - # TODO: should actually be GreedyBytes since the game skips over unknown ids - ErrorWithMessage(lambda ctx: f"Unknown type: {ctx.type}"), - )) - )), + items=construct.PrefixedArray( + construct.Int16ul, + Struct( + type=Hex(construct.Int32ul), + data=construct.Prefixed( + construct.Int16ul, + construct.Switch( + construct.this.type, + { + 0x7B68B3A0: DataEnumValue, + 0xB9187C94: DataEnumValue, + 0xEC5D9069: DataEnumValue, + 0x8333A604: DataEnumValue, + 0x5CA42B9D: DataEnumValue, + # LdrToEnum_ProductionWorkStageEnu + 0x113F9CE1: Hex(Int32ul), + }, + # TODO: should actually be GreedyBytes since the game skips over unknown ids + ErrorWithMessage(lambda ctx: f"Unknown type: {ctx.type}"), + ), + ), + ), + ), ), ) @@ -110,18 +123,19 @@ construct.Check(lambda ctx: ctx.unk1 == 3), "resource1" / GUID, "unk2" / construct.PrefixedArray(Int32ul, GUID), - "unk3" / construct.PrefixedArray(Int32ul, Struct( - "unk1" / Int32ul, - "unk2" / Int32ul, - "unk3" / Int32ul, - "unk4" / construct.Const(0, Int32ul) - )), + "unk3" + / construct.PrefixedArray( + Int32ul, Struct("unk1" / Int32ul, "unk2" / Int32ul, "unk3" / Int32ul, "unk4" / construct.Const(0, Int32ul)) + ), "resource2" / GUID, - construct.Terminated + construct.Terminated, ) RoomHeader = FormDescriptor( - "HEAD", 0, 0, Struct( + "HEAD", + 0, + 0, + Struct( game_area_header=SingleTypeChunkDescriptor("RMHD", GameAreaHeader), performance_groups=SingleTypeChunkDescriptor("PGRP", PerformanceGroups), generated_object_map=SingleTypeChunkDescriptor("LGEN", GeneratedObjectMap), @@ -161,10 +175,13 @@ SizeofAllocationsForEventCriteriaSLdrFromStream = Struct( a=Int32ul, - b=construct.If(construct.this.a != 0, Struct( - a=CalculateAllocatedMemoryForTypedefInterfaceSLdrFromCRC32, - b=construct.Prefixed(Int32ul, GreedyBytes), - )), + b=construct.If( + construct.this.a != 0, + Struct( + a=CalculateAllocatedMemoryForTypedefInterfaceSLdrFromCRC32, + b=construct.Prefixed(Int32ul, GreedyBytes), + ), + ), ) SizeofAllocationsForActionPayloadSLdrFromStream = SizeofAllocationsForEventCriteriaSLdrFromStream SizeofAllocationsForLinkDataSLdrFromStream = SizeofAllocationsForEventCriteriaSLdrFromStream @@ -208,40 +225,46 @@ def _decode(self, obj, context, path): ) ScriptData = Struct( - sdhr=SingleTypeChunkDescriptor("SDHR", Struct( - properties_count=Int32ul, - instance_data_count=Int32ul, - weird_count=Int32ul, - guids=construct.Array(construct.this.weird_count, GUID), - unk=construct.Array(construct.this.weird_count, Struct( - a=Int32ul, - b=Int32ul - )) - )), + sdhr=SingleTypeChunkDescriptor( + "SDHR", + Struct( + properties_count=Int32ul, + instance_data_count=Int32ul, + weird_count=Int32ul, + guids=construct.Array(construct.this.weird_count, GUID), + unk=construct.Array(construct.this.weird_count, Struct(a=Int32ul, b=Int32ul)), + ), + ), properties=construct.Array(construct.this.sdhr.properties_count, SingleTypeChunkDescriptor("SDEN", Property)), - instance_data=construct.Array(construct.this.sdhr.instance_data_count, SingleTypeChunkDescriptor("IDTA", Struct( - guid=GUID, - str=ConstructPooledString, - connections=construct.PrefixedArray( - construct.Int16ul, + instance_data=construct.Array( + construct.this.sdhr.instance_data_count, + SingleTypeChunkDescriptor( + "IDTA", Struct( - skip1=construct.Bytes(0x1a), - event_criteria_sldr=SizeofAllocationsForEventCriteriaSLdrFromStream, - action_payload_sldr=SizeofAllocationsForActionPayloadSLdrFromStream, - skip2=construct.Bytes(0x13), + guid=GUID, + str=ConstructPooledString, + connections=construct.PrefixedArray( + construct.Int16ul, + Struct( + skip1=construct.Bytes(0x1A), + event_criteria_sldr=SizeofAllocationsForEventCriteriaSLdrFromStream, + action_payload_sldr=SizeofAllocationsForActionPayloadSLdrFromStream, + skip2=construct.Bytes(0x13), + ), + ), + script_links=construct.PrefixedArray( + construct.Int16ul, + Struct( + a=Int32ul, + b=GUID, + c=SizeofAllocationsForLinkDataSLdrFromStream, + skip2=construct.Bytes(0x12), + ), + ), + skip_the_rest=GreedyBytes, ), ), - script_links=construct.PrefixedArray( - construct.Int16ul, - Struct( - a=Int32ul, - b=GUID, - c=SizeofAllocationsForLinkDataSLdrFromStream, - skip2=construct.Bytes(0x12), - ) - ), - skip_the_rest=GreedyBytes, - ))), + ), ) GameObjectComponent = Struct( @@ -249,32 +272,47 @@ def _decode(self, obj, context, path): property_idx=Int32ul, instance_idx=Int32ul, ) -GeneratedGameObject = SingleTypeChunkDescriptor("GGOB", Struct( - generated_game_object_id=GUID, - components=PrefixedArray(Int16ul, GameObjectComponent) -)) +GeneratedGameObject = SingleTypeChunkDescriptor( + "GGOB", Struct(generated_game_object_id=GUID, components=PrefixedArray(Int16ul, GameObjectComponent)) +) -Layer = FormDescriptor("LAYR", 0, 0, Struct( - header=SingleTypeChunkDescriptor("LHED", Struct( - name=construct.PascalString(Int32ul, "utf8"), - id=GUID, - unk=Int32ul, - id2=GUID, - rest=GreedyBytes, - )), - generated_script_object=FormDescriptor("GSRP", 0, 0, UntilEof(GeneratedGameObject)), - components=FormDescriptor("SRIP", 0, 0, SingleTypeChunkDescriptor("COMP", UntilEof(GameObjectComponent))), -)) +Layer = FormDescriptor( + "LAYR", + 0, + 0, + Struct( + header=SingleTypeChunkDescriptor( + "LHED", + Struct( + name=construct.PascalString(Int32ul, "utf8"), + id=GUID, + unk=Int32ul, + id2=GUID, + rest=GreedyBytes, + ), + ), + generated_script_object=FormDescriptor("GSRP", 0, 0, UntilEof(GeneratedGameObject)), + components=FormDescriptor("SRIP", 0, 0, SingleTypeChunkDescriptor("COMP", UntilEof(GameObjectComponent))), + ), +) ROOM = FormDescriptor( - "ROOM", 147, 160, Struct( + "ROOM", + 147, + 160, + Struct( header=RoomHeader, strp=SingleTypeChunkDescriptor("STRP", STRP), script_data=FormDescriptor("SDTA", 0, 0, ScriptData), - layers=FormDescriptor("LYRS", 0, 0, construct.Array( - lambda ctx: len(ctx._._.header.performance_groups[0].layer_guids), - Layer, - )), + layers=FormDescriptor( + "LYRS", + 0, + 0, + construct.Array( + lambda ctx: len(ctx._._.header.performance_groups[0].layer_guids), + Layer, + ), + ), ), ) @@ -303,7 +341,7 @@ def get_pooled_string(self, pooled_string: PooledString) -> bytes: if pooled_string.index == -1: return pooled_string.size_or_str else: - return self.raw.strp.pools[0][pooled_string.index:pooled_string.index + pooled_string.size_or_str] + return self.raw.strp.pools[0][pooled_string.index : pooled_string.index + pooled_string.size_or_str] def properties_of_type(self, t: type[T]) -> typing.Iterator[T]: for prop in self.raw.script_data.properties: @@ -321,8 +359,9 @@ def test(self): instances: dict[int, Instance] = {} def _add_inst(inst): - instances[inst.instance_idx] = Instance(instance_data[inst.instance_idx].guid, - properties[inst.property_idx]) + instances[inst.instance_idx] = Instance( + instance_data[inst.instance_idx].guid, properties[inst.property_idx] + ) for layer in self.raw.layers: for comp in layer.components: @@ -336,8 +375,10 @@ def _add_inst(inst): for i in range(weird_count): guids[i] unk = weird[i] - if type(instances[unk.a].properties.data).__name__ != "EntityProperties" and type( - instances[unk.b].properties.data).__name__ != "EntityProperties": + if ( + type(instances[unk.a].properties.data).__name__ != "EntityProperties" + and type(instances[unk.b].properties.data).__name__ != "EntityProperties" + ): print(f"i: {i}") print(f"a: {instances[unk.a]}") print(f"b: {instances[unk.b]}") diff --git a/src/retro_data_structures/formats/scan.py b/src/retro_data_structures/formats/scan.py index a199f26..4fd1c8e 100644 --- a/src/retro_data_structures/formats/scan.py +++ b/src/retro_data_structures/formats/scan.py @@ -41,14 +41,18 @@ "junk" / GreedyRange(Byte), ) -Prime23SCAN = Aligned(32, Struct( - "magic" / Const("SCAN", FourCC), - "unknown1" / Const(2, Int32ub), - "unknown2" / Byte, - "instance_count" / Const(1, Int32ub), - "scannable_object_info" / ConstructScriptInstance, - "dependencies" / DGRP, -), b"\xff") +Prime23SCAN = Aligned( + 32, + Struct( + "magic" / Const("SCAN", FourCC), + "unknown1" / Const(2, Int32ub), + "unknown2" / Byte, + "instance_count" / Const(1, Int32ub), + "scannable_object_info" / ConstructScriptInstance, + "dependencies" / DGRP, + ), + b"\xff", +) SCAN = IfThenElse(game_check.is_prime1, Prime1SCAN, Prime23SCAN) @@ -87,12 +91,14 @@ def dependencies_for(self) -> typing.Iterator[Dependency]: yield Dependency(it.type, it.id, True) _scannable_object_info: ScriptInstance | None = None + @property def scannable_object_info(self) -> ScriptInstance: assert self.target_game != Game.PRIME if self._scannable_object_info is None: - self._scannable_object_info = ScriptInstance(self._raw.scannable_object_info, self.target_game, - on_modify=self.rebuild_dependencies) + self._scannable_object_info = ScriptInstance( + self._raw.scannable_object_info, self.target_game, on_modify=self.rebuild_dependencies + ) return self._scannable_object_info def rebuild_dependencies(self): @@ -101,7 +107,5 @@ def rebuild_dependencies(self): return scan_info = self.scannable_object_info.get_properties() self._raw.dependencies = [ - {"asset_type": dep.type, "asset_id": dep.id} - for dep in scan_info.dependencies_for(self.asset_manager) + {"asset_type": dep.type, "asset_id": dep.id} for dep in scan_info.dependencies_for(self.asset_manager) ] - diff --git a/src/retro_data_structures/formats/script_layer.py b/src/retro_data_structures/formats/script_layer.py index d90ee78..0140df0 100644 --- a/src/retro_data_structures/formats/script_layer.py +++ b/src/retro_data_structures/formats/script_layer.py @@ -48,7 +48,8 @@ Skip(1, Int32ub), "_layer_size_address" / Tell, Seek(lambda this: (this._layer_count or len(this.layers)) * Int32ub.sizeof(), 1), - "layers" / PrefixedArray( + "layers" + / PrefixedArray( Pointer(construct.this._._layer_count_address, Int32ub), Prefixed( Pointer(lambda this: this._._layer_size_address + this._index * Int32ub.sizeof(), Int32ub), @@ -74,26 +75,26 @@ def ConstructScriptLayer(identifier): def new_layer(index: int | None, target_game: Game) -> Container: if target_game <= Game.PRIME: raise NotImplementedError - return Container({ - "magic": "SCLY" if index is not None else "SCGN", - "unknown": 0, - "layer_index": index, - "version": 1, - "script_instances": [] - }) + return Container( + { + "magic": "SCLY" if index is not None else "SCGN", + "unknown": 0, + "layer_index": index, + "version": 1, + "script_instances": [], + } + ) SCLY = IfThenElse( - game_check.current_game_at_least(game_check.Game.ECHOES), - ConstructScriptLayer("SCLY"), - ScriptLayerPrime + game_check.current_game_at_least(game_check.Game.ECHOES), ConstructScriptLayer("SCLY"), ScriptLayerPrime ) SCGN = ConstructScriptLayer("SCGN") -def dependencies_for_layer(asset_manager: AssetManager, - instances: typing.Iterable[ScriptInstance] - ) -> typing.Iterator[Dependency]: +def dependencies_for_layer( + asset_manager: AssetManager, instances: typing.Iterable[ScriptInstance] +) -> typing.Iterator[Dependency]: deps: list[Dependency] = [] for instance in instances: deps.extend(instance.mlvl_dependencies_for(asset_manager)) @@ -189,10 +190,7 @@ def remove_instance(self, instance: InstanceRef): instance = self._get_instance_by_name(instance) instance = resolve_instance_id(instance) - matching_instances = [ - i for i in self._raw.script_instances - if i.id == instance - ] + matching_instances = [i for i in self._raw.script_instances if i.id == instance] if not matching_instances: raise KeyError(instance) diff --git a/src/retro_data_structures/formats/script_object.py b/src/retro_data_structures/formats/script_object.py index d9a6f2c..bc1d61f 100644 --- a/src/retro_data_structures/formats/script_object.py +++ b/src/retro_data_structures/formats/script_object.py @@ -70,11 +70,11 @@ def layer(self) -> int: @property def area(self) -> int: - return (self >> 16) & 0x3ff + return (self >> 16) & 0x3FF @property def instance(self) -> int: - return self & 0xffff + return self & 0xFFFF @dataclasses.dataclass(frozen=True) @@ -167,16 +167,26 @@ def _build(self, obj: ScriptInstanceRaw, stream, context, path): header_construct, body_construct = _PrimeRawScript sub_stream = io.BytesIO() - body_construct._build(construct.Container( - instance_id=obj.id, - connections=[conn.as_construct() for conn in obj.connections], - base_property=obj.base_property, - ), sub_stream, context, path) + body_construct._build( + construct.Container( + instance_id=obj.id, + connections=[conn.as_construct() for conn in obj.connections], + base_property=obj.base_property, + ), + sub_stream, + context, + path, + ) - header_construct._build(construct.Container( - type=obj.type, - raw_data=sub_stream.getvalue(), - ), stream, context, path) + header_construct._build( + construct.Container( + type=obj.type, + raw_data=sub_stream.getvalue(), + ), + stream, + context, + path, + ) ConstructScriptInstance = _ConstructScriptInstance() @@ -200,15 +210,15 @@ def _resolve_to_enum(correct_type: type[E], value: str | enum.Enum) -> E: def _try_quick_get_name(data: bytes) -> str | None: try: # Is first property EditorProperties? - if data[8:12] != b'%ZE\x80': + if data[8:12] != b"%ZE\x80": return None # 12:14 (first prop size) # 14:16 (EditorProperties, prop count) - if data[16:20] != b'INAM': + if data[16:20] != b"INAM": return None string_size = struct.unpack_from(">H", data, 20)[0] - return data[22:22 + string_size - 1].decode("ascii") + return data[22 : 22 + string_size - 1].decode("ascii") except IndexError: return None @@ -330,11 +340,13 @@ def add_connection(self, state: str | State, message: str | Message, target: Ins target = resolve_instance_id(target) - self.connections = self.connections + (Connection( - state=_resolve_to_enum(correct_state, state), - message=_resolve_to_enum(correct_message, message), - target=target - ),) + self.connections = self.connections + ( + Connection( + state=_resolve_to_enum(correct_state, state), + message=_resolve_to_enum(correct_message, message), + target=target, + ), + ) def remove_connection(self, connection: Connection): self.connections = [c for c in self.connections if c != connection] @@ -350,6 +362,7 @@ def mlvl_dependencies_for(self, asset_manager: AssetManager) -> Iterator[Depende InstanceIdRef = InstanceId | int | ScriptInstance InstanceRef = InstanceIdRef | str + def resolve_instance_id(inst: InstanceIdRef) -> InstanceId: if isinstance(inst, InstanceId): return inst diff --git a/src/retro_data_structures/formats/strg.py b/src/retro_data_structures/formats/strg.py index 165b9c2..13a238e 100644 --- a/src/retro_data_structures/formats/strg.py +++ b/src/retro_data_structures/formats/strg.py @@ -195,6 +195,7 @@ def _compute_corruption_strings_size(ctx): image_regex = re.compile(r"&image=(?:.+?,)*?((?:[a-fA-F0-9]+,?)+);") font_regex = re.compile(r"&font=([a-fA-F0-9]+?);") + class Strg(BaseResource): @classmethod def resource_type(cls) -> AssetType: @@ -207,6 +208,7 @@ def construct_class(cls, target_game: Game) -> construct.Construct: def dependencies_for(self) -> typing.Iterator[Dependency]: def _str_to_deps(id_str: str): yield from self.asset_manager.get_dependencies_for_asset(int(id_str, 16)) + for lang in self.languages: for string in self.get_strings(lang): for match in image_regex.finditer(string): @@ -216,7 +218,6 @@ def _str_to_deps(id_str: str): for match in font_regex.finditer(string): yield from _str_to_deps(match.group(1)) - @property def languages(self) -> typing.Iterator[str]: if self._raw.prime3: diff --git a/src/retro_data_structures/formats/world_geometry.py b/src/retro_data_structures/formats/world_geometry.py index 2e4cc02..25aaada 100644 --- a/src/retro_data_structures/formats/world_geometry.py +++ b/src/retro_data_structures/formats/world_geometry.py @@ -23,10 +23,8 @@ def lazy_world_geometry(): - return Optional(Struct( - "materials" / MaterialSet, - "data" / GreedyBytes - )) + return Optional(Struct("materials" / MaterialSet, "data" / GreedyBytes)) + # # TODO: FlagEnum # WorldModelHeader = Struct("visor_flags" / Int32ub, "transform" / Transform4f, "bounding_box" / AABox) diff --git a/src/retro_data_structures/game_check.py b/src/retro_data_structures/game_check.py index 073eb28..5bca599 100644 --- a/src/retro_data_structures/game_check.py +++ b/src/retro_data_structures/game_check.py @@ -92,7 +92,7 @@ def is_valid_asset_id(self, asset_id: int | uuid.UUID) -> bool: def mlvl_dependencies_to_ignore(self) -> tuple[AssetId]: if self == Game.ECHOES: # Textures/Misc/VisorSteamQtr.TXTR - return (0x7b2ea5b1,) + return (0x7B2EA5B1,) return () def audio_group_dependencies(self): @@ -106,11 +106,11 @@ def special_ancs_dependencies(self, ancs: AssetId): if self == Game.ECHOES: if ancs == 0xC043D342: # every gun animation needs these i guess - yield Dependency("TXTR", 0x9e6f9531, False) - yield Dependency("TXTR", 0xcea098fe, False) - yield Dependency("TXTR", 0x607638ea, False) - yield Dependency("TXTR", 0x578e51b8, False) - yield Dependency("TXTR", 0x1e7b6c64, False) + yield Dependency("TXTR", 0x9E6F9531, False) + yield Dependency("TXTR", 0xCEA098FE, False) + yield Dependency("TXTR", 0x607638EA, False) + yield Dependency("TXTR", 0x578E51B8, False) + yield Dependency("TXTR", 0x1E7B6C64, False) if ancs == 0x2E980BF2: # samus ANCS from Hive Chamber A @@ -167,9 +167,11 @@ def _emitparse(self, code: construct.CodeGen): def _emitbuild(self, code: construct.CodeGen): code.append("from retro_data_structures import game_check") - return (f"(({self.thensubcon._compilebuild(code)})" - f" if (game_check.get_current_game(this) >= game_check.Game.{self.target_game.name})" - f" else ({self.elsesubcon._compilebuild(code)}))") + return ( + f"(({self.thensubcon._compilebuild(code)})" + f" if (game_check.get_current_game(this) >= game_check.Game.{self.target_game.name})" + f" else ({self.elsesubcon._compilebuild(code)}))" + ) def current_game_at_least_else(target: Game, subcon1, subcon2) -> IfThenElse: diff --git a/src/retro_data_structures/properties/__init__.py b/src/retro_data_structures/properties/__init__.py index 4979d04..6db4f7c 100644 --- a/src/retro_data_structures/properties/__init__.py +++ b/src/retro_data_structures/properties/__init__.py @@ -11,15 +11,19 @@ def get_game_object(game: Game, four_cc: str | int) -> type[BaseObjectType]: if game == Game.PRIME: from .prime import objects as prime_objects + return prime_objects.get_object(four_cc) elif game == Game.ECHOES: from .echoes import objects as echoes_objects + return echoes_objects.get_object(four_cc) elif game == Game.CORRUPTION: from .corruption import objects as corruption_objects + return corruption_objects.get_object(four_cc) elif game == Game.PRIME_REMASTER: from .prime_remastered import objects as prime_remastered_objects + return prime_remastered_objects.get_object(four_cc) else: raise ValueError(f"Unknown Game: {game}") diff --git a/src/retro_data_structures/properties/base_property.py b/src/retro_data_structures/properties/base_property.py index 2db0323..dcc194f 100644 --- a/src/retro_data_structures/properties/base_property.py +++ b/src/retro_data_structures/properties/base_property.py @@ -46,8 +46,9 @@ def _is_property_mrea_or_mlvl(self, field: dataclasses.Field) -> bool: asset_types = field.metadata.get("asset_types", []) return any((typ in asset_types) for typ in ("MLVL", "MREA")) - def _dependencies_for_field(self, field: dataclasses.Field, asset_manager: AssetManager - ) -> typing.Iterator[Dependency]: + def _dependencies_for_field( + self, field: dataclasses.Field, asset_manager: AssetManager + ) -> typing.Iterator[Dependency]: if issubclass(field.type, BaseProperty): prop: BaseProperty = getattr(self, field.name) yield from prop.dependencies_for(asset_manager) @@ -56,7 +57,7 @@ def _dependencies_for_field(self, field: dataclasses.Field, asset_manager: Asset sound_id: int = getattr(self, field.name) yield from asset_manager.get_audio_group_dependency(sound_id) - elif issubclass(field.type, int) and (field.default == 0xFFFFFFFF or 'asset_types' in field.metadata): + elif issubclass(field.type, int) and (field.default == 0xFFFFFFFF or "asset_types" in field.metadata): if self._is_property_mrea_or_mlvl(field): return asset_id: AssetId = getattr(self, field.name) diff --git a/tests/conftest.py b/tests/conftest.py index 39eaf5e..664b830 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -53,11 +53,9 @@ def prime3_asset_manager(prime3_iso_provider): def pytest_generate_tests(metafunc): fixture_names: list[str] = list(metafunc.fixturenames) - asset_manager_fixtures = [fixture for fixture in metafunc.fixturenames - if fixture.endswith("_asset_manager")] + asset_manager_fixtures = [fixture for fixture in metafunc.fixturenames if fixture.endswith("_asset_manager")] if asset_manager_fixtures: - asset_id_fixtures = [fixture_name for fixture_name in fixture_names - if fixture_name.endswith("_asset_id")] + asset_id_fixtures = [fixture_name for fixture_name in fixture_names if fixture_name.endswith("_asset_id")] if asset_id_fixtures: if len(asset_manager_fixtures) > 1: raise ValueError("Test has more than one asset_manager") @@ -71,7 +69,7 @@ def pytest_generate_tests(metafunc): raise RuntimeError(f"Unsupported {game_name} for id list") for fixture_name in asset_id_fixtures: - asset_type = fixture_name[:-len("_asset_id")] + asset_type = fixture_name[: -len("_asset_id")] asset_ids = [ pytest.param(asset.id, id=f"0x{asset.id:08x}") for asset in asset_ids @@ -82,10 +80,20 @@ def pytest_generate_tests(metafunc): def pytest_addoption(parser): - parser.addoption('--fail-if-missing', action='store_true', dest="fail_if_missing", - default=False, help="Fails tests instead of skipping, in case any asset is missing") - parser.addoption('--skip-dependency-tests', action='store_true', dest="skip_dependency_tests", - default=False, help="Skips tests that involves calculating dependencies") + parser.addoption( + "--fail-if-missing", + action="store_true", + dest="fail_if_missing", + default=False, + help="Fails tests instead of skipping, in case any asset is missing", + ) + parser.addoption( + "--skip-dependency-tests", + action="store_true", + dest="skip_dependency_tests", + default=False, + help="Skips tests that involves calculating dependencies", + ) def pytest_configure(config: pytest.Config): @@ -95,6 +103,6 @@ def pytest_configure(config: pytest.Config): markers = [] if config.option.skip_dependency_tests: - markers.append('not skip_dependency_tests') + markers.append("not skip_dependency_tests") config.option.markexpr = " and ".join(markers) diff --git a/tests/formats/test_ancs.py b/tests/formats/test_ancs.py index 3ed95a8..31941c8 100644 --- a/tests/formats/test_ancs.py +++ b/tests/formats/test_ancs.py @@ -8,14 +8,18 @@ def test_compare_p1(prime1_asset_manager): # Resources/Uncategorized/alpha_metaree.ANCS test_lib.parse_and_build_compare( - prime1_asset_manager, 0xBBEE2818, Ancs, + prime1_asset_manager, + 0xBBEE2818, + Ancs, ) def test_compare_p2(prime2_asset_manager): # Resources/Uncategorized/annihilatorBeam.ANCS test_lib.parse_and_build_compare( - prime2_asset_manager, 0x4C4B3D9D, Ancs, + prime2_asset_manager, + 0x4C4B3D9D, + Ancs, ) diff --git a/tests/formats/test_anim.py b/tests/formats/test_anim.py index caeb240..f188566 100644 --- a/tests/formats/test_anim.py +++ b/tests/formats/test_anim.py @@ -10,7 +10,9 @@ def test_compare_p2(prime2_asset_manager): # Resources/Uncategorized/01_gate_open.ANIM test_lib.parse_and_build_compare( - prime2_asset_manager, 0x101367C6, Anim, + prime2_asset_manager, + 0x101367C6, + Anim, ) @@ -57,6 +59,4 @@ def test_missile_launcher(prime1_asset_manager, prime2_asset_manager): def test_no_dependencies(prime2_asset_manager): result = list(prime2_asset_manager.get_dependencies_for_asset(0x5E2F550E)) - assert result == [ - Dependency(type='ANIM', id=0x5E2F550E) - ] + assert result == [Dependency(type="ANIM", id=0x5E2F550E)] diff --git a/tests/formats/test_cinf.py b/tests/formats/test_cinf.py index 6b28310..0a49984 100644 --- a/tests/formats/test_cinf.py +++ b/tests/formats/test_cinf.py @@ -10,12 +10,16 @@ def test_compare_p1(prime1_asset_manager): # Resources/Uncategorized/tickspin.CINF test_lib.parse_and_build_compare( - prime1_asset_manager, 0x9A0FAE84, Cinf, + prime1_asset_manager, + 0x9A0FAE84, + Cinf, ) def test_compare_p2(prime2_asset_manager): # Resources/Uncategorized/Swamplands_Luminoth_Hologram.CINF test_lib.parse_and_build_compare( - prime2_asset_manager, 0xD6BA53FA, Cinf, + prime2_asset_manager, + 0xD6BA53FA, + Cinf, ) diff --git a/tests/formats/test_cmdl.py b/tests/formats/test_cmdl.py index cf6bc6c..b39fd85 100644 --- a/tests/formats/test_cmdl.py +++ b/tests/formats/test_cmdl.py @@ -22,7 +22,7 @@ def chunks(lst, n): """Yield successive n-sized chunks from lst.""" for i in range(0, len(lst), n): - yield lst[i: i + n] + yield lst[i : i + n] def test_compare(prime2_asset_manager): @@ -44,10 +44,10 @@ def test_compare(prime2_asset_manager): def test_dependencies_p2(prime2_asset_manager): result = list(prime2_asset_manager.get_dependencies_for_asset(0x6FE2E8A0)) assert result == [ - Dependency(type='TXTR', id=326302585), - Dependency(type='TXTR', id=1583844215), - Dependency(type='TXTR', id=2998183659), - Dependency(type='TXTR', id=3392247412), - Dependency(type='TXTR', id=3714447378), - Dependency(type='CMDL', id=1877141664) + Dependency(type="TXTR", id=326302585), + Dependency(type="TXTR", id=1583844215), + Dependency(type="TXTR", id=2998183659), + Dependency(type="TXTR", id=3392247412), + Dependency(type="TXTR", id=3714447378), + Dependency(type="CMDL", id=1877141664), ] diff --git a/tests/formats/test_cskr.py b/tests/formats/test_cskr.py index 3e1918e..577fcd7 100644 --- a/tests/formats/test_cskr.py +++ b/tests/formats/test_cskr.py @@ -6,10 +6,13 @@ # Skin + def test_compare_p1(prime1_asset_manager): # Resources/NoARAM/Fusion.CSKR test_lib.parse_and_build_compare( - prime1_asset_manager, 0x627F684B, Cskr, + prime1_asset_manager, + 0x627F684B, + Cskr, ) @@ -17,5 +20,7 @@ def test_compare_p2(prime2_asset_manager): # Resources/SamusGunLow/Holo.CSKR test_lib.parse_and_build_compare( - prime2_asset_manager, 0xD9828657, Cskr, + prime2_asset_manager, + 0xD9828657, + Cskr, ) diff --git a/tests/formats/test_hier.py b/tests/formats/test_hier.py index c71d670..74d3d4a 100644 --- a/tests/formats/test_hier.py +++ b/tests/formats/test_hier.py @@ -9,5 +9,7 @@ def test_compare_p2(prime2_asset_manager): # Resources/NoARAM/DUMB_ScanHierarchy.DUMB test_lib.parse_and_build_compare( - prime2_asset_manager, 0xDD79DC2A, Hier, + prime2_asset_manager, + 0xDD79DC2A, + Hier, ) diff --git a/tests/formats/test_mlvl.py b/tests/formats/test_mlvl.py index ebd772a..c7f7581 100644 --- a/tests/formats/test_mlvl.py +++ b/tests/formats/test_mlvl.py @@ -26,40 +26,13 @@ _EXPECTED_DEPENDENCY = { "Great Temple": {}, "Torvus Bog": { - "Torvus Lagoon": { - "missing": {}, - "extra": { - "1st Pass": {("PART", "0x28747f78")} - } - }, - "Ruined Alcove": { - "missing": {}, - "extra": { - "1st Pass": {("PART", "0x28747f78")} - } - }, - "Undertemple": { - "missing": {}, - "extra": { - "Ingsporb battle": {("AGSC", "0xfa61924c")} - } - } - }, - "Sanctuary Fortress": { - "Entrance Defense Hall": { - "missing": {}, - "extra": { - "1st pass": {("AGSC", "0xc8739bec")} - } - } + "Torvus Lagoon": {"missing": {}, "extra": {"1st Pass": {("PART", "0x28747f78")}}}, + "Ruined Alcove": {"missing": {}, "extra": {"1st Pass": {("PART", "0x28747f78")}}}, + "Undertemple": {"missing": {}, "extra": {"Ingsporb battle": {("AGSC", "0xfa61924c")}}}, }, + "Sanctuary Fortress": {"Entrance Defense Hall": {"missing": {}, "extra": {"1st pass": {("AGSC", "0xc8739bec")}}}}, "Temple Grounds": { - "Hive Chamber A": { - "missing": {}, - "extra": { - "Default": {("TXTR", "0xbf916e5a")} - } - }, + "Hive Chamber A": {"missing": {}, "extra": {"Default": {("TXTR", "0xbf916e5a")}}}, "Trooper Security Station": { "missing": {}, "extra": { @@ -68,8 +41,8 @@ ("TXTR", "0x286d4e4a"), ("TXTR", "0x5a7f9d53"), } - } - } + }, + }, }, "Agon Wastes": { "Agon Temple": { @@ -79,9 +52,9 @@ ("CMDL", "0x792f1949"), ("CMDL", "0x952eae59"), } - } + }, } - } + }, } @@ -121,13 +94,11 @@ def test_mlvl_dependencies(prime2_asset_manager: AssetManager): missing = { layer_name: old_layer.difference(new_layer) - for (layer_name, old_layer), new_layer - in zip(old.items(), new.values()) + for (layer_name, old_layer), new_layer in zip(old.items(), new.values()) } extra = { layer_name: new_layer.difference(old_layer) - for (layer_name, old_layer), new_layer - in zip(old.items(), new.values()) + for (layer_name, old_layer), new_layer in zip(old.items(), new.values()) } missing = {n: miss for n, miss in missing.items() if miss} extra = {n: ext for n, ext in extra.items() if ext} @@ -143,9 +114,12 @@ def test_mlvl_dependencies(prime2_asset_manager: AssetManager): world_reports[mlvl.world_name][area.name] = {"missing": missing, "extra": extra} if write_reports: _write_to_file( - {"missing": {n: list(miss) for n, miss in missing.items() if miss}, - "extra": {n: list(ext) for n, ext in extra.items() if ext}}, - f) + { + "missing": {n: list(miss) for n, miss in missing.items() if miss}, + "extra": {n: list(ext) for n, ext in extra.items() if ext}, + }, + f, + ) else: logging.info(msg) if write_reports: @@ -160,9 +134,10 @@ def test_mlvl_dependencies(prime2_asset_manager: AssetManager): "Great Temple": {}, "Agon Wastes": {}, "Torvus Bog": {}, - "Sanctuary Fortress": {} + "Sanctuary Fortress": {}, } + @pytest.mark.skip_dependency_tests def test_module_dependencies(prime2_asset_manager: AssetManager): write_reports = os.environ.get("WRITE_DEPENDENCIES_REPORTS", "") != "" diff --git a/tests/formats/test_mrea.py b/tests/formats/test_mrea.py index 35b65a8..135d27c 100644 --- a/tests/formats/test_mrea.py +++ b/tests/formats/test_mrea.py @@ -18,11 +18,7 @@ def test_compare_p1(prime1_asset_manager): # are not multiples of 32; building always pads to 32 # Resources/Worlds/EndCinema/!EndCinema_Master/01_endcinema.MREA - test_lib.parse_and_build_compare( - prime1_asset_manager, - 0xB4B41C48, - Mrea - ) + test_lib.parse_and_build_compare(prime1_asset_manager, 0xB4B41C48, Mrea) def test_compare_p2(prime2_asset_manager, mrea_asset_id: AssetId): diff --git a/tests/formats/test_pak_gc.py b/tests/formats/test_pak_gc.py index df0cf46..5a30bc8 100644 --- a/tests/formats/test_pak_gc.py +++ b/tests/formats/test_pak_gc.py @@ -9,6 +9,7 @@ # ruff: noqa: E501 + @pytest.fixture(name="compressed_resource") def _compressed_resource(): return { @@ -16,21 +17,21 @@ def _compressed_resource(): "asset": {"type": "TXTR", "id": 1114209557}, "contents": { "data": b"\x00\x00\n\xcc\x01\xec\x12\x00a\x00@G\x00\x00\x00\x05j\x01\n\xaa\x02\x00\n\xcc\x00\x00-\x0c\x00m" - b"\x02\xa0|\x03Z\x03\xaa\xcc-\x00\x00m\x02\x00\xc1\x00\xaaU\x04\xcc-\x0f\x00\xaa\xa0\x00 *\x01\x00" - b"\nN\r\n\xcc-\x0c\x00(\xfc\x01>}\x02\xaa\x04#H\x02+\x0c\x00B\x06\xa0\n \x02\r\x00\x00 \x9e\x01\x00" - b"\n \x06\x9c\x03L@ 6\xad\x03\x0cN5\x0f\xff6\x0c\x00\x04\xcc\xcc0\x00\xff\xff@7\x0c\x00 (\xfe\x03" - b"\xaa\xaa V.\x07\x0f\xff:\x0c\x00:\xec\x03 #\xfc\x03 a\x00\x00 \x91\xfc\x03lE \r\xff\x0fDD\x00T" - b"\x154\x0e\x00\x0f\xff\x06\x07O\xff8\x00\x00~\x1ctD6t\x00\xb4\x0bm\x04\xf03\x0c\x00 \x1f|\x03 !" - b"\xfe\x13\x88\x88\x84\x01.\xbc\x05\x8c0\xdc1n3\x00U\x00\x00*\xd4\x00.<\x06Y\x03@<<\x06,\x84\x01l0(<" - b"\x06\x04wu\x8a\xaa\x80\x00\xabL\x00pEl\x00Qq\rY\x00\x0e'\x0f\x00\x08\xaa\xa8M\x00\xba\xda\x03" - b"\xaa\xf2H\x04M\x00\x00\xec\x00(\xaf\x00\x0c\xcf\x88L\x9d\x01\xaa\x00\x04D`\x05l\nl\x0b\xfe\x04" - b"\xfc\xc6M#\xf8\x0e\x0eDA\xc8\rl\nO\x0b\xaa`\x06`Q\x04\n`\t\xb0\x06\x00\nX%L\x00\x05`\xaf\xfb" - b"\x06\xa0\x12\"\nn\x03\x82(j\x0c'\x826\x14\x05\x11\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff" - b"\xff\xff\xff\xff\xff\xff", + b"\x02\xa0|\x03Z\x03\xaa\xcc-\x00\x00m\x02\x00\xc1\x00\xaaU\x04\xcc-\x0f\x00\xaa\xa0\x00 *\x01\x00" + b"\nN\r\n\xcc-\x0c\x00(\xfc\x01>}\x02\xaa\x04#H\x02+\x0c\x00B\x06\xa0\n \x02\r\x00\x00 \x9e\x01\x00" + b"\n \x06\x9c\x03L@ 6\xad\x03\x0cN5\x0f\xff6\x0c\x00\x04\xcc\xcc0\x00\xff\xff@7\x0c\x00 (\xfe\x03" + b"\xaa\xaa V.\x07\x0f\xff:\x0c\x00:\xec\x03 #\xfc\x03 a\x00\x00 \x91\xfc\x03lE \r\xff\x0fDD\x00T" + b"\x154\x0e\x00\x0f\xff\x06\x07O\xff8\x00\x00~\x1ctD6t\x00\xb4\x0bm\x04\xf03\x0c\x00 \x1f|\x03 !" + b"\xfe\x13\x88\x88\x84\x01.\xbc\x05\x8c0\xdc1n3\x00U\x00\x00*\xd4\x00.<\x06Y\x03@<<\x06,\x84\x01l0(<" + b"\x06\x04wu\x8a\xaa\x80\x00\xabL\x00pEl\x00Qq\rY\x00\x0e'\x0f\x00\x08\xaa\xa8M\x00\xba\xda\x03" + b"\xaa\xf2H\x04M\x00\x00\xec\x00(\xaf\x00\x0c\xcf\x88L\x9d\x01\xaa\x00\x04D`\x05l\nl\x0b\xfe\x04" + b"\xfc\xc6M#\xf8\x0e\x0eDA\xc8\rl\nO\x0b\xaa`\x06`Q\x04\n`\t\xb0\x06\x00\nX%L\x00\x05`\xaf\xfb" + b"\x06\xa0\x12\"\nn\x03\x82(j\x0c'\x826\x14\x05\x11\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff" + b"\xff\xff\xff\xff\xff\xff", "value": ( b"\x00\x00\x00\x00\x00@\x00@\x00\x00\x00\x05\x00\x00\x00\x00\n\xaa\xaa\xaa\n\xcc\xcc\xcc" b"\n\xcc\xcc\xcc\n\xcc\xcc\xcc\n\xcc\xcc\xcc\n\xcc\xcc\xaa\n\xcc\xcc\xa0\x00\x00\x00\x00" @@ -283,7 +284,7 @@ def test_compare_from_build(): b"\xaa\x00\x03D\xaa\x00\x00\x00\xab\xaa\x80\x00\x8a\xaa\x80\x00\xfe\x00\x00\x00C\x00\x00\x00\xcc\xc2\x00\x00\xff\xf4\x00\x88DA\x00\xaa\x00\x00\x00\xaa\x00\x08\xaa\xba" b'\x00\x08\xaa\xa8\xaa`\x06\xaa\xa0\x00\x00\n`\x9f\xeb\x06\x00\x9c\x80\x00\x00\x9e\xb0\x00`\x9f\xea\x06\xa0\x12"\n\xaa`\x06\xaa\x82(\x00\x00\x00\x00,\xa2' b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" - ) + ), ), PakFile( asset_id=239414538, @@ -319,7 +320,7 @@ def test_compare_from_build(): b"PPPPP\xff\x8f\x0f\xff\xff\xff\xff\xff\xff\x8f\x0f\x8f\x8f\x8f\x8f\x8f\x8fO\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x1fOOOOOO\x1fO\xde\xbb\xbb\xbb\xbb\xdeOO\xbb\x9a\xde" b"\xdew\xbbOO\xbb\xbb\x88PP\xbbOO\xbb\xbb\xee\xbcP\xbbOO\xbb\xbb\xee\xdew\xbbOO\xde\xbb\xbb\xbb\xbb\xdeO\x1fOOOOOO\x1fo~~o\x00\x00\x00\x00~\xab\x88~\x00\x00\x00\x00" b"~\xcd\x9a~\x00\x00\x00\x00o~~o\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff" - ) + ), ), PakFile( asset_id=564256465, @@ -345,7 +346,7 @@ def test_compare_from_build(): b"\x00\x00\x00\x00A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" b"\x00\x00\x00+\x00\x00\x08\xd8\x12j\xdb0\xdc\xb8 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x16\x98\x00\x00\x00\x00\x00\x00\x00\x00" b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff" - ) + ), ), PakFile( asset_id=568030977, @@ -420,7 +421,7 @@ def test_compare_from_build(): b"\xaa\x00\x00\x00\xab\xaa\x80\x00\x8a\xaa\x80\x00\xff\xe0\x00\x00D\x10\x00\x00\xcc\xa0\x00\x00\xff\xe0\x00\x88D0\x00\xaa\x00\x00\x00\xaa\x00\x08\xaa\xba" b"\x00\x08\xaa\xa8\xaa`\x06\xaa\xa0\x00\x00\n`\x8e\xfa\x06\x00\x18\xbc\x00\x00\x9e\xc7\x00`\x8f\xe8\x06\xa0\x02!\n\xaa`\x06\xaa\x82(\x00\x00\x00\x00)\xc2" b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\x00" - ) + ), ), PakFile( asset_id=638062812, @@ -495,9 +496,9 @@ def test_compare_from_build(): b"\xaa\x00\x00\x00\xab\xaa\x80\x00\x8a\xaa\x80\x00\xff\xe0\x00\x00D\x10\x00\x00\xcc\xa0\x00\x00\xff\xe0\x00\x88D0\x00\xaa\x00\x00\x00\xaa\x00\x08\xaa\xba" b"\x00\x08\xaa\xa8\xaa`\x06\xaa\xa0\x00\x00\n`\x8e\xfa\x06\x00\x18\xbc\x00\x00\x9e\xc7\x00`\x8f\xe8\x06\xa0\x02!\n\xaa`\x06\xaa\x82(\x00\x00\x00\x00)\xc2" b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\x00" - ) + ), ), - ] + ], ) result = PAK_GC.build(source, target_game=game) diff --git a/tests/formats/test_particle_script.py b/tests/formats/test_particle_script.py index b243f62..965cb44 100644 --- a/tests/formats/test_particle_script.py +++ b/tests/formats/test_particle_script.py @@ -34,7 +34,16 @@ def test_particles_srsc(prime2_asset_manager: AssetManager, srsc_asset_id): def test_particles_part(prime2_asset_manager: AssetManager, part_asset_id): - if part_asset_id in {0x851bee5c, 0xa40f7d8b, 0xe03127e6, 0x4fb5d427, - 0x324cbebf, 0xac4863f1, 0x5fe8f8ca, 0x5e41c887, 0xc7a3ae86}: + if part_asset_id in { + 0x851BEE5C, + 0xA40F7D8B, + 0xE03127E6, + 0x4FB5D427, + 0x324CBEBF, + 0xAC4863F1, + 0x5FE8F8CA, + 0x5E41C887, + 0xC7A3AE86, + }: pytest.xfail() test_lib.parse_and_build_compare_auto_manager(prime2_asset_manager, part_asset_id) diff --git a/tests/formats/test_scan.py b/tests/formats/test_scan.py index edd0254..7df4fc9 100644 --- a/tests/formats/test_scan.py +++ b/tests/formats/test_scan.py @@ -9,14 +9,18 @@ def test_compare_p1(prime1_asset_manager): # Resources/Uncategorized/Chozo Lore 002.SCAN test_lib.parse_and_build_compare( - prime1_asset_manager, 0x60AA2A56, Scan, + prime1_asset_manager, + 0x60AA2A56, + Scan, ) def test_compare_p2(prime2_asset_manager): # Resources/Uncategorized/Brizgee_0.SCAN test_lib.parse_and_build_compare( - prime2_asset_manager, 0x62572238, Scan, + prime2_asset_manager, + 0x62572238, + Scan, ) @@ -24,5 +28,7 @@ def test_compare_p2(prime2_asset_manager): def test_compare_p3(prime3_asset_manager): # Resources/uncategorized/Your PED Suit will allow you to absorb this Phazon.SCAN test_lib.parse_and_build_compare( - prime3_asset_manager, 0x5258014E053BFD2C, Scan, + prime3_asset_manager, + 0x5258014E053BFD2C, + Scan, ) diff --git a/tests/formats/test_script_api.py b/tests/formats/test_script_api.py index d8a5142..13bc265 100644 --- a/tests/formats/test_script_api.py +++ b/tests/formats/test_script_api.py @@ -13,24 +13,30 @@ from retro_data_structures.formats.mlvl import Area, Mlvl -@pytest.mark.parametrize(["layer", "area", "instance", "expected"], [ - (0, 0, 0, 0x00000000), - (1, 0, 0, 0x04000000), - (0, 1, 0, 0x00010000), - (0, 0, 1, 0x00000001), - (5, 2, 1, 0x14020001), -]) +@pytest.mark.parametrize( + ["layer", "area", "instance", "expected"], + [ + (0, 0, 0, 0x00000000), + (1, 0, 0, 0x04000000), + (0, 1, 0, 0x00010000), + (0, 0, 1, 0x00000001), + (5, 2, 1, 0x14020001), + ], +) def test_instance_id_new(layer, area, instance, expected): assert InstanceId.new(layer, area, instance) == expected -@pytest.mark.parametrize(["correct_type", "value", "expected"], [ - (_prime_enums.State, _prime_enums.State.Exited, _prime_enums.State.Exited), - (_prime_enums.State, _echoes_enums.State.Exited, _prime_enums.State.Exited), - (_echoes_enums.State, _prime_enums.State.Exited, _echoes_enums.State.Exited), - (_echoes_enums.State, 'EXIT', _echoes_enums.State.Exited), - (_echoes_enums.State, _echoes_enums.State.Exited, _echoes_enums.State.Exited), -]) +@pytest.mark.parametrize( + ["correct_type", "value", "expected"], + [ + (_prime_enums.State, _prime_enums.State.Exited, _prime_enums.State.Exited), + (_prime_enums.State, _echoes_enums.State.Exited, _prime_enums.State.Exited), + (_echoes_enums.State, _prime_enums.State.Exited, _echoes_enums.State.Exited), + (_echoes_enums.State, "EXIT", _echoes_enums.State.Exited), + (_echoes_enums.State, _echoes_enums.State.Exited, _echoes_enums.State.Exited), + ], +) def test_resolve_to_enum(correct_type, value, expected): assert script_object._resolve_to_enum(correct_type, value) == expected @@ -38,7 +44,8 @@ def test_resolve_to_enum(correct_type, value, expected): @pytest.fixture def prime2_mlvl(prime2_asset_manager) -> Mlvl: # Agon Wastes - return prime2_asset_manager.get_parsed_asset(0x42b935e4) + return prime2_asset_manager.get_parsed_asset(0x42B935E4) + @pytest.fixture def prime2_area(prime2_mlvl: Mlvl) -> Area: @@ -54,6 +61,7 @@ def test_add_layer(prime2_area: Area, active: bool): assert layer.active == active assert layer.index == 2 + def test_get_instance(prime2_area: Area): idx, name = 0x0045006B, "Pickup Object" inst = prime2_area.get_instance(idx) @@ -62,6 +70,7 @@ def test_get_instance(prime2_area: Area): inst = prime2_area.get_instance(name) assert inst.id == idx + def test_remove_instance(prime2_area: Area): old_len = len(list(prime2_area.all_instances)) prime2_area.remove_instance("Pickup Object") @@ -73,18 +82,22 @@ def test_add_instance(prime2_area: Area): from retro_data_structures.enums import echoes from retro_data_structures.properties.echoes.objects.SpecialFunction import SpecialFunction - inst = prime2_area.get_layer("Default").add_instance_with(SpecialFunction( - function=echoes.Function.Darkworld, - )) + inst = prime2_area.get_layer("Default").add_instance_with( + SpecialFunction( + function=echoes.Function.Darkworld, + ) + ) assert inst.type == SpecialFunction assert prime2_area.mrea.build() is not None + def test_add_memory_relay(prime2_area: Area): relay = prime2_area.get_layer("Default").add_memory_relay("Test") save = prime2_area._parent_mlvl.savw assert any(state["instance_id"] == relay.id for state in save.raw.memory_relays) + @pytest.mark.parametrize("name", ("Test1", "Test2")) @pytest.mark.parametrize("active", (False, True)) def test_edit_layer(prime2_area: Area, name: str, active: bool): diff --git a/tests/formats/test_strg.py b/tests/formats/test_strg.py index ae6bc5b..8547844 100644 --- a/tests/formats/test_strg.py +++ b/tests/formats/test_strg.py @@ -13,13 +13,17 @@ def test_compare_p1(prime1_asset_manager, strg_asset_id: AssetId): test_lib.parse_and_build_compare( - prime1_asset_manager, strg_asset_id, Strg, + prime1_asset_manager, + strg_asset_id, + Strg, ) def test_compare_p2(prime2_asset_manager, strg_asset_id: AssetId): test_lib.parse_and_build_compare( - prime2_asset_manager, strg_asset_id, Strg, + prime2_asset_manager, + strg_asset_id, + Strg, ) @@ -28,17 +32,23 @@ def test_compare_p3(prime3_asset_manager): # with name table # Resources/strings/metroid3/gui/fesliderpopup.STRG test_lib.parse_and_build_compare( - prime3_asset_manager, 0x0D53311DE8B26040, Strg, + prime3_asset_manager, + 0x0D53311DE8B26040, + Strg, ) # without name table # Resources/strings/uncategorized/Action.STRG test_lib.parse_and_build_compare( - prime3_asset_manager, 0x8A3242A997AAEDE7, Strg, + prime3_asset_manager, + 0x8A3242A997AAEDE7, + Strg, ) # echoes format # Resources/strings/metroid2/ingame/languageselection.STRG test_lib.parse_and_build_compare( - prime3_asset_manager, 0x08417493AF6B57E2, Strg, + prime3_asset_manager, + 0x08417493AF6B57E2, + Strg, ) diff --git a/tests/formats/test_tree.py b/tests/formats/test_tree.py index 93924f6..872e467 100644 --- a/tests/formats/test_tree.py +++ b/tests/formats/test_tree.py @@ -8,5 +8,7 @@ def test_compare_p2(prime2_asset_manager): # Resources/Logbook/DUMB_ScanTree.DUMB test_lib.parse_and_build_compare_construct( - prime2_asset_manager, 0x95B61279, TREE, + prime2_asset_manager, + 0x95B61279, + TREE, ) diff --git a/tests/test_compression.py b/tests/test_compression.py index 7b354e0..2dff3aa 100644 --- a/tests/test_compression.py +++ b/tests/test_compression.py @@ -8,9 +8,7 @@ def test_build_parse_lzo(): s = Struct( - header=Prefixed(VarInt, GreedyBytes), - length=VarInt, - body=compression.LZOCompressedBlock(construct.this.length) + header=Prefixed(VarInt, GreedyBytes), length=VarInt, body=compression.LZOCompressedBlock(construct.this.length) ) data = { @@ -23,4 +21,3 @@ def test_build_parse_lzo(): decoded = s.parse(encoded) assert data == decoded - diff --git a/tests/test_lib.py b/tests/test_lib.py index 1f68da6..b8845c6 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -24,10 +24,7 @@ def _parse_assets_file(name: str) -> list[TypedAsset]: with Path(__file__).parent.joinpath("test_files", name).open() as f: data = json.load(f) - return [ - TypedAsset(item["id"], item["type"]) - for item in data - ] + return [TypedAsset(item["id"], item["type"]) for item in data] PRIME_ASSET_IDS = _parse_assets_file("assets_prime.json") @@ -62,8 +59,8 @@ def parse_and_build_compare_auto_manager(asset_manager: AssetManager, asset_id: def parse_and_build_compare( - asset_manager: AssetManager, asset_id: AssetId, resource_class: type[BaseResource], - print_data=False) -> tuple[RawResource, BaseResource, bytes]: + asset_manager: AssetManager, asset_id: AssetId, resource_class: type[BaseResource], print_data=False +) -> tuple[RawResource, BaseResource, bytes]: resource = asset_manager.get_raw_asset(asset_id) assert resource.type == resource_class.resource_type() @@ -80,8 +77,8 @@ def parse_and_build_compare( def parse_and_build_compare_construct( - asset_manager: AssetManager, asset_id: AssetId, con: construct.Construct, - print_data=False) -> tuple[RawResource, BaseResource, bytes]: + asset_manager: AssetManager, asset_id: AssetId, con: construct.Construct, print_data=False +) -> tuple[RawResource, BaseResource, bytes]: resource = asset_manager.get_raw_asset(asset_id) decoded = con.parse(resource.data, target_game=asset_manager.target_game) diff --git a/tests/test_properties.py b/tests/test_properties.py index 58b021f..b4698f5 100644 --- a/tests/test_properties.py +++ b/tests/test_properties.py @@ -66,67 +66,69 @@ def test_import_and_create_prime_remastered(path: Path): def test_door(): - data = (b'\xFF\xFF\xFF\xFF\x06\x9a\x00\x16%ZE\x80\x00H\x00\x04INAM\x00\x05Door\x00XFRM\x00$\xc1\xb6' - b'\xad\xc9\xc3BwZ\xc26\x9e\xcb\x00\x00\x00\x00\x00\x00\x00\x00C4\x00\x00?\x80\x00\x00?\x80\x00' - b'\x00?\x80\x00\x00ACTV\x00\x01\x01])\x8aC\x00\x04\x00\x00\x00\x03\xf3D\xc0\xb0\x00\x0c>\xb333@' - b'\xa0\x00\x00@\x80\x00\x00.hl*\x00\x0c\xbe333\x00\x00\x00\x00@\x00\x00\x00\xcf\x90\xd1^\x00' - b'\x16\x00\x02\xf0f\x89\x19\x00\x04?\x80\x00\x00:-\x17\xe4\x00\x04?\x80\x00\x00{q\xae\x90\x03' - b'\xf9\x00\x1d\xac\x8b\xb2\xa7\x00\x1d\x00\x03\x85-8q\x00\x04B\xc8\x00\x00h\xac\xbd\x86\x00\x04' - b'\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01\xb1\x9bml\x00\x1d\x00\x03\x85-8q\x00\x04B\xc8\x00\x00h' - b'\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01\x00$D\xc6\x00\x1d\x00\x03\x85-8q' - b'\x00\x04B\xc8\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01\xe6\xba\xcd' - b'\xe5\x00\x1d\x00\x03\x85-8q\x00\x04B\xc8\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}' - b'\xa2\x00\x01\x01\xd9\x9c\x04\x00\x00\x1d\x00\x03\x85-8q\x00\x04\x00\x00\x00\x00h\xac\xbd\x86' - b'\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01\xc7\r\x8b\x8b\x00\x1d\x00\x03\x85-8q\x00\x04' - b'\x00\x00\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01\x86o\x91\xbf\x00' - b'\x1d\x00\x03\x85-8q\x00\x04\x00\x00\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2' - b'\x00\x01\x01h\xae\x13\xa0\x00\x1d\x00\x03\x85-8q\x00\x04B\xc8\x00\x00h\xac\xbd\x86\x00\x04' - b'\x00\x00\x00\x00\x93g}\xa2\x00\x01\x00\xf0\xb2\xf4\xcf\x00\x1d\x00\x03\x85-8q\x00\x04B\xc8' - b'\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x00\x00\xef\xe8\xcb\x00\x1d' - b'\x00\x03\x85-8q\x00\x04B\xc8\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01' - b'\x01\xbbp\t?\x00\x1d\x00\x03\x85-8q\x00\x04\x00\x00\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00' - b'\x01\x93g}\xa2\x00\x01\x01\xbf\xac"\x9f\x00\x1d\x00\x03\x85-8q\x00\x04\x00\x00\x00\x00h\xac' - b'\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01P\xdd\xe8\x91\x00\x1d\x00\x03\x85-8q' - b'\x00\x04\x00\x00\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01\x97\xb5' - b'\xc6\x8b\x00\x1d\x00\x03\x85-8q\x00\x04\x00\x00\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00' - b'\x93g}\xa2\x00\x01\x01\xf4\xce\x8a\xcf\x00\x1d\x00\x03\x85-8q\x00\x04\x00\x00\x00\x00h\xac' - b'\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01\x1dRX\x8d\x00\x1d\x00\x03\x85-8q\x00' - b'\x04\x00\x00\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01d\xdd\xd5C' - b'\x00\x1d\x00\x03\x85-8q\x00\x04\x00\x00\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}' - b'\xa2\x00\x01\x01=b_\x10\x00\x1d\x00\x03\x85-8q\x00\x04\x00\x00\x00\x00h\xac\xbd\x86\x00\x04' - b'\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01Y\x1b\x80\xe3\x00\x1d\x00\x03\x85-8q\x00\x04\x00\x00' - b'\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01\xa4B\xb4\xa3\x00\x1d\x00' - b'\x03\x85-8q\x00\x04B\xc8\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01*' - b'\x0f\x05$\x00\x1d\x00\x03\x85-8q\x00\x04B\xc8\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00' - b'\x93g}\xa2\x00\x01\x01\x94\x9e<\x86\x00\x1d\x00\x03\x85-8q\x00\x04B\xc8\x00\x00h\xac\xbd\x86' - b'\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01[\xa9\x91\xb1\x00\x1d\x00\x03\x85-8q\x00\x04B' - b'\xc8\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01i$\xb8\xc1\x00\x1d' - b'\x00\x03\x85-8q\x00\x04B\xc8\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01' - b'\x01\x1a\xb7\x8b\xef\x00\x1d\x00\x03\x85-8q\x00\x04B\xc8\x00\x00h\xac\xbd\x86\x00\x04\x00\x00' - b'\x00\x00\x93g}\xa2\x00\x01\x01\xfc\xdc\xb7\xd4\x00\x1d\x00\x03\x85-8q\x00\x04B\xc8\x00\x00h\xac' - b'\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x011\x1f\xb0a\x00\x1d\x00\x03\x85-8q\x00\x04B' - b'\xc8\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01\x89\xd2]\xf4\x00\x1d\x00' - b'\x03\x85-8q\x00\x04\x00\x00\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x013V' - b'\xd3~\x00\x1d\x00\x03\x85-8q\x00\x04\x00\x00\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}' - b'\xa2\x00\x01\x01\xe2_\xb0\x8c\x00\x0c\xce\xb0\x91\xf7\x00\x00\x00\x00\x00\x00\x00\x03\xb2\x0c\xc2q' - b'\x00\x04kx\xfd\x92\xae[!\x14\x00\x04kx\xfd\x92G\xb4\xe8c\x00\x10\x00\x00\x00\x00?\x80\x00\x00?\x80' - b'\x00\x00?\x80\x00\x00%\x89\xc3\xf0\x00\x04\x98\xcb\xbf\xb8~9\x7f\xed\x01M\x00\x11\xb0(\xdb\x0e\x00' - b'\xa0\x00\x0f\xcdc\x9bF\x00\x01\x01\x1d\x01\x1a9\x00\x04?\x80\x00\x00\xec\xdaAc\x00\x04\x00\x00\x00' - b'\x00>,\xd3\x8d\x00\x04?\x80\x00\x00\x03(\xe3\x1b\x00\x04A\xa0\x00\x00\xa3>[\x0e\x00\x10?\x80\x00\x00?' - b'\x80\x00\x00?\x80\x00\x00?\x80\x00\x00\xa7\x18\x10\xe9\x00\x01\x01k^u\t\x00\x04\x00\x00\x00\x01b' - b'\x8ej\xc3\x00\x04\x00\x00\x00\x01\xd1\x9d\xe7u\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\xca\xc1\xe7x\x00\x04\x00\x00\x00\x04g\xf4\xd3\xde\x00\x04\x00\x00\x00\x04\xfbzz\xbb\x00' - b'\x01\x01a\xa9@\xd6\x00\x01\x00\x1fq_\xd3\x00\x04\x00\x00\x00\x007[\xfd|\x00\x0c\x00\x01\xb9N\x9b' - b'\xe7\x00\x04\x18f?9\xc0\xba\x9e\x18\x00\x04\xff\xff\xff\xff\x9f\x02}\x91\x00\x04\xff\xff\xff\xffk' - b'\x1f\xbc:\x00\x04\xff\xff\xff\xff\xeb\x1d\x06\xbe\x00\x04\xff\xff\xff\xff\x14\x99\x80<\x00\x01\x01' - b'\x90\xaa4\x1f\x00\x04?\x80\x00\x00|&\x9e\xbc\x00\x04?\x80\x00\x00\x05\xad%\x0e\x00\x13\x00\x02' - b'\xfe\x9d\xc2f\x00\x01\x00\xca\x19\xe8\xc6\x00\x04\x00\x00\x00\x0f\xcdL\x81\xa1\x00\x01\x00y\x92c' - b'\xf1\x00\x01\x00\xed:n\x87\x00\x01\x01\xf0y\x81\xe8\x00\x01\x00m\xf38E\x00\x01\x00\xc7\x12\x84|' - b'\x00\x04\x00\x00\x00\x7f\xba&\x00\xd7\x00\x04\x00\x00\x00\x7f\x85\x01\x15\xe4\x00\x0c\x00\x00\x00' - b'\x00\x00\x00\x00\x00@ \x00\x00\xa1\xdf\xfa\xd2\x00\x01\x00\xde\xe70\xf5\x00\x01\x00 \x07\xb7\x1d' - b'\x00\x04?\x00\x00\x00\xf1\xa5\r)\x00\x04?\x00\x00\x00\x06\xdc\xf1\x18\x00\x04>\x80\x00\x00]\xcf\nd' - b'\x00\x04?\x00\x00\x00\xcd\xcaY+\x00\x04?\x00\x00\x00\xcc\x00\x9f5\x00\x01\x00\xc2\x97e\xea\x00' - b'\x01\x00\x9e\xc6\'\x12\x00\x0c\x00\x01\xb9N\x9b\xe7\x00\x04\xff\xff\xff\xff') + data = ( + b"\xFF\xFF\xFF\xFF\x06\x9a\x00\x16%ZE\x80\x00H\x00\x04INAM\x00\x05Door\x00XFRM\x00$\xc1\xb6" + b"\xad\xc9\xc3BwZ\xc26\x9e\xcb\x00\x00\x00\x00\x00\x00\x00\x00C4\x00\x00?\x80\x00\x00?\x80\x00" + b"\x00?\x80\x00\x00ACTV\x00\x01\x01])\x8aC\x00\x04\x00\x00\x00\x03\xf3D\xc0\xb0\x00\x0c>\xb333@" + b"\xa0\x00\x00@\x80\x00\x00.hl*\x00\x0c\xbe333\x00\x00\x00\x00@\x00\x00\x00\xcf\x90\xd1^\x00" + b"\x16\x00\x02\xf0f\x89\x19\x00\x04?\x80\x00\x00:-\x17\xe4\x00\x04?\x80\x00\x00{q\xae\x90\x03" + b"\xf9\x00\x1d\xac\x8b\xb2\xa7\x00\x1d\x00\x03\x85-8q\x00\x04B\xc8\x00\x00h\xac\xbd\x86\x00\x04" + b"\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01\xb1\x9bml\x00\x1d\x00\x03\x85-8q\x00\x04B\xc8\x00\x00h" + b"\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01\x00$D\xc6\x00\x1d\x00\x03\x85-8q" + b"\x00\x04B\xc8\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01\xe6\xba\xcd" + b"\xe5\x00\x1d\x00\x03\x85-8q\x00\x04B\xc8\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}" + b"\xa2\x00\x01\x01\xd9\x9c\x04\x00\x00\x1d\x00\x03\x85-8q\x00\x04\x00\x00\x00\x00h\xac\xbd\x86" + b"\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01\xc7\r\x8b\x8b\x00\x1d\x00\x03\x85-8q\x00\x04" + b"\x00\x00\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01\x86o\x91\xbf\x00" + b"\x1d\x00\x03\x85-8q\x00\x04\x00\x00\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2" + b"\x00\x01\x01h\xae\x13\xa0\x00\x1d\x00\x03\x85-8q\x00\x04B\xc8\x00\x00h\xac\xbd\x86\x00\x04" + b"\x00\x00\x00\x00\x93g}\xa2\x00\x01\x00\xf0\xb2\xf4\xcf\x00\x1d\x00\x03\x85-8q\x00\x04B\xc8" + b"\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x00\x00\xef\xe8\xcb\x00\x1d" + b"\x00\x03\x85-8q\x00\x04B\xc8\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01" + b"\x01\xbbp\t?\x00\x1d\x00\x03\x85-8q\x00\x04\x00\x00\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00" + b'\x01\x93g}\xa2\x00\x01\x01\xbf\xac"\x9f\x00\x1d\x00\x03\x85-8q\x00\x04\x00\x00\x00\x00h\xac' + b"\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01P\xdd\xe8\x91\x00\x1d\x00\x03\x85-8q" + b"\x00\x04\x00\x00\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01\x97\xb5" + b"\xc6\x8b\x00\x1d\x00\x03\x85-8q\x00\x04\x00\x00\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00" + b"\x93g}\xa2\x00\x01\x01\xf4\xce\x8a\xcf\x00\x1d\x00\x03\x85-8q\x00\x04\x00\x00\x00\x00h\xac" + b"\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01\x1dRX\x8d\x00\x1d\x00\x03\x85-8q\x00" + b"\x04\x00\x00\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01d\xdd\xd5C" + b"\x00\x1d\x00\x03\x85-8q\x00\x04\x00\x00\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}" + b"\xa2\x00\x01\x01=b_\x10\x00\x1d\x00\x03\x85-8q\x00\x04\x00\x00\x00\x00h\xac\xbd\x86\x00\x04" + b"\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01Y\x1b\x80\xe3\x00\x1d\x00\x03\x85-8q\x00\x04\x00\x00" + b"\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01\xa4B\xb4\xa3\x00\x1d\x00" + b"\x03\x85-8q\x00\x04B\xc8\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01*" + b"\x0f\x05$\x00\x1d\x00\x03\x85-8q\x00\x04B\xc8\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00" + b"\x93g}\xa2\x00\x01\x01\x94\x9e<\x86\x00\x1d\x00\x03\x85-8q\x00\x04B\xc8\x00\x00h\xac\xbd\x86" + b"\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01[\xa9\x91\xb1\x00\x1d\x00\x03\x85-8q\x00\x04B" + b"\xc8\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01i$\xb8\xc1\x00\x1d" + b"\x00\x03\x85-8q\x00\x04B\xc8\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01" + b"\x01\x1a\xb7\x8b\xef\x00\x1d\x00\x03\x85-8q\x00\x04B\xc8\x00\x00h\xac\xbd\x86\x00\x04\x00\x00" + b"\x00\x00\x93g}\xa2\x00\x01\x01\xfc\xdc\xb7\xd4\x00\x1d\x00\x03\x85-8q\x00\x04B\xc8\x00\x00h\xac" + b"\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x011\x1f\xb0a\x00\x1d\x00\x03\x85-8q\x00\x04B" + b"\xc8\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x01\x89\xd2]\xf4\x00\x1d\x00" + b"\x03\x85-8q\x00\x04\x00\x00\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}\xa2\x00\x01\x013V" + b"\xd3~\x00\x1d\x00\x03\x85-8q\x00\x04\x00\x00\x00\x00h\xac\xbd\x86\x00\x04\x00\x00\x00\x00\x93g}" + b"\xa2\x00\x01\x01\xe2_\xb0\x8c\x00\x0c\xce\xb0\x91\xf7\x00\x00\x00\x00\x00\x00\x00\x03\xb2\x0c\xc2q" + b"\x00\x04kx\xfd\x92\xae[!\x14\x00\x04kx\xfd\x92G\xb4\xe8c\x00\x10\x00\x00\x00\x00?\x80\x00\x00?\x80" + b"\x00\x00?\x80\x00\x00%\x89\xc3\xf0\x00\x04\x98\xcb\xbf\xb8~9\x7f\xed\x01M\x00\x11\xb0(\xdb\x0e\x00" + b"\xa0\x00\x0f\xcdc\x9bF\x00\x01\x01\x1d\x01\x1a9\x00\x04?\x80\x00\x00\xec\xdaAc\x00\x04\x00\x00\x00" + b"\x00>,\xd3\x8d\x00\x04?\x80\x00\x00\x03(\xe3\x1b\x00\x04A\xa0\x00\x00\xa3>[\x0e\x00\x10?\x80\x00\x00?" + b"\x80\x00\x00?\x80\x00\x00?\x80\x00\x00\xa7\x18\x10\xe9\x00\x01\x01k^u\t\x00\x04\x00\x00\x00\x01b" + b"\x8ej\xc3\x00\x04\x00\x00\x00\x01\xd1\x9d\xe7u\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\xca\xc1\xe7x\x00\x04\x00\x00\x00\x04g\xf4\xd3\xde\x00\x04\x00\x00\x00\x04\xfbzz\xbb\x00" + b"\x01\x01a\xa9@\xd6\x00\x01\x00\x1fq_\xd3\x00\x04\x00\x00\x00\x007[\xfd|\x00\x0c\x00\x01\xb9N\x9b" + b"\xe7\x00\x04\x18f?9\xc0\xba\x9e\x18\x00\x04\xff\xff\xff\xff\x9f\x02}\x91\x00\x04\xff\xff\xff\xffk" + b"\x1f\xbc:\x00\x04\xff\xff\xff\xff\xeb\x1d\x06\xbe\x00\x04\xff\xff\xff\xff\x14\x99\x80<\x00\x01\x01" + b"\x90\xaa4\x1f\x00\x04?\x80\x00\x00|&\x9e\xbc\x00\x04?\x80\x00\x00\x05\xad%\x0e\x00\x13\x00\x02" + b"\xfe\x9d\xc2f\x00\x01\x00\xca\x19\xe8\xc6\x00\x04\x00\x00\x00\x0f\xcdL\x81\xa1\x00\x01\x00y\x92c" + b"\xf1\x00\x01\x00\xed:n\x87\x00\x01\x01\xf0y\x81\xe8\x00\x01\x00m\xf38E\x00\x01\x00\xc7\x12\x84|" + b"\x00\x04\x00\x00\x00\x7f\xba&\x00\xd7\x00\x04\x00\x00\x00\x7f\x85\x01\x15\xe4\x00\x0c\x00\x00\x00" + b"\x00\x00\x00\x00\x00@ \x00\x00\xa1\xdf\xfa\xd2\x00\x01\x00\xde\xe70\xf5\x00\x01\x00 \x07\xb7\x1d" + b"\x00\x04?\x00\x00\x00\xf1\xa5\r)\x00\x04?\x00\x00\x00\x06\xdc\xf1\x18\x00\x04>\x80\x00\x00]\xcf\nd" + b"\x00\x04?\x00\x00\x00\xcd\xcaY+\x00\x04?\x00\x00\x00\xcc\x00\x9f5\x00\x01\x00\xc2\x97e\xea\x00" + b"\x01\x00\x9e\xc6'\x12\x00\x0c\x00\x01\xb9N\x9b\xe7\x00\x04\xff\xff\xff\xff" + ) from retro_data_structures.enums.echoes import Effect from retro_data_structures.properties.echoes.archetypes.WeaponVulnerability import WeaponVulnerability @@ -142,19 +144,20 @@ def test_door(): def test_p1r_world_teleporter(): data = ( - b'\x08\x00\x7fQ\x9a\xd4\x10\x00\xa2a@\x91z\xc1&H\x94\xf5\xbeO\xaf\x90\xca\xe7l\xfb_\x9a\x10\x00C' - b'\x13\xf2\x92i\x03\xd3B\x90\xca\x989g\x10r^r\xee\xeaj4\x00\x03\x00\x85\xd8\x89\xa5\x10\x00\x00' - b'\x00\x00\x10\x00\x00\x00\xf0\xf0\x00\x00\x00w(\x9aJ\xf0\xc0\xf0\xd6\x08\x00@\x00\x00\x00\t\x00' - b'\x00\x00\x01:\xc0\x87\x08\x00J\x00\x00\x00\x12\x00\x00\x00yfS\xc2 \x00\x03\x00Q\xe5I&\x04\x00' - b'\x00\x00\x00@\xc6[\xbb\xd2\x04\x00\x00\x00\x00@\xb2\x99\x94\x7f\x04\x00\x00\x00\x00@\x8e\x1f' - b'\xb3\xa7\x10\x00\x00\x00\x00\x10\x00\x00\x00\xf0\xf0\x00\x00\x00o\xa5a\xd0!\xe8\xb5O \x00\x03' - b'\x00Q\xe5I&\x04\x00\x00\x00\x80?\xc6[\xbb\xd2\x04\x00\x00\x00\x80?\xb2\x99\x94\x7f\x04\x00\x00' - b'\x00\x80?*\xe6:\xbb \x00\x03\x00Q\xe5I&\x04\x00\x00\x00\x80?\xc6[\xbb\xd2\x04\x00\x00\x00\x80?' - b'\xb2\x99\x94\x7f\x04\x00\x00\x00\x80?#\xbb\x07T\x10\x00\x00\x00\x00\x10\x1a\x00 \xfd\xf0\x00' - b'\x00\x00\x00\x00\x11\x89' + b"\x08\x00\x7fQ\x9a\xd4\x10\x00\xa2a@\x91z\xc1&H\x94\xf5\xbeO\xaf\x90\xca\xe7l\xfb_\x9a\x10\x00C" + b"\x13\xf2\x92i\x03\xd3B\x90\xca\x989g\x10r^r\xee\xeaj4\x00\x03\x00\x85\xd8\x89\xa5\x10\x00\x00" + b"\x00\x00\x10\x00\x00\x00\xf0\xf0\x00\x00\x00w(\x9aJ\xf0\xc0\xf0\xd6\x08\x00@\x00\x00\x00\t\x00" + b"\x00\x00\x01:\xc0\x87\x08\x00J\x00\x00\x00\x12\x00\x00\x00yfS\xc2 \x00\x03\x00Q\xe5I&\x04\x00" + b"\x00\x00\x00@\xc6[\xbb\xd2\x04\x00\x00\x00\x00@\xb2\x99\x94\x7f\x04\x00\x00\x00\x00@\x8e\x1f" + b"\xb3\xa7\x10\x00\x00\x00\x00\x10\x00\x00\x00\xf0\xf0\x00\x00\x00o\xa5a\xd0!\xe8\xb5O \x00\x03" + b"\x00Q\xe5I&\x04\x00\x00\x00\x80?\xc6[\xbb\xd2\x04\x00\x00\x00\x80?\xb2\x99\x94\x7f\x04\x00\x00" + b"\x00\x80?*\xe6:\xbb \x00\x03\x00Q\xe5I&\x04\x00\x00\x00\x80?\xc6[\xbb\xd2\x04\x00\x00\x00\x80?" + b"\xb2\x99\x94\x7f\x04\x00\x00\x00\x80?#\xbb\x07T\x10\x00\x00\x00\x00\x10\x1a\x00 \xfd\xf0\x00" + b"\x00\x00\x00\x00\x11\x89" ) from retro_data_structures.properties.prime_remastered.objects import WorldTeleporterTooMP1 + teleporter = WorldTeleporterTooMP1.from_bytes(data) encoded = teleporter.to_bytes() @@ -162,4 +165,3 @@ def test_p1r_world_teleporter(): assert teleporter == teleporter2 assert encoded == data - diff --git a/tools/compare_paks.py b/tools/compare_paks.py index c5e6e53..f52746f 100644 --- a/tools/compare_paks.py +++ b/tools/compare_paks.py @@ -121,5 +121,5 @@ def main(): print("I'm DONE") -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/tools/create_file_list.py b/tools/create_file_list.py index 56716ef..9256b68 100644 --- a/tools/create_file_list.py +++ b/tools/create_file_list.py @@ -17,14 +17,11 @@ def main(): manager = AssetManager(IsoFileProvider(args.path), target_game=game) - result = [ - {"id": asset_id, "type": manager.get_asset_type(asset_id)} - for asset_id in manager.all_asset_ids() - ] + result = [{"id": asset_id, "type": manager.get_asset_type(asset_id)} for asset_id in manager.all_asset_ids()] Path(__file__).parents[1].joinpath("test", "test_files", f"assets_{game.name.lower()}.json").write_text( json.dumps(result, indent=4) ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/tools/profile_properties.py b/tools/profile_properties.py index fa0b6b7..a0df410 100644 --- a/tools/profile_properties.py +++ b/tools/profile_properties.py @@ -16,11 +16,14 @@ SerializedData = construct.Struct( game=construct.VarInt, - header=construct.PrefixedArray(construct.VarInt, construct.Struct( - identifier=construct.PascalString(construct.VarInt, "utf-8"), - instance_id=construct.VarInt, - size=construct.VarInt, - )), + header=construct.PrefixedArray( + construct.VarInt, + construct.Struct( + identifier=construct.PascalString(construct.VarInt, "utf-8"), + instance_id=construct.VarInt, + size=construct.VarInt, + ), + ), data=construct.GreedyBytes, ) @@ -43,11 +46,13 @@ def do_dump_properties_mrea(game: Game, args): for layer in mrea.script_layers: for instance in layer.instances: - header.append({ - "identifier": f"{instance.name} of mrea 0x{asset_id:08x}", - "instance_id": instance.id, - "size": len(instance.raw_properties), - }) + header.append( + { + "identifier": f"{instance.name} of mrea 0x{asset_id:08x}", + "instance_id": instance.id, + "size": len(instance.raw_properties), + } + ) if game == Game.PRIME: name = prime_objects.get_object(instance.type_name).__name__ else: @@ -90,11 +95,13 @@ def do_dump_properties_room(game: Game, args): continue body = instance.data.to_bytes() - header.append({ - "identifier": f"{i} property of room {asset_id} ({instance.data.__class__.__name__})", - "instance_id": instance.type_id, - "size": len(body), - }) + header.append( + { + "identifier": f"{i} property of room {asset_id} ({instance.data.__class__.__name__})", + "instance_id": instance.type_id, + "size": len(body), + } + ) data.append(body) print(f"Wrote properties for {asset_id}") @@ -112,8 +119,8 @@ def do_dump_properties_room(game: Game, args): def _parse_properties( # noqa: PLR0912 Too many branches - game: Game, property_data: construct.Container, build: bool, compare: bool - ): + game: Game, property_data: construct.Container, build: bool, compare: bool +): start_time = time.time() data = io.BytesIO(property_data.data) for instance in property_data.header: @@ -138,8 +145,10 @@ def _parse_properties( # noqa: PLR0912 Too many branches after = data.tell() if after - before != instance.size: - print(f"Instance {instance.identifier} of type {type_name} read {after - before} bytes, " - f"expected {instance.size}") + print( + f"Instance {instance.identifier} of type {type_name} read {after - before} bytes, " + f"expected {instance.size}" + ) if build: new_encoded = the_property.to_bytes() @@ -191,5 +200,5 @@ def main(): do_parse_properties(game, args) -if __name__ == '__main__': +if __name__ == "__main__": main()