84b009c33e
Implements a complete serial upload workflow that allows uploading and immediately testing Lua games via USB serial connection. New Components: - SerialUploader: Receives files via serial, writes to SD card - upload_game.py: Python tool for sending files from host computer - Protocol: Text-based with base64 encoding for reliability Key Features: - Uploads file to /games folder on SD card - Overwrites existing files (FA_CREATE_ALWAYS) - Auto-launches uploaded game immediately - Proper memory cleanup (prevents Lua state conflicts) SD Card Fixes: - Fixed SPI speed management (12.5MHz for SD, 32MHz for display) - Fixed SD write protocol (poll for data response token) - Added speed switching wrappers around all FatFS operations - Cleaned up excessive debug output Game Launcher Improvements: - Added clear_games() to prevent duplicate registrations - Added cleanup in select_game_by_name() to delete old instances - Added exact match priority in game selection - LuaGameLoader now has clear_factory_data() for memory cleanup Integration: - Added serial_uploader to CMakeLists.txt - Integrated into main loop in basic1.cpp - Re-scans games after upload to pick up new files Documentation: - UPLOAD_TOOL.md: Usage instructions - sd_card_best_practices.md: Critical lessons learned Known Issues: - Game launch after upload occasionally causes freeze (needs investigation) - Display may not refresh properly after upload Usage: python upload_game.py games/lua_examples/2048.lua /dev/tty.usbmodem101 Co-Authored-By: Claude <noreply@anthropic.com>
130 lines
4.0 KiB
C++
130 lines
4.0 KiB
C++
// ============================================================================
|
|
// GAME LAUNCHER HEADER
|
|
// ============================================================================
|
|
// Menu system for selecting and launching games
|
|
|
|
#ifndef GAME_LAUNCHER_H
|
|
#define GAME_LAUNCHER_H
|
|
|
|
#include <stdint.h>
|
|
#include <vector>
|
|
#include <functional>
|
|
#include "input_event.h"
|
|
#include "game.h"
|
|
|
|
// Forward declarations
|
|
class LowLevelRenderer;
|
|
class LowLevelGUI;
|
|
class InputManager;
|
|
|
|
/**
|
|
* @brief Game entry in launcher menu
|
|
*/
|
|
struct GameEntry {
|
|
const char* name; // Display name
|
|
const char* description; // Short description
|
|
std::function<Game*(uint16_t, uint16_t, LowLevelRenderer*, LowLevelGUI*, InputManager*)> factory; // Factory function
|
|
};
|
|
|
|
/**
|
|
* @brief Game Launcher - Menu system for game selection
|
|
*
|
|
* Displays a list of available games and allows selection via:
|
|
* - Touch: Tap on game name
|
|
* - Buttons: KEY0 to navigate, KEY1 to select
|
|
*
|
|
* Returns the selected game instance for the main loop to run.
|
|
*/
|
|
class GameLauncher {
|
|
public:
|
|
/**
|
|
* @brief Construct GameLauncher
|
|
* @param width Display width in pixels
|
|
* @param height Display height in pixels
|
|
* @param renderer Pointer to low-level rendering interface
|
|
* @param gui Pointer to GUI drawing primitives
|
|
* @param input_manager Pointer to input manager for capability queries
|
|
*/
|
|
GameLauncher(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager);
|
|
|
|
/**
|
|
* @brief Register a game in the launcher
|
|
* @param name Game display name
|
|
* @param description Short description
|
|
* @param factory Function or lambda to create game instance
|
|
*/
|
|
void register_game(const char* name, const char* description,
|
|
std::function<Game*(uint16_t, uint16_t, LowLevelRenderer*, LowLevelGUI*, InputManager*)> factory);
|
|
|
|
/**
|
|
* @brief Draw the launcher menu
|
|
*/
|
|
void draw();
|
|
|
|
/**
|
|
* @brief Process input event in launcher
|
|
* @param event Input event from InputManager
|
|
* @return true if a game was selected (check get_selected_game())
|
|
*/
|
|
bool update(const InputEvent& event);
|
|
|
|
/**
|
|
* @brief Get the currently selected game instance
|
|
* @return Pointer to selected game, or nullptr if none selected
|
|
*/
|
|
Game* get_selected_game();
|
|
|
|
/**
|
|
* @brief Reset launcher to show menu again
|
|
*/
|
|
void reset();
|
|
|
|
/**
|
|
* @brief Clear all registered games (useful before re-scanning)
|
|
*/
|
|
void clear_games();
|
|
|
|
/**
|
|
* @brief Check if a game is currently selected
|
|
* @return true if game selected, false if in menu
|
|
*/
|
|
bool is_game_selected() const { return selected_game != nullptr; }
|
|
|
|
/**
|
|
* @brief Select a game by name (for programmatic launching)
|
|
* @param name Game name to select (partial match supported)
|
|
* @return true if game was found and launched, false otherwise
|
|
*/
|
|
bool select_game_by_name(const char* name);
|
|
|
|
private:
|
|
uint16_t width;
|
|
uint16_t height;
|
|
LowLevelRenderer* renderer;
|
|
LowLevelGUI* gui;
|
|
InputManager* input_manager;
|
|
|
|
std::vector<GameEntry> games;
|
|
int selected_index; // Currently highlighted game
|
|
Game* selected_game; // Currently running game (nullptr = in menu)
|
|
int current_page; // Current page in pagination
|
|
|
|
// Menu layout constants
|
|
static const int MENU_Y_START = 60;
|
|
static const int MENU_ITEM_HEIGHT = 40;
|
|
static const int MENU_PADDING = 10;
|
|
static const int GAMES_PER_PAGE = 4;
|
|
static const int NAV_BUTTON_Y = 235; // Bottom navigation buttons
|
|
static const int PREV_BUTTON_X = 30;
|
|
static const int NEXT_BUTTON_X = 200;
|
|
static const int BUTTON_WIDTH = 150;
|
|
static const int BUTTON_HEIGHT = 40;
|
|
|
|
// Helper functions for pagination
|
|
int get_total_pages() const;
|
|
int get_page_start_index() const;
|
|
int get_page_end_index() const;
|
|
};
|
|
|
|
#endif // GAME_LAUNCHER_H
|