XISOSharp 1.0.1

There is a newer version of this package available.
See the version list below for details.

Requires NuGet 5.0 or higher.

dotnet add package XISOSharp --version 1.0.1
                    
NuGet\Install-Package XISOSharp -Version 1.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="XISOSharp" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="XISOSharp" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="XISOSharp" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add XISOSharp --version 1.0.1
                    
#r "nuget: XISOSharp, 1.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package XISOSharp@1.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=XISOSharp&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=XISOSharp&version=1.0.1
                    
Install as a Cake Tool

XISOSharp

NuGet NuGet

A pure C# class library for creating, extracting, listing, and rewriting Xbox ISO (XISO) disc images. A direct conversion of the extract-xiso tool from C to C# — byte-identical output, no native dependencies — extended with the archival power of XboxKit (Redump video/filler/seed/wipe/trim/petrify/update/rebuild, ZAR) and the modern packing of xdvdfs (build-image remapping, CISO compress/decompress, checksums). All logic — including the AVL tree, Boyer-Moore search, XISO header verification, directory traversal, format rewriting, and media-enable patching — is ported directly from the reference C implementation.

Full documentation (getting started, CLI reference, archival workflows, library API) lives in the docs folder on GitHub.


Table of Contents


Installation

dotnet add package XISOSharp

The package is strong-name signed and includes XML documentation, Source Link for debugging, and .snupkg symbol packages for all target frameworks.


Supported Disc Formats

Format Description Lseek Offset
RAW Raw XISO (no offset) 0
GLOBAL Retail/Xbox Live discs 0x0FD90000
XGD2 Xbox 360 XGD2 discs (same as GLOBAL) 0x0FD90000
XGD3 Xbox 360 XGD3 discs 0x02080000
XGD2 Hybrid Xbox 360 hybrid discs 0x89D80000
XGD1 Xbox 360 XGD1 discs 0x18300000

The library automatically detects the disc format during verification by probing each known offset.


Quick Start

using XISOSharp;

// Extract all files from an XISO image
XisoReader.DecodeXiso("game.iso", "output_folder", ExtractMode.Extract, out _);

// List all files in an XISO image (prints to console)
XisoReader.DecodeXiso("game.iso", null, ExtractMode.List, out _);

// Create an XISO image from a directory
XisoWriter.CreateXiso("source_folder", "output_folder", null, null, out _, "game.iso", null);

API Reference

XisoReader

Static class for reading and processing XISO disc images.

DecodeXiso

Main entry point for processing an XISO image. Verifies the image header, then performs extraction, listing, or rewriting based on the specified mode.

public static int DecodeXiso(
    string xisoPath,
    string? outputPath,
    ExtractMode mode,
    out string? outIsoPath,
    bool llCompat = false,
    CancellationToken cancellationToken = default,
    string? outputName = null,
    int? skipSectors = null,
    int? prependSectors = null)
Parameter Type Description
xisoPath string Path to the XISO file to process.
outputPath string? Output directory for extraction or rewrite output. When null in extract mode, a directory named after the ISO is created.
mode ExtractMode Operating mode: Extract, List, or Rewrite.
outIsoPath out string? Receives the path to the output ISO file when in rewrite mode.
llCompat bool If true, uses backwards-compatible (non-optimized) right-offset calculation. Defaults to false.
cancellationToken CancellationToken Token to monitor for cancellation requests.
outputName string? Custom output filename for rewrite mode. When null, the original filename with .iso extension is used.
skipSectors int? Optional number of 2048-byte sectors to skip in the source file before the XISO filesystem begins (Redump-style images with a video partition). When set, offset probing is skipped and the header must be at skipSectors * 2048 + 0x10000.
prependSectors int? Optional number of 2048-byte sectors to prepend to the output image in rewrite mode, leaving room for a video partition. Stored sector numbers remain partition-relative. Ignored in non-rewrite modes.

Returns: 0 on success, non-zero on error.

Exceptions:

  • FileNotFoundException — input file does not exist
  • InvalidDataException — file is not a valid XISO image
  • IOException — read errors
  • ExtractErrorException — non-fatal extraction error (see error code)
DecodeXisoAsync

Asynchronous wrapper around DecodeXiso that runs the synchronous engine on a thread pool thread.

public static async Task<(int Result, string? OutIsoPath)> DecodeXisoAsync(
    string xisoPath,
    string? outputPath,
    ExtractMode mode,
    bool llCompat = false,
    CancellationToken cancellationToken = default)

Returns: A tuple containing Result (0 on success) and OutIsoPath (the output ISO path in rewrite mode).

Extract, UnpackImage, List, Tree, DecodeXiso, and DecodeXisoAsync each have a Stream overload taking (Stream imageStream, string imageName, ...) in place of the input path — for memory, network, or embedded-resource images. The stream must be readable and seekable and is left open; imageName (typically the file name) drives output naming and messages. Rewrite stays path-only.

