|
Pokered Save Editor 2
Pokemon Red & Blue save file editor - Qt 6 C++/QML
|
The DEBUG-only automation harness (added 2026-07-09, feat(app): debug-only automation harness) turns manual iteration into a fast, hands-off loop: launch straight to the screen you're editing with a save already loaded, then preview QML edits live via hot-reload — no clicking through the New File modal, no relaunch on every tweak. All of it is gated behind QT_DEBUG; release builds don't contain any of it (the servers/flags compile to no-ops).
Sources: projects/app/src/boot/debuglaunch.cpp (launch flags), projects/app/src/boot/debugserver.cpp (TCP control channel), projects/app/ui/window/mainwindow.cpp (setupHotReload, reloadQml, saveShot, the QmlDiskInterceptor).
For any QML/screen work, prefer this over the rebuild-relaunch cycle:
| Flag | Effect |
|---|---|
| --sav <path> / --save <path> | Load this .sav on startup (setPath + reopenFile). |
| --screen <name> | After load, dismiss the startup New File modal and navigate to <name>. |
| --select <spec> | Selection for screens that need one — party:N for pokemonDetails (opens party mon N). random / source:id reserved (not fully wired). |
| --hot | Enable live QML hot-reload (load QML from the source tree + watch it). |
| --shot <path> | After navigating (≈600 ms settle), grab the view to this PNG. Works backgrounded. |
| --minimized | Start minimized (drive/screenshot without the window popping up / stealing focus). |
A newline-delimited-JSON TCP server (DEBUG-only, GUI thread, safe to touch QML/widgets). Send one JSON object per line, get one reply per line. Verbs:
Drive it from PowerShell with a TcpClient (write a line, read a line). Great for scripted checks: navigate, set a field, shot, then eyeball.
--hot installs a QmlDiskInterceptor (a QQmlAbstractUrlInterceptor) that redirects qrc:/…qml|js URLs to the matching file on the source tree (PSE_QML_SOURCE_DIR), so the running app loads QML from disk instead of the baked qrc. A QFileSystemWatcher over <root>/ui/**/*.qml|*.js fires on save (debounced 160 ms; re-arms the watch because editors rewrite/rename on save) and calls reloadQml().
reloadQml() = engine->clearComponentCache() + tear down and re-setSource(App.qml) — i.e. it rebuilds the whole QML tree from the root. Per-file partial reload is deliberately NOT attempted — in a single-QQuickWidget app the engine caches whole compiled components; reloading just the changed file would leave consumers of a shared component holding a stale cached copy (a subtle "quality loss" the project forbids). So the reload is always full and reliable.
It preserves context (2026-07-10 fix). A full-tree reload otherwise dumps you back at Home + the New File modal. reloadQml() now, before tearing down, records the current screen (reverse-looks-up the top of Router::stack to its registered name); after the reload it polls until App.qml re-seats its startup stack, dismisses the New File modal, and navigates back to that screen (pokemonDetails re-opens party mon 0, since it needs a selection). The loaded save survives automatically — it lives in C++ (Bridge/FileManagement), not QML, so only navigation needs restoring. Net effect: edit a screen's QML, save, and you stay on that screen with your save loaded, changes applied. (Verified over the TCP channel: title → reload → title returns the same screen; Home/modals are intentionally not auto-restored.)