XisoReader (XISOSharp namespace) is a static class providing all read-side
operations: verification, extraction, listing, tree traversal, rewriting, volume info,
directory listing, auditing, hashing, and copy-out.
- Method index
- VerifyXiso
- Extract / List / Tree / Rewrite
- Resume interrupted unpacks
- DecodeXiso (main entry)
- DecodeXisoAsync
- GetVolumeInfo
- ListDirectory / GetEntryInfo
- CopyOut
- ComputeFileHash / ComputeDirectoryHashes
- AuditXiso
- Disc offset probing
| Method | Description |
|---|---|
VerifyXiso |
Low-level header verification; returns root directory metadata + disc offset |
Extract |
Extract an image to a directory |
UnpackImage |
Unpack the whole image; auto-detects the optimized layout and ISO-named default output |
List |
List top-level entries |
Tree |
Recursive listing with sizes and totals |
Rewrite |
Rewrite an image into the optimized layout |
DecodeXiso |
Main entry point (all modes) |
DecodeXisoAsync |
Async wrapper of DecodeXiso |
OpenImageStream |
Open an image for reading (.cso-aware); caller owns the stream |
IsOptimizedImage |
Probe the optimized tag (path or stream overload) |
GetVolumeInfo |
Volume descriptor metadata without throwing |
ListDirectory |
Metadata of entries in a directory |
ListDirectoryFlat |
Entry names of a directory (non-recursive convenience) |
GetEntryInfo |
Metadata of one entry by path |
CopyOut |
Copy one file or directory out of an image |
ComputeFileHash |
Hash one file (MD5/SHA-256/… via HashAlgorithmName) |
ComputeDirectoryHashes |
Hash every file under a path |
GetXexInfo |
Parse the Xbox 360 XEX2 header of a .xex file |
AuditXiso |
Deep integrity audit |
public static (uint rootDirSector, uint rootDirSize, long discLseek) VerifyXiso(
Stream fs, string isoName, int? skipSectors = null)Verifies the header magic at all known disc offsets (or at the skipSectors offset
when given) and returns the root directory table location and the detected disc offset.
| Parameter | Meaning |
|---|---|
fs |
Open, readable stream positioned anywhere (file, memory, block-device backed) |
isoName |
Display name used in error messages |
skipSectors |
Treat the XISO as starting N sectors into the file (Redump). Negative → ArgumentOutOfRangeException |
Throws: XisoFormatException (invalid/corrupt), IOException (file too short),
XisoEmptyException (no files).
public static int Extract(
string xisoPath, string? outputPath, bool llCompat,
CancellationToken cancellationToken = default, int? skipSectors = null,
UnpackOptions? options = null, IProgress<ProgressInfo>? progress = null)
public static int UnpackImage(
string isoPath, string? outputPath = null,
CancellationToken cancellationToken = default, int? skipSectors = null,
UnpackOptions? options = null, IProgress<ProgressInfo>? progress = null)
public static int List(
string xisoPath, bool llCompat,
CancellationToken cancellationToken = default, int? skipSectors = null)
public static int Tree(
string xisoPath, bool llCompat,
CancellationToken cancellationToken = default, int? skipSectors = null)
public static int Rewrite(
string xisoPath, string? outputPath, out string? outIsoPath,
CancellationToken cancellationToken = default,
string? outputName = null, int? skipSectors = null, int? prependSectors = null,
IProgress<ProgressInfo>? progress = null)| Parameter | Meaning |
|---|---|
xisoPath |
Path of the ISO (for Rewrite, the source; the .old rename is internal) |
outputPath |
null → extract into an ISO-named subdirectory of the current directory; otherwise the target directory |
llCompat |
true = legacy linked-list right-offset calculation; false = optimized layout |
outputName |
Rewrite only: custom output filename (default: original name with .iso) |
skipSectors |
Read offset (Redump video partition), in 2048-byte sectors |
prependSectors |
Rewrite only: reserve zero-filled sectors before the filesystem |
progress |
Rewrite: structured progress channel (IProgress<ProgressInfo>) — see XisoWriter API. Extract: a FileAdded event per file actually written (skipped/excluded files are silent) |
options |
Extract/unpack only: resume options — see Resume interrupted unpacks |
UnpackImage is the convenience form of Extract: it probes the optimized-tag marker
to pick llCompat automatically and defaults the output directory to the ISO name
(minus .iso), so callers never need to know the image layout.
All return 0 on success.
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 (ArgumentException otherwise) and is
left open; imageName (typically the file name) drives output naming and
messages. IsOptimizedImage has a matching Stream overload (position
restored), and OpenImageStream — the .cso-aware opener the path APIs use
internally — is public too. Rewrite stays path-only (it is file-identity based,
the .old dance), so the stream DecodeXiso refuses ExtractMode.Rewrite
with ArgumentException.
using var image = new MemoryStream(File.ReadAllBytes("game.iso"));
int rc = XisoReader.UnpackImage(image, "game.iso", "./out"); // stream stays openpublic sealed class UnpackOptions
{
public bool SkipExisting { get; set; }
public bool ContinueOnError { get; set; }
public bool ShouldSkip(string destPath, long fileSize);
}When SkipExisting is set, Extract / UnpackImage / CopyOut leave any
destination file already holding the same byte count untouched (logged as
skip: <path>) instead of overwriting it — re-running an interrupted unpack
completes the missing files instead of redoing the finished ones. XISO stores no
per-file timestamps, so size is the identity signal: a same-size file is assumed
to be a complete earlier write, while a missing or short file (a torn write) is
rewritten. Cancellation is honored per entry (OperationCanceledException), and the
process working directory is restored even when the run aborts.
When ContinueOnError is set, a per-file failure (uncreatable destination,
truncated data, failed write) is recorded and skipped instead of aborting;
an uncreatable directory skips its subtree. The run still throws at the end —
an ExtractErrorException with code ErrExtractFailed listing every failure
(the xdvdfs Failed to unpack image wrapper) — so the failure is visible to
callers. Structural table corruption still aborts immediately (TODO #16).
public sealed class ExtractFileException : ExtractErrorException
{
public string InternalPath { get; } // entry inside the image
public string DestPath { get; } // destination being written
public uint StartSector { get; } // partition-relative data sector
public long FileSize { get; } // reported size in bytes
public long BytesRead { get; } // bytes read before a short read (-1 otherwise)
}Every per-file extraction failure carries this context (TODO #9, xdvdfs #187):
Message reads Failed to extract "<entry>" (sector N, M bytes) -> "<dest>": <reason>, with the OS error as InnerException. New
ExtractError codes: ErrFileTruncated (-5004, image data ends early),
ErrFileWrite (-5005, destination create/write failed), ErrExtractFailed
(-5006, end-of-run summary under ContinueOnError). Extracted files are
integrity-checked: out-of-image data ranges are refused before the destination
is created, and the on-disk length is re-statted after the copy.
public static int DecodeXiso(
string xisoPath,
string? outputPath,
ExtractMode mode,
out string? outIsoPath,
bool llCompat,
CancellationToken cancellationToken = default,
string? outputName = null,
int? skipSectors = null,
int? prependSectors = null,
IProgress<ProgressInfo>? progress = null,
UnpackOptions? unpackOptions = null)The generic entry point used by the wrappers above. mode is one of ExtractMode:
Extract, List, Tree, Rewrite, GenerateAvl (internal use), or Verify.
The Stream overload is covered under Stream-based overloads.
public static async Task<(int Result, string? OutIsoPath)> DecodeXisoAsync(
string xisoPath, string? outputPath, ExtractMode mode,
bool llCompat = false, CancellationToken cancellationToken = default,
string? outputName = null, int? skipSectors = null, int? prependSectors = null,
IProgress<ProgressInfo>? progress = null, UnpackOptions? unpackOptions = null)Runs DecodeXiso on the thread pool. Returns the result code and, in rewrite mode,
the output path.
public static VolumeInfo GetVolumeInfo(string isoPath)Reads the volume descriptor without throwing on validation errors. Returns a
VolumeInfo record:
| Member | Type | Meaning |
|---|---|---|
IsValid |
bool |
Header magic found |
RootDirSector |
uint |
Root directory table sector |
RootDirSize |
uint |
Root directory table size (bytes) |
DiscLseek |
long |
Detected disc offset |
FileLength |
long |
File size |
TotalSectors |
long |
Total sectors |
public static IReadOnlyList<EntryInfo> ListDirectory(string isoPath, string internalPath = "/")
public static IReadOnlyList<string> ListDirectoryFlat(string isoPath, string internalPath = "/")
public static EntryInfo? GetEntryInfo(string isoPath, string internalPath)internalPathuses forward slashes, e.g."/","/subdir","/subdir/file.bin".ListDirectoryreturnsEntryInforecords:Name,IsDirectory,StartSector,FileSize,Attributes,LeftChildOffset,RightChildOffset.ListDirectoryFlatreturns just the entry names — the library behind the CLI's--lsflag.- Throws
InvalidDataExceptionwhen a path does not exist. GetEntryInforeturnsnullfor a missing path.
public static void CopyOut(string isoPath, string internalPath, string destPath,
UnpackOptions? options = null, CancellationToken cancellationToken = default)Copies one file — or an entire directory, recursively — out of the image to
destPath without a full extraction. With SkipExisting, up-to-date destinations
are skipped per Resume interrupted unpacks.
public static byte[]? ComputeFileHash(
string isoPath, string internalPath, HashAlgorithmName algorithm)
public static IReadOnlyList<(string Path, byte[] Hash)> ComputeDirectoryHashes(
string isoPath, string internalPath, HashAlgorithmName algorithm)- Hash one file (
nullif not found) or every file under a directory (recursive). - Any
HashAlgorithmNameworks — the CLI exposes--md5and--sha256.
public static XexInfo? GetXexInfo(string isoPath, string internalPath)Parses the Xbox 360 executable (XEX2) header of a .xex file inside the image. All
fields are read big-endian per the XEX2 specification (see xex2_info.h in
xenia):
XexInfo member |
Meaning |
|---|---|
ModuleFlags |
Title / DLL / user-mode etc. bit flags |
HeaderSize |
XEX header region size (typically 0x4000) |
EntryPoint |
Entry point RVA (optional header) |
ImageBaseAddress |
Image base address (optional header) |
ImageSize / LoadAddress |
Security info |
Region |
NTSC-U / NTSC-J / PAL bit flags |
AllowedMediaTypes |
Media type bitmask (hard disk, DVD-9, …) |
MediaId / TitleId / Version |
Execution info |
Platform / DiscNumber / DiscCount |
Execution info |
EncryptionType / CompressionType |
File format info |
Returns null when the path does not exist, points to a directory, or the file is not
an XEX2 executable. Validated against retail Xbox 360 Redump images (Perfect Dark Zero, Payday 2). The CLI exposes this as --xex-info.
public static byte[] ComputeImageChecksum(string isoPath, CancellationToken ct = default);
public static string ComputeImageChecksumHex(string isoPath, CancellationToken ct = default);In XISOSharp namespace via XisoChecksum (xdvdfs checksum compat): deterministic SHA3-256 over SortedDictionary Ordinal /DIR/FILE UTF-8 path bytes + streamed file data (IncrementalHash SHA3_256, BCL on .NET 8+). NOT SHA256 of full image. CLI checksum prints hex tab path. See xdvdfs Compat.
// Video / update
bool XisoRedump.TryExtractVideo(string redumpPath, string? outputVideoPath, out string? outPath);
bool XisoRedump.TryExtractUpdate(string redumpPath, string? outputUpdatePath, string? outputVideoPath = null);
// Filler / seed / wipe / trim / petrify
byte[] XisoOperations.ExtractFiller(string isoPath, long isoOffset = 0);
uint? XisoOperations.ExtractSeed(string isoPath); // XGD1 only, brute-force
int XisoOperations.WipeFiller(string inputPath, string outputPath);
int XisoOperations.TrimXiso(string inputPath, string? outputPath);
int XisoSkeleton.Petrify(string inputPath, string? skeletonPath, string? hashPath);
// Rebuild lossless
int XisoRedump.RebuildRedump(string xisoPath, string videoPath, string? fillerOrSeedPath, string? updatePath, string outputRedumpPath, int[]? securitySectors = null, IProgress<ProgressInfo>? progress = null, CancellationToken ct = default);
// Ranges
(IReadOnlyList<(long Start,long End)> SysRanges, IReadOnlyList<(long Start,long End)> FileRanges) XisoRanges.GetXisoRanges(string isoPath, long isoOffset = 0);
IReadOnlyList<(long Start,long End)> XisoRanges.MergeRanges(IEnumerable<(long,long)> a, IEnumerable<(long,long)> b);
IReadOnlyList<(string Path, long Offset, uint Size)> XisoRanges.CollectFileEntries(FileStream fs, long isoOffset);All ported from LibXGD/XGD.cs:11 tables (XgdTables.cs) + XDVDFS.cs (GetValidSectors/ProcessXISO). See Archival and xdvdfs Compat.
public static (uint rootDirSector, uint rootDirSize, long discLseek) VerifyXiso(IBlockDevice dev, string isoName, int? skipSectors = null);
public static AuditResult AuditXiso(IBlockDevice dev);
public static IReadOnlyList<EntryInfo> ListDirectory(IBlockDevice dev, string internalPath = "/");IBlockDevice stack under XISOSharp.Core/BlockDevice/ (FileBlockDevice/MemoryBlockDevice/OffsetBlockDevice/CisoBlockDevice). Mirror xdvdfs-core/src/blockdev.rs — enables in-memory golden fixtures + CISO random-access via CisoBlockDevice single-sector cache without no_std target. All FileStream overloads delegate to these.
public static AuditResult AuditXiso(string isoPath)Deep integrity audit — the library behind the CLI's -V flag:
- header magic at all known offsets
- optimized-tag presence at offset 31337
- full directory tree walk with cycle detection
- sector bounds for every entry (file and directory)
- reserved attribute bits (
0x08,0x40) - filename validity
Returns AuditResult (IsValid, FilesChecked, DirsChecked, Issues).
VerifyXiso probes the header at these offsets, in order (via Constants.cs:127 + XgdTables.cs + IBlockDevice offset probes):
| # | Offset | Layout |
|---|---|---|
| 1 | 0x00000000 |
RAW |
| 2 | 0x0FD90000 |
GLOBAL / XGD2 |
| 3 | 0x02080000 |
XGD3 |
| 4 | 0x89D80000 |
Hybrid (XGD2-Hybrid) — native since 2026-08-26 (Xgd2HybridLseekOffset) |
| 5 | 0x18300000 |
XGD1 |
When skipSectors is provided, probing is skipped and the header must be at
skipSectors × 2048 + 0x10000.
See also: Library Overview · XisoWriter API · Utilities & Types · XISO Format