diff --git a/FinModelUtility/Fin/Fin.Compression Tests/Fin.Compression Tests.csproj b/FinModelUtility/Fin/Fin.Compression Tests/Fin.Compression Tests.csproj new file mode 100644 index 000000000..e558ec457 --- /dev/null +++ b/FinModelUtility/Fin/Fin.Compression Tests/Fin.Compression Tests.csproj @@ -0,0 +1,35 @@ + + + + net9.0 + Fin.Compression_Tests + latest + enable + enable + false + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/FinModelUtility/Fin/Fin.Compression Tests/Lz770x10GoldenTests.cs b/FinModelUtility/Fin/Fin.Compression Tests/Lz770x10GoldenTests.cs new file mode 100644 index 000000000..679ad8c26 --- /dev/null +++ b/FinModelUtility/Fin/Fin.Compression Tests/Lz770x10GoldenTests.cs @@ -0,0 +1,51 @@ +using System.Reflection; + +using fin.compression; +using fin.io; +using fin.testing; + +namespace Fin.Compression_Tests; + +public class Lz77GoldenTests { + [Test] + [TestCaseSource(nameof(Get0x10GoldenDirectories_))] + public void Test0x10( + IFileHierarchyDirectory goldenDirectory) + => this.AssertGolden(goldenDirectory); + + private static IFileHierarchyDirectory[] Get0x10GoldenDirectories_() + => GoldenAssert.GetGoldenDirectories( + GoldenAssert.GetRootGoldensDirectory( + Assembly.GetExecutingAssembly()) + .AssertGetExistingSubdir("Lz77/0x10")) + .ToArray(); + + public void AssertGolden(IFileHierarchyDirectory goldenSubdir) { + var inputDirectory = goldenSubdir.AssertGetExistingSubdir("input"); + var lz10File = inputDirectory.FilesWithExtension(".lz77").Single(); + + var outputDirectory = goldenSubdir.AssertGetExistingSubdir("output"); + var hasGoldenExport = !outputDirectory.IsEmpty; + + GoldenAssert.RunInTestDirectory( + goldenSubdir, + tmpDirectory => { + var targetDirectory = + hasGoldenExport ? tmpDirectory : outputDirectory.Impl; + + using var br = lz10File.OpenReadAsBinary(); + var decompressedBytes = new Lz77Decompressor().Decompress(br); + + var targetFile = new FinFile( + Path.Combine(targetDirectory.FullPath, + $"{lz10File.NameWithoutExtension}.bin")); + targetFile.WriteAllBytes(decompressedBytes); + + if (hasGoldenExport) { + GoldenAssert.AssertFilesInDirectoriesAreIdentical( + tmpDirectory, + outputDirectory.Impl); + } + }); + } +} \ No newline at end of file diff --git a/FinModelUtility/Fin/Fin.Compression Tests/goldens/LZSS_0x10/basabasa_fly/input/basabasa_fly.lzss b/FinModelUtility/Fin/Fin.Compression Tests/goldens/Lz77/0x10/basabasa_fly/input/basabasa_fly.lz77 similarity index 100% rename from FinModelUtility/Fin/Fin.Compression Tests/goldens/LZSS_0x10/basabasa_fly/input/basabasa_fly.lzss rename to FinModelUtility/Fin/Fin.Compression Tests/goldens/Lz77/0x10/basabasa_fly/input/basabasa_fly.lz77 diff --git a/FinModelUtility/Fin/Fin.Compression Tests/goldens/Lz77/0x10/basabasa_fly/output/basabasa_fly.bin b/FinModelUtility/Fin/Fin.Compression Tests/goldens/Lz77/0x10/basabasa_fly/output/basabasa_fly.bin new file mode 100644 index 000000000..950526a90 Binary files /dev/null and b/FinModelUtility/Fin/Fin.Compression Tests/goldens/Lz77/0x10/basabasa_fly/output/basabasa_fly.bin differ diff --git a/FinModelUtility/FinModelUtility.sln b/FinModelUtility/FinModelUtility.sln index 222ebc4ee..538ccc653 100644 --- a/FinModelUtility/FinModelUtility.sln +++ b/FinModelUtility/FinModelUtility.sln @@ -199,6 +199,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Brres", "Formats\Brres\Brre EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fin.Compression", "Fin\Fin.Compression\Fin.Compression.csproj", "{01B2C0BD-02AA-4273-8277-8D3016D7A738}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fin.Compression Tests", "Fin\Fin.Compression Tests\Fin.Compression Tests.csproj", "{A5BE17A0-2910-4791-90AC-74FA8A924E48}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -687,6 +689,14 @@ Global {01B2C0BD-02AA-4273-8277-8D3016D7A738}.Release|x64.Build.0 = Release|x64 {01B2C0BD-02AA-4273-8277-8D3016D7A738}.Release|x86.ActiveCfg = Release|x86 {01B2C0BD-02AA-4273-8277-8D3016D7A738}.Release|x86.Build.0 = Release|x86 + {A5BE17A0-2910-4791-90AC-74FA8A924E48}.Debug|x64.ActiveCfg = Debug|x64 + {A5BE17A0-2910-4791-90AC-74FA8A924E48}.Debug|x64.Build.0 = Debug|x64 + {A5BE17A0-2910-4791-90AC-74FA8A924E48}.Debug|x86.ActiveCfg = Debug|x86 + {A5BE17A0-2910-4791-90AC-74FA8A924E48}.Debug|x86.Build.0 = Debug|x86 + {A5BE17A0-2910-4791-90AC-74FA8A924E48}.Release|x64.ActiveCfg = Release|x64 + {A5BE17A0-2910-4791-90AC-74FA8A924E48}.Release|x64.Build.0 = Release|x64 + {A5BE17A0-2910-4791-90AC-74FA8A924E48}.Release|x86.ActiveCfg = Release|x86 + {A5BE17A0-2910-4791-90AC-74FA8A924E48}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -772,6 +782,7 @@ Global {6A9BD73D-0443-491E-BC65-9656F197571F} = {F825789E-A737-4B16-AB6B-37342AC56705} {BB25DCC2-44E0-4CD7-B666-3BC2CD3EB650} = {6A9BD73D-0443-491E-BC65-9656F197571F} {01B2C0BD-02AA-4273-8277-8D3016D7A738} = {98E57180-48B9-4648-AE42-7C7C67120471} + {A5BE17A0-2910-4791-90AC-74FA8A924E48} = {98E57180-48B9-4648-AE42-7C7C67120471} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4CFECBD7-FE88-4CC9-8E1B-6F7805F4F45A} diff --git a/FinModelUtility/Formats/Nitro/Nitro/src/api/NsbmdModelImporter.cs b/FinModelUtility/Formats/Nitro/Nitro/src/api/NsbmdModelImporter.cs index 8a61c2565..8c9de8c7a 100644 --- a/FinModelUtility/Formats/Nitro/Nitro/src/api/NsbmdModelImporter.cs +++ b/FinModelUtility/Formats/Nitro/Nitro/src/api/NsbmdModelImporter.cs @@ -5,6 +5,9 @@ using fin.model.io.importers; using fin.util.sets; +using schema.binary; +using schema.binary.attributes; + namespace nitro.api; public class NsbmdModelImporter : IModelImporter { @@ -20,6 +23,51 @@ public IModel Import(NsbmdModelFileBundle fileBundle) { var nsbmdData = new Lz77Decompressor().Decompress(nsbmdFile.OpenReadAsBinary()); + var ms = new MemoryStream(nsbmdData); + var br = new SchemaBinaryReader(ms); + var nsbmd = br.ReadNew(); + return model; } +} + +[BinarySchema] +public partial class Nsbmd : IBinaryDeserializable { + public uint SectionSize { get; set; } + public uint RenderCommandsOffset { get; set; } + public uint MaterialsOffset { get; set; } + public uint PiecesOffset { get; set; } + public uint InverseBindsOffset { get; set; } + + [SequenceLengthSource(3)] + public byte[] Unknown1 { get; set; } + + public byte NumObjects { get; set; } + public byte NumMaterials { get; set; } + public byte NumPieces { get; set; } + + [SequenceLengthSource(2)] + public byte[] Unknown2 { get; set; } + + // TODO: Fixed-point float + public uint UpScale { get; set; } + + // TODO: Fixed-point float + public uint DownScale { get; set; } + + public ushort NumVerts { get; set; } + public ushort NumSurfs { get; set; } + public ushort NumTris { get; set; } + public ushort NumQuads { get; set; } + + // TODO: Fixed-point half + public ushort BoundingBoxXMin { get; set; } + public ushort BoundingBoxYMin { get; set; } + public ushort BoundingBoxZMin { get; set; } + public ushort BoundingBoxXMax { get; set; } + public ushort BoundingBoxYMax { get; set; } + public ushort BoundingBoxZMax { get; set; } + + [SequenceLengthSource(8)] + public byte[] Unknown3 { get; set; } } \ No newline at end of file