Skip to content
Peterson Fernandes edited this page Aug 19, 2026 · 1 revision

Codecs

CHDSharp implements all ten decompression codecs used by the CHD format. Every codec is a delegate with the signature

delegate ChdError ChdReader(byte[] buffIn, int buffInLength, byte[] buffOut, int buffOutLength, ChdCodecState codec);

dispatched per hunk by ChdBlockRead.ReadBlock according to the map entry's compression type. ChdCodecState holds reusable per-codec scratch (LZMA dictionary window, zstd decompressor, FLAC decoder, Huffman lookup tables) so sequential hunks do not reallocate.

Codec FourCC CD variant Implementation in CHDSharp
Zlib zlib cdzl System.IO.Compression.DeflateStream (managed)
LZMA lzma cdlz Custom pure-C# port of the LZMA SDK decoder
Huffman huff Custom pure-C# Huffman decoder
FLAC flac cdfl Custom pure-C# FLAC decoder
Zstd zstd cdzs ZstdSharp.Port (pure C#)
AVHuff avhu Custom pure-C# A/V Huffman decoder

zlib / cdzl

Raw DEFLATE (-MAX_WBITS, i.e. no zlib wrapper — exactly what chdman writes). CHDSharp uses the managed DeflateStream.

The cdzl CD variant splits each CD frame (2352 bytes of sector data + 96 bytes of subcode = 2448 total) into two streams:

  1. The hunk header carries an ECC-bitmap (1 bit per frame: ECC/sync present or stripped) plus the compressed lengths.
  2. Sector data is zlib-compressed, subcode is zlib-compressed.
  3. On decode, CHDSharp reassembles frames and regenerates the sync header and ECC bytes (CdRom.EccGenerate) for frames flagged as ECC-stripped.

lzma / cdlz

CHD LZMA hunks are raw, headerless LZMA payloads — there is no 5-byte properties header in the stream. Both chdman (encoder) and libchdr (decoder) use fixed settings:

  • lc = 3, lp = 0, pb = 2 → properties byte 0x5D (93).
  • Dictionary size = the hunk size (always ≥ the maximum back-reference distance, since each hunk is compressed independently).

CHDSharp synthesizes these properties and decodes with its own C# port of the LZMA SDK decoder (LZMA/): full literal/match state machine, four repeat distances, range-coder validation, end-marker handling, and 32/64-bit output paths. The reusable dictionary buffer is amortized across hunks via ChdCodecState.

The cdlz variant uses LZMA for sector data and zlib for subcode, with the same ECC/sync regeneration as cdzl.

huff

A custom static 8-bit Huffman codec. The tree itself is Huffman-encoded in the hunk (a 24-code/6-bit meta-Huffman with RLE runs); decoding uses a 16-bit-wide lookup table (HuffmanDecoder). Each hunk decodes exactly hunkbytes symbols. Overflow or under-consumption of the bitstream after flushing is treated as invalid data.

The same machinery (ImportTreeRle) powers AVHuff audio trees and the V5 compressed-map decoding.

flac / cdfl

CHD FLAC hunks are headerless (no STREAMINFO): a single marker byte ('L' = little-endian PCM, 'B' = big-endian PCM) followed by raw FLAC frames. CHDSharp's custom decoder (Flac/AudioDecoder.cs) is a from-scratch C# FLAC decoder (derived from the CUETools.Flake lineage) that supports:

  • 16-bit and 24-bit PCM;
  • mono and all stereo channel modes — left/right, left-side, right-side, mid/side;
  • subframe types: constant, verbatim, fixed (orders 0–4), LPC (orders 1–32, coefficient precision 1–15 bits, 32/64-bit accumulation);
  • all standard block sizes plus 8/16-bit custom sizes;
  • Rice residual coding (methods 0 and 1, partition order ≤ 8, escape codes);
  • CRC-8 (frame header) and CRC-16 (frame) verification, enabled by default.

The marker byte selects endianness; CHDSharp byte-swaps when the stream is big-endian.

The cdfl variant uses FLAC (always byte-swapped) for sector data and zlib for subcode.

zstd / cdzs

Zstandard via the managed ZstdSharp.Port package — no native code. Each hunk is a single-frame zstd block; the decompressed length must exactly equal hunkbytes. The cdzs variant uses zstd for both sector data and subcode with ECC/sync regeneration.

avhu (A/V Huffman)

The laserdisc A/V codec — the one codec libchdr 0.3.0 does not implement. Each hunk is one video frame:

[0]    metasize (1 byte)      metadata payload size
[1]    channels (1 byte)      audio channel count (≤ 16)
[2-3]  samples (2 bytes)      audio samples per channel
[4-5]  width (2 bytes)        video width
[6-7]  height (2 bytes)       video height
[8-9]  audio huffman size     0xFFFF => FLAC audio, 0 => raw deltas, else tree size
[10..] per-channel compressed sizes (2 bytes each)
       metadata | audio trees | per-channel audio | video data

Audio is encoded one mono stream per channel:

  • 0xFFFF → each channel is a headerless mono FLAC stream (16-bit @ 48 kHz in MAME's encoder). CHDSharp configures its FLAC decoder as 16-bit mono and swaps to big-endian output, matching MAME's flac_decoder::reset(48000, 1, ...).
  • non-0xFFFF → two delta Huffman trees (hi/lo bytes) per hunk; samples are delta-decoded from a running previous sample.
  • 0 → uncompressed 16-bit deltas.

Video is delta-RLE Huffman: the first byte must have bit 0x80 set (the only encoding AVHuff produces — lossless). Three delta-RLE Huffman contexts (Y, Cb, Cr) decode width × height bytes of 16-bit YUY2 (Cb,Y,Cr,Y order), with per-row RLE flushing.

History: a stereo laserdisc bug (decoder configured with the header's channel count instead of mono) broke extraction of files like dlair.chd / cubeqst.chd with Chderrdecompressionerror; the fix and a stereo regression fixture (v5_av_stereo.chd) are in the repository.


CD codec shared behavior

The CD variants (cdzl, cdlz, cdzs, cdfl) share the CD frame model:

CD frame = 2352 bytes sector data + 96 bytes subcode = 2448 bytes
Codec Sector stream Subcode stream ECC/sync regeneration
cdzl zlib zlib
cdlz lzma zlib
cdzs zstd zstd
cdfl flac zlib — (stored losslessly)

ECC regeneration uses the standard CD-ROM Reed–Solomon P/Q parity computation (CdRom.EccGenerate).


Error behavior

All codecs report failures as ChdError values:

  • Chderrinvaliddata — structurally invalid compressed stream (bad sync, truncated, tree import failure, bitstream overflow).
  • Chderrdecompressionerror — decompression produced the wrong length, CRC mismatch, or an unexpected exception.

ChdFile.ReadHunk catches exceptions from codec internals, logs the inner exception, and returns Chderrdecompressionerror, so corrupt hunks never surface as raw InvalidDataExceptions.

Clone this wiki locally