XDVDFS is the read-only filesystem Microsoft shipped on both original Xbox and Xbox 360 game discs. It is deliberately minimal: no allocation bitmap, no fragmentation, no journal. Every file is a single contiguous run of sectors, because the disc is written once by a mastering tool and never modified.
Its one interesting design decision is that a directory is not a list. It is a balanced binary search tree serialised into a contiguous byte range, so resolving one name inside a directory of a thousand entries costs ten comparisons instead of a thousand. On a 2001-era DVD drive with a seek time measured in tens of milliseconds, that mattered.
All integers are little-endian.
The unit of addressing is a 2048-byte sector. Sector numbers in the filesystem are relative to the start of the game partition, not to the start of the image file. On a full retail dump the game partition begins at a fixed byte offset, past the DVD-Video compatibility area:
| Disc format | Partition base | Notes |
|---|---|---|
| Raw / extracted | 0x00000000 |
Filesystem-only image, no video area |
| XGD1 | 0x18300000 |
Original Xbox |
| XGD2 | 0x0FD90000 |
Xbox 360, 2005–2011 |
| XGD3 | 0x02080000 |
Xbox 360, 2011 onward |
The volume descriptor occupies exactly one sector at sector 32 relative to
the partition base — that is, base + 0x10000.
| Offset | Size | Field |
|---|---|---|
0x000 |
20 | Magic MICROSOFT*XBOX*MEDIA |
0x014 |
4 | Root directory table start sector |
0x018 |
4 | Root directory table size, in bytes |
0x01C |
8 | Volume creation time, Windows FILETIME |
0x024 |
1992 | Unused, filler |
0x7EC |
20 | Magic MICROSOFT*XBOX*MEDIA again |
The magic appears twice, at the first and last 20 bytes of the sector. Checking both is the cheap way to reject a sector that happens to start with the right bytes — probe both before accepting a partition base.
FILETIME counts 100-nanosecond ticks since 1601-01-01 UTC. In practice it
records when the mastering tool built the image, which is a useful (and rarely
tampered with) date stamp for a release.
A directory's contents live in a contiguous byte range starting at its
start sector, size bytes long. size is always a multiple of 2048; the
tail of the final sector is padded with 0xFF.
Entries are 4-byte aligned. Child pointers are expressed in 4-byte units
measured from the start of the table, so an offset value of n means byte
n * 4. The root of the tree is always at offset 0.
| Offset | Size | Field |
|---|---|---|
0x00 |
2 | Left subtree offset, in 4-byte units (0xFFFF = none) |
0x02 |
2 | Right subtree offset, in 4-byte units (0xFFFF = none) |
0x04 |
4 | Start sector of the file, or of the subdirectory's own table |
0x08 |
4 | Size in bytes |
0x0C |
1 | Attributes |
0x0D |
1 | Filename length, in bytes |
0x0E |
n | Filename, ASCII, not NUL-terminated |
| Bit | Meaning |
|---|---|
0x01 |
Read-only |
0x02 |
Hidden |
0x04 |
System |
0x10 |
Directory |
0x20 |
Archive |
0x80 |
Normal |
Retail discs set 0x20 (archive) on nearly every file and 0x10 on
directories; the other bits are rare and worth noting when they appear.
Two details will bite a naive implementation:
Padding looks like an entry. The 0xFF padding at the end of a table
decodes as left = right = 0xFFFF, start sector = 0xFFFFFFFF. Treat that
combination as a terminator rather than as a real entry.
Offset 0 is ambiguous. It is both the valid offset of the tree root and the
value some mastering tools write to mean "no child". Since the root is reached
before any child pointer is followed, treating a child offset of 0 as "none"
is safe, and it prevents an infinite loop back to the root.
A defensive walker should also keep a set of visited offsets, bound the table size it is willing to allocate, and refuse an entry whose filename would run past the end of the table. Corrupt or deliberately malformed images exist.
Entries are sorted for a case-insensitive comparison that orders by name
length first and byte value second. This is not the same as an ASCII sort, so
do not assume an in-order traversal will match sort output. Extraction tools
should sort by path themselves after the walk, which is what tools/xdvdfs.py
does.
See tools/xdvdfs.py for a complete reader:
autodetection of the partition base, volume descriptor parsing, an iterative
tree walk with the guards described above, CSV manifest output, and extraction.