TITLESCREEN

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.

sourcesector 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 trackdrop 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:

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:

platformwherefields
PlayStation, PlayStation 2SYSTEM.CNF in the root directory: BOOT = cdrom:\SLUS_005.94;1 or BOOT2 = cdrom0:\SLUS_200.02;1, VER = 1.1serial (SLUS-00594: the file name with _ as - and the dot removed), version
PSPPSP_GAME/PARAM.SFODISC_ID, DISC_VERSION, TITLE
PlayStation 3PS3_GAME/PARAM.SFOTITLE_ID, VERSION, TITLE
Saturnthe IP.BIN block, sector 0 of the volume trackhardware 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-CDthe system header, sector 0 of the volume trackhardware 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 GDthe IP.BIN block, sector 0 of the volume trackhardware 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, Wiithe first 96 bytes of the image6-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 Umeta/meta.xml of the game partitionproduct_code, title_id, title_version
Xboxdefault.xbe in the root of the game partition: the certificatetitle id, version, title name
Xbox 360default.xex in the root of the game partition: the execution idtitle id, version, media id
PC Engine CD, 3DO, CD-i, Neo Geo CD, PC-FX, CD32, Jaguar CDno publisher identity in a fixed placenone; 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

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.