Deep-review hardening and GUI experience release. A four-pass code review of the
whole repository produced 80 tracked findings (path escapes from crafted image
names, in-place patch collisions with the tag/descriptor sectors, CWD leaks,
partial-output artifacts, CLI contract mismatches, vacuous tests, doc drift);
this release resolves them, adds the follow-up regressions found while
re-reviewing the fixes, and closes a final pass over those commits (API
compatibility, -c . default output, host-safe rewrite/list, audit/extraction
parity, remap cleanup). The desktop app gains About/Donate/Exit actions, F8
screenshots, and a shared startup update check.
Namespace note: data models, records, and enums now live in
XISOSharp.Models — including the previously root-namespace UnpackOptions,
ProcessRunResult, ExplorerNode, and XisoExplorerOptions; add
using XISOSharp.Models; when upgrading from 1.4.1. Targets remain net8.0 /
net9.0 / net10.0. Full suite green on all three: 1817 passed / 1 opt-in
skip (1818) on net10.0 and 1756 passed / 1 opt-in skip (1757) on
net8.0/net9.0. The suite now disables xUnit parallelization assembly-wide
because create/extract mutate the process-wide current directory.
- GUI: Donate / About / Exit header actions and a proper About dialog, F8
window screenshots, a startup "update available" prompt, tooltips throughout,
corrected drag-and-drop routing (including
.zarin multi-item drops), and Wipe/Trim output paths that actually work. - One update checker: the CLI and GUI now share
UpdateCore(version parsing/comparison, RID mapping, asset naming, 24-hour disk cache), so the GUI no longer probes GitHub on every start and the two front-ends cannot drift. - Safety-first patching and writing: crafted image names cannot escape the
destination, in-place patches never allocate over the optimized tag or the
ECMA-119 descriptors (even for header-only volumes), the working directory is
always restored, and failed or cancelled runs no longer leave truncated
.iso/.cso/.zarartifacts — including thebuild-imageremap path. - Backup discipline:
.oldbackups survive failed rewrites, an existing backup is never overwritten,-Donly unlinks after a fully successful (and validated) rewrite, and a pre-existing.zar/split part is never deleted by a failed call. - Compatibility parity: listing and rewriting accept every structurally valid name on every host (a colon name is only refused at extract on Windows), and audit now rejects an oversized root table exactly like extraction does.
- Telemetry hardening: the shared API key is no longer a plaintext literal
(double-Base64
ApiKeyStore), expected operational warnings stay in the local log (NoBugReport), and the bug-report dedupe map is bounded. - Tester: the list comparison now parses what the tools actually print and decodes extract-xiso's Latin-1 output, so non-ASCII names compare correctly.
- Crafted image names that could escape the destination are rejected or
sanitized everywhere: separators, the Windows drive-relative colon
(
C:evil), and any trailing dot/space that Win32 strips during path normalization (foo.no longer collides withfoo). - Host safety applies where host paths are built: extraction refuses such names on Windows, while listing, tree, rewrite, and ZAR packing follow the reference tool and accept them on every host (a colon stays a valid name on Unix).
- Every directory walk bounds child offsets by the recorded table size (not just image length), so a corrupt offset into another file's data can no longer audit as valid while extraction rejects it.
- The xdvdfs all-zero empty-table sentinel is handled by
XisoRangesand the auditor like the0xFFFFsentinel;./..structural records are skipped consistently (children still walked) acrossTraverseXiso,AuditWalk,SalvageWalk,CollectWalk,GetValidSectors, andCollectFileEntries. - A truncated directory-entry name now fails the walk instead of silently dropping the entry and its right-sibling chain from the sector map.
- Zero-size directory entries (a known real-world xdvdfs quirk) list as empty instead of failing the new table bounds.
- The sector layout reserves the optimized tag (partition sector 15) and the ISO9660 descriptor pair (sectors 16-17) plus the layout-tool signature sector when present — including header-only (no files) volumes — so an allocation can never overwrite them.
- Table and extent sector counts use 64-bit math: a ~4 GiB file is no longer dropped from the allocation map by a 32-bit wrap.
- The root directory size is recorded even when the table stays in place, so appended entries stay reachable.
- Directory tables fail instead of truncating when a child offset would encode
the
0xFFFFempty-child sentinel.
CreateXisorestores the working directory on every path (success, error, cancellation); a relative-dresolves against the caller's CWD, not the source directory the write phase changes into.- A current-directory source (
-c ./-c ..) packs the source and defaults the output to the source's parent (mirroring-c <leaf>from that parent); the same-leaf collision guard no longer aborts the default case. - Failed/cancelled creates delete the partial output — including the
build-imageremap writer — and a failed compress deletes the partial.csoand closes split-part handles before deleting (FileShare.Noneblocked the delete on Windows). CreateZaronly deletes an output it created: a pre-existing.zarsurvives a parse failure or a cancellation before packing.- CISO reading validates version/alignment/size and bounds the index table against the file; reads at/after the logical end return no data instead of the last block's zero padding; the final index entry rejects 31-bit overflow.
- ZAR packing rejects a zero-size root table (nonzero sector), treats a zero-size nested directory as empty, and fails on a truncated name.
- Split CSO/ISO part discovery appends
.{n}to the base name, so dotted names (My.Game.1.cso) reopen correctly. Latin1Encodingrange validation is overflow-safe and follows theEncodingcontract (ArgumentOutOfRangeExceptionfor indexes).XisoExplorer.Disposeserializes with keep-open reads;XisoPathsresolves both sides of an input==output check against one CWD snapshot (and fixes containment under filesystem roots).
- The repairer writes a missing optimized tag at the disc offset for prepended/Redump images instead of corrupting the video partition.
- Path and block-device audits agree on the trailing header magic and probe the tag at the detected disc offset, including the empty-image path.
- Audit and extraction agree on the root table: a root size past the end of the image is reported invalid instead of auditing clean.
--silentis order-independent (--silent --checksumworks like--checksum --silent, and with-v/--helpin any position) and is still rejected without--checksum.- Mode-specific flags are rejected loudly instead of silently ignored:
--file-timeoutside create,--preserve-attrsoutside rewrite,--jobs/--policyoutside--zar, and--jobswith--compressor a multi-mode redump run (where the parallel path can never take effect). --wipe/--trimhonor-o(the output is no longer processed as a second input);--ciso-splitrefuses a source named like its own first part;--is-optimizedhonors--skip-sectors.- Rewrite:
-Donly deletes the.oldbackup after a fully successful (validated) rewrite, and one file's failure no longer suppresses a later file's success output or keeps its backup. - Help text corrected:
--helpis real help,--validate-strictis parity-only (mismatches exit 2 either way), the--skip-sectorswording matches what create mode accepts, and the redump modes show-o <output>instead of a positional second file (which is parsed as another input). - stdout/stderr are always UTF-8 rather than the host console code page, so
Latin-1 filenames (
café.txt) render and pipe identically on every platform and match xdvdfs's UTF-8 output.
- Header actions: Donate (PureLogicCode page), About (version, description, credits, links), Exit; new About and message-box windows.
- F8 saves a PNG screenshot of the active window to a
Screenshotfolder beside the app (falling back to%LocalAppData%/XISOSharp/Screenshot). - Startup update prompt (shared 24-hour cache with the CLI; disable with
XISO_NO_UPDATE_CHECK=1), subscribing to window events before probing so a fast show can never leave the dialog pending. - Drag-and-drop routing: only folders containing
*.isogo to Batch;.cso/.zar/.img-only folders go to Create; a dropped.zarroutes to the Rebuild tab in single- and multi-item drops. Wipe/Trim pass the chosen output through-ocorrectly. CliStatusis always updated on the UI thread; the CLI-run cancel/timeout/ start failures reach the log panel; GUI settings (CLI path, overwrite default) persist to%AppData%/XISOSharp/gui-settings.jsonagain.- Screenshot diagnostics are treated as expected conditions (no bug report) and the fallback folder is tried for security exceptions too.
- Tooltips across the main window;
--probe-cliand--self-testheadless helpers kept green.
- CLI, GUI, and Tester share one Serilog pipeline; expected operational
warnings (usage/validation refusals, missing-file probes, non-zero CLI exits,
unreadable dropped paths, screenshot environment issues) are tagged
NoBugReportand stay local, while genuine Warning+ events still report. - The shared ApplicationStats / bug-report API key is stored double-Base64
encoded (
ApiKeyStore) and decoded once at startup — obfuscation, not encryption, but no plaintext literal in sources or bundles. - The bug-report dedupe dictionary prunes stale keys in long-running hosts;
TestDataWriterreleases its mutex only when it owns it;ProcessRunnerfalls back to a stable working directory when the inherited CWD was deleted.
ParseListOutputparses the real\name (N bytes)format emitted by both tools; extract-xiso output is decoded as Latin-1 socafé.txtand friends compare equal. Two empty parses are a failure, not a vacuous pass.- Removed dead helpers/wrappers (
HashUtil.ComputeMd5/IsAllZero,XisoFileEntry.IsSmall, unused sync wrappers includingRunQuietAsync).
- Fixed drift: test counts,
--helpbehavior, Release-build packing, battle harness default, coverage artifact OS, Tester CI claim, coverage figures, missing PowerShell scripts, staleConversionPlan.mdlinks, and thedocs/troubleshooting.mdverification-script section. publish-cli.ps1/publish-gui.ps1no longer delete the protectedpublish*/<rid>trees: they stage into%TEMP%and merge the same-named files over the artifact store (AGENTS.md hard rule).publish-cli.ps1 -Ziprequires an exact release tag or-Version <x.y.z>so a pre-release run can never overwrite a shippedrelease_*.zip.- Namespace docs match the shipped surface again (
UnpackOptions,ProcessRunResult,ExplorerNode,XisoExplorerOptionsinXISOSharp; typed models/enums inXISOSharp.Models; interfaces inXISOSharp.Interfaces), and the library samples include the needed usings. docs/is served both as a Docsify site on GitHub Pages (_sidebar.mdleft menu) and mirrored to the GitHub Wiki bywiki.yml+.github/sync-wiki.ps1, which generatesHome.mdand_Sidebar.mdfrom the same sources.
- New coverage includes: review regressions (path escapes, table bounds,
sentinel handling, split naming, partial-output cleanup, pre-existing-output
preservation, offset-image repair, CLI flag contracts), the follow-up fixes
(
-c .default output, colon names through list/tree/rewrite, audit root-size parity,build-imagecancellation cleanup,--jobsrejection with--compress), GUI command builders and services, shared process runner/update checker, internals (SHA3, Latin-1, bounded sub-streams), zero-size directories, and Latin-1 process output decoding. - The suite runs fully sequentially (assembly-wide
[CollectionBehavior(DisableTestParallelization = true)]) so the shared process CWD is never mutated concurrently. - Suite: net8.0/net9.0 — 1757 tests (1756 passed, 1 opt-in skip); net10.0 — 1818 tests (1817 passed, 1 opt-in skip, adds the GUI tests).