From 11a41ffdf9070cb34fb0facd1804ee59f98e08ea Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Sat, 23 Aug 2025 23:30:21 +0100 Subject: [PATCH] [ZOO] Implement support for compression method 1. --- Aaru.Archives/Zoo/Extern.cs | 21 ++++ Aaru.Archives/Zoo/Files.cs | 34 ++++++ Aaru.Archives/Zoo/Info.cs | 1 + Aaru.Archives/Zoo/LzdStream.cs | 182 +++++++++++++++++++++++++++++ Aaru.Archives/Zoo/Unimplemented.cs | 43 ------- 5 files changed, 238 insertions(+), 43 deletions(-) create mode 100644 Aaru.Archives/Zoo/Extern.cs create mode 100644 Aaru.Archives/Zoo/LzdStream.cs delete mode 100644 Aaru.Archives/Zoo/Unimplemented.cs diff --git a/Aaru.Archives/Zoo/Extern.cs b/Aaru.Archives/Zoo/Extern.cs new file mode 100644 index 000000000..2cadfd6ed --- /dev/null +++ b/Aaru.Archives/Zoo/Extern.cs @@ -0,0 +1,21 @@ +using System; +using System.Runtime.InteropServices; +using Aaru.CommonTypes.Interfaces; + +namespace Aaru.Archives; + +public sealed partial class Zoo : IArchive +{ + [LibraryImport("Aaru.Compression.Native")] + private static partial IntPtr CreateLZDContext(); + + [LibraryImport("libAaru.Compression.Native")] + private static partial void DestroyLZDContext(IntPtr ctx); + + [LibraryImport("libAaru.Compression.Native")] + private static partial int LZD_FeedNative(IntPtr ctx, [In] byte[] inputBuffer, nuint inputSize); + + [LibraryImport("libAaru.Compression.Native")] + private static partial int LZD_DrainNative(IntPtr ctx, [Out] byte[] outputBuffer, nuint outputCapacity, + out nuint produced); +} \ No newline at end of file diff --git a/Aaru.Archives/Zoo/Files.cs b/Aaru.Archives/Zoo/Files.cs index 3f2540569..040bf7766 100644 --- a/Aaru.Archives/Zoo/Files.cs +++ b/Aaru.Archives/Zoo/Files.cs @@ -30,8 +30,11 @@ using System; using System.IO; using System.Runtime.InteropServices; using Aaru.CommonTypes.Enums; +using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Structs; +using Aaru.Filters; using Aaru.Helpers; +using Aaru.Helpers.IO; using FileAttributes = System.IO.FileAttributes; namespace Aaru.Archives; @@ -161,5 +164,36 @@ public sealed partial class Zoo return ErrorNumber.NoError; } + /// + public ErrorNumber GetEntry(int entryNumber, out IFilter filter) + { + filter = null; + + if(!Opened) return ErrorNumber.NotOpened; + + if(entryNumber < 0 || entryNumber >= _files.Count) return ErrorNumber.OutOfRange; + + Direntry entry = _files[entryNumber]; + + if(entry.packing_method > 1) return ErrorNumber.NotImplemented; + + Stream stream = new OffsetStream(new NonClosableStream(_stream), + _files[entryNumber].offset, + _files[entryNumber].offset + _files[entryNumber].size_now); + + if(_files[entryNumber].org_size == 0) stream = new MemoryStream([]); + + if(entry.packing_method == 1) stream = new ForcedSeekStream(entry.org_size, stream); + + filter = new ZZZNoFilter(); + ErrorNumber errno = filter.Open(stream); + + if(errno == ErrorNumber.NoError) return ErrorNumber.NoError; + + stream.Close(); + + return errno; + } + #endregion } \ No newline at end of file diff --git a/Aaru.Archives/Zoo/Info.cs b/Aaru.Archives/Zoo/Info.cs index e2da15ce6..30c3bf70e 100644 --- a/Aaru.Archives/Zoo/Info.cs +++ b/Aaru.Archives/Zoo/Info.cs @@ -32,6 +32,7 @@ using Aaru.CommonTypes.Interfaces; using Aaru.Helpers; using Aaru.Logging; using Spectre.Console; +using Marshal = Aaru.Helpers.Marshal; namespace Aaru.Archives; diff --git a/Aaru.Archives/Zoo/LzdStream.cs b/Aaru.Archives/Zoo/LzdStream.cs new file mode 100644 index 000000000..0ce72f247 --- /dev/null +++ b/Aaru.Archives/Zoo/LzdStream.cs @@ -0,0 +1,182 @@ +using System; +using System.Diagnostics; +using System.IO; + +namespace Aaru.Archives; + +public sealed partial class Zoo +{ +#region Nested type: LZDStatus + + internal enum LZDStatus + { + OK = 0, + NEED_INPUT = 1, + NEED_OUTPUT = 2, + DONE = 3, + ERROR = -1 + } + +#endregion + +#region Nested type: LzdStream + + public class LzdStream : Stream + { + private const int INPUT_CHUNK = 8192; + private const int OUTPUT_CHUNK = 8192; + + private readonly Stream _baseStream; + private readonly byte[] _inBuffer = new byte[INPUT_CHUNK]; + private readonly byte[] _outBuffer = new byte[OUTPUT_CHUNK]; + + private IntPtr _ctx; + private bool _disposed; + private bool _eof; // native says DONE + private bool _flushed; // whether we've sent the final empty feed + private int _outCount; + private int _outOffset; + + public LzdStream(Stream compressedStream) + { + _baseStream = compressedStream ?? throw new ArgumentNullException(nameof(compressedStream)); + _ctx = CreateLZDContext(); + + if(_ctx == IntPtr.Zero) throw new InvalidOperationException("Failed to create LZD context"); + } + + public override bool CanRead => true; + public override bool CanSeek => false; + public override bool CanWrite => false; + public override long Length => throw new NotSupportedException(); + + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + public override void Flush() {} + + public override int Read(byte[] buffer, int offset, int count) + { + if(_disposed) throw new ObjectDisposedException(nameof(LzdStream)); + if(buffer == null) throw new ArgumentNullException(nameof(buffer)); + if(offset < 0 || count < 0 || offset + count > buffer.Length) throw new ArgumentOutOfRangeException(); + + int totalRead = 0; + int totalOut = 0; + int iter = 0; + + while(totalRead < count) + { + Debug.WriteLine($"-- LOOP iter={iter++} total_out={totalOut} --"); + + // serve leftovers first + if(_outOffset < _outCount) + { + int toCopy = Math.Min(count - totalRead, _outCount - _outOffset); + Buffer.BlockCopy(_outBuffer, _outOffset, buffer, offset + totalRead, toCopy); + _outOffset += toCopy; + totalRead += toCopy; + totalOut += toCopy; + + continue; + } + + if(_eof) break; // nothing more to decode + + // drain from native + LZDStatus status = TryDrain(out int produced); + Debug.WriteLine($"[DRAIN] produced={produced} status={status} flushed={_flushed} eof={_eof}"); + + + if(produced > 0) + { + _outCount = produced; + _outOffset = 0; + + continue; // go copy them on next loop + } + + if(status == LZDStatus.NEED_INPUT) + { + int n = _baseStream.Read(_inBuffer, 0, _inBuffer.Length); + + if(n > 0) + { + _eof = false; // we're not done, fresh data incoming + Debug.WriteLine($"[FEED] size={n} flushed={_flushed} (real data)"); + + var f = (LZDStatus)LZD_FeedNative(_ctx, _inBuffer, (UIntPtr)n); + if(f == LZDStatus.ERROR) ThrowDecoderError(); + + continue; + } + + // end of base stream: flush native once + if(!_flushed) + { + Debug.WriteLine($"[FEED] size=0 flushed={_flushed} (final empty feed)"); + var f = (LZDStatus)LZD_FeedNative(_ctx, Array.Empty(), UIntPtr.Zero); + if(f == LZDStatus.ERROR) ThrowDecoderError(); + _flushed = true; + Debug.WriteLine(">>> SET _flushed=true"); + + continue; + } + + // no more to give + _eof = true; + Debug.WriteLine(">>> SET _eof=true (DONE from decoder)"); + + break; + } + + if(status == LZDStatus.DONE) + { + _eof = true; + Debug.WriteLine(">>> SET _eof=true (no more data and already flushed)"); + + break; + } + + // if OK but no bytes, loop again + } + + Debug.WriteLine($"TOTAL OUT={totalOut} bytes"); + + return totalRead; + } + + private LZDStatus TryDrain(out int produced) + { + var st = (LZDStatus)LZD_DrainNative(_ctx, _outBuffer, (UIntPtr)_outBuffer.Length, out UIntPtr p); + if(st == LZDStatus.ERROR) ThrowDecoderError(); + if(st == LZDStatus.DONE) _eof = true; + produced = (int)p; + + return st; + } + + private static void ThrowDecoderError() => throw new IOException("LZD decompression error"); + + protected override void Dispose(bool disposing) + { + if(!_disposed) + { + DestroyLZDContext(_ctx); + _ctx = IntPtr.Zero; + _disposed = true; + } + + base.Dispose(disposing); + } + + public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); + public override void SetLength(long value) => throw new NotSupportedException(); + public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); + } + +#endregion +} \ No newline at end of file diff --git a/Aaru.Archives/Zoo/Unimplemented.cs b/Aaru.Archives/Zoo/Unimplemented.cs deleted file mode 100644 index de1cda731..000000000 --- a/Aaru.Archives/Zoo/Unimplemented.cs +++ /dev/null @@ -1,43 +0,0 @@ -// /*************************************************************************** -// Aaru Data Preservation Suite -// ---------------------------------------------------------------------------- -// -// Filename : Unimplemented.cs -// Author(s) : Natalia Portillo -// -// Component : Zoo plugin. -// -// --[ License ] -------------------------------------------------------------- -// -// This library is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as -// published by the Free Software Foundation; either version 2.1 of the -// License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, see . -// -// ---------------------------------------------------------------------------- -// Copyright © 2011-2025 Natalia Portillo -// ****************************************************************************/ - -using System; -using Aaru.CommonTypes.Enums; -using Aaru.CommonTypes.Interfaces; - -namespace Aaru.Archives; - -public sealed partial class Zoo -{ -#region IArchive Members - - /// - public ErrorNumber GetEntry(int entryNumber, out IFilter filter) => throw new NotImplementedException(); - -#endregion -} \ No newline at end of file