Repository navigation
ReleaseNotes
Maintenance release on top of v1.5.1: the solution is now clean under the ReSharper inspector, CI deploys the wiki and documentation site, and the docs/READMEs were refreshed. No public API changes.
The whole solution is clean under the ReSharper command-line inspector: jb inspectcode CSharp_CHDSharp.sln --severity=WARNING reports zero warnings or errors (it reported 308
before the cleanup). The root .editorconfig records the policy:
- The four vendored codec projects (
VendoredZLib,VendoredLZMA,VendoredFlac,VendoredZSTD) are third-party ports kept verbatim. Their style/quality inspections are disabled there because byte-for-bytechdmanparity depends on the code staying untouched. - Consumer-facing models (public library records, the JSON corpus manifest, the WPF data models) are exempt from "never used" inspections: their members are read by callers, serializers and XAML bindings.
- First-party findings were fixed, including redundant null checks in the CLI encode paths, a
write-only AVI stream field, and dead tester state (
LogEntry/LogEntriesand the unreadPerFileResult.FilePath).
-
ci.ymlgained adocsjob: after the three-OS build/test matrix passes on a push tomain, it regenerates the GitHub wiki fromdocs/and requests a Pages rebuild. The tag-triggeredrelease.ymldoes the same on every release, so code, wiki and site stay in sync. - The documentation site keeps its dark-by-default palette with a persisted light/dark toggle.
- Every README (root, library, CLI, tests, generator, tester, benchmarks and
docs/) starts with its badge row.
- Full
dotnet testacrossnet8.0/net9.0/net10.0: 14,004 passed, 0 failed. - Full battle harness: 3,174/3,174 checks, byte-identical
chdmanoutput. -
jb inspectcode CSharp_CHDSharp.sln --severity=WARNING: 0 findings.
Two cue-layout divergences from chdman that made real multisession rips fail are fixed. Both
were found through bug reports against CHDStudio (which uses this library as its built-in
encoder) and are now covered by chdman createcd byte-parity fixtures.
CueParser.ResolveTrackLengths computed endFrames = leadOut - index00 and compared it with the
track's frame count using two int values. MAME compares a uint32_t frame count against a
signed int32_t end frame (cdrom.cpp:2748), so a negative end frame converts to a huge unsigned
value and the trim is skipped. Cues that declare REM LEAD-OUT 00:00:00 (or any address at or
before the last track) therefore failed with:
Track 2: the session lead-out values leave a negative length (-11400 frames); the cue's
REM SESSION/LEAD-OUT values do not match the image.
while chdman createcd encoded the same cue successfully (the no-op lead-out leaves the cue's
INDEX positions in charge). The parser now performs the same unsigned comparison, so the layout -
and the CHD bytes - match chdman.
ChdEncoder.ReadCdHunk treated the last PadFrames frames of a track as zero-filled padding. In
chdman the pad start is an unsigned offset from the track start (chdman.cpp:471-472); when the
pad is larger than the track the offset wraps below the track start, the pad region is
unreachable, and every frame is read from the source file. The encoder now mirrors that rule
instead of writing a fully zeroed track, and the GD-ROM data/pad/split ordering follows the same
frame layout (splitStart = frames - splitFrames, padStart = splitStart - padFrames).
- New
chdman createcdbyte-parity fixtures: zero lead-out, lead-out before the track, and the pad-larger-than-track layout. - Parser regression tests for both lead-out shapes.
-
CHDSharpEncoderTest454/454,CHDSharpTest4214/4214, battle harness--quick905/905 checks.
First release produced end-to-end by GitHub Actions: one tag builds the six platform bundles, creates the GitHub release, publishes the NuGet package, and syncs the wiki and documentation site.
Tag-triggered (v*), with a guard that fails when the tag does not match Directory.Build.props. Three jobs:
-
Bundles & GitHub release -
scripts/package-release.ps1publishes the CLI forwin-x64,win-arm64,linux-x64,linux-arm64,osx-x64andosx-arm64as framework-dependent single-file binaries (the .NET runtime is not embedded), appendsLICENSE.txt,README.md,README.txtandWhatsNew.md, zips them asCHDSharp_<rid>_v<version>.zip, and attaches them to the GitHub release. -
NuGet publish - packs
CHDSharpLiband pushes to nuget.org; skips versions that are already published (idempotent re-runs). -
Wiki & Pages - regenerates the wiki pages from
docs/viascripts/prepare-wiki.ps1(including the wiki side menu) and pushes them to the wiki repository; requests a Pages rebuild.
GitHub releases now ship only the CLI; the WPF Tester remains source-only.
VersionCheckService built the expected release-asset name from the currently running version and mapped every OS to win-*, so the direct download link could never match (historical assets were also named CHDSharpCli_...). It now looks up CHDSharp_{os}-{arch}_v{releaseVersion}.zip - the exact names the release ships - and falls back to the releases page when the platform has no bundle.
- The
docs/folder is deployed twice: as the Jekyll site (GitHub Pages) and as the GitHub wiki, with a shared side-menu structure. - Dark theme by default on the documentation site (persisted light/dark toggle).
- Test-spawned CLI runs set
CHDSHARP_NO_BUG_REPORTS=1/CHDSHARP_NO_TELEMETRY=1(CI workflow and the test harness), so local full-suite runs no longer file bug reports. - Badges (CI, release, NuGet, platform, parity, license) on every README.
| Bundle | Platform | Architecture |
|---|---|---|
CHDSharp_win-x64_v1.5.0.zip |
Windows | x64 |
CHDSharp_win-arm64_v1.5.0.zip |
Windows | ARM64 |
CHDSharp_linux-x64_v1.5.0.zip |
Linux | x64 |
CHDSharp_linux-arm64_v1.5.0.zip |
Linux | ARM64 |
CHDSharp_osx-x64_v1.5.0.zip |
macOS | x64 (Intel) |
CHDSharp_osx-arm64_v1.5.0.zip |
macOS | ARM64 (Apple Silicon) |
Framework-dependent single-file builds; requires the .NET 10 runtime.
dotnet add package CHDSharp --version 1.5.0
Targets net8.0, net9.0, net10.0. Pure C# - zero native dependencies.
Ports MAME's multisession cue handling (REM SESSION / LEAD-IN / LEAD-OUT / session pregap) — the older IsoBuster/ImgBurn/DiscImageCreator layout rules — so multisession cues that chdman converts produce the same CHD through the built-in encoder.
Cues describing more than one session (REM SESSION n) carry session lead-out/lead-in times and sometimes a session REM PREGAP. MAME's parse_cue uses them to lay the image out — the built-in parser previously ignored them, so those cues could be encoded with a different track layout than chdman.
-
CueParsernow parses the REM session extensions; tracks carrySession,LeadInandLeadOut(MAME'strack_input_info), andCdTocFlags.MultisessionmirrorsCD_FLAG_MULTISESSION. -
Single-bin layouts (IsoBuster/ImgBurn): an absolute
REM LEAD-OUTtrims the session's last track and the inter-session dummy data becomes zero-filled pad frames; the lead-out is rewritten to the duration to the next track. -
Multi-bin layouts (Redump/DiscImageCreator): the standard lead-out (6750 frames for the first session, 2250 afterwards) and lead-in (4500 frames) are synthesized when the cue omits them; a session
REM PREGAPis folded into the new session's lead-in. - The
.imgextension heuristic is replicated: DiscImageCreator images have no dummy data, so the gap stays part of the next track's data. - Malformed lead-out values that MAME's math would turn into negative lengths are rejected with a clear
InvalidDataExceptioninstead of being encoded as a silently wrong image. - Verified byte-identical to
chdman createcdfor single-bin.bin, single-bin.imgand multi-bin synthetic fixtures (MultisessionCueChdmanValidationTests); full suite 451/451.
dotnet add package CHDSharp --version 1.4.5
Targets net8.0, net9.0, net10.0. Pure C# - zero native dependencies.
NuGet: published -
dotnet add package CHDSharp --version 1.4.5.
Two field-reported cdrdao/Redump descriptor divergences from MAME fixed; the built-in encoder now converts those files byte-identically to chdman.
A Redump Dreamcast .cue stores the single-density and high-density images in one descriptor, separated by REM SINGLE-DENSITY AREA / REM HIGH-DENSITY AREA comments. MAME's parse_cue detects this (is_gdicue), moves each track's pregap onto the previous track as a split region, and pads the last single-density track up to LBA 45000; chdman marks the result with CHGD GD-ROM metadata. The built-in cue parser ignored the comments, producing a plain-CD TOC with wrong frame counts, logical size and metadata.
-
CueParsernow applies the fullis_gdromlayout fixups;CdTrackcarriesSplitFrames(pregap sectors stored ahead of their own file) andMulticueArea(density area). -
EncodeCdreads split frames from the next track's file start without the current track's file offset, keeping the current track's audio swap — exactly like MAME'schd_cd_compressor::read_data. - Verified on a real 18 Wheeler — American Pro Trucker (USA) Redump multi-cue (1.19 GB): byte-identical CHD to
chdman createcd(same SHA-1, sameCHGDmetadata), andchdman extractcdon our output restores all three original track files byte-for-byte.
cdrdao emits a bare START (no time) in its documented TOC examples. MAME's parse_toc tokenizes the missing time as an empty token and msf_to_frames("") is 0, so chdman converts those files with no pregap. The built-in TocParser rejected the line with Malformed START command: START, failing every .toc that chdman accepts.
- A bare
STARTnow setsPregap = 0(identical toSTART 00:00:00), matching MAME 0.289'scdrom.cppparse_tocexactly. - Regression tests pin the parse (
Toc_BareStart_MeansZeroPregap) and full byte parity withchdman createcdfor bothSTARTforms (SHA-256 comparison inTocParserChdmanValidationTests).
-
CHDSharpEncoderTest: 441/441 (parsers, encoder, codec and chdman-validation suites).
dotnet add package CHDSharp --version 1.4.4
Targets net8.0, net9.0, net10.0. Pure C# - zero native dependencies.
Consolidates all changes since v1.4.2. Closes the partial-tail raw-encode corruption and the last FLAC byte-parity divergence. Battle harness green: 3174/3174 synthetic vs MAME 0.289.
EncodeRaw/createraw on inputs of ≥ 513 hunks whose length is not a hunk multiple could produce a self-consistent-but-wrong CHD: stale compression ring-buffer slots from the previous fill cycle leaked into partial final hunks. The fix landed alongside guardrail tests so the class of bug cannot silently return:
- a synthetic partial-tail probe that pins the exact stale-slot bytes,
- partial-last-hunk unit tests per codec,
- a resized long-tail case in the battle harness (
raw-encode long-tail x flac/zlib/zstd).
The vendored FLAC encoder now matches chdman byte-for-byte on the remaining divergence: the fixed-predictor analysis window and the tukey window's cosf rounding (native cosf semantics replicated). Verified byte-identical on:
- the 7.8 GB Perfect Dark Zero XGD2 image (1,912,816 hunks, chdman default codec list),
- a 100 MB partial-tail slice of the same source,
- three single-hunk repro cases.
- Stale work-buffer tail + LZMA match-finder insert parity (raw encode).
- Detailed decompression-failure messages: hunk index, codec, CRC/truncation reasons.
- chdman-style progress + speed-meter output.
-
CHDSharpTest: 12,648/12,648 (436/436 per TFM on net8.0/net9.0/net10.0 → 1,308/1,308 aggregate via the console runners). -
CHDSharpEncoderTest: 1,308/1,308. -
CHDSharpBattleTest: 3174/3174, includingraw-encode long-tail x *for all three codecs.
dotnet add package CHDSharp --version 1.4.3
Targets net8.0, net9.0, net10.0. Pure C# - zero native dependencies.
Final byte-parity gaps closed against MAME 0.289. Battle harness stays green: 2907/2907 synthetic + 3003/3003 real-world checks.
The raw encoder now replicates chd.cpp's 1 MiB compression work buffer (RingBufferedRawReader): a 256-hunk ring filled in 128-hunk batches (async_read), where ring slots past EOF or past a short final batch keep the stale bytes of the cycle that previously occupied them. Hunks are compressed from the ring — stale tail bytes included — while the raw SHA-1 folds only the valid bytes, exactly like m_compsha1.append(dest, numbytes). This fixes the LZMA byte-parity hash-update-order bug: createraw on inputs whose length is not a multiple of the hunk size now produces byte-identical files and identical SHA-1 to chdman. Companion fixes in FlacLpcMath, LibFlacEncoder, LzBinTree and LzmaEncoder keep the remaining codec paths byte-exact.
- Size parsing now matches
chdman.cpp:2035—sscanf("%I64u")reads leading digits only and silently ignores trailing characters ("512K"= 512 bytes). - Small sizes are rounded up to the guessed-CHS product (
guess_chsparity): the CHD logical size is exactlycylinders * heads * sectors * bytes_per_sector, and hunks past the source's data are encoded from the work buffer's stale slots (see above), matchingchdman createhd -i. -
InfoTest/VerifyTestnow receive the full arg list so strict option validation (duplicate-i/--inputdetection etc.) matcheschdman'score_optionsparser;inforeturns exit code 1 on failure.
createld / extractld AVI output is now byte-identical to chdman (AviWriter fixes).
DVD metadata is written as exactly one NUL byte (length 1), not an empty payload — chd.h:351's std::string overload passes input.length() + 1, so write_metadata(DVD_METADATA_TAG, 0, "") stores a single NUL. This corrects the v1.4.1 note (which documented an empty payload).
-
createrawaccepts--hunk-size/--unit-sizealiases alongside--hunksize/--unitsize/-hs/-us. -
extractcd%tfilename-template regex rewritten with named groups (escaped%%and width/sign specifiers handled correctly). -
hashcommand help text corrected (--result text|json|sfv,--tracks, no phantomcountargument). - New
ChdEncodeOptions.LogicalLengthBytesoverride for byte-parity encoding of over-/under-sizedcreatehdsources.
-
CHDSharpBattleTestmulti-targetsnet8.0;net9.0;net10.0withLangVersion=latestand copies the built CLI next to the battle exe for self-contained parity runs (BattleHarness.CliFull.cs— fullchdmanCLI suite, 2463 lines). - Formatting and style pass across CLI, encoder, and vendored codecs; analyzer warnings silenced (MA0008 StructLayout, unused-variable suppressions).
-
divergency.mdremoved;Missmatch.mdnow records the battle result: 2907 checks — 2907 passed, 0 failed.
- New
docs/cli-commands.md(413 lines) — fullchdman/CHDSharp argument tables for every command;CHDSharpCli/README.mdexpanded. - CHD format history added to
docs/chd-format.md; language simplified and stale DVD metadata notes corrected. - Wiki home (
docs/README.md) rewritten with CHD history;ReleaseNotesadded to sidebar and_config.yml; docs site URL fixed; Peterson Fernandes added to key contributors.
dotnet add package CHDSharp --version 1.4.2
Targets net8.0, net9.0, net10.0. Pure C# — zero native dependencies.
Pre-built self-contained single-file executables (binary: CHDSharp):
| Binary | Platform | Architecture |
|---|---|---|
CHDSharp_win-x64_v1.4.2.zip |
Windows | x64 |
CHDSharp_win-arm64_v1.4.2.zip |
Windows | ARM64 |
CHDSharp_linux-x64_v1.4.2.zip |
Linux | x64 |
CHDSharp_linux-arm64_v1.4.2.zip |
Linux | ARM64 |
CHDSharpTester_win-x64_v1.4.2.zip |
Windows | x64 |
CHDSharpTester_win-arm64_v1.4.2.zip |
Windows | ARM64 |
Complete chdman parity — 16 audited discrepancies (D1–D16) plus EdgeGaps §1–§3 fixed and verified against MAME 0.289. All 2907/2907 synthetic + 3003/3003 real-world battle checks pass.
CHDSharp createhd -i image.img -o out.chd now synthesizes the GDDD hard-disk geometry tag
(CYLS:…HEADS:…SECS:…BPS:…) via MetadataWriter.BuildHardDiskMetadata, matching
chdman createhd -i byte-for-byte. The 51-byte delta on all 3 HDD test images is gone and
chdman info shows identical GDDD on both products. Honours -isb/-ish/-ib/-ih slicing when present.
CHDSharpLib.ChdFile.ExtractToDirectory(..., cooked: true) now writes cooked sectors
(track.DataSize per frame, 2048/2352/…, subcode omitted, audio byte-swapped) and
CHDSharp extractcd defaults to cooked (with --raw/--raw-frames to keep 2448-byte frames).
The 43 CD + 3 GD-ROM extractcd battleground cases flip from 0/43 to 43/43 parity
and disc.bin sizes match chdman (e.g. Akai Shizuku 8,773,030 B).
cdzl/cdfl on audio-bearing discs historically picked different FLAC subframes than
MAME's native libFLAC (container bytes differed while Data SHA-1/overall SHA-1 and
chdman verify stayed identical). v1.4.3 fixed the two root causes — the fixed-predictor
analysis window and the tukey-window cosf rounding — and createcd/copy with flac
in the codec list is now byte-identical to chdman (verified on the 56-file corpus and a
7.8 GB dual-layer XGD2 image).
Ported chdman has_physical_pregap / padframes / splitframes fixup for GD-ROM Redump CUE/BIN.
ChdTrackInfo now exposes SplitFrames / PhysFrameOfs, CHDFile adds TryWriteGdRomTrackCooked with cross-track reads and the 45000-frame high-density boundary skip, and extractcd emits REM SINGLE-DENSITY AREA / REM HIGH-DENSITY AREA. MODE_GDI (pad-aware LBA) vs MODE_CUEBIN (split CUE with pad/split) are now handled per chdman (is_splitbin = mode==GDI || --splitbin || (is_gdrom && mode==CUEBIN)).
Option parsing now matches chdman verbatim: unknown / duplicate / missing-parameter errors, per-command valid sets (createraw rejects -tp/-d, etc.), isb/ish/ib/ih mutual exclusion (Start offset cannot be in both…), and parse_number trailing-B handling (10MB). Error phrasing (Required parameters missing, Multiple parameters…) matches chdman.cpp.
-
copynow usesget_compression_defaults:2426per media type — HDD/DVD →lzma,zlib,huff,flac, CD/GD →cdlz,cdlz,cdfl, laserdisc →avhu(ChdEncoder.GetDefaultCopyCodecs). - Parent hunk-size inheritance and factor check (
parse_hunk_size:1331,hunk % input && input % hunk) forcreateraw/createhd/createcd/createdvd/createld/copy. -
createhdtemplate+parent /chs+parent guards (do_create_hd:1980/1998),IDENTCHS extraction from ATA bytes 2/6/12 (>=16_514_064 → cyl=0),GDDDfallback,filesize % sectorsizecheck andguess_chsparity. -
info --verbosenow shows per-codecSELF/PARENT/MINIhunk buckets andverifythrottles to 0.5 s withError:tostderr(report_error:950parity).
-
MetadataWriter.BuildDvdMetadatanow returns an empty payload (length 0) perchdman write_metadata(DVD_METADATA_TAG,0,"")instead of a single0x00byte. -
CHDFileextraction now uses a 32 MiB temp buffer aligned tooutputFrameSizewith batch writes, correct audio byte-swap (CUEBIN always, GDI onlyVersion>4perchdman 2959/2994), and proper subcode preservation forMODE_NORMAL(.toc) vs warn/omit forCUEBIN/GDI.
-
listtemplatesnow shows all 17 templates (added 4× Quantum Fireball CR). -
createraw/createhddefaults now correctly uses_default_raw_compression/s_default_hd_compression(lzma,zlib,huff,flac) and enforce blank HDnoneonly. -
createrawunit-size / hunk-size defaults, 16 B–1 MiB limits and granularity checks matchchdman. -
addmeta --valuetextno longer appends a trailing NUL (text.size()parity) andextractcd --outputbinnow requires%twhenis_splitbin.
New docs/chd-deep-reference.md — audited expansion of References/CHDInfo.md against MAME 0.289 (chd.h/chd.cpp/chdcodec.h) and CHDSharpLib (CHDHeaders.cs/CHDBlockRead.cs/MapCompressor.cs). 9 inline ⚠ Corrections (ZLIB_PLUS secondary codec, cdzs subcode zstd, work buffer 256 vs 257, RLE thresholds 4–19/20–275, promotion before RLE, implicit datastart offset, DVD empty payload, uncompressed-map offsetWord, hunkbytes limits) and a full V1–V5, codec, and map-encoding reference. Sidebar updated. docs/chd-format.md corrected to 0.289 and RLE description fixed.
Added Meziantou.Analyzer 3.0.190 to every project (was 3.0.177 centrally) and fixed all analyzer warnings. Zero build warnings. VendoredZSTD/VendoredZLib style cleanups only — no codec behaviour change.
dotnet add package CHDSharp --version 1.4.1
Targets net8.0, net9.0, net10.0. Pure C# — zero native dependencies.
Pre-built self-contained single-file executables (binary: CHDSharp):
| Binary | Platform | Architecture |
|---|---|---|
CHDSharp_win-x64_v1.4.1.zip |
Windows | x64 |
CHDSharp_win-arm64_v1.4.1.zip |
Windows | ARM64 |
CHDSharp_linux-x64_v1.4.1.zip |
Linux | x64 |
CHDSharp_linux-arm64_v1.4.1.zip |
Linux | ARM64 |
CHDSharpTester_win-x64_v1.4.1.zip |
Windows | x64 |
CHDSharpTester_win-arm64_v1.4.1.zip |
Windows | ARM64 |
The CLI (binary renamed CHDSharp) now accepts every chdman subcommand with the same
option names and exit-code conventions:
info, verify, createraw, createhd, createcd, createdvd, createld,
extractraw, extracthd, extractcd, extractdvd, extractld, copy, addmeta,
delmeta, dumpmeta, listtemplates.
Common options mirror chdman (--input/-i, --output/-o, --inputparent/-ip,
--outputparent/-op, --compression/-c, --hunksize/-hs, --unitsize/-us,
--numprocessors/-np, --force/-f, --verbose/-v, --chs, --sectorsize/-ss, …) and the
convenience commands from earlier versions (directory scan, --list, --random, --parent,
--toc, --cue, --classify, --detect, --hash, --batch) are all still available.
The new CHDSharpBattleTest harness exhaustively cross-checks the CHDSharpLib decoder
and the CHDSharp.Encoder encoder against MAME's chdman.exe on a deterministic corpus
of raw and CD images — chdman create*, verify, info, extract parity, and byte-identical
encode checks for every writable codec, plus delta/parent, CD, and A/V laserdisc scenarios.
The full suite passes 2611/2611 checks (synthetic corpus); a real-world scan of 56 CHDs
passes 3003/3003 checks. It can also scan real-world *.chd folders
(--real <dir>) to battle-test any collection. See Testing.
Every encoder output now matches MAME's chdman byte-for-byte, closing the three
previously non-exact paths:
-
createld(laserdisc AVHuff) — fixed an off-by-four AVCCidx1base offset that shifted audio by one sample and misaligned video; the AVHuff codec now correctly compresses zero-padded single-frame hunks; and the mono-FLAC encoder now evaluates every fixed-predictor order per frame (matching libFLAC 1.4.3's behaviour for the mono/48 kHz avhu path). -
cdzs(CD Zstandard) — the in-repoVendoredZSTDport (ZstdSharp 0.7.6 source, a C-to-C# port of the zstd 1.5.5 tree that MAME bundles) now emits frames byte-identical to C zstd for the same hunk buffers, replacing the previous NuGetZstdSharp.Portdependency. -
zstd(raw Zstandard) — same fix ascdzs; the encoder'szstdoutput is now byte-identical tochdman createraw -c zstd.
The ZstdSharp.Port NuGet package has been replaced by an in-repo VendoredZSTD project
containing a full C-to-C# port of the zstd 1.5.5 source tree (the same version MAME
bundles). Both the encoder and decoder are included. Every codec is vendored in-repo
(no native dependencies); the only runtime NuGet dependencies are the optional
Microsoft.Extensions.Logging.Abstractions logging abstraction and System.IO.Hashing.
A deep code review across the CLI, library, and battle harness fixed 38 bugs. Highlights:
-
Exit codes & error handling — the CLI now returns proper exit codes (
0/1/3),CheckFile(Stream)catchesIOException, andVerifyListlogs the exception type verbatim. -
Encoding fixes —
createcddefaults tocdlz,cdzl,cdfl;createhd --inputroutes toEncodeRaw;verify --inputparentis passed toCheckFileWithParent; all create/copy commands require--forceto overwrite;dumpmeta --forceis implemented;extractcd --splitbindelegates to the library's per-track writer and--outputbinupdates.gdifor GD-ROM;LzmaStream.Seekreturns the correct position. -
Concurrency — the parallel-verification master error is now thread-safe
(
StrongBox+Interlocked),DisposeAsyncdrains per-thread codec states, and the read-ahead manager no longer over-releases its semaphore. -
Robustness —
ReadHunk(Span)caches hunks on success,FindRepeatedBlocksvalidates map offset bounds,ValidateSizeLimitsrejectsUnitbytes == 0,MemoryMappedFileis disposed when view creation fails, andextractrawwrites to a temp file before renaming. -
Diagnostics — progress callbacks use
Interlocked, version display usesBuildinstead ofMinorRevision, bug reports scrub PII, and API keys are no longer stored in plain text. -
CLI helpers —
--batchfilters by correct extensions,Checkdirfilters exact.chdextension and skips symlinks,helpcovers all missing commands, andaddmetarejects non-ASCII tag names.
Stabilized flaky tests so the full suite passes consistently on every target framework.
Resolved all compiler, analyzer, and Rider-inspection warnings across VendoredZSTD
and VendoredZlib, including XML doc-comment and constant compile errors, and removed the
outdated References/MissingFeatures.md.
-
CLI binary renamed — the CLI executable is now
CHDSharp(e.g.CHDSharp.exe) instead ofCHDSharpCli. Subcommand syntax changed to thechdmanstyle (CHDSharp createcd -o out.chd -i in.cue); the old--create/--createcd/--createhd/--copyforms are replaced. -
ZstdSharp.Portpackage removed — code that referenced it directly must now use the library's own codecs; no action is needed for normal library use.
dotnet add package CHDSharp --version 1.4.0
Pre-built self-contained single-file executables (binary renamed from CHDSharpCli to CHDSharp):
| Binary | Platform | Architecture |
|---|---|---|
CHDSharp_win-x64_v1.4.0.zip |
Windows | x64 |
CHDSharp_win-arm64_v1.4.0.zip |
Windows | ARM64 |
CHDSharp_linux-x64_v1.4.0.zip |
Linux | x64 |
CHDSharp_linux-arm64_v1.4.0.zip |
Linux | ARM64 |
CHDSharpTester_win-x64_v1.4.0.zip |
Windows | x64 |
CHDSharpTester_win-arm64_v1.4.0.zip |
Windows | ARM64 |
CHDSharp v1.3.0 merges the encoder subsystem into the main library, adds hard disk metadata support, hardens security, and fixes critical decoder bugs. The library now provides full CHD read/write capabilities in a single NuGet package with zero native dependencies.
The CHDSharpEncoder project has been merged into CHDSharpLib. The library now includes full CHD creation/encoding capabilities (ChdEncoder) alongside the existing reader. No separate package needed.
-
Raw binary encoding --
ChdEncoder.EncodeRaw()with auto or custom hunk/unit sizes -
CD image encoding --
ChdEncoder.EncodeCd()from CUE, GDI, ISO, TOC, or NRG -
Blank HD creation --
ChdEncoder.CreateBlank()/CreateBlankWithChs()for zero-filled images -
Re-compression --
ChdEncoder.Copy()re-compresses existing CHDs with new codecs - Delta/parent CHDs -- Create differential CHDs against a parent
- All 10 codecs -- zlib, zstd, lzma, huff, flac, cdzl, cdlz, cdzs, cdfl, none; best-per-hunk selection
- Parallel encoding -- 1-64 workers, deterministic output regardless of worker count
-
100% chdman match -- Byte-identical output vs
chdmanfor all codecs
-
IDNT metadata -- Read/write
IDNTmetadata (ATA IDENTIFY DEVICE response, 512 bytes) preserving original drive model, serial, CHS geometry, and firmware revision. Access viaChdFile.IdentDataproperty.--ident <path>flag oncreatehdCLI command. -
KEY metadata -- Read/write
KEYmetadata (encryption key) used by OG Xbox and other platforms with encrypted HDD contents. Access viaChdFile.KeyDataproperty. -
CIS metadata -- Read/write
CISmetadata (Card Information Structure) used by PC Engine CD and other PCMCIA platforms. Access viaChdFile.PcmciaCisDataproperty.
- Bounded metadata string parsing -- Hardened track metadata parsing against crafted payloads. TYPE/SUBTYPE/PGTYPE/PGSUB fields are capped at 15 characters. Track metadata payloads > 4 KiB are rejected. Embedded null bytes in payloads are rejected. Metadata entries > 64 KiB are rejected at the storage layer.
-
Deflate decoder infinite-loop guard -- Added
here.bits == 0guards in the inflate state machine. When a Huffman table entry hasbits=0, the decoder transitions toBadmode and returnsZ_DATA_ERRORinstead of looping indefinitely.
-
ChdImageStream--ChdFile.OpenAsStream()returns a read-only, seekableStreamwrapping the decompressed CHD. SupportsRead,ReadAsync,Seek,Position, andLength. -
Span<byte>read overloads --ReadHunk(uint, Span<byte>)andRead(ulong, Span<byte>, int)for zero-copy paths. -
Threaded read-ahead decompression --
ChdFile.ConfigureReadAhead(int)enables background pre-decompression of upcoming hunks. -
Lazy parent resolution --
ParentResolvercallback resolves parents by SHA1/MD5 hash on first read. -
CD/GD-ROM track (TOC) parsing -- Full track layout via
Tracksproperty withChdTrackInfo, CUE/GDI generation, and extraction. -
LBA/MSF sector reads --
ReadSector(),ReadSectorMsf(), andReadFrame()for CD/GD-ROM sector access. -
UnitBytesproperty -- Derives sector size from metadata for all CHD versions.
- Fixed VendoredZLib
#elsecompilation branches using incorrect field names (snake_case vs PascalCase) - Fixed deflate decoder infinite-loop on crafted input (miniz 3.1.2 fix parity)
-
Target frameworks:
net8.0,net9.0,net10.0 - Operating systems: Windows, Linux, macOS
- Zero native dependencies -- all codecs implemented in pure C#
dotnet add package CHDSharp --version 1.3.0
Pre-built self-contained single-file executables (binary renamed from CHDSharpCli to CHDSharp):
| Binary | Platform | Architecture |
|---|---|---|
CHDSharp_win-x64_v1.3.0.zip |
Windows | x64 |
CHDSharp_win-arm64_v1.3.0.zip |
Windows | ARM64 |
CHDSharp_linux-x64_v1.3.0.zip |
Linux | x64 |
CHDSharp_linux-arm64_v1.3.0.zip |
Linux | ARM64 |
CHDSharpTester_win-x64_v1.3.0.zip |
Windows | x64 |
CHDSharpTester_win-arm64_v1.3.0.zip |
Windows | ARM64 |
- The
CHDSharpEncoderproject is no longer available as a separate package. All encoder functionality is now inCHDSharpLibunder theCHDSharp.Encodernamespace. - The
ChdEncoderclass replaces the oldCHDSharpEncoder.ChdEncoderclass. - All existing reading APIs remain unchanged and fully backward compatible.
-
Peterson Fernandes -- extended Gordon Jefferyes' original CHD reader into a full-featured library: encoder, CLI, NuGet package, VendoredZSTD, async/stream APIs, parent/child chaining, parallel verification and encoding, V5 compressed maps, DVD/GD-ROM/laserdisc support, 100%
chdmanbyte-parity, and a comprehensive test suite - Gordon Jefferyes -- original C# CHDSharp reader (RomVault)
-
MAME -- CHD format specification and
chdmanreference implementation - libchdr (Romain Tisseraud) -- C reference library
-
ZstdSharp.Port (Oleg Stepanischev) -- pure C# Zstd port, vendored in-repo as
VendoredZSTD - CUETools.Flake (Grigory Chudov) -- FLAC encoder (LGPL 2.1)
CHDSharp
Format & internals
API & usage
CLI & writing
Parity & validation
Operations
Reference