Files
Matt Nadareski 8f49e190d8 Fix everything
2026-03-24 19:17:25 -04:00

171 lines
5.7 KiB
C#

using System.IO;
using System.Text;
using SabreTools.Data.Models.PFF;
using SabreTools.IO.Extensions;
using SabreTools.Numerics.Extensions;
using static SabreTools.Data.Models.PFF.Constants;
#pragma warning disable IDE0017 // Simplify object initialization
namespace SabreTools.Serialization.Readers
{
public class PFF : BaseBinaryReader<Archive>
{
/// <inheritdoc/>
public override Archive? Deserialize(Stream? data)
{
// If the data is invalid
if (data is null || !data.CanRead)
return null;
try
{
// Cache the current offset
long initialOffset = data.Position;
// Create a new archive to fill
var archive = new Archive();
#region Header
// Try to parse the header
var header = ParseHeader(data);
if (header.Signature == Version0SignatureString)
{
if (header.FileSegmentSize != Version0HSegmentSize)
return null;
}
else if (header.Signature == Version2SignatureString)
{
if (header.FileSegmentSize != Version2SegmentSize)
return null;
}
else if (header.Signature == Version3SignatureString)
{
if (header.FileSegmentSize != Version2SegmentSize
&& header.FileSegmentSize != Version3SegmentSize)
{
return null;
}
}
else if (header.Signature == Version4SignatureString)
{
if (header.FileSegmentSize != Version4SegmentSize)
return null;
}
else
{
return null;
}
// Set the archive header
archive.Header = header;
#endregion
#region Segments
// Get the segments
long offset = initialOffset + header.FileListOffset;
if (offset < initialOffset || offset >= data.Length)
return null;
// Seek to the segments
data.SeekIfPossible(offset, SeekOrigin.Begin);
// Create the segments array
archive.Segments = new Segment[header.NumberOfFiles];
// Read all segments in turn
for (int i = 0; i < header.NumberOfFiles; i++)
{
archive.Segments[i] = ParseSegment(data, header.FileSegmentSize);
}
#endregion
#region Footer
// Get the footer offset
offset = initialOffset + header.FileListOffset + (header.FileSegmentSize * header.NumberOfFiles);
if (offset < initialOffset || offset >= data.Length)
return null;
// Seek to the footer
data.SeekIfPossible(offset, SeekOrigin.Begin);
// Set the archive footer
archive.Footer = ParseFooter(data);
#endregion
return archive;
}
catch
{
// Ignore the actual error
return null;
}
}
/// <summary>
/// Parse a Stream into a Footer
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Footer on success, null on error</returns>
public static Footer ParseFooter(Stream data)
{
var obj = new Footer();
obj.SystemIP = data.ReadUInt32LittleEndian();
obj.Reserved = data.ReadUInt32LittleEndian();
byte[] kingTag = data.ReadBytes(4);
obj.KingTag = Encoding.ASCII.GetString(kingTag);
return obj;
}
/// <summary>
/// Parse a Stream into a Header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Header on success, null on error</returns>
public static Header ParseHeader(Stream data)
{
var obj = new Header();
obj.HeaderSize = data.ReadUInt32LittleEndian();
byte[] signature = data.ReadBytes(4);
obj.Signature = Encoding.ASCII.GetString(signature);
obj.NumberOfFiles = data.ReadUInt32LittleEndian();
obj.FileSegmentSize = data.ReadUInt32LittleEndian();
obj.FileListOffset = data.ReadUInt32LittleEndian();
return obj;
}
/// <summary>
/// Parse a Stream into a Segment
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="segmentSize">PFF segment size</param>
/// <returns>Filled Segment on success, null on error</returns>
public static Segment ParseSegment(Stream data, uint segmentSize)
{
var obj = new Segment();
obj.Deleted = data.ReadUInt32LittleEndian();
obj.FileLocation = data.ReadUInt32LittleEndian();
obj.FileSize = data.ReadUInt32LittleEndian();
obj.PackedDate = data.ReadUInt32LittleEndian();
byte[] fileName = data.ReadBytes(0x10);
obj.FileName = Encoding.ASCII.GetString(fileName).TrimEnd('\0');
if (segmentSize > Version2SegmentSize)
obj.ModifiedDate = data.ReadUInt32LittleEndian();
if (segmentSize > Version3SegmentSize)
obj.CompressionLevel = data.ReadUInt32LittleEndian();
return obj;
}
}
}