From 3425a28bf77eabc12b8a7720924c44f8fb698678 Mon Sep 17 00:00:00 2001 From: Lani Aung Date: Sun, 22 Sep 2024 12:48:12 -0600 Subject: [PATCH] WIP, but reading more about this --- .../Archives/LzhExtractor.cs | 120 +++++++++++++++++- 1 file changed, 115 insertions(+), 5 deletions(-) diff --git a/fxl.codes.kisekae.data/Archives/LzhExtractor.cs b/fxl.codes.kisekae.data/Archives/LzhExtractor.cs index 8f6aea1..9b0d0bc 100644 --- a/fxl.codes.kisekae.data/Archives/LzhExtractor.cs +++ b/fxl.codes.kisekae.data/Archives/LzhExtractor.cs @@ -4,19 +4,115 @@ namespace fxl.codes.kisekae.data.Archives; public class LzhExtractor { - public async void Open(Stream stream, string filename) + private int _crc16 = -1; + private int _crc32 = -1; + private DateTime _created; + private string _method; + private string _name; + private int _sizeCompressed; + private int _sizeCompressedExtended; + private int _sizeExtension; + private int _sizeHeader; + private int _sizeUncompressed; + public long DataPosition; + + public async Task ReadHeader(Stream stream) { - var headerSize = stream.ReadByte(); + _sizeHeader = stream.ReadByte(); + if (_sizeHeader <= 0) throw new Exception("Zero sized LHA file"); + var headerSum = stream.ReadByte(); var methodBytes = new byte[5]; await stream.ReadExactlyAsync(methodBytes); + _method = Encoding.ASCII.GetString(methodBytes); + if (string.IsNullOrEmpty(_method) || !_method.StartsWith('-')) throw new Exception("Invalid LHA file signature"); - var method = Encoding.ASCII.GetString(methodBytes); - var skipSize = await ReadLittleEndian(stream); - var origSize = await ReadLittleEndian(stream); + _sizeExtension = 0; + _sizeCompressedExtended = await ReadLittleEndian(stream); + _sizeUncompressed = await ReadLittleEndian(stream); var time = await ReadLittleEndian(stream); + _created = GetDateTime(time); + var attribute = stream.ReadByte(); var level = stream.ReadByte(); + int next; + + switch (level) + { + case 0: + _sizeCompressed = _sizeCompressedExtended; + _name = await GetStringValue(stream, stream.ReadByte()); + _crc16 = await ReadLittleEndian(stream, 2); + next = 0; + break; + case 1: + _name = await GetStringValue(stream, stream.ReadByte()); + _crc16 = await ReadLittleEndian(stream, 2); + stream.Position++; + next = await ReadLittleEndian(stream, 2); + break; + case 2: + _sizeCompressed = _sizeCompressedExtended; + _crc16 = await ReadLittleEndian(stream, 2); + stream.Position++; + next = await ReadLittleEndian(stream, 2); + break; + default: throw new Exception("Invalid LHA file header"); + } + + if (level > 0) + while (next != 0) + { + _sizeExtension += next; + var type = stream.ReadByte(); + var size = next - 3; + + switch (type) + { + case 0: break; + case 1: + _name = ""; + for (; size > 0; size--) _name += (char)stream.ReadByte(); + break; + case 2: + case 0x52: + case 0x53: + stream.Position += size; + size = 0; + break; + case 0x3f: break; + case 0x40: + case 0x50: + stream.Position += 2; + size -= 2; + break; + case 0x51: + case 0x54: + stream.Position += 4; + size -= 4; + break; + } + + stream.Position += size; + next = await ReadLittleEndian(stream, 2); + } + + if (level == 1) _sizeCompressed = _sizeCompressedExtended - _sizeExtension; + + DataPosition = level switch + { + 0 => _sizeHeader + 2, + 1 => _sizeHeader + _sizeExtension + 2, + 2 => _sizeHeader, + _ => DataPosition + }; + } + + private static async Task GetStringValue(Stream stream, int length) + { + var buffer = new byte[length]; + await stream.ReadExactlyAsync(buffer); + return Encoding.ASCII.GetString(buffer); } private static async Task ReadLittleEndian(Stream stream, int length = 4) @@ -31,4 +127,18 @@ public class LzhExtractor var b3 = (x >> 0) & 255; return (b0 << 0) + (b1 << 8) + (b2 << 16) + (b3 << 24); } + + private static DateTime GetDateTime(int t) + { + var year = ((t >> 25) & 0x7f) + 1980; + var month = (t >> 21) & 0x0f; + var day = (t >> 16) & 0x1f; + var hour = (t >> 11) & 0x1f; + var minute = (t >> 5) & 0x3f; + var second = (t & 0x1f) * 2; + + if (month is < 1 or > 12 || day is < 1 or > 31 || hour >= 24 || minute >= 60 || second > 60) return DateTime.MinValue; + + return new DateTime(year, month, day, hour, minute, second); + } } \ No newline at end of file