ACIDCAT . FILE FORMAT REFERENCE

PSF Anatomy

Portable Sound Formateight consoles . one container
rev 2026.09
magic PSF + version
endian little
container own
program zlib, checksummed

A PSF file is a console's sound program and the memory it runs in, compressed, with a checksum and a tag block. Neill Corlett made it in 2002 for PlayStation dumps, and then everyone who needed to ship the same thing for a different chip borrowed it: one version byte names the machine, and nothing else about the container changes. So a .psf, a .minigsf, a .ssf and a .usf are the same file with a different byte 3.

a whole file, 221 bytes

This is a complete Game Boy Advance mini: sixteen bytes of header, a 22-byte compressed program, and a tag block. Nothing is left out. The program inflates to fourteen bytes, and twelve of those are a header, which is the point of a mini -- the tune lives in a library file the tag names, and this file is just the song number.

▸the version bytewhich machine

Three letters and then the only byte that differs between platforms.

0x01PSF1, PlayStation
0x02PSF2, PlayStation 2
0x11SSF, Sega Saturn
0x12DSF, Sega Dreamcast
0x21USF, Nintendo 64
0x22GSF, Game Boy Advance
0x23SNSF, Super Nintendo
0x242SF, Nintendo DS
0x25NCSF, Nintendo DS (Nitro Composer)
0x41QSF, Capcom QSound arcade boards

The letters alone are not enough to identify a file: PSF opens ordinary text too. A reader checks the version byte with them.

▸the program, and its checksumzlib . CRC32

After the header and the reserved area comes the program, zlib-compressed, of the size the header states. What the program IS depends on the machine: a PlayStation executable, an N64 ROM image, a GBA ROM. The container does not say, and a reader that does not know the platform's program layout should not guess at it.

What the container does say is whether the program is intact. The header carries a CRC32 of the compressed bytes. Most formats give a reader nothing to check against; this one hands over a checksum, so "the program is undamaged" is a fact rather than a hope.

▸minis and libraries_lib

A soundtrack shares one sound engine and one sample set across every track. Shipping all of it in every file would multiply a game's music by its track count, so the format splits it: the shared part goes in a library (.psflib, .gsflib, .ssflib...) and each track is a mini whose program is a few bytes patched over the library's, and whose _lib tag names the library.

librarythe engine and the samples; carries no tags, because it is data
minithe song number, and the tags; loads on top of the library
_lib, _lib2 ...a mini may name several, loaded in order

A mini separated from its library is a song number with nothing to play. The library sits beside it by convention, and a reader can check.

▸the GBA program headerGSF only . 12 bytes

Once the program is inflated, a GSF program opens with three little-endian words and then exactly that much ROM:

A library's ROM is the whole game's sound side, up to sixteen megabytes. A mini's is one or two bytes at an offset inside the library's image: the song number, written where the engine reads it. This header is decoded because it is documented and holds on every file measured; the other machines' programs are reported as a size that inflates and checksums, and nothing more is claimed.

▸the DS program and its SAVE block2SF only . 8 bytes + a reserved area

A 2SF program is the GBA layout without the entry point: two little-endian words, offset and length, then that many bytes of DS ROM. A library holds the whole cartridge image, up to 64 MB inflated; a mini holds two bytes, the song number, at an offset inside it.

2SF is the one machine that uses the header's reserved area. When present it is a SAVE block: the four letters, a little-endian zlib size, the CRC32 of the zlib stream, then the stream. Inflated, it has the same shape as the program: offset, length, data. It is a patch into the emulator's save state; a mini's is four bytes, and a mini may carry a SAVE patch and a tag and no program at all, the library holding everything else.

programu32 offset, u32 length, ROM
reserved"SAVE", u32 zlib size, u32 CRC32, zlib → u32 offset, u32 length, data
▸the tag block[TAG] . key=value

Optional, at the end, marked by five literal bytes. Then one key=value per line, LF-separated, UTF-8. A key that repeats is a value continued on the next line.

title, artist, gamewhat a player shows
year, genre, copyrightcatalogue fields
length, fadehow long to play, then how long to fade, as m:ss or seconds
volumea gain to apply
psfby, gsfby, ssfby ...who made the rip, named per platform
_libthe library; the one tag that changes what plays rather than what is shown
▸byte orderlittle-endian

The three header words are little-endian, as is the GBA header inside a GSF program. Other machines' programs follow their own machine; the container does not care what is inside the zlib.