diff --git a/Aaru.Archives/Arc/Files.cs b/Aaru.Archives/Arc/Files.cs
index 0f50ea034..4964577ae 100644
--- a/Aaru.Archives/Arc/Files.cs
+++ b/Aaru.Archives/Arc/Files.cs
@@ -149,7 +149,7 @@ public sealed partial class Arc
if((int)_entries[entryNumber].Method >= 20) return ErrorNumber.InvalidArgument;
- if(_entries[entryNumber].Method > Method.CrunchFastHash) return ErrorNumber.NotSupported;
+ if(_entries[entryNumber].Method > Method.Squash) return ErrorNumber.NotSupported;
Stream stream = new OffsetStream(new NonClosableStream(_stream),
_entries[entryNumber].DataOffset,
@@ -172,6 +172,12 @@ public sealed partial class Arc
if(_entries[entryNumber].Method == Method.CrunchFastHash)
stream = new CrunchStream(stream, _entries[entryNumber].Uncompressed, true, true);
+ if(_entries[entryNumber].Method == Method.CrunchDynamic)
+ stream = new LzwStream(stream, _entries[entryNumber].Uncompressed, false);
+
+ if(_entries[entryNumber].Method == Method.Squash)
+ stream = new LzwStream(stream, _entries[entryNumber].Uncompressed, true);
+
filter = new ZZZNoFilter();
ErrorNumber errno = filter.Open(stream);
diff --git a/Aaru.Compression/Aaru.Compression.csproj b/Aaru.Compression/Aaru.Compression.csproj
index 0399746a8..8aeb6b905 100644
--- a/Aaru.Compression/Aaru.Compression.csproj
+++ b/Aaru.Compression/Aaru.Compression.csproj
@@ -41,6 +41,7 @@
+
diff --git a/Aaru.Compression/Arc/LzwStream.cs b/Aaru.Compression/Arc/LzwStream.cs
new file mode 100644
index 000000000..96db0e7c2
--- /dev/null
+++ b/Aaru.Compression/Arc/LzwStream.cs
@@ -0,0 +1,116 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace Aaru.Compression.Arc;
+
+public partial class LzwStream : Stream
+{
+ readonly byte[] _decoded;
+ readonly long _length;
+ long _position;
+
+ public LzwStream(Stream compressedStream, long decompressedLength, bool squash)
+ {
+ if(compressedStream == null) throw new ArgumentNullException(nameof(compressedStream));
+ if(!compressedStream.CanRead) throw new ArgumentException("Stream must be readable", nameof(compressedStream));
+ if(decompressedLength < 0) throw new ArgumentOutOfRangeException(nameof(decompressedLength));
+
+ // Read full compressed data into memory
+ compressedStream.Position = 0;
+ byte[] inBuf = new byte[compressedStream.Length];
+ compressedStream.ReadExactly(inBuf, 0, inBuf.Length);
+
+ // Allocate output buffer
+ _decoded = new byte[decompressedLength];
+ nint outLen = (nint)decompressedLength;
+
+ int err;
+
+ // Call native decompressor
+ err = squash
+ ? arc_decompress_squash(inBuf, inBuf.Length, _decoded, ref outLen)
+ : arc_decompress_crunch_dynamic(inBuf, inBuf.Length, _decoded, ref outLen);
+
+ if(err != 0) throw new InvalidOperationException("LZW decompression failed");
+
+ // Adjust actual length in case it differs
+ _length = outLen;
+ _position = 0;
+ }
+
+ public override bool CanRead => true;
+ public override bool CanSeek => true;
+ public override bool CanWrite => false;
+ public override long Length => _length;
+
+ public override long Position
+ {
+ get => _position;
+ set => Seek(value, SeekOrigin.Begin);
+ }
+
+ [LibraryImport("libAaru.Compression.Native")]
+ public static partial int arc_decompress_squash(byte[] in_buf, nint in_len, byte[] out_buf, ref nint out_len);
+
+ [LibraryImport("libAaru.Compression.Native")]
+ public static partial int arc_decompress_crunch_dynamic(byte[] in_buf, nint in_len, byte[] out_buf,
+ ref nint out_len);
+
+ public override void Flush()
+ {
+ // no-op
+ }
+
+ ///
+ /// Reads up to bytes from the decompressed buffer
+ /// into , starting at .
+ ///
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ if(buffer == null) throw new ArgumentNullException(nameof(buffer));
+ if(offset < 0) throw new ArgumentOutOfRangeException(nameof(offset));
+ if(count < 0) throw new ArgumentOutOfRangeException(nameof(count));
+ if(offset + count > buffer.Length) throw new ArgumentException("offset+count exceeds buffer length");
+
+ long remaining = _length - _position;
+
+ if(remaining <= 0) return 0;
+
+ int toRead = (int)Math.Min(count, remaining);
+ Array.Copy(_decoded, _position, buffer, offset, toRead);
+ _position += toRead;
+
+ return toRead;
+ }
+
+ ///
+ /// Sets the current position within the decompressed buffer.
+ ///
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ long newPos = origin switch
+ {
+ SeekOrigin.Begin => offset,
+ SeekOrigin.Current => _position + offset,
+ SeekOrigin.End => _length + offset,
+ _ => throw new ArgumentException("Invalid SeekOrigin", nameof(origin))
+ };
+
+ if(newPos < 0 || newPos > _length) throw new IOException("Attempt to seek outside the buffer");
+
+ _position = newPos;
+
+ return _position;
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException("Cannot resize decompressed buffer");
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ throw new NotSupportedException("Stream is read-only");
+ }
+}
\ No newline at end of file