Denon Reverse Engineering Help

Engine

This is the main DJ application that the user interacts with. This does all the mixing, cloud file loading, and almost everything else the user sees.

Application Data

Path: /usr/Engine/Engine

Available in firmware for

Hardware

Hardware ID

Available

Mixstream Pro

NH08

✅

Mixstream Pro GO

NH10

✅

Mixstream Pro +

NH08S

✅

Prime 2

JC16

✅

Prime 4

JC11

✅

Prime 4 Plus

JC11S

✅

Prime Go

JP11

✅

Prime Go Plus

JP11S

✅

SC5000

JP07

✅

SC5000M (No Product Page)

JP08

✅

SC6000

JP13

✅

SC6000M

JP14

✅

SC Live 2

JP20

✅

SC Live 4

JP21

✅

Binary Overview

Measured against /usr/Engine/Engine from the SC6000 5.0.4 firmware:

Property

Value

Format

ELF 32-bit, ARM:LE:32:v8

Size

~44 MB (.text alone is ~30 MB)

Image base

0x00010000

.text

0016d840 - 01d7a667

Real functions

~46,500 (per .ARM.exidx)

Stripped

Yes, but RTTI, .ARM.exidx and assertion strings survive

Hardening

Stack canaries, _FORTIFY_SOURCE

Debug link

.gnu_debuglink present: an unstripped build exists upstream

Internal codename: planck

Assertion strings retain full build paths, which exposes the source tree layout and confirms the air.planck.* logging categories seen in the runtime output below:

/usr/src/debug/planck/0.0.0+git/App/src/Planck/gui/Views/BeatGridEditView/BeatGridEditViewController.cpp:253 /usr/src/debug/planck/0.0.0+git/Libraries/private/Planck/Librarian/src/Database/... /usr/src/debug/planck/0.0.0+git/Libraries/Streaming/Providers/Eaas/src/GRPCWebAccessor.cpp:473

The two top-level trees are App/src/ (Planck, PlanckEngine) and Libraries/ (split into private/, shared/ and Streaming/).

Third-party libraries

All dynamically linked, so versions can be read from the runtime's LD_LIBRARY_PATH:

Library

Use

Qt (QtQuick/QML, Qt Network)

Entire UI and event system

gRPC (libgrpc.so.37)

Device-to-device and cloud RPC

Protocol Buffers

Wire format for all RPC

OpenSSL

Digests, RSA, EVP

CryptoPP

Additional hashing, bundled with Widevine

Widevine CDM

Streaming DRM (wvcdm::, video_widevine::)

TagLib

Audio file metadata parsing

zplane Elastique

Time-stretch / pitch-shift

Boost

Assorted utilities, incl. the UUID SHA-1 in Engine Library

libudev

Hotplug detection for USB media

spdlog

Logging

Crashpad

Crash reporting (.note.crashpad.info)

Embedded QML

The Qt resource system stores QML as plaintext in .rodata: component source, property bindings, and JavaScript are all directly readable with strings. These call into a Planck bridge object exposed to the QML engine, for example:

Planck.getProperty("/Engine/Sync/Network/SyncType") Planck.engineConnect.numberOfMixerChannels Planck.networkInformation() Planck.isRadxa()

The /Some/Slash/Path strings are keys into a global state map (StateMap), which is also how remote devices' properties are addressed: /RemoteState/%1/RemoteProperties/....

Network services

Engine runs gRPC servers as well as clients, exposing enginelibrary.v1, enginesync.v1, networktrust.v1 and remotehostscreen.v1. Discovery is a custom UDP scheme (airNetworkDiscoverer/airNetworkExchange) rather than mDNS. See Engine Networking for the full method inventory.

Reverse engineering notes

Ghidra's stock auto-analysis finds only about half the functions in this binary. Engine Ghidra Analysis documents how to recover the rest from .ARM.exidx and how to name them from RTTI vtables and assertion strings.

Command-line arguments

Engine parses its arguments with QCommandLineParser, and calls setSingleDashWordOptionMode(ParseAsLongOptions), so long names take a single dash — -skipFirmwareUpdate, not just --skipFirmwareUpdate. Both forms work. This is why the launch script in /usr/Engine/Scripts/engine can write -d0 -loggerOptions "...".

The table below is the complete set for 5.0.4, recovered from the registration function at 00bb746c, which constructs QCommandLineOption and calls addOption exactly 44 times. All 44 names were recovered, and there is no second registration site, so nothing is missing. --version also works, via addVersionOption().

Being registered does not mean being implemented. The Read column records whether the name is referenced anywhere other than its own addOption call — i.e. whether setting it could do anything at all. Eleven of the 44 are never read, so they are inert; see Options that are never read. no in that column is not a claim about what an option would do, only that nothing looks at it.

Options with an empty Value column are flags. Value names and descriptions are the binary's own.

Option

Value

Type

Read

Description

aaa

flag

C++, QML

Enables the Access All Areas mode.

activeFocusItem

flag

no

Shows the item with active focus.

d0

flag

argv

Sends debug output to the console, instead of a log file.

dcolor

flag

argv

Output console only debug with ANSI color.

dcolour

flag

argv

Output console only debug with ANSI colour.

demoDriveDir

path

path

C++

Adds a directory as a demo drive

detectUILags

flag

C++

Prints a message if frame to frame time exceeds a 30 milliseconds threshold

detectUILagsThreshold

threshold

int (ms)

C++

Overrides the default threshold in milliseconds for detectUILags

dfilterGdpr

flag

argv

Enables gdpr filtering for log files, and sentry.

dimensionInfo

flag

no

Shows the width and height of the application.

disableElastiqueOnTrackDeck

flag

C++

Disables elastique for track decks.

discoPort

port

int

C++, argv

Sets the network discovery port.

dshowNoise

flag

argv

Shows noise normally filtered out from debug output.

dumpBigLog

flag

C++

Writes a one off 16 MB of log file data.

ecoMode

flag

C++

Locks the UI update rate to 30 fps.

enableAutoSelectSingleSource

flag

no

If only one device is connected it will be selected.

extraDriveDir

path,ejectable

path[,bool]

C++

Adds a directory as a source drive. The optional parameter ejectable can be set to true but else would default to false.

forceEnableShortcuts

flag

C++

Enables shortcuts via QWERTY keyboard on embedded.

frameTimer

flag

QML

Shows a frame timer.

freezeCheckThreadPools

flag

C++

Checks whether the asynchronous librarian runner or main thread pool are frozen.

gigTimer

flag

no

Shows the gig timer

ignoreDatabaseCheck

flag

C++

Disables the Database schema hash check.

laser

flag

no

Just try that yourself.

loggerOptions

options

CSV list

C++, argv

Comma separated list of options to modify the content and the layout of the logging output. Possible values are: %1.

noAnalyzerOutput

flag

C++

(no description: the binary literally stores ....)

noFeatureUpdatePopup

flag

C++

Do not show the new feature update pop-up message.

noOnBoardingScreen

flag

C++

Hides the on-boarding screen.

noPromptForDb

flag

C++

Dismiss user prompts during database migration

noSplashScreen

flag

C++, QML

Hides the splash screen.

nosmpopup

flag

no

Disable Sync Manager popup message boxes.

onlyOneAnalyzer

flag

C++

Will spawn only a single offline analyzer.

packMissingFiles

flag

C++

Packs files even if the audio file is missing.

pageCacheSize

bytes

int

C++

Sets the page cache size.

reportTags

tags

path list

C++

Paths to JSON files with custom analytics report tags

samplerDemoDriveDir

path

path

C++

Adds a directory as a sampler demo drive

showSystemMonitor

flag

no

Shows the System Monitor

sideDisplays

flag

no

Show left and right side screens.

simulateMixer

number of channels

int

C++

Simulates a network mixer.

simulatePowerFail

flag

C++, QML

Enables option for simulating a power failure.

skipFirmwareUpdate

flag

C++

Skip firmware updates. See MIDI Devices.

spoofSensors

flag

C++

Uses fake sensors instead.

updateServerUrl

URL

URL

no

