diff --git a/data/tr2/ship/cfg/TR2X_strings.json5 b/data/tr2/ship/cfg/TR2X_strings.json5 index 7875a5e49..1ec20cd31 100644 --- a/data/tr2/ship/cfg/TR2X_strings.json5 +++ b/data/tr2/ship/cfg/TR2X_strings.json5 @@ -187,6 +187,44 @@ }, ], + "demos": [ + { + "title": "Venice", + "objects": { + "key_1": {"name": "Boathouse Key"}, + "key_2": {"name": "Steel Key"}, + "key_3": {"name": "Iron Key"}, + }, + }, + + { + "title": "Wreck of the Maria Doria", + "objects": { + "key_1": {"name": "Rest Room Key"}, + "key_2": {"name": "Rusty Key"}, + "key_3": {"name": "Cabin Key"}, + "puzzle_1": {"name": "Circuit Breaker"}, + }, + }, + + { + "title": "Tibetan Foothills", + "objects": { + "tiger": {"name": "Snow Leopard"}, + "key_1": {"name": "Drawbridge Key"}, + "key_2": {"name": "Hut Key"}, + "puzzle_4": {"name": "The Seraph"}, + }, + }, + ], + + "cutscenes": [ + {"title": "Cutscene 1"}, + {"title": "Cutscene 2"}, + {"title": "Cutscene 3"}, + {"title": "Cutscene 4"}, + ], + "objects": { "small_medipack": {"name": "Small Medipack"}, "large_medipack": {"name": "Large Medipack"}, diff --git a/src/libtrx/game/game_string_table/common.c b/src/libtrx/game/game_string_table/common.c index 2740ab3df..3abbc2f6f 100644 --- a/src/libtrx/game/game_string_table/common.c +++ b/src/libtrx/game/game_string_table/common.c @@ -81,24 +81,59 @@ static void M_Apply(const GS_TABLE *const table) } } -void GameStringTable_Apply(const int32_t level_num) +void GameStringTable_Apply(const GAME_FLOW_LEVEL *const level) { const GS_FILE *const gs_file = &g_GST_File; - if (level_num < -1 || level_num >= gs_file->level_count) { - LOG_WARNING( - "Trying to apply unavailable strings for level %d", level_num); - return; - } - - LOG_DEBUG("loading file %d", level_num); Object_ResetNames(); M_Apply(&gs_file->global); for (int32_t i = 0; i < GF_GetLevelCount(GFL_NORMAL); i++) { - GF_SetLevelTitle(GF_GetLevel(i, GFL_NORMAL), gs_file->levels[i].title); + GF_SetLevelTitle( + GF_GetLevel(i, GFL_NORMAL), gs_file->levels.entries[i].title); + } + +#if TR_VERSION == 2 + // TODO: TR1 still has everything in a single linear sequence + for (int32_t i = 0; i < GF_GetLevelCount(GFL_DEMO); i++) { + GF_SetLevelTitle( + GF_GetLevel(i, GFL_DEMO), gs_file->demos.entries[i].title); + } + for (int32_t i = 0; i < GF_GetLevelCount(GFL_CUTSCENE); i++) { + GF_SetLevelTitle( + GF_GetLevel(i, GFL_CUTSCENE), gs_file->cutscenes.entries[i].title); } - if (level_num != -1) { - M_Apply(&gs_file->levels[level_num].table); +#endif + + if (level != NULL) { +#if TR_VERSION == 1 + // TODO: TR1 still has everything in a single linear sequence + const GS_LEVEL_TABLE *const level_table = &gs_file->levels; +#elif TR_VERSION == 2 + const GS_LEVEL_TABLE *level_table = NULL; + switch (level->type) { + case GFL_NORMAL: + case GFL_SAVED: + level_table = &gs_file->levels; + break; + case GFL_DEMO: + level_table = &gs_file->demos; + break; + case GFL_CUTSCENE: + level_table = &gs_file->cutscenes; + break; + case GFL_TITLE: + level_table = NULL; + break; + default: + ASSERT_FAIL(); + } +#endif + + if (level_table != NULL) { + ASSERT(level->num >= 0); + ASSERT(level->num < level_table->count); + M_Apply(&level_table->entries[level->num].table); + } } M_DoObjectAliases(); } diff --git a/src/libtrx/game/game_string_table/priv.c b/src/libtrx/game/game_string_table/priv.c index 482ced7d0..619357a51 100644 --- a/src/libtrx/game/game_string_table/priv.c +++ b/src/libtrx/game/game_string_table/priv.c @@ -2,7 +2,7 @@ #include "memory.h" -void GS_Table_Free(GS_TABLE *const gs_table) +static void M_FreeTable(GS_TABLE *const gs_table) { if (gs_table == NULL) { return; @@ -31,18 +31,25 @@ void GS_Table_Free(GS_TABLE *const gs_table) } } +static void M_FreeLevelsTable(GS_LEVEL_TABLE *const levels) +{ + if (levels->entries != NULL) { + for (int32_t i = 0; i < levels->count; i++) { + Memory_FreePointer(&levels->entries[i].title); + M_FreeTable(&levels->entries[i].table); + } + } + levels->count = 0; +} + void GS_File_Free(GS_FILE *const gs_file) { if (gs_file == NULL) { return; } - GS_Table_Free(&gs_file->global); - if (gs_file->levels != NULL) { - for (int32_t i = 0; i < gs_file->level_count; i++) { - Memory_FreePointer(&gs_file->levels[i].title); - GS_Table_Free(&gs_file->levels[i].table); - } - } + M_FreeTable(&gs_file->global); + M_FreeLevelsTable(&gs_file->levels); + M_FreeLevelsTable(&gs_file->demos); + M_FreeLevelsTable(&gs_file->cutscenes); Memory_FreePointer(&gs_file->levels); - gs_file->level_count = 0; } diff --git a/src/libtrx/game/game_string_table/priv.h b/src/libtrx/game/game_string_table/priv.h index fb56a157f..6f037e3e3 100644 --- a/src/libtrx/game/game_string_table/priv.h +++ b/src/libtrx/game/game_string_table/priv.h @@ -24,9 +24,15 @@ typedef struct { } GS_LEVEL; typedef struct { - int32_t level_count; + int32_t count; + GS_LEVEL *entries; +} GS_LEVEL_TABLE; + +typedef struct { GS_TABLE global; - GS_LEVEL *levels; + GS_LEVEL_TABLE levels; + GS_LEVEL_TABLE demos; + GS_LEVEL_TABLE cutscenes; } GS_FILE; extern GS_FILE g_GST_File; diff --git a/src/libtrx/game/game_string_table/reader.c b/src/libtrx/game/game_string_table/reader.c index f73fb2d01..6776a36eb 100644 --- a/src/libtrx/game/game_string_table/reader.c +++ b/src/libtrx/game/game_string_table/reader.c @@ -8,7 +8,9 @@ #include "memory.h" static void M_LoadTableFromJSON(JSON_OBJECT *root_obj, GS_TABLE *out_table); -static void M_LoadLevelsFromJSON(JSON_OBJECT *obj, GS_FILE *gs_file); +static void M_LoadLevelsFromJSON( + JSON_OBJECT *obj, const char *key, GAME_FLOW_LEVEL_TYPE level_type, + GS_LEVEL_TABLE *gs_level_table); static void M_LoadTableFromJSON( JSON_OBJECT *const root_obj, GS_TABLE *const out_table) @@ -76,27 +78,29 @@ static void M_LoadTableFromJSON( } } -static void M_LoadLevelsFromJSON(JSON_OBJECT *const obj, GS_FILE *const gs_file) +static void M_LoadLevelsFromJSON( + JSON_OBJECT *const obj, const char *const key, + const GAME_FLOW_LEVEL_TYPE level_type, GS_LEVEL_TABLE *const gs_level_table) { - JSON_ARRAY *const jlvl_arr = JSON_ObjectGetArray(obj, "levels"); + JSON_ARRAY *const jlvl_arr = JSON_ObjectGetArray(obj, key); if (jlvl_arr == NULL) { - Shell_ExitSystem("'levels' must be a list"); + Shell_ExitSystemFmt("'%s' must be a list", key); return; } - if (jlvl_arr->length != (size_t)GF_GetLevelCount(GFL_NORMAL)) { + if (jlvl_arr->length != (size_t)GF_GetLevelCount(level_type)) { Shell_ExitSystemFmt( - "'levels' length must match with the game flow level count (got: " + "'%s' length must match with the game flow level count (got: " "%d, expected: %d)", - jlvl_arr->length, GF_GetLevelCount(GFL_NORMAL)); + key, jlvl_arr->length, GF_GetLevelCount(level_type)); } - gs_file->level_count = jlvl_arr->length; - gs_file->levels = Memory_Alloc(sizeof(GS_LEVEL) * jlvl_arr->length); + gs_level_table->count = jlvl_arr->length; + gs_level_table->entries = Memory_Alloc(sizeof(GS_LEVEL) * jlvl_arr->length); JSON_ARRAY_ELEMENT *jlvl_elem = jlvl_arr->start; for (size_t i = 0; i < jlvl_arr->length; i++, jlvl_elem = jlvl_elem->next) { - GS_LEVEL *const level = &gs_file->levels[i]; + GS_LEVEL *const level = &gs_level_table->entries[i]; JSON_OBJECT *const jlvl_obj = JSON_ValueAsObject(jlvl_elem->value); if (jlvl_obj == NULL) { @@ -141,7 +145,13 @@ void GameStringTable_LoadFromFile(const char *const path) GS_FILE *const gs_file = &g_GST_File; JSON_OBJECT *root_obj = JSON_ValueAsObject(root); M_LoadTableFromJSON(root_obj, &gs_file->global); - M_LoadLevelsFromJSON(root_obj, gs_file); + M_LoadLevelsFromJSON(root_obj, "levels", GFL_NORMAL, &gs_file->levels); +#if TR_VERSION == 2 + // TODO: TR1 still has everything in a single linear sequence + M_LoadLevelsFromJSON(root_obj, "demos", GFL_DEMO, &gs_file->demos); + M_LoadLevelsFromJSON( + root_obj, "cutscenes", GFL_CUTSCENE, &gs_file->cutscenes); +#endif if (root != NULL) { JSON_ValueFree(root); diff --git a/src/libtrx/include/libtrx/game/game_string_table.h b/src/libtrx/include/libtrx/game/game_string_table.h index 302bfd08f..1b918a468 100644 --- a/src/libtrx/include/libtrx/game/game_string_table.h +++ b/src/libtrx/include/libtrx/game/game_string_table.h @@ -3,5 +3,5 @@ #include void GameStringTable_LoadFromFile(const char *path); -void GameStringTable_Apply(int32_t level_num); +void GameStringTable_Apply(const GAME_FLOW_LEVEL *level); void GameStringTable_Shutdown(void); diff --git a/src/tr1/game/level.c b/src/tr1/game/level.c index 166c4ab40..bcc103ddd 100644 --- a/src/tr1/game/level.c +++ b/src/tr1/game/level.c @@ -927,7 +927,7 @@ bool Level_Initialise(const GAME_FLOW_LEVEL *const level) Lara_InitialiseLoad(NO_ITEM); Level_Load(level); - GameStringTable_Apply(level_num); + GameStringTable_Apply(level); if (g_Lara.item_num != NO_ITEM) { Lara_Initialise(level); diff --git a/src/tr2/decomp/decomp.c b/src/tr2/decomp/decomp.c index eb32a04de..d3472adad 100644 --- a/src/tr2/decomp/decomp.c +++ b/src/tr2/decomp/decomp.c @@ -35,7 +35,7 @@ static CAMERA_INFO m_LocalCamera = {}; GAME_FLOW_COMMAND TitleSequence(void) { - GameStringTable_Apply(-1); + GameStringTable_Apply(NULL); if (!Level_Initialise(0, GFL_TITLE)) { return (GAME_FLOW_COMMAND) { .action = GF_EXIT_GAME }; } diff --git a/src/tr2/game/game_flow/sequencer.c b/src/tr2/game/game_flow/sequencer.c index 33d0b7d19..3c6019f45 100644 --- a/src/tr2/game/game_flow/sequencer.c +++ b/src/tr2/game/game_flow/sequencer.c @@ -33,7 +33,6 @@ GAME_FLOW_COMMAND GF_DoCutsceneSequence(const int32_t cutscene_num) bool GF_DoFrontendSequence(void) { - GameStringTable_Apply(-1); if (g_GameFlow.title_level == NULL) { return false; } @@ -45,7 +44,6 @@ bool GF_DoFrontendSequence(void) GAME_FLOW_COMMAND GF_DoLevelSequence( const int32_t start_level, const GAME_FLOW_LEVEL_TYPE type) { - GameStringTable_Apply(start_level); int32_t current_level = start_level; while (true) { if (current_level > GF_GetLevelCount(GFL_NORMAL) - 1) { diff --git a/src/tr2/game/level.c b/src/tr2/game/level.c index 766f1b647..f8e7f1473 100644 --- a/src/tr2/game/level.c +++ b/src/tr2/game/level.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -811,6 +812,7 @@ bool Level_Initialise( if (!Level_Load(level)) { return false; } + GameStringTable_Apply(level); if (g_Lara.item_num != NO_ITEM) { Lara_Initialise(level);