Skip to content

Commit

Permalink
Fixed reading of NJ files with multiple blocks not working
Browse files Browse the repository at this point in the history
Added texturelist support for NJ files
  • Loading branch information
Justin113D committed Mar 31, 2024
1 parent a91658d commit c47d74d
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 19 deletions.
13 changes: 12 additions & 1 deletion src/SA3D.Modeling/File/FileHeaders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,20 @@ internal class FileHeaders
public const ushort BM = (ushort)0x4D42u;

/// <summary>
/// NM (motion) file header.
/// Texture list block header
/// </summary>
public const ushort TL = (ushort)0x4C54u;

/// <summary>
/// NM (motion) block header.
/// </summary>
public const uint NMDM = 0x4D444D4Eu;

/// <summary>
/// Point of 0 block header
/// </summary>
public const uint POF0 = 0x30464F50u;

#endregion
}
}
83 changes: 68 additions & 15 deletions src/SA3D.Modeling/File/ModelFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using static SA3D.Modeling.File.FileHeaders;
using SA3D.Modeling.File.Structs;
using SA3D.Modeling.ObjectData.Structs;
using SA3D.Texturing.Texname;

namespace SA3D.Modeling.File
{
Expand All @@ -31,6 +32,11 @@ public class ModelFile
/// </summary>
public Node Model { get; }

/// <summary>
/// Texture name list from NJ files
/// </summary>
public TextureNameList? TextureNames { get; }

/// <summary>
/// Meta data of the file.
/// </summary>
Expand All @@ -44,12 +50,14 @@ public class ModelFile
/// <param name="model">Attach format of the file.</param>
/// <param name="metaData">Hierarchy tip of the file.</param>
/// <param name="nj">Meta data of the file.</param>
public ModelFile(ModelFormat format, Node model, MetaData metaData, bool nj)
/// <param name="textureNames">Texture name list of the file.</param>
public ModelFile(ModelFormat format, Node model, MetaData metaData, bool nj, TextureNameList? textureNames)
{
Format = format;
Model = model;
MetaData = metaData;
NJFile = nj;
TextureNames = textureNames;
}


Expand Down Expand Up @@ -88,9 +96,13 @@ public static bool CheckIsModelFile(EndianStackReader reader)
/// <param name="address">Address at which to check.</param>
public static bool CheckIsModelFile(EndianStackReader reader, uint address)
{
bool njFile = reader.ReadUShort(address) is NJ or GJ;
reader.PushBigEndian(njFile && reader.CheckBigEndian32(address + 8));

try
{
_ = GetNJModelBlockAddress(reader, address);
_ = GetNJModelBlockAddress(reader, address, out _);
reader.PopEndian();
return true;
}
catch(FormatException) { }
Expand All @@ -104,9 +116,12 @@ public static bool CheckIsModelFile(EndianStackReader reader, uint address)
case BUFMDL:
break;
default:
reader.PopEndian();
return false;
}

reader.PopEndian();

return reader[address + 7] <= CurrentModelVersion;
}

Expand Down Expand Up @@ -163,11 +178,12 @@ public static ModelFile Read(EndianStackReader reader)
/// <returns>The model file that was read.</returns>
public static ModelFile Read(EndianStackReader reader, uint address)
{
reader.PushBigEndian(false);
bool njFile = reader.ReadUShort(address) is NJ or GJ;
reader.PushBigEndian(njFile && reader.CheckBigEndian32(address + 8));

try
{
return reader.ReadUShort(address) is NJ or GJ
return njFile
? ReadNJ(reader, address)
: ReadSA(reader, address);
}
Expand All @@ -178,20 +194,22 @@ public static ModelFile Read(EndianStackReader reader, uint address)
}


