Files
basic1/games/lua_game_loader.cpp
Adolfo Reyna 8d176925f8 Fix garbage characters in hardware game names
Store game names and descriptions in persistent LuaGameFactoryData
structure instead of local stack variables to prevent dangling pointers.
Same fix as emulator version.
2026-02-07 13:09:16 -05:00

166 lines
5.1 KiB
C++

// ============================================================================
// LUA GAME LOADER - IMPLEMENTATION
// ============================================================================
// Discovers Lua scripts on SD card and integrates with game launcher
#include "lua_game_loader.h"
#include "lua_game.h"
#include <stdio.h>
#include <string.h>
#include <vector>
extern "C" {
#include "ff.h"
}
// Structure to hold script path for factory closure
struct LuaGameFactoryData {
char script_path[256];
char name[64];
char description[128];
};
static std::vector<LuaGameFactoryData*> factory_data_list;
// Factory wrapper that captures script path
static Game* lua_game_factory_wrapper(uint16_t width, uint16_t height,
LowLevelRenderer* renderer, LowLevelGUI* gui,
InputManager* input_manager, void* user_data) {
LuaGameFactoryData* data = (LuaGameFactoryData*)user_data;
return new LuaGame(data->script_path, width, height, renderer, gui, input_manager);
}
bool LuaGameLoader::parse_metadata(const char* script_path, char* name, char* description) {
FIL fil;
FRESULT fr;
// Default name from filename
const char* filename = strrchr(script_path, '/');
if (filename) {
filename++;
} else {
filename = script_path;
}
// Remove .lua extension for default name
strncpy(name, filename, 63);
name[63] = '\0';
char* ext = strstr(name, ".lua");
if (ext) *ext = '\0';
// Default empty description
description[0] = '\0';
// Try to open file and parse metadata comments
fr = f_open(&fil, script_path, FA_READ);
if (fr != FR_OK) {
printf("LuaGameLoader: Warning - could not open %s for metadata\n", script_path);
return false;
}
// Read first 512 bytes to look for metadata comments
char buffer[512];
UINT bytes_read;
fr = f_read(&fil, buffer, sizeof(buffer) - 1, &bytes_read);
f_close(&fil);
if (fr != FR_OK) {
return false;
}
buffer[bytes_read] = '\0';
// Parse metadata comments: -- NAME: Game Name
char* line = buffer;
while (line && (line - buffer) < bytes_read) {
char* next_line = strchr(line, '\n');
if (next_line) *next_line = '\0';
// Check for -- NAME:
if (strncmp(line, "-- NAME:", 8) == 0) {
const char* value = line + 8;
while (*value == ' ') value++; // Skip spaces
strncpy(name, value, 63);
name[63] = '\0';
}
// Check for -- DESC:
else if (strncmp(line, "-- DESC:", 8) == 0) {
const char* value = line + 8;
while (*value == ' ') value++;
strncpy(description, value, 127);
description[127] = '\0';
}
if (next_line) {
line = next_line + 1;
} else {
break;
}
}
return true;
}
int LuaGameLoader::register_all_games(GameLauncher* launcher) {
DIR dir;
FILINFO fno;
FRESULT fr;
int count = 0;
printf("LuaGameLoader: Scanning /games directory for .lua scripts...\n");
// Open /games directory
fr = f_opendir(&dir, "/games");
if (fr != FR_OK) {
printf("LuaGameLoader: Could not open /games directory (error %d)\n", fr);
printf("LuaGameLoader: Make sure SD card is mounted and /games exists\n");
return 0;
}
// Scan for .lua files
while (true) {
fr = f_readdir(&dir, &fno);
if (fr != FR_OK || fno.fname[0] == 0) break; // End of directory
// Skip directories
if (fno.fattrib & AM_DIR) continue;
// Check for .lua extension
size_t len = strlen(fno.fname);
if (len < 5 || strcmp(fno.fname + len - 4, ".lua") != 0) continue;
// Build full path
char script_path[256];
snprintf(script_path, sizeof(script_path), "/games/%s", fno.fname);
// Create factory data (persistent for game lifetime)
LuaGameFactoryData* data = new LuaGameFactoryData();
strncpy(data->script_path, script_path, sizeof(data->script_path) - 1);
data->script_path[sizeof(data->script_path) - 1] = '\0';
// Parse metadata directly into persistent storage
parse_metadata(script_path, data->name, data->description);
printf("LuaGameLoader: Found %s - '%s'\n", fno.fname, data->name);
factory_data_list.push_back(data);
// Register with launcher - using lambda factory pattern
launcher->register_game(
data->name,
data->description[0] ? data->description : "Lua Script",
[data](uint16_t width, uint16_t height, LowLevelRenderer* renderer,
LowLevelGUI* gui, InputManager* input_manager) -> Game* {
return new LuaGame(data->script_path, width, height, renderer, gui, input_manager);
}
);
count++;
}
f_closedir(&dir);
printf("LuaGameLoader: Registered %d Lua games\n", count);
return count;
}