gamebook: load expanded books from PSRAM

This commit is contained in:
clement
2026-06-20 09:31:05 +02:00
parent fd7af95bcf
commit e37fa9ded2
+16 -3
View File
@@ -9,6 +9,7 @@
#include "cJSON.h" #include "cJSON.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_heap_caps.h"
#include "display_ui.h" #include "display_ui.h"
#include "media_manager.h" #include "media_manager.h"
@@ -17,7 +18,7 @@ static const char *TAG = "gamebook";
#define GAMEBOOK_DIR "/sdcard/gamebook" #define GAMEBOOK_DIR "/sdcard/gamebook"
#define LIBRARY_JSON GAMEBOOK_DIR "/library.json" #define LIBRARY_JSON GAMEBOOK_DIR "/library.json"
#define JSON_MAX (64 * 1024) /* per-file JSON sanity cap */ #define JSON_MAX (256 * 1024) /* expanded books are ~80 KB of JSON */
#define LIB_MAX 6 /* matches the display tile grid */ #define LIB_MAX 6 /* matches the display tile grid */
typedef enum { GB_OFF, GB_LIBRARY, GB_STORY } gb_mode_t; typedef enum { GB_OFF, GB_LIBRARY, GB_STORY } gb_mode_t;
@@ -39,6 +40,10 @@ static volatile gb_mode_t s_mode = GB_OFF;
bool gamebook_active(void) { return s_mode != GB_OFF; } bool gamebook_active(void) { return s_mode != GB_OFF; }
/* PSRAM allocators for cJSON — book trees are large (see gamebook_init). */
static void *gb_malloc(size_t sz) { return heap_caps_malloc(sz, MALLOC_CAP_SPIRAM); }
static void gb_free(void *p) { heap_caps_free(p); }
/* Read a whole JSON file from the SD into a cJSON tree (caller frees). */ /* Read a whole JSON file from the SD into a cJSON tree (caller frees). */
static cJSON *load_json(const char *path) static cJSON *load_json(const char *path)
{ {
@@ -48,13 +53,13 @@ static cJSON *load_json(const char *path)
long sz = ftell(f); long sz = ftell(f);
rewind(f); rewind(f);
if (sz <= 0 || sz > JSON_MAX) { fclose(f); ESP_LOGW(TAG, "%s size %ld", path, sz); return NULL; } if (sz <= 0 || sz > JSON_MAX) { fclose(f); ESP_LOGW(TAG, "%s size %ld", path, sz); return NULL; }
char *buf = malloc((size_t)sz + 1); char *buf = heap_caps_malloc((size_t)sz + 1, MALLOC_CAP_SPIRAM);
if (!buf) { fclose(f); return NULL; } if (!buf) { fclose(f); return NULL; }
size_t rd = fread(buf, 1, (size_t)sz, f); size_t rd = fread(buf, 1, (size_t)sz, f);
fclose(f); fclose(f);
buf[rd] = '\0'; buf[rd] = '\0';
cJSON *root = cJSON_Parse(buf); cJSON *root = cJSON_Parse(buf);
free(buf); heap_caps_free(buf);
if (!root) ESP_LOGW(TAG, "malformed JSON: %s", path); if (!root) ESP_LOGW(TAG, "malformed JSON: %s", path);
return root; return root;
} }
@@ -285,6 +290,14 @@ void gamebook_stop(void)
void gamebook_init(void) void gamebook_init(void)
{ {
/* Keep the large book trees in PSRAM — an expanded book is ~80 KB of JSON,
* whose parsed node tree would exhaust the internal heap on this firmware. */
static cJSON_Hooks hooks = {
.malloc_fn = gb_malloc,
.free_fn = gb_free,
};
cJSON_InitHooks(&hooks);
/* Only register the pad hook here — the SD and media_manager aren't up yet /* Only register the pad hook here — the SD and media_manager aren't up yet
* at this point in boot. main.c calls gamebook_start() once they are, to * at this point in boot. main.c calls gamebook_start() once they are, to
* boot into the library. */ * boot into the library. */