Files

165 lines
4.9 KiB
C#
Raw Permalink Normal View History

2025-08-28 09:56:52 -04:00
using System.Collections.Generic;
using System.IO;
2025-09-26 13:06:18 -04:00
using SabreTools.Data.Models.GZIP;
2025-08-28 09:56:52 -04:00
using SabreTools.IO.Extensions;
2026-03-24 19:17:25 -04:00
using SabreTools.Numerics.Extensions;
using SabreTools.Text.Extensions;
2025-09-26 13:06:18 -04:00
using static SabreTools.Data.Models.GZIP.Constants;
2026-01-27 12:03:01 -05:00
#pragma warning disable IDE0017 // Simplify object initialization
2025-09-26 14:57:20 -04:00
namespace SabreTools.Serialization.Readers
{
public class GZip : BaseBinaryReader<Archive>
{
/// <inheritdoc/>
public override Archive? Deserialize(Stream? data)
{
2025-08-28 07:54:22 -04:00
// If the data is invalid
2026-01-25 14:30:18 -05:00
if (data is null || !data.CanRead)
2025-08-28 07:54:22 -04:00
return null;
try
{
// Cache the current offset
long initialOffset = data.Position;
var archive = new Archive();
2025-08-28 09:56:52 -04:00
#region Header
var header = ParseHeader(data);
2026-01-25 14:30:18 -05:00
if (header is null)
2025-08-28 09:56:52 -04:00
return null;
archive.Header = header;
#endregion
// Seek to the end to read the trailer
2025-10-27 22:43:56 -04:00
data.SeekIfPossible(-8, SeekOrigin.End);
2025-08-28 09:56:52 -04:00
#region Trailer
var trailer = ParseTrailer(data);
2026-01-25 14:30:18 -05:00
if (trailer is null)
2025-08-28 09:56:52 -04:00
return null;
archive.Trailer = trailer;
#endregion
2025-08-28 07:54:22 -04:00
return archive;
}
catch
{
// Ignore the actual error
return null;
}
}
2025-08-28 09:56:52 -04:00
/// <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.ID1 = data.ReadByteValue();
obj.ID2 = data.ReadByteValue();
if (obj.ID1 != ID1 || obj.ID2 != ID2)
return null;
2025-09-01 16:43:21 -04:00
obj.CompressionMethod = (CompressionMethod)data.ReadByteValue();
obj.Flags = (Flags)data.ReadByteValue();
obj.LastModifiedTime = data.ReadUInt32LittleEndian();
obj.ExtraFlags = (ExtraFlags)data.ReadByteValue();
obj.OperatingSystem = (OperatingSystem)data.ReadByteValue();
2025-08-28 09:56:52 -04:00
#if NET20 || NET35
2025-09-01 16:43:21 -04:00
if ((obj.Flags & Flags.FEXTRA) != 0)
2025-08-28 09:56:52 -04:00
#else
2025-09-01 16:43:21 -04:00
if (obj.Flags.HasFlag(Flags.FEXTRA))
2025-08-28 09:56:52 -04:00
#endif
{
2025-09-01 16:43:21 -04:00
obj.ExtraLength = data.ReadUInt16LittleEndian();
2025-08-28 09:56:52 -04:00
// Cache the current position
long currentPosition = data.Position;
// Read the raw data first
obj.ExtraFieldBytes = data.ReadBytes(obj.ExtraLength);
2025-10-27 22:43:56 -04:00
data.SeekIfPossible(currentPosition, SeekOrigin.Begin);
2025-08-28 09:56:52 -04:00
List<ExtraFieldData> extraFields = [];
2025-09-01 16:43:21 -04:00
while (data.Position < currentPosition + obj.ExtraLength)
2025-08-28 09:56:52 -04:00
{
var extraField = ParseExtraFieldData(data);
2026-01-25 14:30:18 -05:00
if (extraField is null)
2025-08-28 09:56:52 -04:00
break;
extraFields.Add(extraField);
}
obj.ExtraField = [.. extraFields];
}
#if NET20 || NET35
2025-09-01 16:43:21 -04:00
if ((obj.Flags & Flags.FNAME) != 0)
2025-08-28 09:56:52 -04:00
#else
2025-09-01 16:43:21 -04:00
if (obj.Flags.HasFlag(Flags.FNAME))
2025-08-28 09:56:52 -04:00
#endif
obj.OriginalFileName = data.ReadNullTerminatedAnsiString();
#if NET20 || NET35
2025-09-01 16:43:21 -04:00
if ((obj.Flags & Flags.FCOMMENT) != 0)
2025-08-28 09:56:52 -04:00
#else
2025-09-01 16:43:21 -04:00
if (obj.Flags.HasFlag(Flags.FCOMMENT))
2025-08-28 09:56:52 -04:00
#endif
obj.FileComment = data.ReadNullTerminatedAnsiString();
#if NET20 || NET35
2025-09-01 16:43:21 -04:00
if ((obj.Flags & Flags.FHCRC) != 0)
2025-08-28 09:56:52 -04:00
#else
2025-09-01 16:43:21 -04:00
if (obj.Flags.HasFlag(Flags.FHCRC))
2025-08-28 09:56:52 -04:00
#endif
obj.CRC16 = data.ReadUInt16LittleEndian();
return obj;
}
/// <summary>
/// Parse a Stream into an ExtraFieldData
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled ExtraFieldData on success, null on error</returns>
public static ExtraFieldData? ParseExtraFieldData(Stream data)
{
var obj = new ExtraFieldData();
2025-09-01 16:43:21 -04:00
obj.SubfieldID1 = data.ReadByteValue();
obj.SubfieldID2 = data.ReadByteValue();
obj.Length = data.ReadUInt16LittleEndian();
obj.Data = data.ReadBytes(obj.Length);
2025-08-28 09:56:52 -04:00
return obj;
}
/// <summary>
/// Parse a Stream into a Trailer
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Trailer on success, null on error</returns>
public static Trailer? ParseTrailer(Stream data)
{
var obj = new Trailer();
obj.CRC32 = data.ReadUInt32LittleEndian();
2025-09-01 16:43:21 -04:00
obj.InputSize = data.ReadUInt32LittleEndian();
2025-08-28 09:56:52 -04:00
return obj;
}
}
}