-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Welcome to the BatchConvertIsoToXiso wiki!
- Introduction
- What is XDVDFS?
- XISO File Structure
- The XDVDFS.cs Class
- Algorithm Deep Dive
- File Entry Structure
- Volume Descriptor
- Process Flow Diagrams
- Integration with XisoWriter
The XDVDFS.cs class is the heart of the native C# XISO processing engine in the Batch ISO to XISO Converter. It implements the Xbox Disc Volume Descriptor File System (XDVDFS) traversal logic to identify, validate, and extract meaningful data from Xbox and Xbox 360 ISO images.
XDVDFS (Xbox Disc Volume Descriptor File System) is Microsoft's proprietary file system used on original Xbox and Xbox 360 game discs. It is based on a binary tree structure for directory entries and uses 2048-byte sectors (standard CD/DVD sector size).
| Feature | Value | Description |
|---|---|---|
| Sector Size | 2048 bytes | Standard DVD sector size |
| Header Offset | 0x10000 (65536 bytes) | XISO header location |
| Magic String | MICROSOFT*XBOX*MEDIA |
Volume descriptor identifier |
| Tree Structure | Binary Search Tree | Directory entries organized as BST |
An XISO file has a specific layout that differs from standard ISO 9660:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β XISO FILE LAYOUT β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββββββββββ β
β β Volume Descriptor β Sector 32 (0x10000 bytes from start) β
β β (0x800 bytes) β Contains: β
β β β - Magic ID ("MICROSOFT*XBOX*MEDIA") β
β β β - Root Directory Table Sector β
β β β - Volume size info β
β ββββββββββββ¬βββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββ β
β β Root Directory β Contains file entries as binary tree nodes β
β β Table β Each entry: 14 bytes header + filename β
β β β β
β ββββββββββββ¬βββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββ β
β β Subdirectories & β More directory tables or file data β
β β File Data β organized throughout the image β
β β β β
β βββββββββββββββββββββββ β
β β
β Note: Standard Xbox ISOs (Redump format) have the game partition at β
β offset 0x18300000 (XGD1), 0xFD90000 (XGD2), etc. β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
BatchConvertIsoToXiso/Services/XisoServices/XDVDFS/XDVDFS.cs
The Xdvdfs static class performs three critical functions:
- Filesystem Traversal: Navigates the binary tree structure of directory entries
- Sector Collection: Identifies all sectors containing valid data (files + metadata)
- Range Optimization: Consolidates contiguous sectors into ranges for efficient copying
private const long XisoHeaderOffset = 0x10000; // 65536 bytes
public static readonly byte[] Magic = "XBOX_DVD_LAYOUT_TOOL_SIG"u8.ToArray();| Constant | Value | Purpose |
|---|---|---|
XisoHeaderOffset |
0x10000 (65536) | Offset to XISO header from partition start |
SectorSize |
2048 | Bytes per sector (from Utils) |
A lightweight struct used for the iterative (non-recursive) tree traversal:
private struct DirectoryWorkItem
{
public long RootOffset; // Byte offset of directory table
public uint RootSize; // Size of directory table in bytes
public long ChildOffset; // Current offset within directory (tree node position)
}The XDVDFS uses an iterative depth-first traversal using a stack, avoiding recursion limits for deep directory structures:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β BINARY TREE TRAVERSAL VISUALIZATION β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Directory Table Layout (each entry is a tree node): β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Entry Structure (variable length): β β
β β ββββββββββββ¬ββββββββββββ¬ββββββββββββ¬βββββββββββ¬βββββββββββ¬βββββββββ β β
β β β Left Ptr β Right Ptr β Sector β Size β Attr β Name β β β
β β β (2 bytes)β (2 bytes) β (4 bytes) β (4 bytes)β (1 byte) β(n bytesβ β β
β β ββββββββββββ΄ββββββββββββ΄ββββββββββββ΄βββββββββββ΄βββββββββββ΄βββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Example Directory Tree: β
β β
β βββββββββββ β
β β Root β β
β β Entry 1 β β
β ββββββ¬βββββ β
β βββββββββββββββΌββββββββββββββ β
β βΌ βΌ βΌ β
β βββββββββββ βββββββββββ βββββββββββ β
β β Left β β Currentβ β Right β β
β β Child β β Entry β β Child β β
β β (0x0002)β β β β (0x0005)β β
β ββββββ¬βββββ ββββββββββ ββββββ¬βββββ β
β β β β
β βΌ βΌ β
β βββββββββββ βββββββββββ β
β β Sub-Dir β β Sub-Dir β β
β β Entry A β β Entry B β β
β βββββββββββ βββββββββββ β
β β
β Traversal Order: Right β Current β Left (stack-based DFS) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
-
Initialize Stack: Push root directory onto stack with
ChildOffset = 0 - Pop Entry: Get next work item from stack
- Cycle Detection: Check if we've visited this position before (prevents infinite loops)
- Read Entry: Parse the binary entry structure from the file stream
- Process Children: Push right child, then after processing current, push left child
- Collect Sectors: Add file/directory sectors to the valid sectors list
// The stack-based iterative approach (simplified)
while (stack.Count > 0)
{
var item = stack.Pop();
// Read entry at current position
var leftChildOffset = Utils.ReadUShort(isoFs); // 0xFFFF = no child
var rightChildOffset = Utils.ReadUShort(isoFs);
var entrySector = Utils.ReadUInt(isoFs);
var entrySize = Utils.ReadUInt(isoFs);
var attributes = (byte)isoFs.ReadByte();
var nameLength = (byte)isoFs.ReadByte();
// Push right child to stack (processed later)
if (rightChildOffset != 0xFFFF)
stack.Push(item with { ChildOffset = rightChildOffset * 4 });
// Process current entry (file or directory)
if (isDirectory)
ProcessDirectory(entrySector, entrySize);
else
ProcessFile(entrySector, entrySize);
// Push left child to stack
if (leftChildOffset != 0xFFFF)
stack.Push(item with { ChildOffset = leftChildOffset * 4 });
}As the tree is traversed, the algorithm collects sectors containing valid data:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SECTOR COLLECTION LOGIC β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Valid sectors include: β
β β
β 1. HEADER SECTORS (always included) β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Sector (offset / 2048) β β
β β Sector (offset / 2048) + 1 (second header sector) β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β 2. DIRECTORY TABLE SECTORS β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β For each directory: β β
β β - Start: RootOffset / 2048 β β
β β - Count: (RootSize + 2047) / 2048 (rounded up) β β
β β β β
β β Example: Directory at offset 0x10000 with size 0x1800 bytes β β
β β - Sector 8 (0x10000 / 2048) β β
β β - Sector 9 (0x10800 / 2048) β β
β β - Sector 10 (0x11000 / 2048) β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β 3. FILE DATA SECTORS β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β For each file: β β
β β - Start: (ISO_Offset + entryOffset) / 2048 β β
β β - Count: (entrySize + 2047) / 2048 (rounded up) β β
β β β β
β β Example: File at sector 500 with size 5000 bytes β β
β β - Start Sector: 500 β β
β β - Sector Count: 3 (ceil(5000/2048)) β β
β β - Sectors: 500, 501, 502 β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
After collecting all valid sectors, they are sorted and consolidated into contiguous ranges:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β RANGE CONSOLIDATION ALGORITHM β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Input: [8, 9, 10, 50, 51, 52, 53, 100, 200, 201, 202] β
β β
β Step 1: Sort (already sorted in this example) β
β β
β Step 2: Group contiguous sequences β
β β
β [8, 9, 10] β Range (8, 10) β
β [50, 51, 52, 53] β Range (50, 53) β
β [100] β Range (100, 100) β
β [200, 201, 202] β Range (200, 202) β
β β
β Output: [(8, 10), (50, 53), (100, 100), (200, 202)] β
β β
β Visualization: β
β β
β Sector: 0ββββββ8βββ10ββββββ50ββββ53ββ100βββ200βββ202βββββ β
β ββββββββββββββββββββββββββββββββββββββββββββ β
β β ββRange 1ββ β2β β3β ββRange 4ββ β
β ββPadding/Unusedββ β
β β
β The ranges represent: β
β - Range 1: Header + Root Directory β
β - Range 2: Subdirectory Table β
β - Range 3: Small File β
β - Range 4: Large File β
β β
β Result: Only 4 copy operations instead of 11 individual sector copies! β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Each file/directory entry in XDVDFS follows this binary format:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FILE ENTRY BINARY STRUCTURE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Offset Size Field Description β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β 0x00 2 bytes LeftSubTree Offset/4 to left child (0xFFFF = none) β
β 0x02 2 bytes RightSubTree Offset/4 to right child (0xFFFF = none) β
β 0x04 4 bytes StartSector Sector where file data begins β
β 0x08 4 bytes FileSize Size of file in bytes β
β 0x0C 1 byte Attributes File attributes bitmask β
β 0x0D 1 byte NameLength Length of filename (0 = empty) β
β 0x0E N bytes FileName ASCII filename (not null-terminated) β
β βββββββ ββββββ Padding To 4-byte boundary β
β β
β Attributes Bitmask: β
β βββββββ¬ββββββ¬ββββββ¬ββββββ¬ββββββ¬ββββββ¬ββββββ¬ββββββ β
β β 7 β 6 β 5 β 4 β 3 β 2 β 1 β 0 β β
β βββββββ΄ββββββ΄ββββββ΄ββββββ΄ββββββ΄ββββββ΄ββββββ΄ββββββ€ β
β β βDir β β β β β β β
β ββββββββββββββββ΄βββββ΄ββββββ΄ββββββ΄ββββββ΄ββββββ΄ββββββ β
β β
β Bit 4 (0x10): Directory flag - Set if entry is a directory β
β β
β Example Entry (hex dump): β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β FF FF 05 00 2A 00 00 00 80 02 00 00 00 0A 64 65 66 61 75 6C β β
β β 74 2E 78 62 65 00 00 00 β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Decoded: β
β - LeftSubTree: 0xFFFF (no left child) β
β - RightSubTree: 0x0005 (right child at offset 0x05 * 4 = 0x14) β
β - StartSector: 0x0000002A (sector 42) β
β - FileSize: 0x00000280 (640 bytes) β
β - Attributes: 0x00 (regular file) β
β - NameLength: 0x0A (10 characters) β
β - FileName: "default.xbe" β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The VolumeDescriptor class handles the XISO header validation:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β VOLUME DESCRIPTOR STRUCTURE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Location: Sector 32 (offset 0x10000 from partition start) β
β Size: 2048 bytes (one sector) β
β β
β Layout: β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Offset Size Field Value β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β 0x000 20 bytes Magic ID 1 "MICROSOFT*XBOX*MEDIA" β β
β β 0x014 4 bytes RootDirSector Sector of root directory table β β
β β 0x018 4 bytes RootDirSize Size of root directory in bytes β β
β β ... ... ... ... β β
β β 0x7EC 20 bytes Magic ID 2 "MICROSOFT*XBOX*MEDIA" (verify) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Validation Strategy (tried in order): β
β β
β ββββββββββββ ββββββββββββ ββββββββββββ β
β β Try 1 ββββ β Try 2 ββββ β Try 3 β β
β β Sector β β β Game β β β Sector β β
β β 32 @ 0 β β β Partitionβ β β 0 @ 0 β β
β β (Standardβ β β Offset β β β (Rebuilt β β
β β XISO) β β β (Redump) β β β XISO) β β
β ββββββββββββ β ββββββββββββ β ββββββββββββ β
β β β β β β β
β βΌ β βΌ β βΌ β
β Success? β Success? β Success? β
β ββββββββ β ββββββββ β ββββββββ β
β β Yes ββββββ β Yes ββββββ β Yes β β
β ββββ¬ββββ ββββ¬ββββ ββββ¬ββββ β
β β β β β
β βΌ βΌ βΌ β
β Return Volume Return Volume Return Volume β
β Descriptor Descriptor Descriptor β
β β
β If all fail β Throw InvalidDataException β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β COMPLETE XISO CONVERSION FLOW β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β ββββββββββββββββ β
β β Start with β β
β β Source ISO β β
β ββββββββ¬ββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββ β
β β Detect ISO Type β β
β β ββ Check file size against β β
β β β known Redump lengths β β
β β ββ If match β Redump ISO β β
β β β (set inputOffset) β β
β β ββ No match β Standard XISO β β
β β (inputOffset = 0) β β
β ββββββββββ¬βββββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββ β
β β Xdvdfs.GetXisoRanges() βββββββββββββββββββββββββββββββ β
β β β β β
β β 1. Read Volume Descriptor β β β
β β ββ Validate magic IDs β β β
β β β β β
β β 2. Add Header Sectors β β β
β β ββ Sectors at 0x10000 β β β
β β β β β
β β 3. Traverse File Tree βββββββββΊβ β β
β β (iterative DFS) β β β
β β ββ For each directory: β β β
β β β Add dir table sectors β β β
β β β Recurse into subdirs β β β
β β ββ For each file: β β β
β β Add file data sectors β β β
β β β β β
β β 4. Sort & Consolidate β β β
β β ββ Create ranges from β β β
β β contiguous sectors β β β
β β β β β
β ββββββββββ¬βββββββββββββββββββββββββ β β
β β β β
β β Returns List<(Start, End)> β β
β βΌ β β
β βββββββββββββββββββββββββββββββββββ β β
β β Copy Valid Ranges to Output β β β
β β β β β
β β For each range (start, end): β β β
β β βββββββββββββββββββββββββββββ β β β
β β β Seek to start * 2048 β β β β
β β β Read (end-start+1)*2048 β β β β
β β β bytes β β β β
β β β Write to output file β β β β
β β β Report progress β β β β
β β βββββββββββββββββββββββββββββ β β β
β β β β β
β ββββββββββ¬βββββββββββββββββββββββββ β β
β β β β
β βΌ β β
β βββββββββββββββββββββββββββββββββββ β β
β β Optional: Verify Output β β β
β β ββ Run XDVDFS validation on βββββββββββββββββββββββββββββββ β
β β the newly created XISO β (Recursive call for verification) β
β βββββββββββββββββββββββββββββββββββ β
β β
β ββββββββββββββββ β
β β Optimized β β
β β XISO File β β
β ββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The XisoWriter class uses XDVDFS to perform the actual ISO conversion:
// From XisoWriter.cs - how GetXisoRanges is used:
await using FileStream isoFs = new(sourcePath, FileMode.Open, FileAccess.Read, FileShare.Read);
// Generate valid ranges based on XDVDFS traversal
List<(uint Start, uint End)> validRanges;
try
{
validRanges = Xdvdfs.GetXisoRanges(isoFs, inputOffset, true, skipSystemUpdate);
}
catch
{
_logger.LogMessage($"[ERROR] '{Path.GetFileName(sourcePath)}' is not a valid Xbox ISO image.");
return FileProcessingStatus.Failed;
}
// Use ranges to copy only valid data
await using FileStream xisoFs = new(destPath, FileMode.Create, FileAccess.Write, FileShare.None);
var buffer = new byte[64 * Utils.SectorSize];
foreach (var (startSector, endSector) in validRanges)
{
var startPos = startSector * Utils.SectorSize;
var length = (endSector - startSector + 1) * Utils.SectorSize;
isoFs.Seek(inputOffset + startPos, SeekOrigin.Begin);
Utils.FillBuffer(isoFs, xisoFs, -1, length, buffer);
}The XDVDFS.cs class is a sophisticated implementation of Xbox filesystem traversal that:
- Parses binary structures - Decodes the proprietary XDVDFS format
- Traverses efficiently - Uses iterative DFS to avoid stack overflow
- Validates thoroughly - Detects cycles and validates magic signatures
- Optimizes storage - Consolidates sectors into minimal copy ranges
- Supports variants - Handles standard XISOs, Redump ISOs, and rebuilt images
This class enables the application to strip away padding and system update data, producing optimized XISO files that are smaller but fully functional for emulation and preservation purposes.