Skip to content

release_3.0.0

Choose a tag to compare

@drpetersonfernandes drpetersonfernandes released this 30 Sep 06:01
· 10 commits to master since this release

3.0.0

September 2026

The cross-platform CHD & logging release. The application was ported from WPF to
Avalonia and now runs on Windows, Linux, and macOS (x64 and ARM64), and
CHDSharp is integrated as a second in-process engine:
Xbox and Xbox 360 ISOs can be converted to CHD v5, and Xbox DVD CHD files can be integrity-tested
and explored without extraction. All logging runs through Serilog with
automatic bug reporting, and the Convert and Test views now offer per-file selection lists with
XISO/ZAR/CSO/CHD output formats.

New Features

  • Cross-platform UI — WPF replaced with Avalonia (same dark theme, colors, fonts, layout,
    and control styling) with one net10.0 codebase. Six release archives are produced:
    win-x64/win-arm64, linux-x64/linux-arm64, and osx-x64/osx-arm64. All imaging
    libraries are pure managed code, so conversion, integrity testing, and exploration work
    identically everywhere. Windows-only integrations degrade gracefully: the read/write speed
    monitor shows N/A (Windows performance counters), file/folder pickers and links use the OS-native
    dialogs, and the 7-Zip CLI fallback is bundled for every platform — 7za.exe/7za_arm64.exe on
    Windows, 7zz_linux_x64/7zz_linux_arm64 on Linux, and the universal 7zz_osx on macOS — with a
    system 7z on PATH as a further fallback.
  • CHD output format — the Convert tab's Output Format selector gains CHD (.chd,
    Compressed Hunks of Data). The optimized game partition is encoded by ChdEncoder.EncodeRaw
    (CHDSharp) with the chdman createdvd preset — 4096-byte hunks, 2048-byte units, the
    lzma,zlib,huff,flac codec list, and the DVD metadata tag. Non-optimized (Redump) inputs are
    rewritten to the game partition first, so Skip $SystemUpdate and Delete Originals work exactly
    as they do for XISO/ZAR/CSO. Check Output Integrity deep-verifies the new CHD — every hunk is
    decompressed and every checksum and hash is validated — before the conversion is reported as
    successful.
  • CHD integrity testing — .chd files are testable, limited to Xbox DVD images. The container
    is verified with Chd.CheckFile (header-only, or every hunk and checksum with Perform Deep Scan), and the Xbox filesystem is audited over the decompressed image through XISOSharp
    (AuditXiso(IBlockDevice)). CD/GD-ROM/hard-disk CHDs and differential child CHDs are rejected
    with a clear message.
  • CHD exploration — the Explorer opens .chd files through a keep-open ChdImageStream
    (hunks decompressed on demand) and lists and copies out entries with the XISOSharp stream APIs.
    Both game-partition-only CHDs (produced by this app) and full Redump-image CHDs (produced by
    chdman createdvd) are supported — partition offsets are auto-detected.
  • Serilog logging pipeline — three sinks: the on-screen log viewer (UiLogSink), a rolling daily
    file log (%LocalAppData%\XISOStudio\logs\log-*.txt, 10 MB per file, 14 files retained),
    and a bug-report sink (BugReportSink) that forwards every Warning-or-higher event to the Bug
    Report API. Avalonia's internal diagnostics are routed through the same pipeline
    (AvaloniaSerilogSink) instead of the Trace-based default. The custom ILogger/LoggerService
    abstraction was removed.
  • Complete bug reports — every report contains === Environment Details === (date, application
    name/version, OS version, architecture, bitness, Windows version, processor count, base directory,
    temp path), === Error Details ===, and — when an exception is attached — === Exception Details ===
    (type, message, source, and stack trace, including nested and aggregate exceptions). The API's
    environment and stackTrace fields are populated as well.
  • Fatal-error reporting — global handlers (AppDomain.UnhandledException,
    DispatcherUnhandledException, TaskScheduler.UnobservedTaskException) report through the same
    pipeline, and fatal shutdown paths send a blocking report before the process exits.
  • Selectable file lists — after choosing an input folder, the Convert view lists every supported
    file (.iso, .zip, .7z, .rar) and the Test view lists every ISO, each with a
    Select checkbox, file name (relative to the input folder), and formatted size. Only ticked
    files are processed. Select All / Deselect All buttons toggle the whole list in one click,
    and toggling Search Subfolders refreshes the list immediately (the list also refreshes after
    each batch so it reflects deleted originals and files moved to _success/_failed).
  • Compressed output formats — the Convert tab now produces ZAR (.zar, ZArchive/zstd,
    loadable directly in Xenia canary) and CSO (.cso, CISO v2/LZ4, byte-identical to
    xdvdfs compress) in addition to optimized XISO. ZAR streams the game-partition tree straight
    into the archive; CSO repacks non-optimized inputs to a temporary XISO first. Skip $SystemUpdate,
    Delete Originals, and integrity checking apply to all formats, and the file list stays
    ISO/archive-only.

