From 74b460390708dd8061585ff259007c76eca85541 Mon Sep 17 00:00:00 2001 From: Hawkynt Date: Fri, 23 Oct 2026 14:31:43 +0200 Subject: [PATCH 1/4] * CVF codecs: clean-room DS/JM/SQ reimplementation, SD-4 encoder with back-references * CvfLzCodec (DS-0-x, JM-0-x, SQ-0-0 and the Stacker dispatch) is replaced by an independent implementation (MsdblLzCodec + ClusterMatchFinder) written from docs/CVF-CODECS-ON-DISK.md and the dmsdos reference vectors. Public API unchanged; CvfLzMethod moves to its own file. Clean-room reimplementation from docs/CVF-CODECS-ON-DISK.md; the implementer did not read the previous code or dmsdos sources (rung 3/4 spec + black-box oracle). * The DS/JM encoder now writes a sync after every 512 decoded bytes and never lets a match cross a 512-byte boundary - measured in all 16 dmsdos DS/JM reference streams and recorded in the spec. A sync followed only by zero padding ends a short cluster (tail reads zero). * Auto in the codec keeps the smaller of DS and JM; GenuineDvr3Writer's Auto also tries SQ. + Sd4Codec rewritten from the spec: the encoder emits short/long matches with every length extension and run-coded code-length tables, codes built by package-merge (optimal, complete, 15-bit limit). Within 10% of the dmsdos SD-4 encoder on the five reference samples; dmsdos reads our SD-4 volumes (and 15-bit-deep main codes) byte-exact. + BB_StackerSd4 building block. # Stacker: a compressed run that needs every sector of its cluster reads as stored (AMAP has no flag for it), so ForceCompress wrote clusters dmsdos and our reader returned verbatim. Such a cluster is now stored raw; verified by dmsdos and our reader. + DmsdosOracle: the dmsdos gates run through WSL on Windows as well as natively on Linux; the existing Stacker/DriveSpace 3/DoubleSpace driver-proof tests use it. + Boundary tests for every CVF codec: empty/1-byte clusters, 512/8K/32K clusters, incompressible data, maximum match lengths and distances, malformed streams. ! Fragmented DriveSpace 3 clusters and fragmented/suballocated Stacker clusters are still refused; they follow in a separate change. --- .../StackerSd4/StackerSd4BuildingBlock.cs | 39 ++ .../FileSystems/Cvf/ClusterMatchFinder.cs | 81 +++ .../FileSystems/Cvf/CvfLzCodec.cs | 387 +++-------- .../FileSystems/Cvf/CvfLzMethod.cs | 23 + .../FileSystems/Cvf/MsdblLzCodec.cs | 268 ++++++++ .../FileSystems/Cvf/PackageMerge.cs | 65 ++ Compression.Core/FileSystems/Cvf/Sd4Codec.cs | 599 ++++++++++-------- Compression.Core/REFERENCE.md | 54 +- .../Cvf/CvfCodecBoundaryTests.cs | 188 ++++++ .../DoubleSpace/DoubleSpaceHugeVolumeTests.cs | 8 +- .../DriveSpace3GenuineDmsdosTests.cs | 9 +- .../DriveSpace3GenuineMaintenanceTests.cs | 8 +- .../DriveSpace3/DriveSpace3HugeVolumeTests.cs | 8 +- Compression.Tests/ExternalFsInteropTests.cs | 9 +- .../Stacker/GenuineStackerCodecOracleTests.cs | 187 ++++++ .../Stacker/GenuineStackerDmsdosTests.cs | 9 +- .../Stacker/StackerHugeVolumeTests.cs | 8 +- Compression.Tests/Support/DmsdosOracle.cs | 53 ++ .../GenuineDvr3Writer.cs | 7 +- .../FileSystem.Stacker/FORMAT-NOTES.md | 2 +- .../GenuineStackerWriter.cs | 9 +- .../StackerFormatDescriptor.cs | 3 +- Hawkynt.FileFormats.FileSystems/REFERENCE.md | 2 +- docs/CVF-CODECS-ON-DISK.md | 26 +- 24 files changed, 1452 insertions(+), 600 deletions(-) create mode 100644 Compression.Core/Dictionary/StackerSd4/StackerSd4BuildingBlock.cs create mode 100644 Compression.Core/FileSystems/Cvf/ClusterMatchFinder.cs create mode 100644 Compression.Core/FileSystems/Cvf/CvfLzMethod.cs create mode 100644 Compression.Core/FileSystems/Cvf/MsdblLzCodec.cs create mode 100644 Compression.Core/FileSystems/Cvf/PackageMerge.cs create mode 100644 Compression.Tests/Cvf/CvfCodecBoundaryTests.cs create mode 100644 Compression.Tests/Stacker/GenuineStackerCodecOracleTests.cs create mode 100644 Compression.Tests/Support/DmsdosOracle.cs diff --git a/Compression.Core/Dictionary/StackerSd4/StackerSd4BuildingBlock.cs b/Compression.Core/Dictionary/StackerSd4/StackerSd4BuildingBlock.cs new file mode 100644 index 000000000..7fb8ff544 --- /dev/null +++ b/Compression.Core/Dictionary/StackerSd4/StackerSd4BuildingBlock.cs @@ -0,0 +1,39 @@ +using System.Buffers.Binary; +using Compression.Core.FileSystems.Cvf; +using Compression.Registry; + +namespace Compression.Core.Dictionary.StackerSd4; + +/// +/// Stacker 4's SD-4 codec as a raw primitive: LZ77 over a 2720-byte window, Huffman-coded with a +/// run-length coded code table (docs/CVF-CODECS-ON-DISK.md). The building-block wrapper stores +/// the uncompressed size (32-bit little-endian) before one SD-4 stream covering the whole input. +/// +public sealed class StackerSd4BuildingBlock : IBuildingBlock { + /// + public string Id => "BB_StackerSd4"; + /// + public string DisplayName => "Stacker SD-4"; + /// + public string Description => "Stac Stacker 4 cluster codec: LZ77 (2720-byte window) with canonical Huffman codes and a run-length coded table"; + /// + public AlgorithmFamily Family => AlgorithmFamily.Dictionary; + + /// + public byte[] Compress(ReadOnlySpan data) { + var stream = Sd4Codec.Encode(data, level: 3); + var result = new byte[4 + stream.Length]; + BinaryPrimitives.WriteInt32LittleEndian(result, data.Length); + stream.CopyTo(result, 4); + return result; + } + + /// + public byte[] Decompress(ReadOnlySpan data) { + if (data.Length < 4) throw new InvalidDataException("Stacker SD-4: missing size header."); + var size = BinaryPrimitives.ReadInt32LittleEndian(data); + if (size < 0) throw new InvalidDataException("Stacker SD-4: negative size."); + var stream = data[4..].ToArray(); + return Sd4Codec.Decode(stream, stream.Length, size); + } +} diff --git a/Compression.Core/FileSystems/Cvf/ClusterMatchFinder.cs b/Compression.Core/FileSystems/Cvf/ClusterMatchFinder.cs new file mode 100644 index 000000000..54708d990 --- /dev/null +++ b/Compression.Core/FileSystems/Cvf/ClusterMatchFinder.cs @@ -0,0 +1,81 @@ +namespace Compression.Core.FileSystems.Cvf; + +/// +/// Hash-chain LZ77 match finder over one cluster, shared by the DS/JM and SD-4 encoders. Every match +/// it reports lies wholly inside the cluster: no CVF codec carries a dictionary from one cluster to +/// the next. +/// +internal sealed class ClusterMatchFinder { + private const int HashBits = 13; + private const int MinLength = 3; + + private readonly byte[] _data; + private readonly int[] _head = new int[1 << HashBits]; + private readonly int[] _previous; + private readonly int _maxDistance; + private readonly int _maxLength; + private readonly int _maxChain; + private readonly int _niceLength; + private int _inserted; + + /// The cluster. + /// The farthest distance the format can express. + /// The longest match the format can express. + /// Search effort, 1 (fastest) and up: how many earlier positions with the same + /// hash are tried, and how long a match must be to end the search early. + public ClusterMatchFinder(byte[] data, int maxDistance, int maxLength, int level) { + this._data = data; + this._previous = new int[data.Length]; + this._maxDistance = maxDistance; + this._maxLength = maxLength; + (this._maxChain, this._niceLength) = level switch { + <= 1 => (16, 64), + 2 => (64, 258), + 3 => (256, 1024), + _ => (1024, int.MaxValue), + }; + Array.Fill(this._head, -1); + } + + /// + /// The longest match for the bytes at , preferring the nearest on ties; + /// (0, 0) when there is none of at least three bytes. Positions before + /// are indexed first, so calls must come in ascending order. + /// + /// Where the match starts. + /// Exclusive bound the match may not run past; the cluster end when omitted. + public (int Length, int Distance) Find(int position, int end = int.MaxValue) { + this.IndexUpTo(position); + var data = this._data; + var limit = Math.Min(this._maxLength, Math.Min(data.Length, end) - position); + if (limit < MinLength) return (0, 0); + + int bestLength = 0, bestDistance = 0; + var candidate = this._head[Hash(data, position)]; + for (var chain = this._maxChain; candidate >= 0 && chain > 0; --chain, candidate = this._previous[candidate]) { + var distance = position - candidate; + if (distance > this._maxDistance) break; + if (data[candidate + bestLength] != data[position + bestLength]) continue; + + var length = 0; + while (length < limit && data[candidate + length] == data[position + length]) ++length; + if (length <= bestLength) continue; + + (bestLength, bestDistance) = (length, distance); + if (length == limit || length >= this._niceLength) break; + } + return bestLength >= MinLength ? (bestLength, bestDistance) : (0, 0); + } + + private void IndexUpTo(int position) { + var last = Math.Min(position, this._data.Length - MinLength + 1); + for (; this._inserted < last; ++this._inserted) { + var h = Hash(this._data, this._inserted); + this._previous[this._inserted] = this._head[h]; + this._head[h] = this._inserted; + } + } + + private static int Hash(byte[] data, int position) + => (int)(((uint)data[position] << 16 | (uint)data[position + 1] << 8 | data[position + 2]) * 2654435761u >> (32 - HashBits)); +} diff --git a/Compression.Core/FileSystems/Cvf/CvfLzCodec.cs b/Compression.Core/FileSystems/Cvf/CvfLzCodec.cs index 414453bd2..118ed3a0d 100644 --- a/Compression.Core/FileSystems/Cvf/CvfLzCodec.cs +++ b/Compression.Core/FileSystems/Cvf/CvfLzCodec.cs @@ -1,324 +1,135 @@ -#pragma warning disable CS1591 -using System; -using System.Collections.Generic; -using System.IO; +using System.Buffers.Binary; using Compression.Core.Deflate; -using Compression.Core.Streams; namespace Compression.Core.FileSystems.Cvf; /// -/// Per-cluster compression methods of the DoubleSpace/DriveSpace/Stacker CVF -/// family (see docs/CVF-CODECS-ON-DISK.md). and -/// are the Stacker 3 and Stacker 4 codecs. -/// -public enum CvfLzMethod { Stored, Ds, Jm, Auto, Sq, Sd4, Sd3 } - -/// -/// Genuine DoubleSpace/DriveSpace per-cluster compression codec (DS-0-x and -/// JM-0-x), verified byte-exact against the independent dmsdos decoder. The -/// bitstream packs bits LSB-first into little-endian 16-bit words; a cluster -/// payload is a 16-bit magic ("DS"=0x5344 / "MJ"=0x4D4A) + 16-bit -/// version + an LZ77 token stream terminated by the 0x113f sync. +/// Per-cluster codecs of the DoubleSpace / DriveSpace / DriveSpace 3 / Stacker compressed volumes, +/// implemented from docs/CVF-CODECS-ON-DISK.md. An MSDBL cluster payload starts with a 16-bit +/// magic and a 16-bit version — "DS" (DS-0-x), "JM" (JM-0-x) or "SQ" (SQ-0-0, +/// raw DEFLATE) — followed by the token stream; a Stacker payload carries no method field and is +/// recognised by its first word. /// public static class CvfLzCodec { - public const uint DS_0_0 = 0x00005344; - public const uint JM_0_0 = 0x00004D4A; - public const uint SQ_0_0 = 0x00005153; - - private const int DsMagic = 0x5344; - private const int JmMagic = 0x4D4A; - private const int SqMagic = 0x5153; - private const int Sync = 0x113f; - private const int MaxLen = 512; // safe for both (DS replen<=512, JM<=513) - - // ── bit writer: bit i -> word[i/16] bit i%16, words stored little-endian ─── - private sealed class BitWriter { - private readonly List _words = []; - private int _bit; - public void Put(int value, int n) { - for (var i = 0; i < n; i++, this._bit++) { - var w = this._bit >> 4; - while (this._words.Count <= w) this._words.Add(0); - if (((value >> i) & 1) != 0) this._words[w] |= (ushort)(1 << (this._bit & 15)); - } - } - public byte[] ToBytes() { - var b = new byte[this._words.Count * 2]; - for (var i = 0; i < this._words.Count; i++) { - b[i * 2] = (byte)this._words[i]; - b[i * 2 + 1] = (byte)(this._words[i] >> 8); - } - return b; - } - } - - // ── bit reader: faithful port of dmsdos dblb_* ───────────────────────────── - private sealed class BitReader { - private static readonly uint[] Mask = - [0x0, 0x1, 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, - 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF]; - private readonly byte[] _d; - private readonly int _len; - private uint _buf; - private int _pb = 32; - private int _pd; - private readonly int _pe; - public BitReader(byte[] d, int len) { this._d = d; this._len = len; this._pe = (len + 1) >> 1; } - private ushort Word(int i) { - var o = i * 2; - var lo = o < this._len ? this._d[o] : 0; - var hi = o + 1 < this._len ? this._d[o + 1] : 0; - return (ushort)(lo | (hi << 8)); - } - public uint Peek() { - if (this._pb >= 16) { - this._buf >>= 16; this._pb -= 16; - if (this._pd < this._pe) this._buf |= (uint)Word(this._pd++) << 16; - } - return this._buf >> this._pb; - } - public void Skip(int n) => this._pb += n; - public uint ReadN(int n) { var u = this.Peek(); this._pb += n; return u & Mask[n]; } + /// Method header of a DS-0-0 payload, read as a little-endian 32-bit word. + public const uint DS_0_0 = 'D' | 'S' << 8; - public int RdOffs() { - var u = this.Peek(); - switch (u & 3) { - case 0: case 2: this._pb += 1 + 6; return (int)(63 & (u >> 1)); - case 1: this._pb += 2 + 8; return (int)((255 & (u >> 2)) + 64); - default: this._pb += 2 + 12; return (int)((4095 & (u >> 2)) + 320); - } - } - public int RdLen() { - var u = this.Peek(); - switch (u & 15) { - case 1: case 3: case 5: case 7: case 9: case 11: case 13: case 15: this._pb += 1; return 3; - case 2: case 6: case 10: case 14: this._pb += 3; return (int)((1 & (u >> 2)) + 4); - case 4: case 12: this._pb += 5; return (int)((3 & (u >> 3)) + 6); - case 8: this._pb += 7; return (int)((7 & (u >> 4)) + 10); - } - switch ((u >> 4) & 15) { - case 1: case 3: case 5: case 7: case 9: case 11: case 13: case 15: this._pb += 9; return (int)((15 & (u >> 5)) + 18); - case 2: case 6: case 10: case 14: this._pb += 11; return (int)((31 & (u >> 6)) + 34); - case 4: case 12: this._pb += 13; return (int)((63 & (u >> 7)) + 66); - case 8: this._pb += 15; return (int)((127 & (u >> 8)) + 130); - } - this._pb += 9; - if ((u & 256) != 0) return (int)this.ReadN(8) + 258; - return -1; - } - } + /// Method header of a JM-0-0 payload, read as a little-endian 32-bit word. + public const uint JM_0_0 = 'J' | 'M' << 8; - // ── length / offset encoders (inverse of RdLen / RdOffs) ─────────────────── - private static void PutLen(BitWriter w, int v) { // v = rdlen value, 3..513 - var x = v - 2; - var c = 0; - while ((1 << (c + 1)) <= x) c++; - w.Put(1 << c, c + 1); // c zero bits then a 1 - w.Put(x - (1 << c), c); // c data bits - } - - private static void PutOffs(BitWriter w, int off) { - if (off <= 63) { w.Put(0, 1); w.Put(off, 6); } - else if (off <= 319) { w.Put(1, 2); w.Put(off - 64, 8); } - else { w.Put(3, 2); w.Put(off - 320, 12); } - } + /// Method header of an SQ-0-0 payload, read as a little-endian 32-bit word. + public const uint SQ_0_0 = 'S' | 'Q' << 8; - // =========================================================================== - // Compression - // =========================================================================== + private const ushort Sd4Signature = 0x0081; + private const int HeaderBytes = 4; /// - /// Compresses one cluster. Returns the payload (4-byte method header + token - /// stream, padded to a 2-byte word), or null if it would not be smaller - /// than the raw cluster (caller stores raw instead). + /// Compresses one cluster. Returns the payload, or when it would not be + /// smaller than the raw cluster (the caller then stores the cluster raw). /// public static byte[]? Compress(ReadOnlySpan data, CvfLzMethod method, int level) { - var bytes = Encode(data, method, level); - if (bytes is null) return null; - return bytes.Length < data.Length ? bytes : null; - } - - /// Encodes a cluster with the given method, always returning the - /// payload (4-byte header + token stream), or null for - /// / unsupported methods. The caller decides - /// whether the result fits the cluster's sector budget. - public static byte[]? Encode(ReadOnlySpan data, CvfLzMethod method, int level) { - switch (method) { - case CvfLzMethod.Ds: return CompressDs(data, level); - case CvfLzMethod.Jm: return CompressJm(data, level); - case CvfLzMethod.Sq: return CompressSq(data); - case CvfLzMethod.Sd4: return Sd4Codec.Encode(data); - case CvfLzMethod.Sd3: return Sd3Codec.Encode(data); - case CvfLzMethod.Auto: - // Per-cluster best-of: each cluster carries its own method header, so we - // pick whichever codec yields the smallest payload for this cluster. - byte[]?[] cands = [CompressDs(data, level), CompressJm(data, level), CompressSq(data)]; - byte[]? best = null; - foreach (var c in cands) if (c is not null && (best is null || c.Length < best.Length)) best = c; - return best; - default: return null; - } + var payload = Encode(data, method, level); + return payload is not null && payload.Length < data.Length ? payload : null; } - private static byte[] CompressDs(ReadOnlySpan data, int level) { - var w = new BitWriter(); - w.Put(DsMagic, 16); - w.Put(0, 16); // version 0 (DS-0-0) - var src = data.ToArray(); - var pos = 0; - while (pos < src.Length) { - var (mlen, moff) = FindMatch(src, pos, 4415, 2, level); - if (mlen >= 2) { - if (moff <= 63) w.Put(0 | (moff << 2), 8); - else if (moff <= 319) w.Put(3 | ((moff - 64) << 3), 11); - else w.Put(7 | ((moff - 320) << 3), 15); - PutLen(w, mlen + 1); // DS: copy length = rdlen - 1 - pos += mlen; - } else { - var b = src[pos++]; - if (b < 128) w.Put(2 | (b << 2), 9); - else w.Put(1 | ((b & 127) << 2), 9); - } - } - w.Put(7, 3); w.Put(Sync - 320, 12); // final sync - return w.ToBytes(); - } - - private static byte[] CompressJm(ReadOnlySpan data, int level) { - var w = new BitWriter(); - w.Put(JmMagic, 16); - w.Put(0, 16); // version 0 (JM-0-0) - var src = data.ToArray(); - var pos = 0; - while (pos < src.Length) { - var (mlen, moff) = FindMatch(src, pos, 4415, 3, level); - if (mlen >= 3) { - w.Put(1, 2); // match token - PutOffs(w, moff); - PutLen(w, mlen); // JM: copy length = rdlen - pos += mlen; - } else { - var b = src[pos++]; - if (b < 128) w.Put(b << 1, 8); - else w.Put(3 | ((b & 127) << 2), 9); - } - } - w.Put(1, 2); w.Put(3, 2); w.Put(Sync - 320, 12); // final sync (rdn(2)=1 + rdoffs 0x113f) - return w.ToBytes(); - } - - // SQ-0-0 (DriveSpace 3 "Ultra") is, byte-for-byte, "SQ" + 16-bit version + a - // raw RFC-1951 DEFLATE stream: dmsdos sq_dec's fixed-Huffman table, length and - // distance base/extra tables, and bit-reversed LSB-first packing all match - // DEFLATE exactly, so our Deflate encoder produces a genuine SQ body. - private static byte[] CompressSq(ReadOnlySpan data) { - using var ms = new MemoryStream(); - ms.WriteByte(0x53); ms.WriteByte(0x51); ms.WriteByte(0); ms.WriteByte(0); // "SQ", version 0 - using (var ds = new RawDeflateStream(ms, CompressionStreamMode.Compress, DeflateCompressionLevel.Default, leaveOpen: true)) - ds.Write(data); - return ms.ToArray(); - } - - private static byte[] DecompressSq(byte[] payload, int inLen, int outLen) { - using var ins = new MemoryStream(payload, 4, inLen - 4); - using var ds = new RawDeflateStream(ins, CompressionStreamMode.Decompress); - var outp = new byte[outLen]; - var n = 0; int r; - while (n < outLen && (r = ds.Read(outp, n, outLen - n)) > 0) n += r; - return outp; - } - - // Greedy longest-match finder. level scales the search depth (chain length). - private static (int Len, int Off) FindMatch(byte[] src, int pos, int maxOff, int minLen, int level) { - var bestLen = 0; var bestOff = 0; - var start = Math.Max(0, pos - maxOff); - var limit = Math.Min(MaxLen, src.Length - pos); - if (limit < minLen) return (0, 0); - var probes = level <= 0 ? 256 : level * 1024; - for (var cand = pos - 1; cand >= start && probes-- > 0; cand--) { - if (src[cand] != src[pos]) continue; - var l = 1; - while (l < limit && src[cand + l] == src[pos + l]) l++; - if (l > bestLen) { bestLen = l; bestOff = pos - cand; if (l >= limit) break; } - } - return bestLen >= minLen ? (bestLen, bestOff) : (0, 0); - } - - // =========================================================================== - // Decompression - // =========================================================================== + /// + /// Encodes a cluster with the given method and always returns the payload, or + /// for . The caller decides whether the + /// result fits the cluster's sector budget. keeps the smaller of DS + /// and JM. + /// + public static byte[]? Encode(ReadOnlySpan data, CvfLzMethod method, int level) => method switch { + CvfLzMethod.Ds => EncodeMsdbl(data, MsdblDialect.Ds, level), + CvfLzMethod.Jm => EncodeMsdbl(data, MsdblDialect.Jm, level), + CvfLzMethod.Auto => Smaller(EncodeMsdbl(data, MsdblDialect.Ds, level), EncodeMsdbl(data, MsdblDialect.Jm, level)), + CvfLzMethod.Sq => EncodeSq(data, level), + CvfLzMethod.Sd3 => Sd3Codec.Encode(data), + CvfLzMethod.Sd4 => Sd4Codec.Encode(data, level), + _ => null, + }; - /// Decompresses a cluster payload to exactly bytes. + /// + /// Decompresses an MSDBL cluster payload (DS, JM or SQ, told apart by its header) to exactly + /// bytes. + /// + /// Unknown method header or a malformed stream. public static byte[] Decompress(byte[] payload, int inLen, int outLen) { - var method = (uint)(payload[0] | (payload[1] << 8) | (payload[2] << 16) | (payload[3] << 24)); - var magic16 = (int)(method & 0xFFFF); - if (magic16 == SqMagic) return DecompressSq(payload, inLen, outLen); - if (magic16 == 0x0081) return Sd4Codec.Decode(payload, inLen, outLen); - var br = new BitReader(payload, inLen); - var read = br.ReadN(16); - if (read != (uint)magic16) throw new InvalidDataException($"CVF codec: bad magic 0x{read:X4}"); - br.ReadN(16); // version - var outp = new byte[outLen]; - var p = 0; - if (magic16 == DsMagic) DecodeDs(br, outp, ref p); - else if (magic16 == JmMagic) DecodeJm(br, outp, ref p); - else throw new InvalidDataException($"CVF codec: unsupported method 0x{method:X8}"); - return outp; + var input = payload.AsSpan(0, Math.Min(inLen, payload.Length)); + if (input.Length < HeaderBytes) throw new InvalidDataException("CVF: cluster payload shorter than its method header."); + + var output = new byte[outLen]; + var stream = input[HeaderBytes..]; + switch (BinaryPrimitives.ReadUInt16LittleEndian(input)) { + case (ushort)DS_0_0: MsdblLzCodec.Decode(stream, output, MsdblDialect.Ds, stopAtEndOfInput: false); break; + case (ushort)JM_0_0: MsdblLzCodec.Decode(stream, output, MsdblDialect.Jm, stopAtEndOfInput: false); break; + case (ushort)SQ_0_0: DecodeSq(stream, output); break; + case var magic: throw new InvalidDataException($"CVF: unknown cluster method header 0x{magic:X4}."); + } + return output; } /// /// Decompresses a Stacker cluster payload to bytes. Stacker records no /// method per cluster; the payload's first little-endian word selects it — 0x0081 SD-4, - /// 0x5344 ("DS") the DoubleSpace codec, anything else SD-3, whose first token is always a + /// 0x5344 ("DS") the DoubleSpace grammar, anything else SD-3, whose first token is always a /// literal and so can never begin with either word. /// + /// A malformed stream. public static byte[] DecompressStacker(byte[] payload, int inLen, int outLen) { - ArgumentNullException.ThrowIfNull(payload); - if (inLen < 2) throw new InvalidDataException("Stacker cluster payload is too short."); - return (payload[0] | (payload[1] << 8)) switch { - 0x0081 => Sd4Codec.Decode(payload, inLen, outLen), - DsMagic => Decompress(payload, inLen, outLen), - _ => Sd3Codec.Decode(payload.AsSpan(0, inLen), outLen), - }; + var input = payload.AsSpan(0, Math.Min(inLen, payload.Length)); + var word = input.Length >= 2 ? BinaryPrimitives.ReadUInt16LittleEndian(input) : -1; + switch (word) { + case Sd4Signature: + return Sd4Codec.Decode(payload, input.Length, outLen); + case (int)DS_0_0 when input.Length >= HeaderBytes: + var output = new byte[outLen]; + MsdblLzCodec.Decode(input[HeaderBytes..], output, MsdblDialect.Ds, stopAtEndOfInput: true); + return output; + default: + return Sd3Codec.Decode(input, outLen); + } } - private static void DecodeDs(BitReader br, byte[] outp, ref int p) { - while (p < outp.Length) { - var u = br.Peek(); - switch (u & 3) { - case 0: br.Skip(8); Copy(outp, ref p, (int)((u >> 2) & 63), br, -1); break; - case 1: br.Skip(9); outp[p++] = (byte)((u >> 2) | 128); break; - case 2: br.Skip(9); outp[p++] = (byte)((u >> 2) & 127); break; - default: - int off; - if ((u & 4) != 0) { br.Skip(15); off = (int)((u >> 3) & 4095) + 320; } - else { br.Skip(11); off = (int)((u >> 3) & 255) + 64; } - Copy(outp, ref p, off, br, -1); - break; - } - } + private static byte[] EncodeMsdbl(ReadOnlySpan data, MsdblDialect dialect, int level) { + var writer = new LsbBitWriter(); + WriteHeader(writer, dialect == MsdblDialect.Ds ? DS_0_0 : JM_0_0); + MsdblLzCodec.Encode(data, writer, dialect, level); + return writer.ToArray(alignment: 2); } - private static void DecodeJm(BitReader br, byte[] outp, ref int p) { - while (p < outp.Length) { - var u = br.Peek(); - switch (u & 3) { - case 0: case 2: br.Skip(8); outp[p++] = (byte)((u >> 1) & 127); break; - case 1: br.Skip(2); Copy(outp, ref p, br.RdOffs(), br, 0); break; - default: br.Skip(9); outp[p++] = (byte)(((u >> 2) & 127) | 128); break; + private static byte[] EncodeSq(ReadOnlySpan data, int level) { + var deflateLevel = level switch { + <= 1 => DeflateCompressionLevel.Fast, + 2 => DeflateCompressionLevel.Default, + 3 => DeflateCompressionLevel.Best, + _ => DeflateCompressionLevel.Maximum, + }; + var stream = DeflateCompressor.Compress(data, deflateLevel); + var payload = new byte[HeaderBytes + stream.Length + (stream.Length & 1)]; + BinaryPrimitives.WriteUInt32LittleEndian(payload, SQ_0_0); + stream.CopyTo(payload, HeaderBytes); + return payload; + } + + // The DEFLATE stream ends at its final block or when the cluster is full, whichever comes first. + private static void DecodeSq(ReadOnlySpan stream, Span output) { + try { + using var input = new MemoryStream(stream.ToArray(), writable: false); + var inflater = new DeflateDecompressor(input); + var filled = 0; + while (filled < output.Length) { + var n = inflater.Decompress(output[filled..]); + if (n == 0) break; + filled += n; } + } catch (Exception e) when (e is IOException or ArgumentException or IndexOutOfRangeException) { + throw new InvalidDataException("SQ: malformed DEFLATE stream.", e); } } - private static void Copy(byte[] outp, ref int p, int off, BitReader br, int k) { - if (off == Sync) return; // sync token mid-stream (shouldn't fill) - var len = br.RdLen() + k; - // Past the end of the input every token reads as a zero-distance, invalid-length match, so - // these checks also guarantee that a truncated stream terminates. - if (len <= 0) throw new InvalidDataException("CVF codec: invalid match length."); - if (off <= 0 || off > p) throw new InvalidDataException("CVF codec: match reaches before the start of the cluster."); - var src = p - off; - for (var i = 0; i < len && p < outp.Length; i++) outp[p++] = outp[src + i]; + private static void WriteHeader(LsbBitWriter writer, uint header) { + for (var i = 0; i < HeaderBytes; ++i) writer.WriteByte((byte)(header >> (8 * i))); } + + private static byte[] Smaller(byte[] a, byte[] b) => b.Length < a.Length ? b : a; } diff --git a/Compression.Core/FileSystems/Cvf/CvfLzMethod.cs b/Compression.Core/FileSystems/Cvf/CvfLzMethod.cs new file mode 100644 index 000000000..acfcc979d --- /dev/null +++ b/Compression.Core/FileSystems/Cvf/CvfLzMethod.cs @@ -0,0 +1,23 @@ +namespace Compression.Core.FileSystems.Cvf; + +/// +/// Per-cluster compression methods of the DoubleSpace/DriveSpace/Stacker CVF family (see +/// docs/CVF-CODECS-ON-DISK.md). and are the Stacker 3 and +/// Stacker 4 codecs. +/// +public enum CvfLzMethod { + /// No compression; the cluster is stored as is. + Stored = 0, + /// DoubleSpace DS-0-x LZ77. + Ds = 1, + /// DriveSpace JM-0-x LZ77 (DriveSpace 3 Normal/High). + Jm = 2, + /// Per cluster the smaller of and , both of which every MSDBL volume reads. + Auto = 3, + /// DriveSpace 3 SQ-0-0 (Ultra): a raw DEFLATE stream behind the method header. + Sq = 4, + /// Stacker 4 SD-4: Huffman-coded LZ77 behind the header word 0x0081. + Sd4 = 5, + /// Stacker 3 SD-3: a bare LZS stream closed by a check byte. + Sd3 = 6, +} diff --git a/Compression.Core/FileSystems/Cvf/MsdblLzCodec.cs b/Compression.Core/FileSystems/Cvf/MsdblLzCodec.cs new file mode 100644 index 000000000..ba8d954ca --- /dev/null +++ b/Compression.Core/FileSystems/Cvf/MsdblLzCodec.cs @@ -0,0 +1,268 @@ +namespace Compression.Core.FileSystems.Cvf; + +/// The two Microsoft LZ77 token grammars of docs/CVF-CODECS-ON-DISK.md. +internal enum MsdblDialect { + /// DoubleSpace DS-0-x: two-bit literal tags, matches copy one byte less than their length code. + Ds, + /// DriveSpace JM-0-x: one-bit tag for low literals, matches copy their full length code. + Jm, +} + +/// +/// The DS-0-x / JM-0-x token stream (without the 4-byte method header): bits LSB-first from +/// consecutive bytes, literals and back-references of up to 4414 bytes distance, and the sync marker +/// (distance 4415) that closes every cluster. +/// +internal static class MsdblLzCodec { + /// The all-ones 12-bit long distance, which is the sync marker rather than a match. + public const int SyncDistance = 320 + 0xFFF; + + /// The farthest real match. + public const int MaxDistance = SyncDistance - 1; + + /// The longest length code: nine-step prefix, 2 + 2^8 + 255. + private const int MaxLengthCode = 513; + + private const int MaxLengthPrefix = 8; + + /// Decoded bytes between two sync markers in an encoded cluster: one sector. + private const int SyncInterval = 512; + + // ── decoding ──────────────────────────────────────────────────────────── + + /// + /// Decodes the token stream in until is full. + /// + /// The token stream, starting right after the method header. + /// The cluster; bytes not written stay as they are (zero). + /// DS or JM. + /// + /// Stacker's use of the DS grammar: a stream that runs out (or reaches its zero padding) before the + /// cluster is full ends there instead of being an error. + /// + /// The number of bytes decoded. + /// The stream is malformed or ends early. + public static int Decode(ReadOnlySpan stream, Span output, MsdblDialect dialect, bool stopAtEndOfInput) { + var reader = new LsbBitReader(stream); + var position = 0; + while (position < output.Length) { + var token = dialect == MsdblDialect.Ds ? ReadDsToken(ref reader) : ReadJmToken(ref reader); + if (reader.Overran) { + if (stopAtEndOfInput) break; + throw new InvalidDataException($"{dialect}: token stream ends after {position} of {output.Length} bytes."); + } + + if (token.Distance == 0) { // literal (a real match never has distance 0) + if (token.Length != 0) { + if (stopAtEndOfInput) break; // zero padding read as a distance-0 match + throw new InvalidDataException($"{dialect}: match with distance 0."); + } + output[position++] = token.Literal; + continue; + } + + if (token.Distance == SyncDistance) { + // A cluster whose data ends before its nominal size is closed by a sync with nothing but + // padding behind it; the remainder of the cluster reads as zero. + if (reader.RestIsZero) break; + continue; + } + + var length = ReadLengthCode(ref reader); + if (reader.Overran) { + if (stopAtEndOfInput) break; + throw new InvalidDataException($"{dialect}: token stream ends inside a match."); + } + if (length < 0) throw new InvalidDataException($"{dialect}: length code has more than {MaxLengthPrefix} leading zeros."); + if (dialect == MsdblDialect.Ds) --length; + if (token.Distance > position) throw new InvalidDataException($"{dialect}: match reaches before the start of the cluster."); + + length = Math.Min(length, output.Length - position); + for (var end = position + length; position < end; ++position) + output[position] = output[position - token.Distance]; + } + return position; + } + + /// A literal ( 0, 0) or the distance of a match/sync. + private readonly record struct Token(byte Literal, int Distance, int Length = 0); + + private static Token ReadDsToken(ref LsbBitReader reader) { + var first = reader.Read(1); + var second = reader.Read(1); + return (first, second) switch { + (0, 0) => Match(reader.Read(6)), + (1, 0) => new((byte)(0x80 | reader.Read(7)), 0), + (0, 1) => new((byte)reader.Read(7), 0), + _ => reader.Read(1) == 0 ? Match(64 + reader.Read(8)) : Match(320 + reader.Read(12)), + }; + } + + private static Token ReadJmToken(ref LsbBitReader reader) { + if (reader.Read(1) == 0) return new((byte)reader.Read(7), 0); + if (reader.Read(1) == 1) return new((byte)(0x80 | reader.Read(7)), 0); + if (reader.Read(1) == 0) return Match(reader.Read(6)); + return reader.Read(1) == 0 ? Match(64 + reader.Read(8)) : Match(320 + reader.Read(12)); + } + + // Distance 0 cannot be a literal here: flag it with a non-zero length so the caller rejects it. + private static Token Match(int distance) => new(0, distance, distance == 0 ? 1 : 0); + + /// Reads z zeros, a one, then z bits: 2 + 2^z + v; -1 for nine zeros. + private static int ReadLengthCode(ref LsbBitReader reader) { + var prefix = 0; + while (reader.Read(1) == 0) { + if (++prefix > MaxLengthPrefix) return -1; + if (reader.Overran) return 0; + } + return 2 + (1 << prefix) + reader.Read(prefix); + } + + // ── encoding ──────────────────────────────────────────────────────────── + + /// + /// Encodes as a token stream with a sync marker after every 512 decoded + /// bytes and after the last one, appended to . As in the streams the dmsdos + /// compressors write, no match runs across a 512-byte boundary (distances still reach back across + /// them), so every sector of the cluster ends on a sync. + /// + public static void Encode(ReadOnlySpan data, LsbBitWriter writer, MsdblDialect dialect, int level) { + // A DS match copies one byte less than its length code, so its shortest copy is two bytes and its + // longest 512; the finder reports matches of three bytes and more. + var maxCopy = dialect == MsdblDialect.Ds ? MaxLengthCode - 1 : MaxLengthCode; + var copyBias = dialect == MsdblDialect.Ds ? 1 : 0; + var bytes = data.ToArray(); + var finder = new ClusterMatchFinder(bytes, MaxDistance, maxCopy, level); + + var sectorStart = 0; + do { + var sectorEnd = Math.Min(sectorStart + SyncInterval, bytes.Length); + var position = sectorStart; + var pending = finder.Find(position, sectorEnd); + while (position < sectorEnd) { + var (length, distance) = pending; + if (length == 0) { + WriteLiteral(writer, dialect, bytes[position]); + pending = ++position < sectorEnd ? finder.Find(position, sectorEnd) : default; + continue; + } + + // One step of lazy evaluation: a longer match starting at the next byte wins over this one. + var next = position + 1 < sectorEnd ? finder.Find(position + 1, sectorEnd) : default; + if (next.Length > length) { + WriteLiteral(writer, dialect, bytes[position]); + ++position; + pending = next; + continue; + } + + WriteMatch(writer, dialect, distance, length + copyBias); + position += length; + pending = position < sectorEnd ? finder.Find(position, sectorEnd) : default; + } + WriteSync(writer, dialect); + sectorStart = sectorEnd; + } while (sectorStart < bytes.Length); + } + + private static void WriteLiteral(LsbBitWriter writer, MsdblDialect dialect, byte value) { + var high = value >= 0x80; + if (dialect == MsdblDialect.Ds) + writer.Write(high ? 0b01 : 0b10, 2); // read order: "1 0" high, "0 1" low + else if (high) + writer.Write(0b11, 2); + else + writer.Write(0, 1); + writer.Write(value & 0x7F, 7); + } + + private static void WriteMatch(LsbBitWriter writer, MsdblDialect dialect, int distance, int lengthCode) { + WriteDistance(writer, dialect, distance); + var prefix = 31 - int.LeadingZeroCount(lengthCode - 2); + writer.Write(1 << prefix, prefix + 1); // `prefix` zeros, then a one + writer.Write(lengthCode - 2 - (1 << prefix), prefix); + } + + private static void WriteSync(LsbBitWriter writer, MsdblDialect dialect) => WriteDistance(writer, dialect, SyncDistance); + + private static void WriteDistance(LsbBitWriter writer, MsdblDialect dialect, int distance) { + if (dialect == MsdblDialect.Ds) { + switch (distance) { + case < 64: writer.Write(0b00, 2); writer.Write(distance, 6); break; + case < 320: writer.Write(0b011, 3); writer.Write(distance - 64, 8); break; + default: writer.Write(0b111, 3); writer.Write(distance - 320, 12); break; + } + return; + } + + writer.Write(0b01, 2); // read order: "1 0" + switch (distance) { + case < 64: writer.Write(0, 1); writer.Write(distance, 6); break; + case < 320: writer.Write(0b01, 2); writer.Write(distance - 64, 8); break; + default: writer.Write(0b11, 2); writer.Write(distance - 320, 12); break; + } + } +} + +/// Reads bits LSB-first from consecutive bytes; past the end it yields zeros and records the overrun. +internal ref struct LsbBitReader { + private readonly ReadOnlySpan _data; + private int _next; + private ulong _buffer; + private int _count; + + public LsbBitReader(ReadOnlySpan data) => this._data = data; + + /// True once a read has needed bits beyond the end of the data. + public readonly bool Overran => (long)this._next * 8 - this._count > (long)this._data.Length * 8; + + /// True when every bit not yet read is zero. + public readonly bool RestIsZero => this._buffer == 0 && (this._next >= this._data.Length || !this._data[this._next..].ContainsAnyExcept((byte)0)); + + /// Reads (at most 24) bits, the first read becoming the least significant. + public int Read(int count) { + while (this._count < count) { + if (this._next < this._data.Length) this._buffer |= (ulong)this._data[this._next] << this._count; + ++this._next; + this._count += 8; + } + var value = (int)(this._buffer & ((1UL << count) - 1)); + this._buffer >>= count; + this._count -= count; + return value; + } +} + +/// Writes bits LSB-first into consecutive bytes. +internal sealed class LsbBitWriter { + private byte[] _bytes = new byte[256]; + private int _length; + private ulong _accumulator; + private int _count; + + /// Appends a whole byte; only valid at a byte boundary. + public void WriteByte(byte value) => this.Write(value, 8); + + /// Appends the low (at most 32) bits of , least significant first. + public void Write(int value, int count) { + this._accumulator |= ((ulong)(uint)value & ((1UL << count) - 1)) << this._count; + this._count += count; + while (this._count >= 8) { + this.Push((byte)this._accumulator); + this._accumulator >>= 8; + this._count -= 8; + } + } + + /// Flushes the partial byte and pads with zero bytes to a multiple of . + public byte[] ToArray(int alignment) { + if (this._count > 0) this.Write(0, 8 - this._count); + while (this._length % alignment != 0) this.Push(0); + return this._bytes.AsSpan(0, this._length).ToArray(); + } + + private void Push(byte value) { + if (this._length == this._bytes.Length) Array.Resize(ref this._bytes, this._bytes.Length * 2); + this._bytes[this._length++] = value; + } +} diff --git a/Compression.Core/FileSystems/Cvf/PackageMerge.cs b/Compression.Core/FileSystems/Cvf/PackageMerge.cs new file mode 100644 index 000000000..1074b827c --- /dev/null +++ b/Compression.Core/FileSystems/Cvf/PackageMerge.cs @@ -0,0 +1,65 @@ +namespace Compression.Core.FileSystems.Cvf; + +/// +/// Optimal length-limited prefix code lengths by the package-merge algorithm (Larmore and Hirschberg, +/// "A fast algorithm for optimal length-limited Huffman codes", JACM 37(3), 1990). The result is +/// always a complete code — Kraft sum exactly one — which SD-4 requires of both its codes. +/// +internal static class PackageMerge { + /// + /// Code lengths for (zero frequency = unused, length 0), none longer + /// than . + /// + /// Fewer than two used symbols, or more than 2^maxLength. + public static int[] Lengths(ReadOnlySpan frequencies, int maxLength) { + var lengths = new int[frequencies.Length]; + var leaves = new List(); + for (var s = 0; s < frequencies.Length; ++s) + if (frequencies[s] > 0) leaves.Add(s); + if (leaves.Count < 2 || leaves.Count > 1L << maxLength) + throw new ArgumentException($"package-merge needs 2..2^{maxLength} used symbols, got {leaves.Count}.", nameof(frequencies)); + + var freq = frequencies.ToArray(); + leaves.Sort((a, b) => freq[a] != freq[b] ? freq[a].CompareTo(freq[b]) : a.CompareTo(b)); + + // Nodes: a leaf (Symbol >= 0) or a package of two nodes from the previous level. + var nodes = new List<(long Weight, int Symbol, int Left, int Right)>(); + var leafNodes = new List(leaves.Count); + foreach (var s in leaves) { + leafNodes.Add(nodes.Count); + nodes.Add((freq[s], s, -1, -1)); + } + + var level = leafNodes; + for (var depth = 1; depth < maxLength; ++depth) { + var packages = new List(level.Count / 2); + for (var i = 0; i + 1 < level.Count; i += 2) { + packages.Add(nodes.Count); + nodes.Add((nodes[level[i]].Weight + nodes[level[i + 1]].Weight, -1, level[i], level[i + 1])); + } + + // Merge the packages into the leaves by weight; leaves first on ties. + var merged = new List(leafNodes.Count + packages.Count); + int a = 0, b = 0; + while (a < leafNodes.Count || b < packages.Count) + merged.Add(b >= packages.Count || (a < leafNodes.Count && nodes[leafNodes[a]].Weight <= nodes[packages[b]].Weight) + ? leafNodes[a++] + : packages[b++]); + level = merged; + } + + // Each appearance of a leaf among the first 2n - 2 items adds one bit to its code. + var stack = new Stack(); + for (var i = 0; i < 2 * leaves.Count - 2; ++i) stack.Push(level[i]); + while (stack.Count > 0) { + var node = nodes[stack.Pop()]; + if (node.Symbol >= 0) { + ++lengths[node.Symbol]; + continue; + } + stack.Push(node.Left); + stack.Push(node.Right); + } + return lengths; + } +} diff --git a/Compression.Core/FileSystems/Cvf/Sd4Codec.cs b/Compression.Core/FileSystems/Cvf/Sd4Codec.cs index 394d2c0d9..58c188081 100644 --- a/Compression.Core/FileSystems/Cvf/Sd4Codec.cs +++ b/Compression.Core/FileSystems/Cvf/Sd4Codec.cs @@ -1,315 +1,418 @@ -#pragma warning disable CS1591 -using System; -using System.IO; - namespace Compression.Core.FileSystems.Cvf; /// -/// Genuine Stacker 4 (SD-4, cluster header 0x0081) per-cluster codec, as -/// specified in docs/CVF-CODECS-ON-DISK.md. SD-4 is Huffman-coded LZ77: a -/// helper code (22 symbols: lengths 0..15 and six run tokens) transmits the 0x150 -/// code lengths of the main code, whose symbols are the 256 literals, 63 short-match -/// tokens, 16 long-match tokens and the end token. -/// -/// reads the complete format — run tokens, short and long -/// matches with length extensions — and is pinned to streams the dmsdos SD-4 -/// encoder wrote. emits an all-literals stream (main code -/// over the cluster's byte frequencies, no match tokens), which the dmsdos driver -/// reads back byte-exact. The bitstream is MSB-first within little-endian 16-bit -/// words; codes are canonical and MSB-first. -/// +/// Stacker 4 (SD-4, cluster header 0x0081) per-cluster codec, implemented from +/// docs/CVF-CODECS-ON-DISK.md. SD-4 is Huffman-coded LZ77: a helper code (code lengths 0..15 +/// and six run tokens) transmits the 336 code lengths of the main code, whose symbols are the 256 +/// literals, 63 short-match tokens, 16 long-match tokens and the end token. Bits are read MSB-first +/// from little-endian 16-bit words; both codes are canonical and complete. /// public static class Sd4Codec { - private const int MaxLen = 10; // keep table2 codes within the decoder's fast table (bn<=10) - - // ── MSB-first bit writer into 16-bit little-endian words ─────────────────── - private sealed class BitWriter { - private readonly System.Collections.Generic.List _words = []; - private uint _cur; - private int _bits; - public void Put(int value, int n) { - for (var i = n - 1; i >= 0; i--) { - this._cur = (this._cur << 1) | (uint)((value >> i) & 1); - if (++this._bits == 16) { this._words.Add((ushort)this._cur); this._cur = 0; this._bits = 0; } - } - } - public byte[] ToBytes() { - if (this._bits > 0) { this._cur <<= 16 - this._bits; this._words.Add((ushort)this._cur); } - var b = new byte[this._words.Count * 2]; - for (var i = 0; i < this._words.Count; i++) { b[i * 2] = (byte)this._words[i]; b[i * 2 + 1] = (byte)(this._words[i] >> 8); } - return b; - } - } - - // ── MSB-first bit reader (mirror of sd4b_rd*) ────────────────────────────── - private sealed class BitReader { - private readonly byte[] _d; private readonly int _len; private long _buf; private int _pb; private int _pd; private readonly int _pe; - public BitReader(byte[] d, int len) { this._d = d; this._len = len; this._pe = (len + 1) >> 1; } - private ushort Word(int i) { var o = i * 2; var lo = o < this._len ? this._d[o] : 0; var hi = o + 1 < this._len ? this._d[o + 1] : 0; return (ushort)(lo | (hi << 8)); } - public int ReadN(int n) { - if (this._pb < n) { this._buf = (this._buf << 16) | (this._pd < this._pe ? Word(this._pd++) : 0u); this._pb += 16; if (this._pb < n) { this._buf = (this._buf << 16) | (this._pd < this._pe ? Word(this._pd++) : 0u); this._pb += 16; } } - this._pb -= n; - return (int)((this._buf >> this._pb) & ((1u << n) - 1)); - } - } - - // ── canonical Huffman: lengths -> (code,len) per symbol, matching sd4b_rdhufi - private static (int[] Code, int[] Len) Canonical(byte[] lengths) { - var cdLn = new int[17]; - foreach (var l in lengths) if (l is > 0 and < 16) cdLn[l]++; - var firstCode = new int[17]; - int us = 0; - for (var i = 1; i <= 16; i++) { firstCode[i] = us; us += cdLn[i]; us <<= 1; } - var next = (int[])firstCode.Clone(); - var code = new int[lengths.Length]; - var len = new int[lengths.Length]; - for (var s = 0; s < lengths.Length; s++) { var l = lengths[s]; if (l == 0) continue; code[s] = next[l]++; len[s] = l; } - return (code, len); - } - - // ── length-limited Huffman code lengths from frequencies ─────────────────── - private static byte[] HuffmanLengths(int[] freq, int maxLen) { - var n = freq.Length; - var lengths = new byte[n]; - var nodes = new System.Collections.Generic.List<(long F, int Sym, int Left, int Right)>(); - for (var s = 0; s < n; s++) if (freq[s] > 0) nodes.Add((freq[s], s, -1, -1)); - if (nodes.Count == 0) return lengths; - if (nodes.Count == 1) { lengths[nodes[0].Sym] = 1; FixKraft(lengths, maxLen); return lengths; } - - var all = new System.Collections.Generic.List<(long F, int Sym, int Left, int Right)>(nodes); - var live = new System.Collections.Generic.List(); - for (var i = 0; i < all.Count; i++) live.Add(i); - while (live.Count > 1) { - live.Sort((a, b) => all[a].F.CompareTo(all[b].F)); - var a = live[0]; var b = live[1]; - live.RemoveRange(0, 2); - all.Add((all[a].F + all[b].F, -1, a, b)); - live.Add(all.Count - 1); - } - AssignDepth(all, live[0], 0, lengths); - FixKraft(lengths, maxLen); - return lengths; - } - - private static void AssignDepth(System.Collections.Generic.List<(long F, int Sym, int Left, int Right)> all, int node, int depth, byte[] lengths) { - var nd = all[node]; - if (nd.Sym >= 0) { lengths[nd.Sym] = (byte)Math.Max(1, depth); return; } - AssignDepth(all, nd.Left, depth + 1, lengths); - AssignDepth(all, nd.Right, depth + 1, lengths); - } - - // Clamp to maxLen and repair the Kraft sum so the code is complete. - private static void FixKraft(byte[] lengths, int maxLen) { - for (var i = 0; i < lengths.Length; i++) if (lengths[i] > maxLen) lengths[i] = (byte)maxLen; - // Kraft sum in units of 2^-maxLen. - long total = 0; - foreach (var l in lengths) if (l > 0) total += 1L << (maxLen - l); - var full = 1L << maxLen; - // Over-full: lengthen some short codes. - while (total > full) { - for (var i = 0; i < lengths.Length && total > full; i++) { - if (lengths[i] > 0 && lengths[i] < maxLen) { total -= 1L << (maxLen - lengths[i]); lengths[i]++; total += 1L << (maxLen - lengths[i]); } - } - } - // Under-full: shorten codes to fill the space (keeps it complete). - var guard = 0; - while (total < full && guard++ < 100000) { - for (var i = 0; i < lengths.Length && total < full; i++) { - if (lengths[i] > 1) { - var delta = 1L << (maxLen - lengths[i]); - if (total + delta <= full) { total += delta; lengths[i]--; } // shortening adds delta again - } - } - if (guard > 99999) break; - } - } + private const int Signature = 0x0081; + private const int HelperSymbols = 22; // code lengths 0..15, run tokens 16..21 + private const int MainSymbols = 0x150; // literals 0..255, tokens 1..80 + private const int MaxCodeLength = 15; + private const int FirstToken = 0x100 - 1; // main symbol of token t is FirstToken + t + private const int FirstLongToken = 64; + private const int EndToken = 80; + private const int ShortMaxLength = 5; + private const int LongMinLength = 6; + private const int LongTokenMaxLength = 21; + private const int MaxMatchLength = LongTokenMaxLength + 0xF + 0xFF + 0xFFF + 0xFFFF; // every extension saturated - public static byte[] Encode(ReadOnlySpan data) { - // table2: Huffman over 0x150 symbols — 256 literals + the end token 0x14F - // (sd4_decomp reads an explicit end token when the output fills). A complete - // code needs >= 2 symbols, so empty/uniform clusters get a nominal literal. - var freq = new int[0x150]; - foreach (var b in data) freq[b]++; - freq[0x14F] = Math.Max(1, freq[0x14F]); // end symbol always present - var nz = 0; foreach (var f in freq) if (f > 0) nz++; - for (var i = 0; i < 256 && nz < 2; i++) if (freq[i] == 0) { freq[i] = 1; nz++; } - var clen = HuffmanLengths(freq, MaxLen); // 0x150 code-lengths - var bmax2 = 0; foreach (var l in clen) if (l > bmax2) bmax2 = l; - if (bmax2 == 0) bmax2 = 1; - - // table1: Huffman over the frequency of each length-VALUE (0..bmax2) in clen. - var lvFreq = new int[0x16]; - foreach (var v in clen) lvFreq[v]++; - var l1 = HuffmanLengths(lvFreq, 7); // table1 bn capped at 7 by the decoder - var bmax1 = 0; foreach (var l in l1) if (l > bmax1) bmax1 = l; - if (bmax1 == 0) bmax1 = 1; - - var (t1code, t1len) = Canonical(l1); - var (t2code, t2len) = Canonical(clen); - - var w = new BitWriter(); - w.Put(0x81, 16); // SD-4 magic - w.Put(0, 3); // start = rdn(3)+1 = 1 - w.Put(bmax2, 5); // ie = bmax2 - w.Put(l1[0], 4); // ca[0] = bmax1 field (length-value 0) - for (var i = 1; i <= bmax2; i++) w.Put(l1[i], 4); // ca[1..bmax2] - for (var i = 0x10; i <= 0x15; i++) w.Put(0, 4); // RLE tokens unused - // emit the 0x150 code-lengths as table1 length-value codes. - foreach (var v in clen) w.Put(t1code[v], t1len[v]); - // emit data as table2 literal codes, then the end token (read by sd4_decomp - // via len_150 bits once the output is full). - foreach (var b in data) w.Put(t2code[b], t2len[b]); - w.Put(t2code[0x14F], t2len[0x14F]); - return w.ToBytes(); - } - - // ── decoding (docs/CVF-CODECS-ON-DISK.md, "SD-4") ────────────────────────── - - private const int HelperSymbols = 0x16; // code lengths 0..15 + run tokens 1..6 - private const int MainSymbols = 0x150; // literals 0..255 + tokens 1..0x50 - private const int EndToken = 0x50; - private const int FirstLongToken = 0x40; - - // Short-match classes: token t (1..0x3F) is class (t-1)/3 with length 3 + (t-1)%3; the offset is - // ShortBase[class] plus ShortBits[class] extra bits. - private static readonly int[] ShortBits = [0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]; + // Short-match distance classes (token t = 1..63: class (t-1)/3, length 3 + (t-1)%3). private static readonly int[] ShortBase = [1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536]; + private static readonly int[] ShortBits = [0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]; - // Long-match offset classes, chosen by a 2-bit selector. + // Long-match distance classes, picked by a 2-bit selector. + private static readonly int[] LongBase = [1, 33, 161, 673]; private static readonly int[] LongBits = [5, 7, 9, 11]; - private static readonly int[] LongBase = [0x1, 0x21, 0xA1, 0x2A1]; - // Escalating length extensions of a long match whose token says 21. + // A long match whose token says 21 carries these extensions, each taken only while the previous one saturated. private static readonly int[] LengthExtensionBits = [4, 8, 12, 16]; + private static readonly int ShortMaxDistance = ShortBase[^1] + (1 << ShortBits[^1]) - 1; // 2047 + private static readonly int LongMaxDistance = LongBase[^1] + (1 << LongBits[^1]) - 1; // 2720 + + // ── decoding ──────────────────────────────────────────────────────────── + /// /// Decodes one SD-4 cluster payload into bytes. A stream that ends (end /// token) before the cluster is full leaves the tail zero. /// - /// Bad magic, an inconsistent code table, or a match that reaches before the cluster start. + /// Bad signature, an inconsistent code table, or a match that reaches before the cluster start. public static byte[] Decode(byte[] payload, int inLen, int outLen) { - var r = new BitReader(payload, inLen); - if (r.ReadN(16) != 0x81) throw new InvalidDataException("SD-4: bad magic."); - - // Helper-table code lengths: symbol 0, symbols first..last, then the six run tokens. - var first = r.ReadN(3) + 1; - var last = r.ReadN(5); - if (first > last || last > 15) throw new InvalidDataException("SD-4: helper table range is invalid."); - var helperLengths = new byte[HelperSymbols]; - helperLengths[0] = (byte)r.ReadN(4); - for (var i = first; i <= last; ++i) helperLengths[i] = (byte)r.ReadN(4); - for (var i = 0x10; i < HelperSymbols; ++i) helperLengths[i] = (byte)r.ReadN(4); - var helper = new CanonicalDecoder(helperLengths); - - var mainLengths = new byte[MainSymbols]; - for (var n = 0; n < MainSymbols;) { - var symbol = helper.Decode(r); - if (symbol < 0x10) { mainLengths[n++] = (byte)symbol; continue; } - if (n == 0 && symbol >= 0x13) throw new InvalidDataException("SD-4: length repeat with no previous length."); - var (value, count) = (symbol - 0x0F) switch { - 1 => ((byte)0, 2), - 2 => ((byte)0, 3), - 3 => ((byte)0, ZeroRunLength(r)), - 4 => (mainLengths[n - 1], 2), - 5 => (mainLengths[n - 1], 3), - _ => (mainLengths[n - 1], RepeatRunLength(r)), - }; - count = Math.Min(count, MainSymbols - n); - mainLengths.AsSpan(n, count).Fill(value); - n += count; - } - var main = new CanonicalDecoder(mainLengths); + var reader = new MsbWordReader(payload.AsSpan(0, Math.Min(inLen, payload.Length))); + if (reader.Read(16) != Signature) throw new InvalidDataException("SD-4: bad signature."); + var main = new CanonicalDecoder(ReadMainLengths(ref reader)); var output = new byte[outLen]; - var p = 0; - while (p < outLen) { - var symbol = main.Decode(r); - if (symbol < 0x100) { output[p++] = (byte)symbol; continue; } + var position = 0; + while (position < outLen) { + var symbol = main.Decode(ref reader); + if (symbol <= 0xFF) { + output[position++] = (byte)symbol; + continue; + } - var token = symbol - 0xFF; + var token = symbol - FirstToken; if (token == EndToken) break; - int length, offset; + int length, distance; if (token < FirstLongToken) { var cls = (token - 1) / 3; length = 3 + (token - 1) % 3; - offset = ShortBase[cls] + r.ReadN(ShortBits[cls]); + distance = ShortBase[cls] + reader.Read(ShortBits[cls]); } else { - var cls = r.ReadN(2); - offset = LongBase[cls] + r.ReadN(LongBits[cls]); - length = token - FirstLongToken + 6; - var saturated = 21; + var selector = reader.Read(2); + distance = LongBase[selector] + reader.Read(LongBits[selector]); + length = token - 58; + var saturated = LongTokenMaxLength; foreach (var bits in LengthExtensionBits) { if (length != saturated) break; - var extra = r.ReadN(bits); - length += extra; + length += reader.Read(bits); saturated += (1 << bits) - 1; } } - if (offset > p) throw new InvalidDataException("SD-4: match reaches before the start of the cluster."); - length = Math.Min(length, outLen - p); // the reference driver clips an overlong final match - for (var k = 0; k < length; ++k, ++p) output[p] = output[p - offset]; + if (distance > position) throw new InvalidDataException("SD-4: match reaches before the start of the cluster."); + length = Math.Min(length, outLen - position); + for (var end = position + length; position < end; ++position) + output[position] = output[position - distance]; } return output; } - // Run token 3: 4 + 3 bits; a saturated 7 continues with 7-bit groups while they read 0x7F. - private static int ZeroRunLength(BitReader r) { - var v = r.ReadN(3); + private static byte[] ReadMainLengths(ref MsbWordReader reader) { + var first = reader.Read(3) + 1; + var last = reader.Read(5); + if (first > last || last > MaxCodeLength) throw new InvalidDataException("SD-4: helper length range is invalid."); + + var helperLengths = new byte[HelperSymbols]; + helperLengths[0] = (byte)reader.Read(4); + for (var i = first; i <= last; ++i) helperLengths[i] = (byte)reader.Read(4); + for (var i = 16; i < HelperSymbols; ++i) helperLengths[i] = (byte)reader.Read(4); + var helper = new CanonicalDecoder(helperLengths); + + var lengths = new byte[MainSymbols]; + for (var n = 0; n < MainSymbols;) { + var symbol = helper.Decode(ref reader); + if (symbol < 16) { + lengths[n++] = (byte)symbol; + continue; + } + if (symbol >= 19 && n == 0) throw new InvalidDataException("SD-4: length repeat with no previous length."); + var (value, count) = symbol switch { + 16 => ((byte)0, 2), + 17 => ((byte)0, 3), + 18 => ((byte)0, ReadZeroRun(ref reader)), + 19 => (lengths[n - 1], 2), + 20 => (lengths[n - 1], 3), + _ => (lengths[n - 1], ReadRepeatRun(ref reader)), + }; + count = Math.Min(count, MainSymbols - n); + lengths.AsSpan(n, count).Fill(value); + n += count; + } + return lengths; + } + + // Helper symbol 18: 4 + 3 bits; a 7 continues with 7-bit groups while they read 0x7F. + private static int ReadZeroRun(ref MsbWordReader reader) { + var v = reader.Read(3); var n = 4 + v; if (v == 7) - do { v = r.ReadN(7); n += v; } while (v == 0x7F); + do n += v = reader.Read(7); while (v == 0x7F); return n; } - // Run token 6: 4 + 3-bit groups, continuing while a group reads 7. - private static int RepeatRunLength(BitReader r) { + // Helper symbol 21: 4 + 3-bit groups, continuing while a group reads 7. + private static int ReadRepeatRun(ref MsbWordReader reader) { var n = 4; int v; - do { v = r.ReadN(3); n += v; } while (v == 7); + do n += v = reader.Read(3); while (v == 7); return n; } + // ── encoding ──────────────────────────────────────────────────────────── + + /// Encodes one cluster with back-references, Huffman-coded behind a run-length coded code table. + /// The cluster. + /// 1 (fastest) and up; deeper match search at higher levels. + public static byte[] Encode(ReadOnlySpan data, int level = 2) + => Encode(data, level, matches: true); + + /// Encodes with back-references switched off when is false — a + /// literals-only stream whose code depth follows the byte statistics alone (used to probe decoders). + internal static byte[] Encode(ReadOnlySpan data, int level, bool matches) { + var symbols = matches ? Parse(data, level) : LiteralsOnly(data); + + var mainFrequencies = new long[MainSymbols]; + foreach (var s in symbols) ++mainFrequencies[s.Symbol]; + EnsureTwoSymbols(mainFrequencies, 0); + var mainLengths = PackageMerge.Lengths(mainFrequencies, MaxCodeLength); + + var runs = RunLengthCode(mainLengths); + var helperFrequencies = new long[HelperSymbols]; + foreach (var r in runs) ++helperFrequencies[r.Symbol]; + EnsureTwoSymbols(helperFrequencies, helperFrequencies[0] == 0 ? 0 : 16); + var helperLengths = PackageMerge.Lengths(helperFrequencies, MaxCodeLength); + + var writer = new MsbWordWriter(); + writer.Write(Signature, 16); + WriteHelperHeader(writer, mainLengths, helperLengths); + + WriteSymbols(writer, runs, helperLengths); + WriteSymbols(writer, symbols, mainLengths); + return writer.ToArray(); + } + + /// A symbol of the main or helper code followed by its extra-bit fields, in stream order. + private readonly record struct CodedSymbol(int Symbol, params (int Value, int Bits)[] Extras); + + private static void WriteSymbols(MsbWordWriter writer, List symbols, int[] lengths) { + var codes = CanonicalCodes(lengths); + foreach (var s in symbols) { + writer.Write(codes[s.Symbol], lengths[s.Symbol]); + foreach (var (value, bits) in s.Extras) writer.Write(value, bits); + } + } + + private static List LiteralsOnly(ReadOnlySpan data) { + var symbols = new List(data.Length + 1); + foreach (var b in data) symbols.Add(new(b)); + symbols.Add(new(FirstToken + EndToken)); + return symbols; + } + + private static List Parse(ReadOnlySpan data, int level) { + var bytes = data.ToArray(); + var finder = new ClusterMatchFinder(bytes, LongMaxDistance, MaxMatchLength, level); + var symbols = new List(bytes.Length / 2 + 2); + + var position = 0; + var pending = Usable(finder.Find(0)); + while (position < bytes.Length) { + if (pending.Length == 0) { + symbols.Add(new(bytes[position], [])); + pending = Usable(finder.Find(++position)); + continue; + } + + var next = position + 1 < bytes.Length ? Usable(finder.Find(position + 1)) : (Length: 0, Distance: 0); + if (next.Length > pending.Length) { + symbols.Add(new(bytes[position], [])); + ++position; + pending = next; + continue; + } + + symbols.Add(MatchSymbol(pending.Length, pending.Distance)); + position += pending.Length; + pending = position < bytes.Length ? Usable(finder.Find(position)) : (Length: 0, Distance: 0); + } + + symbols.Add(new(FirstToken + EndToken, [])); + return symbols; + } + + // Lengths 3..5 only reach as far as the short classes; everything longer may use the long ones. + private static (int Length, int Distance) Usable((int Length, int Distance) match) + => match.Length is > 0 and <= ShortMaxLength && match.Distance > ShortMaxDistance ? (0, 0) : match; + + private static CodedSymbol MatchSymbol(int length, int distance) { + if (length <= ShortMaxLength) { + var cls = ClassOf(ShortBase, ShortBits, distance); + var token = 1 + cls * 3 + (length - 3); + return new(FirstToken + token, [(distance - ShortBase[cls], ShortBits[cls])]); + } + + var selector = ClassOf(LongBase, LongBits, distance); + var extras = new List<(int, int)> { (selector, 2), (distance - LongBase[selector], LongBits[selector]) }; + var longToken = FirstLongToken + Math.Min(length, LongTokenMaxLength) - LongMinLength; + var remaining = length - LongTokenMaxLength; + foreach (var bits in LengthExtensionBits) { + if (remaining < 0) break; + var max = (1 << bits) - 1; + extras.Add((Math.Min(remaining, max), bits)); + remaining -= max; // continues only when this extension saturated + } + return new(FirstToken + longToken, [.. extras]); + } + + private static int ClassOf(int[] bases, int[] bits, int distance) { + for (var c = bases.Length - 1; c >= 0; --c) + if (distance >= bases[c]) + return distance < bases[c] + (1 << bits[c]) ? c : throw new ArgumentOutOfRangeException(nameof(distance)); + throw new ArgumentOutOfRangeException(nameof(distance)); + } + + // A complete prefix code needs at least two symbols; give a spare one a nominal count. + private static void EnsureTwoSymbols(long[] frequencies, int spare) { + var used = frequencies.Count(f => f > 0); + for (var s = spare; used < 2 && s < frequencies.Length; ++s) + if (frequencies[s] == 0) { frequencies[s] = 1; ++used; } + } + /// - /// Canonical MSB-first prefix code: codes ascend with length and, within a length, with symbol - /// number. The code must be complete (Kraft sum exactly one) with lengths of at most 15 bits. + /// Transmits the main code lengths as helper symbols: a length as itself, runs of zeros as tokens + /// 16/17/18, repeats of the previous length as tokens 19/20/21. + /// + private static List RunLengthCode(int[] lengths) { + var runs = new List(); + for (var i = 0; i < lengths.Length;) { + var value = lengths[i]; + var run = 1; + while (i + run < lengths.Length && lengths[i + run] == value) ++run; + i += run; + + if (value == 0) { + runs.Add(run switch { + 1 => new(0), + 2 => new(16), + 3 => new(17), + < 11 => new(18, (run - 4, 3)), + _ => new(18, [(7, 3), .. Groups(run - 11, 7)]), + }); + continue; + } + + runs.Add(new(value)); + var repeats = run - 1; + switch (repeats) { + case 0: break; + case 1: runs.Add(new(value)); break; + case 2: runs.Add(new(19)); break; + case 3: runs.Add(new(20)); break; + default: runs.Add(new(21, [.. Groups(repeats - 4, 3)])); break; + } + } + return runs; + } + + /// + /// Splits into -bit groups where every group but the + /// last is saturated (all ones) and the last is not, which is how the decoder knows to stop. + /// + private static IEnumerable<(int Value, int Bits)> Groups(int total, int bits) { + var saturated = (1 << bits) - 1; + for (; total >= saturated; total -= saturated) yield return (saturated, bits); + yield return (total, bits); + } + + + private static void WriteHelperHeader(MsbWordWriter writer, int[] mainLengths, int[] helperLengths) { + int shortest = MaxCodeLength, longest = 1; + foreach (var l in mainLengths) { + if (l == 0) continue; + shortest = Math.Min(shortest, l); + longest = Math.Max(longest, l); + } + var first = Math.Min(shortest, 8); + writer.Write(first - 1, 3); + writer.Write(longest, 5); + writer.Write(helperLengths[0], 4); + for (var i = first; i <= longest; ++i) writer.Write(helperLengths[i], 4); + for (var i = 16; i < HelperSymbols; ++i) writer.Write(helperLengths[i], 4); + } + + /// Canonical codes: ascending by length, then by symbol, each the previous plus one. + private static int[] CanonicalCodes(int[] lengths) { + var perLength = new int[MaxCodeLength + 1]; + foreach (var l in lengths) ++perLength[l]; + perLength[0] = 0; + var next = new int[MaxCodeLength + 2]; + for (var len = 1; len <= MaxCodeLength; ++len) next[len + 1] = (next[len] + perLength[len]) << 1; + var codes = new int[lengths.Length]; + for (var s = 0; s < lengths.Length; ++s) + if (lengths[s] != 0) codes[s] = next[lengths[s]]++; + return codes; + } + + /// + /// Canonical MSB-first prefix code decoder. The code must be complete (Kraft sum exactly one) with + /// lengths of at most 15 bits. /// private sealed class CanonicalDecoder { - private readonly int[] _count = new int[16]; + private readonly int[] _perLength = new int[MaxCodeLength + 1]; private readonly int[] _symbols; public CanonicalDecoder(ReadOnlySpan lengths) { foreach (var l in lengths) { - if (l > 15) throw new InvalidDataException("SD-4: code length above 15."); - ++this._count[l]; + if (l > MaxCodeLength) throw new InvalidDataException("SD-4: code length above 15."); + ++this._perLength[l]; } - this._count[0] = 0; - var left = 1; - for (var len = 1; len <= 15; ++len) { - left = (left << 1) - this._count[len]; - if (left < 0) throw new InvalidDataException("SD-4: over-subscribed code table."); + this._perLength[0] = 0; + var free = 1; + for (var len = 1; len <= MaxCodeLength; ++len) { + free = (free << 1) - this._perLength[len]; + if (free < 0) throw new InvalidDataException("SD-4: over-subscribed code table."); } - if (left != 0) throw new InvalidDataException("SD-4: incomplete code table."); + if (free != 0) throw new InvalidDataException("SD-4: incomplete code table."); - var offsets = new int[16]; - for (var len = 1; len < 15; ++len) offsets[len + 1] = offsets[len] + this._count[len]; - this._symbols = new int[offsets[15] + this._count[15]]; + var offsets = new int[MaxCodeLength + 2]; + for (var len = 1; len <= MaxCodeLength; ++len) offsets[len + 1] = offsets[len] + this._perLength[len]; + this._symbols = new int[offsets[MaxCodeLength + 1]]; for (var s = 0; s < lengths.Length; ++s) if (lengths[s] != 0) this._symbols[offsets[lengths[s]]++] = s; } - public int Decode(BitReader r) { - int code = 0, firstCode = 0, index = 0; - for (var len = 1; len <= 15; ++len) { - code |= r.ReadN(1); - var count = this._count[len]; - if (code - firstCode < count) return this._symbols[index + code - firstCode]; + public int Decode(ref MsbWordReader reader) { + int code = 0, first = 0, index = 0; + for (var len = 1; len <= MaxCodeLength; ++len) { + code |= reader.Read(1); + var count = this._perLength[len]; + if (code - first < count) return this._symbols[index + code - first]; index += count; - firstCode = (firstCode + count) << 1; + first = (first + count) << 1; code <<= 1; } throw new InvalidDataException("SD-4: undecodable bit sequence."); } } + + /// Reads bits MSB-first from little-endian 16-bit words; zeros past the end. + private ref struct MsbWordReader { + private readonly ReadOnlySpan _data; + private int _next; + private uint _buffer; + private int _count; + + public MsbWordReader(ReadOnlySpan data) => this._data = data; + + public int Read(int count) { + if (count == 0) return 0; + while (this._count < count) { + var lo = this._next < this._data.Length ? this._data[this._next] : 0; + var hi = this._next + 1 < this._data.Length ? this._data[this._next + 1] : 0; + this._next += 2; + this._buffer = this._buffer << 16 | (uint)(lo | hi << 8); + this._count += 16; + } + this._count -= count; + return (int)(this._buffer >> this._count) & ((1 << count) - 1); + } + } + + /// Writes bits MSB-first into little-endian 16-bit words. + private sealed class MsbWordWriter { + private readonly List _bytes = []; + private int _word; + private int _count; + + public void Write(int value, int count) { + for (var i = count - 1; i >= 0; --i) { + this._word = this._word << 1 | (value >> i & 1); + if (++this._count < 16) continue; + this._bytes.Add((byte)this._word); + this._bytes.Add((byte)(this._word >> 8)); + (this._word, this._count) = (0, 0); + } + } + + public byte[] ToArray() { + if (this._count > 0) this.Write(0, 16 - this._count); + return [.. this._bytes]; + } + } } diff --git a/Compression.Core/REFERENCE.md b/Compression.Core/REFERENCE.md index 4aeff3afb..86163b50a 100644 --- a/Compression.Core/REFERENCE.md +++ b/Compression.Core/REFERENCE.md @@ -3318,6 +3318,26 @@ SQX multimedia compressor/decompressor using delta coders (E0-E4) and arithmetic | `Decode` | `static byte[] Decode(byte[] compressed, int originalSize)` | Decompresses multimedia data. | | `Encode` | `static byte[] Encode(ReadOnlySpan data)` | Compresses multimedia data using automatic delta order selection + arithmetic coding. | +### Namespace `Compression.Core.Dictionary.StackerSd4` + +[`StackerSd4BuildingBlock`](#stackersd4buildingblock) + +#### `StackerSd4BuildingBlock` + +Stacker 4's SD-4 codec as a raw primitive: LZ77 over a 2720-byte window, Huffman-coded with a run-length coded code table (`docs/CVF-CODECS-ON-DISK.md`). The building-block wrapper stores the uncompressed size (32-bit little-endian) before one SD-4 stream covering the whole input. + +Implements `IBuildingBlock`. + +| Member | Signature | Summary | +| --- | --- | --- | +| `StackerSd4BuildingBlock` | `StackerSd4BuildingBlock()` | | +| `Description` | `string Description { get; }` | | +| `DisplayName` | `string DisplayName { get; }` | | +| `Family` | `AlgorithmFamily Family { get; }` | | +| `Id` | `string Id { get; }` | | +| `Compress` | `byte[] Compress(ReadOnlySpan data)` | | +| `Decompress` | `byte[] Decompress(ReadOnlySpan data)` | | + ### Namespace `Compression.Core.Dictionary.SuffixTree` [`SuffixTreeBuildingBlock`](#suffixtreebuildingblock) @@ -5627,17 +5647,17 @@ Implements `IEquatable`. #### `CvfLzCodec` -Genuine DoubleSpace/DriveSpace per-cluster compression codec (DS-0-x and JM-0-x), verified byte-exact against the independent dmsdos decoder. The bitstream packs bits LSB-first into little-endian 16-bit words; a cluster payload is a 16-bit magic (`"DS"`=0x5344 / `"MJ"`=0x4D4A) + 16-bit version + an LZ77 token stream terminated by the 0x113f sync. +Per-cluster codecs of the DoubleSpace / DriveSpace / DriveSpace 3 / Stacker compressed volumes, implemented from `docs/CVF-CODECS-ON-DISK.md`. An MSDBL cluster payload starts with a 16-bit magic and a 16-bit version — `"DS"` (DS-0-x), `"JM"` (JM-0-x) or `"SQ"` (SQ-0-0, raw DEFLATE) — followed by the token stream; a Stacker payload carries no method field and is recognised by its first word. | Member | Signature | Summary | | --- | --- | --- | -| `DS_0_0` | `const uint DS_0_0` | | -| `JM_0_0` | `const uint JM_0_0` | | -| `SQ_0_0` | `const uint SQ_0_0` | | -| `Compress` | `static byte[] Compress(ReadOnlySpan data, CvfLzMethod method, int level)` | Compresses one cluster. Returns the payload (4-byte method header + token stream, padded to a 2-byte word), or `null` if it would not be smaller than the raw cluster (caller stores raw instead). | -| `DecompressStacker` | `static byte[] DecompressStacker(byte[] payload, int inLen, int outLen)` | Decompresses a Stacker cluster payload to `outLen` bytes. Stacker records no method per cluster; the payload's first little-endian word selects it — `0x0081` SD-4, `0x5344` ("DS") the DoubleSpace codec, anything else SD-3, whose first token is always a literal and so can never begin with either word. | -| `Decompress` | `static byte[] Decompress(byte[] payload, int inLen, int outLen)` | Decompresses a cluster payload to exactly `outLen` bytes. | -| `Encode` | `static byte[] Encode(ReadOnlySpan data, CvfLzMethod method, int level)` | Encodes a cluster with the given method, always returning the payload (4-byte header + token stream), or `null` for `Stored` / unsupported methods. The caller decides whether the result fits the cluster's sector budget. | +| `DS_0_0` | `const uint DS_0_0` | Method header of a DS-0-0 payload, read as a little-endian 32-bit word. | +| `JM_0_0` | `const uint JM_0_0` | Method header of a JM-0-0 payload, read as a little-endian 32-bit word. | +| `SQ_0_0` | `const uint SQ_0_0` | Method header of an SQ-0-0 payload, read as a little-endian 32-bit word. | +| `Compress` | `static byte[] Compress(ReadOnlySpan data, CvfLzMethod method, int level)` | Compresses one cluster. Returns the payload, or `null` when it would not be smaller than the raw cluster (the caller then stores the cluster raw). | +| `DecompressStacker` | `static byte[] DecompressStacker(byte[] payload, int inLen, int outLen)` | Decompresses a Stacker cluster payload to `outLen` bytes. Stacker records no method per cluster; the payload's first little-endian word selects it — `0x0081` SD-4, `0x5344` ("DS") the DoubleSpace grammar, anything else SD-3, whose first token is always a literal and so can never begin with either word. | +| `Decompress` | `static byte[] Decompress(byte[] payload, int inLen, int outLen)` | Decompresses an MSDBL cluster payload (DS, JM or SQ, told apart by its header) to exactly `outLen` bytes. | +| `Encode` | `static byte[] Encode(ReadOnlySpan data, CvfLzMethod method, int level)` | Encodes a cluster with the given method and always returns the payload, or `null` for `Stored`. The caller decides whether the result fits the cluster's sector budget. `Auto` keeps the smaller of DS and JM. | #### `CvfLzMethod` @@ -5645,13 +5665,13 @@ Per-cluster compression methods of the DoubleSpace/DriveSpace/Stacker CVF family | Value | Numeric | Summary | | --- | --- | --- | -| `Stored` | `0` | | -| `Ds` | `1` | | -| `Jm` | `2` | | -| `Auto` | `3` | | -| `Sq` | `4` | | -| `Sd4` | `5` | | -| `Sd3` | `6` | | +| `Stored` | `0` | No compression; the cluster is stored as is. | +| `Ds` | `1` | DoubleSpace DS-0-x LZ77. | +| `Jm` | `2` | DriveSpace JM-0-x LZ77 (DriveSpace 3 Normal/High). | +| `Auto` | `3` | Per cluster the smaller of `Ds` and `Jm`, both of which every MSDBL volume reads. | +| `Sq` | `4` | DriveSpace 3 SQ-0-0 (Ultra): a raw DEFLATE stream behind the method header. | +| `Sd4` | `5` | Stacker 4 SD-4: Huffman-coded LZ77 behind the header word `0x0081`. | +| `Sd3` | `6` | Stacker 3 SD-3: a bare LZS stream closed by a check byte. | #### `Sd3Codec` @@ -5664,12 +5684,12 @@ Stacker 3 ("SD-3") per-cluster codec, as specified in `docs/CVF-CODECS-ON-DISK.m #### `Sd4Codec` -Genuine Stacker 4 (SD-4, cluster header `0x0081`) per-cluster codec, as specified in `docs/CVF-CODECS-ON-DISK.md`. SD-4 is Huffman-coded LZ77: a helper code (22 symbols: lengths 0..15 and six run tokens) transmits the 0x150 code lengths of the main code, whose symbols are the 256 literals, 63 short-match tokens, 16 long-match tokens and the end token. `Decode` reads the complete format — run tokens, short and long matches with length extensions — and is pinned to streams the dmsdos SD-4 encoder wrote. `Encode` emits an all-literals stream (main code over the cluster's byte frequencies, no match tokens), which the dmsdos driver reads back byte-exact. The bitstream is MSB-first within little-endian 16-bit words; codes are canonical and MSB-first. +Stacker 4 (SD-4, cluster header `0x0081`) per-cluster codec, implemented from `docs/CVF-CODECS-ON-DISK.md`. SD-4 is Huffman-coded LZ77: a helper code (code lengths 0..15 and six run tokens) transmits the 336 code lengths of the main code, whose symbols are the 256 literals, 63 short-match tokens, 16 long-match tokens and the end token. Bits are read MSB-first from little-endian 16-bit words; both codes are canonical and complete. | Member | Signature | Summary | | --- | --- | --- | | `Decode` | `static byte[] Decode(byte[] payload, int inLen, int outLen)` | Decodes one SD-4 cluster payload into `outLen` bytes. A stream that ends (end token) before the cluster is full leaves the tail zero. | -| `Encode` | `static byte[] Encode(ReadOnlySpan data)` | | +| `Encode` | `static byte[] Encode(ReadOnlySpan data, int level = 2)` | Encodes one cluster with back-references, Huffman-coded behind a run-length coded code table. | ### Namespace `Compression.Core.Image` diff --git a/Compression.Tests/Cvf/CvfCodecBoundaryTests.cs b/Compression.Tests/Cvf/CvfCodecBoundaryTests.cs new file mode 100644 index 000000000..ab1057dbd --- /dev/null +++ b/Compression.Tests/Cvf/CvfCodecBoundaryTests.cs @@ -0,0 +1,188 @@ +using Compression.Core.FileSystems.Cvf; + +namespace Compression.Tests.Cvf; + +/// +/// Boundary behaviour of every CVF cluster codec (docs/CVF-CODECS-ON-DISK.md): empty and one-byte +/// clusters, the three cluster sizes the volumes use, incompressible data, and the longest match length +/// and farthest distance each grammar can express — one past each must still round-trip, by falling back +/// to a shorter form. +/// +[TestFixture] +public sealed class CvfCodecBoundaryTests { + + private static readonly CvfLzMethod[] AllMethods = [CvfLzMethod.Ds, CvfLzMethod.Jm, CvfLzMethod.Sq, CvfLzMethod.Sd3, CvfLzMethod.Sd4]; + + private static bool IsStacker(CvfLzMethod method) => method is CvfLzMethod.Sd3 or CvfLzMethod.Sd4; + + private static byte[] RoundTrip(byte[] data, CvfLzMethod method, int level = 2) { + var payload = CvfLzCodec.Encode(data, method, level)!; + return IsStacker(method) + ? CvfLzCodec.DecompressStacker(payload, payload.Length, data.Length) + : CvfLzCodec.Decompress(payload, payload.Length, data.Length); + } + + private static byte[] Random(int length, int seed) { + var data = new byte[length]; + new Random(seed).NextBytes(data); + return data; + } + + [Test, Category("Codec")] + public void GivenAnEmptyCluster_WhenEncodedAndDecoded_ThenNothingComesBack([ValueSource(nameof(AllMethods))] CvfLzMethod method) + => Assert.That(RoundTrip([], method), Is.Empty); + + [Test, Category("Codec")] + public void GivenAOneByteCluster_WhenEncodedAndDecoded_ThenTheByteComesBack( + [ValueSource(nameof(AllMethods))] CvfLzMethod method, [Values(0x00, 0x7F, 0x80, 0xFF)] int value) + => Assert.That(RoundTrip([(byte)value], method), Is.EqualTo(new[] { (byte)value })); + + [Test, Category("Codec")] + public void GivenAClusterOfEachVolumeSize_WhenEncodedAndDecoded_ThenItIsByteExact( + [ValueSource(nameof(AllMethods))] CvfLzMethod method, [Values(512, 8192, 32768)] int size, + [Values("text", "zeros", "random")] string shape) { + var data = shape switch { + "zeros" => new byte[size], + "random" => Random(size, size), + _ => CvfSamples.Get("text-32k")[..size], + }; + + Assert.That(RoundTrip(data, method), Is.EqualTo(data)); + } + + [Test, Category("Codec")] + public void GivenIncompressibleData_WhenCompressed_ThenTheCallerIsToldToStoreIt([Values(CvfLzMethod.Ds, CvfLzMethod.Jm, CvfLzMethod.Sq, CvfLzMethod.Sd4)] CvfLzMethod method) + => Assert.That(CvfLzCodec.Compress(Random(8192, 99), method, 2), Is.Null); + + [Test, Category("Codec")] + public void GivenCompressibleData_WhenCompressed_ThenThePayloadIsSmaller([ValueSource(nameof(AllMethods))] CvfLzMethod method) { + var data = CvfSamples.Get("text-8k"); + + Assert.That(CvfLzCodec.Compress(data, method, 2)!.Length, Is.LessThan(data.Length / 2)); + } + + // DS copies at most 512 bytes per match, JM 513; SD-4's long matches extend at 21, 36, 291 and 4386. + [Test, Category("Codec")] + public void GivenARunAroundEachMaximumMatchLength_WhenEncodedAndDecoded_ThenItIsByteExact( + [ValueSource(nameof(AllMethods))] CvfLzMethod method, + [Values(2, 3, 5, 6, 20, 21, 22, 35, 36, 37, 290, 291, 292, 511, 512, 513, 514, 1025, 4385, 4386, 4387, 8191)] int runLength) { + var data = Random(8192 + 64, runLength); + data.AsSpan(17, runLength).Fill(data[16]); // a literal then a run: one long distance-1 match + + Assert.That(RoundTrip(data, method), Is.EqualTo(data)); + } + + // DS/JM reach 4414 (4415 is the sync marker); SD-4 short matches 2047, long ones 2720; SD-3 2047. + [Test, Category("Codec")] + public void GivenARepeatAtEachDistanceBoundary_WhenEncodedAndDecoded_ThenItIsByteExact( + [ValueSource(nameof(AllMethods))] CvfLzMethod method, + [Values(1, 63, 64, 319, 320, 2047, 2048, 2720, 2721, 4414, 4415, 4416)] int distance, + [Values(3, 5, 6, 40)] int length) { + var data = Random(distance + length + 32, distance * 7 + length); + data.AsSpan(8, length).CopyTo(data.AsSpan(8 + distance)); + + Assert.That(RoundTrip(data, method), Is.EqualTo(data)); + } + + [Test, Category("Codec")] + public void GivenADsOrJmPayload_WhenEncoded_ThenItCarriesTheMethodHeaderAndAWholeNumberOfWords( + [Values(CvfLzMethod.Ds, CvfLzMethod.Jm, CvfLzMethod.Sq)] CvfLzMethod method) { + var payload = CvfLzCodec.Encode(CvfSamples.Get("x86-8k"), method, 1)!; + var expected = method switch { CvfLzMethod.Ds => "DS\0\0"u8.ToArray(), CvfLzMethod.Jm => "JM\0\0"u8.ToArray(), _ => "SQ\0\0"u8.ToArray() }; + + Assert.Multiple(() => { + Assert.That(payload[..4], Is.EqualTo(expected)); + Assert.That(payload.Length % 2, Is.Zero); + }); + } + + [Test, Category("Codec")] + public void GivenAutoMethod_WhenEncoded_ThenTheSmallerOfDsAndJmIsKept() { + var data = CvfSamples.Get("table-8k"); + var ds = CvfLzCodec.Encode(data, CvfLzMethod.Ds, 2)!; + var jm = CvfLzCodec.Encode(data, CvfLzMethod.Jm, 2)!; + + Assert.That(CvfLzCodec.Encode(data, CvfLzMethod.Auto, 2)!.Length, Is.EqualTo(Math.Min(ds.Length, jm.Length))); + } + + [Test, Category("Codec")] + public void GivenStoredMethod_WhenEncoded_ThenThereIsNoPayload() + => Assert.That(CvfLzCodec.Encode(CvfSamples.Get("text-8k"), CvfLzMethod.Stored, 2), Is.Null); + + [Test, Category("Codec")] + public void GivenAPayloadPaddedToWholeSectors_WhenDecoded_ThenThePaddingIsIgnored( + [Values(CvfLzMethod.Ds, CvfLzMethod.Jm, CvfLzMethod.Sq)] CvfLzMethod method) { + var data = CvfSamples.Get("dir-8k"); + var payload = CvfLzCodec.Encode(data, method, 2)!; + var padded = new byte[(payload.Length + 511) / 512 * 512]; + payload.CopyTo(padded, 0); + + Assert.That(CvfLzCodec.Decompress(padded, padded.Length, data.Length), Is.EqualTo(data)); + } + + // A file's last cluster may hold fewer sectors than the cluster: its stream closes with the sync and + // the rest of the cluster reads as zero. + [Test, Category("Codec")] + public void GivenAStreamShorterThanTheCluster_WhenDecoded_ThenTheTailIsZero([Values(CvfLzMethod.Ds, CvfLzMethod.Jm)] CvfLzMethod method) { + var data = CvfSamples.Get("text-8k")[..1536]; + var payload = CvfLzCodec.Encode(data, method, 2)!; + + var cluster = CvfLzCodec.Decompress(payload, payload.Length, 8192); + + Assert.Multiple(() => { + Assert.That(cluster[..1536], Is.EqualTo(data)); + Assert.That(cluster[1536..], Is.All.EqualTo((byte)0)); + }); + } + + [Test, Category("Codec")] + public void GivenATruncatedStream_WhenDecoded_ThenItIsRejected([Values(CvfLzMethod.Ds, CvfLzMethod.Jm)] CvfLzMethod method) { + var payload = CvfLzCodec.Encode(CvfSamples.Get("text-8k"), method, 2)!; + + Assert.That(() => CvfLzCodec.Decompress(payload[..(payload.Length / 2)], payload.Length / 2, 8192), Throws.InstanceOf()); + } + + [Test, Category("Codec")] + public void GivenAnUnknownMethodHeader_WhenDecoded_ThenItIsRejected() + => Assert.That(() => CvfLzCodec.Decompress("XY\0\0\0\0\0\0"u8.ToArray(), 8, 512), Throws.InstanceOf()); + + [Test, Category("Codec")] + public void GivenAPayloadShorterThanItsHeader_WhenDecoded_ThenItIsRejected() + => Assert.That(() => CvfLzCodec.Decompress([0x44, 0x53], 2, 512), Throws.InstanceOf()); + + [Test, Category("Codec")] + public void GivenAMatchBeforeTheClusterStart_WhenDecoded_ThenItIsRejected() { + // DS: "0 0" + distance 5 (LSB-first) then a length code — with nothing decoded yet. + var writer = new LsbBitWriter(); + foreach (var b in "DS\0\0"u8) writer.WriteByte(b); + writer.Write(0b00, 2); writer.Write(5, 6); writer.Write(1, 1); + var payload = writer.ToArray(2); + + Assert.That(() => CvfLzCodec.Decompress(payload, payload.Length, 512), Throws.InstanceOf()); + } + + [Test, Category("Codec")] + public void GivenALengthCodeWithNineLeadingZeros_WhenDecoded_ThenItIsRejected() { + var writer = new LsbBitWriter(); + foreach (var b in "JM\0\0"u8) writer.WriteByte(b); + writer.Write(0x41, 8); // literal 'A' ("0" + 7 bits) + writer.Write(0b01, 2); writer.Write(0, 1); writer.Write(1, 6); // match, distance 1 + writer.Write(0, 9); writer.Write(1, 1); // nine zeros: invalid + var payload = writer.ToArray(2); + + Assert.That(() => CvfLzCodec.Decompress(payload, payload.Length, 512), Throws.InstanceOf()); + } + + [Test, Category("Codec")] + public void GivenDifferentLevels_WhenEncoded_ThenEveryLevelRoundTripsAndHigherIsNoLarger( + [Values(CvfLzMethod.Ds, CvfLzMethod.Jm, CvfLzMethod.Sd4)] CvfLzMethod method) { + var data = CvfSamples.Get("x86-8k"); + var sizes = new List(); + foreach (var level in new[] { 1, 2, 3, 4 }) { + Assert.That(RoundTrip(data, method, level), Is.EqualTo(data), $"level {level}"); + sizes.Add(CvfLzCodec.Encode(data, method, level)!.Length); + } + + Assert.That(sizes[^1], Is.LessThanOrEqualTo(sizes[0])); + } +} diff --git a/Compression.Tests/DoubleSpace/DoubleSpaceHugeVolumeTests.cs b/Compression.Tests/DoubleSpace/DoubleSpaceHugeVolumeTests.cs index 9d53e7c56..15e22d666 100644 --- a/Compression.Tests/DoubleSpace/DoubleSpaceHugeVolumeTests.cs +++ b/Compression.Tests/DoubleSpace/DoubleSpaceHugeVolumeTests.cs @@ -41,8 +41,7 @@ public void Large_Volume_RoundTrips_ViaReader() { [Test, Category("DriverProof")] public void Large_Volume_IsReadByRealDmsdosDriver() { - var build = DmsdosCache.EnsureTools(); - if (build is null) Assert.Ignore("dmsdos unavailable."); + DmsdosOracle.Require(); var files = BigSet(150, 16 * 1024, 2); // ~300 clusters var w = new GenuineCvfWriter { CompressionMethod = CvfLzMethod.Auto, CompressionLevel = 1 }; @@ -52,13 +51,12 @@ public void Large_Volume_IsReadByRealDmsdosDriver() { var cvf = Path.Combine(Path.GetTempPath(), $"cwb-dblhuge-{Guid.NewGuid():N}.cvf"); try { File.WriteAllBytes(cvf, image); - var (_, det) = DmsdosCache.RunTool(DmsdosCache.CvfTest(build!), $"\"{cvf}\" -v"); + var det = Encoding.ASCII.GetBytes(DmsdosOracle.Detect(cvf)); Assert.That(Encoding.ASCII.GetString(det), Does.Contain("drivespace CVF (version 2)")); foreach (var idx in new[] { 0, 50, 100, 149 }) { var (name, data) = files[idx]; - var (exit, raw) = DmsdosCache.RunTool(DmsdosCache.DcRead(build!), $"\"{cvf}\" /{name} raw"); + var (exit, payload) = DmsdosOracle.ReadFile(cvf, name); Assert.That(exit, Is.EqualTo(0), $"dcread {name}"); - var payload = DmsdosCache.PayloadAfterDiagnostics(raw); Assert.That(payload.AsSpan(0, data.Length).ToArray(), Is.EqualTo(data), $"{name} byte-exact"); } } finally { try { File.Delete(cvf); } catch { } } diff --git a/Compression.Tests/DriveSpace3/DriveSpace3GenuineDmsdosTests.cs b/Compression.Tests/DriveSpace3/DriveSpace3GenuineDmsdosTests.cs index e26bee17e..7b322a90d 100644 --- a/Compression.Tests/DriveSpace3/DriveSpace3GenuineDmsdosTests.cs +++ b/Compression.Tests/DriveSpace3/DriveSpace3GenuineDmsdosTests.cs @@ -30,9 +30,7 @@ private static (string Name, byte[] Data)[] SampleFiles() { [TestCase(CvfLzMethod.Sq)] [TestCase(CvfLzMethod.Auto)] public void GenuineDvr3_IsMountedAndReadByteExact_ByRealDmsdosDriver(CvfLzMethod method) { - var build = DmsdosCache.EnsureTools(); - if (build is null) - Assert.Ignore("dmsdos tools unavailable (need Linux + git + cmake + C compiler, or set CWB_DMSDOS_BUILD). Skipping driver-proof gate."); + DmsdosOracle.Require(); var files = SampleFiles(); var writer = new GenuineDvr3Writer { CompressionMethod = method, CompressionLevel = 2 }; @@ -44,7 +42,7 @@ public void GenuineDvr3_IsMountedAndReadByteExact_ByRealDmsdosDriver(CvfLzMethod File.WriteAllBytes(cvfPath, image); // 1. Detection: the real driver must recognise it as DriveSpace 3. - var (_, detOut) = DmsdosCache.RunTool(DmsdosCache.CvfTest(build!), $"\"{cvfPath}\" -v"); + var detOut = Encoding.ASCII.GetBytes(DmsdosOracle.Detect(cvfPath)); var detected = Encoding.ASCII.GetString(detOut); Assert.That(detected, Does.Contain("drivespace 3 CVF"), $"dmsdos did not detect a genuine DriveSpace 3 CVF; got: {detected.Trim()}"); @@ -52,9 +50,8 @@ public void GenuineDvr3_IsMountedAndReadByteExact_ByRealDmsdosDriver(CvfLzMethod // 2. Read-back (write proof): every file must decompress byte-exact // through the real driver — i.e. our writer's output is genuine. foreach (var (name, expected) in files) { - var (exit, raw) = DmsdosCache.RunTool(DmsdosCache.DcRead(build!), $"\"{cvfPath}\" /{name} raw"); + var (exit, payload) = DmsdosOracle.ReadFile(cvfPath, name); Assert.That(exit, Is.EqualTo(0), $"dcread failed for {name}"); - var payload = DmsdosCache.PayloadAfterDiagnostics(raw); Assert.That(payload.Length, Is.GreaterThanOrEqualTo(expected.Length), $"dcread returned too few bytes for {name}"); Assert.That(payload.AsSpan(0, expected.Length).ToArray(), Is.EqualTo(expected), diff --git a/Compression.Tests/DriveSpace3/DriveSpace3GenuineMaintenanceTests.cs b/Compression.Tests/DriveSpace3/DriveSpace3GenuineMaintenanceTests.cs index ddc22e12c..25f7566c3 100644 --- a/Compression.Tests/DriveSpace3/DriveSpace3GenuineMaintenanceTests.cs +++ b/Compression.Tests/DriveSpace3/DriveSpace3GenuineMaintenanceTests.cs @@ -70,8 +70,7 @@ public void Add_Then_Remove_RoundTrips() { [Test, Category("DriverProof")] public void RebuiltImage_StillReadByRealDmsdosDriver() { - var build = DmsdosCache.EnsureTools(); - if (build is null) Assert.Ignore("dmsdos unavailable."); + DmsdosOracle.Require(); var d = new DriveSpace3FormatDescriptor(); using var ms = new MemoryStream(); ms.Write(MakeGenuine()); ms.Position = 0; @@ -81,12 +80,11 @@ public void RebuiltImage_StillReadByRealDmsdosDriver() { var cvf = Path.Combine(Path.GetTempPath(), $"cwb-dvr3reb-{Guid.NewGuid():N}.cvf"); try { File.WriteAllBytes(cvf, img); - var (_, det) = DmsdosCache.RunTool(DmsdosCache.CvfTest(build!), $"\"{cvf}\" -v"); + var det = Encoding.ASCII.GetBytes(DmsdosOracle.Detect(cvf)); Assert.That(Encoding.ASCII.GetString(det), Does.Contain("drivespace 3 CVF")); foreach (var (n, data) in Seed) { - var (exit, raw) = DmsdosCache.RunTool(DmsdosCache.DcRead(build!), $"\"{cvf}\" /{n} raw"); + var (exit, payload) = DmsdosOracle.ReadFile(cvf, n); Assert.That(exit, Is.EqualTo(0), $"dcread {n}"); - var payload = DmsdosCache.PayloadAfterDiagnostics(raw); Assert.That(payload.AsSpan(0, data.Length).ToArray(), Is.EqualTo(data), $"{n} byte-exact after rebuild"); } } finally { try { File.Delete(cvf); } catch { } } diff --git a/Compression.Tests/DriveSpace3/DriveSpace3HugeVolumeTests.cs b/Compression.Tests/DriveSpace3/DriveSpace3HugeVolumeTests.cs index bda8d7eda..01bf2fa30 100644 --- a/Compression.Tests/DriveSpace3/DriveSpace3HugeVolumeTests.cs +++ b/Compression.Tests/DriveSpace3/DriveSpace3HugeVolumeTests.cs @@ -68,8 +68,7 @@ public void Large_Volume_FullReadWrite_AddRemove_ViaDescriptor() { [Test, Category("DriverProof")] public void Large_Volume_IsReadByRealDmsdosDriver() { - var build = DmsdosCache.EnsureTools(); - if (build is null) Assert.Ignore("dmsdos unavailable."); + DmsdosOracle.Require(); var files = BigSet(120, 64 * 1024, 2); // ~240 clusters, mixed var w = new GenuineDvr3Writer { CompressionMethod = CvfLzMethod.Auto, CompressionLevel = 1 }; @@ -79,14 +78,13 @@ public void Large_Volume_IsReadByRealDmsdosDriver() { var cvf = Path.Combine(Path.GetTempPath(), $"cwb-dvr3huge-{Guid.NewGuid():N}.cvf"); try { File.WriteAllBytes(cvf, image); - var (_, det) = DmsdosCache.RunTool(DmsdosCache.CvfTest(build!), $"\"{cvf}\" -v"); + var det = Encoding.ASCII.GetBytes(DmsdosOracle.Detect(cvf)); Assert.That(Encoding.ASCII.GetString(det), Does.Contain("drivespace 3 CVF")); // sample a handful across the volume foreach (var idx in new[] { 0, 37, 80, 119 }) { var (name, data) = files[idx]; - var (exit, raw) = DmsdosCache.RunTool(DmsdosCache.DcRead(build!), $"\"{cvf}\" /{name} raw"); + var (exit, payload) = DmsdosOracle.ReadFile(cvf, name); Assert.That(exit, Is.EqualTo(0), $"dcread {name}"); - var payload = DmsdosCache.PayloadAfterDiagnostics(raw); Assert.That(payload.AsSpan(0, data.Length).ToArray(), Is.EqualTo(data), $"{name} byte-exact"); } } finally { try { File.Delete(cvf); } catch { } } diff --git a/Compression.Tests/ExternalFsInteropTests.cs b/Compression.Tests/ExternalFsInteropTests.cs index 616ce7d8f..772c3b401 100644 --- a/Compression.Tests/ExternalFsInteropTests.cs +++ b/Compression.Tests/ExternalFsInteropTests.cs @@ -2303,9 +2303,7 @@ public void DoubleSpace_OurImage_DmsdosAccepts(Compression.Core.FileSystems.Cvf. // mount it, and read every file back byte-exact. The feature-rich // DoubleSpaceWriter emits a self-consistent (non-genuine) layout; the // driver-genuine emitter is GenuineCvfWriter, proven here. - var build = DmsdosCache.EnsureTools(); - if (build is null) - Assert.Ignore("dmsdos tools unavailable (need Linux + git + cmake + C compiler, or set CWB_DMSDOS_BUILD)."); + DmsdosOracle.Require(); var files = new (string Name, byte[] Data)[] { ("HELLO.TXT", SmallText), @@ -2318,14 +2316,13 @@ public void DoubleSpace_OurImage_DmsdosAccepts(Compression.Core.FileSystems.Cvf. var cvfPath = Path.Combine(this._tmpDir, "dblspace_genuine.cvf"); File.WriteAllBytes(cvfPath, image); - var (_, det) = DmsdosCache.RunTool(DmsdosCache.CvfTest(build!), $"\"{cvfPath}\" -v"); + var det = Encoding.ASCII.GetBytes(DmsdosOracle.Detect(cvfPath)); Assert.That(Encoding.ASCII.GetString(det), Does.Contain("drivespace CVF (version 2)"), "dmsdos did not detect our CVF as a genuine DoubleSpace/DriveSpace v2 volume"); foreach (var (name, expected) in files) { - var (exit, raw) = DmsdosCache.RunTool(DmsdosCache.DcRead(build!), $"\"{cvfPath}\" /{name} raw"); + var (exit, payload) = DmsdosOracle.ReadFile(cvfPath, name); Assert.That(exit, Is.EqualTo(0), $"dcread failed for {name}"); - var payload = DmsdosCache.PayloadAfterDiagnostics(raw); Assert.That(payload.Length, Is.GreaterThanOrEqualTo(expected.Length), $"short read for {name}"); Assert.That(payload.AsSpan(0, expected.Length).ToArray(), Is.EqualTo(expected), $"{name} did not read back byte-exact through the dmsdos driver"); diff --git a/Compression.Tests/Stacker/GenuineStackerCodecOracleTests.cs b/Compression.Tests/Stacker/GenuineStackerCodecOracleTests.cs new file mode 100644 index 000000000..4db2bebb1 --- /dev/null +++ b/Compression.Tests/Stacker/GenuineStackerCodecOracleTests.cs @@ -0,0 +1,187 @@ +using System.Text; +using Compression.Core.FileSystems.Cvf; +using Compression.Tests.Cvf; +using Compression.Tests.Support; +using FileSystem.Stacker; + +namespace Compression.Tests.Stacker; + +/// +/// Stacker cluster codecs against the dmsdos driver as a black box (Linux natively, Windows through +/// WSL): volumes our writer builds must read back byte-exact through dcread and through our own +/// reader. Covers the SD-4 encoder with back-references and the whole-cluster ForceCompress case. +/// +[TestFixture] +public sealed class GenuineStackerCodecOracleTests { + private const int ClusterBytes = 8192; + + private string _dir = null!; + + [SetUp] + public void SetUp() => Directory.CreateDirectory(this._dir = Path.Combine(Path.GetTempPath(), $"cwb-stac-oracle-{Guid.NewGuid():N}")); + + [TearDown] + public void TearDown() { + try { Directory.Delete(this._dir, true); } catch (IOException) { /* best effort */ } + } + + private static byte[] Build(CvfLzMethod method, bool force, params (string Name, byte[] Data)[] files) { + var writer = new GenuineStackerWriter { CompressionMethod = method, CompressionLevel = 3, ForceCompress = force }; + foreach (var (n, d) in files) writer.AddFile(n, d); + return writer.Build(); + } + + private static void AssertOurReaderReads(byte[] image, (string Name, byte[] Data)[] files) { + using var reader = new GenuineStackerReader(new MemoryStream(image)); + foreach (var (name, expected) in files) + Assert.That(reader.Extract(reader.Entries.Single(e => e.Name == name)), Is.EqualTo(expected), $"our reader: {name}"); + } + + private void AssertDmsdosReads(byte[] image, (string Name, byte[] Data)[] files) { + var path = Path.Combine(this._dir, "stac.cvf"); + File.WriteAllBytes(path, image); + Assert.That(DmsdosOracle.Detect(path), Does.Contain("stacker"), "dmsdos does not detect the volume"); + foreach (var (name, expected) in files) { + var (exit, data) = DmsdosOracle.ReadFile(path, name); + Assert.That(exit, Is.Zero, $"dcread failed for {name}"); + Assert.That(data.Length, Is.GreaterThanOrEqualTo(expected.Length), $"dcread returned too few bytes for {name}"); + Assert.That(data[..expected.Length], Is.EqualTo(expected), $"dmsdos: {name}"); + } + } + + /// A cluster whose SD-3 payload needs all 16 sectors: 15 sectors < payload ≤ 16 sectors. + internal static byte[] WholeClusterPayloadData() { + var random = new byte[ClusterBytes]; + new Random(4711).NextBytes(random); + int lo = 0, hi = ClusterBytes; // number of random bytes; the rest repeats one byte + while (lo < hi) { + var mid = (lo + hi) / 2; + var size = Sd3Codec.Encode(Mix(random, mid)).Length; + if (size <= 15 * 512) lo = mid + 1; else hi = mid; + } + var data = Mix(random, lo); + var sectors = (Sd3Codec.Encode(data).Length + 511) / 512; + Assume.That(sectors, Is.EqualTo(16), "could not construct a whole-cluster SD-3 payload"); + return data; + + static byte[] Mix(byte[] random, int count) { + var d = new byte[ClusterBytes]; + random.AsSpan(0, count).CopyTo(d); + return d; + } + } + + [Test, Category("Codec")] + public void GivenForceCompress_WhenACompressedClusterNeedsAllSectors_ThenItIsStoredAndReadsBackExact() { + var data = WholeClusterPayloadData(); + (string, byte[])[] files = [("FULL.BIN", data)]; + + var image = Build(CvfLzMethod.Sd3, force: true, files); + + AssertOurReaderReads(image, files); + } + + [Test, Category("DriverProof")] + public void GivenForceCompress_WhenACompressedClusterNeedsAllSectors_ThenDmsdosReadsItBackExact() { + DmsdosOracle.Require(); + (string, byte[])[] files = [("FULL.BIN", WholeClusterPayloadData())]; + + AssertDmsdosReads(Build(CvfLzMethod.Sd3, force: true, files), files); + } + + private static (string, byte[])[] CodecSamples() { + var text = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat("SD-4 encoder verification line with some repetition.\r\n", 600))); + var runs = new byte[3 * ClusterBytes]; + for (var i = 0; i < runs.Length; ++i) runs[i] = (byte)(i / 5000); // long matches: every extension + return [ + ("TEXT.TXT", text), + ("RUNS.BIN", runs), + ("SAMPTEXT.TXT", CvfSamples.Get("text-8k")), + ("SAMPX86.BIN", CvfSamples.Get("x86-8k")), + ("SAMPTAB.BIN", CvfSamples.Get("table-8k")), + ("SAMPALPH.BIN", CvfSamples.Get("alphabet-8k")), + ("SAMPDIR.BIN", CvfSamples.Get("dir-8k")), + ("ONE.BIN", [0x5A]), + ]; + } + + [Test, Category("DriverProof")] + public void GivenSd4ClustersWithBackReferences_WhenReadByDmsdos_ThenEveryFileIsByteExact([Values(CvfLzMethod.Sd4, CvfLzMethod.Auto)] CvfLzMethod method) { + DmsdosOracle.Require(); + var files = CodecSamples(); + var image = Build(method, force: false, files); + + AssertDmsdosReads(image, files); + AssertOurReaderReads(image, files); + } + + /// + /// One cluster whose byte counts grow just faster than Fibonacci (1, 1, 3, 5, 9, 15, … 1973, each + /// the sum of the two before plus one, so no tie lets a code stay shallow), the rest one more value: + /// an optimal code over them is 16 bits deep, so a literals-only SD-4 stream needs the full 15-bit + /// limit. + /// + internal static byte[] SkewedCluster() { + var data = new List(ClusterBytes); + int a = 1, b = 1; + for (var symbol = 0; symbol < 16; ++symbol, (a, b) = (b, a + b + 1)) + data.AddRange(Enumerable.Repeat((byte)(symbol * 13), a)); + data.AddRange(Enumerable.Repeat((byte)250, ClusterBytes - data.Count)); + var array = data.ToArray(); + new Random(1).Shuffle(array); + return array; + } + + // The 5-bit "last" field after the signature word is the longest main-code length. + private static int LongestMainCode(byte[] payload) => (payload[2] | payload[3] << 8) >> 8 & 0x1F; + + [Test, Category("Codec")] + public void GivenSkewedLiterals_WhenSd4Encoded_ThenTheMainCodeUsesTheFull15BitsAndRoundTrips() { + var data = SkewedCluster(); + var payload = Sd4Codec.Encode(data, level: 2, matches: false); + + Assert.Multiple(() => { + Assert.That(LongestMainCode(payload), Is.EqualTo(15)); + Assert.That(Sd4Codec.Decode(payload, payload.Length, data.Length), Is.EqualTo(data)); + }); + } + + // A stored volume whose one cluster is then rewritten as a compressed run holding the given payload. + private static byte[] VolumeWithClusterPayload(byte[] cluster, byte[] payload) { + var image = Build(CvfLzMethod.Stored, force: false, ("SKEW.BIN", cluster)); + const int amapEntry = (2 + 3) * 512 + 2 * 3; // AMAP area 0 for cluster 2 (FAT start 2) + var sector = image[amapEntry] | image[amapEntry + 1] << 8; + var sectors = (payload.Length + 511) / 512; + Assume.That(sectors, Is.LessThan(16), "payload must be shorter than the cluster"); + Array.Clear(image, sector * 512, 16 * 512); + payload.CopyTo(image, sector * 512); + image[amapEntry + 2] = (byte)(sectors - 1); // flags 0: compressed + return image; + } + + [Test, Category("Codec")] + public void GivenAPatchedSd4Cluster_WhenReadByOurReader_ThenItIsByteExact() { + var data = SkewedCluster(); + (string, byte[])[] files = [("SKEW.BIN", data)]; + + AssertOurReaderReads(VolumeWithClusterPayload(data, Sd4Codec.Encode(data, 2, matches: false)), files); + } + + [Test, Category("DriverProof")] + public void GivenSd4CodesOf15Bits_WhenReadByDmsdos_ThenTheClusterIsByteExact() { + DmsdosOracle.Require(); + var data = SkewedCluster(); + (string, byte[])[] files = [("SKEW.BIN", data)]; + + AssertDmsdosReads(VolumeWithClusterPayload(data, Sd4Codec.Encode(data, 2, matches: false)), files); + } + + [Test, Category("Codec")] + public void GivenSd4Clusters_WhenEncoded_ThenTheyAreWithinTenPercentOfTheDmsdosEncoder( + [Values("text-8k", "x86-8k", "table-8k", "alphabet-8k", "dir-8k")] string sample) { + var ours = Sd4Codec.Encode(CvfSamples.Get(sample), level: 3).Length; + var theirs = CvfReferenceVectorTests.Load($"{sample}.sd4").Length; + + Assert.That(ours, Is.LessThanOrEqualTo(theirs * 11 / 10), $"ours {ours} B, dmsdos {theirs} B"); + } +} diff --git a/Compression.Tests/Stacker/GenuineStackerDmsdosTests.cs b/Compression.Tests/Stacker/GenuineStackerDmsdosTests.cs index efa89ffb3..439273de3 100644 --- a/Compression.Tests/Stacker/GenuineStackerDmsdosTests.cs +++ b/Compression.Tests/Stacker/GenuineStackerDmsdosTests.cs @@ -30,9 +30,7 @@ private static (string Name, byte[] Data)[] SampleFiles() { [TestCase(CvfLzMethod.Sd3)] [TestCase(CvfLzMethod.Auto)] public void GenuineStacker_IsMountedAndReadByteExact_ByRealDmsdosDriver(CvfLzMethod method) { - var build = DmsdosCache.EnsureTools(); - if (build is null) - Assert.Ignore("dmsdos tools unavailable (need Linux + git + cmake + C compiler, or set CWB_DMSDOS_BUILD). Skipping driver-proof gate."); + DmsdosOracle.Require(); var files = SampleFiles(); var writer = new GenuineStackerWriter { CompressionMethod = method, CompressionLevel = 2 }; @@ -44,16 +42,15 @@ public void GenuineStacker_IsMountedAndReadByteExact_ByRealDmsdosDriver(CvfLzMet File.WriteAllBytes(cvfPath, image); // 1. Detection. - var (_, detOut) = DmsdosCache.RunTool(DmsdosCache.CvfTest(build!), $"\"{cvfPath}\" -v"); + var detOut = Encoding.ASCII.GetBytes(DmsdosOracle.Detect(cvfPath)); var detected = Encoding.ASCII.GetString(detOut); Assert.That(detected, Does.Contain("stacker version 3 CVF"), $"dmsdos did not detect a genuine Stacker 3 CVF; got: {detected.Trim()}"); // 2. Write proof: the real driver reads every file back byte-exact. foreach (var (name, expected) in files) { - var (exit, raw) = DmsdosCache.RunTool(DmsdosCache.DcRead(build!), $"\"{cvfPath}\" /{name} raw"); + var (exit, payload) = DmsdosOracle.ReadFile(cvfPath, name); Assert.That(exit, Is.EqualTo(0), $"dcread failed for {name}"); - var payload = DmsdosCache.PayloadAfterDiagnostics(raw); Assert.That(payload.Length, Is.GreaterThanOrEqualTo(expected.Length), $"dcread returned too few bytes for {name}"); Assert.That(payload.AsSpan(0, expected.Length).ToArray(), Is.EqualTo(expected), diff --git a/Compression.Tests/Stacker/StackerHugeVolumeTests.cs b/Compression.Tests/Stacker/StackerHugeVolumeTests.cs index eeb6b1fd6..1eb1abd9a 100644 --- a/Compression.Tests/Stacker/StackerHugeVolumeTests.cs +++ b/Compression.Tests/Stacker/StackerHugeVolumeTests.cs @@ -40,8 +40,7 @@ public void Above512Clusters_RoundTrips_ViaReader() { [Test, Category("DriverProof")] public void Above512Clusters_IsReadByRealDmsdosDriver() { - var build = DmsdosCache.EnsureTools(); - if (build is null) Assert.Ignore("dmsdos unavailable."); + DmsdosOracle.Require(); var files = BigSet(300, 16 * 1024, 2); // ~600 clusters var w = new GenuineStackerWriter { CompressionMethod = CvfLzMethod.Auto, CompressionLevel = 1 }; @@ -51,13 +50,12 @@ public void Above512Clusters_IsReadByRealDmsdosDriver() { var cvf = Path.Combine(Path.GetTempPath(), $"cwb-stachuge-{Guid.NewGuid():N}.cvf"); try { File.WriteAllBytes(cvf, image); - var (_, det) = DmsdosCache.RunTool(DmsdosCache.CvfTest(build!), $"\"{cvf}\" -v"); + var det = Encoding.ASCII.GetBytes(DmsdosOracle.Detect(cvf)); Assert.That(Encoding.ASCII.GetString(det), Does.Contain("stacker")); foreach (var idx in new[] { 0, 100, 200, 299 }) { var (name, data) = files[idx]; - var (exit, raw) = DmsdosCache.RunTool(DmsdosCache.DcRead(build!), $"\"{cvf}\" /{name} raw"); + var (exit, payload) = DmsdosOracle.ReadFile(cvf, name); Assert.That(exit, Is.EqualTo(0), $"dcread {name}"); - var payload = DmsdosCache.PayloadAfterDiagnostics(raw); Assert.That(payload.AsSpan(0, data.Length).ToArray(), Is.EqualTo(data), $"{name} byte-exact"); } } finally { try { File.Delete(cvf); } catch { } } diff --git a/Compression.Tests/Support/DmsdosOracle.cs b/Compression.Tests/Support/DmsdosOracle.cs new file mode 100644 index 000000000..94a1465e5 --- /dev/null +++ b/Compression.Tests/Support/DmsdosOracle.cs @@ -0,0 +1,53 @@ +namespace Compression.Tests.Support; + +/// +/// The GPL dmsdos tools as a black-box oracle on any host: natively on Linux (built by +/// ), through WSL on Windows (cloned and built once under +/// ~/.cache/cwb-dmsdos inside the distro). Only the tools' behaviour is used — volumes go in, +/// detection text and file contents come out. +/// +internal static class DmsdosOracle { + private static readonly Lazy Build = new(Locate); + + /// Skips the calling test when the tools cannot be had on this machine. + public static void Require() { + if (Build.Value is null) + Assert.Ignore("dmsdos tools unavailable (need Linux or WSL with git + cmake + a C compiler, or CWB_DMSDOS_BUILD)."); + } + + /// What cvftest -v makes of the volume. + public static string Detect(string cvfPath) + => FsInteropToolbox.RunWsl($"'{Build.Value}/cvftest' {FsInteropToolbox.WinToWsl(cvfPath)} -v 2>&1").StdOut; + + /// + /// Reads from the root of the volume with dcread … raw; the bytes are + /// the whole clusters the driver decoded, so callers compare the first file-size bytes. + /// + public static (int Exit, byte[] Data) ReadFile(string cvfPath, string name) { + var outFile = $"{cvfPath}.{name}.out"; + try { + var r = FsInteropToolbox.RunWsl( + $"'{Build.Value}/dcread' {FsInteropToolbox.WinToWsl(cvfPath)} /{name} raw > {FsInteropToolbox.WinToWsl(outFile)} 2>/dev/null"); + var raw = File.Exists(outFile) ? File.ReadAllBytes(outFile) : []; + return (r.ExitCode, DmsdosCache.PayloadAfterDiagnostics(raw)); + } finally { + try { File.Delete(outFile); } catch (IOException) { /* best effort */ } + } + } + + private static string? Locate() { + if (!OperatingSystem.IsWindows()) return DmsdosCache.EnsureTools(); + if (!FsInteropToolbox.WslAvailable) return null; + + const string script = + "set -e; d=$HOME/.cache/cwb-dmsdos; " + + "if [ ! -x $d/build/dcread ] || [ ! -x $d/build/cvftest ]; then " + + "command -v git >/dev/null && command -v cmake >/dev/null && command -v cc >/dev/null; " + + "mkdir -p $d; [ -d $d/src/.git ] || git clone -q --depth 1 " + DmsdosCache.RepoUrl + " $d/src; " + + "cmake -S $d/src -B $d/build -DCMAKE_BUILD_TYPE=Release -DCMAKE_POLICY_VERSION_MINIMUM=3.5 >/dev/null 2>&1; " + + "cmake --build $d/build -j 2 >/dev/null 2>&1; fi; echo $d/build"; + var result = FsInteropToolbox.RunWsl(script); + var lines = result.StdOut.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + return result.ExitCode == 0 && lines.Length > 0 ? lines[^1] : null; + } +} diff --git a/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.DriveSpace3/GenuineDvr3Writer.cs b/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.DriveSpace3/GenuineDvr3Writer.cs index 97ae13e73..bff647676 100644 --- a/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.DriveSpace3/GenuineDvr3Writer.cs +++ b/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.DriveSpace3/GenuineDvr3Writer.cs @@ -66,6 +66,8 @@ public sealed class GenuineDvr3Writer { /// smaller of compressed/stored is chosen per cluster (auto-best). public bool ForceCompress { get; init; } + private static byte[] Smaller(byte[] a, byte[] b) => b.Length < a.Length ? b : a; + /// Adds a file to the root directory of the compressed volume. public void AddFile(string name, byte[] data) { ArgumentNullException.ThrowIfNull(name); @@ -120,7 +122,10 @@ public byte[] Build() { var physCursor = physDataStart; var layout = new List(); foreach (var (cl, full) in clusterFull) { - var comp = CvfLzCodec.Encode(full, this.CompressionMethod, this.CompressionLevel); + // Auto covers every codec a DriveSpace 3 volume reads: DS/JM (the codec's Auto) and SQ. + var comp = this.CompressionMethod == CvfLzMethod.Auto + ? Smaller(CvfLzCodec.Encode(full, CvfLzMethod.Auto, this.CompressionLevel)!, CvfLzCodec.Encode(full, CvfLzMethod.Sq, this.CompressionLevel)!) + : CvfLzCodec.Encode(full, this.CompressionMethod, this.CompressionLevel); var ksize = comp is null ? Spc : (comp.Length + Ss - 1) / Ss; if (comp is not null && ksize <= Spc && (ksize < Spc || this.ForceCompress)) { layout.Add(new ClusterPlan(cl, comp, ksize, true, physCursor)); diff --git a/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.Stacker/FORMAT-NOTES.md b/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.Stacker/FORMAT-NOTES.md index b1d46e809..687195d16 100644 --- a/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.Stacker/FORMAT-NOTES.md +++ b/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.Stacker/FORMAT-NOTES.md @@ -12,7 +12,7 @@ > [`docs/CVF-CODECS-ON-DISK.md`](../../../docs/CVF-CODECS-ON-DISK.md). The reader > decodes the full SD-4 format (run tokens, short and long matches), pinned to > clusters the dmsdos SD-3/SD-4 compressors wrote (`CvfReferenceVectorTests`); the -> writer's SD-4 output is literals-only. The AMAP flag nibble is honoured: +> writer's SD-4 output uses back-references and run-coded tables, read back by dmsdos. The AMAP flag nibble is honoured: > fragmented and suballocated clusters are refused, not guessed at. > - **The legacy `StackerWriter` (invented `STKMAP01` trailer) is NOT genuine:** > `dmsdos` panics on it. It is retained only for self-round-trip. diff --git a/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.Stacker/GenuineStackerWriter.cs b/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.Stacker/GenuineStackerWriter.cs index 718afd2d2..fe11ba9dc 100644 --- a/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.Stacker/GenuineStackerWriter.cs +++ b/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.Stacker/GenuineStackerWriter.cs @@ -74,7 +74,10 @@ public sealed class GenuineStackerWriter { /// Codec effort (search depth). Higher = better ratio, slower. public int CompressionLevel { get; init; } = 1; - /// Keep a compressed cluster even if it does not shrink (auto-best off). + /// Keep a compressed cluster even if it does not shrink (auto-best off). Stacker cannot + /// record a compressed run that fills a whole cluster — such a run reads as stored — so a payload that + /// needs every sector of its cluster is stored raw regardless; any shorter payload is kept compressed + /// either way, which leaves this option without effect on Stacker volumes. public bool ForceCompress { get; init; } private readonly List<(string Name, byte[] Data)> _files = []; @@ -147,7 +150,9 @@ public byte[] Build() { _ => CvfLzCodec.Encode(full, CvfLzMethod.Ds, this.CompressionLevel), }; var ksize = comp is null ? Spc : (comp.Length + Ss - 1) / Ss; - if (comp is not null && ksize < Spc || (comp is not null && this.ForceCompress && ksize <= Spc)) { + // A run as long as the cluster always reads as stored (the AMAP has no flag for a whole-cluster + // compressed run), so a payload needing all Spc sectors is stored raw even under ForceCompress. + if (comp is not null && ksize < Spc) { layout.Add(new ClusterPlan(cl, comp!, ksize, physCursor)); physCursor += ksize; } else { diff --git a/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.Stacker/StackerFormatDescriptor.cs b/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.Stacker/StackerFormatDescriptor.cs index 63e9ac31f..be615e7ae 100644 --- a/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.Stacker/StackerFormatDescriptor.cs +++ b/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.Stacker/StackerFormatDescriptor.cs @@ -154,7 +154,8 @@ public sealed class StackerFormatDescriptor : IFormatDescriptor, IArchiveFormatO new FormatOptionDescriptor( Key: "ForceCompress", DisplayName: "Force compression", Kind: FormatOptionKind.Boolean, Default: "false", - Description: "Keep the compressed form even when it does not shrink a cluster.", + Description: "Keep the compressed form even when it does not shrink a cluster. A compressed run " + + "filling a whole cluster cannot be told from a stored one on Stacker, so such a cluster is still stored.", DependsOn: "Compatibility=Genuine"), ]; diff --git a/Hawkynt.FileFormats.FileSystems/REFERENCE.md b/Hawkynt.FileFormats.FileSystems/REFERENCE.md index 446cc52d5..a53eb4bf9 100644 --- a/Hawkynt.FileFormats.FileSystems/REFERENCE.md +++ b/Hawkynt.FileFormats.FileSystems/REFERENCE.md @@ -12929,7 +12929,7 @@ Builds a genuine Stac Electronics STACVOL — the on-disk shape a real Stacker d | `GenuineStackerWriter` | `GenuineStackerWriter()` | | | `CompressionLevel` | `int CompressionLevel { get; init; }` | Codec effort (search depth). Higher = better ratio, slower. | | `CompressionMethod` | `CvfLzMethod CompressionMethod { get; init; }` | Per-cluster compression: Stored (default), Sd3 (Stacker 3), Sd4 (Stacker 4), Ds, or Auto (smallest of Ds/Sd3/Sd4 per cluster). The Stacker driver tells them apart by the payload's first word — 0x0081 SD-4, 0x5344 ("DS") the DoubleSpace codec, anything else SD-3. JM/SQ map to Ds. | -| `ForceCompress` | `bool ForceCompress { get; init; }` | Keep a compressed cluster even if it does not shrink (auto-best off). | +| `ForceCompress` | `bool ForceCompress { get; init; }` | Keep a compressed cluster even if it does not shrink (auto-best off). Stacker cannot record a compressed run that fills a whole cluster — such a run reads as stored — so a payload that needs every sector of its cluster is stored raw regardless; any shorter payload is kept compressed either way, which leaves this option without effect on Stacker volumes. | | `Timestamp` | `DateTime Timestamp { get; init; }` | Creation/modification timestamp stamped on every file entry. Default (before 1980) leaves the FAT date/time fields zero. | | `Version` | `int Version { get; init; }` | Stacker major version recorded in the decoded superblock (< 410 ⇒ v3). | | `VolumeLabel` | `string VolumeLabel { get; init; }` | Optional inner-volume label (≤11 chars). Empty = no label entry. | diff --git a/docs/CVF-CODECS-ON-DISK.md b/docs/CVF-CODECS-ON-DISK.md index b2c7052ed..26b757432 100644 --- a/docs/CVF-CODECS-ON-DISK.md +++ b/docs/CVF-CODECS-ON-DISK.md @@ -3,7 +3,8 @@ The per-cluster compression formats of the DOS compressed-volume family: Microsoft DoubleSpace / DriveSpace (DS-0-x, JM-0-x), DriveSpace 3 (JM-0-1 "High", SQ-0-0 "Ultra") and Stac Stacker (SD-3, SD-4). `Compression.Core.FileSystems.Cvf` (`CvfLzCodec`, `Sd3Codec`, -`Sd4Codec`) implements this document; the container layouts (MDFAT, AMAP, BitFAT) live in the +`Sd4Codec`) implements this document — it was written from this document and the reference vectors +alone, with dmsdos run only as a black box; the container layouts (MDFAT, AMAP, BitFAT) live in the `FORMAT-NOTES.md` beside each file-system reader. ## 📚 Provenance @@ -45,6 +46,10 @@ its kind), together with whether the run fills a whole cluster: | 1 | 0 | **fragmented** (first sector starts `0xED`, piece count, 4-byte piece list) | stored (directory) | | 1 | 1 | **suballocated** (signature `0x1234` at the sector end) | suballocated | +A compressed cluster can therefore never occupy all of its cluster's sectors: a run that long reads +as stored whatever its content (the table above). A writer whose +compressed payload needs every sector stores the cluster raw instead. + Fragmented and suballocated clusters are refused by `GenuineStackerReader` — no sample of either has been verified. @@ -85,6 +90,17 @@ decoder does not require the final sync. Copies may overlap (distance shorter than length) and are performed byte by byte. +**Measured: one sync per sector.** In all 16 DS/JM reference streams (8 KiB and 32 KiB clusters) +the dmsdos compressors emit a sync after every 512 decoded bytes, not only after the last one, and +no match runs across a 512-byte boundary (0 of all matches) — distances do reach back across +boundaries (up to 4414). Our encoder writes the same shape. The decoder accepts syncs anywhere. + +**A cluster shorter than its nominal size** (a file's last cluster, MDFAT decoded size below the +cluster) ends with a sync followed by nothing but zero padding; decoding stops there and the rest of +the cluster reads as zero. A sync with non-zero bits behind it is a mid-stream marker and decoding +continues. Stacker's use of the DS grammar also ends where the stream runs out or reaches its zero +padding (a `0 0` token with distance 0). + ## 🔢 SQ-0-0 Bytes `53 51` ("SQ"), a 16-bit version (0), then a raw **RFC 1951 DEFLATE** stream: the same block @@ -166,6 +182,12 @@ is extended by 4 bits; if the sum is 36, by 8 more; at 291 by 12 more; at 4386 b not needed to recover the data). A match reaching before the cluster start is invalid; a final match longer than the space left is clipped. +**Our encoder** emits short and long matches (all four length extensions), literals and the end +token; it transmits the code lengths with all six run tokens and builds both codes with package-merge +(optimal, length-limited, always complete). Measured: dmsdos reads main codes 15 bits deep +(`GivenSd4CodesOf15Bits_WhenReadByDmsdos_ThenTheClusterIsByteExact`), so the encoder uses the format's +full 15-bit limit for both codes. + ## ✅ Verification * `Compression.Tests/Cvf/ReferenceVectors/` — 32 cluster payloads written by the dmsdos @@ -179,8 +201,6 @@ match longer than the space left is clipped. ## ⚠️ Not covered -* **Our SD-4 encoder emits literals only.** The decoder reads the whole format; an encoder that - emits match and run tokens has not been written. * **DriveSpace 3 fragmented clusters** (an MDFAT flag dmsdos itself describes as guessed) and **Stacker fragmented / suballocated clusters** are not read. * No Microsoft or Stac tool has produced or read any of these streams on this machine. From ce6ef9c0f9164b0a3b0928f02cad04d7b0404a66 Mon Sep 17 00:00:00 2001 From: Hawkynt Date: Mon, 26 Oct 2026 09:54:08 +0100 Subject: [PATCH 2/4] + Stacker: fragmented clusters read, layout measured against dmsdos GenuineStackerReader refused every fragmented cluster (AMAP bit 7 set, run shorter than the cluster). The layout is now read: the AMAP run is the first piece, its first sector carries 0xED, the number of further pieces, their summed sector count minus one, and 4-byte entries (16-bit sector, sectors - 1, a fourth byte); the compressed payload starts after the entries and continues through the pieces. An entry whose fourth byte is non-zero is refused - its meaning was never observed. Rung 4/5 by measurement: no specification or sample volume exists, so candidate layouts were built and dmsdos (run only as a black box, its source not read) judged them through dcread. Six accepted shapes (one to three further pieces, SD-3 and SD-4 payloads) are kept as DriverProof tests, and a wrong piece total is refused by dmsdos and by us alike. Recorded in docs/CVF-CODECS-ON-DISK.md. ! Stacker suballocated clusters and DriveSpace 3 fragmented clusters stay refused: no layout measured. --- .../Stacker/StackerFragmentedClusterTests.cs | 151 ++++++++++++++++++ .../FileSystem.Stacker/FORMAT-NOTES.md | 3 +- .../GenuineStackerReader.cs | 52 +++++- docs/CVF-CODECS-ON-DISK.md | 30 +++- 4 files changed, 230 insertions(+), 6 deletions(-) create mode 100644 Compression.Tests/Stacker/StackerFragmentedClusterTests.cs diff --git a/Compression.Tests/Stacker/StackerFragmentedClusterTests.cs b/Compression.Tests/Stacker/StackerFragmentedClusterTests.cs new file mode 100644 index 000000000..d4b717d8e --- /dev/null +++ b/Compression.Tests/Stacker/StackerFragmentedClusterTests.cs @@ -0,0 +1,151 @@ +using Compression.Core.FileSystems.Cvf; +using Compression.Tests.Cvf; +using Compression.Tests.Support; +using FileSystem.Stacker; + +namespace Compression.Tests.Stacker; + +/// +/// Stacker fragmented clusters (docs/CVF-CODECS-ON-DISK.md, "Stacker fragmented clusters"): a +/// compressed cluster split over the AMAP run and further pieces named by a list in the run's first +/// sector. The layout was established by building volumes and seeing which ones the dmsdos driver reads +/// back; the DriverProof cases here keep that comparison, and our reader must read the same volumes. +/// +[TestFixture] +public sealed class StackerFragmentedClusterTests { + private const int Ss = 512; + private const int AmapEntryOfCluster2 = (2 + 3) * Ss + 2 * 3; // AMAP area 0 (FAT start 2), cluster 2 + + /// How to damage an otherwise valid piece list. + internal enum Damage { None, WrongTotal, FourthByteSet, NoSignature, PiecePastEnd } + + /// + /// A one-file volume whose single cluster is stored fragmented: a first run of + /// sectors holding the list, then pieces of the given sector counts (the + /// last takes whatever is left of the payload). + /// + internal static byte[] Volume(byte[] cluster, byte[] payload, int firstRun, int[] middlePieces, Damage damage = Damage.None) { + var writer = new GenuineStackerWriter { CompressionMethod = CvfLzMethod.Stored }; + writer.AddFile("F.BIN", cluster); + var image = writer.Build(); + var run = image[AmapEntryOfCluster2] | image[AmapEntryOfCluster2 + 1] << 8; + Array.Clear(image, run * Ss, 16 * Ss); + + var pieceCount = middlePieces.Length + 1; + var headerBytes = 4 + 4 * pieceCount; + var inFirst = firstRun * Ss - headerBytes; + var rest = payload.Length - inFirst - middlePieces.Sum() * Ss; + Assume.That(rest, Is.GreaterThan(0), "the last piece must hold part of the payload"); + int[] counts = [.. middlePieces, (rest + Ss - 1) / Ss]; + + var h = run * Ss; + image[h] = damage == Damage.NoSignature ? (byte)0xEC : (byte)0xED; + image[h + 1] = (byte)pieceCount; + var total = counts.Sum() - 1 + (damage == Damage.WrongTotal ? 1 : 0); + image[h + 2] = (byte)total; + image[h + 3] = (byte)(total >> 8); + + payload.AsSpan(0, inFirst).CopyTo(image.AsSpan(h + headerBytes)); + var at = inFirst; + var sector = run + 20; + for (var i = 0; i < counts.Length; ++i, sector += 10) { + var e = h + 4 + 4 * i; + var named = damage == Damage.PiecePastEnd && i == counts.Length - 1 ? image.Length / Ss + 5 : sector; + image[e] = (byte)named; + image[e + 1] = (byte)(named >> 8); + image[e + 2] = (byte)(counts[i] - 1); + image[e + 3] = damage == Damage.FourthByteSet ? (byte)1 : (byte)0; + var take = Math.Min(counts[i] * Ss, payload.Length - at); + payload.AsSpan(at, take).CopyTo(image.AsSpan(sector * Ss)); + at += take; + } + image[AmapEntryOfCluster2 + 2] = (byte)(0x80 | (firstRun - 1)); // bit 7: fragmented; run shorter than the cluster + return image; + } + + private static byte[] Payload(string codec, byte[] data) => codec == "sd3" ? Sd3Codec.Encode(data) : Sd4Codec.Encode(data); + + private static IEnumerable Shapes() { + foreach (var codec in new[] { "sd3", "sd4" }) { + yield return new TestCaseData(codec, 2, Array.Empty()).SetName($"{{m}}({codec}, two pieces)"); + yield return new TestCaseData(codec, 2, new[] { 2 }).SetName($"{{m}}({codec}, three pieces)"); + yield return new TestCaseData(codec, 1, new[] { 1, 1 }).SetName($"{{m}}({codec}, four pieces, one-sector first run)"); + } + } + + private static byte[] Read(byte[] image) { + using var reader = new GenuineStackerReader(new MemoryStream(image)); + return reader.Extract(reader.Entries.Single(e => e.Name == "F.BIN")); + } + + [Test, Category("Codec"), TestCaseSource(nameof(Shapes))] + public void GivenAFragmentedCluster_WhenRead_ThenItIsByteExact(string codec, int firstRun, int[] middle) { + var data = CvfSamples.Get("text-8k"); + + Assert.That(Read(Volume(data, Payload(codec, data), firstRun, middle)), Is.EqualTo(data)); + } + + [Test, Category("DriverProof"), TestCaseSource(nameof(Shapes))] + public void GivenAFragmentedCluster_WhenReadByDmsdos_ThenItIsByteExact(string codec, int firstRun, int[] middle) { + DmsdosOracle.Require(); + var data = CvfSamples.Get("text-8k"); + var path = Path.Combine(Path.GetTempPath(), $"cwb-stac-frag-{Guid.NewGuid():N}.cvf"); + try { + File.WriteAllBytes(path, Volume(data, Payload(codec, data), firstRun, middle)); + var (exit, bytes) = DmsdosOracle.ReadFile(path, "F.BIN"); + + Assert.That(exit, Is.Zero, "dcread failed"); + Assert.That(bytes.Length, Is.GreaterThanOrEqualTo(data.Length)); + Assert.That(bytes[..data.Length], Is.EqualTo(data)); + } finally { + File.Delete(path); + } + } + + // dmsdos refuses a list whose total disagrees with its entries ("sector count mismatch"); so do we. + [Test, Category("DriverProof")] + public void GivenAWrongPieceTotal_WhenReadByDmsdos_ThenItIsRefusedAsByUs() { + DmsdosOracle.Require(); + var data = CvfSamples.Get("text-8k"); + var image = Volume(data, Sd3Codec.Encode(data), 2, [2], Damage.WrongTotal); + var path = Path.Combine(Path.GetTempPath(), $"cwb-stac-frag-{Guid.NewGuid():N}.cvf"); + try { + File.WriteAllBytes(path, image); + var (exit, bytes) = DmsdosOracle.ReadFile(path, "F.BIN"); + + Assert.That(exit != 0 || bytes.Length < data.Length || !bytes[..data.Length].SequenceEqual(data), Is.True, + "dmsdos read a cluster whose piece total is wrong"); + Assert.That(() => Read(image), Throws.InstanceOf()); + } finally { + File.Delete(path); + } + } + + [Test, Category("Codec")] + public void GivenAWrongPieceTotal_WhenRead_ThenItIsRejected() { + var data = CvfSamples.Get("text-8k"); + + Assert.That(() => Read(Volume(data, Sd3Codec.Encode(data), 2, [2], Damage.WrongTotal)), Throws.InstanceOf()); + } + + [Test, Category("Codec")] + public void GivenAPieceEntryWithItsFourthByteSet_WhenRead_ThenItIsRefusedNotGuessed() { + var data = CvfSamples.Get("text-8k"); + + Assert.That(() => Read(Volume(data, Sd3Codec.Encode(data), 2, [2], Damage.FourthByteSet)), Throws.InstanceOf()); + } + + [Test, Category("Codec")] + public void GivenAFragmentedRunWithoutItsSignature_WhenRead_ThenItIsRejected() { + var data = CvfSamples.Get("text-8k"); + + Assert.That(() => Read(Volume(data, Sd3Codec.Encode(data), 2, [2], Damage.NoSignature)), Throws.InstanceOf()); + } + + [Test, Category("Codec")] + public void GivenAPiecePastTheEndOfTheVolume_WhenRead_ThenItIsRejected() { + var data = CvfSamples.Get("text-8k"); + + Assert.That(() => Read(Volume(data, Sd3Codec.Encode(data), 2, [2], Damage.PiecePastEnd)), Throws.InstanceOf()); + } +} diff --git a/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.Stacker/FORMAT-NOTES.md b/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.Stacker/FORMAT-NOTES.md index 687195d16..2e974f21c 100644 --- a/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.Stacker/FORMAT-NOTES.md +++ b/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.Stacker/FORMAT-NOTES.md @@ -13,7 +13,8 @@ > decodes the full SD-4 format (run tokens, short and long matches), pinned to > clusters the dmsdos SD-3/SD-4 compressors wrote (`CvfReferenceVectorTests`); the > writer's SD-4 output uses back-references and run-coded tables, read back by dmsdos. The AMAP flag nibble is honoured: -> fragmented and suballocated clusters are refused, not guessed at. +> fragmented clusters are read (layout measured against dmsdos, see the spec); +> suballocated clusters are refused, not guessed at. > - **The legacy `StackerWriter` (invented `STKMAP01` trailer) is NOT genuine:** > `dmsdos` panics on it. It is retained only for self-round-trip. > diff --git a/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.Stacker/GenuineStackerReader.cs b/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.Stacker/GenuineStackerReader.cs index 9687298ad..29c716b46 100644 --- a/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.Stacker/GenuineStackerReader.cs +++ b/Hawkynt.FileFormats.FileSystems/FileSystems/FileSystem.Stacker/GenuineStackerReader.cs @@ -162,6 +162,48 @@ private ClusterKind Classify(int sectors, int flags) { }; } + // A fragmented cluster (docs/CVF-CODECS-ON-DISK.md, "Stacker fragmented clusters"): the AMAP run is + // the first piece; its first sector starts 0xED, the number n of further pieces, a 16-bit total of + // their sectors minus one, and n entries (16-bit sector, sectors - 1, a byte that must be 0). The + // compressed payload starts right after the entries and continues through the pieces in order. + private byte[] FragmentedPayload(int cluster, int phys, int sectors) { + var first = phys * Ss; + if (first + sectors * Ss > this._data.Length) + throw new InvalidDataException($"Stacker (genuine): fragmented cluster {cluster} lies past the end of the volume."); + if (this._data[first] != 0xED) + throw new InvalidDataException($"Stacker (genuine): fragmented cluster {cluster} lacks its 0xED piece list."); + + var pieces = this._data[first + 1]; + var headerBytes = 4 + 4 * pieces; + if (headerBytes > sectors * Ss) + throw new InvalidDataException($"Stacker (genuine): fragmented cluster {cluster} has a piece list longer than its first run."); + + var listed = new List<(int Sector, int Count)>(pieces); + var sum = 0; + for (var i = 0; i < pieces; ++i) { + var e = first + 4 + 4 * i; + if (this._data[e + 3] != 0) + throw new NotSupportedException($"Stacker (genuine): fragmented cluster {cluster} uses a piece entry whose fourth byte is set; its meaning has not been verified."); + var count = this._data[e + 2] + 1; + listed.Add((this._data[e] | this._data[e + 1] << 8, count)); + sum += count; + } + if (BinaryPrimitives.ReadUInt16LittleEndian(this._data.AsSpan(first + 2)) != sum - 1) + throw new InvalidDataException($"Stacker (genuine): fragmented cluster {cluster} piece total does not match its piece list."); + + var payload = new byte[sectors * Ss - headerBytes + sum * Ss]; + this._data.AsSpan(first + headerBytes, sectors * Ss - headerBytes).CopyTo(payload); + var at = sectors * Ss - headerBytes; + foreach (var (sector, count) in listed) { + var off = (long)sector * Ss; + if (sector == 0 || off + count * Ss > this._data.Length) + throw new InvalidDataException($"Stacker (genuine): fragmented cluster {cluster} names a piece past the end of the volume."); + this._data.AsSpan((int)off, count * Ss).CopyTo(payload.AsSpan(at)); + at += count * Ss; + } + return payload; + } + private int NextCluster(int cluster) { var fatOff = this._fatStart * Ss; if (this._fat16) @@ -205,9 +247,15 @@ public byte[] Extract(StackerEntry entry) { } break; } + case ClusterKind.Fragmented: { + var payload = this.FragmentedPayload(cluster, phys, sectors); + var full = Compression.Core.FileSystems.Cvf.CvfLzCodec.DecompressStacker(payload, payload.Length, clusterBytes); + Array.Copy(full, 0, output, written, want); + break; + } case var kind: - // Fragmented and suballocated runs exist on real volumes, but no sample of either has - // been verified, so they are refused rather than guessed at. + // Suballocated runs exist on real volumes, but their layout has not been verified, so + // they are refused rather than guessed at. throw new NotSupportedException($"Stacker (genuine): cluster {cluster} of '{entry.Name}' is {kind.ToString().ToLowerInvariant()}; reading such clusters is not supported."); } written += want; diff --git a/docs/CVF-CODECS-ON-DISK.md b/docs/CVF-CODECS-ON-DISK.md index 26b757432..dc9a6a93e 100644 --- a/docs/CVF-CODECS-ON-DISK.md +++ b/docs/CVF-CODECS-ON-DISK.md @@ -50,8 +50,32 @@ A compressed cluster can therefore never occupy all of its cluster's sectors: a as stored whatever its content (the table above). A writer whose compressed payload needs every sector stores the cluster raw instead. -Fragmented and suballocated clusters are refused by `GenuineStackerReader` — no sample of either -has been verified. +### Stacker fragmented clusters + +Measured with dmsdos as a black box: volumes were built with candidate layouts and kept only where +`dcread` returned the cluster byte-exact (no Stac software or sample volume was available, so this is +"as dmsdos reads it"; `StackerFragmentedClusterTests` keeps the comparison). + +* The AMAP entry (bit 7 set, bit 5 clear) names the **first piece**; its sector count covers that + piece only, header sector included. An AMAP count covering all pieces is rejected. +* The first piece's first sector begins with a header: + + | Offset | Size | Field | + |---|---|---| + | 0 | 1 | `0xED` | + | 1 | 1 | *n*, the number of further pieces | + | 2 | 2 | little-endian: the further pieces' sector counts summed, minus one (dmsdos refuses the cluster when this disagrees with the list) | + | 4 | 4·*n* | one entry per further piece: 16-bit little-endian sector, sector count − 1, one byte that was 0 in every accepted volume | + +* The compressed payload (chosen by its first word as for any Stacker cluster; SD-3 and SD-4 were + verified, a DS payload was not read by dmsdos in this position) starts right after the entries, at byte 4 + 4·*n* of the first piece, and continues through the + pieces in list order. A stored payload is not possible: dmsdos decompresses fragmented clusters. +* Accepted: one to three further pieces, first pieces of one and two sectors, SD-3 and SD-4 payloads. + Not established: the meaning of the entries' fourth byte (perhaps the sector's high bits on volumes + past 65535 sectors) — `GenuineStackerReader` refuses an entry where it is non-zero. + +Suballocated clusters (bit 7 and bit 5 set) are refused by `GenuineStackerReader`; their layout has +not been measured. ## 🔢 DS-0-x and JM-0-x @@ -202,5 +226,5 @@ full 15-bit limit for both codes. ## ⚠️ Not covered * **DriveSpace 3 fragmented clusters** (an MDFAT flag dmsdos itself describes as guessed) and - **Stacker fragmented / suballocated clusters** are not read. + **Stacker suballocated clusters** are not read: no layout has been measured for either. * No Microsoft or Stac tool has produced or read any of these streams on this machine. From 92496ad4b62bd2149b9cc872f007cd3f9967c678 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 08:56:03 +0000 Subject: [PATCH 3/4] * refresh the application screenshot --- screenshots/main-window.png | Bin 65657 -> 66228 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/screenshots/main-window.png b/screenshots/main-window.png index e3795c0d04a8041389e0aaa3edc8cc5c89e36820..ffd51c2f536e3289a30437ad6e4e9a846264b65a 100644 GIT binary patch literal 66228 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 literal 65657 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

J-t(C?D*vNbSd$Mpldw%6G^3`$t6)P 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 O*JNyHeySLKDeAvN*8HCU From 63878feda516d52ce29a5b55369fd835394bc2fb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 08:58:23 +0000 Subject: [PATCH 4/4] * refresh the archive browser screenshot --- docs/screenshots/archive-browser.png | Bin 66518 -> 65657 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/screenshots/archive-browser.png b/docs/screenshots/archive-browser.png index de88d7dd43f38a733fe5557dd0e66b254aa140ee..e3795c0d04a8041389e0aaa3edc8cc5c89e36820 100644 GIT binary patch literal 65657 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

J-t(C?D*vNbSd$Mpldw%6G^3`$t6)P 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 O*JNyHeySLKDeAvN*8HCU literal 66518 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 Kj%4xcAO8X%gq{lk