- Integrated Lua 5.4 engine (32-bit mode for embedded ARM) - Created LuaGame wrapper class implementing Game interface - Added C++ bindings exposing renderer, game state, and input to Lua - Implemented SD card loader for automatic .lua game discovery - Updated GameLauncher to support std::function for lambda captures - Made Game class members public for Lua bindings access - Added example Lua games: counter, snake, bouncing ball - Included comprehensive API documentation Games can now be written as .lua text files on SD card and loaded without recompilation. Build size: 747KB UF2, Lua VM uses ~50-80KB RAM.
3.3 KiB
3.3 KiB
Game Save System Implementation Plan (RP2350/RP2040)
This document outlines the strategy for implementing non-volatile game save states using the onboard Flash memory.
1. Objectives
- Persistence: Save game state so progress is kept after power-off.
- Multi-game Support: Allow multiple games to have independent saves without overwriting each other.
- Safety: Ensure flash writes don't interfere with program execution or interrupt handlers.
- Wear Leveling: Minimize unnecessary writes to prolong flash lifespan.
2. Flash Memory Mapping
The RP2350 on this board has 8MB of Flash memory mapped starting at XIP_BASE (0x10000000). We will reserve the top section for data.
| Memory Range | Purpose | Size |
|---|---|---|
0x10000000 - 0x106FFFFF |
Program Code, Fonts, Assets | 7MB |
0x10700000 - 0x107EFFFF |
Reserved/Future | 960KB |
0x107F0000 - 0x107F0FFF |
Save Registry | 4KB (1 Sector) |
0x107F1000 - 0x107FFFFF |
Game Save Slots | 60KB (15 Slots) |
3. Architecture Components
A. PersistentStorage Manager (lib/persistent_storage.h)
A singleton or static helper class that handles raw flash operations.
bool save_exists(const char* game_id)bool save_data(const char* game_id, void* data, size_t size)bool load_data(const char* game_id, void* buffer, size_t size)
B. Updated Game Interface (lib/game.h)
Add methods to the base class to standardize how state is handled.
virtual const char* get_game_id() const = 0;
virtual size_t get_save_data(void* buffer, size_t max_size) = 0;
virtual void load_save_data(const void* buffer, size_t size) = 0;
C. Save Registry
To avoid collisions, the first sector (0x107F0000) will store a lookup table:
struct RegistryEntry {
char game_id[16];
uint32_t sector_index;
uint32_t data_size;
uint32_t checksum;
};
4. Implementation Steps
- Create
PersistentStorage:- Implement flash sector erasing and programming using
pico/flash.h. - Include interrupt protection using
save_and_disable_interrupts().
- Implement flash sector erasing and programming using
- Update
GameBase Class:- Modify
lib/game.hto include the virtual persistence methods.
- Modify
- Implement in Monopoly:
- Create a
MonopolySaveStatestruct (POD - Plain Old Data). - Populate it from game variables in
get_save_data.
- Create a
- Integrate with Launcher:
- Modify
GameLauncherto check for existing saves when a game is selected. - Modify the main loop to trigger
save_dataat logical points (e.g., end of turn/exit).
- Modify
5. Collision Avoidance Logic
- When a game requests a save for the first time, the
PersistentStoragemanager scans the Registry. - If the
game_idisn't found, it allocates the next available 4KB sector. - It writes the specific
game_idand its assignedsector_indexback to the Registry sector. - Sequential saves for that ID will always use that specific sector.
6. Important Notes
- Flash Wear: Each sector supports ~100k erase cycles. Total lifespan depends on frequency of saves.
- Alignment: Data must be programmed in multiples of
FLASH_PAGE_SIZE(256 bytes). - Recursion: Never call flash functions from within an interrupt or callback that might be triggered during a write.