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 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 headerSum = stream.ReadByte();
|
||||||
var methodBytes = new byte[5];
|
var methodBytes = new byte[5];
|
||||||
await stream.ReadExactlyAsync(methodBytes);
|
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);
|
_sizeExtension = 0;
|
||||||
var skipSize = await ReadLittleEndian(stream);
|
_sizeCompressedExtended = await ReadLittleEndian(stream);
|
||||||
var origSize = await ReadLittleEndian(stream);
|
_sizeUncompressed = await ReadLittleEndian(stream);
|
||||||
var time = await ReadLittleEndian(stream);
|
var time = await ReadLittleEndian(stream);
|
||||||
|
_created = GetDateTime(time);
|
||||||
|
|
||||||
var attribute = stream.ReadByte();
|
var attribute = stream.ReadByte();
|
||||||
var level = 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)
|
private static async Task<int> ReadLittleEndian(Stream stream, int length = 4)
|
||||||
@@ -31,4 +127,18 @@ public class LzhExtractor
|
|||||||
var b3 = (x >> 0) & 255;
|
var b3 = (x >> 0) & 255;
|
||||||
return (b0 << 0) + (b1 << 8) + (b2 << 16) + (b3 << 24);
|
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