Skip to content

Commit

Permalink
Fixed NJ block finding in model files
Browse files Browse the repository at this point in the history
  • Loading branch information
Justin113D committed Mar 31, 2024
1 parent c47d74d commit 9fb6975
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 63 deletions.
12 changes: 9 additions & 3 deletions src/SA3D.Modeling/File/FileHeaders.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace SA3D.Modeling.File
using System.Collections.Generic;

namespace SA3D.Modeling.File
{
internal class FileHeaders
{
Expand Down Expand Up @@ -96,10 +98,14 @@ internal class FileHeaders
public const uint NMDM = 0x4D444D4Eu;

/// <summary>
/// Point of 0 block header
/// Texture list block header in a hashset for finding the block
/// </summary>
public const uint POF0 = 0x30464F50u;
public static readonly HashSet<ushort> TextureListBlockHeaders = new() { TL };

/// <summary>
/// model block headers in a hashset for finding the block
/// </summary>
public static readonly HashSet<ushort> ModelBlockHeaders = new() { CM, BM };
#endregion
}
}
96 changes: 36 additions & 60 deletions src/SA3D.Modeling/File/ModelFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using SA3D.Modeling.File.Structs;
using SA3D.Modeling.ObjectData.Structs;
using SA3D.Texturing.Texname;
using System.Collections.Generic;

namespace SA3D.Modeling.File
{
Expand Down Expand Up @@ -99,30 +100,32 @@ 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
bool result;

if(FindNJBlockAddress(reader, address, ModelBlockHeaders, out _, out _))
{
_ = GetNJModelBlockAddress(reader, address, out _);
reader.PopEndian();
return true;
result = true;
}
catch(FormatException) { }

switch(reader.ReadULong(address) & HeaderMask)
else
{
case SA1MDL:
case SADXMDL:
case SA2MDL:
case SA2BMDL:
case BUFMDL:
break;
default:
reader.PopEndian();
return false;
switch(reader.ReadULong(address) & HeaderMask)
{
case SA1MDL:
case SADXMDL:
case SA2MDL:
case SA2BMDL:
case BUFMDL:
result = reader[address + 7] <= CurrentModelVersion;
break;
default:
result = false;
break;
}
}

reader.PopEndian();

return reader[address + 7] <= CurrentModelVersion;
reader.PopEndian();
return result;
}


Expand Down Expand Up @@ -193,78 +196,51 @@ public static ModelFile Read(EndianStackReader reader, uint address)
}
}


private static uint GetNJModelBlockAddress(EndianStackReader reader, uint address, out ushort blockHeader)
private static bool FindNJBlockAddress(EndianStackReader reader, uint address, HashSet<ushort> targetType, out ushort blockHeader, out uint blockAddress)
{
uint blockAddress = address;
while(address < reader.Length + 8)
blockAddress = address;
while(blockAddress < 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 && fullheader is not POF0)
{
throw new FormatException("Malformatted NJ data.");
}

if(blockHeader is BM or CM)
if(njHeader is NJ or GJ && targetType.Contains(blockHeader))
{
return blockAddress;
return true;
}

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

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)
if((njHeader == 0 && blockHeader == 0) || blockSize == 0)
{
texturelistBlockAddress = blockAddress;
return true;
break;
}

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

texturelistBlockAddress = 0;
blockHeader = 0;
blockAddress = 0;
return false;
}

private static ModelFile ReadNJ(EndianStackReader reader, uint address)
{
if(!FindNJBlockAddress(reader, address, ModelBlockHeaders, out ushort blockHeader, out uint blockAddress))
{
throw new FormatException("No model block found");
}

TextureNameList? textureNames = null;
if(GetNJTextureNameBlockAddress(reader, address, out uint texturelistBlockAddress))
if(FindNJBlockAddress(reader, address, TextureListBlockHeaders, out _, out uint texturelistBlockAddress))
{
uint textureListAddress = texturelistBlockAddress + 8;
reader.ImageBase = unchecked((uint)-textureListAddress);
textureNames = TextureNameList.Read(reader, textureListAddress, new());
reader.ImageBase = 0;
}

uint blockAddress = GetNJModelBlockAddress(reader, address, out ushort blockHeader);

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

Expand Down

0 comments on commit 9fb6975

Please sign in to comment.