From 1c3ac817b4031504289df3b935e0877f19b5f718 Mon Sep 17 00:00:00 2001 From: Steven Franklin Date: Thu, 5 Sep 2024 14:30:07 -0500 Subject: [PATCH] repair pf_mushr_fr.bmsad - IfThenElse checks for a too-small component field length and replaces the correct value - Throws an error on parsing if the size of a DreadComponent.fields is 0x1C and DreadComponent.fields.fields is not length 1 and an eDefaultCollisionMaterial - applied / formatting to bmsad.py instead of = formatting in applicable locations - updated tests to check parse-build-parse on this file --- .../formats/bmsad.py | 99 +++++++++++-------- tests/formats/test_bmsad.py | 16 +-- 2 files changed, 61 insertions(+), 54 deletions(-) diff --git a/src/mercury_engine_data_structures/formats/bmsad.py b/src/mercury_engine_data_structures/formats/bmsad.py index ce61e2ba..a9f68a0c 100644 --- a/src/mercury_engine_data_structures/formats/bmsad.py +++ b/src/mercury_engine_data_structures/formats/bmsad.py @@ -12,6 +12,7 @@ Flag, Float32l, Hex, + IfThenElse, Int8ul, Int16ul, Int32sl, @@ -42,8 +43,9 @@ # Functions FunctionArgument = Struct( - type=Char, - value=Switch( + "type" / Char, + "value" + / Switch( construct.this.type, { "s": StrId, @@ -56,10 +58,11 @@ ) Functions = make_vector( Struct( - name=StrId, - unk1=Flag, - unk2=Flag, - params=common_types.DictAdapter( + "name" / StrId, + "unk1" / Flag, + "unk2" / Flag, + "params" + / common_types.DictAdapter( common_types.make_vector( common_types.DictElement( FunctionArgument, @@ -241,7 +244,7 @@ def SRDependencies(): return Switch(construct.this.type, component_dependencies) -FieldsSwitch = construct.Switch( +FieldsSwitch = Switch( lambda ctx: find_charclass_for_type(ctx._._.type), fieldtypes(Game.DREAD), ErrorWithMessage(lambda ctx: f"Unknown component type: {ctx._._.type}", construct.SwitchError), @@ -257,32 +260,44 @@ def _not_implemented(code): # Components DreadComponent = Struct( - type=StrId, - unk_1=Int32sl, - unk_2=Int32sl, - fields=PrefixedAllowZeroLen( + "type" / StrId, + "unk_1" / Int32sl, + "unk_2" / Int32sl, + "fields" + / PrefixedAllowZeroLen( Int32ul, Struct( - empty_string=PropertyEnum, - root=PropertyEnum, - fields=FieldsSwitch, + "empty_string" / PropertyEnum, + "root" / PropertyEnum, + "fields" + / IfThenElse( + # repairs incorrect encoding on actors/props/pf_mushr_fr/charclasses/pf_mushr_fr.bmsad + construct.this._parsing and construct.this._.len == 0x1C, + Struct( + Const(1, Int32ul), + Const("eDefaultCollisionMaterial", PropertyEnum), + "eDefaultCollisionMaterial" / construct.Computed(0), + ), + FieldsSwitch, + ), ), ), - extra_fields=ComplexIf( + "extra_fields" + / ComplexIf( lambda this: get_type_lib_dread().is_child_of(this.type, "CComponent"), ExtraFields, ), - functions=Functions, - dependencies=DreadDependencies(), + "functions" / Functions, + "dependencies" / DreadDependencies(), ) SRComponent = Struct( - type=StrId, - unk_1=Hex(Int32ul), - unk_2=Float32l, - functions=Functions, - fields=ExtraFields, - dependencies=SRDependencies(), + "type" / StrId, + "unk_1" / Hex(Int32ul), + "unk_2" / Float32l, + "functions" / Functions, + "fields" / ExtraFields, + "dependencies" / SRDependencies(), ) # Header @@ -313,25 +328,25 @@ def _not_implemented(code): ) SRHeader = Struct( - model_name=StrId, - ignore_samus=Flag, - unk_2a=Float, - unk_2b=Float, - unk_2c=Float, - model_scale=Float, - unk_2e=Float, - hitbox_dimensions=CVector3D, - unk_2g=Float, - unk_3=Int8ul, - unk_4=Int32ul, - other_magic=Int32sl, - unk_5=Flag, - unk_5b=Flag, - unk_6=Flag, - category=StrId, - unk_7=Flag, - sub_actors=make_vector(StrId), - unk_8=Int32ul, + "model_name" / StrId, + "ignore_samus" / Flag, + "unk_2a" / Float, + "unk_2b" / Float, + "unk_2c" / Float, + "model_scale" / Float, + "unk_2e" / Float, + "hitbox_dimensions" / CVector3D, + "unk_2g" / Float, + "unk_3" / Int8ul, + "unk_4" / Int32ul, + "other_magic" / Int32sl, + "unk_5" / Flag, + "unk_5b" / Flag, + "unk_6" / Flag, + "category" / StrId, + "unk_7" / Flag, + "sub_actors" / make_vector(StrId), + "unk_8" / Int32ul, ) # BMSAD diff --git a/tests/formats/test_bmsad.py b/tests/formats/test_bmsad.py index 0db80c5d..e1171717 100644 --- a/tests/formats/test_bmsad.py +++ b/tests/formats/test_bmsad.py @@ -1,17 +1,12 @@ -import contextlib - -import construct import pytest -from tests.test_lib import parse_build_compare_editor +from tests.test_lib import parse_build_compare_editor, parse_build_compare_editor_parsed from mercury_engine_data_structures import dread_data, samus_returns_data from mercury_engine_data_structures.file_tree_editor import FileTreeEditor from mercury_engine_data_structures.formats import dread_types from mercury_engine_data_structures.formats.bmsad import ActorDefFunc, Bmsad -expected_dread_failures = { - "actors/props/pf_mushr_fr/charclasses/pf_mushr_fr.bmsad", -} +dread_must_reencode = ["actors/props/pf_mushr_fr/charclasses/pf_mushr_fr.bmsad"] expected_sr_failures = set() sr_missing = [ @@ -357,12 +352,9 @@ @pytest.mark.parametrize("bmsad_path", dread_data.all_files_ending_with(".bmsad")) def test_compare_dread_all(dread_file_tree, bmsad_path): - if bmsad_path in expected_dread_failures: - expectation = pytest.raises(construct.ConstructError) + if bmsad_path in dread_must_reencode: + parse_build_compare_editor_parsed(Bmsad, dread_file_tree, bmsad_path) else: - expectation = contextlib.nullcontext() - - with expectation: parse_build_compare_editor(Bmsad, dread_file_tree, bmsad_path)