Overwrites the URL for the default update server (https://autoupdate.airmusictech.com/PrimeUpdates.xml).

virtualDrivesDir

path

path

no

A directory to put dynamically created virtual drives.

watchdog

flag

no

(no description: the binary literally stores ...)

How options are consumed

Three separate mechanisms read arguments, which matters because checking only the first produces false negatives — d0 looks unused until the second is taken into account:

Mechanism

What it looks like in the binary

Used by

QCommandLineParser::isSet(QString) through the wrapper at 00bb1908

a standalone name literal

most options

a raw argv scan against dash-prefixed literals at 01d7ac08

-d0, -dcolour, -dcolor, -loggerOptions, -dshowNoise, -dfilterGdpr

the logger, which initialises before Qt's parser exists

the QML bridge Planck.hasCommandLineArgument(name)/Planck.commandLineArgumentValue(name)

the name inside a QML blob, not a standalone literal

aaa, frameTimer, noSplashScreen, simulatePowerFail, renderInfo, wifiPassword

The QML channel is easy to miss: Engine's QML is stored as plaintext, so those names appear only as substrings of a much larger string and never as their own literal.

Options that are never read

activeFocusItem, dimensionInfo, enableAutoSelectSingleSource, gigTimer, laser, nosmpopup, showSystemMonitor, sideDisplays, updateServerUrl, virtualDrivesDir, watchdog.

For each of these the name occurs exactly once in the binary and the only reference to it is the addOption call, so no code path can observe it. They parse without error and then do nothing. laser is the clearest case — its description, "Just try that yourself.", is the whole joke.

Additional checks behind that claim:

  • Engine's embedded QML contains none of them, in either ASCII or UTF-16.

  • No other /usr/Engine binary or data file mentions them.

  • Only isSet(const QString&) is linked; isSet(const QCommandLineOption&) is not, so an option can only ever be queried through its name string.

  • The same holds in 5.1.0-beta8: all eleven still occur once and none appear in a hasCommandLineArgument call.

  • laser specifically has been registered-but-unread in every release checked — 1.6.2, 2.4.0, 3.4.0, 4.3.4, 5.0.4 and 5.1.0-beta8.

Names that are queried but never registered

Six names are read by code yet never passed to addOption, so QCommandLineParser does not know them: runTests, repeatTests, repeatTestsFor, validateAMCResultsPath, renderInfo and wifiPassword. Qt's own QCommandLineParser: option not defined: warning string is present in the binary. An unregistered name makes parse() record an unknown-option error and makes isSet() warn and return false, so these read as leftover scaffolding rather than usable switches.

runTests is the interesting one, because MidiDeviceControl::quitAndStartFirmwareUpdate (00595758) tests it first and returns immediately when set — which would make the whole firmware-update exit a no-op. See MIDI Devices.

Version coverage

skipFirmwareUpdate was added in 2.0.0; it is absent from every 1.x build. The Read column was determined against 5.0.4 and spot-checked against 5.1.0-beta8; the rest of the table has not been diffed across versions.

Runtime Console Output

LD_LIBRARY_PATH=/usr/qt/lib:${LD_LIBRARY_PATH} /usr/Engine/Engine -d0

QIconvCodec::convertToUnicode: using Latin-1 for conversion, iconv_open failed QIconvCodec::convertFromUnicode: using Latin-1 for conversion, iconv_open failed DropboxManager: Dropbox application has not been found. Flush to zero not applied. This may lead to audio dropouts and/or freezes. QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-root' Unable to set double buffer mode! (Invalid argument) QVirtualKeyboardPlugin default IM has been initialized [I] ============================================================================ [2024-10-11T13:38:24.111Z] [ ] [I] Engine - 4.0.1 [2024-10-11T13:38:24.111Z] [ ] [I] ============================================================================ [2024-10-11T13:38:24.112Z] [ ] [I] Command Line Arguments: [2024-10-11T13:38:24.112Z] [ ] [I] -d0 [2024-10-11T13:38:24.112Z] [ ] [I] DenonDJ "PRIME GO 4.0.1 2c873aee05" starting up [2024-10-11T13:38:24.123Z] [ ] [I] sentry id: 12b64ccd-13d3-4d97-829a-8d3d0a54e538 [2024-10-11T13:38:24.126Z] [ ] [I] session id: 0df0b322-f5cb-4844-88ee-bf6e1b1c7f7b [2024-10-11T13:38:24.126Z] [ ] [I] Anonymous Statistics : enabled [2024-10-11T13:38:24.177Z] [ ] [I] Automatic crash reporting: enabled [2024-10-11T13:38:24.194Z] [ ] [2705:2705:20241011,093824.209004:INFO retry_upload_thread.cc:39] Start [I] Local UUID: "{34b78ffa-056f-488b-b068-bc42742d5665}" [2024-10-11T13:38:24.254Z] [ ] [W] The port isn't' opened for Midi::Out::PRIME GO Control Surface [2024-10-11T13:38:24.268Z] [air.devicemanager.midi.o] [I] Semantic version: 4.0.1 [2024-10-11T13:38:24.492Z] [ ] [ ] _analyzerApplicationPath "OfflineAnalyzer" [2024-10-11T13:38:24.515Z] [ ] [W] There is no databases available! [2024-10-11T13:38:24.700Z] [ ] [W] Only C and default locale supported with the posix collation implementation [2024-10-11T13:38:24.702Z] [ ] [W] Numeric mode unsupported in the posix collation implementation [2024-10-11T13:38:24.702Z] [ ] [I] TimeZone detect started "JP11" "4.0.1" "2c873aee05" [2024-10-11T13:38:24.705Z] [air.planck.controller.ti] [W] EDisks filesystem added: "/io/github/inmusicdev/edisksd/dev/mmcblk0p6" label: "1244c94bdd26dbae" [2024-10-11T13:38:24.717Z] [ ] [I] New device connected: "/io/github/inmusicdev/edisksd/dev/mmcblk0p6" [2024-10-11T13:38:24.717Z] [air.devicemanager.hotplu] [I] "/io/github/inmusicdev/edisksd/dev/mmcblk0p6" mounted at "8a5edab282632443" [2024-10-11T13:38:24.719Z] [air.devicemanager.hotplu] [I] "/io/github/inmusicdev/edisksd/dev/mmcblk0p6" mounted at "/usr/4b2f4473e24574e8/601ad8d0cac495d5.0" [2024-10-11T13:38:24.722Z] [air.devicemanager.hotplu] [W] EDisks filesystem added: "/io/github/inmusicdev/edisksd/dev/mmcblk0p7" label: "b27b20f5e019cb4f" [2024-10-11T13:38:24.726Z] [ ] [I] New device connected: "/io/github/inmusicdev/edisksd/dev/mmcblk0p7" [2024-10-11T13:38:24.726Z] [air.devicemanager.hotplu] [I] "/io/github/inmusicdev/edisksd/dev/mmcblk0p7" mounted at "bd47413b5c03dfc6" [2024-10-11T13:38:24.728Z] [air.devicemanager.hotplu] [I] "/io/github/inmusicdev/edisksd/dev/mmcblk0p7" mounted at "/97227e70daeb56c4" [2024-10-11T13:38:24.731Z] [air.devicemanager.hotplu] [W] [ 0x430c2dc ] Job Failed "Reading Playlists" "" [2024-10-11T13:38:24.738Z] [air.planck.jobcontroller] [ ] OfflineAnalyzer (0): QIconvCodec::convertToUnicode: using Latin-1 for conversion, iconv_open failed [2024-10-11T13:38:24.739Z] [ ] [ ] OfflineAnalyzer (0): QIconvCodec::convertFromUnicode: using Latin-1 for conversion, iconv_open failed [2024-10-11T13:38:24.740Z] [ ] [ ] OfflineAnalyzer (1): QIconvCodec::convertToUnicode: using Latin-1 for conversion, iconv_open failed [2024-10-11T13:38:24.740Z] [ ] [ ] OfflineAnalyzer (1): QIconvCodec::convertFromUnicode: using Latin-1 for conversion, iconv_open failed [2024-10-11T13:38:24.740Z] [ ] [I] TimeZone detect started "JP11" "4.0.1" "2c873aee05" [2024-10-11T13:38:25.342Z] [air.planck.controller.ti] [ ] Core Dump disabled [2024-10-11T13:38:25.348Z] [ ] [I] TimeZone detected: "America/New_York" [2024-10-11T13:38:25.450Z] [air.planck.controller.ti] [I] Board revision: 10 [2024-10-11T13:38:30.778Z] [qml ] [ ] FORCING SWAP INTERVAL = 1 [2024-10-11T13:38:31.031Z] [ ] [ ] FORCING SWAP INTERVAL = 1 [2024-10-11T13:38:31.656Z] [ ] [I] TimeZone detected: "America/New_York" [2024-10-11T13:38:31.657Z] [air.planck.controller.ti] [C] Planck init complete, handing over to exec. Took 7909 ms [2024-10-11T13:38:31.840Z] [air.planck ] [W] Failed to set discovery name Error: bluetooth::error::Code::NoUsableAdapter [2024-10-11T13:38:31.844Z] [air.planck.bluetooth.con] [W] Failed to set set discoverable Error: bluetooth::error::Code::NoUsableAdapter [2024-10-11T13:38:31.844Z] [air.planck.bluetooth.con] [W] setLatestLocalVersion version: "" path: "" [2024-10-11T13:38:32.083Z] [ ] [W] setLatestLocalVersion version: "" path: "" [2024-10-11T13:38:32.090Z] [ ] [W] JobController state: [2024-10-11T13:38:32.091Z] [air.planck.jobcontroller] [W] current number of threads: 8 [2024-10-11T13:38:32.091Z] [air.planck.jobcontroller] [W] maximum number of threads: 8 [2024-10-11T13:38:32.091Z] [air.planck.jobcontroller] [W] queue length: 8 jobs size: 10 [2024-10-11T13:38:32.091Z] [air.planck.jobcontroller] [W] Jobs running: [2024-10-11T13:38:32.091Z] [air.planck.jobcontroller] [W] "Searching on device" count 1 [2024-10-11T13:38:32.091Z] [air.planck.jobcontroller] [W] Jobs queued: [2024-10-11T13:38:32.092Z] [air.planck.jobcontroller] [W] "Checking updates" count 2 [2024-10-11T13:38:32.092Z] [air.planck.jobcontroller] [W] "Processing view state controller event" count 1 [2024-10-11T13:38:32.092Z] [air.planck.jobcontroller] [W] "Searching on device" count 4 [2024-10-11T13:38:32.092Z] [air.planck.jobcontroller] [W] "Updating network drives" count 1 [2024-10-11T13:38:32.092Z] [air.planck.jobcontroller] [W] JobController state: [2024-10-11T13:38:32.095Z] [air.planck.jobcontroller] [W] current number of threads: 8 [2024-10-11T13:38:32.096Z] [air.planck.jobcontroller] [W] maximum number of threads: 8 [2024-10-11T13:38:32.096Z] [air.planck.jobcontroller] [W] queue length: 13 jobs size: 15 [2024-10-11T13:38:32.096Z] [air.planck.jobcontroller] [W] Jobs running: [2024-10-11T13:38:32.096Z] [air.planck.jobcontroller] [W] "Searching on device" count 1 [2024-10-11T13:38:32.096Z] [air.planck.jobcontroller] [W] Jobs queued: [2024-10-11T13:38:32.096Z] [air.planck.jobcontroller] [W] "Checking updates" count 2 [2024-10-11T13:38:32.097Z] [air.planck.jobcontroller] [W] "Loading persisted samples" count 1 [2024-10-11T13:38:32.097Z] [air.planck.jobcontroller] [W] "Processing view state controller event" count 2 [2024-10-11T13:38:32.097Z] [air.planck.jobcontroller] [W] "Reading History" count 1 [2024-10-11T13:38:32.097Z] [air.planck.jobcontroller] [W] "Reading Playlists" count 1 [2024-10-11T13:38:32.097Z] [air.planck.jobcontroller] [W] "Searching on device" count 4 [2024-10-11T13:38:32.097Z] [air.planck.jobcontroller] [W] "Updating network drives" count 2 [2024-10-11T13:38:32.097Z] [air.planck.jobcontroller] [W] qrc:/ControlSurfaceModules/ControlSurfaceModules/private/STMBank.qml:14:2: QML Connections: Implicitly defined onFoo [2024-10-11T13:38:32.178Z] [ ] [W] properties in Connections are deprecated. Use this syntax instead: function onFoo(<arguments>) { ... } [2024-10-11T13:38:32.178Z] [ ] [I] TimeZone detect triggered during cooldown [2024-10-11T13:38:32.186Z] [air.planck.controller.ti] [W] qrc:/ControlSurfaceModules/ControlSurfaceModules/private/STMBank.qml:14:2: QML Connections: Implicitly defined onFoo [2024-10-11T13:38:32.255Z] [ ] [W] properties in Connections are deprecated. Use this syntax instead: function onFoo(<arguments>) { ... } [2024-10-11T13:38:32.255Z] [ ] [W] qrc:/ControlSurfaceModules/ControlSurfaceModules/HardwareMixerMic.qml:35:2: QML Connections: Implicitly defined [2024-10-11T13:38:32.256Z] [ ] [W] onFoo properties in Connections are deprecated. Use this syntax instead: function onFoo(<arguments>) { ... } [2024-10-11T13:38:32.256Z] [ ] [W] qrc:/ControlSurfaceModules/ControlSurfaceModules/HardwareMixerMic.qml:35:2: QML Connections: Implicitly defined [2024-10-11T13:38:32.290Z] [ ] [W] onFoo properties in Connections are deprecated. Use this syntax instead: function onFoo(<arguments>) { ... } [2024-10-11T13:38:32.290Z] [ ] [W] qrc:/ControlSurfaceModules/ControlSurfaceModules/VUMeter.qml:109: TypeError: Cannot read property 'c5fafca31e7f33c7' [2024-10-11T13:38:32.291Z] [ ] [W] of null [2024-10-11T13:38:32.291Z] [ ] [W] qrc:/ControlSurfaceModules/ControlSurfaceModules/VUMeter.qml:108: TypeError: Cannot read property 'c5fafca31e7f33c7' [2024-10-11T13:38:32.291Z] [ ] [W] of null [2024-10-11T13:38:32.291Z] [ ] [W] qrc:/ControlSurfaceModules/ControlSurfaceModules/VUMeter.qml:109: TypeError: Cannot read property 'c5fafca31e7f33c7' [2024-10-11T13:38:32.291Z] [ ] [W] of null [2024-10-11T13:38:32.291Z] [ ] [W] qrc:/ControlSurfaceModules/ControlSurfaceModules/VUMeter.qml:108: TypeError: Cannot read property 'c5fafca31e7f33c7' [2024-10-11T13:38:32.291Z] [ ] [W] of null [2024-10-11T13:38:32.291Z] [ ] [ ] No special power on request [2024-10-11T13:38:32.295Z] [qml ] [W] setLatestLocalVersion version: "" path: "" [2024-10-11T13:38:39.462Z] [ ] [W] setLatestLocalVersion version: "" path: "" [2024-10-11T13:38:39.488Z] [ ] [W] MigrationFinished uuid for path "851c9b64ee7abb42" "9b7a80aa-4f11-4118-a42f-d631c2dddd34" [2024-10-11T13:38:39.488Z] [air.planck.database ] [W] MigrationFinished uuid for path "a394739a3f3a75b5" "0992b059-9b42-4a42-b840-b63e49fdf11c" [2024-10-11T13:38:39.497Z] [air.planck.database ]
  • Controller Assignments covers how the hardware controls different functions of this application.

  • MIDI Devices covers how this application finds its control surface, wheel displays and mixer over MIDI, and where their MCU firmware and versions live.

  • Engine Ghidra Analysis covers getting this binary into a navigable state in Ghidra.

  • Engine Networking covers the gRPC services, discovery, and cloud endpoints.

18 August 2026