WIP, but reading more about this
This commit is contained in:
@@ -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<string> GetStringValue(Stream stream, int length)
|
||||
{
|
||||
var buffer = new byte[length];
|
||||
await stream.ReadExactlyAsync(buffer);
|
||||
return Encoding.ASCII.GetString(buffer);
|
||||
}
|
||||
|
||||
private static async Task<int> 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user