Open, lossless JSON patch tools and direct transport discovery for the Arturia
MiniFreak and MicroFreak. The installed command is freakout; the historical
freak-patch and minifreak-patch command names remain as compatible aliases.
The project keeps device support honest and explicit:
- MiniFreak: mature
.mnfxparsing and writing plus independent Collage USB reads of the active patch and any of the 512 saved slots. One hundred fifty-eight named parameters have verified, read-back writes from JSON. - MicroFreak: USB discovery, firmware identification, preset and wavetable reads, guarded saved-preset writes, and arbitrary user-wavetable uploads are available. Preset and wavetable read/write plus the 128-slot sample-directory inventory and lossless sample-body download have an independent CoreMIDI backend. A firmware-5 bank scan found 107 self-named fields without brittle absolute offsets; 101 have bounded metadata-scaled, normalized, bipolar, signed-offset, or live-destination JSON values, while six internal/action fields remain explicitly raw-only. The firmware-owned full Init template is also readable directly as editable JSON.
- Writes: MiniFreak active-buffer and occupied-slot edits are enabled only for verified fields. MicroFreak lossless JSON and wavetable writes require an explicit acknowledgement and use backup, stable preflight, exact readback, and automatic rollback on failure. Empty MicroFreak Init preset slots are refused because the upload protocol cannot recreate their empty representation.
This is an independent open-source project and is not affiliated with Arturia.
See NOTICE.md for the clean-room provenance and redistribution
boundary.
python3 -m venv .venv
.venv/bin/pip install -e .Independent MicroFreak preset and wavetable transport uses CoreMIDI through
mido. The separately installed
elektroid-cli remains an optional,
proven compatibility backend. Elektroid is GPL-3.0; this MIT package invokes
its command-line interface and does not incorporate its source code.
freak-patch devices
freak-patch capabilities
freak-patch json-schema > patch.schema.jsonExample discovery output:
coremidi:Arturia MicroFreak: microfreak backend=direct connector=arturia-microfreak-sysex firmware=not probed
usb:1c75:0602:1: minifreak backend=direct connector=arturia-minifreak-collage-usb firmware=not probed
Direct discovery is bounded and read-only. It reports the actual patch bus:
paired MIDI SysEx for MicroFreak and vendor-specific Collage USB for MiniFreak.
Use freak-patch devices --backend elektroid for the optional compatibility
inventory and its firmware identification.
Every document declares its target and contains only that device's block:
{
"schema_version": "arturia-freak-patch/1",
"device": "microfreak",
"metadata": {
"name": "NervousKeys",
"category": "Keys",
"source_slot": 1
},
"shared": {},
"microfreak": {
"archive": {
"version_tag": "174",
"category_id": 2,
"init": 0,
"p1": 16
},
"decoded_parameters": {
"filter.cutoff": 0.402417065950499
},
"parameter_evidence": {
"filter.cutoff": {
"status": "hardware_raw_rw_fw5_semantics_unconfirmed",
"raw_value": 13186,
"byte_offsets": [88, 93, 94],
"flag_mask": 16,
"encoding": "unsigned_15bit_normalized"
}
},
"raw_payload_base64": "...",
"decoded_parameter_support": "partial"
}
}MiniFreak documents use a minifreak block containing the complete normalized
parameter dictionary and, optionally, a compact recipe. MicroFreak documents
retain the complete raw payload and keep verified named parameters separate.
Convert files losslessly:
freak-patch to-json MyPatch.mnfx MyPatch.json
freak-patch to-json MyPatch.mfp MyPatch.json
freak-patch from-json MyPatch.json rebuilt.mfp
freak-patch from-json MyPatch.json rebuilt.mfpzPlain .mfp/project-bank .mbp preset objects and official one-entry .mfpz
containers share the same lossless core. .mfpz output uses the expected
0_preset archive member and can be converted back to JSON directly.
MIDI Control Center's observed MicroFreak project directory can also be round-tripped as one lossless, explicitly slotted JSON document:
freak-patch microfreak-bank-to-json /path/to/project bank.json
freak-patch microfreak-bank-from-json bank.json /path/to/restored-project
freak-patch microfreak-bank-to-json Factory6.mfprojz factory6.json
freak-patch microfreak-bank-from-json factory6.json Rebuilt.mfprojzThis models both the synchronized .mbp directory and the confirmed
.mfprojz ZIP topology (project directory, bank directory, numbered preset
objects). It preserves the opaque archive tag—observed values include 134,
174, numeric project identifiers, and DEVBUILD—plus the 18-bit
characteristics field and raw 8-bit category field.
An extracted project generated by this tool was also accepted by MIDI Control
Center 1.23.0's official library scanner: the project appeared as
Codex Preset Probe, and its two generated slots were decoded as Carrytron
and Comb-Me. The app's archive-picker path remains a separate pending UI
check because its custom canvas does not expose automatable controls on macOS.
The characteristics are decoded by name in JSON (Acid, Aggressive,
Ambient, …, Soundtrack) while the exact bit string remains alongside them.
They can be updated without hand-editing the bitset:
freak-patch set-microfreak-characteristics patch.json tagged.json Ambient SoftMicroFreak wavetables can be prepared as either plain .mfw or the official
one-entry .mfwz container; both are accepted by guarded upload commands:
freak-patch wavetable prepare microfreak table.wav table.mfwz --name MyTableComplete 16-slot Control Center wavetable directories also round-trip through
lossless JSON. .mfwbz project/bank ZIP output is available for testing, but
remains explicitly marked research until a generated archive is imported
back into Control Center:
freak-patch microfreak-wavetable-bank-to-json /path/to/wave-bank waves.json
freak-patch microfreak-wavetable-bank-from-json waves.json Waves.mfwbzRead a saved MicroFreak patch directly from the connected device:
freak-patch pull 1 42 patch-42.json
freak-patch push-microfreak 1 42 edited.json backups/slot-42.mfp \
--i-understand-this-writes
freak-patch pull-microfreak-direct 42 patch-42-direct.json
freak-patch pull-microfreak-init-direct microfreak-init-template.json
freak-patch set-microfreak-json patch-42-direct.json filter.cutoff 0.5 edited.json
# Cross-engine edits must start from a preset whose VCO.Type layout supports
# the requested engine. Firmware-5 presets support all 22 engines.
freak-patch set-microfreak-json firmware-5-preset.json osc.type 20 scan-grains.json
freak-patch push-microfreak-direct 42 edited.json backups/slot-42-direct.mfp \
--select-after-write --i-understand-this-writes
freak-patch select-microfreak-direct 42 \
--i-understand-this-changes-live-buffer
freak-patch microfreak-globals-direct
freak-patch microfreak-globals-json-direct > microfreak-globals.json
freak-patch microfreak-global-codes-direct 2 19 20
freak-patch microfreak-global-write-probe keyboard.root_note 1 \
--recovery-slot 320 --i-understand-this-changes-device-setting
freak-patch set-microfreak-global-direct keyboard.root_note 1 \
backups/root-before.json --i-understand-this-changes-device-setting
freak-patch microfreak-samples-direct --occupied-only
freak-patch microfreak-sample-storage-direct
freak-patch microfreak-live-word-direct 0
freak-patch microfreak-live-table-direct > microfreak-live-table.json
freak-patch microfreak-live-structured-direct > microfreak-current-fields.json
freak-patch microfreak-current-overlay-json-direct 320 current-over-slot320.json
freak-patch microfreak-sequence-playback-direct sequence-playback.json \
--source-slot 5 --recovery-slot 320 \
--i-understand-this-temporarily-changes-clock-source
freak-patch microfreak-live-cc-probe filter.cutoff 49 0 \
--recovery-slot 320 --i-understand-this-changes-live-buffer
freak-patch microfreak-live-word-write-probe 257 9546 \
--recovery-slot 320 --i-understand-this-changes-live-buffer
freak-patch microfreak-live-record-write-probe 257 9546 \
--recovery-slot 320 --i-understand-this-changes-live-buffer
freak-patch collect-microfreak-saved-live-direct saved-live.json \
--recovery-slot 320 --i-understand-this-changes-live-buffer
python3 tools/analyze_microfreak_saved_live_corpus.py saved-live.json --json
freak-patch collect-microfreak-oscillator-types-direct oscillator-types.json \
--recovery-slot 320 --i-understand-this-changes-live-buffer
python3 tools/analyze_microfreak_oscillator_type_corpus.py \
oscillator-types.json --json
freak-patch collect-microfreak-oscillator-cc-direct oscillator-cc.json \
--recovery-slot 320 --i-understand-this-changes-live-buffer
freak-patch microfreak-sample-download-direct 1 sample-1.raw
freak-patch push-microfreak-sample-direct 2 sample-1.raw \
backups/sample-2-before.mfsample --name MySample \
--i-understand-this-writes
freak-patch clear-microfreak-sample-direct 2 \
backups/sample-2-before-clear.mfsample --i-understand-this-writesThe live-table command performs one read-only session across the complete firmware-bounded active synth object: 24 groups with 16 raw 16-bit words each. The JSON preserves every wire address, unsigned and signed interpretations, and the exact reply payload. Hardware has answered all 384 addresses. The 20 documented CC controls are semantically labeled; non-CC words remain open.
The structured live command adds the bulk saved/live correlation layer. A
diversity optimizer selected 40 of the 320 saved presets to maximize variation
across all 107 firmware-tagged fields; every preset was paired with all 384
live words and slot 320 restored exactly. Ninety-five varying tags matched live
word vectors byte-for-byte. Ninety-two were initially unambiguous, while the
three chord-offset tags covaried. One saved-preset sentinel mapped Kbd.Hold,
Kbd.Root, Arp.Dice, and Seq.XiceRst to nine exact live aliases; a second
raw sentinel separated all three chord offsets across six aliases. Independent
operation-49 writes confirmed each newly mapped field and restored the complete
table. Oscillator type is the separately proven normalized word 0000. One
read now returns 100 named current fields (99 structured plus oscillator type),
with no varying structured tag left ambiguous. Seven constant tags remain without live
addresses. JSON classifies six as tentative UI-action placeholders and
Gen.Panel as tentative legacy panel state, based on their names, groups,
all-zero values across 320 saved presets, and absence from the live table. The
normal interpreted editor refuses them; the explicit raw research editor still
preserves access without claiming they are sound parameters. The non-word
sequence body also remains explicitly unresolved rather than guessed.
The generic live-CC probe reads all 384 words, sends one documented CC, reads
the complete table again, sends an explicit inverse CC, and requires an exact
final comparison; it recalls the named recovery preset if the inverse is not
exact. On firmware 5.0.0.36, cutoff CC 23 changed only its three known
aliases (0101, 0F0E, 1008) and slot 320 restored every word. Hold CC 64
changed no operation-41 word and Hold-off reproduced the baseline directly.
That negative result is retained: the documented performance CC is not assumed
to be the saved tag Kbd.Hold; the latter was subsequently mapped through
saved-preset activation and operation 49 at 010D/020A/0303.
For qualified current-state capture, microfreak-current-overlay-json-direct
combines all 100 mapped operation-41 fields with a complete caller-selected
saved-slot base. It explicitly distinguishes fields applied to that engine's
tagged layout, live fields absent from the layout, and sequence/header bytes
preserved from the saved base. It is intentionally labelled partial-current,
not a lossless unsaved-current dump. A hardware cutoff-CC test changed only
VCF.Cutoff; the overlay changed exactly its two unpacked saved-value bytes,
then slot-320 recall restored all 384 live words exactly.
Global settings are a separate transport surface. The guarded operation-42
probe reads the original value with operation 43, writes the target, reads it
back, compares the complete patch live table, writes the original value, and
verifies both final states. Keyboard Root Note code 46 was proven 0 -> 1 -> 0; each operation-43 readback matched and no operation-41 word changed.
The live JSON therefore records keyboard.root_note as a global counterpart
to constant tag Kbd.Root, not as a fabricated live-word address.
The labeled global JSON combines Arturia's installed device description with
firmware-5 setter bounds, without requiring either artifact at runtime. All 43
named global settings now have explicit allowed wire values. Thirty-four use
device-description labels; nine hidden settings use the firmware's clamp
domains and retain raw labels where units are unknown. The durable setter
writes a JSON before-state backup and requires exact operation-43 target
readback. A failed readback triggers an inverse write and independently
verifies recovery. A second hardware probe proved hidden Automation Out code
23 from On -> Off -> On, with exact readback and no patch-table changes.
Oscillator type is decoded and edited through firmware 5's self-describing
VCO.Type field, not the older fixed byte. A 320-preset hardware sweep proved
that its metadata-scaled integer matches live word 0000 for every preset and
restored slot 320 with no live-table differences. The sweep observed engine
indices 1 through 17 in the connected saved bank. A separate complete CC 9
sweep then activated all 22 firmware engines, read the expected normalized
engine value back from live word 0000, and restored all 384 live words
exactly. This hardware-proves WaveUser, Sample, Scan Grains, Cloud Grains, and
Hit Grains at runtime indices 18 through 22 even though they were absent from
that saved bank.
Firmware-generation layout is an explicit boundary. Historical presets have a
VCO.Type.metadata maximum below 22 and cannot safely be migrated merely by
changing that byte: a guarded attempt produced a different engine and was
restored. Start an edit targeting engines 18 through 22 from a genuine
firmware-5 preset. Arturia's installed New Presets 5.0 library supplies such
payloads with a 22-engine, 110-field layout. A guarded edit of one of those
payloads from Sample to Scan Grains was written to slot 320, read back exactly
over the wire, selected with live word 0000 = 29788, and then restored to the
original preset with zero differences across the live table. The .mfp
archive wrapper version is local file metadata and is normalized on device;
wire-state verification therefore compares the transmitted header fields and
all 4,672 payload bytes rather than that non-transmitted wrapper byte.
The write probe temporarily changes one hardware-correlated live word, reads
all aliases back, and restores the complete table. Operation-40 request bytes
must each be 7-bit clean; when the original value is not representable, the
required recovery slot is recalled and all 384 words must match the baseline.
No preset save/store command is sent.
The sample inventory is a read-only, lossless view of all 128 directory
headers. It reports the name, byte length, memory address, checksum, device ID,
and exact raw header. The download command uses the firmware's sequential 4 KiB
read stream and writes the exact device bytes; a 384,000-byte hardware sample
matched across complete reads before and after the upload experiment. The
guarded uploader accepts raw mono 32 kHz PCM16LE, performs the allocation and
header/body transactions, downloads the complete stored body independently,
and compares its name, length, checksum, and every audio byte. A 384,000-byte
copy of Ney was uploaded to empty slot 2 as CodexProbe, matched exactly,
and was cleared back to empty; the neighboring slot-1 body retained its
original SHA-256. Sample backups are lossless .mfsample recovery artifacts:
the exact 28-byte directory header followed by the exact PCM body.
Direct selection sends bank-select plus program-change, so a guarded JSON write can be made active without pressing the preset encoder. MIDI does not acknowledge program changes; the command reports that distinction explicitly.
Firmware 5.0.0.36 also responds at reserved bank 4, program 0, beyond the
four saved-preset banks. It returns a stable Init header plus a complete
4,672-byte body. A bounded test selected slot 320, read the pseudo-slot, changed
the live cutoff through documented CC 23, read it again, and reselected slot
320; the header and every body byte remained identical. The Init command
therefore labels this as a firmware template, not as the unsaved current patch.
The earlier live-buffer CC correlation experiment is still available for protocol research:
freak-patch live microfreak-sentinel work/cc-plan.json \
--i-understand-this-changes-live-buffer
# This legacy experiment requires physically saving the selected test preset.
freak-patch pull-microfreak-direct 320 work/cc-after.json
freak-patch live analyze-microfreak-sentinel \
work/cc-before.json work/cc-after.json work/cc-plan.json work/cc-analysis.jsonThis is no longer the primary MicroFreak mapping path. Firmware-5 preset bodies are self-describing after MIDI unpacking, so normal research can read all saved slots and edit tagged parameters without requiring a device-button press:
freak-patch set-microfreak-structured-value \
patch-42-direct.json VCF.Cutoff 0.5 edited.json
freak-patch set-microfreak-sequence-note \
patch-42-direct.json A 1 1 60 sequence-edited.json
freak-patch set-microfreak-sequence-velocity \
patch-42-direct.json A 1 1 100 sequence-velocity-edited.json
freak-patch set-microfreak-sequence-status \
patch-42-direct.json A 1 tie sequence-tie-edited.json
freak-patch set-microfreak-sequence-automation \
patch-42-direct.json A 1 1 64 sequence-automation-edited.json
freak-patch set-microfreak-sequence-destination \
patch-42-direct.json A 1 filter.cutoff sequence-destination-edited.jsonBoth 64-step Sequence A/B blocks are retained in JSON. The four note slots are
editable; source-specific high-byte non-note tokens are shown separately as
note_bytes and round-trip exactly. Four raw 0..127 velocities are editable.
The four 8-bit automation values and their presence mask are also explicit and
editable. Each pattern's following 18-byte trailer is retained losslessly. Its
first four little-endian words are now exposed as the automation lanes'
operation-41 destination addresses, with mapped parameter names and FFFF as
unused. All 17 nonempty destination addresses in the connected 320-preset bank
were already mapped live words. Hardware playback of MotivSeq then matched
lanes 1..3 at 0101, 0102, and 0602 to outgoing cutoff CC 23, resonance
CC 83, and envelope-decay CC 106 respectively. Sequence byte 12 is exposed as
note_status: 0 = rest,
1 = trigger, and 2 = tie. The mapping was verified by MIDI-clock playback
and the MicroFreak's own outgoing Note On/Off boundaries; note_event_code
retains the exact raw byte. Trailer byte 8 is the per-pattern gate percentage:
a guarded 50 to 10 sentinel moved Note Off from clock 4 to the Note On
clock. Trailer byte 9 mirrors the Seq.Length value but is not authoritative;
changing only it did not shorten playback, while changing the tagged field to
its minimum produced an exact four-step loop. JSON now exposes the UI domains
as Seq.GateLen = 10..90 and Seq.Length = 4..64, and the normal editor keeps
both trailer mirrors synchronized. The final two bytes in each step and
trailer bytes 10..17 remain raw-only.
Normal MIDI Control Center startup is not a current-patch reader. Two passive firmware-5 traces each fetched 512 preset headers plus the complete sample/wavetable inventory, but zero preset bodies or active edit buffers. The saved-slot backend is therefore the proven MicroFreak patch path today; the separate current-buffer command remains protocol research.
microfreak-sequence-playback-direct provides a behavioral partial-current
fallback for the selected active pattern. It temporarily selects USB clock,
sends Start/Clock plus a held transposition note, and records the MicroFreak's
own outgoing notes, velocities, automation CCs, and clock-relative boundaries
as JSON. With --source-slot, --recovery-slot is mandatory; the command
recalls that recovery slot and requires all 384 live words to match the
preflight baseline. Clock Source is backed up, restored, and read back in all
successful captures. This is not labelled a lossless Sequence A/B object dump:
inactive/raw bytes and selecting both patterns remain open.
The remaining firmware-backed SysEx work is tracked explicitly in
docs/microfreak-firmware-notes.md and docs/transport-roadmap.md. All seven
read-only operation-49/6 kind-0x13 status selectors now have raw hardware
captures and a decoded JSON command:
freak-patch microfreak-status-records-direct status-records.jsonThe leading remaining avenues are caller analysis for kinds 0x1E and 0x18
and resolution of computed indirect dispatches that might reach the active
Sequence A/B object. Operation 4C remains disabled pending readback/rollback
proof, and operation 53 remains an unsafe static-only surface that must not be
hardware-probed yet.
Adding --live-snapshot-every N samples operation 41 at exact external-clock
boundaries. By default it reads all 384 words; repeated --live-field options
restrict the trace to named fields and all their aliases for finer timing. A
96-clock hardware trace of MotivSeq captured 102 automation CCs while the
targeted live words tracked three saved automation destinations. The aliases
can differ slightly while smoothed motion is advancing because they are read
serially; stable preset and recovery reads still agree exactly.
The direct MiniFreak Collage read transport is available with:
freak-patch pull-current active.json
freak-patch pull-minifreak 1 slot-1.jsonThe first command reads the active edit buffer. The second reads durable slot
- Those can differ even when they have the same preset name. MiniFreak V must be installed because its binary currently supplies the Collage protobuf schema, but MiniFreak V is not launched and does not perform the transfer.
One hundred fifty-eight exact corpus-mapped fields can be edited offline in a direct-read JSON document and written to the active buffer. Forty use compact session deltas; the other 118 use the activating whole-resource transaction. The command updates the signed 16-bit value and regenerates the payload checksum:
freak-patch set-minifreak-json active.json filter.cutoff 0.5 edited.json
freak-patch push-current edited.json backups/active-before.bin \
--i-understand-this-writes
freak-patch push-slot edited-slot.json 256 backups/slot-256-before.bin \
--i-understand-this-writesThe session-delta keys cover oscillator tuning/shape/volume, glide and bend
range, the filter, envelopes, LFOs, macros, and voice controls. Mapped FX,
keyboard, sequencer, and arpeggiator fields automatically use whole-resource
activation.
freak-patch capabilities minifreak reports the current boundary, while
pull-current includes every key, byte offset, .mnfx name, encoding, session
ID candidate, and live_session_write status in JSON. push-current refuses
every byte difference outside the 158 mapped fields,
backs up the current 3,328-byte buffer, selects session delta or resource store,
and
requires exact full-buffer readback. It automatically restores the backup on
failure. A successful edit remains active-buffer-only; save it on the hardware
if it should survive preset recall or power cycling.
Persistent writes to occupied MiniFreak slots are supported with a fresh raw backup, exact readback, and automatic restore on failure. Empty-slot creation is disabled: firmware 4.0.1 accepts an Init payload but its resource-remove operation returns an I/O error, so the original empty state cannot yet be restored programmatically.
Hardware JSON keeps each mapped normalized value beside its signed 16-bit
raw value, byte offset, and encoding. The 3,328-byte payload is retained
losslessly; checksum byte 4 is regenerated so the XOR of the complete payload
is 0xff.
Bulk reverse-engineering tools avoid serial knob-by-knob work:
freak-patch sentinel generate presets/minifreak-default-base.mnfx work/sentinel
freak-patch sentinel collect-corpus /path/to/mnfx-exports work/corpus
freak-patch sentinel analyze-corpus work/corpus/corpus.json -o work/corpus/map.json
freak-patch sentinel verify-session-map work/corpus/session-backup.bin \
--i-understand-this-writesThe corpus analyzer pairs uniquely named .mnfx exports with rearranged device
slots, tests multiple numeric encodings, and ranks binary offsets. The session
probe assigns unique sentinels to all mapped IDs in one shot, locates their
actual offsets independently, and requires exact baseline restoration.
Change a live parameter using Arturia's documented CC surface:
freak-patch live controls minifreak
freak-patch live controls microfreak
freak-patch live set minifreak filter.cutoff 42Live CC is explicitly separate from patch storage: it changes the active sound, does not save a slot, and provides no readback.
List and read MicroFreak tables without changing the device:
freak-patch wavetable list 1
freak-patch wavetable pull 1 1 ney.jsonPrepare a MicroFreak upload artifact offline from an exact-format WAV:
freak-patch wavetable prepare microfreak table.wav table.mfw --name "My Table"The WAV must be mono PCM16, 32 kHz, and exactly 8,192 samples: 32 cycles of
256 samples. Upload either the prepared .mfw or a lossless JSON pull:
freak-patch wavetable push 1 2 table.mfw backups/slot-2.mfw \
--i-understand-this-writes
freak-patch push-microfreak-wavetable-direct 2 table.mfw backups/slot-2-direct.mfw \
--i-understand-this-writesArbitrary upload to a previously empty slot is hardware-verified through both the compatibility and independent backends. A failed or mismatched write restores an occupied backup or clears a previously empty slot. The lower-level same-content transport probe remains available for diagnostics.
MiniFreak raw wavetable validation currently accepts the format observed by
the open-source MiniFreak converter: 189 frames of 512 signed 24-bit samples.
The Collage schema exposes wavetable resources and the device reports 32 fwt
entries, but MiniFreak firmware 4.0.1 does not expose user wavetable import.
No MiniFreak wavetable write command is enabled.
The original commands remain available:
freak-patch build recipe.json --base exported-init.mnfx -o patch.mnfx
freak-patch show patch.mnfx
freak-patch dump patch.mnfx
freak-patch diff a.mnfx b.mnfxUse an Init preset exported from your own MiniFreak V installation as the
base. Local .mnfx, .mfp, and wavetable exports are ignored by Git so vendor
and personal preset data are not accidentally published.
PYTHONPATH=src python3 -m unittest discover -s tests -vSee Open-source landscape, Transport roadmap, and the evidence-ranked MicroFreak firmware notes. The separate firmware-to-SysEx analysis playbook records the reusable method and MiniFreak handoff breadcrumbs. The separate MIDI Control Center analysis records host-side framing, wavetable, and subcommand-dictionary findings. A parallel MiniFreak firmware research TODO tracks both analysis passes while keeping desktop and hardware claims apart.