ACIDCAT . FILE FORMAT REFERENCE

RF64 / BW64 Anatomy

EBU Tech 330664-bit RIFF . .wav
rev 2026.07
magic RF64 .. WAVE
endian little
sizes 64-bit in ds64
sentinel 0xFFFFFFFF

RF64 is RIFF/WAVE grown past 4 GB. A plain WAV stores its sizes in 32-bit fields, which top out at 4 GB. RF64 keeps the exact WAV chunk layout but writes the sentinel 0xFFFFFFFF into the RIFF and data size fields, and adds one mandatory chunk right after the header: ds64, carrying the real sizes as 64-bit integers. A reader that sees 0xFFFFFFFF must go read ds64 instead. BW64 (the newer name, EBU Tech 3306) is the same structure. This matters to a tool: trust the 32-bit field and you compute a 4.29 GB size for every RF64 file, which is exactly the bug acidcat's forensic scan once had until it learned to read ds64. The record drawn below is a real ds64 body. Hover any field to light its bytes; click a field with a + to open its table. Color marks kind (see the key).

file regions

Identical to WAV, with two twists: the magic is RF64 (or BW64) not RIFF, and ds64 must be the first chunk. The 32-bit sizes are placeholders; the truth is in ds64.

RF64 header0x00 . 12 bytes

'RF64' (or 'BW64'), then a u32 size field that holds the sentinel 0xFFFFFFFF, then the form type 'WAVE'. The sentinel is the flag: it says "my real size does not fit here, read ds64".

magic"RF64" (or "BW64") at 0x00, "WAVE" at 0x08
riff sizeu32 at 0x04 = 0xFFFFFFFF (sentinel); the real 64-bit size is in ds64
acidcatsees the sentinel and reads ds64 for the true end, so the trailing/forensic scan runs correctly
ds64 chunk0x0C . 64-bit sizes . drawn below

The mandatory first chunk. It carries the real riff size, the real data size, and the sample count as 64-bit little-endian integers, then a table that overrides the size of any other chunk that also overflowed 32 bits. The body below is 28 bytes: three u64 and a u32 table length of zero (no overrides needed for this file).

layoutriffSize(u64) . dataSize(u64) . sampleCount(u64) . tableLength(u32) . then tableLength override entries
override tableeach entry is a 4cc + u64: the real size of another chunk that also holds the 0xFFFFFFFF sentinel
cross-checkriffSize + 8 should equal the file length; acidcat uses this as the real container end
fmt + data + the reststandard WAV

From here it is ordinary WAV: fmt, data, and any LIST/bext/cue the writer added. The one difference is the data chunk's 32-bit size, which is also 0xFFFFFFFF when the audio exceeds 4 GB; its real size is the dataSize in ds64.

fmtsame as WAV: format tag, channels, sample rate, bits
dataaudio; size field is 0xFFFFFFFF when it overflows, real size from ds64
everything elsewalked exactly as a WAV; RF64 changes only how sizes are read
what acidcat reads

The same as WAV, plus the ds64 override. The important part is defensive: never trust the 0xFFFFFFFF sentinel as a real size.

ds64-aware decodeinspect . carve . repair
readthe 64-bit riff/data sizes and sample count from ds64, and the WAV fmt/data as usual
container end_rf64_end reads ds64's riffSize so carve --trailing and the anomaly scan use the real end
the bugwithout ds64 the sentinel makes the declared end ~4.29 GB, so the trailing/polyglot scan silently never ran -- fixed
repairRF64 sizes live in ds64, so the WAV size-cascade repair reads them there rather than the sentinels