Skip to content

PDB format findings from hardware-verified CDJ export writer (Swift + Rust) #46

@alevizio

Description

@alevizio

Hi James! I've been building DJUSB, a cross-platform app (macOS/Windows/Linux) that exports USB drives for Pioneer CDJ hardware. Your Kaitai Struct specs in crate-digger were the foundation for the PDB writer — thank you for all that reverse-engineering work.

During development and testing against a real CDJ, I discovered several format details that might be useful to document or add to the specs. Sharing them here in case they help others:

Page Header Packed Bitfield (offset 0x18–0x1A)

The 3 bytes at offset 0x18 in the page header encode a packed 24-bit value:

  • Low 13 bits = num_row_offsets (number of row offset entries at end of page)
  • High 11 bits = num_rows (actual row count)

This is different from treating them as separate fields. Getting this wrong causes the CDJ to silently skip the page.

INDEX (Strange) Page Requirements

For INDEX pages (page_flags = 0x64), these exact values are required or the CDJ ignores the table entirely:

  • free_size = 0
  • u5 = 0x1FFF
  • nrl (num_rows_large) = 0x1FFF
  • u6 = 0x03EC (1004)

Row Subtype Values

Track, artist, and album rows have mandatory subtype values. Wrong subtypes make rows invisible to the CDJ:

  • Track: 0x0024
  • Artist: 0x0060
  • Album: 0x0080

DATA Page Bytes 0x19–0x1A Must Be Zero

CDJ firmware explicitly checks bytes at offset 0x19–0x1A in DATA pages. Non-zero values cause the page to be rejected. (This is within the packed bitfield region mentioned above.)

Header Offset 0x10

The unknown field at file header offset 0x10 must be 5 (verified against reference rekordbox export). Other values may cause rejection on some firmware versions.

20 Table Types Required

The directory at offset 0x1C must contain entries for all 20 table types (0–19), even if most are empty. Missing tables = CDJ ignores the entire file.

Track Row String Indices

Track rows have 21 string index slots starting at offset 0x5E. The mapping:

  • 10 = date_added, 11 = release_date, 12 = mix_name
  • 14 = analyze_path, 15 = analyze_date, 16 = comment
  • 17 = title, 19 = filename, 20 = file_path

ANLZ Tag Ordering

ANLZ files (.DAT/.EXT) require the PPTH tag to be first after the PMAI header. Audio file path is encoded in UTF-16BE with null terminator. Moving PPTH to any other position causes the CDJ to not associate the analysis with the track.


All of this was verified against a CDJ with reference rekordbox PDB exports. The project has both a Swift PDB writer and a Rust PDB writer with 172+ tests if any of this is useful as reference implementations.

Happy to contribute PRs to the Kaitai specs or documentation if any of these findings would be welcome. Thanks again for making crate-digger — it made this project possible!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions