From c1c8174b2768e450c9d66ba057387e91bfd014f2 Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Sat, 19 Sep 2026 21:38:09 -0500 Subject: [PATCH] Use CUTSCENE/INSERT item names as full asset paths directly cutsceneCutsceneResolve/cutsceneInsertResolveAndSplice no longer inject a "cutscenes/%s.jsonc" prefix/suffix around an item's "name" field - matching cutsceneSystemLoad, which already takes a full path. Fixes a latent double-prefix bug in initial.jsonc's CUTSCENE reference (also had a "jsonsc" typo) that this change would otherwise have exposed. Co-Authored-By: Claude Sonnet 5 --- assets/battle/moves.jsonc | 2 +- assets/cutscenes/initial.jsonc | 2 +- src/dusk/rpg/cutscene/item/cutsceneitem.c | 20 ++++-------- src/dusk/rpg/cutscene/item/cutsceneitem.h | 39 +++++++++++------------ 4 files changed, 27 insertions(+), 36 deletions(-) diff --git a/assets/battle/moves.jsonc b/assets/battle/moves.jsonc index 83d6120b..f6870e77 100644 --- a/assets/battle/moves.jsonc +++ b/assets/battle/moves.jsonc @@ -5,7 +5,7 @@ "power": 12, "mpCost": 4, "target": "SINGLE_ENEMY", - "cutscene": "battle/fire" + "cutscene": "cutscenes/battle/fire.jsonc" } } } diff --git a/assets/cutscenes/initial.jsonc b/assets/cutscenes/initial.jsonc index c4be0d23..75068b7b 100644 --- a/assets/cutscenes/initial.jsonc +++ b/assets/cutscenes/initial.jsonc @@ -62,7 +62,7 @@ }, { "type": "CUTSCENE", - "name": "main_menu" + "name": "cutscenes/main_menu.jsonc" } ] } diff --git a/src/dusk/rpg/cutscene/item/cutsceneitem.c b/src/dusk/rpg/cutscene/item/cutsceneitem.c index 130b0757..149b9c04 100644 --- a/src/dusk/rpg/cutscene/item/cutsceneitem.c +++ b/src/dusk/rpg/cutscene/item/cutsceneitem.c @@ -411,10 +411,7 @@ bool_t cutsceneCutsceneUpdate( } cutscene_t * cutsceneCutsceneResolve(const char_t *name) { - char_t path[ASSET_FILE_NAME_MAX]; - stringFormat(path, sizeof(path), "cutscenes/%s.jsonc", name); - - assetentry_t *entry = assetLock(path, ASSET_LOADER_TYPE_JSON, NULL); + assetentry_t *entry = assetLock(name, ASSET_LOADER_TYPE_JSON, NULL); errorret_t result = assetRequireLoaded(entry); if(errorIsNotOk(result)) { assetUnlockEntry(entry); @@ -459,11 +456,11 @@ errorret_t cutsceneCutsceneRefLoad( errorThrow("Cutscene item (%s): missing 'name'", itemTypeLabel); } - // Only validate that the eventual "cutscenes/.jsonc" path would - // fit - the reference itself is not resolved/locked until this item - // actually runs (see cutsceneCutsceneStart/cutsceneCutsceneResolve). - int32_t pathLen = stringFormat(NULL, 0, "cutscenes/%s.jsonc", name); - if(pathLen < 0 || (size_t)pathLen >= ASSET_FILE_NAME_MAX) { + // Only validate that name itself would fit - the reference itself is + // not resolved/locked until this item actually runs (see + // cutsceneCutsceneStart/cutsceneCutsceneResolve). + int32_t nameLen = stringFormat(NULL, 0, "%s", name); + if(nameLen < 0 || (size_t)nameLen >= CUTSCENE_CUTSCENE_REF_NAME_MAX) { errorThrow("Cutscene item (%s): 'name' too long", itemTypeLabel); } @@ -492,10 +489,7 @@ bool_t cutsceneInsertUpdate( } void cutsceneInsertResolveAndSplice(const char_t *name) { - char_t path[ASSET_FILE_NAME_MAX]; - stringFormat(path, sizeof(path), "cutscenes/%s.jsonc", name); - - assetentry_t *entry = assetLock(path, ASSET_LOADER_TYPE_JSON, NULL); + assetentry_t *entry = assetLock(name, ASSET_LOADER_TYPE_JSON, NULL); errorret_t result = assetRequireLoaded(entry); if(errorIsNotOk(result)) { assetUnlockEntry(entry); diff --git a/src/dusk/rpg/cutscene/item/cutsceneitem.h b/src/dusk/rpg/cutscene/item/cutsceneitem.h index 5d04d431..006b79a0 100644 --- a/src/dusk/rpg/cutscene/item/cutsceneitem.h +++ b/src/dusk/rpg/cutscene/item/cutsceneitem.h @@ -60,9 +60,8 @@ typedef struct cutscene_s cutscene_t; -// Max length of a CUTSCENE/INSERT item's "name" field - bounded by what -// fits in the "cutscenes/.jsonc" path built from it (see -// cutsceneCutsceneRefLoad), so this is never reached in practice. +// Max length of a CUTSCENE/INSERT item's "name" field - it's the full +// asset path to load, so this is bounded by ASSET_FILE_NAME_MAX directly. #define CUTSCENE_CUTSCENE_REF_NAME_MAX ASSET_FILE_NAME_MAX // A CUTSCENE (or INSERT) item's target. A C-authored cutscene_t* set @@ -230,7 +229,7 @@ void cutsceneCutsceneStart( /** * Resolves a JSON-authored CUTSCENE item's referenced cutscene by name: - * locks+loads assets/cutscenes/.jsonc, parses it into + * locks+loads name as a plain ASSET_LOADER_TYPE_JSON asset, parses it into * CUTSCENE_SYSTEM.loadedItems/loadedScene, then unlocks the asset entry * like any other asset - the parsed items don't reference the doc past * that (see cutsceneInsertResolveAndSplice's matching comment for why). @@ -240,8 +239,7 @@ void cutsceneCutsceneStart( * error overlay (matching cutsceneSystemLoad's runtime-failure handling) * and returns NULL. * - * @param name Bare cutscene name (without "cutscenes/" prefix or - * ".jsonc" suffix). + * @param name Full asset path to the referenced cutscene's JSON file. * @return The resolved cutscene, or NULL on failure. */ cutscene_t * cutsceneCutsceneResolve(const char_t *name); @@ -265,10 +263,9 @@ bool_t cutsceneCutsceneUpdate( * (rather than its own file) since it's tightly bound to the asset/loader * system itself. Only stores the 'name' string (which points into the * cutscene asset's still-alive yyjson doc, so no copy is needed) - - * resolving it to the referenced assets/cutscenes/.jsonc asset is - * deferred to cutsceneCutsceneStart, the first time this item actually - * runs, so a cutscene referencing many others doesn't pay to load every - * one of them up front. + * resolving it to the referenced asset is deferred to cutsceneCutsceneStart, + * the first time this item actually runs, so a cutscene referencing many + * others doesn't pay to load every one of them up front. * * @param itemObj The item's JSON object. * @param item Destination item, already zeroed by the caller. @@ -281,9 +278,10 @@ errorret_t cutsceneCutsceneLoad( /** * Shared JSON parsing body for both CUTSCENE and INSERT items - both need - * exactly a 'name' string field, validated to fit the eventual - * "cutscenes/.jsonc" asset path. Only stores the name (see - * cutsceneCutsceneLoad for why resolving is deferred). + * exactly a 'name' string field, the full asset path to the referenced + * cutscene's JSON file, validated to fit CUTSCENE_CUTSCENE_REF_NAME_MAX. + * Only stores the name (see cutsceneCutsceneLoad for why resolving is + * deferred). * * @param itemObj The item's JSON object. * @param outRef Destination ref, already zeroed by the caller. @@ -318,19 +316,18 @@ void cutsceneInsertStart( /** * Resolves a JSON-authored INSERT item's referenced cutscene by name and * splices it into the running cutscene (CUTSCENE_SYSTEM.scene): locks+ - * loads assets/cutscenes/.jsonc as a plain ASSET_LOADER_TYPE_JSON - * asset, parses it into a small scratch buffer local to this call - * (capacity CUTSCENE_INSERT_ITEMS_MAX) straight out of the asset entry's - * own doc, then unlocks the entry like any other asset - nothing needs - * to keep the doc itself resident past the parse: every cutscene item - * field that could reference it owns its string data by value (see + * loads name as a plain ASSET_LOADER_TYPE_JSON asset, parses it into a + * small scratch buffer local to this call (capacity + * CUTSCENE_INSERT_ITEMS_MAX) straight out of the asset entry's own doc, + * then unlocks the entry like any other asset - nothing needs to keep the + * doc itself resident past the parse: every cutscene item field that could + * reference it owns its string data by value (see * cutscenecutsceneref_t/cutscenemarker_t's doc comments), and * cutsceneSystemInsertCutscene copies the scratch items into the running * scene's own array right after. On failure, opens the fatal error * overlay, matching cutsceneCutsceneResolve. * - * @param name Bare cutscene name (without "cutscenes/" prefix or - * ".jsonc" suffix). + * @param name Full asset path to the referenced cutscene's JSON file. */ void cutsceneInsertResolveAndSplice(const char_t *name);