diff --git a/Aaru.Filesystems/NTFS/Compression.cs b/Aaru.Filesystems/NTFS/Compression.cs
index acb03e5e9..f49e1fbde 100644
--- a/Aaru.Filesystems/NTFS/Compression.cs
+++ b/Aaru.Filesystems/NTFS/Compression.cs
@@ -138,4 +138,89 @@ public sealed partial class NTFS
return output;
}
+
+ ///
+ /// Decompresses a single Xpress (LZ77) compressed frame.
+ /// Implements the Microsoft Xpress Compression Algorithm (MS-XCA 2.3, plain LZ77).
+ ///
+ /// The compressed frame data.
+ /// Expected size of the decompressed output.
+ /// The decompressed data, or null if decompression fails.
+ static byte[] DecompressXpress(byte[] compressedData, int uncompressedSize)
+ {
+ var output = new byte[uncompressedSize];
+ var srcOffset = 0;
+ var dstOffset = 0;
+
+ while(dstOffset < uncompressedSize)
+ {
+ // Read 32-bit flags word
+ if(srcOffset + 4 > compressedData.Length) break;
+
+ var flags = BitConverter.ToUInt32(compressedData, srcOffset);
+ srcOffset += 4;
+
+ // Process 32 bits, LSB first
+ for(var bit = 0; bit < 32 && dstOffset < uncompressedSize; bit++)
+ {
+ if((flags & 1u << bit) == 0)
+ {
+ // Literal byte
+ if(srcOffset >= compressedData.Length) return output;
+
+ output[dstOffset++] = compressedData[srcOffset++];
+ }
+ else
+ {
+ // Match reference
+ if(srcOffset + 2 > compressedData.Length) return output;
+
+ var matchValue = BitConverter.ToUInt16(compressedData, srcOffset);
+ srcOffset += 2;
+
+ int matchOffset = (matchValue >> 3) + 1;
+ int matchLength = (matchValue & 7) + 3;
+
+ // Extended length encoding
+ if((matchValue & 7) == 7)
+ {
+ if(srcOffset >= compressedData.Length) return output;
+
+ byte extraLength = compressedData[srcOffset++];
+
+ if(extraLength == 255)
+ {
+ // Read 16-bit length
+ if(srcOffset + 2 > compressedData.Length) return output;
+
+ matchLength = BitConverter.ToUInt16(compressedData, srcOffset);
+ srcOffset += 2;
+
+ // If the 16-bit length is 0, read 32-bit length
+ if(matchLength == 0)
+ {
+ if(srcOffset + 4 > compressedData.Length) return output;
+
+ matchLength = (int)BitConverter.ToUInt32(compressedData, srcOffset);
+ srcOffset += 4;
+ }
+ }
+ else
+ matchLength = extraLength + 7 + 3;
+ }
+
+ // Validate back-reference
+ if(dstOffset - matchOffset < 0) return null;
+
+ // Copy bytes (byte-by-byte for overlapping regions)
+ int srcPos = dstOffset - matchOffset;
+
+ for(var i = 0; i < matchLength && dstOffset < uncompressedSize; i++)
+ output[dstOffset++] = output[srcPos++];
+ }
+ }
+ }
+
+ return output;
+ }
}
\ No newline at end of file