diff --git a/idf_zacus/components/gamebook/gamebook.c b/idf_zacus/components/gamebook/gamebook.c index 1562d0e..b5ad0d7 100644 --- a/idf_zacus/components/gamebook/gamebook.c +++ b/idf_zacus/components/gamebook/gamebook.c @@ -9,6 +9,7 @@ #include "cJSON.h" #include "esp_log.h" +#include "esp_heap_caps.h" #include "display_ui.h" #include "media_manager.h" @@ -17,7 +18,7 @@ static const char *TAG = "gamebook"; #define GAMEBOOK_DIR "/sdcard/gamebook" #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 */ 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; } +/* 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). */ static cJSON *load_json(const char *path) { @@ -48,13 +53,13 @@ static cJSON *load_json(const char *path) long sz = ftell(f); rewind(f); 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; } size_t rd = fread(buf, 1, (size_t)sz, f); fclose(f); buf[rd] = '\0'; cJSON *root = cJSON_Parse(buf); - free(buf); + heap_caps_free(buf); if (!root) ESP_LOGW(TAG, "malformed JSON: %s", path); return root; } @@ -285,6 +290,14 @@ void gamebook_stop(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 * at this point in boot. main.c calls gamebook_start() once they are, to * boot into the library. */