diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 755d12e6..3078d9a1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.0.287 + rev: v0.0.290 hooks: - id: ruff args: [ --fix, --exit-non-zero-on-fix ] \ No newline at end of file diff --git a/src/mercury_engine_data_structures/formats/__init__.py b/src/mercury_engine_data_structures/formats/__init__.py index 07c4d582..8bba034f 100644 --- a/src/mercury_engine_data_structures/formats/__init__.py +++ b/src/mercury_engine_data_structures/formats/__init__.py @@ -8,6 +8,7 @@ from mercury_engine_data_structures.formats.bmmdef import Bmmdef from mercury_engine_data_structures.formats.bmsad import Bmsad from mercury_engine_data_structures.formats.bmsas import Bmsas +from mercury_engine_data_structures.formats.bmsbk import Bmsbk from mercury_engine_data_structures.formats.bmscc import Bmscc from mercury_engine_data_structures.formats.bmscu import Bmscu from mercury_engine_data_structures.formats.bmsld import Bmsld @@ -16,6 +17,7 @@ from mercury_engine_data_structures.formats.bmsnav import Bmsnav from mercury_engine_data_structures.formats.bmssd import Bmssd from mercury_engine_data_structures.formats.bmtre import Bmtre +from mercury_engine_data_structures.formats.bmtun import Bmtun from mercury_engine_data_structures.formats.brem import Brem from mercury_engine_data_structures.formats.bres import Bres from mercury_engine_data_structures.formats.brev import Brev @@ -35,12 +37,14 @@ "BMBLS": Bmbls, "BMMAP": Bmmap, "BMMDEF": Bmmdef, + "BMSBK": Bmsbk, "BMSCP": Bmscp, "BMSSD": Bmssd, "BMSSK": Bmssk, "BMSSS": Bmsss, "BMSAD": Bmsad, "BMSAS": Bmsas, + "BMTUN": Bmtun, "BRFLD": Brfld, "BMSCC": Bmscc, "BMSCD": Bmscc, diff --git a/src/mercury_engine_data_structures/formats/bmsbk.py b/src/mercury_engine_data_structures/formats/bmsbk.py new file mode 100644 index 00000000..b6d9b149 --- /dev/null +++ b/src/mercury_engine_data_structures/formats/bmsbk.py @@ -0,0 +1,61 @@ +import functools + +from construct import ( + Array, + Const, + Construct, + Container, + Flag, + Float32l, + Hex, + Int32ul, + Rebuild, + Struct, + Terminated, +) + +from mercury_engine_data_structures.common_types import CVector3D, StrId, make_vector +from mercury_engine_data_structures.formats.base_resource import BaseResource +from mercury_engine_data_structures.game_check import Game + +Block = Struct( + "pos" / CVector3D, + "unk2" / Int32ul, + "unk3" / Int32ul, + "unk4" / Float32l, + "name1" / StrId, + "name2" / StrId, +) + +def _rebuild_blocks(ctx: Container) -> int: + return sum(len(group.blocks) for group in ctx.types) + +def _rebuild_types(ctx: Container) -> int: + return len(ctx.types) + +BlockGroup = Struct( + "_num_blocks" / Rebuild(Int32ul, _rebuild_blocks), + "_num_types" / Rebuild(Int32ul, _rebuild_types), + "unk_bool" / Flag, # always true? + "types" / Array(lambda this: this._num_types, Struct( + "block_type" / StrId, + "blocks" / make_vector(Block), + )), +) + +BMSBK = Struct( + "magic" / Const(b"MSBK"), + "version" / Hex(Int32ul), + "block_groups" / make_vector(BlockGroup), + "collision_cameras" / make_vector(Struct( + "name" / StrId, + "entries" / make_vector(Int32ul), + )), + Terminated, +) + +class Bmsbk(BaseResource): + @classmethod + @functools.lru_cache + def construct_class(cls, target_game: Game) -> Construct: + return BMSBK diff --git a/src/mercury_engine_data_structures/formats/bmtun.py b/src/mercury_engine_data_structures/formats/bmtun.py new file mode 100644 index 00000000..905ee0dc --- /dev/null +++ b/src/mercury_engine_data_structures/formats/bmtun.py @@ -0,0 +1,55 @@ +import functools + +import construct +from construct.core import ( + Array, + Const, + Construct, + Flag, + Float32l, + Hex, + Int32sl, + Int32ul, + Struct, + Switch, +) + +from mercury_engine_data_structures.common_types import Char, Float, StrId, make_dict +from mercury_engine_data_structures.construct_extensions.misc import ErrorWithMessage +from mercury_engine_data_structures.formats import BaseResource +from mercury_engine_data_structures.game_check import Game + +# Functions +TunableParam = Struct( + type=Char, + value=Switch( + construct.this.type, + { + 's': StrId, + 'f': Float, + 'b': Flag, + 'i': Int32sl, + 'v': Array(3, Float32l) + }, + ErrorWithMessage(lambda ctx: f"Unknown argument type: {ctx.type}", construct.SwitchError) + ) +) + +TunableClass = Struct( + "tunables" / make_dict(TunableParam), +) + +# BMTUN +BMTUN = Struct( + "_magic" / Const(b"MTUN"), + "version" / Const(0x00050001, Hex(Int32ul)), + "classes" / make_dict(TunableClass), + construct.Terminated, +) + + +class Bmtun(BaseResource): + @classmethod + @functools.lru_cache + def construct_class(cls, target_game: Game) -> Construct: + return BMTUN diff --git a/src/mercury_engine_data_structures/formats/pkg.py b/src/mercury_engine_data_structures/formats/pkg.py index 01dee7a6..2e0e6da9 100644 --- a/src/mercury_engine_data_structures/formats/pkg.py +++ b/src/mercury_engine_data_structures/formats/pkg.py @@ -17,7 +17,7 @@ from mercury_engine_data_structures import dread_data, samus_returns_data from mercury_engine_data_structures.construct_extensions.alignment import AlignTo from mercury_engine_data_structures.formats.base_resource import AssetId, BaseResource, NameOrAssetId, resolve_asset_id -from mercury_engine_data_structures.game_check import Game, get_current_game +from mercury_engine_data_structures.game_check import Game def _file_entry(target_game: Game): @@ -73,9 +73,8 @@ def _parse(self, stream, context, path) -> construct.Container: # Get the file headers file_headers = self.file_headers_type._parsereport(stream, context, path) - if get_current_game(context) == Game.DREAD: - # Align to 128 bytes - AlignTo(128)._parsereport(stream, context, path) + # Align to 128 bytes + AlignTo(128)._parsereport(stream, context, path) files = construct.ListContainer() for i, header in enumerate(file_headers): @@ -99,9 +98,8 @@ def _build(self, obj: construct.Container, stream, context, path): # Skip over file headers construct.stream_seek(stream, len(obj.files) * file_entry_size, 1, path) - if get_current_game(context) == Game.DREAD: - # Align to 128 bytes - AlignTo(128)._build(None, stream, context, path) + # Align to 128 bytes + AlignTo(128)._build(None, stream, context, path) header_end = construct.stream_tell(stream, path) diff --git a/tests/formats/test_bmsbk.py b/tests/formats/test_bmsbk.py new file mode 100644 index 00000000..edc9be57 --- /dev/null +++ b/tests/formats/test_bmsbk.py @@ -0,0 +1,16 @@ +import pytest +from tests.test_lib import parse_build_compare_editor + +from mercury_engine_data_structures import samus_returns_data +from mercury_engine_data_structures.formats.bmsbk import Bmsbk + +all_sr_bmsbk = [name for name in samus_returns_data.all_name_to_asset_id().keys() + if name.endswith(".bmsbk")] + + +@pytest.mark.parametrize("bmsbk_path", all_sr_bmsbk) +def test_bmsbk(samus_returns_tree, bmsbk_path): + try: + parse_build_compare_editor(Bmsbk, samus_returns_tree, bmsbk_path) + except FileNotFoundError: + pytest.skip(f"{bmsbk_path} does not exist") diff --git a/tests/formats/test_bmtun.py b/tests/formats/test_bmtun.py new file mode 100644 index 00000000..1491796e --- /dev/null +++ b/tests/formats/test_bmtun.py @@ -0,0 +1,13 @@ +import pytest +from tests.test_lib import parse_build_compare_editor + +from mercury_engine_data_structures import samus_returns_data +from mercury_engine_data_structures.formats.bmtun import Bmtun + +all_sr_bmtun = [name for name in samus_returns_data.all_name_to_asset_id().keys() + if name.endswith(".bmtun")] + + +@pytest.mark.parametrize("bmtun_path", all_sr_bmtun) +def test_bmtun(samus_returns_tree, bmtun_path): + parse_build_compare_editor(Bmtun, samus_returns_tree, bmtun_path)