Improvements

  • Every catch block now logs — previously silent cleanup, retry, and ignore paths log at an
    appropriate level, and public service methods log failures with context.
  • Fewer false reports — expected user/environmental problems (corrupt or password-protected
    archives, missing files, unsupported images, disk-space/network errors) are logged at Information
    level so they never generate bug reports; genuine failures are logged at Warning/Error/Fatal.
  • Diagnostics on disk — the rolling log file records the startup version and the full session log
    for troubleshooting.
  • Single source of truth for supported files — the SupportedFiles filter is shared by the UI
    lists and the orchestrator folder scans, so the UI can never offer a file the converter cannot handle.
  • Responsive with large folders — list items are added in chunks of 100 on the UI thread, so
    folders with thousands of files stay responsive.
  • New orchestrator API — IOrchestratorService.ConvertFilesAsync and TestFilesAsync process an
    explicit file list; the folder-scanning ConvertAsync/TestAsync remain for compatibility.
  • Side-by-side layout — the Convert and Test views (folder pickers, options, and the selectable
    file list) now occupy the left panel, while the log viewer / XISO explorer fills the right panel,
    with a draggable splitter between them. On the Explorer tab the file picker and the explorer list
    share the full window width and the log panel is hidden.
  • Collapsible Options — the Options panel on both tabs is an Expander styled to match the
    theme; click its header to collapse or expand it and give the file list more room.
  • Theme-consistent list styling — new FileListDataGridStyle and related styles match the dark
    terminal theme.
  • Bundled 7-Zip fallback on every platform — the 7-Zip CLI fallback is no longer Windows-only:
    release archives now ship the official 7-Zip console binary for the matching platform
    (7za.exe/7za_arm64.exe, 7zz_linux_x64/7zz_linux_arm64, or the universal 7zz_osx), with
    7-Zip-License.txt, so complex/unsupported archives extract out of the box on Linux and macOS
    too; a system 7z on PATH remains as a further fallback.

Breaking Changes

  • CUE/BIN input support was removed. The bundled bchunk.exe and the .cue file type are gone:
    the application now supports only .iso (Redump full-disc images) and already-optimized XISO
    files, plus the archive formats (.zip, .7z, .rar). .cue/.bin files are no longer listed
    or processed. The application and test projects report version 3.0.0
    (AssemblyVersion/FileVersion) so the update checker sees this release over 2.8.0.

Bug Fixes

A full review of the XISOStudio and XISOStudio.Tests projects found and fixed
38 verified defects. The most significant ones:

Data safety

  • Archive deletion predicate was too loose — an archive could be deleted when only one of its
    images was converted, losing the unprocessed contents. The extractor now reports skipped entries
    (ArchiveExtractionResult), and the archive is removed only when every entry was extracted and
    every extracted image was converted or explicitly skipped by the user.
  • Same-named inputs overwrote each other — two inputs sharing a base name (for example
    Disc1/game.iso and Disc2/game.iso) mapped to one output path; the engines delete a pre-existing
    output, so the second conversion destroyed the first and both originals were then deleted. Output
    paths are now reserved per batch (game.iso, game (2).iso, …).
  • Invalid inputs were treated as failures and could count toward deletion — engines now return a
    distinct FileProcessingStatus.InvalidInput, which never deletes the source and keeps archives alive.
  • Test temp copies leaked when the cloud copy failed or the user canceled the cloud retry; the
    cleanup finally now covers every exit path.

Conversion correctness

  • Skip $SystemUpdate was ignored for already-optimized inputs written as CSO/CHD — those inputs
    are now rewritten through the $SystemUpdate filter before compression.
  • Wrong XGD partition offset for Redump-type-5 images with an unknown/unreadable video PVD — the
    dead GetXgdType fallback is now reachable, so XGD2 offsets are selected correctly.
  • Split CISO cloud copies dropped the .1 part marker and only copied part 1; every part is now
    copied with its original numbering, and a split set is recognized as such.
  • Free-space pre-checks used the uncompressed size for compressed outputs, rejecting conversions
    that would easily fit; compressed formats now use a size-aware estimate.
  • Temp-path and zip-slip path comparisons were case-insensitive on case-sensitive file systems;
    they now follow the platform (PathHelper.PathComparison).
  • 7-Zip arguments were built by string interpolation, so quotes in paths could split or inject
    arguments; the fallback now uses ProcessStartInfo.ArgumentList.
  • Required-space arithmetic could overflow for extreme sizes; the buffer addition now saturates at
    long.MaxValue.

