Identify / specification
Title Screen disc fingerprint, version 1 (fp1)
Status: draft 2026-10-07, version 1 (revised 2026-10-07, section 9). Part of the Title Screen contract; implementable from this text alone. Frozen at the first public archive; any change is fp2.
1. Purpose and limits
A full canonical hash of a disc image (CANONICAL-FORM.md, section 5) reads the whole image: minutes for an 8 GB Wii disc, longer for a 40 GB PlayStation 3 one. fp1 reads 64 sectors (128 KB) plus the image size and answers in milliseconds, from any container, so a library of discs can be identified at the speed of a directory listing.
fp1 is a lookup key, not an identity. It returns candidates. When one dump carries the value, a tool reports that dump with basis = fp1; when several do, the tool reports them all and the full hash decides. A consumer that needs certainty asks for the full hash. fp1 also identifies copies that the canonical form cannot (section 6), which is the second reason it exists.
2. The logical image
fp1 is computed over a logical image: a sequence of N sectors of 2048 bytes of user data, numbered from 0. Every container and every physical sector size maps to it the same way, which is what makes the value container independent.
| source | sector i of the logical image |
|---|---|
.iso, .wud, or any 2048-byte-per-sector image (also a decoded RVZ, WIA, GCZ, CSO, ZSO, WUX, CHD) | bytes [2048 i, 2048 i + 2048) of the image; N = floor(bytes / 2048) |
a 2352-byte-per-sector track (.bin, a reconstructed CHD track) | the user data of raw sector i of the volume track: bytes 24 to 2071 of the sector when the mode byte (offset 15) is 2; bytes 16 to 2063 otherwise (mode 1, and the mode-0 sectors that pad some tracks); N = the track's sector count |
a 2048-byte-per-sector track (MODE1/2048, as some CHDs and .gdi sets store data tracks) | as an .iso: bytes [2048 i, 2048 i + 2048) of the track |
a 2336-byte-per-sector track (MODE2/2336) | bytes 8 to 2055 of each sector |
| a 2448-byte-per-sector track | drop the last 96 bytes of each sector, then as 2352 |
The volume track of a multi-track disc is its first data track: track 1 on nearly every CD; on a disc whose track 1 is audio (some PC Engine CD titles open with a warning track), the first data track after it. For a GD-ROM it is the first data track of the high-density area: track 3 (LBA 45000 onward; the track after REM HIGH-DENSITY AREA in Redump's cue, the third line of a .gdi). Audio tracks and later data tracks are not part of the logical image. A disc with no data track has no fp1.
Platform-specific bases:
- Xbox and Xbox 360: the logical image is the XDVDFS game partition, not the whole disc. Find it by its magic: the 20 bytes
MICROSOFT*XBOX*MEDIAat the start of sector 32 of the partition. Test the partition base offsets 0 (an extracted XISO), 0x18300000 (Xbox, XGD1), 0xFD90000 (XGD2) and 0x2080000 (XGD3) in that order; the first base whose sector 32 carries the magic is the partition start, andNcounts from it to the end of the file. A Redump image and an XISO of the same game then give the samefp1. - GameCube and Wii: the whole image as held. A Dolphin-scrubbed image (unused areas zeroed) is a different image and gives a different value; the archive records the scrubbed value as its own row when a scan has seen it, with
status = scan-only. - PlayStation 3: the whole image as held. Redump's image is encrypted; most copies in the wild are decrypted. The two states give different values and the archive records both when known. The header identity (section 5) is readable in both states, because the first region of a PS3 disc is in clear.
- PlayStation, PlayStation 2 on CD, Saturn, Sega CD, PC Engine CD, 3DO, CD-i, Neo Geo CD, PC-FX, CD32, Jaguar CD: the volume track as above. A 2048-byte
.isoof such a disc maps to the same logical image as its 2352-byte.bin, which is the point: the.isocannot be identified by the canonical form, and is byfp1. - PlayStation 2 on DVD, PSP, Wii U: the whole image.
3. Sample positions
Sixty-four sector numbers, in this order:
p[0] = 16 (the ISO 9660 primary volume descriptor)
p[k + 1] = floor((N - 1) * k / 62) for k = 0 .. 62
So p[1] = 0 and p[63] = N - 1, with the rest spread evenly. Positions may repeat when N is small; a repeated sector is hashed again each time. A position >= N (only p[0] when N <= 16) contributes 2048 zero bytes. Integer arithmetic only; floor is integer division of non-negative values.
4. The value
fp1 = SHA-1( uint64_be(N) || S[p[0]] || S[p[1]] || ... || S[p[63]] )
uint64_be(N) is N as an 8-byte big-endian unsigned integer; S[i] is sector i of the logical image (2048 bytes); || is byte concatenation. Written as 40 lowercase hexadecimal characters. The platform is not part of the value; a lookup may filter by platform when the caller knows it.
5. The header identity (companion, not part of the value)
An implementation also reads what the disc says about itself and reports it beside fp1, so a human sees "SLUS-00594 v1.1" and a tool can narrow candidates. These fields are informative; the archive stores them on the dump (header_title, header_version, serial), and a submission carries them. Where to read them, all within the first sectors or the root directory of the volume:
| platform | where | fields |
|---|---|---|
| PlayStation, PlayStation 2 | SYSTEM.CNF in the root directory: BOOT = cdrom:\SLUS_005.94;1 or BOOT2 = cdrom0:\SLUS_200.02;1, VER = 1.1 | serial (SLUS-00594: the file name with _ as - and the dot removed), version |
| PSP | PSP_GAME/PARAM.SFO | DISC_ID, DISC_VERSION, TITLE |
| PlayStation 3 | PS3_GAME/PARAM.SFO | TITLE_ID, VERSION, TITLE |
| Saturn | the IP.BIN block, sector 0 of the volume track | hardware id (bytes 0 to 15, SEGA SEGASATURN ), maker id (16 to 31), product number (32 to 41), version (42 to 47), release date (48 to 55), area symbols (64 to 73), title (96 to 207) |
| Sega CD, Mega-CD | the system header, sector 0 of the volume track | hardware id (bytes 0 to 13, SEGADISCSYSTEM), domestic title (288 to 335), overseas title (336 to 383), product number and version (384 to 397, GM T-xxxxx-xx), area symbols (496 to 498) |
| Dreamcast, Naomi GD, Triforce GD | the IP.BIN block, sector 0 of the volume track | hardware id (bytes 0 to 15, SEGA SEGAKATANA ), maker id (16 to 31), area symbols (48 to 55), product number (64 to 73), version (74 to 79), release date (80 to 87), title (128 to 255) |
| GameCube, Wii | the first 96 bytes of the image | 6-character game id (bytes 0 to 5: system, 2-letter game, region, 2-letter maker), disc number (byte 6, counted from 0: reported plus one), version (byte 7), internal name (bytes 32 to 95) |
| Wii U | meta/meta.xml of the game partition | product_code, title_id, title_version |
| Xbox | default.xbe in the root of the game partition: the certificate | title id, version, title name |
| Xbox 360 | default.xex in the root of the game partition: the execution id | title id, version, media id |
| PC Engine CD, 3DO, CD-i, Neo Geo CD, PC-FX, CD32, Jaguar CD | no publisher identity in a fixed place | none; the ISO 9660 volume id (sector 16, bytes 40 to 71) is reported as volume_id |
6. What fp1 identifies that the canonical form cannot
- A CD title held as a 2048-byte
.iso, a CHD with 2048-byte data tracks, a.cdi, an.img,.mdfor.nrg(after the implementation locates the volume track in those formats; version 1 of the reference implementation handles.iso,.bin/.cue,.gdiand CHD and reports the others as unsupported). - An Xbox or Xbox 360 game reduced to its XISO partition.
- A decrypted PlayStation 3 image, once a scan has recorded its value.
- A scrubbed GameCube or Wii image, once a scan has recorded its value.
And what it does not: a fan translation or hack that kept the disc layout usually changes bytes in some sampled sectors and so gets a new value, but this is not guaranteed; when two dumps share one fp1, the lookup says so and the full hash decides.
7. Test vectors
N = 1000, every sector all zero bytes
positions: 16, 0, 16, 32, 48, 64, 80, 96, 112, 128, 145, 161, 177, 193, 209, 225, 241,
257, 273, 290, 306, 322, 338, 354, 370, 386, 402, 418, 435, 451, 467, 483,
499, 515, 531, 547, 563, 580, 596, 612, 628, 644, 660, 676, 692, 708, 725,
741, 757, 773, 789, 805, 821, 837, 853, 870, 886, 902, 918, 934, 950, 966,
982, 999
fp1 = 15a79a32ba9600c3f55c4f1e5300958b4ce2d525
N = 1000, sector i has byte j = (i + j) mod 256
fp1 = 2b947dd73db40870b29f7140c23c5bf63e3d06cc
N = 10, the same pattern
positions: 16, 0,0,0,0,0,0,0, 1,1,1,1,1,1,1, 2,2,2,2,2,2,2, 3,3,3,3,3,3,3, 4,4,4,4,4,4,4,
5,5,5,5,5,5,5, 6,6,6,6,6,6,6, 7,7,7,7,7,7,7, 8,8,8,8,8,8, 9
(position 16 is beyond N and contributes 2048 zero bytes)
fp1 = ac6aab86ba516d317308b0db93ecd375c0420d48
Reference computation, complete:
import hashlib, struct
def fp1(read_sector, n): # read_sector(i) -> 2048 bytes
h = hashlib.sha1(struct.pack('>Q', n))
for p in [16] + [((n - 1) * k) // 62 for k in range(63)]:
h.update(read_sector(p) if p < n else bytes(2048))
return h.hexdigest()
8. Lookup semantics
lookup?fp1=<hex> returns every dump whose file_hash rows carry that value (any state: canonical, scrubbed, decrypted, XISO), each with its releases and work, basis = fp1, and the row's status. One candidate is an identification by fingerprint; several are a request for the full hash. A dump may carry several fp1 rows (encrypted and decrypted PS3, canonical and scrubbed Wii) and the same value never belongs to two dumps unless the sampled sectors really are identical.
9. Revisions to the draft
Clarifications from the first scans of real copies (2026-10-07). No value changes for a disc a conforming implementation could already read.
- Section 2: the mode byte rule covers mode-0 sectors; 2048-byte tracks are listed; the volume track is the first data track (PC Engine CD discs whose track 1 is audio), and is found from Redump's cue for a GD-ROM.
- Section 5: the Saturn, Sega CD and Dreamcast header offsets were one row with the Dreamcast product offsets given for Saturn; each now has its own row with the real offsets. The GC/Wii disc byte counts from 0.