Skip to content

Commit

Permalink
Fix Spline Writes
Browse files Browse the repository at this point in the history
  • Loading branch information
Shadowth117 authored and dreamsyntax committed Aug 7, 2024
1 parent 1697475 commit 2b6494f
Show file tree
Hide file tree
Showing 2 changed files with 272 additions and 4 deletions.
257 changes: 257 additions & 0 deletions HeroesPowerPlant/ShadowSplineEditor/POF0.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace AquaModelLibrary.Data.Ninja
{
/// <summary>
/// NOF0 precursor. Compresses offsets via masking. Likely abandoned due to shrinking concerns on size and rising concerns on speed.
/// </summary>
public class POF0
{
public static byte mask = 0xC0;

/// <summary>
/// Incoming pointers MUST be aligned to 4 bytes! Setting align to false will ensure the POF0 byte array itself is not aligned.
/// </summary>
public static byte[] GeneratePOF0(List<uint> offsets, bool align = true)
{
List<byte> bytes = new List<byte>
{
0x50,
0x4F,
0x46,
0x30
};

var data = GenerateRawPOF0(offsets, align);
bytes.AddRange(BitConverter.GetBytes(data.Length));
bytes.AddRange(data);

return bytes.ToArray();
}

/// <summary>
/// Incoming pointers MUST be aligned to 4 bytes! Setting align to false will ensure the POF0 byte array itself is not aligned.
/// </summary>
public static byte[] GeneratePOF0(List<int> offsets, bool align = true)
{
List<byte> bytes = new List<byte>
{
0x50,
0x4F,
0x46,
0x30
};

var data = GenerateRawPOF0(offsets, align);
bytes.AddRange(BitConverter.GetBytes(data.Length));
bytes.AddRange(data);

return bytes.ToArray();
}

/// <summary>
/// Incoming pointers MUST be aligned to 4 bytes! Setting align to false will ensure the POF0 byte array itself is not aligned.
/// </summary>
public static byte[] GenerateRawPOF0(List<uint> offsets, bool align = true)
{
List<byte> pofBytes = new List<byte>();
uint lastPof = 0;
foreach (var offset in offsets)
{
pofBytes.AddRange(CalcPOF0Pointer(lastPof, offset));
lastPof = offset;
}

if (align == true)
{
while (pofBytes.Count % 4 != 0)
{
pofBytes.Add(0);
}
}

return pofBytes.ToArray();
}

/// <summary>
/// Incoming pointers MUST be aligned to 4 bytes! Setting align to false will ensure the POF0 byte array itself is not aligned.
/// </summary>
public static byte[] GenerateRawPOF0(List<int> offsets, bool align = true)
{
List<byte> pofBytes = new List<byte>();
int lastPof = 0;
foreach (var offset in offsets)
{
pofBytes.AddRange(CalcPOF0Pointer((uint)lastPof, (uint)offset));
lastPof = offset;
}

if (align == true)
{
while (pofBytes.Count % 4 != 0)
{
pofBytes.Add(0);
}
}

return pofBytes.ToArray();
}

/// <summary>
/// Takes in the previous POF0 address (before it's compressed) and the current address. As POF0 is made up of relative addresses, we need both to calculate the return here.
/// </summary>
private static byte[] CalcPOF0Pointer(uint lastPOF, uint currentAddress)
{
byte[] finalPOF;
uint offsetDiff = currentAddress - lastPOF;
uint offsetDiv = offsetDiff / 4;

if (offsetDiff > 0xFF)
{
if (offsetDiff > 0xFFFF)
{
var bytes = BitConverter.GetBytes(offsetDiff / 4);
finalPOF = new byte[] { (byte)(0xC0 + bytes[3]), bytes[2], bytes[1], bytes[0] };
}
else
{
short shortCalc = (short)(offsetDiv);
var bytes = BitConverter.GetBytes(shortCalc);
finalPOF = new byte[] { (byte)(0x80 + bytes[1]), bytes[0] };
}
}
else
{
byte byteCalc = (byte)offsetDiv;
byteCalc += 0x40;
finalPOF = new byte[] { byteCalc };
}

return finalPOF;
}

/// <summary>
/// For POF0 data which includes the magic and size
/// </summary>
public static List<uint> GetPof0Offsets(byte[] pof0Bytes)
{
var magic = Encoding.UTF8.GetString(pof0Bytes, 0, 4);
if (magic == "POF0")
{
var size = BitConverter.ToInt32(pof0Bytes, 4);
byte[] arr = new byte[size];
Array.Copy(pof0Bytes, 8, arr, 0, pof0Bytes.Length - 8);
return GetRawPOF0Offsets(arr);
}
else
{
return GetRawPOF0Offsets(pof0Bytes);
}
}

/// <summary>
/// For POF0 data which does not have the magic and size
/// </summary>
public static List<uint> GetRawPOF0Offsets(byte[] pof0Bytes)
{
uint currentOffset = 0;
List<uint> pof = new List<uint>();
int index = 0;
while (index < pof0Bytes.Length)
{
uint pointer = pof0Bytes[index++];

switch (pointer & mask)
{
case 0x40:
pointer -= 0x40;
break;
case 0x80:
pointer -= 0x80;
pointer *= 0x100;
pointer += pof0Bytes[index++];
break;
case 0xC0:
pointer -= 0xC0;
pointer *= 0x1000000;
pointer += pof0Bytes[index++] * (uint)0x10000;
pointer += pof0Bytes[index++] * (uint)0x100;
pointer += pof0Bytes[index++];
break;
}
currentOffset += 4 * pointer;
pof.Add(currentOffset);
}

return pof;
}

/// <summary>
/// For POF0 data which includes the magic and size
/// </summary>
public static List<uint> GetPof0OffsetsWithBase(byte[] pof0Bytes, out List<uint> pof0RawOffsets)
{
var magic = Encoding.UTF8.GetString(pof0Bytes, 0, 4);
if (magic == "POF0")
{
var size = BitConverter.ToInt32(pof0Bytes, 4);
byte[] arr = new byte[size];
Array.Copy(pof0Bytes, 8, arr, 0, pof0Bytes.Length - 8);
return GetRawPOF0OffsetsWithBase(arr, out pof0RawOffsets);
}
else
{
return GetRawPOF0OffsetsWithBase(pof0Bytes, out pof0RawOffsets);
}
}

/// <summary>
/// For POF0 data which does not have the magic and size
/// </summary>
public static List<uint> GetRawPOF0OffsetsWithBase(byte[] pof0Bytes, out List<uint> pof0RawOffsets)
{
uint currentOffset = 0;
List<uint> pof = new List<uint>();
pof0RawOffsets = new List<uint>();
int index = 0;
while (index < pof0Bytes.Length)
{
uint pointer = pof0Bytes[index++];
bool skip = false;
switch (pointer & mask)
{
case 0x40:
pof0RawOffsets.Add(pointer);
pointer -= 0x40;
break;
case 0x80:
pof0RawOffsets.Add(BitConverter.ToUInt16(pof0Bytes, index - 1));
pointer -= 0x80;
pointer *= 0x100;
pointer += pof0Bytes[index++];
break;
case 0xC0:
pof0RawOffsets.Add(BitConverter.ToUInt32(pof0Bytes, index - 1));
pointer -= 0xC0;
pointer *= 0x1000000;
pointer += pof0Bytes[index++] * (uint)0x10000;
pointer += pof0Bytes[index++] * (uint)0x100;
pointer += pof0Bytes[index++];
break;
default:
skip = true;
break;
}
if (!skip)
{
currentOffset += 4 * pointer;
pof.Add(currentOffset);
}
}

return pof;
}
}
}
19 changes: 15 additions & 4 deletions HeroesPowerPlant/ShadowSplineEditor/ShadowSplineEditor.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using HeroesONE_R.Structures;
using AquaModelLibrary.Data.Ninja;
using HeroesONE_R.Structures;
using HeroesPowerPlant.Shared.Utilities;
using SharpDX;
using System;
Expand Down Expand Up @@ -170,7 +171,7 @@ private static List<ShadowSpline> ReadShadowSplineFile(string fileName, Endianne
public IEnumerable<byte> ShadowSplinesToByteArray(string shadowFolderNamePrefix)
{
List<byte> bytes = new List<byte>();

List<int> offsetLocations = new List<int>();
bytes.AddRange(BitConverter.GetBytes(0));
bytes.AddRange(BitConverter.GetBytes(0));
bytes.AddRange(BitConverter.GetBytes(0));
Expand Down Expand Up @@ -198,19 +199,23 @@ public IEnumerable<byte> ShadowSplinesToByteArray(string shadowFolderNamePrefix)

for (int i = 0; i < Splines.Count; i++)
{
offsetLocations.Add(bytes.Count - 0x20 + 0x8);
offsets.Add(bytes.Count - 0x20);
bytes.AddRange(Splines[i].ToByteArray(bytes.Count - 0x20));
}

for (int i = 0; i < Splines.Count; i++)
{
offsetLocations.Add(4 * i);
byte[] offsetBytes = BitConverter.GetBytes(offsets[i]);

bytes[0x20 + 4 * i + 0] = offsetBytes[3];
bytes[0x20 + 4 * i + 1] = offsetBytes[2];
bytes[0x20 + 4 * i + 2] = offsetBytes[1];
bytes[0x20 + 4 * i + 3] = offsetBytes[0];

offsetLocations.Add(offsets[i] + 0x2C);
offsets.Add(bytes.Count - 0x20);
byte[] nameOffset = BitConverter.GetBytes(bytes.Count - 0x20);

bytes[offsets[i] + 0x20 + 0x2C] = nameOffset[3];
Expand All @@ -227,10 +232,14 @@ public IEnumerable<byte> ShadowSplinesToByteArray(string shadowFolderNamePrefix)
while (bytes.Count % 0x4 != 0)
bytes.Add(0);

offsets.Add(bytes.Count - 0x20);
int section5startOffset = bytes.Count - 0x20;

bytes.Add(0x40);
offsetLocations.Sort();
var pof0 = POF0.GenerateRawPOF0(offsetLocations);
bytes.AddRange(pof0);

/*
for (int i = 1; i < Splines.Count; i++)
bytes.Add(0x41);
Expand All @@ -244,8 +253,10 @@ public IEnumerable<byte> ShadowSplinesToByteArray(string shadowFolderNamePrefix)
while (bytes.Count % 0x4 != 0)
bytes.Add(0);
*/

int section5length = bytes.Count - section5startOffset - 0x20;
int pof0Length = pof0.Length;

for (int i = 0; i < 8; i++)
bytes.Add(0);
Expand Down Expand Up @@ -273,7 +284,7 @@ public IEnumerable<byte> ShadowSplinesToByteArray(string shadowFolderNamePrefix)
bytes[6] = aux[1];
bytes[7] = aux[0];

aux = BitConverter.GetBytes(section5length);
aux = BitConverter.GetBytes(pof0Length);

bytes[8] = aux[3];
bytes[9] = aux[2];
Expand Down

0 comments on commit 2b6494f

Please sign in to comment.