Tests?
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
namespace fxl.codes.kisekae.data.Archives.Algorithms;
|
||||
|
||||
public class HuffmanNode
|
||||
{
|
||||
public HuffmanNode(char value)
|
||||
{
|
||||
}
|
||||
|
||||
public HuffmanNode(HuffmanNode left, HuffmanNode right)
|
||||
{
|
||||
Left = left;
|
||||
Left.Side = HuffmanNodeSide.Left;
|
||||
Left.Parent = this;
|
||||
|
||||
Right = right;
|
||||
Right.Side = HuffmanNodeSide.Right;
|
||||
Right.Parent = this;
|
||||
|
||||
Weight = left.Weight + right.Weight;
|
||||
}
|
||||
|
||||
public char? Value { get; set; }
|
||||
public int Weight { get; set; } = 1;
|
||||
|
||||
public HuffmanNode? Parent { get; set; }
|
||||
public HuffmanNode? Left { get; set; }
|
||||
public HuffmanNode? Right { get; set; }
|
||||
public HuffmanNodeSide? Side { get; set; }
|
||||
}
|
||||
|
||||
public enum HuffmanNodeSide
|
||||
{
|
||||
Left,
|
||||
Right
|
||||
}
|
||||
@@ -6,7 +6,7 @@ internal static class EndianUtility
|
||||
{
|
||||
public static int ToLittleEndian(this ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
var x = BitConverter.ToInt32(bytes);
|
||||
var x = bytes.Length == 2 ? BitConverter.ToInt16(bytes) : BitConverter.ToInt32(bytes);
|
||||
return BitConverter.IsLittleEndian ? x : BinaryPrimitives.ReverseEndianness(x);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using fxl.codes.kisekae.data.Archives.LhHeaders;
|
||||
using fxl.codes.kisekae.data.Entities;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
@@ -8,29 +7,13 @@ public class LhCodecService(ILogger<LhCodecService> logger)
|
||||
{
|
||||
public async Task<Kisekae?> ReadArchive(Stream stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
var header = new LhHeader(in stream);
|
||||
switch (header.MethodId)
|
||||
{
|
||||
case "-lh0-": break;
|
||||
case "-lzs-": break;
|
||||
case "-lz4-": break;
|
||||
case "-lh1-": break;
|
||||
case "-lh2-": break;
|
||||
case "-lh3-": break;
|
||||
case "-lh4-": break;
|
||||
case "-lh5-": break;
|
||||
case "-lh6-": break;
|
||||
case "-lh7-": break;
|
||||
default: throw new Exception("Unknown method");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e, "Unable to open archive");
|
||||
}
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
return null;
|
||||
internal static IEnumerable<LhContainer> GetFiles(ref readonly Stream stream)
|
||||
{
|
||||
var files = new List<LhContainer>();
|
||||
while (stream.Position + 21 < stream.Length) files.Add(new LhContainer(in stream));
|
||||
return files;
|
||||
}
|
||||
}
|
||||
+19
-7
@@ -1,19 +1,20 @@
|
||||
using System.Text;
|
||||
|
||||
namespace fxl.codes.kisekae.data.Archives.LhHeaders;
|
||||
namespace fxl.codes.kisekae.data.Archives;
|
||||
|
||||
internal class LhHeader
|
||||
internal class LhContainer
|
||||
{
|
||||
public LhHeader(ref readonly Stream stream)
|
||||
public LhContainer(ref readonly Stream stream)
|
||||
{
|
||||
HeaderDataPosition = stream.Position;
|
||||
var bytes = new byte[21];
|
||||
stream.Position = 0;
|
||||
stream.ReadExactly(bytes);
|
||||
|
||||
var span = bytes.AsSpan();
|
||||
Level = span[20];
|
||||
MethodId = Encoding.ASCII.GetString(span[2..7]);
|
||||
CompressedSize = EndianUtility.ToLittleEndian(span[7..11]);
|
||||
CompressedFile = new byte[CompressedSize];
|
||||
UncompressedSize = EndianUtility.ToLittleEndian(span[11..15]);
|
||||
Created = EndianUtility.ToDateTime(span[15..19]);
|
||||
FileOrDirectory = span[19];
|
||||
@@ -36,7 +37,7 @@ internal class LhHeader
|
||||
public int HeaderSize { get; private set; }
|
||||
public int HeaderCheckSum { get; private set; }
|
||||
public string MethodId { get; private set; }
|
||||
public int CompressedSize { get; private set; }
|
||||
public int CompressedSize { get; }
|
||||
public int UncompressedSize { get; private set; }
|
||||
public DateTime Created { get; private set; }
|
||||
public int FileOrDirectory { get; private set; }
|
||||
@@ -45,25 +46,33 @@ internal class LhHeader
|
||||
|
||||
public string? FileName { get; private set; }
|
||||
|
||||
public long HeaderDataPosition { get; }
|
||||
public long FileDataPosition { get; private set; }
|
||||
public byte[] CompressedFile { get; }
|
||||
|
||||
private void Level0(ReadOnlySpan<byte> span, ref readonly Stream stream)
|
||||
{
|
||||
HeaderSize = span[0];
|
||||
HeaderCheckSum = span[1];
|
||||
stream.Position = span.Length;
|
||||
FileName = GetFilename(in stream);
|
||||
Crc16 = (short)stream.ReadLittleEndian(2);
|
||||
|
||||
FileDataPosition = stream.Position;
|
||||
stream.ReadExactly(CompressedFile);
|
||||
}
|
||||
|
||||
private void Level1(ReadOnlySpan<byte> span, ref readonly Stream stream)
|
||||
{
|
||||
Level0(span, in stream);
|
||||
HeaderSize = span[0];
|
||||
HeaderCheckSum = span[1];
|
||||
FileName = GetFilename(in stream);
|
||||
Crc16 = (short)stream.ReadLittleEndian(2);
|
||||
OperatingSystem = (char)stream.ReadByte();
|
||||
|
||||
SetExtendedHeaders(in stream);
|
||||
|
||||
FileDataPosition = stream.Position;
|
||||
stream.ReadExactly(CompressedFile);
|
||||
}
|
||||
|
||||
private void Level2(ReadOnlySpan<byte> span, ref readonly Stream stream)
|
||||
@@ -71,8 +80,11 @@ internal class LhHeader
|
||||
HeaderSize = span[..2].ToLittleEndian();
|
||||
Crc16 = (short)stream.ReadLittleEndian(2);
|
||||
OperatingSystem = (char)stream.ReadByte();
|
||||
|
||||
SetExtendedHeaders(in stream);
|
||||
|
||||
FileDataPosition = stream.Position;
|
||||
stream.ReadExactly(CompressedFile);
|
||||
}
|
||||
|
||||
private static string GetFilename(ref readonly Stream stream)
|
||||
@@ -1,29 +1,32 @@
|
||||
using fxl.codes.kisekae.data.Archives.Algorithms;
|
||||
using fxl.codes.kisekae.data.Archives.LhHeaders;
|
||||
|
||||
namespace fxl.codes.kisekae.data.Archives.LhMethods;
|
||||
|
||||
internal abstract class BaseLhMethod(ref readonly Stream stream, LhHeader header, int slidingWindowBytes, int matchingWindowBytes, IAlgorithm? algorithm)
|
||||
internal abstract class BaseLhMethod(LhContainer container, int slidingWindowBytes, int matchingWindowBytes, IAlgorithm? algorithm)
|
||||
{
|
||||
public int SlidingWindow => slidingWindowBytes * 1024;
|
||||
|
||||
public void InitializeAlgorithm()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal class Lzs(ref readonly Stream stream, LhHeader header) : BaseLhMethod(in stream, header, 2, 17, null);
|
||||
internal class Lzs(LhContainer container) : BaseLhMethod(container, 2, 17, null);
|
||||
|
||||
internal class Lz4(ref readonly Stream stream, LhHeader header) : BaseLhMethod(in stream, header, 0, 0, null);
|
||||
internal class Lz4(LhContainer container) : BaseLhMethod(container, 0, 0, null);
|
||||
|
||||
internal class Lh0(ref readonly Stream stream, LhHeader header) : BaseLhMethod(in stream, header, 0, 0, null);
|
||||
internal class Lh0(LhContainer container) : BaseLhMethod(container, 0, 0, null);
|
||||
|
||||
internal class Lh1(ref readonly Stream stream, LhHeader header) : BaseLhMethod(in stream, header, 4, 60, new HuffmanDynamic());
|
||||
internal class Lh1(LhContainer container) : BaseLhMethod(container, 4, 60, new HuffmanDynamic());
|
||||
|
||||
internal class Lh2(ref readonly Stream stream, LhHeader header) : BaseLhMethod(in stream, header, 8, 256, new HuffmanDynamic());
|
||||
internal class Lh2(LhContainer container) : BaseLhMethod(container, 8, 256, new HuffmanDynamic());
|
||||
|
||||
internal class Lh3(ref readonly Stream stream, LhHeader header) : BaseLhMethod(in stream, header, 8, 256, new HuffmanStatic());
|
||||
internal class Lh3(LhContainer container) : BaseLhMethod(container, 8, 256, new HuffmanStatic());
|
||||
|
||||
internal class Lh4(ref readonly Stream stream, LhHeader header) : BaseLhMethod(in stream, header, 4, 256, new HuffmanStatic());
|
||||
internal class Lh4(LhContainer container) : BaseLhMethod(container, 4, 256, new HuffmanStatic());
|
||||
|
||||
internal class Lh5(ref readonly Stream stream, LhHeader header) : BaseLhMethod(in stream, header, 8, 256, new HuffmanStatic());
|
||||
internal class Lh5(LhContainer container) : BaseLhMethod(container, 8, 256, new HuffmanStatic());
|
||||
|
||||
internal class Lh6(ref readonly Stream stream, LhHeader header) : BaseLhMethod(in stream, header, 32, 256, new HuffmanStatic());
|
||||
internal class Lh6(LhContainer container) : BaseLhMethod(container, 32, 256, new HuffmanStatic());
|
||||
|
||||
internal class Lh7(ref readonly Stream stream, LhHeader header) : BaseLhMethod(in stream, header, 64, 256, new HuffmanStatic());
|
||||
internal class Lh7(LhContainer container) : BaseLhMethod(container, 64, 256, new HuffmanStatic());
|
||||
Reference in New Issue
Block a user