- Fix SD card MISO pin (was using display MISO instead of SD MISO) - Add FatFS mounting after SD card initialization - Restore SPI baudrate on all SD init failure paths - Add case-insensitive .lua file extension check - Filter out hidden 8.3 filename entries - Add SPI speed management functions for shared SPI bus - Wrap all FatFS operations with SPI speed switching - Restore display SPI speed (32 MHz) after SD operations - Add debug output to Lua game loader This fixes slow display refresh when SD card is present and enables reliable Lua game loading from SD card /games directory.
212 lines
6.6 KiB
C++
212 lines
6.6 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 "sd_card.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';
|
|
|
|
// Set SPI speed for SD card operations
|
|
uint prev_speed = sd_card_set_spi_speed();
|
|
|
|
// 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);
|
|
sd_card_restore_spi_speed(prev_speed);
|
|
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);
|
|
|
|
// Restore SPI speed
|
|
sd_card_restore_spi_speed(prev_speed);
|
|
|
|
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");
|
|
|
|
// Set SPI speed for SD card operations
|
|
uint prev_speed = sd_card_set_spi_speed();
|
|
|
|
// 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");
|
|
sd_card_restore_spi_speed(prev_speed);
|
|
return 0;
|
|
}
|
|
|
|
// Scan for .lua files
|
|
while (true) {
|
|
fr = f_readdir(&dir, &fno);
|
|
if (fr != FR_OK) {
|
|
printf("LuaGameLoader: Error reading directory (error %d)\n", fr);
|
|
break;
|
|
}
|
|
if (fno.fname[0] == 0) {
|
|
printf("LuaGameLoader: End of directory reached\n");
|
|
break; // End of directory
|
|
}
|
|
|
|
printf("LuaGameLoader: Found entry: %s (attrib=0x%02X)\n", fno.fname, fno.fattrib);
|
|
|
|
// Skip directories
|
|
if (fno.fattrib & AM_DIR) {
|
|
printf("LuaGameLoader: Skipping directory\n");
|
|
continue;
|
|
}
|
|
|
|
// Skip hidden files (these are short 8.3 filename entries)
|
|
if (fno.fattrib & AM_HID) {
|
|
printf("LuaGameLoader: Skipping hidden file (short filename entry)\n");
|
|
continue;
|
|
}
|
|
|
|
// Check for .lua extension (case-insensitive)
|
|
size_t len = strlen(fno.fname);
|
|
printf("LuaGameLoader: Filename length: %d\n", len);
|
|
if (len < 5) {
|
|
printf("LuaGameLoader: Filename too short\n");
|
|
continue;
|
|
}
|
|
|
|
// Case-insensitive check for .lua or .LUA
|
|
const char* ext = fno.fname + len - 4;
|
|
if (strcasecmp(ext, ".lua") != 0) {
|
|
printf("LuaGameLoader: Not a .lua file (ext=%s)\n", ext);
|
|
continue;
|
|
}
|
|
|
|
printf("LuaGameLoader: Valid .lua file!\n");
|
|
|
|
// 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);
|
|
|
|
// Restore SPI speed
|
|
sd_card_restore_spi_speed(prev_speed);
|
|
|
|
printf("LuaGameLoader: Registered %d Lua games\n", count);
|
|
return count;
|
|
}
|