game-strings: use OG JSON as a fallback in expansions

Resolves #2847.
This commit is contained in:
Marcin Kurczewski 2025-04-26 11:23:46 +02:00
parent 5a50de02ed
commit 864589bf0a
No known key found for this signature in database
GPG key ID: CC65E6FD28CAE42A
8 changed files with 81 additions and 36 deletions

View file

@ -11,6 +11,7 @@
- changed the "enable EIDOS logo" option to disable the Core Design and Bink Video Codec FMVs as well; renamed to "enable legal" (#2741)
- changed sprite pickups to respect the water tint if placed underwater (#2673)
- changed save to take priority over load when both inputs are held on the same frame, in line with OG (#2833)
- changed The Unfinished Business strings to default to the OG strings file for the main tables (#2847)
- fixed the bilinear filter to not readjust the UVs (#2258)
- fixed disabling the cutscenes causing the game to exit (#2743, regression from 4.8)
- fixed anisotropy filter causing black lines on certain GPUs (#902)

View file

@ -1,4 +1,5 @@
## [Unreleased](https://github.com/LostArtefacts/TRX/compare/tr2-1.0.1...develop) - ××××-××-××
- changed The Golden Mask strings to default to the OG strings file for the main tables (#2847)
- fixed Lara voiding if she stops on a tile with a closing door, and the door isn't on a portal (#2848)
- fixed guns carried by enemies not being converted to ammo if Lara has picked up the same gun elsewhere in the same level (#2856)
- fixed guns carried by enemies not being converted to ammo if Lara starts the level with the gun and the game has later been reloaded (#2850, regression from 1.0)

View file

@ -1,9 +1,11 @@
#include "debug.h"
#include "filesystem.h"
#include "game/game_flow.h"
#include "game/game_string.h"
#include "game/game_string_table.h"
#include "game/game_string_table/priv.h"
#include "game/objects/names.h"
#include "game/shell.h"
#include "log.h"
#include "memory.h"
@ -11,8 +13,6 @@
typedef void (*M_LOAD_STRING_FUNC)(const char *, const char *);
GS_FILE g_GST_File = {};
static struct {
GAME_OBJECT_ID target_object_id;
GAME_OBJECT_ID source_object_id;
@ -27,6 +27,8 @@ static struct {
{ .target_object_id = NO_OBJECT },
};
static VECTOR *m_GST_Layers = nullptr;
static void M_Apply(const GS_TABLE *table);
static void M_ApplyLevelTitles(
const GS_FILE *gs_file, GF_LEVEL_TABLE_TYPE level_table_type);
@ -90,17 +92,19 @@ static void M_ApplyLevelTitles(
GF_GetLevelTable(level_table_type);
const GS_LEVEL_TABLE *const gs_level_table =
&gs_file->level_tables[level_table_type];
if (gs_level_table->count == 0) {
return;
}
ASSERT(gs_level_table->count == level_table->count);
for (int32_t i = 0; i < level_table->count; i++) {
GF_SetLevelTitle(
&level_table->levels[i], gs_level_table->entries[i].title);
}
}
void GameStringTable_Apply(const GF_LEVEL *const level)
static void M_ApplyLayer(
const GF_LEVEL *const level, const GS_FILE *const gs_file)
{
const GS_FILE *const gs_file = &g_GST_File;
Object_ResetNames();
M_Apply(&gs_file->global);
for (int32_t i = 0; i < GFLT_NUMBER_OF; i++) {
@ -126,10 +130,44 @@ void GameStringTable_Apply(const GF_LEVEL *const level)
M_Apply(&gs_level_table->entries[level->num].table);
}
}
}
void GameStringTable_Apply(const GF_LEVEL *const level)
{
Object_ResetNames();
ASSERT(m_GST_Layers != nullptr);
for (int32_t i = 0; i < m_GST_Layers->count; i++) {
const GS_FILE *const gs_file = Vector_Get(m_GST_Layers, i);
M_ApplyLayer(level, gs_file);
}
M_DoObjectAliases();
}
void GameStringTable_Init(void)
{
m_GST_Layers = Vector_Create(sizeof(GS_FILE));
}
void GameStringTable_Shutdown(void)
{
GS_File_Free(&g_GST_File);
if (m_GST_Layers != nullptr) {
for (int32_t i = 0; i < m_GST_Layers->count; i++) {
GS_FILE *const gs_file = Vector_Get(m_GST_Layers, i);
GS_File_Free(gs_file);
}
Vector_Free(m_GST_Layers);
m_GST_Layers = nullptr;
}
}
void GameStringTable_Load(const char *const path, const bool load_levels)
{
char *data = nullptr;
if (!File_Load(path, &data, nullptr)) {
Shell_ExitSystemFmt("failed to open strings file (path: %d)", path);
}
GS_FILE *gs_file = GS_File_CreateFromString(data, load_levels);
ASSERT(m_GST_Layers != nullptr);
Vector_Add(m_GST_Layers, gs_file);
Memory_FreePointer(&data);
}

View file

@ -1,6 +1,7 @@
#pragma once
#include "game/game_flow/enum.h"
#include "vector.h"
#include <stdint.h>
@ -35,7 +36,7 @@ typedef struct {
GS_LEVEL_TABLE level_tables[GFLT_NUMBER_OF];
} GS_FILE;
extern GS_FILE g_GST_File;
void GS_Table_Free(GS_TABLE *gs_table);
GS_FILE *GS_File_CreateFromString(const char *data, bool load_levels);
void GS_File_Free(GS_FILE *gs_file);

View file

@ -1,4 +1,3 @@
#include "filesystem.h"
#include "game/game_flow.h"
#include "game/game_string_table.h"
#include "game/game_string_table/priv.h"
@ -130,24 +129,14 @@ static void M_LoadLevelsFromJSON(
}
}
void GameStringTable_LoadFromFile(const char *const path)
GS_FILE *GS_File_CreateFromString(
const char *const data, const bool load_levels)
{
char *data = nullptr;
if (!File_Load(path, &data, nullptr)) {
Shell_ExitSystemFmt("failed to open strings file (path: %d)", path);
}
GameStringTable_LoadFromString(data);
Memory_FreePointer(&data);
}
void GameStringTable_LoadFromString(const char *const data)
{
GameStringTable_Shutdown();
JSON_VALUE *root = nullptr;
GS_FILE *const gs_file = Memory_Alloc(sizeof(GS_FILE));
JSON_PARSE_RESULT parse_result;
root = JSON_ParseEx(
JSON_VALUE *root = JSON_ParseEx(
data, strlen(data), JSON_PARSE_FLAGS_ALLOW_JSON5, nullptr, nullptr,
&parse_result);
if (root == nullptr) {
@ -157,15 +146,16 @@ void GameStringTable_LoadFromString(const char *const data)
parse_result.error_line_no, parse_result.error_row_no, data);
}
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, "levels", GFLT_MAIN);
M_LoadLevelsFromJSON(root_obj, gs_file, "demos", GFLT_DEMOS);
M_LoadLevelsFromJSON(root_obj, gs_file, "cutscenes", GFLT_CUTSCENES);
if (load_levels) {
M_LoadLevelsFromJSON(root_obj, gs_file, "levels", GFLT_MAIN);
M_LoadLevelsFromJSON(root_obj, gs_file, "demos", GFLT_DEMOS);
M_LoadLevelsFromJSON(root_obj, gs_file, "cutscenes", GFLT_CUTSCENES);
}
if (root != nullptr) {
JSON_ValueFree(root);
root = nullptr;
}
return gs_file;
}

View file

@ -2,7 +2,8 @@
#include <stdint.h>
void GameStringTable_LoadFromFile(const char *path);
void GameStringTable_LoadFromString(const char *data);
void GameStringTable_Apply(const GF_LEVEL *level);
void GameStringTable_Init(void);
void GameStringTable_Shutdown(void);
void GameStringTable_Load(const char *path, bool load_levels);
void GameStringTable_Apply(const GF_LEVEL *level);

View file

@ -152,6 +152,8 @@ void Shell_Shutdown(void)
Console_Shutdown();
GameBuf_Shutdown();
Savegame_Shutdown();
GameStringTable_Shutdown();
GF_Shutdown();
Output_Shutdown();
@ -206,7 +208,11 @@ void Shell_Main(void)
GF_Init();
GF_LoadFromFile(m_ModPaths[m_Args.mod].game_flow_path);
GameStringTable_LoadFromFile(m_ModPaths[m_Args.mod].game_strings_path);
GameStringTable_Init();
if (m_Args.mod != M_MOD_OG) {
GameStringTable_Load(m_ModPaths[M_MOD_OG].game_strings_path, false);
}
GameStringTable_Load(m_ModPaths[m_Args.mod].game_strings_path, true);
GameStringTable_Apply(nullptr);
Savegame_Init();

View file

@ -478,7 +478,12 @@ void Shell_Main(void)
GF_Init();
GF_LoadFromFile(m_ModPaths[m_Args.mod].game_flow_path);
GameStringTable_LoadFromFile(m_ModPaths[m_Args.mod].game_strings_path);
GameStringTable_Init();
if (m_Args.mod != M_MOD_OG) {
GameStringTable_Load(m_ModPaths[M_MOD_OG].game_strings_path, false);
}
GameStringTable_Load(m_ModPaths[m_Args.mod].game_strings_path, true);
GameStringTable_Apply(nullptr);
GameBuf_Init();
@ -582,8 +587,9 @@ void Shell_Main(void)
void Shell_Shutdown(void)
{
GameStringTable_Shutdown();
GF_Shutdown();
GameString_Shutdown();
Console_Shutdown();
Render_Shutdown();
Text_Shutdown();
@ -591,6 +597,7 @@ void Shell_Shutdown(void)
GameBuf_Shutdown();
Config_Shutdown();
EnumMap_Shutdown();
GameString_Shutdown();
}
const char *Shell_GetConfigPath(void)