Tests?
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,50 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using fxl.codes.kisekae.data.Archives;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
|
|
||||||
|
namespace fxl.codes.kisekae.data.test;
|
||||||
|
|
||||||
|
public class LhContainerTests
|
||||||
|
{
|
||||||
|
private readonly Assembly _assembly = typeof(LhContainerTests).Assembly;
|
||||||
|
private readonly List<string> _resourceNames = new();
|
||||||
|
private LhCodecService _service;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
foreach (var name in _assembly.GetManifestResourceNames()) _resourceNames.Add(name);
|
||||||
|
var logger = new Logger<LhCodecService>(NullLoggerFactory.Instance);
|
||||||
|
_service = new LhCodecService(logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHeader()
|
||||||
|
{
|
||||||
|
foreach (var resource in _resourceNames)
|
||||||
|
{
|
||||||
|
using var stream = _assembly.GetManifestResourceStream(resource);
|
||||||
|
if (stream == null) continue;
|
||||||
|
|
||||||
|
var files = LhCodecService.GetFiles(in stream);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(files, Is.Not.Empty);
|
||||||
|
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
Assert.That(file.FileName, Is.Not.Null, "Filename is {0}", file.FileName);
|
||||||
|
Assert.That(file.CompressedSize, Is.GreaterThan(0), "Compressed size is {0}", file.CompressedSize);
|
||||||
|
Assert.That(file.UncompressedSize, Is.GreaterThan(0), "Uncompressed size is {0}", file.UncompressedSize);
|
||||||
|
Assert.That(file.UncompressedSize, Is.GreaterThan(file.CompressedSize), "Uncompressed is larger than compressed");
|
||||||
|
Assert.That(file.MethodId, Is.Not.Null, "Method id is {0}", file.MethodId);
|
||||||
|
Assert.That(file.MethodId, Does.StartWith("-l"));
|
||||||
|
Assert.That(file.MethodId, Does.EndWith("-"));
|
||||||
|
Console.WriteLine($"Archive name: {file.FileName}, compression method: {file.MethodId}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
namespace fxl.codes.kisekae.data.test;
|
|
||||||
|
|
||||||
public class LhMethodTests
|
|
||||||
{
|
|
||||||
[SetUp]
|
|
||||||
public void Setup()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Test1()
|
|
||||||
{
|
|
||||||
Assert.Pass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -23,4 +23,19 @@
|
|||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Examples\"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="Examples\2RANMAS.lzh"/>
|
||||||
|
<EmbeddedResource Include="Examples\2RANMAS.lzh">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</EmbeddedResource>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\fxl.codes.kisekae.data\fxl.codes.kisekae.data.csproj"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -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)
|
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);
|
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 fxl.codes.kisekae.data.Entities;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
@@ -8,29 +7,13 @@ public class LhCodecService(ILogger<LhCodecService> logger)
|
|||||||
{
|
{
|
||||||
public async Task<Kisekae?> ReadArchive(Stream stream)
|
public async Task<Kisekae?> ReadArchive(Stream stream)
|
||||||
{
|
{
|
||||||
try
|
throw new NotImplementedException();
|
||||||
{
|
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
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];
|
var bytes = new byte[21];
|
||||||
stream.Position = 0;
|
|
||||||
stream.ReadExactly(bytes);
|
stream.ReadExactly(bytes);
|
||||||
|
|
||||||
var span = bytes.AsSpan();
|
var span = bytes.AsSpan();
|
||||||
Level = span[20];
|
Level = span[20];
|
||||||
MethodId = Encoding.ASCII.GetString(span[2..7]);
|
MethodId = Encoding.ASCII.GetString(span[2..7]);
|
||||||
CompressedSize = EndianUtility.ToLittleEndian(span[7..11]);
|
CompressedSize = EndianUtility.ToLittleEndian(span[7..11]);
|
||||||
|
CompressedFile = new byte[CompressedSize];
|
||||||
UncompressedSize = EndianUtility.ToLittleEndian(span[11..15]);
|
UncompressedSize = EndianUtility.ToLittleEndian(span[11..15]);
|
||||||
Created = EndianUtility.ToDateTime(span[15..19]);
|
Created = EndianUtility.ToDateTime(span[15..19]);
|
||||||
FileOrDirectory = span[19];
|
FileOrDirectory = span[19];
|
||||||
@@ -36,7 +37,7 @@ internal class LhHeader
|
|||||||
public int HeaderSize { get; private set; }
|
public int HeaderSize { get; private set; }
|
||||||
public int HeaderCheckSum { get; private set; }
|
public int HeaderCheckSum { get; private set; }
|
||||||
public string MethodId { 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 int UncompressedSize { get; private set; }
|
||||||
public DateTime Created { get; private set; }
|
public DateTime Created { get; private set; }
|
||||||
public int FileOrDirectory { get; private set; }
|
public int FileOrDirectory { get; private set; }
|
||||||
@@ -45,25 +46,33 @@ internal class LhHeader
|
|||||||
|
|
||||||
public string? FileName { get; private set; }
|
public string? FileName { get; private set; }
|
||||||
|
|
||||||
|
public long HeaderDataPosition { get; }
|
||||||
public long FileDataPosition { get; private set; }
|
public long FileDataPosition { get; private set; }
|
||||||
|
public byte[] CompressedFile { get; }
|
||||||
|
|
||||||
private void Level0(ReadOnlySpan<byte> span, ref readonly Stream stream)
|
private void Level0(ReadOnlySpan<byte> span, ref readonly Stream stream)
|
||||||
{
|
{
|
||||||
HeaderSize = span[0];
|
HeaderSize = span[0];
|
||||||
HeaderCheckSum = span[1];
|
HeaderCheckSum = span[1];
|
||||||
stream.Position = span.Length;
|
|
||||||
FileName = GetFilename(in stream);
|
FileName = GetFilename(in stream);
|
||||||
Crc16 = (short)stream.ReadLittleEndian(2);
|
Crc16 = (short)stream.ReadLittleEndian(2);
|
||||||
|
|
||||||
FileDataPosition = stream.Position;
|
FileDataPosition = stream.Position;
|
||||||
|
stream.ReadExactly(CompressedFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Level1(ReadOnlySpan<byte> span, ref readonly Stream stream)
|
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();
|
OperatingSystem = (char)stream.ReadByte();
|
||||||
|
|
||||||
SetExtendedHeaders(in stream);
|
SetExtendedHeaders(in stream);
|
||||||
|
|
||||||
FileDataPosition = stream.Position;
|
FileDataPosition = stream.Position;
|
||||||
|
stream.ReadExactly(CompressedFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Level2(ReadOnlySpan<byte> span, ref readonly Stream stream)
|
private void Level2(ReadOnlySpan<byte> span, ref readonly Stream stream)
|
||||||
@@ -71,8 +80,11 @@ internal class LhHeader
|
|||||||
HeaderSize = span[..2].ToLittleEndian();
|
HeaderSize = span[..2].ToLittleEndian();
|
||||||
Crc16 = (short)stream.ReadLittleEndian(2);
|
Crc16 = (short)stream.ReadLittleEndian(2);
|
||||||
OperatingSystem = (char)stream.ReadByte();
|
OperatingSystem = (char)stream.ReadByte();
|
||||||
|
|
||||||
SetExtendedHeaders(in stream);
|
SetExtendedHeaders(in stream);
|
||||||
|
|
||||||
FileDataPosition = stream.Position;
|
FileDataPosition = stream.Position;
|
||||||
|
stream.ReadExactly(CompressedFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetFilename(ref readonly Stream stream)
|
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.Algorithms;
|
||||||
using fxl.codes.kisekae.data.Archives.LhHeaders;
|
|
||||||
|
|
||||||
namespace fxl.codes.kisekae.data.Archives.LhMethods;
|
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 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());
|
||||||
@@ -19,4 +19,8 @@
|
|||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4"/>
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<InternalsVisibleTo Include="fxl.codes.kisekae.data.test"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Reference in New Issue
Block a user