3. Architecture
This page describes the solution structure, the runtime startup sequence, and the high-level data flow of the application. Detailed deep dives live in Conversion Pipeline, Extraction & Verification, Services Reference, and Utilities Reference.
3.1 Solution Structure
CSharp_CHDStudio.sln
├── CHDStudio/ (Avalonia app, net10.0;net10.0-windows)
│ ├── App.axaml(.cs) → startup, Serilog, exception handlers
│ ├── AppConfig.cs → central configuration
│ ├── MainWindow.axaml(.cs) → UI + all batch logic
│ ├── AboutWindow.axaml(.cs) → about dialog
│ ├── Models/
│ │ ├── FileItem.cs → bindable file row (name, size, selected)
│ │ ├── GitHubRelease.cs → GitHub API release model
│ │ └── PbpExtractionResult.cs → PBP extraction outcome
│ ├── Services/
│ │ ├── AppHttpClient.cs → singleton HttpClient (TLS 1.2/1.3)
│ │ ├── ArchiveService.cs → zip/7z/rar extraction, CSO, 7za fallback
│ │ ├── BugReportApiSink.cs → Serilog sink → bug API
│ │ ├── BugReportService.cs → bug report client + exclusion list
│ │ ├── ChdExplorerService.cs → CHD file-system explorer + Image Info report
│ │ ├── ChdSharpEncoderService.cs → in-process CHD encoder (CHDSharp library)
│ │ ├── FileEventRecord.cs / FileWatchEventType.cs
│ │ ├── FileWatcherService.cs → missing-file diagnostics
│ │ ├── LegacyCleanupService.cs → removes legacy files/folders
│ │ ├── ScreenshotService.cs → window screenshot capture (RenderTargetBitmap)
│ │ ├── StatsService.cs → anonymous usage stats
│ │ └── UpdateService.cs → GitHub update checks
│ └── Utilities/
│ ├── BinCueGenerator.cs → auto-cue generation for bin-only archives
│ ├── ChdChecksumReport.cs → whole-image + per-track SHA-1/CRC-32/XXH3 report writer
│ ├── ChdInfoReport.cs → Explorer Image Info report builder
│ ├── ChdSharpProgressLogger.cs → 10%-step CHDSharp progress logging
│ ├── CueFileLineTransform.cs / CueFileReference.cs / CueNormalizationResult.cs
│ ├── CueNormalizer.cs → encoding detection + canonicalization
│ ├── CueWorkDirectory.cs(.Result) → self-contained ASCII cue work dirs
│ ├── DiscImageKind.cs → what a file turned out to be
│ ├── DiscImageSignature.cs → magic-byte content identification
│ ├── FileExtensions.cs → all extension constants and sets
│ ├── GameFileParser.cs → cue/gdi/toc referenced-file resolution
│ ├── IMp3Decoder.cs / Mp3ToWavDecoder.cs
│ ├── InputFileFilter.cs → drops raw images a descriptor already covers
│ ├── IsoSectorValidator.cs → sector-size alignment checks
│ ├── PathUtils.cs → temp dirs, path sanitizing, relative paths
│ ├── RawCdImageDetector.cs → raw 2352 sector sniffing + cue staging
│ ├── RetryingFileOperations.cs → retry-with-backoff delete/move
│ ├── TrackBinCueBuilder.cs → multi-FILE cue for "(Track N)" bin sets
│ └── Ecm/ → in-process ECM decoding
│ ├── CdSectorEccEdc.cs → regenerates sector EDC + Reed-Solomon parity
│ ├── EcmImageDecoder.cs → ECM block-stream decoder
│ └── EcmDecodeResult.cs
├── CHDStudio.Tests/ (xUnit; Fixtures/ holds ecm-sample.ecm, rar-multipart/, MdsV2/ and laserdisc-small.avi)
├── MDSSharp/ (Alcohol 120% .mds/.mdf parsing; net8.0;net9.0;net10.0)
├── CCDSharp/ (CloneCD .ccd/.img/.sub parsing; net10.0;net8.0)
├── CSOSharp/ (CSO/CISO decompression; net10.0;net8.0)
├── PBPSharp/ (PBP/SFO parsing; net10.0;net8.0)
├── ISZSharp/ (UltraISO ISZ decompression; net8.0;net9.0;net10.0)
└── References/ (third-party sources — not part of the build)
Dependency graph
┌──────────────────────────────────┐
│ CHDStudio │ (Avalonia app)
└───┬───────┬───────┬───────┬──────┘
Project refs │ │ │ │
┌───────────▼─┐ ┌───▼──────▼──┐ ┌──▼──────────┐
│ CCDSharp │ │ CSOSharp │ │ PBPSharp │
└──────────────┘ └─────────────┘ └─────────────┘
┌──────────────────────┐ ┌─────────────────────┐
│ MDSSharp │ │ ISZSharp │
└──────────────────────┘ └─────────────────────┘
NuGet: CHDSharp 1.4.3, Avalonia, SharpCompress, NAudio, Serilog
- The app references
MDSSharp,CCDSharp,CSOSharp,PBPSharpandISZSharpas project references. - All five libraries are packable and expose internals to
CHDStudio.TestsviaInternalsVisibleTo;MDSSharp,CCDSharp,CSOSharp,PBPSharpandISZSharpall multi-targetnet8.0;net9.0;net10.0. CHDStudio.Testsreferences the app (internals visible) plusMDSSharp,CSOSharp,PBPSharpandISZSharp— but notCCDSharp(there are no CCDSharp unit tests today; see Testing).
Why ISZ and Alcohol support moved into libraries.
ISZSharpandMDSSharpwere split out of the app’s utilities into standalone packable projects (they are self-contained formats with redistributable value), while ECM decoding remains in-app because it is tightly coupled to the cue staging flow. The test project references both new libraries directly.
3.2 Startup Sequence
App ctor
├─ Encoding.RegisterProvider(CodePagesEncodingProvider.Instance) ← legacy codepages (CP932/CP949/CP1251...)
├─ new BugReportService(...) → App.SharedBugReportService
├─ new StatsService(...)
├─ ConfigureSerilog()
│ ├─ file sink: %LocalAppData%\CHDStudio\logs\CHDStudio-.log (daily, 7 retained)
│ ├─ debug sink
│ └─ BugReportApiSink (forwards Warning+ to the bug API)
└─ subscribe: AppDomain.UnhandledException, Dispatcher.UIThread.UnhandledException,
TaskScheduler.UnobservedTaskException, Exit
OnStartup
├─ acquire global mutex "Global\CHDStudio_SingleInstance" (second instance → exit)
├─ ShutdownMode = OnMainWindowClose
├─ apply dark Fluent theme (Avalonia)
├─ delete legacy 7z_x64.dll / 7z_arm64.dll
├─ _statsService.RecordUsageAsync() (fire-and-forget)
└─ type preloading on background thread
MainWindow ctor
├─ probe chdman/7za (app directory first, then PATH); the CHDSharp encoder is built in
├─ construct services (ArchiveService, ScreenshotService, FileWatcherService)
├─ wire F8 screenshot hotkey (window KeyDown)
├─ InitializeStatusBar
├─ after 2 s: CleanupLeftoverTempDirectories + LegacyCleanupService.RunInBackground
└─ log environment details
MainWindow Loaded
├─ create performance counters (write/read speed)
├─ apply CLI folder argument if present
├─ CheckDependenciesAndNotifyUser (chdman presence on Windows; built-in CHDSharp always available)
└─ UpdateService.CheckForNewVersionAsync (background)
Line references: App.axaml.cs:35–145, MainWindow.axaml.cs:87–172.
3.3 Runtime Data Flow — Conversion
User clicks Start Conversion
└─ StartConversionButton_ClickAsync (MainWindow.axaml.cs:1275)
├─ validate paths (ValidateAndNormalizePath)
├─ read options (delete originals, smaller-first, force CD/DVD, timeout)
├─ RenewCancellationTokenSource
├─ SetControlsState(false)
└─ PerformBatchConversionAsync (:1684)
├─ encoder preflight (Windows): probe chdman access + compatibility;
│ continue on the built-in CHDSharp encoder when missing or failing
│ (on Linux/macOS the built-in encoder is used directly)
├─ optional sort by size (smaller first)
├─ CheckDiskSpace (free space warnings)
├─ InputFileFilter + ResolveOutputCollisions (batch preflight)
└─ per file: ProcessSingleFileForConversionAsync
├─ missing file? → FileWatcherService diagnostics
├─ TryResolveByContentAsync ← content before extension
│ ├─ split volume set → SplitImageJoiner (MDSSharp) → classify
│ ├─ Isz → ResolveIszAsync (ISZSharp) → classify
│ ├─ Ecm → ResolveEcmAsync (Utilities/Ecm) → classify
│ ├─ Chd → skip ("already a CHD")
│ └─ container extension, plain image inside → generated cue
├─ else route by extension:
│ .cso → ProcessCsoFileForConversionAsync
│ archive→ ProcessArchiveFileForConversionAsync
│ .pbp → ProcessPbpFileForConversionAsync (InvalidHeader → content-routed)
│ .ccd → ProcessCcdFileForConversionAsync
│ .mds → ProcessMdsFileForConversionAsync (MDSSharp)
│ other → TryStageCueForRawImageAsync → direct conversion
├─ ValidateDependentFilesAsync (cue/gdi/toc)
├─ TryDirectConversionAsync
│ └─ ConvertToChdAsync → chdman primary on Windows with the
│ built-in CHDSharp fallback; built-in only
│ on Linux/macOS; writes <name>.<hex>.chdtmp,
│ moves on success
├─ fallback: TryRetryConversionViaTempCopyAsync
└─ HandleConversionResultAsync
├─ success → optionally delete originals + prune empty dirs
└─ failure → leave the destination alone, keep source
3.4 Runtime Data Flow — Extraction & Verification
Extraction: StartExtractionButton_ClickAsync (:915)
└─ PerformBatchExtractionAsync (:1761)
└─ per file: ExtractChdAsync (:4142)
├─ pick command: auto-detect via CHD metadata, or explicit CD/DVD/HDD
├─ ChdFile.Open (CHDSharp) — corrupt CHD → clear error, continue
├─ DVD/HDD → ExtractChdToSingleFile (streamed 4 MB buffer)
└─ CD/GDI → ExtractChdTracksToDirectory (temp dir → retrying moves)
Verification: StartVerificationButton_ClickAsync (:1413)
└─ PerformBatchVerificationAsync (:4005)
└─ per file: VerifyChdAsync (:6004) — CHDSharp Chd.CheckFile
└─ optional move to Success/Failed via MoveVerifiedFileAsync (:4086)
└─ RetryingFileOperations.TryMoveAsync (retries ~45 s on locks)
3.5 Concurrency & Threading Model
- UI thread: all Avalonia controls; dispatcher invocations are used from worker contexts (
Dispatcher.UIThread.Invoke/InvokeAsync, withDispatcherPriority.Backgroundfor chunked list loading). - Worker threads:
Task.Runfor file scanning, archive extraction, chdman process orchestration, in-process CHDSharp encoding, screenshot rendering. - Cancellation: one
CancellationTokenSourceper operation, guarded by aLock(_cts,_ctsLock,MainWindow.axaml.cs:31–32); cancellation is observed at every loop iteration and propagated into chdman via a linked timeout CTS. - Chdman process: stdout/stderr are redirected and parsed asynchronously (
OutputDataReceived/ErrorDataReceived); the process is killed (process.Kill(true)) on cancellation/timeout, and the app waits 300 ms before temp cleanup so file handles are released. - Speed telemetry:
PerformanceCounter-based disk write/read rates sampled every second (AppConfig.WriteSpeedUpdateIntervalMs = 1000). - Operation state: an interlocked
_operationRunningStateplusSetControlsStateguards the UI against re-entrancy; a_pendingCloseflag lets the window close gracefully mid-operation.
3.6 Logging Pipeline
LogMessage / LogWarning / LogError (MainWindow)
└─ Serilog (Log.Information/Warning/Error)
├─ Debug sink
├─ File sink → %LocalAppData%\CHDStudio\logs\CHDStudio-YYYYMMDD.log
└─ BugReportApiSink → BugReportService.SendBugReportAsync
(Warning+ only; exclusion patterns drop known-noise; single in-flight send)
See Bug Reporting System for the full contract.