|
Pokered Save Editor 2
Pokemon Red & Blue save file editor - Qt 6 C++/QML
|
How the whole machine fits together, grounded in the actual code. For the quick version see ../context/architecture.md; this is the in-depth account.
A Game Boy Pokemon Red/Blue save file is a fixed 32 KB (0x8000) blob of packed bytes. This app loads that blob, expands it into a tree of friendly C++ objects, lets the user edit those objects through a Qt Quick (QML) UI, then flattens the tree back into the blob – touching only the exact bytes an edit requires. The editing happens on the expanded tree; the raw blob is the source of truth on disk.
Each lower layer is a shared library the next one links against. Dependencies only ever point left.
| Layer | Role | System map |
|---|---|---|
| common | Foundation types and helpers: fixed-width integer aliases, Random, Utility, the QML-ownership guard. | common.md |
| db | The game's static data (every Pokemon, move, item, map, font glyph, ...) loaded from JSON assets into singleton databases. Read-only reference data, independent of any save file. | db.md (pending) |
| savefile | The byte-exact engine: parse the raw save, expand it into objects, flatten it back. Knows nothing about UI. | savefile.md (pending) |
| app | The Qt executable: boot sequence, the Bridge that exposes everything to QML, image/engine providers, QML list models, and the QML UI itself. | app.md (pending) |
main() is deliberately tiny – it delegates to boot() and execs the returned app (app/src/main.cpp, app/src/boot/boot.cpp):
Order matters: the databases must be fully built before any QML type that reads them is registered, and registration must precede the window that instantiates QML.
Once running, a single context property brg is QML's doorway to all of C++. The core chain QML traverses:
Bridge also hangs many non-save objects off the same brg root: the game databases used by pickers (fonts, randomPlayerName, ...), the Settings, the Router, and a fleet of QML list models (pokedexModel, mapSelectModel, itemMarketModel, ...) that adapt C++ data into Qt item-model form for QML views.
For QML to read straight through that pointer chain, every QObject type in it must be a complete type at the property's MOC translation unit – fully included, not forward-declared, not Q_DECLARE_OPAQUE_POINTER'd. That single rule (and why it was the cause of the long-standing "undefined chain" bug) is documented in ../decisions/architecture.md and ../reference/qt-patterns.md.
SaveFile (savefile/src/pse-savefile/savefile.h) owns the three pieces – the raw data, the dataExpanded tree, and a SaveFileToolset for byte-level access – and exposes the verbs: expandData(), flattenData(), resetData(), eraseExpansion(), and randomizeExpansion() (the constrained "still playable" randomizer).
SaveFile::flattenData() is documented in-code as: "Flatten expansion back to the save file, overwriting its current contents with only data that's strictly necessary. A critical rule." That is the heart of the project: an edit flips only the exact bytes it must and leaves every other byte untouched – no normalizing, no repacking, no rewriting regions that didn't change. Corrupting an otherwise-valid save is the worst outcome the app can produce. See ../context/principles.md -> "Save File Integrity Is Sacred".