shell: add -s/--save argument support

This commit is contained in:
Marcin Kurczewski 2025-03-15 19:52:45 +01:00
parent fe3170b82a
commit 28479fe0ce
6 changed files with 112 additions and 53 deletions

View file

@ -2,6 +2,7 @@
- added quadrilateral interpolation (#354)
- added `/flood` and `/drain` console commands
- added support for `-l`/`--level` argument to play a single level
- added support for `-s`/`--save` argument to immediately start a saved game
- added support for custom levels to use `disable_floor` in the gameflow, similar to TR2's Floating Islands (#2541)
- added drawing of object mesh spheres to the `/debug` console command
- changed the Controls screen to hide the reset and unbind texts when changing a key (#2103)

View file

@ -612,7 +612,8 @@ Not all options are turned on by default. Refer to `TR1X_ConfigTool.exe` for det
- added an option to pause sound in the inventory screen
- added ability to skip FMVs with the Action key
- added ability to make freshly triggered (runaway) Pierre replace an already existing (runaway) Pierre
- expanded internal game memory limit from 3.5 MB to 128 MB
- added -l/--level and -s/--save command line arguments
- expanded internal game memory limit from 3.5 MB to unlimited (within system memory cap)
- expanded moveable limit from 256 to 10240
- expanded maximum object textures from 2048 to unlimited (within game's overall memory cap)
- expanded maximum sprite textures from 512 to unlimited (within game's overall memory cap)

View file

@ -7,6 +7,7 @@
- added a `/fps` console command
- added `/flood` and `/drain` console commands
- added support for `-l`/`--level` argument to play a single level
- added support for `-s`/`--save` argument to immediately start a saved game
- added the ability to specify per-level SFX files rather than enforcing the default (main.sfx) on all levels (#2615)
- added the camera shutter sound to cutscenes for photo mode (#2280)
- added Italian localization to the config tool

View file

@ -181,7 +181,8 @@ game with new enhancements and features.
- added ability to skip FMVs with both the Action key
- added ability to skip end credits with the Action and Escape keys
- added the ability to specify per-level SFX files rather than enforcing the default (main.sfx) on all levels
- expanded internal game memory limit from 7.5 MB to 128 MB
- added -l/--level and -s/--save command line arguments
- expanded internal game memory limit from 7.5 MB to unlimited (within system memory cap)
- expanded maximum object textures from 2048 to unlimited (within game's overall memory cap)
- expanded maximum sprite textures from 512 to unlimited (within game's overall memory cap)
- expanded maximum texture pages from 32 to 128

View file

@ -28,6 +28,7 @@
#include <libtrx/game/game_string_table.h>
#include <libtrx/game/ui/common.h>
#include <libtrx/memory.h>
#include <libtrx/strings.h>
#include <stdarg.h>
#include <stdint.h>
@ -44,6 +45,12 @@ typedef enum {
M_MOD_CUSTOM_LEVEL,
} M_MOD;
typedef struct {
M_MOD mod;
const char *level_to_play;
int32_t save_to_load;
} SHELL_ARGS;
static struct {
char *game_flow_path;
char *game_strings_path;
@ -65,13 +72,48 @@ static struct {
.game_strings_path = "cfg/TR1X_strings_level.json5",
},
};
static M_MOD m_ActiveMod = M_MOD_UNKNOWN;
static SHELL_ARGS m_Args = {
.mod = M_MOD_UNKNOWN,
.level_to_play = nullptr,
.save_to_load = -1,
};
static const char *m_CurrentGameFlowPath;
static void M_ParseArgs(SHELL_ARGS *out_args);
static void M_LoadConfig(void);
static void M_HandleConfigChange(const EVENT *event, void *data);
static void M_ParseArgs(SHELL_ARGS *const out_args)
{
const char **args = nullptr;
int32_t arg_count = 0;
Shell_GetCommandLine(&arg_count, &args);
out_args->mod = M_MOD_OG;
for (int32_t i = 0; i < arg_count; i++) {
if (!strcmp(args[i], "-gold")) {
out_args->mod = M_MOD_UB;
}
if (!strcmp(args[i], "-demo_pc")) {
out_args->mod = M_MOD_DEMO_PC;
}
if ((!strcmp(args[i], "-l") || !strcmp(args[i], "--level"))
&& i + 1 < arg_count) {
out_args->level_to_play = args[i + 1];
out_args->mod = M_MOD_CUSTOM_LEVEL;
}
if ((!strcmp(args[i], "-s") || !strcmp(args[i], "--save"))
&& i + 1 < arg_count) {
if (String_ParseInteger(args[i + 1], &out_args->save_to_load)) {
out_args->save_to_load--;
}
}
}
}
static void M_HandleConfigChange(const EVENT *const event, void *const data)
{
const CONFIG *const old = &g_Config;
@ -130,31 +172,12 @@ const char *Shell_GetConfigPath(void)
const char *Shell_GetGameFlowPath(void)
{
return m_ModPaths[m_ActiveMod].game_flow_path;
return m_ModPaths[m_Args.mod].game_flow_path;
}
void Shell_Main(void)
{
m_ActiveMod = M_MOD_OG;
const char *level_to_play = nullptr;
const char **args = nullptr;
int32_t arg_count = 0;
Shell_GetCommandLine(&arg_count, &args);
for (int32_t i = 0; i < arg_count; i++) {
if (!strcmp(args[i], "-gold")) {
m_ActiveMod = M_MOD_UB;
}
if (!strcmp(args[i], "-demo_pc")) {
m_ActiveMod = M_MOD_DEMO_PC;
}
if ((!strcmp(args[i], "-l") || !strcmp(args[i], "--level"))
&& i + 1 < arg_count) {
level_to_play = args[i + 1];
m_ActiveMod = M_MOD_CUSTOM_LEVEL;
}
}
M_ParseArgs(&m_Args);
GameString_Init();
EnumMap_Init();
@ -183,8 +206,8 @@ void Shell_Main(void)
Screen_Init();
GF_Init();
GF_LoadFromFile(m_ModPaths[m_ActiveMod].game_flow_path);
GameStringTable_LoadFromFile(m_ModPaths[m_ActiveMod].game_strings_path);
GF_LoadFromFile(m_ModPaths[m_Args.mod].game_flow_path);
GameStringTable_LoadFromFile(m_ModPaths[m_Args.mod].game_strings_path);
GameStringTable_Apply(nullptr);
Savegame_Init();
@ -194,15 +217,19 @@ void Shell_Main(void)
GameBuf_Init();
Console_Init();
if (level_to_play != nullptr) {
if (m_Args.level_to_play != nullptr) {
Memory_Free(g_GameFlow.level_tables[GFLT_MAIN].levels[0].path);
g_GameFlow.level_tables[GFLT_MAIN].levels[0].path =
Memory_DupStr(level_to_play);
Memory_DupStr(m_Args.level_to_play);
}
GF_COMMAND gf_cmd = level_to_play != nullptr
GF_COMMAND gf_cmd = m_Args.save_to_load != -1
? (GF_COMMAND) { .action = GF_START_SAVED_GAME,
.param = m_Args.save_to_load }
: m_Args.level_to_play != nullptr
? (GF_COMMAND) { .action = GF_START_GAME, .param = 0 }
: GF_DoFrontendSequence();
bool loop_continue = !Shell_IsExiting();
while (loop_continue) {
LOG_INFO(
@ -260,7 +287,7 @@ void Shell_Main(void)
break;
case GF_EXIT_TO_TITLE:
if (level_to_play != nullptr) {
if (m_Args.level_to_play != nullptr) {
gf_cmd = (GF_COMMAND) { .action = GF_EXIT_GAME };
} else if (g_GameFlow.title_level == nullptr) {
Shell_ExitSystem("Title disabled");
@ -284,7 +311,7 @@ void Shell_Main(void)
EnumMap_Shutdown();
GameString_Shutdown();
if (level_to_play != nullptr) {
if (m_Args.level_to_play != nullptr) {
Memory_FreePointer(&g_GameFlow.level_tables[GFLT_MAIN].levels[0].path);
}
}

View file

@ -28,6 +28,7 @@
#include <libtrx/game/shell.h>
#include <libtrx/game/ui/common.h>
#include <libtrx/memory.h>
#include <libtrx/strings.h>
#include <SDL2/SDL.h>
#include <stdarg.h>
@ -39,6 +40,12 @@ typedef enum {
M_MOD_CUSTOM_LEVEL,
} M_MOD;
typedef struct {
M_MOD mod;
const char *level_to_play;
int32_t save_to_load;
} SHELL_ARGS;
static struct {
char *game_flow_path;
char *game_strings_path;
@ -52,7 +59,13 @@ static struct {
.game_strings_path = "cfg/TR2X_strings_level.json5",
},
};
static M_MOD m_ActiveMod = M_MOD_UNKNOWN;
static SHELL_ARGS m_Args = {
.mod = M_MOD_UNKNOWN,
.level_to_play = nullptr,
.save_to_load = -1,
};
static Uint64 m_UpdateDebounce = 0;
static void M_SyncToWindow(void);
@ -72,6 +85,7 @@ static void M_HandleQuit(void);
static void M_ConfigureOpenGL(void);
static bool M_CreateGameWindow(void);
static void M_ParseArgs(SHELL_ARGS *out_args);
static void M_LoadConfig(void);
static void M_HandleConfigChange(const EVENT *event, void *data);
@ -284,6 +298,29 @@ static bool M_CreateGameWindow(void)
return true;
}
static void M_ParseArgs(SHELL_ARGS *const out_args)
{
const char **args = nullptr;
int32_t arg_count = 0;
Shell_GetCommandLine(&arg_count, &args);
out_args->mod = M_MOD_OG;
for (int32_t i = 0; i < arg_count; i++) {
if ((!strcmp(args[i], "-l") || !strcmp(args[i], "--level"))
&& i + 1 < arg_count) {
out_args->level_to_play = args[i + 1];
out_args->mod = M_MOD_CUSTOM_LEVEL;
}
if ((!strcmp(args[i], "-s") || !strcmp(args[i], "--save"))
&& i + 1 < arg_count) {
if (String_ParseInteger(args[i + 1], &out_args->save_to_load)) {
out_args->save_to_load--;
}
}
}
}
static void M_LoadConfig(void)
{
Config_Read();
@ -338,20 +375,7 @@ static void M_HandleConfigChange(const EVENT *const event, void *const data)
// TODO: refactor the hell out of me
void Shell_Main(void)
{
m_ActiveMod = M_MOD_OG;
const char *level_to_play = nullptr;
const char **args = nullptr;
int32_t arg_count = 0;
Shell_GetCommandLine(&arg_count, &args);
for (int32_t i = 0; i < arg_count; i++) {
if ((!strcmp(args[i], "-l") || !strcmp(args[i], "--level"))
&& i + 1 < arg_count) {
level_to_play = args[i + 1];
m_ActiveMod = M_MOD_CUSTOM_LEVEL;
}
}
M_ParseArgs(&m_Args);
GameString_Init();
EnumMap_Init();
@ -382,8 +406,8 @@ void Shell_Main(void)
Render_Reset(RENDER_RESET_PARAMS);
GF_Init();
GF_LoadFromFile(m_ModPaths[m_ActiveMod].game_flow_path);
GameStringTable_LoadFromFile(m_ModPaths[m_ActiveMod].game_strings_path);
GF_LoadFromFile(m_ModPaths[m_Args.mod].game_flow_path);
GameStringTable_LoadFromFile(m_ModPaths[m_Args.mod].game_strings_path);
GameStringTable_Apply(nullptr);
Savegame_Init();
@ -392,15 +416,19 @@ void Shell_Main(void)
GameBuf_Init();
if (level_to_play != nullptr) {
if (m_Args.level_to_play != nullptr) {
Memory_Free(g_GameFlow.level_tables[GFLT_MAIN].levels[0].path);
g_GameFlow.level_tables[GFLT_MAIN].levels[0].path =
Memory_DupStr(level_to_play);
Memory_DupStr(m_Args.level_to_play);
}
GF_COMMAND gf_cmd = level_to_play != nullptr
GF_COMMAND gf_cmd = m_Args.save_to_load != -1
? (GF_COMMAND) { .action = GF_START_SAVED_GAME,
.param = m_Args.save_to_load }
: m_Args.level_to_play != nullptr
? (GF_COMMAND) { .action = GF_START_GAME, .param = 0 }
: GF_DoFrontendSequence();
bool loop_continue = !Shell_IsExiting();
while (loop_continue) {
LOG_INFO(
@ -470,7 +498,7 @@ void Shell_Main(void)
}
Config_Write();
if (level_to_play != nullptr) {
if (m_Args.level_to_play != nullptr) {
Memory_FreePointer(&g_GameFlow.level_tables[GFLT_MAIN].levels[0].path);
}
}
@ -495,7 +523,7 @@ const char *Shell_GetConfigPath(void)
const char *Shell_GetGameFlowPath(void)
{
return m_ModPaths[m_ActiveMod].game_flow_path;
return m_ModPaths[m_Args.mod].game_flow_path;
}
void Shell_Start(void)