using System.IO; using System.Text; using SabreTools.Data.Models.SpoonInstaller; using SabreTools.IO.Extensions; using SabreTools.Numerics.Extensions; #pragma warning disable IDE0017 // Simplify object initialization namespace SabreTools.Serialization.Readers { public class SpoonInstaller : BaseBinaryReader { /// public override SFX? 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 SFX to populate var sfx = new SFX(); // Try to parse the footer from the data end var footer = ParseFooter(data); if (footer.TablePointer == 0 || initialOffset + footer.TablePointer > data.Length) return null; // Set the footer sfx.Footer = footer; // Get to the entry table offset long tableOffset = initialOffset + footer.TablePointer; if (tableOffset < initialOffset || tableOffset >= data.Length) return null; // Seek to the entry table data.SeekIfPossible(tableOffset, SeekOrigin.Begin); // Try to parse the entry table var table = ParseTable(data, footer.EntryCount); if (table.Length != footer.EntryCount) return null; // Set the entry table sfx.Entries = table; return sfx; } catch { // Ignore the actual error return null; } } /// /// Parse a Stream into a Footer /// /// Stream to parse /// Filled Footer on success, null on error public static Footer ParseFooter(Stream data) { // Seek from the end (24 bytes has to use -23) data.SeekIfPossible(-23, SeekOrigin.End); var obj = new Footer(); obj.Unknown0 = data.ReadUInt32LittleEndian(); obj.Unknown1 = data.ReadUInt32LittleEndian(); obj.TablePointer = data.ReadUInt32LittleEndian(); obj.EntryCount = data.ReadUInt32LittleEndian(); obj.Unknown2 = data.ReadUInt32LittleEndian(); obj.Unknown3 = data.ReadUInt32LittleEndian(); return obj; } /// /// Parse a Stream into an entry table /// /// Stream to parse /// Number of entries to parse /// Filled entry table on success, null on error public static FileEntry[] ParseTable(Stream data, uint count) { if (count == 0) return []; var obj = new FileEntry[count]; for (uint i = 0; i < count; i++) { obj[i] = ParseFileEntry(data); } return obj; } /// /// Parse a Stream into a FileEntry /// /// Stream to parse /// Filled FileEntry on success, null on error public static FileEntry ParseFileEntry(Stream data) { var obj = new FileEntry(); obj.FileOffset = data.ReadUInt32LittleEndian(); obj.CompressedSize = data.ReadUInt32LittleEndian(); obj.UncompressedSize = data.ReadUInt32LittleEndian(); obj.Crc32 = data.ReadUInt32LittleEndian(); obj.FilenameLength = data.ReadByteValue(); byte[] nameBytes = data.ReadBytes(obj.FilenameLength); obj.Filename = Encoding.ASCII.GetString(nameBytes); return obj; } } }