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