Takes a disc image file of Sly 2 or Sly 3 (including some of the demos), and lets you listen to (and download, raw or rendered to wav) all of its streamed music and ambient tracks.
Update 06/04/25: When a disc is loaded, you can make a .zip file containing all tracks which are currently listed on the select box (based on the filters applied with checkboxes)
Because this operation takes a lot of memory and CPU, there is a maximum limit on how many tracks you can put in a single zip file (and the page will tell you if there are too many)
If you have CPU to spare, you can bypass this limit by writing and running the phrase option_noZipLimit=true in your browser's console (see developer tools on MDN)
Update 18/01/26: I restructured the audio processing so that it's more complex but faster.
I added a playing bar so you can jump at any point in the track.
Exported wav files now contain embedded loop points readable by some audio editors and DAWs (notably Wavosaur and Reaper, but not Audacity).
Update 22/01/26: Remade the page entirely to use ES6 modules. No change in functionality, but hopefully friendlier to archival sites that modify javascript files.
I will soon start working on a separate page that lets you insert your own music and SFX tracks in a Sly 2/3 disc, within the space limiations of existing audio tracks.
No because you're not uploading anything anywhere. This page does all the work locally, in your browser (preferably Firefox), without relying on a server after it's loaded.
It's a bit like vgmstream's web player, but in pure javascript.
Update 06/04/25: However, it will take a lot of time (and memory) to make a single .zip file that contains every audio track on the disc, if you choose to do so.
Because this page is made for a specific format used in Sly 2 and 3, and I had to manually find and classify each track. These games in particular have their entire file structure hidden, so searching for them is not as easy as opening a folder and reading a name.
Yes if you put this file in the same directory and rename it ".kpv.txth" (dots are important).
Read more about vgmstream's TXTH files on their github
Ps2DiscFile.discIdPromise).
knownIDs at the end of slyDiscMusic_data.js),SlyAudioContext.playTrack)WavSink.render).Yes (see slyDiscMusic_ioV2.js, function loadFileDataPromise)
KpvToPsx.decodeFirstSector in streamBlockNodesV2.js):
| Offset | Format | Value | Notes |
|---|---|---|---|
| 0x000 | ascii string | " KPV" (0x20 0x4B 0x50 0x56) | |
| 0x004 | Uint32 LE | length and loopEnd: number of used bytes per channel |
each channel occupies an integer number of 0x800 sectors, so this value should be accordingly rounded up to get the actual file size |
| 0x008 | Uint32 LE | unknown (sector size?): always 0x800 | |
| 0x00C | Uint32 LE | interleave*2: double of channel interleave |
Usually 0x2000, meaning that channels alternate every 0x1000 bytes. |
| 0x010 | Uint32 LE | sampleRate | |
| 0x014 | Uint32 LE | channels | |
| 0x018 | byte⁠[channels]⁠[4] | channel mapping | see below |
| 0x7FC | Uint32 LE | loopStart: byte offset of loop start point in each channel |
this single value is all the way to the end of the disc sector, separate from the rest of the header |
| Byte 0 | Byte 1 | Uint16 LE |
|---|---|---|
| 0x7F | Layer number | 0x0000 for Mono layers,0x005A and 0x010E to couple 2 channels into a Stereo layer |
PsxToPcm.processLine in streamBlockNodesV2.js, based on vgmstream implementation)| Byte 0 | Byte 1 | Bytes 2...15 |
|---|---|---|
Low Nibble: shiftHigh Nibble: coef |
Flags (unused) |
28 signed nibbles (low before high): minisamplesnew sample = minisample<<(12-shift)+ prev[0]*coefTable[0][ coef]+ prev[1]*coefTable[1][ coef] |
| where "coefTable" is the same as the one given in vgmstream,"prev[n]" are the (n+1)th past output samples that you must keep track of when decoding. | ||
0xC0 00 00 00 ...).
It looks like Sly 2/3 associate every room to a reasonably small section of the disc, so that elements of the room can be loaded relatively quickly.
As a result, if separate indoor rooms share the same music or SFX, that music track needs to be duplicated, so that it exists near the data of both rooms.
Sly 3 pushed this idea to an unfeasible length:
In every room where the Guru can enter, he can transform or ride enemies (both actions are accompanied by their own music).
And in almost every room, Bentley can activate the Grapple Cam, which also has its own music.
As a result, these 3 music tracks are duplicated over and over and over and over and over and over and over again.
Respectively: 31 times (Guru Transforms), 9 times (Guru Rides), and 38 times (Grapple Cam).
The combined length of these tracks on disc, without loops, is probably longer than the time I have heard them in the game, especially "Guru Transforms".
And that's why these 3 get the special derogatory moniker of "frequent duplicates".
It looks like every file in Sly 2/3 is stored in groups of 32 sectors (treating the disc as if it were structured in "macrosectors" of 0x10000 bytes each).
When a file ends before completely filling a "macrosector", the remaining space is filled by repeating the beginning of the same file (instead of being left blank).
In some demos, that space may be filled with the beginning of a different file, so there's a slyght chance this leftover data may be of interest to passionate Sly dataminers.
Without doing any analysis myself, I'm putting up the option to download this leftover data for anyone else to use or ignore.