diff --git a/Aaru.Archives/Arc/Files.cs b/Aaru.Archives/Arc/Files.cs
index 4964577ae..30a354919 100644
--- a/Aaru.Archives/Arc/Files.cs
+++ b/Aaru.Archives/Arc/Files.cs
@@ -4,6 +4,7 @@ using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Structs;
using Aaru.Compression.Arc;
+using Aaru.Compression.Pak;
using Aaru.Filters;
using Aaru.Helpers.IO;
using FileAttributes = System.IO.FileAttributes;
@@ -149,7 +150,7 @@ public sealed partial class Arc
if((int)_entries[entryNumber].Method >= 20) return ErrorNumber.InvalidArgument;
- if(_entries[entryNumber].Method > Method.Squash) return ErrorNumber.NotSupported;
+ if(_entries[entryNumber].Method > Method.Crush) return ErrorNumber.NotSupported;
Stream stream = new OffsetStream(new NonClosableStream(_stream),
_entries[entryNumber].DataOffset,
@@ -178,6 +179,9 @@ public sealed partial class Arc
if(_entries[entryNumber].Method == Method.Squash)
stream = new LzwStream(stream, _entries[entryNumber].Uncompressed, true);
+ if(_entries[entryNumber].Method == Method.Crush)
+ stream = new CrushStream(stream, _entries[entryNumber].Uncompressed);
+
filter = new ZZZNoFilter();
ErrorNumber errno = filter.Open(stream);
diff --git a/Aaru.Compression/Aaru.Compression.csproj b/Aaru.Compression/Aaru.Compression.csproj
index 8aeb6b905..08709e5d0 100644
--- a/Aaru.Compression/Aaru.Compression.csproj
+++ b/Aaru.Compression/Aaru.Compression.csproj
@@ -52,6 +52,7 @@
+
diff --git a/Aaru.Compression/Pak/CrushStream.cs b/Aaru.Compression/Pak/CrushStream.cs
new file mode 100644
index 000000000..5721d54b3
--- /dev/null
+++ b/Aaru.Compression/Pak/CrushStream.cs
@@ -0,0 +1,108 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace Aaru.Compression.Pak;
+
+public partial class CrushStream : Stream
+{
+ readonly byte[] _decoded;
+ readonly long _length;
+ long _position;
+
+ public CrushStream(Stream compressedStream, long decompressedLength)
+ {
+ 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;
+
+ // Call native decompressor
+ int err = pak_decompress_crush(inBuf, inBuf.Length, _decoded, ref outLen);
+
+ if(err != 0) throw new InvalidOperationException("Crush 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 pak_decompress_crush(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