Robustness

  • One unreadable file aborted the whole integrity-test batch — each test iteration now has the
    same per-file error handling as conversion.
  • File-list scans failed entirely if one file vanished or locked mid-scan — sizes are read through
    a safe helper.
  • Concurrent file-list refreshes could clear or overwrite the current list — stale scans are
    discarded with a generation counter.
  • Temp cleanup deleted any XISOStudio_* folder (no ownership or age check); it now removes
    only app-created GUID work folders older than six hours, so user folders and another instance's
    active folders are left alone.
  • Canceled cleanup could mask the original error — finally cleanup passes
    CancellationToken.None and the retry helper swallows cancellation.
  • Explorer could be disposed while a background copy-out was using it — copy-outs now hold an
    explorer lease (SemaphoreSlim) and disposal is deferred until they finish.
  • Exit button bypassed the close confirmation (desktop.Shutdown() forces the window closed even
    when Closing cancels); it now calls Close(), and the closing flow is reentrancy-guarded so two
    prompts cannot appear and discarded-task exceptions are observed.
  • Completion dialogs appeared for canceled or never-started batches and the UI could stay disabled
    if a dialog failed; operation state is reset before summaries and the summary is tailored to the
    outcome.
  • Failed explorer opens kept a disposed explorer and stale UI; the reference is cleared before
    opening and the grid is emptied on failure.
  • Same-second screenshots overwrote each other — names now include milliseconds plus a collision
    probe.
  • Non-DVD CHDs were accepted by the explorer and only failed later; they are now rejected at open
    time via Chd.Classify, matching the integrity service.
  • Disk-monitor error status was erased immediately by StopMonitoring(), and a
    PerformanceCounter could leak when priming failed; both are fixed.
  • UNC paths never reached the network-status branch because GetDriveLetter returns null for
    them and CurrentDriveLetter starts as null.

Logging and reporting

  • Environmental/transient events (locked files, full disks, offline networks, permission problems,
    update checks, URL opening, performance counters) were logged at Warning/Error and auto-uploaded

    as bug reports. They now log at Information, and retries only retry genuinely transient I/O errors.
  • Cloud-file error codes were wrong/dead (0x80070146 instead of 0x8007016A, and a raw Win32
    code compared against a full HRESULT); detection now compares the masked HRESULT.
  • Bug-report send failures were completely silent — they are now recorded in the log at
    Information level.
  • Fatal-error reporting blocked the UI for a full 5 seconds and could fail to send — the HTTP call
    uses ConfigureAwait(false) so the report completes while the UI thread is waiting.

Platform and infrastructure

  • Constructors mutated the injected HttpClient (timeout, default headers); headers and timeouts
    are now applied per request, so a shared or reused client cannot be corrupted.
  • ProcessTerminatorHelper could throw from its initial HasExited probe — it now also catches
    Win32Exception.
  • The "Invalid ISO" counter counted every failure (disk errors, access denied, move failures);
    engines and the integrity service now report a dedicated InvalidIsoCount, so the "Many files were
    not valid Xbox ISOs" warning only counts genuinely invalid images.

