Architecture
| Getting Started | Using the App | Technical Reference | Project |
|---|---|---|---|
| Home | Usage Guide | Architecture | Repository |
| Installation | Conversion Methods | XDVDFS Technical Docs | Building from Source |
| XISO Explorer | Troubleshooting & FAQ | Release Notes |
The application is a cross-platform Avalonia (.NET 10, net10.0) desktop app — one codebase for Windows, Linux, and macOS — built on modern software engineering principles: dependency injection, service-oriented design, interface-driven contracts, and a comprehensive xUnit test suite.
Solution Layout
CSharp_XISOStudio.sln
├── XISOStudio/ Main Avalonia application
│ ├── Program.cs Entry point (Avalonia AppBuilder)
│ ├── App.axaml(.cs) Theme/styles + DI composition, global error handlers
│ ├── MainWindow.axaml(.cs) Shell window + navigation
│ ├── MainWindow.ConversionAndTesting.cs Convert/Test workflows (UI layer)
│ ├── MainWindow.FileSelection.cs Folder scanning + selectable file lists (UI layer)
│ ├── MainWindow.XIsoExplorerLogic.cs Explorer workflows (UI layer)
│ ├── MainWindow.CheckForUpdatesAsync.cs Update check integration
│ ├── MainWindow.UIHelpersAndWindowEvents.cs UI helpers, links, window events
│ ├── AboutWindow.axaml(.cs) About dialog
│ ├── Dialogs/ MessageBoxWindow (cross-platform modal dialogs)
│ ├── Interfaces/ One interface per service (IOrchestratorService, IXisoSharpService, ...)
│ ├── Models/ DTOs and enums (FileProcessingStatus, FileItem, BatchOperationProgress, ...)
│ └── Services/ All business logic
│ ├── OrchestratorService.cs Batch pipeline coordination
│ ├── SupportedFiles.cs Extension filters shared by the UI lists and folder scans
│ ├── XisoSharpService.cs XISO/ZAR/CSO conversion via the XISOSharp library
│ ├── ChdService.cs CHD conversion via the CHDSharp library
│ ├── XisoIntegrityService.cs Structural audit + deep scan via XISOSharp / ZArchiveSharp / CHDSharp
│ ├── ImageExplorerFactory.cs Opens the right IImageExplorer (XISO/CISO, ZAR or CHD)
│ ├── XisoImageExplorer.cs IImageExplorer over ISO/CSO via XisoExplorer
│ ├── ZarImageExplorer.cs IImageExplorer over ZAR via ZArchiveReader (zip-slip safe)
│ ├── ChdImageExplorer.cs IImageExplorer over CHD via CHDSharp + XISOSharp (on-demand decompression)
│ ├── ImagePaths.cs Shared internal-path normalization helpers
│ ├── FileExtractorService.cs Archive handling (zip/7z/rar), locked-file retries
│ ├── FileMoverService.cs File moves with network/lock retries
│ ├── DiskMonitorService.cs Read/write speed and free-space monitoring
│ ├── BugReportService.cs Automatic bug reporting client
│ ├── BugReportSink.cs Serilog sink: forwards Warning+ events to the bug report API
│ ├── UiLogSink.cs Serilog sink: on-screen log pane
│ ├── StatsService.cs Anonymous usage statistics client
│ ├── UpdateChecker.cs GitHub release update checks
│ └── ... Formatting, path helpers, etc.
└── XISOStudio.Tests/ xUnit + Moq test suite
The bundled 7-Zip CLI fallback (7za.exe/7za_arm64.exe on Windows, 7zz_linux_x64/7zz_linux_arm64 on Linux, 7zz_osx on macOS) is copied next to the executable and invoked as an isolated child process when SharpCompress cannot extract an archive; a system 7z on PATH is used as a further fallback. All XISO and CHD encoding/decoding is performed in-process by the XISOSharp and CHDSharp NuGet packages, and all image libraries are pure managed code with no native dependencies.
Dependency Injection
App.ConfigureServices registers every service with Microsoft.Extensions.DependencyInjection. All core logic is decoupled from the UI behind interfaces, enabling the service layer to be unit-tested without the UI framework.
| Service | Lifetime | Responsibility |
|---|---|---|
Serilog ILogger |
Singleton | Structured logging pipeline (UI, rolling file, and bug-report sinks) |
IDiskMonitorService |
Singleton | Drive throughput counters (Windows performance counters; N/A on Linux/macOS) and free-space queries |
IOrchestratorService |
Singleton | Batch pipeline: per-file dispatch for the selected files, progress, cancellation |
IXisoSharpService |
Singleton | XISO/ZAR/CSO conversion via the XISOSharp library |
IChdService |
Singleton | CHD conversion via the CHDSharp library (rewrites non-optimized inputs through IXisoSharpService, then encodes and verifies) |
IXisoIntegrityService |
Singleton | Structural audit + deep scan via XISOSharp (ISO/CSO), ZArchiveSharp (ZAR) and CHDSharp (CHD container + Xbox filesystem) |
IImageExplorer |
Per open image | Explorer over ISO/CSO (XisoExplorer), ZAR (ZArchiveReader) or CHD (ChdImageStream + XISOSharp), built by ImageExplorerFactory |
IFileExtractor |
Transient | Archive extraction with fallbacks and lock retries |
IFileMover |
Transient | Move/copy operations with retry + backoff |
IBugReportService |
Singleton | Sends exception reports to the developer endpoint |
IStatsService |
Singleton | Anonymous usage statistics |
IUpdateChecker |
Singleton | Queries the GitHub releases API |
IMessageBoxService, IUrlOpener, IScreenshotService |
Singleton | UI-adjacent helpers kept testable |
HTTP clients are created through IHttpClientFactory with named clients and pooled-connection handlers.
Conversion Pipeline
MainWindow (Convert tab)
├─ scans the input folder for supported files (SupportedFiles filter, recursive option)
├─ user ticks the files to process (selectable DataGrid list)
└─► OrchestratorService (ConvertFilesAsync)
├─ for each selected file:
│ ├─ .zip/.7z/.rar ──► FileExtractorService ──► temp ISO ──► convert ──► cleanup
│ └─ .iso ──► XisoSharpService (in-process: XISO / ZAR / CSO)
│ or ChdService (CHDSharp CHD encode + deep verify)
├─ after each file: optional integrity check, optional original deletion,
│ file moves (retry-aware), progress + stats updates
└─ final summary (success/fail/skip counts, elapsed time)
The requested output format flows from the UI through ConvertFilesAsync/ConvertAsync:
XISO, ZAR, and CSO go to IXisoSharpService.ConvertIsoAsync — XISO uses
XisoReader.Rewrite, ZAR streams the game-partition tree via XisoZarchive.CreateZar (Redump
partition offsets detected with XgdTables), and CSO repacks non-optimized inputs to a
temporary XISO and calls CisoWriter.CompressToCso (CISO v2/LZ4). CHD goes to
IChdService.ConvertIsoToChdAsync, which reuses the optimized-XISO rewrite for non-optimized
inputs and then calls ChdEncoder.EncodeRaw (CHDSharp) with the chdman createdvd preset
(4096-byte hunks, 2048-byte units, lzma,zlib,huff,flac, DVD metadata), followed by a
header check and — when output integrity is enabled — a full Chd.CheckFile deep verification.
Already-optimized inputs are skipped by default; when Skip $SystemUpdate is enabled they are
rewritten through the $SystemUpdate filter before CSO/CHD packing, so the option is honored for
every output format.
The folder-scanning ConvertAsync/TestAsync overloads remain available for callers that want the
orchestrator to discover files itself; the UI always passes the explicit list of ticked files.
The test pipeline (TestAsync/TestFilesAsync → IXisoIntegrityService) dispatches by extension:
.iso and .cso (single or split .1.cso) go through XisoReader.AuditXiso(...,
requireOptimizedTag: false) and, with the deep scan enabled, a sequential read of the whole
decompressed image; .zar is opened, tree-walked, and (deep scan) fully decompressed through
ZArchiveReader; .chd (Xbox DVD images only) is verified with Chd.CheckFile (header-only, or
every hunk and checksum with the deep scan) and the Xbox filesystem is audited through
XisoReader.AuditXiso(IBlockDevice) over a ChdImageStream. Split CISO continuation parts are
hidden from the list and move together with part 1.
Safety characteristics of the pipeline:
- Pre-flight checks — output-drive free space (size-aware for compressed formats) and FAT32 file-size limits are verified before conversion starts; failures skip the file with a clear message instead of failing late.
- Environmental errors are surfaced, not reported — disk-space and network failures stop or skip with actionable messages and are excluded from automatic bug reports.
- Transient failures retry — locked files and network hiccups use exponential backoff (see
FileExtractorService,FileMoverService); permanent errors are not retried. - Atomic replace-originals — deletion of inputs happens only after the converted file exists and (optionally) passes validation. An archive is removed only when every entry was extracted and every extracted image was converted; skipped entries or unconverted images keep the archive.
- Unique output names — output paths are reserved per batch, so two inputs with the same base name cannot overwrite each other.
- Per-file isolation — an unreadable, missing, or invalid file is reported individually and never aborts the remaining batch.
- Age- and ownership-checked temp cleanup — only app-created GUID work folders older than six hours are deleted, so user folders and another instance’s active work folders are safe.
- Lease-guarded explorer — background copy-outs hold an explorer lease; the explorer is never disposed mid-extraction, and the close flow is reentrancy-guarded.
- Cancellation is cooperative — child processes and I/O loops observe a
CancellationToken; cleanup runs withCancellationToken.Noneso it cannot mask the original error.
Logging
Logging uses a single Serilog pipeline configured by App.ConfigureLogging() — called from Program.Main before Avalonia initializes its platform subsystems, and idempotently from the App constructor — with three sinks:
- UI (
UiLogSink) — timestamped lines in the on-screen log pane. The window queues them in a bounded buffer (LogViewBuffer, newest 2,000 lines) and updates the viewer in throttled batches (500 lines per 100 ms), so a logging burst can neither queue one dispatcher callback per line nor grow the text control without limit; a re-entrancy guard keeps a failing viewer from feeding back into the pipeline. XISOSharp’s chunked console output is reassembled into complete, sanitized lines (LibraryOutputLineBuffer) first, so its backspace/space progress animation never floods the viewer. - File — rolling daily log under the per-user application-data folder (
%LocalAppData%\XISOStudio\logson Windows,~/.local/share/XISOStudio/logsor~/Library/Application Support/XISOStudio/logselsewhere) aslog-*.txt(10 MB per file, 14 files retained) with level and exception details. - Bug report (
BugReportSink) — every event at Warning or higher is forwarded to the bug report API (fire-and-forget, never throws). Avalonia framework events (SourceContext = "Avalonia") are written to the viewer and log file but excluded from automatic reports.
Avalonia’s own diagnostics are routed into the same pipeline by AvaloniaSerilogSink, which is installed before the platform subsystems start so startup warnings are captured too.
Services inject Serilog.ILogger and log with structured message templates. Expected user/environmental errors are logged at Information level so they do not generate bug reports; genuine defects log at Warning/Error/Fatal. Failed bug-report deliveries are recorded at Information level instead of being silently dropped.
Error Handling and Reporting
Three layers of defense:
- Global handlers in
App(AppDomain.UnhandledException,Dispatcher.UIThread.UnhandledException,TaskScheduler.UnobservedTaskException) log through Serilog and keep the app alive where possible; fatal shutdown paths also send a blocking report. - Per-operation catches translate known failure classes (disk full, access denied, FAT32 limits, locked files, invalid images) into user-facing messages and log at an appropriate level.
- Automatic bug reports are sent by the Serilog
BugReportSinkfor Warning+ events, with complete environment, error, and exception sections; expected environmental errors stay at Information level and are shown to the user instead.
Models
| Model | Purpose |
|---|---|
FileProcessingStatus |
Per-file outcome (converted/skipped/failed/already-optimized/invalid-input) |
BatchOperationProgress |
Progress snapshot used for UI updates, including the invalid-image count |
ArchiveExtractionResult |
Archive extraction outcome (success + skipped entries) used by the archive-deletion safety check |
IsoTestResultStatus |
Test-view outcome states |
ImageEntry |
One file/directory inside an image or archive (name, path, size, type) |
XisoExplorerItem |
Row model for the explorer list (wraps an ImageEntry) |
GitHubReleaseInfo |
Deserialized GitHub release payload |
CloudRetryResult |
Result of a cloud-hydration retry |
Testing
The XISOStudio.Tests project (xUnit, Moq) covers models, services, and helper utilities with
1,432 tests:
dotnet test CSharp_XISOStudio.sln
The suite includes service tests (e.g., OrchestratorServiceTests, FileExtractorServiceTests, XisoSharpServiceTests, XisoIntegrityServiceTests) plus model and helper coverage. Tests run
sequentially (CollectionBehavior(DisableTestParallelization = true)) because XISOSharp uses
process-wide static state and the process working directory during extract/pack. Analyzers
(Meziantou, Roslynator) enforce code quality on both projects.