Reading the disc image in place
2026-10-23, tenth session. It ran beside the sessions on track props and on the MMX and 3D-card builds.

The browser viewer opened with ?image=http://127.0.0.1:5183/POD2_0.CUE&file=ARC.BV4. The status bar reads "283 of 283 files" from the image, with the chunks kept in OPFS. © Ubi Soft; shown from our own viewer.
Why
Until now every tool read target/disc/, a folder that GameFrame's discinfo.py extracted from the image. The plan (docs/PLAN.md) asked for two more modules. The first reads CD images directly. The second puts every place the files can come from behind one interface: a folder, an image, or a URL the player gives. A player with only the BIN and CUE can now use the viewer as it is.
A CD image is three layers
The CUE sheet says where each track starts in the BIN. POD's sheet lists two tracks: data in Mode 1, and the music as one audio track after a two-second pregap. The BIN stores raw 2,352-byte sectors. In a Mode 1 sector the 2,048 bytes of data sit at offset 16, behind a sync pattern and a header. In a Mode 2 Form 1 sector they sit at 24, behind an extra subheader. On top of the sectors sits the ISO 9660 volume, which starts at sector 16 with a descriptor and leads to the directory tree.
The new crate, disc-image, reads all three. It does so through one small trait, "read these bytes at this offset", so the same parser works on a file, on memory, or on a source that has to fetch first.
Checked against the extraction
Measured. On the player's image the crate finds exactly what discinfo.py reports: 173,791 data sectors, 65,953 audio sectors, and 781 files in 51 directories. Six of the files are hidden: the AVI videos. The test then reads all 781 files, 354,753,467 bytes, out of the BIN and compares each with the extracted copy. Every byte matches. pod-viewer --image target/cd/POD2_0.CUE list prints the same 289 DATA lines as the folder.
The test also builds a small image of its own in memory, in every layout: a cooked ISO, raw Mode 1, raw Mode 2, a Joliet tree with long names, a CUE sheet with audio after the data, and one file per track. So the code is tested without any game files, which never go into git.
In the browser: fetch only what is read
A web page cannot read a file synchronously, but the parser is synchronous. A chunk cache joins the two. When a read needs bytes the cache does not hold, it fails with a list of the missing chunks. The page fetches those chunks (an HTTP range request, or a slice of a dropped file) and runs the read again. The chunks are 1 MB each and are kept in the browser's private file system (OPFS), or in IndexedDB where OPFS cannot write.
Measured in the browser built into the app, with viewer/serve-image.sh serving the image on 127.0.0.1. The first visit read the 283 non-video files in 152 range requests, in 0.85 seconds. A reload made two requests, the CUE sheet and the BIN's size, and took everything else from OPFS. Dropping the .cue with its .bin onto the page works too:

POD2_0.CUE and POD2_0.BIN dropped together; ?file=DIC_GAM.BMD opened the French text table as soon as it arrived. © Ubi Soft; shown from our own viewer.
The test also showed an old bug. Each new batch of arriving files reset the open file, so a ?file= link opened its file and closed it at once. The viewer now keeps the open file open while the rest arrive.
archive.org and CORS
The image is also on archive.org, and the plan asked whether a page could read it there. It cannot. Measured from a page on 127.0.0.1 with a public-domain item: the download URL redirects to a data node, and neither sends Access-Control-Allow-Origin, so the browser blocks the bytes. The data node does answer range requests, and archive.org's metadata API does allow other origins. Natively nothing stands in the way: pod-viewer --url URL --md5 HEX downloads the image once into target/download/, resumes it if it breaks, and checks the MD5. The viewer holds no URL. Whether to fetch the game at all is the player's decision.
Next
- Read a file only when it is opened, so the browser shows the list before it reads 300 MB.
- Play the CD audio track from the image, now that its sectors are at hand.
- Offer
disc-imageto GameFrame's shared crates, with its synthetic tests.