using var image = new MemoryStream(File.ReadAllBytes("game.iso"));
int rc = XisoReader.UnpackImage(image, "game.iso", "./out"); // stream stays open
Destination filesystems (IFilesystem, TODO #7)

UnpackImage also accepts any IFilesystem destination in place of outputPath:

public static int UnpackImage(
    string isoPath, IFilesystem filesystem,
    CancellationToken cancellationToken = default, int? skipSectors = null,
    UnpackOptions? options = null, IProgress<ProgressInfo>? progress = null)

public static int UnpackImage(
    Stream imageStream, string imageName, IFilesystem filesystem,
    CancellationToken cancellationToken = default, int? skipSectors = null,
    UnpackOptions? options = null, IProgress<ProgressInfo>? progress = null)

Files land at the filesystem root (no ISO-named subdirectory, no process working-directory changes) with the same per-file hardening as the disk path: skip-existing resume probes the destination filesystem, truncated data and failed writes throw the same ExtractFileException errors. Paths are destination-root-relative, /-separated, case-insensitive; FileLength returns -1 for unresolvable paths (never skipped, reports as truncated).

Built-in implementations:

Type Behavior
LocalFilesystem(string? root = null) Local disk under root (cwd-relative when null, matching the legacy unpack); LocalFilesystem.Instance is the shared cwd-relative instance; parent directories are not auto-created
MemoryFilesystem In-process byte snapshots committed when the unpack closes each file's stream; re-creating a file truncates it; parents auto-created; inspect via ReadAllBytes(path), FileNames, DirectoryNames
var memory = new MemoryFilesystem();
XisoReader.UnpackImage("game.iso", memory);
byte[] defaultXbe = memory.ReadAllBytes("default.xbe"); // never touched the disk

XisoReader.UnpackImage("game.iso", new LocalFilesystem(@"D:\games\out")); // disk, no chdir
Image explorer (XisoExplorer, TODO #11)

UI-agnostic explorer over one .iso or .cso image (the engine behind the Tester's Explore tab). The constructor probes the volume eagerly; every operation opens and closes the image, so instances are safe for concurrent background use. Paths are image-internal, /-separated, case-insensitive.

public sealed class XisoExplorer
{
    public XisoExplorer(string isoPath); // fail fast: ArgumentException / FileNotFoundException / XisoFormatException
    public string IsoPath { get; }
    public VolumeInfo Volume { get; }
    public IReadOnlyList<ExplorerNode> ListChildren(string internalPath);
    public ExplorerNode? GetNode(string internalPath); // synthetic "/" root; null when missing
    public void CopyOut(string internalPath, string destPath, UnpackOptions? options = null,
        CancellationToken cancellationToken = default, IProgress<ProgressInfo>? progress = null);
    public string? ComputeHashHex(string internalPath, HashAlgorithmName algorithm); // null when missing
    public XexInfo? GetXexInfo(string internalPath); // null when missing/dir/not XEX2
    public XbeInfo? GetXbeInfo(string internalPath); // null when missing/dir/not XBEH
    public static string Combine(string directory, string name);
    public static string Normalize(string? internalPath);
}

public sealed record ExplorerNode(
    string Name, string FullPath, bool IsDirectory, long Size, uint StartSector, byte Attributes);
var explorer = new XisoExplorer("game.iso");
foreach (var node in explorer.ListChildren("/sub"))
    Console.WriteLine($"{node.FullPath} ({node.Size} B)");
explorer.CopyOut("/docs/readme.txt", "./readme.txt");
string? sha256 = explorer.ComputeHashHex("/default.xbe", HashAlgorithmName.SHA256);
Image splitting (XisoSplitter, TODO #17)

Splits a plain .iso into FATX-friendly parts and reassembles them (the plain-ISO counterpart to CSO --ciso-split). Parts are a pure byte partition at sector-aligned cut points (<base>.1.iso, <base>.2.iso, …), so concatenation restores the image bit-for-bit. Inputs are pre-validated, the joined output is post-validated, existing targets are refused, and partial outputs are removed on failure/cancel.

public static class XisoSplitter
{
    public const long DefaultPartSizeBytes = 4294967296L; // FATX 4 GiB cap
    public static IReadOnlyList<string> Split(string isoPath, string outputBase, long partSizeBytes,
        CancellationToken cancellationToken = default, IProgress<ProgressInfo>? progress = null);
    public static IReadOnlyList<string> SplitHalves(string isoPath, string outputBase,
        CancellationToken cancellationToken = default, IProgress<ProgressInfo>? progress = null);
    public static string Join(string firstPartPath, string outputPath,
        CancellationToken cancellationToken = default, IProgress<ProgressInfo>? progress = null);
    public static string PartPath(string outputBase, int partIndex); // 0-based
    public static bool IsSplitPath(string? path); // *.1.iso
}
// XisoReader facades: SplitXiso / SplitXisoHalves / JoinSplitXiso
// CLI: split [--size <bytes|half>] [--output <base>] <image>… / join [--output <file>] <first.1.iso>…
var parts = XisoReader.SplitXiso("game.iso", "game", XisoSplitter.DefaultPartSizeBytes);
XisoReader.JoinSplitXiso(parts[0], "rejoined.iso");
VerifyXiso

Low-level method that validates the XISO header and returns root directory metadata. Most users should use DecodeXiso instead.

public static (uint rootDirSector, uint rootDirSize, long discLseek) VerifyXiso(
    Stream fs, string isoName, int? skipSectors = null)
Parameter Type Description
fs Stream Open stream positioned anywhere.
isoName string Display name for error messages.
skipSectors int? Optional number of 2048-byte sectors to skip from the start of the file before the XISO filesystem begins. When set, the header magic is verified at skipSectors * SectorSize + HeaderOffset and offset probing is skipped. Negative values throw ArgumentOutOfRangeException.

Returns: Tuple of (rootDirSector, rootDirSize, discLseek).

Exceptions:

  • InvalidDataException — no valid XISO header found or trailing magic byte mismatch
  • IOException — file too short to contain expected header data
  • ExtractErrorException — root directory sector and size are both zero (empty ISO)
Tree

Recursively lists all files in an XISO image in a tree format, showing full paths and sizes.

public static int Tree(
    string xisoPath,
    bool llCompat = false,
    CancellationToken cancellationToken = default)
GetVolumeInfo

Reads the XISO volume descriptor and returns metadata about the image without throwing on validation errors.

public static VolumeInfo GetVolumeInfo(string isoPath)

Returns: A VolumeInfo record containing IsValid, RootDirSector, RootDirSize, DiscLseek, DiscFormat (friendly layout name: RAW, GLOBAL (XGD2), XGD3, XGD2 Hybrid, XGD1, Unknown), FileLength, and TotalSectors.

ListDirectory

Returns metadata about all entries in the specified directory within an XISO image.

public static IReadOnlyList<EntryInfo> ListDirectory(string isoPath, string internalPath = "/")
Parameter Type Description
isoPath string Path to the XISO file.
internalPath string Path within the ISO (e.g. "/" for root, "/subdir" for a subdirectory).

Returns: List of EntryInfo records, each containing Name, IsDirectory, StartSector, FileSize, Attributes, LeftChildOffset, RightChildOffset.

GetEntryInfo

Returns metadata about a specific file or directory entry within an XISO image.

public static EntryInfo? GetEntryInfo(string isoPath, string internalPath)

Returns: An EntryInfo record, or null if the path does not exist.

CopyOut

Copies a single file or directory from an XISO image to the local filesystem. If the path points to a file, it is extracted to destPath. If the path points to a directory, all its contents are recursively extracted.

public static void CopyOut(string isoPath, string internalPath, string destPath)

Exceptions:

  • InvalidDataException — path does not exist in the XISO
  • IOException — read or write errors
CopyIn

Copies a host file into an XISO image, modifying it in place: replaces the entry at internalPath when it exists, or adds it as a new file when only its parent directory exists. A .old backup of the pre-patch image is written first unless createBackup is false.

public static void CopyIn(string isoPath, string hostFile, string internalPath, bool createBackup = true)

Exceptions:

  • FileNotFoundException — host file does not exist
  • InvalidDataException — path malformed, parent missing, target is a directory, or data does not fit in free space
  • XisoFormatException — not a valid XISO image
  • IOException — read or write errors
ComputeFileHash

Computes the hash of a single file within an XISO image.

public static byte[]? ComputeFileHash(string isoPath, string internalPath, HashAlgorithmName algorithm)
Parameter Type Description
isoPath string Path to the XISO file.
internalPath string Path within the ISO (e.g. "/subdir/file.xbe").
algorithm HashAlgorithmName Hash algorithm to use (HashAlgorithmName.MD5 or HashAlgorithmName.SHA256).

Returns: Hash bytes, or null if the file does not exist.

ComputeDirectoryHashes

Computes hashes for all files in a directory (or the entire image) within an XISO.

public static List<(string Path, byte[] Hash)> ComputeDirectoryHashes(
    string isoPath, string internalPath, HashAlgorithmName algorithm)

Returns: List of (path, hash) tuples for all files.

GetXexInfo

Parses the Xbox 360 XEX2 header of a .xex file inside the image: module flags, header size, entry point, image base/size, load address, region, allowed media types, media/title IDs, version, platform, disc number/count, encryption and compression types. Path and Stream overloads.

public static XexInfo? GetXexInfo(string isoPath, string internalPath)
public static XexInfo? GetXexInfo(Stream imageStream, string imageName, string internalPath)

Returns: An XexInfo record, or null when the entry is missing, a directory, or not an XEX2 executable.

Exceptions:

  • FileNotFoundException — input file does not exist (path overload)
  • XisoFormatException — not a valid XISO image
  • IOException — read errors
GetXbeInfo

Parses the original-Xbox XBEH header + certificate of a .xbe file inside the image — no reference tool does this: base address, entry point, section count, init flags, cert size/timestamp, title ID and UTF-16 title name (+ 16 alternate title IDs), allowed-media, region, and ratings bitmasks, disc number, version. All multi-byte fields are read little-endian (cert address is a load pointer: file offset = address − base). Path and Stream overloads.

public static XbeInfo? GetXbeInfo(string isoPath, string internalPath)
public static XbeInfo? GetXbeInfo(Stream imageStream, string imageName, string internalPath)

Returns: An XbeInfo record, or null when the entry is missing, a directory, not an XBEH executable, or the certificate is out of range, below base, truncated, or the wrong size.

Exceptions:

  • FileNotFoundException — input file does not exist (path overload)
  • XisoFormatException — not a valid XISO image
  • IOException — read errors
AuditXiso

Performs a deep integrity audit of an XISO image. Validates the header, walks the entire directory tree, checks sector bounds, detects cycles, validates filenames and attributes, and verifies the optimized tag.

public static AuditResult AuditXiso(string isoPath)
Parameter Type Description
isoPath string Path to the XISO file to audit.

Returns: An AuditResult record with IsValid, FilesChecked, DirsChecked, and Issues.

Exceptions:

  • FileNotFoundException — input file does not exist
  • IOException — read errors
Repair

Repairs the audit's safely-patchable (class-C) issues in place: reserved attribute bits (rewritten with MaskAttributes), a missing optimized tag (exact CreateXiso tag bytes at offset 31337), and path separators in filenames (length-preserving _ substitution; left alone on collision). Every patch is length-preserving — image size never changes. Pointers from corrupt records are not trusted (converges over bounded passes); truncation, structural, and refused classes are reported, never patched. A .old backup is written first unless createBackup is false.

public static RepairResult Repair(string isoPath, bool createBackup = true, bool dryRun = false)

Returns: A RepairResult record with Fixed, Remaining, BackupPath, DryRun, and Success (true when the image now passes the audit).

Exceptions:

  • FileNotFoundException — input file does not exist
  • InvalidDataException — CISO container or split part (decompress/reassemble first)
  • XisoFormatException — not a valid XISO image
  • IOException — read or write errors
Salvage

Rebuilds a readable image from a corrupt one: a bounded walk reusing the auditor's hardening limits carries every entry reachable without tripping a truncation or structural gate into a staging directory, then repacks it via CreateXiso into a fresh plain .iso. The source is only read, never modified (CISO input allowed); the rebuilt image is re-audited.

public static SalvageResult Salvage(string sourcePath, string? outputPath = null)

outputPath defaults to the source's directory plus the source stem with a .salvaged.iso suffix; an existing file is overwritten and a missing parent directory is created.

Returns: A SalvageResult record with Copied, Skipped, OutputPath, and OutputIssues (Success when the rebuilt image passes the audit).

Exceptions:

  • FileNotFoundException — source file does not exist
  • XisoFormatException — not a valid XISO image, or the tree root itself is unreachable
  • IOException — read or write errors, or repacking failed
GetFileTime / SetFileTime

Read and write the Windows FILETIME field of the volume descriptor (xdvdfs-compatible: 0 is 1601-01-01, the deterministic-image value). Path and IBlockDevice overloads for reading; the setter takes a raw value or a DateTimeOffset (see FileTimeHelper).

public static ulong GetFileTimeRaw(string isoPath, int? skipSectors = null)
public static DateTimeOffset GetFileTime(string isoPath, int? skipSectors = null)
public static ulong GetFileTimeRaw(IBlockDevice dev, string isoName = "memory", int? skipSectors = null)
public static DateTimeOffset GetFileTime(IBlockDevice dev, string isoName = "memory", int? skipSectors = null)
public static void SetFileTime(string isoPath, ulong fileTime, int? skipSectors = null)
public static void SetFileTime(string isoPath, DateTimeOffset dateTime, int? skipSectors = null)

Exceptions:

  • FileNotFoundException — input file does not exist (path overloads)
  • XisoFormatException — not a valid XISO image
  • IOException — read or write errors

Static class for creating and rewriting XISO disc images.

CreateXiso

Creates a new XISO from a local directory, or rewrites an existing ISO using a pre-built AVL tree.

public static int CreateXiso(
    string rootDirectory,
    string? outputDirectory,
    AvlNode? inRoot,
    Stream? sourceStream,
    out string? outIsoPath,
    string? inName,
    ProgressCallback? progressCallback = null,
    CancellationToken cancellationToken = default,
    int? prependSectors = null,
    IReadOnlyList<string>? excludePatterns = null,
    IProgress<ProgressInfo>? progress = null)
Parameter Type Description
rootDirectory string Source directory for creation, or base name for rewrite mode.
outputDirectory string? Directory where the output ISO is written. When null, the current working directory is used.
inRoot AvlNode? Pre-built AVL tree root. When null, the tree is generated from the file system.
sourceStream Stream? Source ISO stream for reading file data in rewrite mode; null when creating from a file system.
outIsoPath out string? Receives the full path of the created output ISO file.
inName string? Explicit output filename. When null, the directory name plus .iso is used.
progressCallback ProgressCallback? Optional callback invoked with (currentBytes, totalBytes) during write.
cancellationToken CancellationToken Token to monitor for cancellation requests.
prependSectors int? Optional number of 2048-byte sectors to prepend to the output image before the XISO filesystem begins, leaving room for a video partition. The placeholder area is zero-filled; stored sector numbers remain partition-relative. Negative values throw ArgumentOutOfRangeException.
excludePatterns IReadOnlyList<string>? Optional glob patterns of files/directories to omit from the image when creating from a file system (see GlobMatcher). Excluded directories are not recursed into. When Logger.RemoveSystemUpdate is set, **/$SystemUpdate/** is implicitly added. Ignored in rewrite mode.

Returns: 0 on success, 1 on error.

CreateXisoAsync

Asynchronous wrapper around CreateXiso.

public static async Task<(int Result, string? OutIsoPath)> CreateXisoAsync(
    string rootDirectory,
    string? outputDirectory,
    AvlNode? inRoot,
    Stream? sourceStream,
    string? inName,
    ProgressCallback? progressCallback = null,
    CancellationToken cancellationToken = default)

Returns: A tuple containing Result (0 on success) and OutIsoPath.

Skip / Prepend Sectors (Redump-style images)

Xbox disc images (especially Redump dumps) contain a video partition followed by the game partition (the XISO filesystem). When the game partition does not start at file offset 0, pass the partition start to the read APIs via skipSectors:

// XGD1 Redump image: game partition starts at sector 0xC180 (0x18300000 bytes)
int result = XisoReader.Extract("game.iso", "output", llCompat: false, skipSectors: 0xC180);

When creating an image that must sit after a video partition, use prependSectors to leave zero-filled space at the start of the file. The two options are symmetric, enabling round-trip reconstruction:

// Write the game partition at the same offset it was read from
XisoWriter.CreateXiso(srcDir, outputDir, null, null, out _, "game.iso", null,
    prependSectors: 0xC180);

Both parameters are also exposed on the CLI as --skip-sectors N and --prepend-sectors N.

GlobMatcher

Matches relative paths against shell-style glob patterns. Used by CreateXiso excludePatterns; also available as a standalone utility.

var matcher = new GlobMatcher(["**/*.tmp", "**/node_modules/**"]);
bool excluded = matcher.IsMatch("sub/notes.tmp"); // true

Supported syntax (use / as the separator; matching is case-insensitive):

Pattern Meaning
* Any characters within a single path segment
? Exactly one character within a single path segment
** As a complete segment: zero or more path segments. A trailing /** also matches the directory itself
[abc], [a-z], [!abc] Character classes with optional !/^ negation
\x Escapes the next character

Patterns without a leading **/ are anchored to the source root. A trailing / is treated as /**. On the CLI, patterns are passed via the repeatable -X <glob> flag; -s implicitly adds **/$SystemUpdate/** (create side matches entries named exactly $SystemUpdate, unlike the extract-side -s check which uses substring matching).


Logger

Static class providing configurable text output. All output is thread-safe and can be redirected.

Properties
Property Type Default Description
Out TextWriter Console.Out Writer for normal output. Set to TextWriter.Null to discard.
Error TextWriter Console.Error Writer for error output. Set to TextWriter.Null to discard.
Fields
Field Type Default Description
Quiet bool false When true, suppresses all non-error output.
RealQuiet bool false When true, suppresses all output including errors.
Warned bool false Set to true when a warning is issued during processing.
TotalBytes long 0 Cumulative bytes written across the current operation.
TotalFiles int 0 Cumulative files processed in the current operation.
TotalBytesAllIsos long 0 Cumulative bytes across all processed ISO images.
TotalFilesAllIsos int 0 Cumulative file count across all processed ISO images.
RemoveSystemUpdate bool false When true, files in $SystemUpdate folders are skipped.
MediaEnable bool true When true, .xbe files are automatically patched for media-enable during creation/rewrite.
XboxDiscLseek long 0 Disc lseek offset detected during verification, used in rewrite mode.
Methods
// Writes a formatted message to Out (unless Quiet is true).
public static void Log(string message, params object?[] args)

// Writes a line to Out (unless Quiet is true). No format arguments.
public static void LogLine(string message)

// Flushes Out (unless Quiet is true).
public static void Flush()

// Writes a formatted error message to Error (unless RealQuiet is true).
public static void LogErr(string message, params object?[] args)

AvlTree

Static class providing AVL (Adelson-Velsky/Landis) balanced binary search tree operations. Used internally for XISO directory indexing but exposed for advanced scenarios.

AvlCompareKey

Compares two strings case-insensitively using ASCII rules.

public static int AvlCompareKey(string lhs, string rhs)
AvlFetch

Looks up a node in the AVL tree by filename.

public static AvlNode? AvlFetch(AvlNode? root, string filename)

Returns: The matching AvlNode or null if not found.

AvlInsert

Inserts a node into the AVL tree, rebalancing as needed. Duplicate filenames are rejected.

public static AvlResult AvlInsert(ref AvlNode? root, AvlNode node)

Returns:

  • AvlResult.AvlBalanced — tree grew taller
  • AvlResult.NoErr — insertion completed without height change
  • AvlResult.AvlError — duplicate key
AvlTraverseDepthFirst

Traverses the AVL tree depth-first in the specified order.

public static int AvlTraverseDepthFirst(
    AvlNode? root,
    TraversalCallback callback,
    object? context,
    AvlTraversalMethod method,
    int depth)

Returns: 0 if the full traversal completed, or the non-zero value returned by the callback (early termination).

FreeTree

Frees an entire AVL tree by clearing all node references so the garbage collector can reclaim memory.

public static void FreeTree(AvlNode? root)

BoyerMoore

Implements the Boyer-Moore string search algorithm for efficient pattern matching in byte arrays. Used internally for media-enable patching of .xbe files.

public class BoyerMoore
{
    public BoyerMoore(byte[] pattern, int alphabetSize = 256)
    public void Init()
    public int Search(byte[] text, int startIndex, int length)
    public int Search(byte[] text)
    public void Done()
}
Member Description
Constructor Initializes a new pattern matcher with the given pattern and alphabet size.
Init() Builds the bad-character and good-suffix shift tables. Must be called before Search.
Search(byte[], int, int) Searches for the pattern within a subrange of the text buffer. Returns the index of the first match, or -1. Throws InvalidOperationException if Init was not called (or after Done).
Search(byte[]) Searches the entire text buffer starting at offset 0. Same InvalidOperationException contract.
Done() Releases the shift tables. Re-initialize before searching again.

FileTimeHelper

Static helper for converting between .NET timestamps and Windows FILETIME values.

public static class FileTimeHelper
{
    public static void WriteFileTimeNow(Span<byte> destination)
}

Writes the current UTC time as a Windows FILETIME (two little-endian 32-bit words, 8 bytes total) into the destination span. Used internally for writing timestamps into XISO headers.


Constants

Static class containing all magic values, offsets, and constants used by the XISO format. See the IntelliSense tooltips or XML documentation for detailed descriptions.

Key constants include:

Constant Value Description
HeaderData "MICROSOFT*XBOX*MEDIA" XISO header magic string
HeaderOffset 0x10000 Offset of XISO header from start of image
SectorSize 2048 One sector = 2 KB
RootDirectorySector 0x108 Sector index of root directory table
GlobalLseekOffset 0x0FD90000 Sector offset for GLOBAL layout
Xgd2LseekOffset 0x0FD90000 Sector offset for XGD2 layout (same as Global)
Xgd3LseekOffset 0x02080000 Sector offset for XGD3 layout
Xgd1LseekOffset 0x18300000 Sector offset for XGD1 layout
ReadWriteBufferSize 0x00200000 2 MB buffer for file copy operations
NumSectors(uint size) Computes the number of sectors required to hold size bytes

Data Types

Enums

ExtractMode

Operating mode for XISO image processing.

Value Description
GenerateAvl Build the AVL tree directory structure without writing an output file.
Extract Extract files from the XISO image to disk.
List List the contents of the XISO image to the logger.
Rewrite Rewrite the XISO image with an optimized AVL directory structure.
Tree Recursively list all files with sizes in a tree format.
Verify Deep-audit the XISO image: validate header, walk tree, check sector bounds, detect cycles.
ExtractError

Error codes for non-fatal extraction failures.

Value Code Description
ErrEndOfSector -5001 Unexpected end of sector while reading a directory entry chain.
ErrIsoRewritten -5002 XISO image has already been rewritten (optimized format detected).
ErrIsoNoFiles -5003 XISO image references no files in its directory table.
ErrFileTruncated -5004 File data ends before the reported size (truncated image or entry pointing past end of image).
ErrFileWrite -5005 Destination file or directory could not be created or written.
ErrExtractFailed -5006 End-of-run summary of a ContinueOnError run: one or more files failed (message lists each).
AvlResult

Result codes returned by AVL tree insertion.

Value Description
NoErr Operation completed successfully without requiring rebalancing.
AvlError An error occurred during the operation (e.g., duplicate key).
AvlBalanced Operation completed and the tree was rebalanced.
AvlTraversalMethod

Traversal order when walking an AVL tree.

Value Description
Prefix Pre-order: visit node before children.
Infix In-order: visit left child, then node, then right child.
Postfix Post-order: visit children before node.
AvlSkew

Skew direction of an AVL tree node.

Value Description
NoSkew Node is balanced (subtrees have equal height).
LeftSkew Left subtree is taller.
RightSkew Right subtree is taller.

Records

VolumeInfo

Metadata about an XISO volume descriptor.

Property Type Description
IsValid bool Whether the volume magic is valid.
RootDirSector uint Sector index of the root directory table.
RootDirSize uint Size of the root directory table in bytes.
DiscLseek long Disc lseek offset detected during probing.
DiscFormat string Friendly disc-layout identity from DiscLseek (RAW, GLOBAL (XGD2), XGD3, XGD2 Hybrid, XGD1, Unknown; Unknown when invalid).
FileLength long Total size of the ISO file in bytes.
TotalSectors long Total number of sectors in the ISO.
EntryInfo

Metadata about a single directory entry within an XISO image.

Property Type Description
Name string Filename of the entry.
IsDirectory bool Whether this entry is a directory.
StartSector uint Sector index where the entry's data begins.
FileSize uint Size of the file data in bytes (0 for directories).
Attributes byte Raw attribute byte (see Constants for flag definitions).
LeftChildOffset ushort Left child offset in the directory tree (0 if none).
RightChildOffset ushort Right child offset in the directory tree (0 if none).
AuditResult

Result of a deep integrity audit of an XISO image.

Property Type Description
IsValid bool Whether the image passed all checks.
FilesChecked int Number of file entries audited.
DirsChecked int Number of directory entries audited.
Issues IReadOnlyList<string> List of human-readable issues found during the audit.
RepairResult

Outcome of an in-place repair pass (Repair).

Property Type Description
Fixed IReadOnlyList<string> Applied (or dry-run would-be) fixes.
Remaining IReadOnlyList<string> Post-repair audit issues; empty when the image passes.
BackupPath string? Path of the .old pre-repair backup, or null when none was written.
DryRun bool Whether this was a preview pass that changed nothing.
Success bool True when Remaining is empty.
SalvageResult

Outcome of a salvage rebuild (Salvage).

Property Type Description
Copied IReadOnlyList<string> Carried image-internal paths (directories carry a trailing /).
Skipped IReadOnlyList<string> Dropped entries with reasons.
OutputPath string Path of the rebuilt plain .iso image.
OutputIssues IReadOnlyList<string> Re-audit issues of the rebuilt image.
Success bool True when the rebuilt image passes the audit.
XexInfo

Xbox 360 XEX2 header of an executable inside the image (GetXexInfo).

Property Type Description
ModuleFlags uint Module flags.
HeaderSize uint Header size in bytes.
EntryPoint uint Entry point address.
ImageBaseAddress uint Image base address.
ImageSize uint Image size in bytes.
LoadAddress uint Load address.
Region uint Region bitmask.
AllowedMediaTypes uint Allowed-media bitmask.
MediaId uint Media ID.
TitleId uint Title ID.
Version uint Version.
Platform byte Platform byte.
DiscNumber byte Disc number of a multi-disc title.
DiscCount byte Total disc count.
EncryptionType ushort Encryption type.
CompressionType ushort File compression type (0 = none, 1 = basic, 2 = normal, 3 = delta).
XbeInfo

Original-Xbox XBEH header + certificate of an executable inside the image (GetXbeInfo).

Property Type Description
BaseAddress uint Image base address (retail: 0x00010000).
EntryPoint uint Entry point address.
SectionCount uint Number of section headers.
InitFlags uint Init flags (e.g. 0x01 mount utility drive).
CertSize uint Certificate size in bytes (retail: 464, 0x1D0).
CertTimeDate uint Certificate timestamp (raw DWORD).
TitleId uint Title ID from the certificate.
TitleName string Title name from the certificate (UTF-16, up to 40 chars).
AlternateTitleIds uint[] Alternate title IDs (16 entries, usually zero).
AllowedMedia uint Allowed-media bitmask (0x01 hard disk … 0x80 media board).
GameRegion uint Game-region bitmask (0x01 NA, 0x02 JP, 0x04 RoW).
GameRatings uint Game-ratings bitmask (ESRB etc.).
DiskNumber uint Disc number of a multi-disc title.
Version uint Game version from the certificate.

Classes

AvlNode

Node in an AVL balanced binary search tree. Used to index XISO directory entries by filename.

Field Type Description
Offset uint Byte offset of this node's directory entry within its parent sector.
DirStart long Start byte position of the directory table this node belongs to.
Filename string Filename (case-insensitive key for the AVL tree).
FileSize uint Size of the file in bytes, or size of the directory entry table for directories.
StartSector uint Sector index where the file data or subdirectory table begins.
Subdirectory AvlNode? Root of an AVL tree containing the children of this directory node, or EmptySubdirectory.
OldStartSector uint Original sector position before rewrite.
Skew AvlSkew Current balance state of this node.
Left AvlNode? Left child in the AVL tree.
Right AvlNode? Right child in the AVL tree.
EmptySubdirectory static AvlNode Singleton sentinel representing an empty subdirectory.
DirEntry

Represents an on-disk directory entry in the XISO filesystem.

Field Type Description
Left DirEntry? Left child directory entry in the on-disk tree.
Parent DirEntry? Parent directory entry.
AvlNode AvlNode? Associated AVL node that indexes this entry by filename.
Filename string Filename of the file or directory.
FilenameLength byte Length of the filename in bytes (ASCII).
ROffset ushort Right-child offset (in DWORDs) within the directory sector.
Attributes byte File attribute flags (e.g., Constants.AttributeDir, Constants.AttributeArc).
FileSize uint Size of the file in bytes, or size of the directory entry table for directories.
StartSector uint Sector index where the file data or subdirectory begins.
CreateList

Describes a source directory and optional output name for creating an XISO image. Entries can be chained for batch creation.

Property Type Description
Path string Source directory path whose contents will be packed into the XISO.
Name string? Optional output filename. When null, the directory name is used.
Next CreateList? Next entry in a linked list of creation tasks, or null.

Delegates

ProgressCallback
public delegate void ProgressCallback(long currentValue, long finalValue);

Invoked during extraction/creation to report progress.

  • currentValue — number of bytes processed so far
  • finalValue — total number of bytes to process (may be 0 if unknown)
TraversalCallback
public delegate int TraversalCallback(AvlNode node, object? context, int depth);

Invoked for each node during an AVL tree traversal.

  • node — the current tree node being visited
  • context — arbitrary context object passed to the traversal
  • depth — current depth within the tree (0 = root)
  • Returns: 0 to continue traversal; any non-zero value stops the traversal

Exceptions

ExtractErrorException

Thrown when a non-fatal extraction error occurs.

public class ExtractErrorException : Exception
{
    public ExtractError ErrorCode { get; }
    public ExtractErrorException(ExtractError code)
}
Member Type Description
ErrorCode ExtractError The specific error code that caused this exception.
ExtractFileException

Per-file extraction failure carrying the full error context: which entry failed, where it lives in the image, where it was going on disk, and the underlying cause as InnerException. Message reads Failed to extract "<entry>" (sector N, M bytes) -> "<dest>": <reason>. Collected across a ContinueOnError run and summarized as ErrExtractFailed.

public sealed class ExtractFileException : ExtractErrorException
{
    public string InternalPath { get; }
    public string DestPath { get; }
    public uint StartSector { get; }
    public long FileSize { get; }
    public long BytesRead { get; }
}
XisoFormatException

Thrown when an XISO image has an invalid format — missing or corrupt header magic, truncated image, or sector pointers that exceed file bounds.

public class XisoFormatException : IOException
{
    public XisoFormatException(string message)
}
XisoEmptyException

Thrown when an XISO image is structurally valid but contains no files.

public class XisoEmptyException : ExtractErrorException
{
    public XisoEmptyException()
}
XisoFileTooLargeException

Thrown when a file exceeds the maximum size supported by the XISO format (4 GB, the 32-bit unsigned integer limit).

public class XisoFileTooLargeException : IOException
{
    public string FileName { get; }
    public long FileSize { get; }
}

Usage Examples

Extracting Files from an XISO

using XISOSharp;

var result = XisoReader.DecodeXiso(
    xisoPath: "game.iso",
    outputPath: "extracted_files",
    mode: ExtractMode.Extract,
    outIsoPath: out _,
    llCompat: false);

if (result == 0)
    Console.WriteLine("Extraction completed successfully.");

With a custom progress callback:

XisoReader.DecodeXiso(
    "game.iso", "output", ExtractMode.Extract, out _,
    llCompat: false,
    cancellationToken: CancellationToken.None);

// Access stats after completion:
Console.WriteLine($"Processed {Logger.TotalFiles} files, {Logger.TotalBytes} bytes");

Listing Files in an XISO

using XISOSharp;

// Lists all files in the XISO to the configured Logger output
XisoReader.DecodeXiso("game.iso", null, ExtractMode.List, out _);

Output appears on Logger.Out (defaults to Console.Out) and includes filename, size, and starting sector for each entry.

Rewriting an XISO

Rewriting rebuilds the directory structure into an optimized AVL layout, reducing fragmentation and potentially improving read performance on original Xbox hardware.

XisoReader.DecodeXiso(
    xisoPath: "game.iso",
    outputPath: "rewritten",
    mode: ExtractMode.Rewrite,
    outIsoPath: out var rewrittenPath,
    llCompat: false);

Console.WriteLine($"Rewritten ISO written to: {rewrittenPath}");

// Optionally delete the original after successful rewrite
if (File.Exists(rewrittenPath))
    File.Delete("game.iso");

Creating an XISO from a Directory

using XISOSharp;

var result = XisoWriter.CreateXiso(
    rootDirectory: "my_game_files",
    outputDirectory: "output",
    inRoot: null,           // Null to generate AVL tree from filesystem
    sourceStream: null,     // Null when creating from filesystem
    outIsoPath: out var isoPath,
    inName: "my_game.iso",
    progressCallback: null,
    cancellationToken: CancellationToken.None);

if (result == 0)
    Console.WriteLine($"ISO created at: {isoPath}");

Auditing, Repairing, and Salvaging an XISO

No reference tool repairs images — these APIs diagnose and recover corrupt ones:

using XISOSharp;

// Diagnose: header, tag, full tree walk, sector bounds, cycles, names
AuditResult audit = XisoReader.AuditXiso("game.iso");
if (!audit.IsValid)
    foreach (var issue in audit.Issues)
        Console.WriteLine($"  - {issue}");

// Repair what is safely patchable in place (keeps game.iso.old unless disabled)
RepairResult repaired = XisoReader.Repair("game.iso");
Console.WriteLine($"fixed {repaired.Fixed.Count}, remaining {repaired.Remaining.Count}");

// Truncated / structurally damaged: rebuild reachable entries into a fresh image
SalvageResult salvaged = XisoReader.Salvage("game.iso"); // -> game.salvaged.iso
Console.WriteLine($"carried {salvaged.Copied.Count}, dropped {salvaged.Skipped.Count}");

Progress Reporting

The ProgressCallback delegate provides real-time progress during creation and extraction:

void OnProgress(long current, long total)
{
    if (total > 0)
    {
        var pct = (double)current / total * 100;
        Console.Write($"\rProgress: {pct:F1}% ({current:N0} / {total:N0} bytes)");
    }
    else
    {
        Console.Write($"\rBytes written: {current:N0}");
    }
}

XisoWriter.CreateXiso(
    "source", "output", null, null, out _,
    inName: "game.iso",
    progressCallback: OnProgress);

Cancellation Support

Both DecodeXiso and CreateXiso accept a CancellationToken:

var cts = new CancellationTokenSource();

// Cancel after 30 seconds
cts.CancelAfter(TimeSpan.FromSeconds(30));

try
{
    XisoReader.DecodeXiso("large.iso", "output", ExtractMode.Extract, out _,
        cancellationToken: cts.Token);
}
catch (OperationCanceledException)
{
    Console.WriteLine("Operation was cancelled.");
}

Async overloads are also available:

var cts = new CancellationTokenSource();

var (result, outPath) = await XisoReader.DecodeXisoAsync(
    "game.iso", "output", ExtractMode.Extract,
    cancellationToken: cts.Token);

Suppressing Output

Configure Logger.Quiet or Logger.RealQuiet before calling any API:

// Suppress informational output, keep errors
Logger.Quiet = true;

// Suppress ALL output including errors
Logger.RealQuiet = true;

XisoReader.DecodeXiso("game.iso", "output", ExtractMode.Extract, out _);

Reset them after processing if you need normal output for subsequent operations.

Disabling Media-Enable Patching

By default, .xbe files are automatically patched to bypass media-check on modified Xbox consoles. Disable this behavior:

Logger.MediaEnable = false;

XisoWriter.CreateXiso("source", "output", null, null, out _, "game.iso", null);

Skipping System Update Folders

Logger.RemoveSystemUpdate = true;

XisoReader.DecodeXiso("game.iso", "output", ExtractMode.Extract, out _);

When enabled, any files within a $SystemUpdate folder are excluded from extraction and creation.

Redirecting Log Output

Send log output to a file or custom TextWriter:

using var logWriter = new StreamWriter("xiso.log");

Logger.Out = logWriter;
Logger.Error = logWriter;

XisoReader.DecodeXiso("game.iso", "output", ExtractMode.Extract, out _);

logWriter.Flush();

Graphical Progress (WPF / Blazor)

The ProgressCallback delegate integrates naturally into GUI frameworks:

// WPF example
var progress = new Progress<(long current, long total)>(p =>
{
    Dispatcher.Invoke(() =>
    {
        if (p.total > 0)
            progressBar.Value = (double)p.current / p.total * 100;
        statusLabel.Text = $"{p.current:N0} / {p.total:N0} bytes";
    });
});

await Task.Run(() =>
{
    XisoReader.DecodeXiso("game.iso", "output", ExtractMode.Extract, out _,
        cancellationToken: tokenSource.Token);
});

Error Handling

The library uses a combination of return codes and exceptions:

Mechanism Usage
Return code (int) 0 = success, non-zero = failure. Returned by DecodeXiso and CreateXiso.
ExtractErrorException Thrown for non-fatal extraction errors. Check ErrorCode for the specific error.
ExtractFileException Per-file extraction failure (entry, sector, expected/actual bytes, OS cause as inner).
XisoFormatException Thrown when an XISO image has an invalid format (corrupt header, truncated, bad sector pointers).
XisoEmptyException Thrown when an XISO image contains no files.
XisoFileTooLargeException Thrown when a file exceeds the 4 GB XISO format limit.
InvalidDataException Thrown when a requested path does not exist within the XISO.
IOException Thrown for file read/write errors.
FileNotFoundException Thrown when the input file does not exist.
OperationCanceledException Thrown when a CancellationToken is triggered.

Example:

try
{
    var result = XisoReader.DecodeXiso("game.iso", "output", ExtractMode.Extract, out _);
    if (result != 0)
        Console.Error.WriteLine("Operation failed.");
}
catch (ExtractErrorException ex)
{
    Console.Error.WriteLine($"Extract error: {ex.ErrorCode} - {ex.Message}");
}
catch (InvalidDataException ex)
{
    Console.Error.WriteLine($"Invalid XISO: {ex.Message}");
}

Thread Safety

The core processing engine is not thread-safe for concurrent operations on the same file. However:

  • Each call to DecodeXiso or CreateXiso is self-contained and safe to call from different threads when processing different files.
  • Logger is safe for concurrent access across multiple operations.
  • Async overloads (DecodeXisoAsync, CreateXisoAsync) run the synchronous engine on a thread pool thread via Task.Run, which is safe for UI thread responsiveness.

Compatibility

Target Version
.NET 8 net8.0
.NET 9 net9.0
.NET 10 net10.0

The library targets net8.0, net9.0, and net10.0. Its only runtime dependency beyond the .NET runtime is ZArchiveSharp (for ZAR archive support); everything else is BCL-only.


Performance

  • 2 MB read/write buffer for file copy operations, configurable via Constants.ReadWriteBufferSize.
  • Boyer-Moore search for efficient .xbe media-enable pattern matching.
  • AVL balanced tree for O(log n) directory lookups during create/rewrite.
  • Synchronous I/O for maximum throughput; async overloads are provided for UI responsiveness, not I/O concurrency.
  • The library produces byte-identical output to the original C extract-xiso tool v2.7.1.

License

MIT License — see the LICENSE file included in the package for full terms.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.0 100 9/13/2026
1.1.0 96 9/13/2026
1.0.2 115 9/11/2026
1.0.1 94 9/11/2026
1.0.0 97 9/11/2026