diff --git a/Bytes/PAK.Deserializer.cs b/Bytes/PAK.Deserializer.cs new file mode 100644 index 00000000..6acb9ceb --- /dev/null +++ b/Bytes/PAK.Deserializer.cs @@ -0,0 +1,27 @@ +using System.IO; + +namespace SabreTools.Serialization.Bytes +{ + public partial class PAK : IByteSerializer + { + /// +#if NET48 + public Models.PAK.File Deserialize(byte[] data, int offset) +#else + public Models.PAK.File? Deserialize(byte[]? data, int offset) +#endif + { + // If the data is invalid + if (data == null) + return null; + + // If the offset is out of bounds + if (offset < 0 || offset >= data.Length) + return null; + + // Create a memory stream and parse that + MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset); + return new Streams.PAK().Deserialize(dataStream); + } + } +} \ No newline at end of file diff --git a/Streams/PAK.Deserializer.cs b/Streams/PAK.Deserializer.cs new file mode 100644 index 00000000..6918dcbc --- /dev/null +++ b/Streams/PAK.Deserializer.cs @@ -0,0 +1,108 @@ +using System.IO; +using System.Text; +using SabreTools.IO; +using SabreTools.Models.PAK; +using static SabreTools.Models.PAK.Constants; + +namespace SabreTools.Serialization.Streams +{ + public partial class PAK : IStreamSerializer + { + /// +#if NET48 + public Models.PAK.File Deserialize(Stream data) +#else + public Models.PAK.File? Deserialize(Stream? data) +#endif + { + // If the data is invalid + if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead) + return null; + + // If the offset is out of bounds + if (data.Position < 0 || data.Position >= data.Length) + return null; + + // Cache the current offset + long initialOffset = data.Position; + + // Create a new Half-Life Package to fill + var file = new Models.PAK.File(); + + #region Header + + // Try to parse the header + var header = ParseHeader(data); + if (header == null) + return null; + + // Set the package header + file.Header = header; + + #endregion + + #region Directory Items + + // Get the directory items offset + uint directoryItemsOffset = header.DirectoryOffset; + if (directoryItemsOffset < 0 || directoryItemsOffset >= data.Length) + return null; + + // Seek to the directory items + data.Seek(directoryItemsOffset, SeekOrigin.Begin); + + // Create the directory item array + file.DirectoryItems = new DirectoryItem[header.DirectoryLength / 64]; + + // Try to parse the directory items + for (int i = 0; i < file.DirectoryItems.Length; i++) + { + var directoryItem = ParseDirectoryItem(data); + file.DirectoryItems[i] = directoryItem; + } + + #endregion + + return file; + } + + /// + /// Parse a Stream into a Half-Life Package header + /// + /// Stream to parse + /// Filled Half-Life Package header on success, null on error + private static Header ParseHeader(Stream data) + { + // TODO: Use marshalling here instead of building + Header header = new Header(); + + byte[] signature = data.ReadBytes(4); + header.Signature = Encoding.ASCII.GetString(signature); + if (header.Signature != SignatureString) + return null; + + header.DirectoryOffset = data.ReadUInt32(); + header.DirectoryLength = data.ReadUInt32(); + + return header; + } + + /// + /// Parse a Stream into a Half-Life Package directory item + /// + /// Stream to parse + /// Filled Half-Life Package directory item on success, null on error + private static DirectoryItem ParseDirectoryItem(Stream data) + { + // TODO: Use marshalling here instead of building + DirectoryItem directoryItem = new DirectoryItem(); + + byte[] itemName = data.ReadBytes(56); + directoryItem.ItemName = Encoding.ASCII.GetString(itemName).TrimEnd('\0'); + directoryItem.ItemOffset = data.ReadUInt32(); + directoryItem.ItemLength = data.ReadUInt32(); + + return directoryItem; + } + } +} \ No newline at end of file diff --git a/Streams/PAK.Serializer.cs b/Streams/PAK.Serializer.cs new file mode 100644 index 00000000..0f58adc0 --- /dev/null +++ b/Streams/PAK.Serializer.cs @@ -0,0 +1,15 @@ +using System; +using System.IO; + +namespace SabreTools.Serialization.Streams +{ + public partial class PAK : IStreamSerializer + { + /// +#if NET48 + public Stream Serialize(Models.PAK.File obj) => throw new NotImplementedException(); +#else + public Stream? Serialize(Models.PAK.File? obj) => throw new NotImplementedException(); +#endif + } +} \ No newline at end of file