Pre-release review follow-up

  • Drag-out and open-from-image extraction blocked the UI thread — the explorer lease helper invoked
    its action inline, so dragging large entries out of an image (or opening them) decompressed on the
    UI thread and froze the window. Explorer actions now run on the thread pool while the lease is held.
  • Drag-and-drop temp files were deleted before the drop target copied them — Windows Explorer and
    some Linux/macOS file managers copy the dropped files asynchronously after the drop returns, so the
    immediate Directory.Delete could truncate the copy. Extracted drag sources are now kept for a few
    minutes and the startup cleanup collects anything left behind.
  • Failed or canceled explorer extractions leaked their temp folders — %TEMP%\ImageExplorer and
    %TEMP%\ImageExplorer_DragDrop work folders are now removed after a failed copy-out, and
    TempFolderCleanupHelper also scans those folders (GUID children older than six hours).
  • Split CISO continuation parts used a hard-coded lowercase extension — on case-sensitive file
    systems (Linux/macOS) game.2.CSO was never found when moving a set to _success/_failed; the
    original extension casing is preserved now.
  • Cross-volume detection was wrong on Linux/macOS — Path.GetPathRoot always returns /, so moves
    between mount points were treated as same-volume renames and skipped the destination free-space
    guard. The actual mount point is resolved through DriveInfo.GetDrives() (longest matching root).
  • Background open-from-image could read a disposed CancellationTokenSource — the token is now
    captured before the task is queued, so closing the window cannot race the extraction.
  • The re-entrancy guard in the Start handlers tore down the running operation — the guard sat
    inside the try whose finally finishes the operation; it now runs before the try.
  • Avalonia platform-startup diagnostics were dropped — the Serilog pipeline and the
    AvaloniaSerilogSink are now configured in Program.Main before the Avalonia platform subsystems
    initialize (the constructor call is idempotent).
  • Avalonia framework warnings were auto-reported as bug reports — BugReportSink now skips events
    whose SourceContext is Avalonia; they are still written to the on-screen viewer and log file.
  • The on-screen log viewer could feed failures back into the logging pipeline — the UI sink has a
    re-entrancy guard and viewer failures are written to Serilog.Debugging.SelfLog instead of Serilog.

Final review follow-up

  • A failed test with a failed move was counted twice — the test-failure report was emitted before the
    _failed move, and a move exception reported the same file again. The failure path now moves first
    (mirroring the success path) and reports the failure once.
  • A cross-volume move skipped for lack of space was reported as a successful move —
    FileMoverService returned normally when the destination lacked free space, so the test view could
    report "passed" while the file was still in the input folder. Insufficient space now throws
    IOException, matching the IFileMover contract, and the file is reported as failed.
  • An archive could be deleted while it still held a lone split-CISO continuation part — the
    archive-retention check used the testable-image filter, which hides *.2.cso parts; the new
    SupportedFiles.IsImage predicate counts every image extension (continuation parts included), so the
    archive is kept.
  • A missing .zar/.chd file counted toward the "not valid Xbox ISOs" warning — like the ISO path,
    the ZAR/CHD paths now only report an invalid image when the file still exists, so vanished files are
    treated as missing rather than invalid.
  • A mis-routed CHD conversion could silently produce an XISO — XisoSharpService fell through to
    the XISO path for OutputFormat.Chd; the unsupported format now throws
    ArgumentOutOfRangeException instead of writing a mislabeled file (CHD encoding lives in
    ChdService).
  • macOS folder validation used a Windows-only case comparison — input/output folder equality and the
    subfolder guard, plus FileMoverService's cross-volume detection, now use
    PathHelper.PathComparison (case-insensitive on Windows and macOS, ordinal on Linux).
  • Cancel and Exit were ignored during the pre-operation temp-folder scan — the cleanup now receives
    the batch cancellation token, so a long cleanup no longer delays cancellation or shutdown.
  • ImagePaths.GetParent returned an unnormalized path for interior repeated slashes — "/a//b"
    returned "/a/"; trailing separators are now trimmed, so the returned parent is always normalized.
  • The on-screen log viewer could freeze the window during conversion — every log line queued its
    own UI callback and the text control grew without bound, so a chatty conversion (XISOSharp's
    per-character backspace progress animation) froze the window and hid the Cancel button. Log lines
    now go through a bounded buffer (LogViewBuffer, newest 2,000 lines, 500 lines flushed per 100 ms),
    and the library's chunked console output is reassembled into complete, sanitized lines
    (LibraryOutputLineBuffer), so the [xiso] control-character noise never reaches the viewer and
    the UI stays responsive to Cancel and Exit.
  • Log messages are rendered as plain text — Serilog 4 renders string properties with JSON-style
    quotes by default (Successfully converted '"game.iso"'); the on-screen viewer and bug reports now
    use the literal format, so names and paths appear as written (Successfully converted 'game.iso'),
    matching the rolling file log.
  • The batch summary could undercount the last file — Progress<T> delivers its callbacks through
    the dispatcher queue, and the batch could finish (inline on the UI thread) before the last queued
    result was processed, so a 2-file batch could report "Successfully converted: 1 files". The finish
    path now drains the dispatcher queue before reading the counters, so the summary and the statistics
    panel always include the last file.
  • CHD conversion was silent in the log — the encoding and verification phases only reported a
    status-bar percentage, so a minutes-long CHD encode showed nothing in the log pane. CHD conversion
    now logs each phase (prepare, encode, verify) and every 5% progress step, matching the XISO/ZAR/CSO
    output.

