Java toolkit for accessing and reverse-engineering a DSP408 via a local proxy stack.
The project combines read, write, scripting, and GUI-capture tooling so that the DSP protocol can be analyzed in a structured way.
A key use case is continuous AI-assisted decoding: while the original GUI stays connected through the proxy, Codex can repeatedly observe GUI writes, read back DSP blocks, compare dumps, and refine protocol hypotheses in an ongoing decode loop.
The project bundles multiple tools:
- Reader CLI
- reads handshake, device/system info, and all known DSP blocks
- Writer CLI
- sends payloads in a controlled way through the proxy to the DSP
- Raw Write Tool
- sends raw payloads or full frames directly to a target device
- Script CLI
- small DSL to automate read, write, dump, and GUI-capture flows
- GUI Capture
- records which frames the original GUI sends via the proxy stream
The DSP408 protocol is not encoded symmetrically between GUI writes and block reads.
That means:
- a GUI action usually produces a write command
- the resulting value is often stored somewhere else in the DSP read blocks
- the read representation is often not identical to the write representation
- therefore protocol decoding requires write observation + readback comparison
This toolkit is designed exactly for that workflow.
One of the most useful workflows is to keep the original DSP GUI connected through the proxy and let an AI agent such as Codex in Codex IDE continuously drive the decode process.
The AI does not need a full protocol map up front.
It can iteratively discover fields by repeating this loop:
- observe which write frame the GUI sends
- perform or confirm a controlled GUI change
- read the DSP blocks again
- compare old vs new block dumps
- identify which bytes changed
- correlate:
- GUI action
- write command
- changed read-block offsets
- store the new hypothesis in documentation or a field library
- repeat
Because the proxy exposes both sides:
- stream side
- what the GUI writes / what the DSP answers
- control side
- controlled scripted access for readback and testing
This allows a continuous reverse-engineering loop where the AI can move forward step by step instead of requiring the full protocol in advance.
A practical cycle looks like this:
- original GUI is connected through the proxy
- AI arms GUI capture
- user changes exactly one setting in the GUI
- AI captures the last interesting write frame
- AI performs a new full dump or selected block reads
- AI diffs the new dump against the previous dump
- AI searches for changed offsets
- AI proposes mapping candidates
- AI documents confirmed findings
- AI repeats with the next value or next parameter
To find read mappings, it is usually not enough to inspect the write payload alone.
In many cases you must:
- trigger a write
- re-read DSP blocks
- compare the before/after dumps
- decode the changed read locations
This is the main reason the project combines:
- GUI write capture
- automated re-dump
- block diffing
- scripting
- repeatable AI workflows
The project is split into several areas:
de.drremote.dsp408.proxy- proxy connection, control/stream channel, session handling
de.drremote.dsp408.dump- full DSP dump and helper functions for byte decoding
de.drremote.dsp408.raw- direct raw access with payload or frame sending
de.drremote.dsp408.script- scripting language, parser, runtime, GUI-capture helpers
de.drremote.dsp408.tool- small public tool facade for read/write/script
de.drremote.dsp408.model- response, status, capture, and frame models
de.drremote.dsp408.util- hex, JSON, and wait helpers
DSP408ProxyDump opens a proxy connection, prepares the session, and reads:
handshake_initdevice_infosystem_info- optional
login - all blocks from
0x00to0x1C
The result is collected as a DumpResult and can be output as JSON.
ProxyClient encapsulates:
- stream connection
- control connection
- session reset
- session initialization
- handshake
- login
- block read
- controlled sending of a payload with expected DSP response
DspRawWriteTool is intended for direct low-level tests.
It can:
- send raw payloads
- send complete frames
- optionally run handshake and login beforehand
- display response frames including checksum, payload hex, and ASCII
The script engine allows small .dspd scripts for:
- connect / disconnect
- status checks
- handshake / login
- read blocks
- send payloads
- conditions with
if / else if / else - loops with
for - GUI capture
- saving output to files
- byte evaluation (
u8,u16le,u32le,ascii,hex,slice) - event-based GUI action capture that ignores
0x40background traffic - write-series helpers for fader decoding such as command/channel filtering and
u16series extraction
With GuiSnifferClient or GuiCaptureDaemon, frames streamed through the proxy can be captured to analyze actions from the original software.
This is especially useful for AI-assisted reverse engineering, because the captured write frames can immediately be correlated with readback changes from fresh dumps.
To use proxy functions, the matching counterpart services must be running.
Typical defaults:
- Stream Host:
127.0.0.1 - Stream Port:
19081 - Control Host:
127.0.0.1 - Control Port:
19082
For GUI decode/capture workflows:
- the original software should already be connected through the proxy
- the project notes mention
127.0.0.1:9761for this - while the GUI remains connected, scripts and AI tooling can continue capturing writes and performing readback analysis
For raw access, default values are:
- Host:
192.168.0.166 - Port:
9761
You can override these via the CLI.
The build produces three separate fat JARs in target/:
target/tracks-dsp-reader-all.jartarget/tracks-dsp-writer-all.jartarget/tracks-dsp-script-all.jar
This keeps daily usage on Windows simple:
java -jar target\tracks-dsp-reader-all.jar ...
java -jar target\tracks-dsp-writer-all.jar ...
java -jar target\tracks-dsp-script-all.jar ...Build command:
mvn -q -DskipTests packageCentral class for normal proxy operation.
Important methods:
connect()status()resetSession()ensureSession()handshakeInit()deviceInfo()systemInfo()handshake()login(pin)readBlock(blockIndex)sendPayload(payload, expectedCommand, strictResponse, label)prepareSession(pin)
Reads a full set of DSP data and returns a DumpResult.
Flow:
- connect
- reset session
- ensure session
- read handshake
- optional login
- read all blocks
- produce JSON result
Serializes a dump in JSON with:
oksummaryhandshakeloginblocks- optional
error
Example structure:
{
"ok": true,
"summary": {
"handshake_ok": true,
"login_used": false,
"blocks_total": 29,
"blocks_bad_checksum": 0
},
"handshake": {
"init": { "...": "..." },
"device": { "...": "..." },
"system": { "...": "..." }
},
"login": null,
"blocks": [
{ "...": "..." }
]
}CLI for full proxy-based DSP dumps.
Supports:
--stream-host <host>--stream-port <port>--control-host <host>--control-port <port>--pin <1234>--out <path>--verbose
CLI for controlled payload injection through the proxy.
Supports:
--payload <hex>--stream-host <host>--stream-port <port>--control-host <host>--control-port <port>--pin <1234>--expected-command <cmd>--label <text>--non-strict--verbose
CLI for quick raw tests against the device.
Supports:
--payload <hex>--frame <hex>--handshake--login <1234>--responses <n>--verbose
Executes .dspd scripts.
Supports, among others:
- variables via
let - control flow with
if,else if,else,for assertprintsleepsave-text- connect / GUI-capture commands
- function style and legacy style for expressions
Example:
java -jar target\tracks-dsp-reader-all.jar --pin 1234 --out out\dump.jsonOptions:
--pin <1234>--out <path>--stream-host <host>--stream-port <port>--control-host <host>--control-port <port>--verbose
Example:
java -jar target\tracks-dsp-writer-all.jar --payload "00 01 03 35 04 01"Options:
--payload <hex>--pin <1234>--expected-command <cmd>--label <text>--non-strict--stream-host <host>--stream-port <port>--control-host <host>--control-port <port>--verbose
Example:
java -jar target\tracks-dsp-script-all.jar --file script-example\01-status.dspdOptions:
--file <path>--stream-host <host>--stream-port <port>--control-host <host>--control-port <port>--verbose
Example:
java -cp target\tracks-dsp-script-all.jar de.drremote.dsp408.raw.DspRawWriteTool --host 192.168.0.166 --port 9761 --payload "00 01 03 35 04 01"For a direct, all-channel phase storage check with automatic safety restore:
java -cp target\tracks-dsp-script-all.jar `
de.drremote.dsp408.raw.Fir408PhaseReadVerifierThe verifier keeps all twelve channels muted, tests each phase as
0 degrees -> 180 degrees -> 0 degrees, and compares the complete config
before and after. Results are written below
out/fir/fir408-phase-readback-verification.
For a direct storage check of all four gate fields on the write-confirmed inputs InA and InD:
java -cp target\tracks-dsp-script-all.jar `
de.drremote.dsp408.raw.Fir408GateReadVerifierThe verifier derives the current gate tuples from the assembled FIR408
configuration, mutes all twelve channels, changes only one field at a time,
and immediately restores the original tuple. It never sends an unmute
command. The complete configuration must match byte-for-byte after the test.
Results are written below
out/fir/fir408-gate-readback-verification.
Examples from the tool:
java -cp target\tracks-dsp-script-all.jar de.drremote.dsp408.raw.DspRawWriteTool --handshake --login 1234 --payload "00 01 03 35 04 01"java -cp target\tracks-dsp-script-all.jar de.drremote.dsp408.raw.DspRawWriteTool --handshake --login 1234 --payload "00 01 03 35 04 00"java -cp target\tracks-dsp-script-all.jar de.drremote.dsp408.raw.DspRawWriteTool --handshake --login 1234 --payload "00 01 03 36 04 01"java -cp target\tracks-dsp-script-all.jar de.drremote.dsp408.raw.DspRawWriteTool --handshake --login 1234 --payload "00 01 04 34 04 18 01"java -cp target\tracks-dsp-script-all.jar de.drremote.dsp408.raw.DspRawWriteTool --handshake --login 1234 --payload "00 01 04 38 04 80 07"Start the daemon:
java -cp target\tracks-dsp-script-all.jar de.drremote.dsp408.script.GuiCaptureDaemonUse the CLI against it:
java -cp target\tracks-dsp-script-all.jar de.drremote.dsp408.script.GuiCaptureDaemonCli arm
java -cp target\tracks-dsp-script-all.jar de.drremote.dsp408.script.GuiCaptureDaemonCli finish 1800 12000
java -cp target\tracks-dsp-script-all.jar de.drremote.dsp408.script.GuiCaptureDaemonCli stopPreferred style uses function-call syntax with () and optional ;. Legacy forms without parentheses are still supported for compatibility.
Comments:
// comment
# legacy commentVariables:
let x = 123;
let name = "test";Quoted strings always stay strings (no auto-typing), even if they look like numbers or booleans:
print("123");
print("true");
print("0012");Output:
print($x);
print("Hello");Conditions:
if ($x == 123) {
print("ok");
} else if ($x > 100) {
print("big");
} else {
print("other");
}Loops:
for (let i in 0..5) {
print($i);
}Assertions:
assert $x == 123;
assert not ($x == 0);Write to file:
save-text("out/result.txt", "Hello World");connectconnect(<streamHost>, <streamPort>, <controlHost>, <controlPort>)disconnectstatusreset-sessionensure-sessionclear-frameshandshakelogin(<pin>)read-block(<block>)
gui-connectgui-connect(<streamHost>, <streamPort>)gui-disconnectgui-capture(<note>, [quietMs], [maxWaitMs])gui-begin-capture()gui-end-capture([quietMs], [maxWaitMs])
len(...)contains(...)starts-with(...)ends-with(...)upper(...)lower(...)trim(...)join(...)at(...)split(...)replace(...)
bytes(...)hex(...)slice(...)ascii(...)u8(...)u16le(...)u32le(...)
write(...)send-payload ...tx ...
connect();
let s = status();
print($s);
print($s.sessionActive);
print($s.injectReady);connect();
reset-session();
ensure-session();
handshake();
let resp = read-block(0x00);
print($resp);
print($resp.payloadHex);gui-connect();
let cap = gui-capture("Please perform the target action in the GUI now", 1500, 12000);
print($cap);
print($cap.lastWrite);connect();
reset-session();
ensure-session();
handshake();
for (let i in 0..5) {
let resp = read-block($i);
save-text("out/block-${i}.txt", $resp.payloadHex);
}The most reliable strategy for finding real field mappings is:
- create a baseline dump
- change exactly one GUI value
- capture the GUI write frame
- create a second dump
- diff both dumps
- identify changed offsets
- repeat with several values
- confirm encoding and scaling
This is important because:
- write command structure and read storage structure are often different
- the same parameter may use one encoding on write and another in block storage
- isolated GUI write capture alone is usually not enough
A strong setup is:
-
keep the original GUI connected through the proxy
-
let Codex run helper scripts / dumps / comparisons
-
trigger one GUI action at a time
-
let Codex analyze:
- last write frame
- changed blocks
- changed offsets
- value scaling candidates
-
update documentation and repeat
In practice, this means Codex can continuously decode the protocol in an iterative loop as long as the proxy and GUI stay active.
The project does not implement an AI by itself; it provides the infrastructure that makes such an AI loop practical.
The de.drremote.dsp408.tool package contains simple entry points:
String json = ReadTool.dumpAllJson(streamHost, streamPort, controlHost, controlPort, pin, verbose);ProxyResponse response = WriteTool.sendPayloadOnce(
streamHost,
streamPort,
controlHost,
controlPort,
pin,
verbose,
payload,
expectedCommand,
strictResponse,
label
);ScriptTool.runScriptFile(streamHost, streamPort, controlHost, controlPort, verbose, file);
ScriptTool.runScriptText(streamHost, streamPort, controlHost, controlPort, verbose, scriptText);StreamChannel.waitForResponse(...) works in two steps:
- it waits for the matching
PC_TO_DSPframe to appear in the stream - then it searches for the next matching
DSP_TO_PCresponse
Optionally it can check for a specific command code.
ProxyClient.ensureSession() waits actively until:
sessionActive == trueinjectReady == true
Both dump mode and raw mode expose checksum status.
Currently, blocks from:
0x00- to
0x1C
are read.
Write functions and raw access can change the DSP state.
Therefore:
- test with read/dump first
- only send known payloads for writes
- if possible, work against a test setup first
- use raw frame access only for low-level debugging
- JSON is handled with a small in-house helper (
JsonUtil), not an external JSON library - the script language is small and deliberately pragmatic
- the parser is built for the internal DSL style, not for a general-purpose language
RawSocketClientis intended for diagnostics and tests, not robust long-running use- some defaults are strongly tied to your local proxy/DSP setup
- many parameters still require repeated GUI-write + readback-diff analysis before they can be considered decoded
- start proxy
- connect DSP/original software
statusreset-sessionensure-sessionhandshakeread-block(...)or full dump
- connect original software through proxy
- keep the GUI connected
gui-connect- capture a GUI action
- re-read DSP blocks
- diff old vs new dumps
- correlate write payload with changed read offsets
- document the result
- repeat in a continuous AI loop
For setups where GUI and DSP communicate continuously, prefer event-based capture instead of fixed waiting windows.
Use:
guiActionCapture(...)
This mode:
- waits for the first real GUI write
- ignores
0x40background traffic - automatically stops after the action settles
It is especially useful for:
- mute / phase toggles
- single parameter clicks
- gain or delay fader moves
The DSL also supports write-series analysis helpers that make fader reverse engineering easier:
writesByCommand(...)writesByCommandAndChannel(...)payloadSeries(...)u16Series(...)changingOffsetsAcrossWrites(...)
These helpers allow you to isolate one fader movement and identify which payload offsets and u16le values change across the write series.
- prepare session
- optional login
- send known payload
- verify response
- verify stored value via fresh dump if needed
Internal reverse-engineering/debugging helper for DSP408-related tests and automation.
Project status: experimental, but already strongly structured around:
- Read
- Write
- Dump
- Script
- GUI Capture
- AI-assisted continuous protocol decoding