private static uint GetNJModelBlockAddress(EndianStackReader reader, uint address)
private static uint GetNJModelBlockAddress(EndianStackReader reader, uint address, out ushort blockHeader)
{
uint blockAddress = address;
while(address < reader.Length + 8)
{
reader.PushBigEndian(false);
uint fullheader = reader.ReadUInt(blockAddress);
ushort njHeader = reader.ReadUShort(blockAddress);
blockHeader = reader.ReadUShort(blockAddress + 2);
reader.PopEndian();

if(njHeader is not NJ or GJ)
if(njHeader is not NJ or GJ && fullheader is not POF0)
{
throw new FormatException("Malformatted NJ data.");
}

ushort blockHeader = reader.ReadUShort(blockAddress + 2);

if(blockHeader is BM or CM)
{
return blockAddress;
Expand All @@ -204,15 +222,50 @@ private static uint GetNJModelBlockAddress(EndianStackReader reader, uint addres
throw new FormatException("No model block found");
}

private static bool GetNJTextureNameBlockAddress(EndianStackReader reader, uint address, out uint texturelistBlockAddress)
{
uint blockAddress = address;
while(address < reader.Length + 8)
{
reader.PushBigEndian(false);
uint fullheader = reader.ReadUInt(blockAddress);
ushort njHeader = reader.ReadUShort(blockAddress);
ushort blockHeader = reader.ReadUShort(blockAddress + 2);
reader.PopEndian();

if(njHeader is not NJ or GJ && fullheader is not POF0)
{
throw new FormatException("Malformatted NJ data.");
}

if(blockHeader is TL)
{
texturelistBlockAddress = blockAddress;
return true;
}

uint blockSize = reader.ReadUInt(blockAddress + 4);
blockAddress += 8 + blockSize;
}

texturelistBlockAddress = 0;
return false;
}

private static ModelFile ReadNJ(EndianStackReader reader, uint address)
{
uint blockAddress = GetNJModelBlockAddress(reader, address);
ushort blockHeader = reader.ReadUShort(blockAddress + 2);
TextureNameList? textureNames = null;
if(GetNJTextureNameBlockAddress(reader, address, out uint texturelistBlockAddress))
{
uint textureListAddress = texturelistBlockAddress + 8;
reader.ImageBase = unchecked((uint)-textureListAddress);
textureNames = TextureNameList.Read(reader, textureListAddress, new());
reader.ImageBase = 0;
}

uint modelAddress = blockAddress + 8;
bool fileEndian = reader.CheckBigEndian32(modelAddress);
reader.PushBigEndian(fileEndian);
uint blockAddress = GetNJModelBlockAddress(reader, address, out ushort blockHeader);

uint modelAddress = blockAddress + 8;
reader.ImageBase = unchecked((uint)-modelAddress);

ModelFormat format = blockHeader switch
Expand All @@ -224,7 +277,7 @@ private static ModelFile ReadNJ(EndianStackReader reader, uint address)

Node model = Node.Read(reader, modelAddress, format, new());

return new(format, model, new(), true);
return new(format, model, new(), true, textureNames);
}

private static ModelFile ReadSA(EndianStackReader reader, uint address)
Expand Down Expand Up @@ -266,7 +319,7 @@ private static ModelFile ReadSA(EndianStackReader reader, uint address)

reader.ImageBase = prevImageBase;

return new(format, model, metaData, false);
return new(format, model, metaData, false, null);
}

private static void CreateWeldings(MetaData metadata, PointerLUT lut)
Expand Down
1 change: 0 additions & 1 deletion src/SA3D.Modeling/PublicAPI/net7.0/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,6 @@ SA3D.Modeling.File.ModelFile
SA3D.Modeling.File.ModelFile.Format.get -> SA3D.Modeling.ObjectData.Enums.ModelFormat
SA3D.Modeling.File.ModelFile.MetaData.get -> SA3D.Modeling.File.MetaData!
SA3D.Modeling.File.ModelFile.Model.get -> SA3D.Modeling.ObjectData.Node!
SA3D.Modeling.File.ModelFile.ModelFile(SA3D.Modeling.ObjectData.Enums.ModelFormat format, SA3D.Modeling.ObjectData.Node! model, SA3D.Modeling.File.MetaData! metaData, bool nj) -> void
SA3D.Modeling.File.ModelFile.NJFile.get -> bool
SA3D.Modeling.File.ModelFile.Write(SA3D.Common.IO.EndianStackWriter! writer) -> void
SA3D.Modeling.File.ModelFile.WriteToBytes() -> byte[]!
Expand Down
3 changes: 2 additions & 1 deletion src/SA3D.Modeling/PublicAPI/net7.0/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@

SA3D.Modeling.File.ModelFile.ModelFile(SA3D.Modeling.ObjectData.Enums.ModelFormat format, SA3D.Modeling.ObjectData.Node! model, SA3D.Modeling.File.MetaData! metaData, bool nj, SA3D.Texturing.Texname.TextureNameList? textureNames) -> void
SA3D.Modeling.File.ModelFile.TextureNames.get -> SA3D.Texturing.Texname.TextureNameList?
3 changes: 2 additions & 1 deletion src/SA3D.Modeling/SA3D.Modeling.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="SA3D.Common" Version="1.4.5" />
<PackageReference Include="SA3D.Common" Version="1.4.6" />
<PackageReference Include="SA3D.Texturing" Version="1.3.6" />
</ItemGroup>

</Project>

0 comments on commit c47d74d

Please sign in to comment.