Internal

  • Added Models/ArchiveExtractionResult (extraction success + skipped entries), the
    FileProcessingStatus.InvalidInput value, BatchOperationProgress.InvalidIsoCount, and
    PathHelper.PathComparison/PathHelper.AddSafetyBuffer.
  • IFileExtractor.ExtractArchiveAsync now returns ArchiveExtractionResult instead of a bool.
  • The xUnit suite grew to 1,432 tests, including regression tests for every fixed defect. Test
    parallelization is disabled (CollectionBehavior(DisableTestParallelization = true)) because
    XISOSharp uses process-wide static state and the process working directory during extract/pack.
  • Ported MainWindow, AboutWindow, App, and theming from XAML/WPF to Avalonia
    (Program.cs, App.axaml, MainWindow.axaml, AboutWindow.axaml); added
    Dialogs/MessageBoxWindow (cross-platform modal dialogs replacing System.Windows.MessageBox)
    and rewrote the screenshot service with RenderTargetBitmap.
  • IMessageBoxService is now async (ShowAsync/ShowErrorAsync/ShowWarningAsync) with
    framework-neutral result/button/icon enums; file and folder pickers use Avalonia's
    StorageProvider; drag-out of explorer entries uses the Avalonia DataTransfer API.
  • Platform guards: DiskMonitorService returns N/A on non-Windows (the System.Diagnostics.PerformanceCounter
    package is referenced but only used under OperatingSystem.IsWindows()), and the bundled 7-Zip CLI
    fallback is copied per platform (7za.exe/7za_arm64.exe on Windows,
    7zz_linux_x64/7zz_linux_arm64 on Linux, 7zz_osx on macOS), with a system 7z/7za/7zz
    on PATH as a further fallback.
  • Retargeted both projects from net10.0-windows to net10.0; CI now builds/tests on
    windows-latest, ubuntu-latest, and macos-latest and publishes all six RIDs.
  • Added the CHDSharp 1.4.3 package (pure managed code, no native dependencies).
  • New IChdService/ChdService (conversion and deep verification), ChdImageExplorer,
    OutputFormat.Chd, .chd filters in SupportedFiles, and a ChdBlockDevice adapter
    (IBlockDevice over ChdImageStream) for the XISOSharp filesystem audit.
  • Tests for the new service (valid/optimized/non-optimized inputs, cancellation-safe cleanup,
    invalid images), CHD integrity testing (valid, deep scan, non-DVD rejection, corruption), the CHD
    explorer (listing, copy-out, missing paths, invalid files), and orchestrator CHD routing.
  • Added Serilog 4.4.0 and Serilog.Sinks.File 7.0.0, plus UiLogSink, BugReportSink,
    AvaloniaSerilogSink, and LoggingSinkExtensions (WriteTo.Ui() / WriteTo.BugReport()
    configuration).
  • Services and windows inject Serilog.ILogger; Interfaces/ILogger and Services/LoggerService
    were deleted.
  • Tests migrated from Mock<ILogger> to a capturing TestLogger sink; added BugReportSinkTests.
  • Added Models/FileItem (selectable list item with INotifyPropertyChanged) and
    Services/SupportedFiles (shared extension filters), plus MainWindow.FileSelection.cs for
    scanning/populating the lists.
  • Removed ExternalToolService/IExternalToolService, the GetReferencedBinFilesFromCue parser,
    and the ProcessCueAsync/ProcessCueInternalAsync pipeline; the archive extraction loop now
    filters with SupportedFiles.IsIso.
  • IXisoSharpService.ConvertIsoAsync replaces ConvertIsoToXisoAsync and dispatches to
    XisoReader.Rewrite (XISO), XisoZarchive.CreateZar (ZAR), or CisoWriter.CompressToCso (CSO);
    new OutputFormat enum, Redump partition-offset detection via XgdTables, and throttled progress
    adapters. IOrchestratorService.ConvertAsync/ConvertFilesAsync take the format and derive the
    .iso/.zar/.cso output name.
  • Added tests for FileItem, SupportedFiles, the new ConvertFilesAsync/TestFilesAsync
    orchestrator overloads (file-list filtering, empty lists, pass/fail moves), ZAR/CSO output
    (round-trip extraction/decompression, $SystemUpdate exclusion, format/extension plumbing).
  • Enabled XML documentation generation for the application project; every public type and member
    is documented and the build reports zero warnings.

Full Changelog: release_2.8.0...release_3.0.0