From 9c07c6e59489ef5a3547d64e182ee41423af3f43 Mon Sep 17 00:00:00 2001 From: Kevin Masterson Date: Wed, 18 Mar 2026 01:40:50 +0200 Subject: [PATCH 1/7] begin switching game support over to OOP virtual inheritance. API + Q3A done --- include/game_api.h | 109 ++++++++++++++---------------------- include/main.h | 15 +++-- src/game_api.cpp | 12 ++-- src/game_cod11mp.cpp | 12 ++-- src/game_codmp.cpp | 12 ++-- src/game_coduomp.cpp | 12 ++-- src/game_jamp.cpp | 12 ++-- src/game_jasp.cpp | 12 ++-- src/game_jk2mp.cpp | 16 +++--- src/game_jk2sp.cpp | 12 ++-- src/game_mohaa.cpp | 12 ++-- src/game_mohbt.cpp | 12 ++-- src/game_mohsh.cpp | 12 ++-- src/game_q2r.cpp | 12 ++-- src/game_q3a.cpp | 94 +++++++++++++++++++------------ src/game_quake2.cpp | 12 ++-- src/game_rtcwmp.cpp | 16 +++--- src/game_rtcwsp.cpp | 12 ++-- src/game_sin.cpp | 12 ++-- src/game_sof2mp.cpp | 16 +++--- src/game_sof2sp.cpp | 12 ++-- src/game_stef2.cpp | 12 ++-- src/game_stvoyhm.cpp | 16 +++--- src/game_stvoysp.cpp | 12 ++-- src/game_wet.cpp | 12 ++-- src/main.cpp | 128 ++++++++++++++++++------------------------- src/mod.cpp | 34 ++++-------- src/plugin.cpp | 30 +++++++--- 28 files changed, 335 insertions(+), 355 deletions(-) diff --git a/include/game_api.h b/include/game_api.h index 1f2bbc4..de3f75c 100644 --- a/include/game_api.h +++ b/include/game_api.h @@ -84,8 +84,8 @@ struct api_supportedgame; struct api_supportedgame_funcs { int* qmm_eng_msgs; // array of engine messages used by QMM int* qmm_mod_msgs; // array of mod messages used by QMM - const char* (*pfnEngMsgNames)(intptr_t); // pointer to a function that returns a string for a given engine message - const char* (*pfnModMsgNames)(intptr_t); // pointer to a function that returns a string for a given mod message + const char* (*pfnEngMsgName)(intptr_t); // pointer to a function that returns a string for a given engine message + const char* (*pfnModMsgName)(intptr_t); // pointer to a function that returns a string for a given mod message bool(*pfnAutoDetect)(api_supportedgame*, APIType); // pointer to a function that handles auto-detection logic for a game. return true to say "that's me!" void*(*pfnEntry)(void*, void*, APIType); // pointer to a function that handles entry point logic for a game. return value is passed back to engine @@ -95,75 +95,41 @@ struct api_supportedgame_funcs { qvm_syscall pfnQVMSyscall; // pointer to a function that handles mod->engine calls from a QVM (NULL = not supported) }; - -// some information for each game engine supported by QMM -struct api_supportedgame { - const char* dllname; // default dll mod filename - const char* qvmname; // default qvm mod filename (NULL = not supported) - const char* moddir; // default moddir name - const char* gamename_long; // long, descriptive, game name - - // this section is made by GEN_GAME_INFO(GAME) - const char* gamename_short; // short initials for game - api_supportedgame_funcs* funcs; // function pointers for this game - - int max_syscall_args; // max number of syscall args that this game needs (unused for now, but nice to have easily available) - int max_vmmain_args; // max number of vmmain args that this game needs (unused for now, but nice to have easily available) +struct GameSupport { + virtual int QMMEngMsg(int) = 0; + virtual int QMMModMsg(int) = 0; + virtual const char* EngMsgName(intptr_t) = 0; + virtual const char* ModMsgName(intptr_t) = 0; + virtual bool AutoDetect(APIType) = 0; + virtual void* Entry(void*, void*, APIType) = 0; + virtual bool ModLoad(void*, APIType) = 0; + virtual void ModUnload() = 0; + + virtual intptr_t syscall(intptr_t, ...) = 0; + virtual intptr_t vmMain(intptr_t, ...) = 0; + + virtual const char* DefaultDLLName() = 0; + virtual const char* DefaultQVMName() { return nullptr; } + virtual const char* DefaultModDir() = 0; + virtual const char* GameName() = 0; + virtual const char* GameCode() = 0; + + virtual int QVMSyscall(uint8_t*, int, int*) { return 0; } }; -extern std::vector api_supportedgames; +extern std::vector api_supportedgames; // macros to make game support a bit easier to do -// generate extern for each game's functions (used at the top of game_api.cpp) -#define GEN_GAME_EXTS(game) extern api_supportedgame_funcs game##_funcs; - -// generate extern for each game's shortcode and funcs struct (used in supportedgames entry in game_api.cpp) -#define GEN_GAME_INFO(game) #game , &game##_funcs - -// generate struct info for the game-specific functions and arrays (used in game_XYZ.cpp) - -// game with QVM support -#define GEN_GAME_FUNCS_QVM(game) \ -static const char* game##_EngMsgNames(intptr_t); \ -static const char* game##_ModMsgNames(intptr_t); \ -static bool game##_AutoDetect(api_supportedgame*, APIType); \ -static void* game##_Entry(void*, void*, APIType); \ -static bool game##_ModLoad(void*, APIType); \ -static void game##_ModUnload(); \ -static intptr_t game##_syscall(intptr_t cmd, ...); \ -static intptr_t game##_vmMain(intptr_t cmd, ...); \ -static int game##_QVMSyscall(uint8_t*, int, int*); \ -api_supportedgame_funcs game##_funcs = { \ - game##_qmm_eng_msgs, game##_qmm_mod_msgs, \ - game##_EngMsgNames, game##_ModMsgNames, \ - game##_AutoDetect, game##_Entry, \ - game##_ModLoad, game##_ModUnload, \ - game##_QVMSyscall, \ -} - -// game with no QVM support -#define GEN_GAME_FUNCS(game) \ -static const char* game##_EngMsgNames(intptr_t); \ -static const char* game##_ModMsgNames(intptr_t); \ -static bool game##_AutoDetect(api_supportedgame*, APIType); \ -static void* game##_Entry(void*, void*, APIType); \ -static bool game##_ModLoad(void*, APIType); \ -static void game##_ModUnload(); \ -static intptr_t game##_syscall(intptr_t cmd, ...); \ -static intptr_t game##_vmMain(intptr_t cmd, ...); \ -api_supportedgame_funcs game##_funcs = { \ - game##_qmm_eng_msgs, game##_qmm_mod_msgs, \ - game##_EngMsgNames, game##_ModMsgNames, \ - game##_AutoDetect, game##_Entry, \ - game##_ModLoad, game##_ModUnload, \ - nullptr, \ -} +// generate extern for each game's support object (used at the top of game_api.cpp) +#define GEN_GAME_EXTS(game) extern GameSupport* game##_gamesupport + +#define GEN_GAME_OBJ(game) static game##_GameSupport gamesupport; GameSupport* game##_gamesupport = &gamesupport // generate a case/string line for use in the *MsgNames functions #define GEN_CASE(x) case x: return #x -// a list of all the engine messages/constants used by QMM. if you change this, update the GEN_GAME_QMM_MSGS macro +// a list of all the engine messages/constants used by QMM. if you change this, update the GEN_GAME_QMM_*_MSGS macros enum { // general purpose QMM_G_PRINT, QMM_G_ERROR, QMM_G_ARGV, QMM_G_ARGC, QMM_G_SEND_CONSOLE_COMMAND, QMM_G_GET_CONFIGSTRING, @@ -171,19 +137,26 @@ enum { QMM_G_CVAR_REGISTER, QMM_G_CVAR_VARIABLE_STRING_BUFFER, QMM_G_CVAR_VARIABLE_INTEGER_VALUE, QMM_CVAR_SERVERINFO, QMM_CVAR_ROM, // files QMM_G_FS_FOPEN_FILE, QMM_G_FS_READ, QMM_G_FS_WRITE, QMM_G_FS_FCLOSE_FILE, QMM_EXEC_APPEND, QMM_FS_READ, + + QMM_ENGINE_MSG_COUNT, }; // a list of all the mod messages used by QMM. if you change this, update the GEN_GAME_QMM_MSGS macro -enum { QMM_GAME_INIT, QMM_GAME_SHUTDOWN, QMM_GAME_CONSOLE_COMMAND, }; +enum { + QMM_GAME_INIT, QMM_GAME_SHUTDOWN, QMM_GAME_CONSOLE_COMMAND, + + QMM_MOD_MSG_COUNT, +}; -// macro to easily output game-specific message values to match the enums above. this macro goes in game_*.cpp -#define GEN_GAME_QMM_MSGS(game) \ - static int game##_qmm_eng_msgs[] = { \ +// macros to easily output game-specific message values to match the enums above +#define GEN_GAME_QMM_ENG_MSGS() \ + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = { \ G_PRINT, G_ERROR, G_ARGV, G_ARGC, G_SEND_CONSOLE_COMMAND, G_GET_CONFIGSTRING, \ G_CVAR_REGISTER, G_CVAR_VARIABLE_STRING_BUFFER, G_CVAR_VARIABLE_INTEGER_VALUE, CVAR_SERVERINFO, CVAR_ROM, \ G_FS_FOPEN_FILE, G_FS_READ, G_FS_WRITE, G_FS_FCLOSE_FILE, EXEC_APPEND, FS_READ, \ - }; \ - static int game##_qmm_mod_msgs[] = { \ + } +#define GEN_GAME_QMM_MOD_MSGS() \ + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = { \ GAME_INIT, GAME_SHUTDOWN, GAME_CONSOLE_COMMAND, \ } diff --git a/include/main.h b/include/main.h index cbab503..0aeeabe 100644 --- a/include/main.h +++ b/include/main.h @@ -27,9 +27,8 @@ struct gameinfo { std::string qmm_file; // filename of qmm dll std::string mod_dir; // active mod dir std::string cfg_path; // qmm config file path - eng_syscall pfnsyscall = nullptr; // game-specific wrapper for syscall. given to plugins and called by QMM - mod_vmMain pfnvmMain = nullptr; // game-specific wrapper for vmMain. given to plugins and called by QMM - api_supportedgame* game = nullptr; // loaded engine from supported games table from game_api.cpp + GameSupport* game = nullptr; // loaded engine from supported games table from game_api.cpp + eng_syscall syscall = nullptr; // syscall from dllEntry (if applicable) to call G_ERROR if needed void* qmm_module_ptr = nullptr; // qmm module pointer bool is_auto_detected = false; // was this engine auto-detected? bool is_shutdown = false; // is game shutting down due to G_ERROR? avoids calling G_ERROR again from GAME_SHUTDOWN @@ -37,12 +36,12 @@ struct gameinfo { }; extern gameinfo g_gameinfo; -#define QMM_ENG_MSG (g_gameinfo.game->funcs->qmm_eng_msgs) -#define QMM_MOD_MSG (g_gameinfo.game->funcs->qmm_mod_msgs) +#define QMM_ENG_MSG g_gameinfo.game->QMMEngMsg +#define QMM_MOD_MSG g_gameinfo.game->QMMModMsg -#define ENG_SYSCALL (g_gameinfo.pfnsyscall) -#define CONSOLE_PRINT(str) ENG_SYSCALL(msg_G_PRINT, str) -#define CONSOLE_PRINTF(str, ...) ENG_SYSCALL(msg_G_PRINT, fmt::format(str, ## __VA_ARGS__).c_str()) +#define ENG_SYSCALL g_gameinfo.game->syscall +#define CONSOLE_PRINT(str) ENG_SYSCALL(msg_G_PRINT, str) +#define CONSOLE_PRINTF(str, ...) ENG_SYSCALL(msg_G_PRINT, fmt::format(str, ## __VA_ARGS__).c_str()) // this is used if we couldn't determine a game engine and we have to fail. // G_ERROR appears to be 1 in all supported dllEntry games. they are different in some GetGameAPI games, diff --git a/src/game_api.cpp b/src/game_api.cpp index 159b781..dfe79fc 100644 --- a/src/game_api.cpp +++ b/src/game_api.cpp @@ -16,7 +16,7 @@ Created By: // cache some dynamic message values that get evaluated a lot intptr_t msg_G_PRINT, msg_GAME_INIT, msg_GAME_CONSOLE_COMMAND, msg_GAME_SHUTDOWN; -// externs for each game's functions/structs +// externs for each game's support objects GEN_GAME_EXTS(COD11MP); GEN_GAME_EXTS(CODMP); GEN_GAME_EXTS(CODUOMP); @@ -41,12 +41,11 @@ GEN_GAME_EXTS(SOF2SP); GEN_GAME_EXTS(STEF2); GEN_GAME_EXTS(STVOYSP); -// add your game's info data here -std::vector api_supportedgames = { - // mod filename qvm mod filename default moddir full gamename short name/funcs #syscall#vmmain +std::vector api_supportedgames = { + Q3A_gamesupport, +}; - // vmMain games - { "qagame" MOD_DLL, "vm/qagame.qvm", "baseq3", "Quake 3 Arena", GEN_GAME_INFO(Q3A), 13, 4 }, +#if 0 { "qagame" SP_DLL X64_DLL, nullptr, ".", "Return to Castle Wolfenstein (SP)", GEN_GAME_INFO(RTCWSP), 13, 5 }, { "jk2mpgame" MOD_DLL, "vm/jk2mpgame.qvm", "base", "Jedi Knight 2: Jedi Outcast (MP)", GEN_GAME_INFO(JK2MP), 13, 3 }, { "jampgame" MOD_DLL, nullptr, "base", "Jedi Knight: Jedi Academy (MP)", GEN_GAME_INFO(JAMP), 13, 6 }, @@ -84,6 +83,7 @@ std::vector api_supportedgames = { #endif }; +#endif const char* APIType_Name(APIType api) { switch (api) { diff --git a/src/game_cod11mp.cpp b/src/game_cod11mp.cpp index e1cd8ac..22a7319 100644 --- a/src/game_cod11mp.cpp +++ b/src/game_cod11mp.cpp @@ -43,7 +43,7 @@ static intptr_t COD11MP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("COD11MP_syscall({} {}) called\n", COD11MP_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("COD11MP_syscall({} {}) called\n", COD11MP_EngMsgName(cmd), cmd); #endif intptr_t ret = 0; @@ -76,7 +76,7 @@ static intptr_t COD11MP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("COD11MP_syscall({} {}) returning {}\n", COD11MP_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("COD11MP_syscall({} {}) returning {}\n", COD11MP_EngMsgName(cmd), cmd, ret); #endif return ret; @@ -89,7 +89,7 @@ static intptr_t COD11MP_vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("COD11MP_vmMain({} {}) called\n", COD11MP_ModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("COD11MP_vmMain({} {}) called\n", COD11MP_ModMsgName(cmd), cmd); #endif if (!orig_vmMain) @@ -102,7 +102,7 @@ static intptr_t COD11MP_vmMain(intptr_t cmd, ...) { ret = orig_vmMain(cmd, QMM_PUT_VMMAIN_ARGS()); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("COD11MP_vmMain({} {}) returning {}\n", COD11MP_ModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("COD11MP_vmMain({} {}) returning {}\n", COD11MP_ModMsgName(cmd), cmd, ret); #endif return ret; @@ -142,7 +142,7 @@ static void COD11MP_ModUnload() { } -static const char* COD11MP_EngMsgNames(intptr_t cmd) { +static const char* COD11MP_EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINTF); GEN_CASE(G_ERROR); @@ -289,7 +289,7 @@ static const char* COD11MP_EngMsgNames(intptr_t cmd) { } -static const char* COD11MP_ModMsgNames(intptr_t cmd) { +static const char* COD11MP_ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_INIT); GEN_CASE(GAME_SHUTDOWN); diff --git a/src/game_codmp.cpp b/src/game_codmp.cpp index abbbca8..94d2c09 100644 --- a/src/game_codmp.cpp +++ b/src/game_codmp.cpp @@ -63,7 +63,7 @@ static intptr_t CODMP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODMP_syscall({} {}) called\n", CODMP_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODMP_syscall({} {}) called\n", CODMP_EngMsgName(cmd), cmd); #endif intptr_t ret = 0; @@ -96,7 +96,7 @@ static intptr_t CODMP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODMP_syscall({} {}) returning {}\n", CODMP_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODMP_syscall({} {}) returning {}\n", CODMP_EngMsgName(cmd), cmd, ret); #endif return ret; @@ -109,7 +109,7 @@ static intptr_t CODMP_vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODMP_vmMain({} {}) called\n", CODMP_ModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODMP_vmMain({} {}) called\n", CODMP_ModMsgName(cmd), cmd); #endif if (!orig_vmMain) @@ -122,7 +122,7 @@ static intptr_t CODMP_vmMain(intptr_t cmd, ...) { ret = orig_vmMain(cmd, QMM_PUT_VMMAIN_ARGS()); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODMP_vmMain({} {}) returning {}\n", CODMP_ModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODMP_vmMain({} {}) returning {}\n", CODMP_ModMsgName(cmd), cmd, ret); #endif return ret; @@ -162,7 +162,7 @@ static void CODMP_ModUnload() { } -static const char* CODMP_EngMsgNames(intptr_t cmd) { +static const char* CODMP_EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINTF); GEN_CASE(G_ERROR); @@ -314,7 +314,7 @@ static const char* CODMP_EngMsgNames(intptr_t cmd) { } -static const char* CODMP_ModMsgNames(intptr_t cmd) { +static const char* CODMP_ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_DEFAULT_0); GEN_CASE(GAME_GET_APIVERSION); diff --git a/src/game_coduomp.cpp b/src/game_coduomp.cpp index 8cf6404..e047ea0 100644 --- a/src/game_coduomp.cpp +++ b/src/game_coduomp.cpp @@ -63,7 +63,7 @@ static intptr_t CODUOMP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODUOMP_syscall({} {}) called\n", CODUOMP_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODUOMP_syscall({} {}) called\n", CODUOMP_EngMsgName(cmd), cmd); #endif intptr_t ret = 0; @@ -96,7 +96,7 @@ static intptr_t CODUOMP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODUOMP_syscall({} {}) returning {}\n", CODUOMP_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODUOMP_syscall({} {}) returning {}\n", CODUOMP_EngMsgName(cmd), cmd, ret); #endif return ret; @@ -109,7 +109,7 @@ static intptr_t CODUOMP_vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODUOMP_vmMain({} {}) called\n", CODUOMP_ModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODUOMP_vmMain({} {}) called\n", CODUOMP_ModMsgName(cmd), cmd); #endif if (!orig_vmMain) @@ -122,7 +122,7 @@ static intptr_t CODUOMP_vmMain(intptr_t cmd, ...) { ret = orig_vmMain(cmd, QMM_PUT_VMMAIN_ARGS()); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODUOMP_vmMain({} {}) returning {}\n", CODUOMP_ModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODUOMP_vmMain({} {}) returning {}\n", CODUOMP_ModMsgName(cmd), cmd, ret); #endif return ret; @@ -162,7 +162,7 @@ static void CODUOMP_ModUnload() { } -static const char* CODUOMP_EngMsgNames(intptr_t cmd) { +static const char* CODUOMP_EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINTF); GEN_CASE(G_ERROR); @@ -314,7 +314,7 @@ static const char* CODUOMP_EngMsgNames(intptr_t cmd) { } -static const char* CODUOMP_ModMsgNames(intptr_t cmd) { +static const char* CODUOMP_ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_DEFAULT_0); GEN_CASE(GAME_GET_APIVERSION); diff --git a/src/game_jamp.cpp b/src/game_jamp.cpp index 23c4f2e..4ba5b80 100644 --- a/src/game_jamp.cpp +++ b/src/game_jamp.cpp @@ -433,7 +433,7 @@ static intptr_t JAMP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_syscall({} {}) called\n", JAMP_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_syscall({} {}) called\n", JAMP_EngMsgName(cmd), cmd); #endif // store return value since we do some stuff after the function call is over @@ -805,7 +805,7 @@ static intptr_t JAMP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_syscall({} {}) returning {}\n", JAMP_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_syscall({} {}) returning {}\n", JAMP_EngMsgName(cmd), cmd, ret); #endif return ret; @@ -818,7 +818,7 @@ static intptr_t JAMP_vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_vmMain({} {}) called\n", JAMP_ModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_vmMain({} {}) called\n", JAMP_ModMsgName(cmd), cmd); #endif // store return value since we do some stuff after the function call is over @@ -879,7 +879,7 @@ static intptr_t JAMP_vmMain(intptr_t cmd, ...) { } #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_vmMain({} {}) returning {}\n", JAMP_ModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_vmMain({} {}) returning {}\n", JAMP_ModMsgName(cmd), cmd, ret); #endif return ret; @@ -955,7 +955,7 @@ static void JAMP_ModUnload() { } -static const char* JAMP_EngMsgNames(intptr_t cmd) { +static const char* JAMP_EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINT); GEN_CASE(G_ERROR); @@ -1296,7 +1296,7 @@ static const char* JAMP_EngMsgNames(intptr_t cmd) { } -static const char* JAMP_ModMsgNames(intptr_t cmd) { +static const char* JAMP_ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_INIT); GEN_CASE(GAME_SHUTDOWN); diff --git a/src/game_jasp.cpp b/src/game_jasp.cpp index b76a6c6..ad0fb29 100644 --- a/src/game_jasp.cpp +++ b/src/game_jasp.cpp @@ -262,7 +262,7 @@ static intptr_t JASP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JASP_syscall({} {}) called\n", JASP_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JASP_syscall({} {}) called\n", JASP_EngMsgName(cmd), cmd); #endif // update export vars before calling into the engine @@ -492,7 +492,7 @@ static intptr_t JASP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JASP_syscall({} {}) returning {}\n", JASP_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JASP_syscall({} {}) returning {}\n", JASP_EngMsgName(cmd), cmd, ret); #endif return ret; @@ -505,7 +505,7 @@ static intptr_t JASP_vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JASP_vmMain({} {}) called\n", JASP_ModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JASP_vmMain({} {}) called\n", JASP_ModMsgName(cmd), cmd); #endif if (!orig_export) @@ -545,7 +545,7 @@ static intptr_t JASP_vmMain(intptr_t cmd, ...) { s_update_export(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JASP_vmMain({} {}) returning {}\n", JASP_ModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JASP_vmMain({} {}) returning {}\n", JASP_ModMsgName(cmd), cmd, ret); #endif return ret; @@ -594,7 +594,7 @@ static void JASP_ModUnload() { } -static const char* JASP_EngMsgNames(intptr_t cmd) { +static const char* JASP_EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINTF); GEN_CASE(G_WRITECAM); @@ -740,7 +740,7 @@ static const char* JASP_EngMsgNames(intptr_t cmd) { } -static const char* JASP_ModMsgNames(intptr_t cmd) { +static const char* JASP_ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAMEV_APIVERSION); GEN_CASE(GAME_INIT); diff --git a/src/game_jk2mp.cpp b/src/game_jk2mp.cpp index f1b0536..c9861f7 100644 --- a/src/game_jk2mp.cpp +++ b/src/game_jk2mp.cpp @@ -55,7 +55,7 @@ static intptr_t JK2MP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2MP_syscall({} {}) called\n", JK2MP_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2MP_syscall({} {}) called\n", JK2MP_EngMsgName(cmd), cmd); #endif intptr_t ret = 0; @@ -88,7 +88,7 @@ static intptr_t JK2MP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2MP_syscall({} {}) returning {}\n", JK2MP_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2MP_syscall({} {}) returning {}\n", JK2MP_EngMsgName(cmd), cmd, ret); #endif return ret; @@ -101,7 +101,7 @@ static intptr_t JK2MP_vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2MP_vmMain({} {}) called\n", JK2MP_ModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2MP_vmMain({} {}) called\n", JK2MP_ModMsgName(cmd), cmd); #endif if (!orig_vmMain) @@ -120,7 +120,7 @@ static intptr_t JK2MP_vmMain(intptr_t cmd, ...) { } #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2MP_vmMain({} {}) returning {}\n", JK2MP_ModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2MP_vmMain({} {}) returning {}\n", JK2MP_ModMsgName(cmd), cmd, ret); #endif return ret; @@ -160,7 +160,7 @@ static void JK2MP_ModUnload() { } -static const char* JK2MP_EngMsgNames(intptr_t cmd) { +static const char* JK2MP_EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINT); GEN_CASE(G_ERROR); @@ -399,7 +399,7 @@ static const char* JK2MP_EngMsgNames(intptr_t cmd) { } -static const char* JK2MP_ModMsgNames(intptr_t cmd) { +static const char* JK2MP_ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_INIT); GEN_CASE(GAME_SHUTDOWN); @@ -429,7 +429,7 @@ static const char* JK2MP_ModMsgNames(intptr_t cmd) { // for double pointers (gentity_t**, vec3_t*, void**), convert them once with vmptr() static int JK2MP_QVMSyscall(uint8_t* membase, int cmd, int* args) { #ifdef _DEBUG - LOG(QMM_LOG_TRACE, "QMM") << fmt::format("JK2MP_QVMSyscall({} {}) called\n", JK2MP_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_TRACE, "QMM") << fmt::format("JK2MP_QVMSyscall({} {}) called\n", JK2MP_EngMsgName(cmd), cmd); #endif int ret = 0; @@ -754,7 +754,7 @@ static int JK2MP_QVMSyscall(uint8_t* membase, int cmd, int* args) { } #ifdef _DEBUG - LOG(QMM_LOG_TRACE, "QMM") << fmt::format("JK2MP_QVMSyscall({} {}) returning {}\n", JK2MP_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_TRACE, "QMM") << fmt::format("JK2MP_QVMSyscall({} {}) returning {}\n", JK2MP_EngMsgName(cmd), cmd, ret); #endif return ret; diff --git a/src/game_jk2sp.cpp b/src/game_jk2sp.cpp index 7adaa66..d2190d9 100644 --- a/src/game_jk2sp.cpp +++ b/src/game_jk2sp.cpp @@ -231,7 +231,7 @@ static intptr_t JK2SP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2SP_syscall({} {}) called\n", JK2SP_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2SP_syscall({} {}) called\n", JK2SP_EngMsgName(cmd), cmd); #endif // update export vars before calling into the engine @@ -413,7 +413,7 @@ static intptr_t JK2SP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2SP_syscall({} {}) returning {}\n", JK2SP_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2SP_syscall({} {}) returning {}\n", JK2SP_EngMsgName(cmd), cmd, ret); #endif return ret; @@ -426,7 +426,7 @@ static intptr_t JK2SP_vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2SP_vmMain({} {}) called\n", JK2SP_ModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2SP_vmMain({} {}) called\n", JK2SP_ModMsgName(cmd), cmd); #endif if (!orig_export) @@ -466,7 +466,7 @@ static intptr_t JK2SP_vmMain(intptr_t cmd, ...) { s_update_export(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2SP_vmMain({} {}) returning {}\n", JK2SP_ModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2SP_vmMain({} {}) returning {}\n", JK2SP_ModMsgName(cmd), cmd, ret); #endif return ret; @@ -515,7 +515,7 @@ static void JK2SP_ModUnload() { } -static const char* JK2SP_EngMsgNames(intptr_t cmd) { +static const char* JK2SP_EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINTF); GEN_CASE(G_WRITECAM); @@ -633,7 +633,7 @@ static const char* JK2SP_EngMsgNames(intptr_t cmd) { } -static const char* JK2SP_ModMsgNames(intptr_t cmd) { +static const char* JK2SP_ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAMEV_APIVERSION); GEN_CASE(GAME_INIT); diff --git a/src/game_mohaa.cpp b/src/game_mohaa.cpp index bba95b5..ae022bf 100644 --- a/src/game_mohaa.cpp +++ b/src/game_mohaa.cpp @@ -333,7 +333,7 @@ static intptr_t MOHAA_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHAA_syscall({} {}) called\n", MOHAA_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHAA_syscall({} {}) called\n", MOHAA_EngMsgName(cmd), cmd); #endif // update export vars before calling into the engine @@ -660,7 +660,7 @@ static intptr_t MOHAA_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHAA_syscall({} {}) returning {}\n", MOHAA_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHAA_syscall({} {}) returning {}\n", MOHAA_EngMsgName(cmd), cmd, ret); #endif return ret; @@ -673,7 +673,7 @@ static intptr_t MOHAA_vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHAA_vmMain({} {}) called\n", MOHAA_ModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHAA_vmMain({} {}) called\n", MOHAA_ModMsgName(cmd), cmd); #endif if (!orig_export) @@ -735,7 +735,7 @@ static intptr_t MOHAA_vmMain(intptr_t cmd, ...) { s_update_export(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHAA_vmMain({} {}) returning {}\n", MOHAA_ModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHAA_vmMain({} {}) returning {}\n", MOHAA_ModMsgName(cmd), cmd, ret); #endif return ret; @@ -788,7 +788,7 @@ static void MOHAA_ModUnload() { } -static const char* MOHAA_EngMsgNames(intptr_t cmd) { +static const char* MOHAA_EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINTF); GEN_CASE(G_DPRINTF); @@ -970,7 +970,7 @@ static const char* MOHAA_EngMsgNames(intptr_t cmd) { } -static const char* MOHAA_ModMsgNames(intptr_t cmd) { +static const char* MOHAA_ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAMEV_APIVERSION); GEN_CASE(GAME_INIT); diff --git a/src/game_mohbt.cpp b/src/game_mohbt.cpp index 2ba9183..7b4ff65 100644 --- a/src/game_mohbt.cpp +++ b/src/game_mohbt.cpp @@ -341,7 +341,7 @@ static intptr_t MOHBT_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHBT_syscall({} {}) called\n", MOHBT_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHBT_syscall({} {}) called\n", MOHBT_EngMsgName(cmd), cmd); #endif // update export vars before calling into the engine @@ -699,7 +699,7 @@ static intptr_t MOHBT_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHBT_syscall({} {}) returning {}\n", MOHBT_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHBT_syscall({} {}) returning {}\n", MOHBT_EngMsgName(cmd), cmd, ret); #endif return ret; @@ -712,7 +712,7 @@ static intptr_t MOHBT_vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHBT_vmMain({} {}) called\n", MOHBT_ModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHBT_vmMain({} {}) called\n", MOHBT_ModMsgName(cmd), cmd); #endif if (!orig_export) @@ -774,7 +774,7 @@ static intptr_t MOHBT_vmMain(intptr_t cmd, ...) { s_update_export(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHBT_vmMain({} {}) returning {}\n", MOHBT_ModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHBT_vmMain({} {}) returning {}\n", MOHBT_ModMsgName(cmd), cmd, ret); #endif return ret; @@ -827,7 +827,7 @@ static void MOHBT_ModUnload() { } -static const char* MOHBT_EngMsgNames(intptr_t cmd) { +static const char* MOHBT_EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINTF); GEN_CASE(G_DPRINTF); @@ -1016,7 +1016,7 @@ static const char* MOHBT_EngMsgNames(intptr_t cmd) { } -static const char* MOHBT_ModMsgNames(intptr_t cmd) { +static const char* MOHBT_ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAMEV_APIVERSION); GEN_CASE(GAME_INIT); diff --git a/src/game_mohsh.cpp b/src/game_mohsh.cpp index a0d901a..86a10f8 100644 --- a/src/game_mohsh.cpp +++ b/src/game_mohsh.cpp @@ -341,7 +341,7 @@ static intptr_t MOHSH_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHSH_syscall({} {}) called\n", MOHSH_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHSH_syscall({} {}) called\n", MOHSH_EngMsgName(cmd), cmd); #endif // update export vars before calling into the engine @@ -699,7 +699,7 @@ static intptr_t MOHSH_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHSH_syscall({} {}) returning {}\n", MOHSH_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHSH_syscall({} {}) returning {}\n", MOHSH_EngMsgName(cmd), cmd, ret); #endif return ret; @@ -712,7 +712,7 @@ static intptr_t MOHSH_vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHSH_vmMain({} {}) called\n", MOHSH_ModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHSH_vmMain({} {}) called\n", MOHSH_ModMsgName(cmd), cmd); #endif if (!orig_export) @@ -774,7 +774,7 @@ static intptr_t MOHSH_vmMain(intptr_t cmd, ...) { s_update_export(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHSH_vmMain({} {}) returning {}\n", MOHSH_ModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHSH_vmMain({} {}) returning {}\n", MOHSH_ModMsgName(cmd), cmd, ret); #endif return ret; @@ -827,7 +827,7 @@ static void MOHSH_ModUnload() { } -static const char* MOHSH_EngMsgNames(intptr_t cmd) { +static const char* MOHSH_EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINTF); GEN_CASE(G_DPRINTF); @@ -1016,7 +1016,7 @@ static const char* MOHSH_EngMsgNames(intptr_t cmd) { } -static const char* MOHSH_ModMsgNames(intptr_t cmd) { +static const char* MOHSH_ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAMEV_APIVERSION); GEN_CASE(GAME_INIT); diff --git a/src/game_q2r.cpp b/src/game_q2r.cpp index 3c5cbdd..8cbc19d 100644 --- a/src/game_q2r.cpp +++ b/src/game_q2r.cpp @@ -288,7 +288,7 @@ static intptr_t Q2R_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q2R_syscall({} {}) called\n", Q2R_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q2R_syscall({} {}) called\n", Q2R_EngMsgName(cmd), cmd); #endif // update export vars before calling into the engine @@ -527,7 +527,7 @@ static intptr_t Q2R_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q2R_syscall({} {}) returning {}\n", Q2R_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q2R_syscall({} {}) returning {}\n", Q2R_EngMsgName(cmd), cmd, ret); #endif return ret; @@ -540,7 +540,7 @@ static intptr_t Q2R_vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q2R_vmMain({} {}) called\n", Q2R_ModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q2R_vmMain({} {}) called\n", Q2R_ModMsgName(cmd), cmd); #endif if (!orig_export) @@ -595,7 +595,7 @@ static intptr_t Q2R_vmMain(intptr_t cmd, ...) { s_update_export(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q2R_vmMain({} {}) returning {}\n", Q2R_ModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q2R_vmMain({} {}) returning {}\n", Q2R_ModMsgName(cmd), cmd, ret); #endif return ret; @@ -646,7 +646,7 @@ static void Q2R_ModUnload() { } -static const char* Q2R_EngMsgNames(intptr_t cmd) { +static const char* Q2R_EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_BROADCAST_PRINT); GEN_CASE(G_COM_PRINT); @@ -741,7 +741,7 @@ static const char* Q2R_EngMsgNames(intptr_t cmd) { } -static const char* Q2R_ModMsgNames(intptr_t cmd) { +static const char* Q2R_ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAMEV_APIVERSION); GEN_CASE(GAME_PREINIT); diff --git a/src/game_q3a.cpp b/src/game_q3a.cpp index b85ef30..8b22484 100644 --- a/src/game_q3a.cpp +++ b/src/game_q3a.cpp @@ -22,17 +22,43 @@ Created By: #include "mod.h" #include "util.h" -GEN_GAME_QMM_MSGS(Q3A); - -GEN_GAME_FUNCS_QVM(Q3A); - +struct Q3A_GameSupport : GameSupport { + virtual int QMMEngMsg(int msg); + virtual int QMMModMsg(int msg); + virtual const char* EngMsgName(intptr_t msg); + virtual const char* ModMsgName(intptr_t msg); + virtual bool AutoDetect(APIType engine_api); + virtual void* Entry(void* syscall, void*, APIType engine_api); + virtual bool ModLoad(void* entry, APIType mod_api); + virtual void ModUnload(); + + virtual intptr_t syscall(intptr_t, ...); + virtual intptr_t vmMain(intptr_t, ...); + + virtual const char* DefaultDLLName() { return "qagame" MOD_DLL; } + virtual const char* DefaultQVMName() { return "vm/qagame.qvm"; } + virtual const char* DefaultModDir() { return "baseq3"; }; + virtual const char* GameName() { return "Quake 3 Arena"; }; + virtual const char* GameCode() { return "Q3A"; } + + virtual int QVMSyscall(uint8_t* membase, int cmd, int* args); + +private: + eng_syscall orig_syscall = nullptr; + mod_vmMain orig_vmMain = nullptr; + + GEN_GAME_QMM_ENG_MSGS(); + GEN_GAME_QMM_MOD_MSGS(); +}; + +GEN_GAME_OBJ(Q3A); // auto-detection logic for Q3A -static bool Q3A_AutoDetect(api_supportedgame* game, APIType engineapi) { - if (engineapi != QMM_API_DLLENTRY) +bool Q3A_GameSupport::AutoDetect(APIType engine_api) { + if (engine_api != QMM_API_DLLENTRY) return false; - if (!str_striequal(g_gameinfo.qmm_file, game->dllname)) + if (!str_striequal(g_gameinfo.qmm_file, this->DefaultDLLName())) return false; if (!str_stristr(g_gameinfo.exe_file, "quake3") && !str_stristr(g_gameinfo.exe_file, "q3ded")) @@ -42,20 +68,14 @@ static bool Q3A_AutoDetect(api_supportedgame* game, APIType engineapi) { } -// original syscall pointer that comes from the game engine -static eng_syscall orig_syscall = nullptr; - -// pointer to vmMain that comes from the mod -static mod_vmMain orig_vmMain = nullptr; - // wrapper syscall function that calls actual engine func in orig_syscall // this is how QMM and plugins will call into the engine -static intptr_t Q3A_syscall(intptr_t cmd, ...) { +intptr_t Q3A_GameSupport::syscall(intptr_t cmd, ...) { QMM_GET_SYSCALL_ARGS(); #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q3A_syscall({} {}) called\n", Q3A_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q3A_GameSupport::syscall({} {}) called\n", EngMsgName(cmd), cmd); #endif intptr_t ret = 0; @@ -87,7 +107,7 @@ static intptr_t Q3A_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q3A_syscall({} {}) returning {}\n", Q3A_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q3A_GameSupport::syscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; @@ -96,11 +116,11 @@ static intptr_t Q3A_syscall(intptr_t cmd, ...) { // wrapper vmMain function that calls actual mod func in orig_vmMain // this is how QMM and plugins will call into the mod -static intptr_t Q3A_vmMain(intptr_t cmd, ...) { +intptr_t Q3A_GameSupport::vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q3A_vmMain({} {}) called\n", Q3A_ModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q3A_GameSupport::vmMain({} {}) called\n", ModMsgName(cmd), cmd); #endif if (!orig_vmMain) @@ -119,32 +139,26 @@ static intptr_t Q3A_vmMain(intptr_t cmd, ...) { } #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q3A_vmMain({} {}) returning {}\n", Q3A_ModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q3A_GameSupport::vmMain({} {}) returning {}\n", ModMsgName(cmd), cmd, ret); #endif return ret; } -static void* Q3A_Entry(void* syscall, void*, APIType) { - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q3A_Entry({}) called\n", syscall); +void* Q3A_GameSupport::Entry(void* syscall, void*, APIType) { + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q3A_GameSupport::Entry({}) called\n", syscall); // store original syscall from engine orig_syscall = (eng_syscall)syscall; - // pointer to wrapper vmMain function that calls actual mod vmMain func - g_gameinfo.pfnvmMain = Q3A_vmMain; - - // pointer to wrapper syscall function that calls actual engine syscall func - g_gameinfo.pfnsyscall = Q3A_syscall; - - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q3A_Entry({}) returning\n", syscall); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q3A_GameSupport::Entry({}) returning\n", syscall); return nullptr; } -static bool Q3A_ModLoad(void* entry, APIType modapi) { +bool Q3A_GameSupport::ModLoad(void* entry, APIType modapi) { if (modapi != QMM_API_DLLENTRY && modapi != QMM_API_QVM) return false; @@ -154,12 +168,22 @@ static bool Q3A_ModLoad(void* entry, APIType modapi) { } -static void Q3A_ModUnload() { +void Q3A_GameSupport::ModUnload() { orig_vmMain = nullptr; } -static const char* Q3A_EngMsgNames(intptr_t cmd) { +int Q3A_GameSupport::QMMEngMsg(int msg) { + return this->qmm_eng_msgs[msg]; +} + + +int Q3A_GameSupport::QMMModMsg(int msg) { + return this->qmm_mod_msgs[msg]; +} + + +const char* Q3A_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINT); GEN_CASE(G_ERROR); @@ -368,7 +392,7 @@ static const char* Q3A_EngMsgNames(intptr_t cmd) { } -static const char* Q3A_ModMsgNames(intptr_t cmd) { +const char* Q3A_GameSupport::ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_INIT); GEN_CASE(GAME_SHUTDOWN); @@ -394,9 +418,9 @@ static const char* Q3A_ModMsgNames(intptr_t cmd) { */ // vec3_t are arrays, so convert them as pointers // for double pointers (gentity_t** and vec3_t*), convert them once with vmptr() -static int Q3A_QVMSyscall(uint8_t* membase, int cmd, int* args) { +int Q3A_GameSupport::QVMSyscall(uint8_t* membase, int cmd, int* args) { #ifdef _DEBUG - LOG(QMM_LOG_TRACE, "QMM") << fmt::format("Q3A_QVMSyscall({} {}) called\n", Q3A_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_TRACE, "QMM") << fmt::format("Q3A_GameSupport::QVMSyscall({} {}) called\n", EngMsgName(cmd), cmd); #endif intptr_t ret = 0; @@ -681,7 +705,7 @@ static int Q3A_QVMSyscall(uint8_t* membase, int cmd, int* args) { } #ifdef _DEBUG - LOG(QMM_LOG_TRACE, "QMM") << fmt::format("Q3A_QVMSyscall({} {}) returning {}\n", Q3A_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_TRACE, "QMM") << fmt::format("Q3A_GameSupport::QVMSyscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; diff --git a/src/game_quake2.cpp b/src/game_quake2.cpp index 8342edd..f2a72dd 100644 --- a/src/game_quake2.cpp +++ b/src/game_quake2.cpp @@ -226,7 +226,7 @@ static intptr_t QUAKE2_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("QUAKE2_syscall({} {}) called\n", QUAKE2_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("QUAKE2_syscall({} {}) called\n", QUAKE2_EngMsgName(cmd), cmd); #endif // update export vars before calling into the engine @@ -455,7 +455,7 @@ static intptr_t QUAKE2_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("QUAKE2_syscall({} {}) returning {}\n", QUAKE2_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("QUAKE2_syscall({} {}) returning {}\n", QUAKE2_EngMsgName(cmd), cmd, ret); #endif return ret; @@ -468,7 +468,7 @@ static intptr_t QUAKE2_vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("QUAKE2_vmMain({} {}) called\n", QUAKE2_ModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("QUAKE2_vmMain({} {}) called\n", QUAKE2_ModMsgName(cmd), cmd); #endif if (!orig_export) @@ -509,7 +509,7 @@ static intptr_t QUAKE2_vmMain(intptr_t cmd, ...) { s_update_export(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("QUAKE2_vmMain({} {}) returning {}\n", QUAKE2_ModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("QUAKE2_vmMain({} {}) returning {}\n", QUAKE2_ModMsgName(cmd), cmd, ret); #endif return ret; @@ -558,7 +558,7 @@ static void QUAKE2_ModUnload() { } -static const char* QUAKE2_EngMsgNames(intptr_t cmd) { +static const char* QUAKE2_EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_BPRINTF); GEN_CASE(G_DPRINTF); @@ -629,7 +629,7 @@ static const char* QUAKE2_EngMsgNames(intptr_t cmd) { } -static const char* QUAKE2_ModMsgNames(intptr_t cmd) { +static const char* QUAKE2_ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAMEV_APIVERSION); GEN_CASE(GAME_INIT); diff --git a/src/game_rtcwmp.cpp b/src/game_rtcwmp.cpp index c097a1b..5f441f6 100644 --- a/src/game_rtcwmp.cpp +++ b/src/game_rtcwmp.cpp @@ -54,7 +54,7 @@ static intptr_t RTCWMP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWMP_syscall({} {}) called\n", RTCWMP_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWMP_syscall({} {}) called\n", RTCWMP_EngMsgName(cmd), cmd); #endif intptr_t ret = 0; @@ -87,7 +87,7 @@ static intptr_t RTCWMP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWMP_syscall({} {}) returning {}\n", RTCWMP_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWMP_syscall({} {}) returning {}\n", RTCWMP_EngMsgName(cmd), cmd, ret); #endif return ret; @@ -100,7 +100,7 @@ static intptr_t RTCWMP_vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWMP_vmMain({} {}) called\n", RTCWMP_ModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWMP_vmMain({} {}) called\n", RTCWMP_ModMsgName(cmd), cmd); #endif if (!orig_vmMain) @@ -113,7 +113,7 @@ static intptr_t RTCWMP_vmMain(intptr_t cmd, ...) { ret = orig_vmMain(cmd, QMM_PUT_VMMAIN_ARGS()); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWMP_vmMain({} {}) returning {}\n", RTCWMP_ModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWMP_vmMain({} {}) returning {}\n", RTCWMP_ModMsgName(cmd), cmd, ret); #endif return ret; @@ -153,7 +153,7 @@ static void RTCWMP_ModUnload() { } -static const char* RTCWMP_EngMsgNames(intptr_t cmd) { +static const char* RTCWMP_EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINT); GEN_CASE(G_ERROR); @@ -373,7 +373,7 @@ static const char* RTCWMP_EngMsgNames(intptr_t cmd) { } -static const char* RTCWMP_ModMsgNames(intptr_t cmd) { +static const char* RTCWMP_ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_INIT); GEN_CASE(GAME_SHUTDOWN); @@ -404,7 +404,7 @@ static const char* RTCWMP_ModMsgNames(intptr_t cmd) { // for double pointers (gentity_t** and vec3_t*), convert them once with vmptr() static int RTCWMP_QVMSyscall(uint8_t* membase, int cmd, int* args) { #ifdef _DEBUG - LOG(QMM_LOG_TRACE, "QMM") << fmt::format("RTCWMP_QVMSyscall({} {}) called\n", RTCWMP_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_TRACE, "QMM") << fmt::format("RTCWMP_QVMSyscall({} {}) called\n", RTCWMP_EngMsgName(cmd), cmd); #endif intptr_t ret = 0; @@ -701,7 +701,7 @@ static int RTCWMP_QVMSyscall(uint8_t* membase, int cmd, int* args) { } #ifdef _DEBUG - LOG(QMM_LOG_TRACE, "QMM") << fmt::format("RTCWMP_QVMSyscall({} {}) returning {}\n", RTCWMP_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_TRACE, "QMM") << fmt::format("RTCWMP_QVMSyscall({} {}) returning {}\n", RTCWMP_EngMsgName(cmd), cmd, ret); #endif return ret; diff --git a/src/game_rtcwsp.cpp b/src/game_rtcwsp.cpp index 50cc4e6..d335599 100644 --- a/src/game_rtcwsp.cpp +++ b/src/game_rtcwsp.cpp @@ -71,7 +71,7 @@ static intptr_t RTCWSP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWSP_syscall({} {}) called\n", RTCWSP_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWSP_syscall({} {}) called\n", RTCWSP_EngMsgName(cmd), cmd); #endif intptr_t ret = 0; @@ -116,7 +116,7 @@ static intptr_t RTCWSP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWSP_syscall({} {}) returning {}\n", RTCWSP_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWSP_syscall({} {}) returning {}\n", RTCWSP_EngMsgName(cmd), cmd, ret); #endif return ret; @@ -129,7 +129,7 @@ static intptr_t RTCWSP_vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWSP_vmMain({} {}) called\n", RTCWSP_ModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWSP_vmMain({} {}) called\n", RTCWSP_ModMsgName(cmd), cmd); #endif if (!orig_vmMain) @@ -142,7 +142,7 @@ static intptr_t RTCWSP_vmMain(intptr_t cmd, ...) { ret = orig_vmMain(cmd, QMM_PUT_VMMAIN_ARGS()); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWSP_vmMain({} {}) returning {}\n", RTCWSP_ModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWSP_vmMain({} {}) returning {}\n", RTCWSP_ModMsgName(cmd), cmd, ret); #endif return ret; @@ -188,7 +188,7 @@ static void RTCWSP_ModUnload() { } -static const char* RTCWSP_EngMsgNames(intptr_t cmd) { +static const char* RTCWSP_EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINT); GEN_CASE(G_ERROR); @@ -400,7 +400,7 @@ static const char* RTCWSP_EngMsgNames(intptr_t cmd) { } -static const char* RTCWSP_ModMsgNames(intptr_t cmd) { +static const char* RTCWSP_ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_INIT); GEN_CASE(GAME_SHUTDOWN); diff --git a/src/game_sin.cpp b/src/game_sin.cpp index d7234f9..ca6f8ff 100644 --- a/src/game_sin.cpp +++ b/src/game_sin.cpp @@ -293,7 +293,7 @@ static intptr_t SIN_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SIN_syscall({} {}) called\n", SIN_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SIN_syscall({} {}) called\n", SIN_EngMsgName(cmd), cmd); #endif // update export vars before calling into the engine @@ -561,7 +561,7 @@ static intptr_t SIN_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SIN_syscall({} {}) returning {}\n", SIN_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SIN_syscall({} {}) returning {}\n", SIN_EngMsgName(cmd), cmd, ret); #endif return ret; @@ -574,7 +574,7 @@ static intptr_t SIN_vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SIN_vmMain({} {}) called\n", SIN_ModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SIN_vmMain({} {}) called\n", SIN_ModMsgName(cmd), cmd); #endif if (!orig_export) @@ -625,7 +625,7 @@ static intptr_t SIN_vmMain(intptr_t cmd, ...) { s_update_export(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SIN_vmMain({} {}) returning {}\n", SIN_ModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SIN_vmMain({} {}) returning {}\n", SIN_ModMsgName(cmd), cmd, ret); #endif return ret; @@ -675,7 +675,7 @@ static void SIN_ModUnload() { } -static const char* SIN_EngMsgNames(intptr_t cmd) { +static const char* SIN_EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_BPRINTF); GEN_CASE(G_DPRINTF); @@ -791,7 +791,7 @@ static const char* SIN_EngMsgNames(intptr_t cmd) { } -static const char* SIN_ModMsgNames(intptr_t cmd) { +static const char* SIN_ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAMEV_APIVERSION); GEN_CASE(GAME_INIT); diff --git a/src/game_sof2mp.cpp b/src/game_sof2mp.cpp index 891c5e4..bf449f8 100644 --- a/src/game_sof2mp.cpp +++ b/src/game_sof2mp.cpp @@ -56,7 +56,7 @@ static intptr_t SOF2MP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2MP_syscall({} {}) called\n", SOF2MP_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2MP_syscall({} {}) called\n", SOF2MP_EngMsgName(cmd), cmd); #endif intptr_t ret = 0; @@ -90,7 +90,7 @@ static intptr_t SOF2MP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2MP_syscall({} {}) returning {}\n", SOF2MP_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2MP_syscall({} {}) returning {}\n", SOF2MP_EngMsgName(cmd), cmd, ret); #endif return ret; @@ -103,7 +103,7 @@ static intptr_t SOF2MP_vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2MP_vmMain({} {}) called\n", SOF2MP_ModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2MP_vmMain({} {}) called\n", SOF2MP_ModMsgName(cmd), cmd); #endif if (!orig_vmMain) @@ -156,7 +156,7 @@ static intptr_t SOF2MP_vmMain(intptr_t cmd, ...) { } #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2MP_vmMain({} {}) returning {}\n", SOF2MP_ModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2MP_vmMain({} {}) returning {}\n", SOF2MP_ModMsgName(cmd), cmd, ret); #endif return ret; @@ -201,7 +201,7 @@ static void SOF2MP_ModUnload() { } -static const char* SOF2MP_EngMsgNames(intptr_t cmd) { +static const char* SOF2MP_EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINT); GEN_CASE(G_ERROR); @@ -474,7 +474,7 @@ static const char* SOF2MP_EngMsgNames(intptr_t cmd) { } -static const char* SOF2MP_ModMsgNames(intptr_t cmd) { +static const char* SOF2MP_ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_INIT); GEN_CASE(GAME_SHUTDOWN); @@ -508,7 +508,7 @@ static const char* SOF2MP_ModMsgNames(intptr_t cmd) { // for double pointers (gentity_t**, vec3_t*, void**), convert them once with vmptr() static int SOF2MP_QVMSyscall(uint8_t* membase, int cmd, int* args) { #ifdef _DEBUG - LOG(QMM_LOG_TRACE, "QMM") << fmt::format("SOF2MP_QVMSyscall({} {}) called\n", SOF2MP_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_TRACE, "QMM") << fmt::format("SOF2MP_QVMSyscall({} {}) called\n", SOF2MP_EngMsgName(cmd), cmd); #endif int ret = 0; @@ -880,7 +880,7 @@ static int SOF2MP_QVMSyscall(uint8_t* membase, int cmd, int* args) { } #ifdef _DEBUG - LOG(QMM_LOG_TRACE, "QMM") << fmt::format("SOF2MP_QVMSyscall({} {}) returning {}\n", SOF2MP_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_TRACE, "QMM") << fmt::format("SOF2MP_QVMSyscall({} {}) returning {}\n", SOF2MP_EngMsgName(cmd), cmd, ret); #endif return ret; diff --git a/src/game_sof2sp.cpp b/src/game_sof2sp.cpp index 755c6f9..54c7934 100644 --- a/src/game_sof2sp.cpp +++ b/src/game_sof2sp.cpp @@ -214,7 +214,7 @@ static intptr_t SOF2SP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2SP_syscall({} {}) called\n", SOF2SP_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2SP_syscall({} {}) called\n", SOF2SP_EngMsgName(cmd), cmd); #endif // update export vars before calling into the engine @@ -376,7 +376,7 @@ static intptr_t SOF2SP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2SP_syscall({} {}) returning {}\n", SOF2SP_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2SP_syscall({} {}) returning {}\n", SOF2SP_EngMsgName(cmd), cmd, ret); #endif return ret; @@ -389,7 +389,7 @@ static intptr_t SOF2SP_vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2SP_vmMain({} {}) called\n", SOF2SP_ModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2SP_vmMain({} {}) called\n", SOF2SP_ModMsgName(cmd), cmd); #endif if (!orig_export) @@ -435,7 +435,7 @@ static intptr_t SOF2SP_vmMain(intptr_t cmd, ...) { s_update_export(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2SP_vmMain({} {}) returning {}\n", SOF2SP_ModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2SP_vmMain({} {}) returning {}\n", SOF2SP_ModMsgName(cmd), cmd, ret); #endif return ret; @@ -486,7 +486,7 @@ static void SOF2SP_ModUnload() { } -static const char* SOF2SP_EngMsgNames(intptr_t cmd) { +static const char* SOF2SP_EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINTF); GEN_CASE(G_DPRINTF); @@ -611,7 +611,7 @@ static const char* SOF2SP_EngMsgNames(intptr_t cmd) { } -static const char* SOF2SP_ModMsgNames(intptr_t cmd) { +static const char* SOF2SP_ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_INIT); GEN_CASE(GAME_SHUTDOWN); diff --git a/src/game_stef2.cpp b/src/game_stef2.cpp index b6f9652..e9a2716 100644 --- a/src/game_stef2.cpp +++ b/src/game_stef2.cpp @@ -491,7 +491,7 @@ static intptr_t STEF2_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STEF2_syscall({} {}) called\n", STEF2_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STEF2_syscall({} {}) called\n", STEF2_EngMsgName(cmd), cmd); #endif // update export vars before calling into the engine @@ -882,7 +882,7 @@ static intptr_t STEF2_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STEF2_syscall({} {}) returning {}\n", STEF2_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STEF2_syscall({} {}) returning {}\n", STEF2_EngMsgName(cmd), cmd, ret); #endif return ret; @@ -895,7 +895,7 @@ static intptr_t STEF2_vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STEF2_vmMain({} {}) called\n", STEF2_ModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STEF2_vmMain({} {}) called\n", STEF2_ModMsgName(cmd), cmd); #endif if (!orig_export) @@ -953,7 +953,7 @@ static intptr_t STEF2_vmMain(intptr_t cmd, ...) { s_update_export(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STEF2_vmMain({} {}) returning {}\n", STEF2_ModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STEF2_vmMain({} {}) returning {}\n", STEF2_ModMsgName(cmd), cmd, ret); #endif return ret; @@ -1003,7 +1003,7 @@ static void STEF2_ModUnload() { } -static const char* STEF2_EngMsgNames(intptr_t cmd) { +static const char* STEF2_EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINTF); GEN_CASE(G_DPRINTF); @@ -1356,7 +1356,7 @@ static const char* STEF2_EngMsgNames(intptr_t cmd) { } -static const char* STEF2_ModMsgNames(intptr_t cmd) { +static const char* STEF2_ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAMEV_APIVERSION); GEN_CASE(GAME_INIT); diff --git a/src/game_stvoyhm.cpp b/src/game_stvoyhm.cpp index ee02c58..2c3f4ff 100644 --- a/src/game_stvoyhm.cpp +++ b/src/game_stvoyhm.cpp @@ -55,7 +55,7 @@ static intptr_t STVOYHM_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYHM_syscall({} {}) called\n", STVOYHM_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYHM_syscall({} {}) called\n", STVOYHM_EngMsgName(cmd), cmd); #endif intptr_t ret = 0; @@ -88,7 +88,7 @@ static intptr_t STVOYHM_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYHM_syscall({} {}) returning {}\n", STVOYHM_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYHM_syscall({} {}) returning {}\n", STVOYHM_EngMsgName(cmd), cmd, ret); #endif return ret; @@ -101,7 +101,7 @@ static intptr_t STVOYHM_vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYHM_vmMain({} {}) called\n", STVOYHM_ModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYHM_vmMain({} {}) called\n", STVOYHM_ModMsgName(cmd), cmd); #endif if (!orig_vmMain) @@ -120,7 +120,7 @@ static intptr_t STVOYHM_vmMain(intptr_t cmd, ...) { } #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYHM_vmMain({} {}) returning {}\n", STVOYHM_ModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYHM_vmMain({} {}) returning {}\n", STVOYHM_ModMsgName(cmd), cmd, ret); #endif return ret; @@ -160,7 +160,7 @@ static void STVOYHM_ModUnload() { } -static const char* STVOYHM_EngMsgNames(intptr_t cmd) { +static const char* STVOYHM_EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINT); GEN_CASE(G_ERROR); @@ -360,7 +360,7 @@ static const char* STVOYHM_EngMsgNames(intptr_t cmd) { } -static const char* STVOYHM_ModMsgNames(intptr_t cmd) { +static const char* STVOYHM_ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_INIT); GEN_CASE(GAME_SHUTDOWN); @@ -388,7 +388,7 @@ static const char* STVOYHM_ModMsgNames(intptr_t cmd) { // for double pointers (gentity_t** and vec3_t*), convert them once with vmptr() static int STVOYHM_QVMSyscall(uint8_t* membase, int cmd, int* args) { #ifdef _DEBUG - LOG(QMM_LOG_TRACE, "QMM") << fmt::format("STVOYHM_QVMSyscall({} {}) called\n", STVOYHM_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_TRACE, "QMM") << fmt::format("STVOYHM_QVMSyscall({} {}) called\n", STVOYHM_EngMsgName(cmd), cmd); #endif int ret = 0; @@ -653,7 +653,7 @@ static int STVOYHM_QVMSyscall(uint8_t* membase, int cmd, int* args) { } #ifdef _DEBUG - LOG(QMM_LOG_TRACE, "QMM") << fmt::format("STVOYHM_QVMSyscall({} {}) returning {}\n", STVOYHM_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_TRACE, "QMM") << fmt::format("STVOYHM_QVMSyscall({} {}) returning {}\n", STVOYHM_EngMsgName(cmd), cmd, ret); #endif return ret; diff --git a/src/game_stvoysp.cpp b/src/game_stvoysp.cpp index ac35581..979d25d 100644 --- a/src/game_stvoysp.cpp +++ b/src/game_stvoysp.cpp @@ -169,7 +169,7 @@ static intptr_t STVOYSP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYSP_syscall({} {}) called\n", STVOYSP_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYSP_syscall({} {}) called\n", STVOYSP_EngMsgName(cmd), cmd); #endif // update export vars before calling into the engine @@ -290,7 +290,7 @@ static intptr_t STVOYSP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYSP_syscall({} {}) returning {}\n", STVOYSP_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYSP_syscall({} {}) returning {}\n", STVOYSP_EngMsgName(cmd), cmd, ret); #endif return ret; @@ -303,7 +303,7 @@ static intptr_t STVOYSP_vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYSP_vmMain({} {}) called\n", STVOYSP_ModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYSP_vmMain({} {}) called\n", STVOYSP_ModMsgName(cmd), cmd); #endif if (!orig_export) @@ -341,7 +341,7 @@ static intptr_t STVOYSP_vmMain(intptr_t cmd, ...) { s_update_export(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYSP_vmMain({} {}) returning {}\n", STVOYSP_ModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYSP_vmMain({} {}) returning {}\n", STVOYSP_ModMsgName(cmd), cmd, ret); #endif return ret; @@ -390,7 +390,7 @@ static void STVOYSP_ModUnload() { } -static const char* STVOYSP_EngMsgNames(intptr_t cmd) { +static const char* STVOYSP_EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINTF); GEN_CASE(G_WRITECAM); @@ -447,7 +447,7 @@ static const char* STVOYSP_EngMsgNames(intptr_t cmd) { } -static const char* STVOYSP_ModMsgNames(intptr_t cmd) { +static const char* STVOYSP_ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAMEV_APIVERSION); GEN_CASE(GAME_INIT); diff --git a/src/game_wet.cpp b/src/game_wet.cpp index 5f7d64a..e069ae3 100644 --- a/src/game_wet.cpp +++ b/src/game_wet.cpp @@ -54,7 +54,7 @@ static intptr_t WET_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("WET_syscall({} {}) called\n", WET_EngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("WET_syscall({} {}) called\n", WET_EngMsgName(cmd), cmd); #endif intptr_t ret = 0; @@ -87,7 +87,7 @@ static intptr_t WET_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("WET_syscall({} {}) returning {}\n", WET_EngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("WET_syscall({} {}) returning {}\n", WET_EngMsgName(cmd), cmd, ret); #endif return ret; @@ -100,7 +100,7 @@ static intptr_t WET_vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("WET_vmMain({} {}) called\n", WET_ModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("WET_vmMain({} {}) called\n", WET_ModMsgName(cmd), cmd); #endif if (!orig_vmMain) @@ -113,7 +113,7 @@ static intptr_t WET_vmMain(intptr_t cmd, ...) { ret = orig_vmMain(cmd, QMM_PUT_VMMAIN_ARGS()); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("WET_vmMain({} {}) returning {}\n", WET_ModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("WET_vmMain({} {}) returning {}\n", WET_ModMsgName(cmd), cmd, ret); #endif return ret; @@ -153,7 +153,7 @@ static void WET_ModUnload() { } -static const char* WET_EngMsgNames(intptr_t cmd) { +static const char* WET_EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINT); GEN_CASE(G_ERROR); @@ -384,7 +384,7 @@ static const char* WET_EngMsgNames(intptr_t cmd) { } -static const char* WET_ModMsgNames(intptr_t cmd) { +static const char* WET_ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_INIT); GEN_CASE(GAME_SHUTDOWN); diff --git a/src/main.cpp b/src/main.cpp index 11caece..14555ce 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -99,9 +99,9 @@ C_DLLEXPORT void dllEntry(void* syscall) { return; } - // store the given syscall pointer as a backup (should get overwritten in game-specific entry handler) - // also if we don't detect a game, we can call syscall(G_ERROR) to shutdown in vmMain - g_gameinfo.pfnsyscall = (eng_syscall)syscall; + // store the given syscall pointer as a backup. + // this is used in case we couldn't detect a game and have to call syscall(G_ERROR) to shutdown in vmMain + g_gameinfo.syscall = (eng_syscall)syscall; main_handle_entry(syscall, nullptr, QMM_API_DLLENTRY); return; @@ -260,14 +260,18 @@ C_DLLEXPORT intptr_t vmMain(intptr_t cmd, ...) { if (!g_gameinfo.game) { if (!g_gameinfo.is_shutdown) { g_gameinfo.is_shutdown = true; - // if the syscall passed to dllEntry was null, this will crash, but this shouldn't happen - ENG_SYSCALL(QMM_FAIL_G_ERROR, "\nFatal QMM Error:\nQMM was unable to determine the game engine.\nPlease set the \"game\" option in qmm2.json.\nRefer to the documentation for more information.\n"); + // if syscall passed to dllEntry was null, revert to std::exit because *shrug* + if (!g_gameinfo.syscall) { + + std::exit(-1); + } + g_gameinfo.syscall(QMM_FAIL_G_ERROR, "\nFatal QMM Error:\nQMM was unable to determine the game engine.\nPlease set the \"game\" option in qmm2.json.\nRefer to the documentation for more information.\n"); } return 0; } #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("vmMain({} {}) called\n", g_gameinfo.game->funcs->pfnModMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("vmMain({} {}) called\n", g_gameinfo.game->ModMsgName(cmd), cmd); #endif if (cmd == msg_GAME_INIT) { @@ -283,14 +287,14 @@ C_DLLEXPORT intptr_t vmMain(intptr_t cmd, ...) { // get mod dir from engine char moddir[256]; - ENG_SYSCALL(QMM_ENG_MSG[QMM_G_CVAR_VARIABLE_STRING_BUFFER], "fs_game", moddir, sizeof(moddir)); + ENG_SYSCALL(QMM_ENG_MSG(QMM_G_CVAR_VARIABLE_STRING_BUFFER), "fs_game", moddir, sizeof(moddir)); moddir[sizeof(moddir) - 1] = '\0'; g_gameinfo.mod_dir = moddir; // the default mod (including all singleplayer games) returns "" for the fs_game, so grab the default mod dir from game info instead if (g_gameinfo.mod_dir.empty()) - g_gameinfo.mod_dir = g_gameinfo.game->moddir; + g_gameinfo.mod_dir = g_gameinfo.game->DefaultModDir(); - LOG(QMM_LOG_INFO, "QMM") << fmt::format("Game: {}/\"{}\" (Source: {})\n", g_gameinfo.game->gamename_short, g_gameinfo.game->gamename_long, g_gameinfo.is_auto_detected ? "Auto-detected" : "Config file"); + LOG(QMM_LOG_INFO, "QMM") << fmt::format("Game: {}/\"{}\" (Source: {})\n", g_gameinfo.game->GameCode(), g_gameinfo.game->GameName(), g_gameinfo.is_auto_detected ? "Auto-detected" : "Config file"); LOG(QMM_LOG_INFO, "QMM") << fmt::format("ModDir: {}\n", g_gameinfo.mod_dir); LOG(QMM_LOG_INFO, "QMM") << fmt::format("Config file: \"{}\" {}\n", g_gameinfo.cfg_path, g_cfg.is_discarded() ? "(error)" : ""); @@ -298,7 +302,7 @@ C_DLLEXPORT intptr_t vmMain(intptr_t cmd, ...) { LOG(QMM_LOG_INFO, "QMM") << "URL: " QMM_URL "\n"; // create qmm_version cvar - ENG_SYSCALL(QMM_ENG_MSG[QMM_G_CVAR_REGISTER], nullptr, "qmm_version", "v" QMM_VERSION, QMM_ENG_MSG[QMM_CVAR_ROM] | QMM_ENG_MSG[QMM_CVAR_SERVERINFO]); + ENG_SYSCALL(QMM_ENG_MSG(QMM_G_CVAR_REGISTER), nullptr, "qmm_version", "v" QMM_VERSION, QMM_ENG_MSG(QMM_CVAR_ROM) | QMM_ENG_MSG(QMM_CVAR_SERVERINFO)); // load mod std::string cfg_mod = cfg_get_string(g_cfg, "mod", "auto"); @@ -306,7 +310,7 @@ C_DLLEXPORT intptr_t vmMain(intptr_t cmd, ...) { cfg_mod = util_get_cmdline_arg("--qmm_mod", cfg_mod); if (!main_load_mod(cfg_mod)) { g_gameinfo.is_shutdown = true; - LOG(QMM_LOG_FATAL, "QMM") << fmt::format("vmMain({}): Unable to load mod using \"{}\"\n", g_gameinfo.game->funcs->pfnModMsgNames(cmd), cfg_mod); + LOG(QMM_LOG_FATAL, "QMM") << fmt::format("vmMain({}): Unable to load mod using \"{}\"\n", g_gameinfo.game->ModMsgName(cmd), cfg_mod); ENG_SYSCALL(QMM_FAIL_G_ERROR, "\nFatal QMM Error:\nQMM was unable to load the mod file.\nPlease set the \"mod\" option in qmm2.json.\nRefer to the documentation for more information.\n"); return 0; } @@ -341,7 +345,7 @@ C_DLLEXPORT intptr_t vmMain(intptr_t cmd, ...) { std::string cfg_execcfg = cfg_get_string(g_cfg, "execcfg", "qmmexec.cfg"); if (!cfg_execcfg.empty()) { LOG(QMM_LOG_NOTICE, "QMM") << fmt::format("Executing config file \"{}\"\n", cfg_execcfg); - ENG_SYSCALL(QMM_ENG_MSG[QMM_G_SEND_CONSOLE_COMMAND], QMM_ENG_MSG[QMM_EXEC_APPEND], fmt::format("exec {}\n", cfg_execcfg).c_str()); + ENG_SYSCALL(QMM_ENG_MSG(QMM_G_SEND_CONSOLE_COMMAND), QMM_ENG_MSG(QMM_EXEC_APPEND), fmt::format("exec {}\n", cfg_execcfg).c_str()); } // we're done! @@ -398,7 +402,7 @@ C_DLLEXPORT intptr_t vmMain(intptr_t cmd, ...) { } #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("vmMain({} {}) returning {}\n", g_gameinfo.game->funcs->pfnModMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("vmMain({} {}) returning {}\n", g_gameinfo.game->ModMsgName(cmd), cmd, ret); #endif return ret; @@ -414,14 +418,14 @@ intptr_t qmm_syscall(intptr_t cmd, ...) { QMM_GET_SYSCALL_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("syscall({} {}) called\n", g_gameinfo.game->funcs->pfnEngMsgNames(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("syscall({} {}) called\n", g_gameinfo.game->EngMsgName(cmd), cmd); #endif // route call to plugins and mod intptr_t ret = main_route(true, cmd, args); // true = is_syscall #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("syscall({} {}) returning {}\n", g_gameinfo.game->funcs->pfnEngMsgNames(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("syscall({} {}) returning {}\n", g_gameinfo.game->EngMsgName(cmd), cmd, ret); #endif return ret; @@ -475,34 +479,15 @@ static void* main_handle_entry(void* import, void* extra, APIType engine) { return nullptr; } - // make sure game support has all required arrays and functions - if (!g_gameinfo.game->funcs->pfnEntry - || !g_gameinfo.game->funcs->qmm_eng_msgs - || !g_gameinfo.game->funcs->qmm_mod_msgs - || !g_gameinfo.game->funcs->pfnEngMsgNames - || !g_gameinfo.game->funcs->pfnModMsgNames - || !g_gameinfo.game->funcs->pfnModLoad - || !g_gameinfo.game->funcs->pfnModUnload) - { - LOG(QMM_LOG_FATAL, "QMM") << fmt::format("{}(): Missing required function(s) for game \"{}\"\n", APIType_Function(engine), g_gameinfo.game->gamename_short); - return nullptr; - } - // now that the game is detected, cache some dynamic message values that get evaluated a lot - msg_G_PRINT = QMM_ENG_MSG[QMM_G_PRINT]; - msg_GAME_INIT = QMM_MOD_MSG[QMM_GAME_INIT]; - msg_GAME_CONSOLE_COMMAND = QMM_MOD_MSG[QMM_GAME_CONSOLE_COMMAND]; - msg_GAME_SHUTDOWN = QMM_MOD_MSG[QMM_GAME_SHUTDOWN]; + msg_G_PRINT = QMM_ENG_MSG(QMM_G_PRINT); + msg_GAME_INIT = QMM_MOD_MSG(QMM_GAME_INIT); + msg_GAME_CONSOLE_COMMAND = QMM_MOD_MSG(QMM_GAME_CONSOLE_COMMAND); + msg_GAME_SHUTDOWN = QMM_MOD_MSG(QMM_GAME_SHUTDOWN); // call the game-specific entry handler (e.g. Q3A_Entry) which will set up the internals to interact // the engine and the mod - void* ret = g_gameinfo.game->funcs->pfnEntry(import, extra, engine); - - // make sure that g_gameinfo.pfnsyscall and g_gameinfo.pfnvmMain were set by the entry handler - if (!g_gameinfo.pfnsyscall || !g_gameinfo.pfnvmMain) { - LOG(QMM_LOG_FATAL, "QMM") << fmt::format("{}(): Game \"{}\" did not set up syscall or vmMain handlers\n", APIType_Function(engine), g_gameinfo.game->gamename_short); - return nullptr; - } + void* ret = g_gameinfo.game->Entry(import, extra, engine); return ret; } @@ -574,25 +559,22 @@ static void main_detect_game(std::string cfg_game, APIType engine) { if (cfg_game.empty()) cfg_game = "auto"; - for (api_supportedgame& game : api_supportedgames) { + // for (api_supportedgame& game : api_supportedgames) { + for (GameSupport* game : api_supportedgames) { // if short name matches config option, we found it! - if (str_striequal(cfg_game, game.gamename_short)) { + if (str_striequal(cfg_game, game->GameCode())) { LOG(QMM_LOG_NOTICE, "QMM") << fmt::format("Found game match for config option \"{}\"\n", cfg_game); - g_gameinfo.game = &game; + g_gameinfo.game = game; g_gameinfo.is_auto_detected = false; - // call the game's auto-detect function if it exists, since it may do some logic - if (game.funcs->pfnAutoDetect) - (void)game.funcs->pfnAutoDetect(&game, engine); + // call the game's auto-detect function, since it may do some logic + (void)game->AutoDetect(engine); return; } - // otherwise, if auto, call the game's auto-detect function if available - if (str_striequal(cfg_game, "auto") - && game.funcs->pfnAutoDetect - && game.funcs->pfnAutoDetect(&game, engine)) - { - LOG(QMM_LOG_INFO, "QMM") << fmt::format("Found game match with auto-detection - \"{}\"\n", game.gamename_short); - g_gameinfo.game = &game; + // otherwise, if auto, call the game's auto-detect function + if (str_striequal(cfg_game, "auto") && game->AutoDetect(engine)) { + LOG(QMM_LOG_INFO, "QMM") << fmt::format("Found game match with auto-detection - \"{}\"\n", game->GameCode()); + g_gameinfo.game = game; g_gameinfo.is_auto_detected = true; return; } @@ -625,10 +607,10 @@ static bool main_load_mod(std::string cfg_mod) { // if "mod" config setting was "auto" if (str_striequal(cfg_mod, "auto")) { // treat as if "mod" config setting was "qmm_" plus the default dll name for this engine - cfg_mod = fmt::format("qmm_{}", g_gameinfo.game->dllname); + cfg_mod = fmt::format("qmm_{}", g_gameinfo.game->DefaultDLLName()); // add QVM filename to search list if this game supports it - if (g_gameinfo.game->qvmname) - try_paths.push_back(g_gameinfo.game->qvmname); + if (g_gameinfo.game->DefaultQVMName()) + try_paths.push_back(g_gameinfo.game->DefaultQVMName()); } // if "mod" config setting was a relative path, do nothing special try_paths.push_back(fmt::format("{}/{}", g_gameinfo.qmm_dir, cfg_mod)); @@ -688,14 +670,14 @@ static bool main_load_plugin(std::string plugin_path) { static void main_handle_command_qmm(intptr_t arg_start) { char arg1[10] = "", arg2[10] = ""; - int argc = (int)ENG_SYSCALL(QMM_ENG_MSG[QMM_G_ARGC]); + int argc = (int)ENG_SYSCALL(QMM_ENG_MSG(QMM_G_ARGC)); qmm_argv(arg_start + 1, arg1, sizeof(arg1)); if (argc > arg_start + 2) qmm_argv(arg_start + 2, arg2, sizeof(arg2)); if (str_striequal("status", arg1) || str_striequal("info", arg1)) { CONSOLE_PRINT("(QMM) QMM v" QMM_VERSION " (" QMM_OS " " QMM_ARCH ")\n"); - CONSOLE_PRINTF("(QMM) Game: {}/\"{}\" ({}) (Source: {})\n", g_gameinfo.game->gamename_short, g_gameinfo.game->gamename_long, APIType_Function(g_gameinfo.api), g_gameinfo.is_auto_detected ? "Auto-detected" : "Config file"); + CONSOLE_PRINTF("(QMM) Game: {}/\"{}\" ({}) (Source: {})\n", g_gameinfo.game->GameCode(), g_gameinfo.game->GameName(), APIType_Function(g_gameinfo.api), g_gameinfo.is_auto_detected ? "Auto-detected" : "Config file"); CONSOLE_PRINTF("(QMM) ModDir: {}\n", g_gameinfo.mod_dir); CONSOLE_PRINTF("(QMM) Config file: \"{}\" {}\n", g_gameinfo.cfg_path, g_cfg.empty() ? "(error)" : ""); CONSOLE_PRINT("(QMM) Built: " QMM_COMPILE " by " QMM_BUILDER "\n"); @@ -773,22 +755,18 @@ static void main_handle_command_qmm(intptr_t arg_start) { // route syscall or vmMain call to plugins and mod static intptr_t main_route(bool is_syscall, intptr_t cmd, intptr_t* args) { - const char* (*msg_names)(intptr_t) = nullptr; - const char* func_name = nullptr; + const char* msg_name; + const char* func_name; if (is_syscall) { - msg_names = g_gameinfo.game->funcs->pfnEngMsgNames; + msg_name = g_gameinfo.game->EngMsgName(cmd); func_name = "syscall"; } else { - msg_names = g_gameinfo.game->funcs->pfnModMsgNames; + msg_name = g_gameinfo.game->ModMsgName(cmd); func_name = "vmMain"; } -#ifdef _DEBUG - const char* msg_name = msg_names(cmd); -#endif - // store max result plugin_res max_result = QMM_UNUSED; // return values from a plugin call @@ -824,9 +802,9 @@ static intptr_t main_route(bool is_syscall, intptr_t cmd, intptr_t* args) { g_plugin_globals.high_result = max_result; // invalid/error result values if (g_plugin_globals.plugin_result == QMM_UNUSED) - LOG(QMM_LOG_WARNING, "QMM") << fmt::format("{}({}): Plugin \"{}\" did not set result flag\n", func_name, msg_names(cmd), p.plugininfo->name); + LOG(QMM_LOG_WARNING, "QMM") << fmt::format("{}({}): Plugin \"{}\" did not set result flag\n", func_name, msg_name, p.plugininfo->name); else if (g_plugin_globals.plugin_result == QMM_ERROR) - LOG(QMM_LOG_ERROR, "QMM") << fmt::format("{}({}): Plugin \"{}\" set result flag QMM_ERROR\n", func_name, msg_names(cmd), p.plugininfo->name); + LOG(QMM_LOG_ERROR, "QMM") << fmt::format("{}({}): Plugin \"{}\" set result flag QMM_ERROR\n", func_name, msg_name, p.plugininfo->name); // if plugin resulted in QMM_OVERRIDE or QMM_SUPERCEDE, set final_ret to this return value else if (g_plugin_globals.plugin_result >= QMM_OVERRIDE) @@ -838,10 +816,10 @@ static intptr_t main_route(bool is_syscall, intptr_t cmd, intptr_t* args) { #ifdef _DEBUG LOG(QMM_LOG_TRACE, "QMM") << fmt::format("Real {}({} {}) called\n", func_name, msg_name, cmd); #endif - if (is_syscall && g_gameinfo.pfnsyscall) - real_ret = g_gameinfo.pfnsyscall(cmd, QMM_PUT_SYSCALL_ARGS()); - else if (!is_syscall && g_gameinfo.pfnvmMain) - real_ret = g_gameinfo.pfnvmMain(cmd, QMM_PUT_VMMAIN_ARGS()); + if (is_syscall) + real_ret = g_gameinfo.game->syscall(cmd, QMM_PUT_SYSCALL_ARGS()); + else + real_ret = g_gameinfo.game->vmMain(cmd, QMM_PUT_VMMAIN_ARGS()); #ifdef _DEBUG LOG(QMM_LOG_TRACE, "QMM") << fmt::format("Real {}({} {}) returning {}\n", func_name, msg_name, cmd, real_ret); @@ -882,7 +860,7 @@ static intptr_t main_route(bool is_syscall, intptr_t cmd, intptr_t* args) { // ignore QMM_UNUSED so plugins can just use return, but still show a message for QMM_ERROR if (g_plugin_globals.plugin_result == QMM_ERROR) - LOG(QMM_LOG_ERROR, "QMM") << fmt::format("syscall({}): Plugin \"{}\" set result flag QMM_ERROR\n", msg_names(cmd), p.plugininfo->name); + LOG(QMM_LOG_ERROR, "QMM") << fmt::format("syscall({}): Plugin \"{}\" set result flag QMM_ERROR\n", msg_name, p.plugininfo->name); // if plugin resulted in QMM_OVERRIDE or QMM_SUPERCEDE, set final_ret to this return value else if (g_plugin_globals.plugin_result >= QMM_OVERRIDE) @@ -901,7 +879,7 @@ void qmm_argv(intptr_t argn, char* buf, intptr_t buflen) { // void trap_Argv(int argn, char* buffer, int bufferSize); // some games don't return pointers because of QVM interaction, so if this returns anything but null // (or true?), we probably are in an api game, and need to get the arg from the return value instead - intptr_t ret = ENG_SYSCALL(QMM_ENG_MSG[QMM_G_ARGV], argn, buf, buflen); + intptr_t ret = ENG_SYSCALL(QMM_ENG_MSG(QMM_G_ARGV), argn, buf, buflen); if (ret > 1) strncpyz(buf, (const char*)ret, (size_t)buflen); } @@ -929,8 +907,10 @@ C_DLLEXPORT void* GetCGameAPI(void* import) { // is loaded, so just get the return value from the mod's GetCGameAPI() function directly if (g_gameinfo.game) { // ?? - if (!g_mod.dll) + if (!g_mod.dll) { + LOG(QMM_LOG_DEBUG, "QMM") << "GetCGameAPI() called! Mod DLL not loaded?\n"; return nullptr; + } LOG(QMM_LOG_DEBUG, "QMM") << "GetCGameAPI() called! Passing on call to mod DLL.\n"; mod_GetGameAPI pfnGCGA = (mod_GetGameAPI)dll_symbol(g_mod.dll, "GetCGameAPI"); return pfnGCGA ? pfnGCGA(import, nullptr) : nullptr; diff --git a/src/mod.cpp b/src/mod.cpp index 4cc97ca..4a6539f 100644 --- a/src/mod.cpp +++ b/src/mod.cpp @@ -41,7 +41,7 @@ bool mod_load(qmm_mod& mod, std::string file) { std::string ext = path_baseext(file); // only allow qvm mods if the game engine supports it - if (str_striequal(ext, EXT_QVM) && g_gameinfo.game->funcs->pfnQVMSyscall) { + if (str_striequal(ext, EXT_QVM) && g_gameinfo.game->DefaultQVMName()) { return s_mod_load_qvm(mod); } // if DLL @@ -83,7 +83,7 @@ bool mod_load(qmm_mod& mod, std::string file) { void mod_unload(qmm_mod& mod) { // call the game-specific mod unload callback - g_gameinfo.game->funcs->pfnModUnload(); + g_gameinfo.game->ModUnload(); qvm_unload(&mod.vm); if (mod.dll) dll_close(mod.dll); @@ -97,8 +97,8 @@ static intptr_t s_mod_qvm_vmmain(intptr_t cmd, ...) { if (!g_mod.vm.memory) { if (!g_gameinfo.is_shutdown) { g_gameinfo.is_shutdown = true; - LOG(QMM_LOG_FATAL, "QMM") << fmt::format("s_mod_vmmain({}): QVM unloaded during previous execution due to a run-time error\n", g_gameinfo.game->funcs->pfnModMsgNames(cmd)); - ENG_SYSCALL(QMM_ENG_MSG[QMM_G_ERROR], "\nFatal QMM Error:\nThe QVM was unloaded during previous execution due to a run-time error.\n"); + LOG(QMM_LOG_FATAL, "QMM") << fmt::format("s_mod_vmmain({}): QVM unloaded during previous execution due to a run-time error\n", g_gameinfo.game->ModMsgName(cmd)); + ENG_SYSCALL(QMM_ENG_MSG(QMM_G_ERROR), "\nFatal QMM Error:\nThe QVM was unloaded during previous execution due to a run-time error.\n"); } return 0; } @@ -118,16 +118,6 @@ static intptr_t s_mod_qvm_vmmain(intptr_t cmd, ...) { // handle syscalls from the QVM. passed to qvm_load static int s_mod_qvm_syscall(uint8_t* membase, int cmd, int* args) { - // if no game-specific qvm handler, we need to error - if (!g_gameinfo.game->funcs->pfnQVMSyscall) { - if (!g_gameinfo.is_shutdown) { - g_gameinfo.is_shutdown = true; - LOG(QMM_LOG_FATAL, "QMM") << fmt::format("s_mod_qvm_syscall({}): No QVM syscall handler found\n", g_gameinfo.game->funcs->pfnEngMsgNames(cmd)); - ENG_SYSCALL(QMM_ENG_MSG[QMM_G_ERROR], "\nFatal QMM Error:\nNo QVM syscall handler found.\n"); - } - return 0; - } - // check for plugin qvm function registration if (cmd >= QMM_QVM_FUNC_STARTING_ID && g_registered_qvm_funcs.count(cmd)) { qmm_plugin* p = g_registered_qvm_funcs[cmd]; @@ -141,7 +131,7 @@ static int s_mod_qvm_syscall(uint8_t* membase, int cmd, int* args) { } // call the game-specific QVM syscall handler - return g_gameinfo.game->funcs->pfnQVMSyscall(membase, cmd, args); + return g_gameinfo.game->QVMSyscall(membase, cmd, args); } @@ -154,17 +144,17 @@ static bool s_mod_load_qvm(qmm_mod& mod) { bool loaded; // load file using engine functions to read into pk3s if necessary - filelen = ENG_SYSCALL(QMM_ENG_MSG[QMM_G_FS_FOPEN_FILE], mod.path.c_str(), &fpk3, QMM_ENG_MSG[QMM_FS_READ]); + filelen = ENG_SYSCALL(QMM_ENG_MSG(QMM_G_FS_FOPEN_FILE), mod.path.c_str(), &fpk3, QMM_ENG_MSG(QMM_FS_READ)); if (filelen <= 0 || !fpk3) { LOG(QMM_LOG_ERROR, "QMM") << fmt::format("mod_load(\"{}\"): Could not open QVM for reading\n", mod.path); if (fpk3) - ENG_SYSCALL(QMM_ENG_MSG[QMM_G_FS_FCLOSE_FILE], fpk3); + ENG_SYSCALL(QMM_ENG_MSG(QMM_G_FS_FCLOSE_FILE), fpk3); goto fail; } filemem.resize((size_t)filelen); - ENG_SYSCALL(QMM_ENG_MSG[QMM_G_FS_READ], filemem.data(), filelen, fpk3); - ENG_SYSCALL(QMM_ENG_MSG[QMM_G_FS_FCLOSE_FILE], fpk3); + ENG_SYSCALL(QMM_ENG_MSG(QMM_G_FS_READ), filemem.data(), filelen, fpk3); + ENG_SYSCALL(QMM_ENG_MSG(QMM_G_FS_FCLOSE_FILE), fpk3); // get data verification setting from config verify_data = cfg_get_bool(g_cfg, "qvmverifydata", true); @@ -179,7 +169,7 @@ static bool s_mod_load_qvm(qmm_mod& mod) { mod.vmbase = (intptr_t)mod.vm.datasegment; // pass the qvm vmMain function pointer to the game-specific mod load handler - if (!g_gameinfo.game->funcs->pfnModLoad((void*)s_mod_qvm_vmmain, QMM_API_QVM)) + if (!g_gameinfo.game->ModLoad((void*)s_mod_qvm_vmmain, QMM_API_QVM)) { LOG(QMM_LOG_ERROR, "QMM") << fmt::format("mod_load(\"{}\"): Mod load failed?\n", mod.path); goto fail; @@ -208,7 +198,7 @@ static bool s_mod_load_dll(qmm_mod& mod, APIType api) { return false; // if mod load handler says good to go, we do too - if (g_gameinfo.game->funcs->pfnModLoad((void*)pfnGGA, api)) { + if (g_gameinfo.game->ModLoad((void*)pfnGGA, api)) { mod.api = api; return true; } @@ -222,7 +212,7 @@ static bool s_mod_load_dll(qmm_mod& mod, APIType api) { return false; // pass vmMain to game-specific mod load handler - if (g_gameinfo.game->funcs->pfnModLoad((void*)pfnvmMain, api)) { + if (g_gameinfo.game->ModLoad((void*)pfnvmMain, api)) { // pass qmm_syscall to mod's dllEntry function pfndllEntry(qmm_syscall); mod.api = api; diff --git a/src/plugin.cpp b/src/plugin.cpp index ca08733..8b4eeef 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -111,6 +111,20 @@ const char* plugin_result_to_str(plugin_res res) { } +// wrapper free function for GameSupport::syscall to pass to plugins +static intptr_t s_plugin_game_syscall(intptr_t cmd, ...) { + QMM_GET_SYSCALL_ARGS(); + return g_gameinfo.game->syscall(cmd, QMM_PUT_SYSCALL_ARGS()); +} + + +// wrapper free function for GameSupport::vmMain to pass to plugins +static intptr_t s_plugin_game_vmMain(intptr_t cmd, ...) { + QMM_GET_VMMAIN_ARGS(); + return g_gameinfo.game->vmMain(cmd, QMM_PUT_VMMAIN_ARGS()); +} + + // returns: -1 if failed to load and don't continue, 0 if failed to load and continue, 1 if loaded int plugin_load(qmm_plugin& p, std::string file) { int ret = 0; @@ -211,7 +225,7 @@ int plugin_load(qmm_plugin& p, std::string file) { // call QMM_Attach. if it fails (returns 0), call QMM_Detach and unload DLL // QMM_Attach(engine syscall, mod vmmain, pointer to plugin result int, table of plugin helper functions, table of plugin variables) - if (!(p.QMM_Attach(g_gameinfo.pfnsyscall, g_gameinfo.pfnvmMain, &g_plugin_globals.plugin_result, &s_pluginfuncs, &s_pluginvars))) { + if (!(p.QMM_Attach(s_plugin_game_syscall, s_plugin_game_vmMain, &g_plugin_globals.plugin_result, &s_pluginfuncs, &s_pluginvars))) { LOG(QMM_LOG_ERROR, "QMM") << fmt::format("plugin_load(\"{}\"): QMM_Attach() returned 0\n", file); // treat this failure specially. this is a valid plugin, but it decided on its own that it shouldn't be loaded ret = -1; @@ -283,7 +297,7 @@ static int s_plugin_helper_IsQVM(plugin_id plid [[maybe_unused]]) { static const char* s_plugin_helper_EngMsgName(plugin_id plid [[maybe_unused]], intptr_t msg) { - const char* ret = g_gameinfo.game->funcs->pfnEngMsgNames(msg); + const char* ret = g_gameinfo.game->EngMsgName(msg); #ifdef _DEBUG LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Plugin pfnEngMsgName(\"{}\", {}) = \"{}\"\n", ((plugin_info*)plid)->name, msg, ret); @@ -294,7 +308,7 @@ static const char* s_plugin_helper_EngMsgName(plugin_id plid [[maybe_unused]], i static const char* s_plugin_helper_ModMsgName(plugin_id plid [[maybe_unused]], intptr_t msg) { - const char* ret = g_gameinfo.game->funcs->pfnModMsgNames(msg); + const char* ret = g_gameinfo.game->ModMsgName(msg); #ifdef _DEBUG LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Plugin pfnModMsgName(\"{}\", {}) = \"{}\"\n", ((plugin_info*)plid)->name, msg, ret); @@ -307,7 +321,7 @@ static const char* s_plugin_helper_ModMsgName(plugin_id plid [[maybe_unused]], i static intptr_t s_plugin_helper_GetIntCvar(plugin_id plid [[maybe_unused]], const char* cvar) { intptr_t ret = 0; if (cvar && *cvar) - ret = ENG_SYSCALL(QMM_ENG_MSG[QMM_G_CVAR_VARIABLE_INTEGER_VALUE], cvar); + ret = ENG_SYSCALL(QMM_ENG_MSG(QMM_G_CVAR_VARIABLE_INTEGER_VALUE), cvar); #ifdef _DEBUG LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Plugin pfnGetIntCvar(\"{}\", \"{}\") = \"{}\"\n", ((plugin_info*)plid)->name, cvar, ret); @@ -327,7 +341,7 @@ static const char* s_plugin_helper_GetStrCvar(plugin_id plid [[maybe_unused]], c if (cvar && *cvar) { // cycle rotating buffer and store string index = (index + 1) & NUM_PLUGIN_STR_BUFFER_MASK; - ENG_SYSCALL(QMM_ENG_MSG[QMM_G_CVAR_VARIABLE_STRING_BUFFER], cvar, str[index], (intptr_t)sizeof(str[index])); + ENG_SYSCALL(QMM_ENG_MSG(QMM_G_CVAR_VARIABLE_STRING_BUFFER), cvar, str[index], (intptr_t)sizeof(str[index])); ret = str[index]; } @@ -340,7 +354,7 @@ static const char* s_plugin_helper_GetStrCvar(plugin_id plid [[maybe_unused]], c static const char* s_plugin_helper_GetGameEngine(plugin_id plid [[maybe_unused]]) { - const char* ret = g_gameinfo.game->gamename_short; + const char* ret = g_gameinfo.game->GameCode(); #ifdef _DEBUG LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Plugin pfnGetGameEngine(\"{}\") = \"{}\"\n", ((plugin_info*)plid)->name, ret); @@ -491,7 +505,7 @@ static void s_plugin_helper_GetConfigString(plugin_id plid [[maybe_unused]], int // (or true?), we need to get the configstring from the return value // instead if (buf && buflen) { - intptr_t ret = ENG_SYSCALL(QMM_ENG_MSG[QMM_G_GET_CONFIGSTRING], index, buf, buflen); + intptr_t ret = ENG_SYSCALL(QMM_ENG_MSG(QMM_G_GET_CONFIGSTRING), index, buf, buflen); if (ret > 1) strncpyz(buf, (const char*)ret, (size_t)buflen); } @@ -621,7 +635,7 @@ static const char* s_plugin_helper_GetConfigString2(plugin_id plid [[maybe_unuse // some games don't return pointers because of QVM interaction, so if this returns anything but null // (or true?), we probably are in an api game, and need to get the configstring from the return value // instead - intptr_t ret = ENG_SYSCALL(QMM_ENG_MSG[QMM_G_GET_CONFIGSTRING], configindex, str[index], sizeof(str[index])); + intptr_t ret = ENG_SYSCALL(QMM_ENG_MSG(QMM_G_GET_CONFIGSTRING), configindex, str[index], sizeof(str[index])); if (ret > 1) strncpyz(str[index], (const char*)ret, sizeof(str[index])); From be5c4caa52f241ed64ec9cf1a24909f5b2376418 Mon Sep 17 00:00:00 2001 From: Kevin Masterson Date: Wed, 18 Mar 2026 13:55:00 +0200 Subject: [PATCH 2/7] finished cod11mp, jamp, jk2mp, rtcwsp, rtcwmp, stvoyhm, sof2mp, and wet, and adjusted a few workings in q3a and game_api.h --- include/game_api.h | 109 +++--- src/game_api.cpp | 49 ++- src/game_cod11mp.cpp | 67 ++-- src/game_jamp.cpp | 816 ++++++++++++++++++++++--------------------- src/game_jk2mp.cpp | 78 +++-- src/game_q3a.cpp | 26 +- src/game_rtcwmp.cpp | 77 ++-- src/game_rtcwsp.cpp | 92 ++--- src/game_sof2mp.cpp | 77 ++-- src/game_stvoyhm.cpp | 77 ++-- src/game_wet.cpp | 68 ++-- 11 files changed, 814 insertions(+), 722 deletions(-) diff --git a/include/game_api.h b/include/game_api.h index de3f75c..2356b1b 100644 --- a/include/game_api.h +++ b/include/game_api.h @@ -77,27 +77,28 @@ enum APIType { const char* APIType_Name(APIType); // return the string form of the enum const char* APIType_Function(APIType); // return the entry function name -struct api_supportedgame; - -// pointers for all the messages/handlers associated with a single game's game support. -// instances of this struct are defined by GEN_GAME_FUNCS_QVM(GAME) or GEN_GAME_FUNCS(GAME) -struct api_supportedgame_funcs { - int* qmm_eng_msgs; // array of engine messages used by QMM - int* qmm_mod_msgs; // array of mod messages used by QMM - const char* (*pfnEngMsgName)(intptr_t); // pointer to a function that returns a string for a given engine message - const char* (*pfnModMsgName)(intptr_t); // pointer to a function that returns a string for a given mod message - - bool(*pfnAutoDetect)(api_supportedgame*, APIType); // pointer to a function that handles auto-detection logic for a game. return true to say "that's me!" - void*(*pfnEntry)(void*, void*, APIType); // pointer to a function that handles entry point logic for a game. return value is passed back to engine - bool(*pfnModLoad)(void*, APIType); // pointer to a function that handles mod loading logic after a DLL is loaded with dll_open. return true to accept DLL and continue. - void(*pfnModUnload)(); // pointer to a function that handles mod unloading logic before a DLL is unloaded - - qvm_syscall pfnQVMSyscall; // pointer to a function that handles mod->engine calls from a QVM (NULL = not supported) +// a list of all the engine messages/constants used by QMM. if you change this, update the GEN_GAME_QMM_*_MSGS macros +enum { + // general purpose + QMM_G_PRINT, QMM_G_ERROR, QMM_G_ARGV, QMM_G_ARGC, QMM_G_SEND_CONSOLE_COMMAND, QMM_G_GET_CONFIGSTRING, + // cvars + QMM_G_CVAR_REGISTER, QMM_G_CVAR_VARIABLE_STRING_BUFFER, QMM_G_CVAR_VARIABLE_INTEGER_VALUE, QMM_CVAR_SERVERINFO, QMM_CVAR_ROM, + // files + QMM_G_FS_FOPEN_FILE, QMM_G_FS_READ, QMM_G_FS_WRITE, QMM_G_FS_FCLOSE_FILE, QMM_EXEC_APPEND, QMM_FS_READ, + + QMM_ENGINE_MSG_COUNT, +}; + +// a list of all the mod messages used by QMM. if you change this, update the GEN_GAME_QMM_MSGS macro +enum { + QMM_GAME_INIT, QMM_GAME_SHUTDOWN, QMM_GAME_CONSOLE_COMMAND, + + QMM_MOD_MSG_COUNT, }; struct GameSupport { - virtual int QMMEngMsg(int) = 0; - virtual int QMMModMsg(int) = 0; + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual const char* EngMsgName(intptr_t) = 0; virtual const char* ModMsgName(intptr_t) = 0; virtual bool AutoDetect(APIType) = 0; @@ -115,6 +116,9 @@ struct GameSupport { virtual const char* GameCode() = 0; virtual int QVMSyscall(uint8_t*, int, int*) { return 0; } +private: + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = {}; + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = {}; }; extern std::vector api_supportedgames; @@ -124,39 +128,24 @@ extern std::vector api_supportedgames; // generate extern for each game's support object (used at the top of game_api.cpp) #define GEN_GAME_EXTS(game) extern GameSupport* game##_gamesupport +// generate game support object (used at the top of game_XYZ.cpp) #define GEN_GAME_OBJ(game) static game##_GameSupport gamesupport; GameSupport* game##_gamesupport = &gamesupport +// reference game support object in api_supportedgames list +#define GET_GAME_OBJ(game) game##_gamesupport + // generate a case/string line for use in the *MsgNames functions #define GEN_CASE(x) case x: return #x -// a list of all the engine messages/constants used by QMM. if you change this, update the GEN_GAME_QMM_*_MSGS macros -enum { - // general purpose - QMM_G_PRINT, QMM_G_ERROR, QMM_G_ARGV, QMM_G_ARGC, QMM_G_SEND_CONSOLE_COMMAND, QMM_G_GET_CONFIGSTRING, - // cvars - QMM_G_CVAR_REGISTER, QMM_G_CVAR_VARIABLE_STRING_BUFFER, QMM_G_CVAR_VARIABLE_INTEGER_VALUE, QMM_CVAR_SERVERINFO, QMM_CVAR_ROM, - // files - QMM_G_FS_FOPEN_FILE, QMM_G_FS_READ, QMM_G_FS_WRITE, QMM_G_FS_FCLOSE_FILE, QMM_EXEC_APPEND, QMM_FS_READ, - - QMM_ENGINE_MSG_COUNT, -}; - -// a list of all the mod messages used by QMM. if you change this, update the GEN_GAME_QMM_MSGS macro -enum { - QMM_GAME_INIT, QMM_GAME_SHUTDOWN, QMM_GAME_CONSOLE_COMMAND, - - QMM_MOD_MSG_COUNT, -}; - -// macros to easily output game-specific message values to match the enums above +// macros to output game-specific message values to match the QMM_ enums above #define GEN_GAME_QMM_ENG_MSGS() \ - const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = { \ + { \ G_PRINT, G_ERROR, G_ARGV, G_ARGC, G_SEND_CONSOLE_COMMAND, G_GET_CONFIGSTRING, \ G_CVAR_REGISTER, G_CVAR_VARIABLE_STRING_BUFFER, G_CVAR_VARIABLE_INTEGER_VALUE, CVAR_SERVERINFO, CVAR_ROM, \ G_FS_FOPEN_FILE, G_FS_READ, G_FS_WRITE, G_FS_FCLOSE_FILE, EXEC_APPEND, FS_READ, \ } #define GEN_GAME_QMM_MOD_MSGS() \ - const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = { \ + { \ GAME_INIT, GAME_SHUTDOWN, GAME_CONSOLE_COMMAND, \ } @@ -201,31 +190,31 @@ constexpr int QMM_MAX_SYSCALL_ARGS = 17; #define ROUTE_EXPORT_VAR(field, cmd) case cmd: ret = (intptr_t)&(orig_export-> field); break // handle calls from engine or mod into QMM -#define GEN_IMPORT(field, cmd) (decltype(qmm_import. field)) +[](intptr_t arg0, intptr_t arg1, intptr_t arg2, intptr_t arg3, intptr_t arg4, intptr_t arg5, intptr_t arg6, intptr_t arg7, intptr_t arg8, intptr_t arg9, intptr_t arg10, intptr_t arg11, intptr_t arg12, intptr_t arg13, intptr_t arg14, intptr_t arg15, intptr_t arg16) { return qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16); } -#define GEN_EXPORT(field, cmd) (decltype(qmm_export. field)) +[](intptr_t arg0, intptr_t arg1, intptr_t arg2, intptr_t arg3, intptr_t arg4, intptr_t arg5, intptr_t arg6, intptr_t arg7, intptr_t arg8) { cgame.is_from_QMM = true; return vmMain(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); } +#define GEN_IMPORT(field, cmd) (decltype(qmm_import. field)) +[](intptr_t arg0, intptr_t arg1, intptr_t arg2, intptr_t arg3, intptr_t arg4, intptr_t arg5, intptr_t arg6, intptr_t arg7, intptr_t arg8, intptr_t arg9, intptr_t arg10, intptr_t arg11, intptr_t arg12, intptr_t arg13, intptr_t arg14, intptr_t arg15, intptr_t arg16) { return ::qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16); } +#define GEN_EXPORT(field, cmd) (decltype(qmm_export. field)) +[](intptr_t arg0, intptr_t arg1, intptr_t arg2, intptr_t arg3, intptr_t arg4, intptr_t arg5, intptr_t arg6, intptr_t arg7, intptr_t arg8) { cgame.is_from_QMM = true; return ::vmMain(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); } // if the syscall lambda types matter (float args in 64-bit games like Q2R), use these // macros to easily generate a lambda with full return and argument type information: // e.g. GEN_IMPORT_2(G_DEBUGGRAPH, void, float, int) // e.g. GEN_IMPORT_2(G_ANIM_TIME, float, dtiki_t*, int) #if defined(QMM_ARCH_64) -#define GEN_IMPORT_0(field, cmd, typeret) +[]() -> typeret { return (typeret)qmm_syscall(cmd); } -#define GEN_IMPORT_1(field, cmd, typeret, type0) +[](type0 arg0) -> typeret { return (typeret)qmm_syscall(cmd, arg0); } -#define GEN_IMPORT_2(field, cmd, typeret, type0, type1) +[](type0 arg0, type1 arg1) -> typeret { return (typeret)qmm_syscall(cmd, arg0, arg1); } -#define GEN_IMPORT_3(field, cmd, typeret, type0, type1, type2) +[](type0 arg0, type1 arg1, type2 arg2) -> typeret { return (typeret)qmm_syscall(cmd, arg0, arg1, arg2); } -#define GEN_IMPORT_4(field, cmd, typeret, type0, type1, type2, type3) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3) -> typeret { return (typeret)qmm_syscall(cmd, arg0, arg1, arg2, arg3); } -#define GEN_IMPORT_5(field, cmd, typeret, type0, type1, type2, type3, type4) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4) -> typeret { return (typeret)qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4); } -#define GEN_IMPORT_6(field, cmd, typeret, type0, type1, type2, type3, type4, type5) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) -> typeret { return (typeret)qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5); } -#define GEN_IMPORT_7(field, cmd, typeret, type0, type1, type2, type3, type4, type5, type6) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6) -> typeret { return (typeret)qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6); } -#define GEN_IMPORT_8(field, cmd, typeret, type0, type1, type2, type3, type4, type5, type6, type7) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7) -> typeret { return (typeret)qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); } -#define GEN_IMPORT_9(field, cmd, typeret, type0, type1, type2, type3, type4, type5, type6, type7, type8) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7, type8 arg8) -> typeret { return (typeret)qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); } -#define GEN_IMPORT_10(field, cmd, typeret, type0, type1, type2, type3, type4, type5, type6, type7, type8, type9) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7, type8 arg8, type9 arg9) -> typeret { return (typeret)qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); } -#define GEN_IMPORT_11(field, cmd, typeret, type0, type1, type2, type3, type4, type5, type6, type7, type8, type9, type10) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7, type8 arg8, type9 arg9, type10 arg10) -> typeret { return (typeret)qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); } -#define GEN_IMPORT_12(field, cmd, typeret, type0, type1, type2, type3, type4, type5, type6, type7, type8, type9, type10, type11) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7, type8 arg8, type9 arg9, type10 arg10, type11 arg11) -> typeret { return (typeret)qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11); } -#define GEN_IMPORT_13(field, cmd, typeret, type0, type1, type2, type3, type4, type5, type6, type7, type8, type9, type10, type11, type12) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7, type8 arg8, type9 arg9, type10 arg10, type11 arg11, type12 arg12) -> typeret { return (typeret)qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12); } -#define GEN_IMPORT_14(field, cmd, typeret, type0, type1, type2, type3, type4, type5, type6, type7, type8, type9, type10, type11, type12, type13) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7, type8 arg8, type9 arg9, type10 arg10, type11 arg11, type12 arg12, type13 arg13) -> typeret { return (typeret)qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13); } -#define GEN_IMPORT_15(field, cmd, typeret, type0, type1, type2, type3, type4, type5, type6, type7, type8, type9, type10, type11, type12, type13, type14) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7, type8 arg8, type9 arg9, type10 arg10, type11 arg11, type12 arg12, type13 arg13, type14 arg14) -> typeret { return (typeret)qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14); } -#define GEN_IMPORT_16(field, cmd, typeret, type0, type1, type2, type3, type4, type5, type6, type7, type8, type9, type10, type11, type12, type13, type14, type15) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7, type8 arg8, type9 arg9, type10 arg10, type11 arg11, type12 arg12, type13 arg13, type14 arg14, type15 arg15) -> typeret { return (typeret)qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15); } -#define GEN_IMPORT_17(field, cmd, typeret, type0, type1, type2, type3, type4, type5, type6, type7, type8, type9, type10, type11, type12, type13, type14, type15, type16) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7, type8 arg8, type9 arg9, type10 arg10, type11 arg11, type12 arg12, type13 arg13, type14 arg14, type15 arg15, type16 arg16) -> typeret { return (typeret)qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16); } +#define GEN_IMPORT_0(field, cmd, typeret) +[]() -> typeret { return (typeret)::qmm_syscall(cmd); } +#define GEN_IMPORT_1(field, cmd, typeret, type0) +[](type0 arg0) -> typeret { return (typeret)::qmm_syscall(cmd, arg0); } +#define GEN_IMPORT_2(field, cmd, typeret, type0, type1) +[](type0 arg0, type1 arg1) -> typeret { return (typeret)::qmm_syscall(cmd, arg0, arg1); } +#define GEN_IMPORT_3(field, cmd, typeret, type0, type1, type2) +[](type0 arg0, type1 arg1, type2 arg2) -> typeret { return (typeret)::qmm_syscall(cmd, arg0, arg1, arg2); } +#define GEN_IMPORT_4(field, cmd, typeret, type0, type1, type2, type3) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3) -> typeret { return (typeret)::qmm_syscall(cmd, arg0, arg1, arg2, arg3); } +#define GEN_IMPORT_5(field, cmd, typeret, type0, type1, type2, type3, type4) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4) -> typeret { return (typeret)::qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4); } +#define GEN_IMPORT_6(field, cmd, typeret, type0, type1, type2, type3, type4, type5) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) -> typeret { return (typeret)::qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5); } +#define GEN_IMPORT_7(field, cmd, typeret, type0, type1, type2, type3, type4, type5, type6) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6) -> typeret { return (typeret)::qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6); } +#define GEN_IMPORT_8(field, cmd, typeret, type0, type1, type2, type3, type4, type5, type6, type7) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7) -> typeret { return (typeret)::qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); } +#define GEN_IMPORT_9(field, cmd, typeret, type0, type1, type2, type3, type4, type5, type6, type7, type8) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7, type8 arg8) -> typeret { return (typeret)::qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); } +#define GEN_IMPORT_10(field, cmd, typeret, type0, type1, type2, type3, type4, type5, type6, type7, type8, type9) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7, type8 arg8, type9 arg9) -> typeret { return (typeret)::qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); } +#define GEN_IMPORT_11(field, cmd, typeret, type0, type1, type2, type3, type4, type5, type6, type7, type8, type9, type10) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7, type8 arg8, type9 arg9, type10 arg10) -> typeret { return (typeret)::qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); } +#define GEN_IMPORT_12(field, cmd, typeret, type0, type1, type2, type3, type4, type5, type6, type7, type8, type9, type10, type11) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7, type8 arg8, type9 arg9, type10 arg10, type11 arg11) -> typeret { return (typeret)::qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11); } +#define GEN_IMPORT_13(field, cmd, typeret, type0, type1, type2, type3, type4, type5, type6, type7, type8, type9, type10, type11, type12) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7, type8 arg8, type9 arg9, type10 arg10, type11 arg11, type12 arg12) -> typeret { return (typeret)::qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12); } +#define GEN_IMPORT_14(field, cmd, typeret, type0, type1, type2, type3, type4, type5, type6, type7, type8, type9, type10, type11, type12, type13) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7, type8 arg8, type9 arg9, type10 arg10, type11 arg11, type12 arg12, type13 arg13) -> typeret { return (typeret)::qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13); } +#define GEN_IMPORT_15(field, cmd, typeret, type0, type1, type2, type3, type4, type5, type6, type7, type8, type9, type10, type11, type12, type13, type14) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7, type8 arg8, type9 arg9, type10 arg10, type11 arg11, type12 arg12, type13 arg13, type14 arg14) -> typeret { return (typeret)::qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14); } +#define GEN_IMPORT_16(field, cmd, typeret, type0, type1, type2, type3, type4, type5, type6, type7, type8, type9, type10, type11, type12, type13, type14, type15) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7, type8 arg8, type9 arg9, type10 arg10, type11 arg11, type12 arg12, type13 arg13, type14 arg14, type15 arg15) -> typeret { return (typeret)::qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15); } +#define GEN_IMPORT_17(field, cmd, typeret, type0, type1, type2, type3, type4, type5, type6, type7, type8, type9, type10, type11, type12, type13, type14, type15, type16) +[](type0 arg0, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7, type8 arg8, type9 arg9, type10 arg10, type11 arg11, type12 arg12, type13 arg13, type14 arg14, type15 arg15, type16 arg16) -> typeret { return (typeret)::qmm_syscall(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16); } #elif defined(QMM_ARCH_32) #define GEN_IMPORT_0(field, cmd, ...) GEN_IMPORT(field, cmd) #define GEN_IMPORT_1(field, cmd, ...) GEN_IMPORT(field, cmd) diff --git a/src/game_api.cpp b/src/game_api.cpp index dfe79fc..b298206 100644 --- a/src/game_api.cpp +++ b/src/game_api.cpp @@ -42,23 +42,52 @@ GEN_GAME_EXTS(STEF2); GEN_GAME_EXTS(STVOYSP); std::vector api_supportedgames = { - Q3A_gamesupport, + // vmMain games + GET_GAME_OBJ(Q3A), + GET_GAME_OBJ(RTCWSP), + GET_GAME_OBJ(JK2MP), + GET_GAME_OBJ(JAMP), + GET_GAME_OBJ(WET), + GET_GAME_OBJ(RTCWMP), + +// these games don't appear to have an official 64-bit version or source port +#if defined(QMM_ARCH_32) + GET_GAME_OBJ(STVOYHM), + GET_GAME_OBJ(SOF2MP), + GET_GAME_OBJ(CODMP), + GET_GAME_OBJ(CODUOMP), + // allow a user to choose "COD11MP" manually if they are playing an old version of CoD (no auto-detection) + GET_GAME_OBJ(COD11MP), +#endif + + // GetGameAPI games + GET_GAME_OBJ(JK2SP), + GET_GAME_OBJ(JASP), + GET_GAME_OBJ(QUAKE2), + GET_GAME_OBJ(SIN), + +// OpenMOHAA adds 64-bit MoH support but the API is very different, so disable it for now +// the rest of the games don't appear to have an official 64-bit version or source port +#if defined(QMM_ARCH_32) + GET_GAME_OBJ(MOHAA), + GET_GAME_OBJ(MOHSH), + GET_GAME_OBJ(MOHBT), + GET_GAME_OBJ(STEF2), + GET_GAME_OBJ(SOF2SP), + GET_GAME_OBJ(STVOYSP), +#endif + +// Q2R only exists for 64-bit Windows +#if defined(QMM_OS_WINDOWS) && defined(QMM_ARCH_64) + GET_GAME_OBJ(Q2R), +#endif }; #if 0 - { "qagame" SP_DLL X64_DLL, nullptr, ".", "Return to Castle Wolfenstein (SP)", GEN_GAME_INFO(RTCWSP), 13, 5 }, - { "jk2mpgame" MOD_DLL, "vm/jk2mpgame.qvm", "base", "Jedi Knight 2: Jedi Outcast (MP)", GEN_GAME_INFO(JK2MP), 13, 3 }, - { "jampgame" MOD_DLL, nullptr, "base", "Jedi Knight: Jedi Academy (MP)", GEN_GAME_INFO(JAMP), 13, 6 }, - { "qagame" MP_DLL X64_DLL, nullptr, "etmain", "Wolfenstein: Enemy Territory", GEN_GAME_INFO(WET), 13, 5 }, - { "qagame" MP_DLL X64_DLL, "vm/qagame.mp.qvm", "main", "Return to Castle Wolfenstein (MP)", GEN_GAME_INFO(RTCWMP), 13, 5 }, // these games don't appear to have an official 64-bit version or source port #if defined(QMM_ARCH_32) - { "qagame" MOD_DLL, "vm/qagame.qvm", "baseef", "Star Trek Voyager: Elite Force (Holomatch)", GEN_GAME_INFO(STVOYHM), 13, 3 }, - { "sof2mp_game" MOD_DLL, "vm/sof2mp_game.qvm","base/mp", "Soldier of Fortune 2: Double Helix (MP)", GEN_GAME_INFO(SOF2MP), 13, 6 }, { "game" MP_DLL MOD_DLL, nullptr, "Main", "Call of Duty (MP)", GEN_GAME_INFO(CODMP), 8, 4 }, { "" UO_DLL MOD_DLL, nullptr, "uo", "Call of Duty: United Offensive (MP)", GEN_GAME_INFO(CODUOMP), 8, 4 }, - // allow a user to choose "COD11MP" manually if they are playing an old version of CoD (no auto-detection) - { "game" MP_DLL MOD_DLL, nullptr, "Main", "Call of Duty v1.1 (MP)", GEN_GAME_INFO(COD11MP), 8, 4 }, #endif // GetGameAPI games diff --git a/src/game_cod11mp.cpp b/src/game_cod11mp.cpp index 22a7319..38d4d1a 100644 --- a/src/game_cod11mp.cpp +++ b/src/game_cod11mp.cpp @@ -19,31 +19,48 @@ Created By: #include "game_cod11mp.h" #include "main.h" -GEN_GAME_QMM_MSGS(COD11MP); -GEN_GAME_FUNCS(COD11MP); +struct COD11MP_GameSupport : public GameSupport { + virtual const char* EngMsgName(intptr_t msg); + virtual const char* ModMsgName(intptr_t msg); + virtual bool AutoDetect(APIType engine_api); + virtual void* Entry(void* syscall, void*, APIType engine_api); + virtual bool ModLoad(void* entry, APIType mod_api); + virtual void ModUnload(); + + virtual intptr_t syscall(intptr_t, ...); + virtual intptr_t vmMain(intptr_t, ...); + + virtual const char* DefaultDLLName() { return "game" MP_DLL MOD_DLL; } + virtual const char* DefaultModDir() { return "Main"; } + virtual const char* GameName() { return "Call of Duty v1.1 (MP)"; } + virtual const char* GameCode() { return "COD11MP"; } + +private: + eng_syscall orig_syscall = nullptr; + mod_vmMain orig_vmMain = nullptr; + + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = GEN_GAME_QMM_MOD_MSGS(); +}; + +GEN_GAME_OBJ(COD11MP); // auto-detection logic for COD11MP (never auto-detect) -static bool COD11MP_AutoDetect(api_supportedgame*, APIType) { +bool COD11MP_GameSupport::AutoDetect(APIType) { return false; } -// original syscall pointer that comes from the game engine -static eng_syscall orig_syscall = nullptr; - -// pointer to vmMain that comes from the mod -static mod_vmMain orig_vmMain = nullptr; - // wrapper syscall function that calls actual engine func in orig_syscall // this is how QMM and plugins will call into the engine -static intptr_t COD11MP_syscall(intptr_t cmd, ...) { +intptr_t COD11MP_GameSupport::syscall(intptr_t cmd, ...) { QMM_GET_SYSCALL_ARGS(); #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("COD11MP_syscall({} {}) called\n", COD11MP_EngMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("COD11MP_GameSupport::syscall({} {}) called\n", EngMsgName(cmd), cmd); #endif intptr_t ret = 0; @@ -76,7 +93,7 @@ static intptr_t COD11MP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("COD11MP_syscall({} {}) returning {}\n", COD11MP_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("COD11MP_GameSupport::syscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; @@ -85,11 +102,11 @@ static intptr_t COD11MP_syscall(intptr_t cmd, ...) { // wrapper vmMain function that calls actual mod func in orig_vmMain // this is how QMM and plugins will call into the mod -static intptr_t COD11MP_vmMain(intptr_t cmd, ...) { +intptr_t COD11MP_GameSupport::vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("COD11MP_vmMain({} {}) called\n", COD11MP_ModMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("COD11MP_GameSupport::vmMain({} {}) called\n", ModMsgName(cmd), cmd); #endif if (!orig_vmMain) @@ -102,32 +119,26 @@ static intptr_t COD11MP_vmMain(intptr_t cmd, ...) { ret = orig_vmMain(cmd, QMM_PUT_VMMAIN_ARGS()); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("COD11MP_vmMain({} {}) returning {}\n", COD11MP_ModMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("COD11MP_GameSupport::vmMain({} {}) returning {}\n", ModMsgName(cmd), cmd, ret); #endif return ret; } -static void* COD11MP_Entry(void* syscall, void*, APIType) { - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("COD11MP_Entry({}) called\n", syscall); +void* COD11MP_GameSupport::Entry(void* syscall, void*, APIType) { + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("COD11MP_GameSupport::Entry({}) called\n", syscall); // store original syscall from engine orig_syscall = (eng_syscall)syscall; - // pointer to wrapper vmMain function that calls actual mod vmMain func orig_vmMain - g_gameinfo.pfnvmMain = COD11MP_vmMain; - - // pointer to wrapper syscall function that calls actual engine syscall func - g_gameinfo.pfnsyscall = COD11MP_syscall; - - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("COD11MP_Entry({}) returning\n", syscall); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("COD11MP_GameSupport::Entry({}) returning\n", syscall); return nullptr; } -static bool COD11MP_ModLoad(void* entry, APIType modapi) { +bool COD11MP_GameSupport::ModLoad(void* entry, APIType modapi) { if (modapi != QMM_API_DLLENTRY) return false; @@ -137,12 +148,12 @@ static bool COD11MP_ModLoad(void* entry, APIType modapi) { } -static void COD11MP_ModUnload() { +void COD11MP_GameSupport::ModUnload() { orig_vmMain = nullptr; } -static const char* COD11MP_EngMsgName(intptr_t cmd) { +const char* COD11MP_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINTF); GEN_CASE(G_ERROR); @@ -289,7 +300,7 @@ static const char* COD11MP_EngMsgName(intptr_t cmd) { } -static const char* COD11MP_ModMsgName(intptr_t cmd) { +const char* COD11MP_GameSupport::ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_INIT); GEN_CASE(GAME_SHUTDOWN); diff --git a/src/game_jamp.cpp b/src/game_jamp.cpp index 4ba5b80..61a940d 100644 --- a/src/game_jamp.cpp +++ b/src/game_jamp.cpp @@ -21,20 +21,410 @@ Created By: #include "main.h" #include "util.h" -GEN_GAME_QMM_MSGS(JAMP); +struct JAMP_GameSupport : public GameSupport { + virtual const char* EngMsgName(intptr_t msg); + virtual const char* ModMsgName(intptr_t msg); + virtual bool AutoDetect(APIType engine_api); + virtual void* Entry(void* syscall, void*, APIType engine_api); + virtual bool ModLoad(void* entry, APIType mod_api); + virtual void ModUnload(); + + virtual intptr_t syscall(intptr_t, ...); + virtual intptr_t vmMain(intptr_t, ...); + + virtual const char* DefaultDLLName() { return "jampgame" MOD_DLL; } + virtual const char* DefaultModDir() { return "base"; } + virtual const char* GameName() { return "Jedi Knight: Jedi Academy (MP)"; } + virtual const char* GameCode() { return "JAMP"; } + +private: + eng_syscall orig_syscall = nullptr; + mod_vmMain orig_vmMain = nullptr; + + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = GEN_GAME_QMM_MOD_MSGS(); + + // these variables are all for the GGA implementation for OpenJK + + // a copy of the apiversion int that comes from the game engine + intptr_t orig_apiversion = 0; + + // a copy of the original import struct that comes from the game engine + game_import_t orig_import = {}; + + // a copy of the original export struct pointer that comes from the mod + game_export_t* orig_export = nullptr; + + // struct with lambdas that call QMM's syscall function. this is given to the mod + game_import_t qmm_import = { + GEN_IMPORT(Print, G_PRINT), + GEN_IMPORT(Error, G_ERROR), + GEN_IMPORT(Milliseconds, G_MILLISECONDS), + GEN_IMPORT(PrecisionTimerStart, G_PRECISIONTIMER_START), + GEN_IMPORT(PrecisionTimerEnd, G_PRECISIONTIMER_END), + GEN_IMPORT(SV_RegisterSharedMemory, G_SET_SHARED_BUFFER), + GEN_IMPORT(RealTime, G_REAL_TIME), + GEN_IMPORT(TrueMalloc, G_TRUEMALLOC), + GEN_IMPORT(TrueFree, G_TRUEFREE), + GEN_IMPORT(SnapVector, G_SNAPVECTOR), + GEN_IMPORT(Cvar_Register, G_CVAR_REGISTER), + GEN_IMPORT(Cvar_Set, G_CVAR_SET), + GEN_IMPORT(Cvar_Update, G_CVAR_UPDATE), + GEN_IMPORT(Cvar_VariableIntegerValue, G_CVAR_VARIABLE_INTEGER_VALUE), + GEN_IMPORT(Cvar_VariableStringBuffer, G_CVAR_VARIABLE_STRING_BUFFER), + GEN_IMPORT(Argc, G_ARGC), + GEN_IMPORT(Argv, G_ARGV), + GEN_IMPORT(FS_Close, G_FS_FCLOSE_FILE), + GEN_IMPORT(FS_GetFileList, G_FS_GETFILELIST), + GEN_IMPORT(FS_Open, G_FS_FOPEN_FILE), + GEN_IMPORT(FS_Read, G_FS_READ), + GEN_IMPORT(FS_Write, G_FS_WRITE), + GEN_IMPORT(AdjustAreaPortalState, G_ADJUST_AREA_PORTAL_STATE), + GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), + GEN_IMPORT(DebugPolygonCreate, G_DEBUG_POLYGON_CREATE), + GEN_IMPORT(DebugPolygonDelete, G_DEBUG_POLYGON_DELETE), + GEN_IMPORT(DropClient, G_DROP_CLIENT), + GEN_IMPORT(EntitiesInBox, G_ENTITIES_IN_BOX), + GEN_IMPORT(EntityContact, G_ENTITY_CONTACT), + GEN_IMPORT(GetConfigstring, G_GET_CONFIGSTRING), + GEN_IMPORT(GetEntityToken, G_GET_ENTITY_TOKEN), + GEN_IMPORT(GetServerinfo, G_GET_SERVERINFO), + GEN_IMPORT(GetUsercmd, G_GET_USERCMD), + GEN_IMPORT(GetUserinfo, G_GET_USERINFO), + GEN_IMPORT(InPVS, G_IN_PVS), + GEN_IMPORT(InPVSIgnorePortals, G_IN_PVS_IGNORE_PORTALS), + GEN_IMPORT(LinkEntity, G_LINKENTITY), + GEN_IMPORT(LocateGameData, G_LOCATE_GAME_DATA), + GEN_IMPORT(PointContents, G_POINT_CONTENTS), + GEN_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND), + GEN_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND), + GEN_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL), + GEN_IMPORT(SetConfigstring, G_SET_CONFIGSTRING), + GEN_IMPORT(SetServerCull, G_SET_SERVER_CULL), + GEN_IMPORT(SetUserinfo, G_SET_USERINFO), + GEN_IMPORT(SiegePersSet, G_SIEGEPERSSET), + GEN_IMPORT(SiegePersGet, G_SIEGEPERSGET), + GEN_IMPORT(Trace, G_TRACE), + GEN_IMPORT(UnlinkEntity, G_UNLINKENTITY), + GEN_IMPORT(ROFF_Clean, G_ROFF_CLEAN), + GEN_IMPORT(ROFF_UpdateEntities, G_ROFF_UPDATE_ENTITIES), + GEN_IMPORT(ROFF_Cache, G_ROFF_CACHE), + GEN_IMPORT(ROFF_Play, G_ROFF_PLAY), + GEN_IMPORT(ROFF_Purge_Ent, G_ROFF_PURGE_ENT), + GEN_IMPORT(ICARUS_RunScript, G_ICARUS_RUNSCRIPT), + GEN_IMPORT(ICARUS_RegisterScript, G_ICARUS_REGISTERSCRIPT), + GEN_IMPORT(ICARUS_Init, G_ICARUS_INIT), + GEN_IMPORT(ICARUS_ValidEnt, G_ICARUS_VALIDENT), + GEN_IMPORT(ICARUS_IsInitialized, G_ICARUS_ISINITIALIZED), + GEN_IMPORT(ICARUS_MaintainTaskManager, G_ICARUS_MAINTAINTASKMANAGER), + GEN_IMPORT(ICARUS_IsRunning, G_ICARUS_ISRUNNING), + GEN_IMPORT(ICARUS_TaskIDPending, G_ICARUS_TASKIDPENDING), + GEN_IMPORT(ICARUS_InitEnt, G_ICARUS_INITENT), + GEN_IMPORT(ICARUS_FreeEnt, G_ICARUS_FREEENT), + GEN_IMPORT(ICARUS_AssociateEnt, G_ICARUS_ASSOCIATEENT), + GEN_IMPORT(ICARUS_Shutdown, G_ICARUS_SHUTDOWN), + GEN_IMPORT(ICARUS_TaskIDSet, G_ICARUS_TASKIDSET), + GEN_IMPORT(ICARUS_TaskIDComplete, G_ICARUS_TASKIDCOMPLETE), + GEN_IMPORT(ICARUS_SetVar, G_ICARUS_SETVAR), + GEN_IMPORT(ICARUS_VariableDeclared, G_ICARUS_VARIABLEDECLARED), + GEN_IMPORT(ICARUS_GetFloatVariable, G_ICARUS_GETFLOATVARIABLE), + GEN_IMPORT(ICARUS_GetStringVariable, G_ICARUS_GETSTRINGVARIABLE), + GEN_IMPORT(ICARUS_GetVectorVariable, G_ICARUS_GETVECTORVARIABLE), + GEN_IMPORT(Nav_Init, G_NAV_INIT), + GEN_IMPORT(Nav_Free, G_NAV_FREE), + GEN_IMPORT(Nav_Load, G_NAV_LOAD), + GEN_IMPORT(Nav_Save, G_NAV_SAVE), + GEN_IMPORT(Nav_AddRawPoint, G_NAV_ADDRAWPOINT), + GEN_IMPORT(Nav_CalculatePaths, G_NAV_CALCULATEPATHS), + GEN_IMPORT(Nav_HardConnect, G_NAV_HARDCONNECT), + GEN_IMPORT(Nav_ShowNodes, G_NAV_SHOWNODES), + GEN_IMPORT(Nav_ShowEdges, G_NAV_SHOWEDGES), + GEN_IMPORT(Nav_ShowPath, G_NAV_SHOWPATH), + GEN_IMPORT(Nav_GetNearestNode, G_NAV_GETNEARESTNODE), + GEN_IMPORT(Nav_GetBestNode, G_NAV_GETBESTNODE), + GEN_IMPORT(Nav_GetNodePosition, G_NAV_GETNODEPOSITION), + GEN_IMPORT(Nav_GetNodeNumEdges, G_NAV_GETNODENUMEDGES), + GEN_IMPORT(Nav_GetNodeEdge, G_NAV_GETNODEEDGE), + GEN_IMPORT(Nav_GetNumNodes, G_NAV_GETNUMNODES), + GEN_IMPORT(Nav_Connected, G_NAV_CONNECTED), + GEN_IMPORT(Nav_GetPathCost, G_NAV_GETPATHCOST), + GEN_IMPORT(Nav_GetEdgeCost, G_NAV_GETEDGECOST), + GEN_IMPORT(Nav_GetProjectedNode, G_NAV_GETPROJECTEDNODE), + GEN_IMPORT(Nav_CheckFailedNodes, G_NAV_CHECKFAILEDNODES), + GEN_IMPORT(Nav_AddFailedNode, G_NAV_ADDFAILEDNODE), + GEN_IMPORT(Nav_NodeFailed, G_NAV_NODEFAILED), + GEN_IMPORT(Nav_NodesAreNeighbors, G_NAV_NODESARENEIGHBORS), + GEN_IMPORT(Nav_ClearFailedEdge, G_NAV_CLEARFAILEDEDGE), + GEN_IMPORT(Nav_ClearAllFailedEdges, G_NAV_CLEARALLFAILEDEDGES), + GEN_IMPORT(Nav_EdgeFailed, G_NAV_EDGEFAILED), + GEN_IMPORT(Nav_AddFailedEdge, G_NAV_ADDFAILEDEDGE), + GEN_IMPORT(Nav_CheckFailedEdge, G_NAV_CHECKFAILEDEDGE), + GEN_IMPORT(Nav_CheckAllFailedEdges, G_NAV_CHECKALLFAILEDEDGES), + GEN_IMPORT(Nav_RouteBlocked, G_NAV_ROUTEBLOCKED), + GEN_IMPORT(Nav_GetBestNodeAltRoute, G_NAV_GETBESTNODEALTROUTE), + GEN_IMPORT(Nav_GetBestNodeAltRoute2, G_NAV_GETBESTNODEALT2), + GEN_IMPORT(Nav_GetBestPathBetweenEnts, G_NAV_GETBESTPATHBETWEENENTS), + GEN_IMPORT(Nav_GetNodeRadius, G_NAV_GETNODERADIUS), + GEN_IMPORT(Nav_CheckBlockedEdges, G_NAV_CHECKBLOCKEDEDGES), + GEN_IMPORT(Nav_ClearCheckedNodes, G_NAV_CLEARCHECKEDNODES), + GEN_IMPORT(Nav_CheckedNode, G_NAV_CHECKEDNODE), + GEN_IMPORT(Nav_SetCheckedNode, G_NAV_SETCHECKEDNODE), + GEN_IMPORT(Nav_FlagAllNodes, G_NAV_FLAGALLNODES), + GEN_IMPORT(Nav_GetPathsCalculated, G_NAV_GETPATHSCALCULATED), + GEN_IMPORT(Nav_SetPathsCalculated, G_NAV_SETPATHSCALCULATED), + GEN_IMPORT(BotAllocateClient, G_BOT_ALLOCATE_CLIENT), + GEN_IMPORT(BotFreeClient, G_BOT_FREE_CLIENT), + GEN_IMPORT(BotLoadCharacter, BOTLIB_AI_LOAD_CHARACTER), + GEN_IMPORT(BotFreeCharacter, BOTLIB_AI_FREE_CHARACTER), + GEN_IMPORT(Characteristic_Float, BOTLIB_AI_CHARACTERISTIC_FLOAT), + GEN_IMPORT(Characteristic_BFloat, BOTLIB_AI_CHARACTERISTIC_BFLOAT), + GEN_IMPORT(Characteristic_Integer, BOTLIB_AI_CHARACTERISTIC_INTEGER), + GEN_IMPORT(Characteristic_BInteger, BOTLIB_AI_CHARACTERISTIC_BINTEGER), + GEN_IMPORT(Characteristic_String, BOTLIB_AI_CHARACTERISTIC_STRING), + GEN_IMPORT(BotAllocChatState, BOTLIB_AI_ALLOC_CHAT_STATE), + GEN_IMPORT(BotFreeChatState, BOTLIB_AI_FREE_CHAT_STATE), + GEN_IMPORT(BotQueueConsoleMessage, BOTLIB_AI_QUEUE_CONSOLE_MESSAGE), + GEN_IMPORT(BotRemoveConsoleMessage, BOTLIB_AI_REMOVE_CONSOLE_MESSAGE), + GEN_IMPORT(BotNextConsoleMessage, BOTLIB_AI_NEXT_CONSOLE_MESSAGE), + GEN_IMPORT(BotNumConsoleMessages, BOTLIB_AI_NUM_CONSOLE_MESSAGE), + GEN_IMPORT(BotInitialChat, BOTLIB_AI_INITIAL_CHAT), + GEN_IMPORT(BotReplyChat, BOTLIB_AI_REPLY_CHAT), + GEN_IMPORT(BotChatLength, BOTLIB_AI_CHAT_LENGTH), + GEN_IMPORT(BotEnterChat, BOTLIB_AI_ENTER_CHAT), + GEN_IMPORT(StringContains, BOTLIB_AI_STRING_CONTAINS), + GEN_IMPORT(BotFindMatch, BOTLIB_AI_FIND_MATCH), + GEN_IMPORT(BotMatchVariable, BOTLIB_AI_MATCH_VARIABLE), + GEN_IMPORT(UnifyWhiteSpaces, BOTLIB_AI_UNIFY_WHITE_SPACES), + GEN_IMPORT(BotReplaceSynonyms, BOTLIB_AI_REPLACE_SYNONYMS), + GEN_IMPORT(BotLoadChatFile, BOTLIB_AI_LOAD_CHAT_FILE), + GEN_IMPORT(BotSetChatGender, BOTLIB_AI_SET_CHAT_GENDER), + GEN_IMPORT(BotSetChatName, BOTLIB_AI_SET_CHAT_NAME), + GEN_IMPORT(BotResetGoalState, BOTLIB_AI_RESET_GOAL_STATE), + GEN_IMPORT(BotResetAvoidGoals, BOTLIB_AI_RESET_AVOID_GOALS), + GEN_IMPORT(BotPushGoal, BOTLIB_AI_PUSH_GOAL), + GEN_IMPORT(BotPopGoal, BOTLIB_AI_POP_GOAL), + GEN_IMPORT(BotEmptyGoalStack, BOTLIB_AI_EMPTY_GOAL_STACK), + GEN_IMPORT(BotDumpAvoidGoals, BOTLIB_AI_DUMP_AVOID_GOALS), + GEN_IMPORT(BotDumpGoalStack, BOTLIB_AI_DUMP_GOAL_STACK), + GEN_IMPORT(BotGoalName, BOTLIB_AI_GOAL_NAME), + GEN_IMPORT(BotGetTopGoal, BOTLIB_AI_GET_TOP_GOAL), + GEN_IMPORT(BotGetSecondGoal, BOTLIB_AI_GET_SECOND_GOAL), + GEN_IMPORT(BotChooseLTGItem, BOTLIB_AI_CHOOSE_LTG_ITEM), + GEN_IMPORT(BotChooseNBGItem, BOTLIB_AI_CHOOSE_NBG_ITEM), + GEN_IMPORT(BotTouchingGoal, BOTLIB_AI_TOUCHING_GOAL), + GEN_IMPORT(BotItemGoalInVisButNotVisible, BOTLIB_AI_ITEM_GOAL_IN_VIS_BUT_NOT_VISIBLE), + GEN_IMPORT(BotGetLevelItemGoal, BOTLIB_AI_GET_LEVEL_ITEM_GOAL), + GEN_IMPORT(BotAvoidGoalTime, BOTLIB_AI_AVOID_GOAL_TIME), + GEN_IMPORT(BotInitLevelItems, BOTLIB_AI_INIT_LEVEL_ITEMS), + GEN_IMPORT(BotUpdateEntityItems, BOTLIB_AI_UPDATE_ENTITY_ITEMS), + GEN_IMPORT(BotLoadItemWeights, BOTLIB_AI_LOAD_ITEM_WEIGHTS), + GEN_IMPORT(BotFreeItemWeights, BOTLIB_AI_FREE_ITEM_WEIGHTS), + GEN_IMPORT(BotSaveGoalFuzzyLogic, BOTLIB_AI_SAVE_GOAL_FUZZY_LOGIC), + GEN_IMPORT(BotAllocGoalState, BOTLIB_AI_ALLOC_GOAL_STATE), + GEN_IMPORT(BotFreeGoalState, BOTLIB_AI_FREE_GOAL_STATE), + GEN_IMPORT(BotResetMoveState, BOTLIB_AI_RESET_MOVE_STATE), + GEN_IMPORT(BotMoveToGoal, BOTLIB_AI_MOVE_TO_GOAL), + GEN_IMPORT(BotMoveInDirection, BOTLIB_AI_MOVE_IN_DIRECTION), + GEN_IMPORT(BotResetAvoidReach, BOTLIB_AI_RESET_AVOID_REACH), + GEN_IMPORT(BotResetLastAvoidReach, BOTLIB_AI_RESET_LAST_AVOID_REACH), + GEN_IMPORT(BotReachabilityArea, BOTLIB_AI_REACHABILITY_AREA), + GEN_IMPORT(BotMovementViewTarget, BOTLIB_AI_MOVEMENT_VIEW_TARGET), + GEN_IMPORT(BotAllocMoveState, BOTLIB_AI_ALLOC_MOVE_STATE), + GEN_IMPORT(BotFreeMoveState, BOTLIB_AI_FREE_MOVE_STATE), + GEN_IMPORT(BotInitMoveState, BOTLIB_AI_INIT_MOVE_STATE), + GEN_IMPORT(BotChooseBestFightWeapon, BOTLIB_AI_CHOOSE_BEST_FIGHT_WEAPON), + GEN_IMPORT(BotGetWeaponInfo, BOTLIB_AI_GET_WEAPON_INFO), + GEN_IMPORT(BotLoadWeaponWeights, BOTLIB_AI_LOAD_WEAPON_WEIGHTS), + GEN_IMPORT(BotAllocWeaponState, BOTLIB_AI_ALLOC_WEAPON_STATE), + GEN_IMPORT(BotFreeWeaponState, BOTLIB_AI_FREE_WEAPON_STATE), + GEN_IMPORT(BotResetWeaponState, BOTLIB_AI_RESET_WEAPON_STATE), + GEN_IMPORT(GeneticParentsAndChildSelection, BOTLIB_AI_GENETIC_PARENTS_AND_CHILD_SELECTION), + GEN_IMPORT(BotInterbreedGoalFuzzyLogic, BOTLIB_AI_INTERBREED_GOAL_FUZZY_LOGIC), + GEN_IMPORT(BotMutateGoalFuzzyLogic, BOTLIB_AI_MUTATE_GOAL_FUZZY_LOGIC), + GEN_IMPORT(BotGetNextCampSpotGoal, BOTLIB_AI_GET_NEXT_CAMP_SPOT_GOAL), + GEN_IMPORT(BotGetMapLocationGoal, BOTLIB_AI_GET_MAP_LOCATION_GOAL), + GEN_IMPORT(BotNumInitialChats, BOTLIB_AI_NUM_INITIAL_CHATS), + GEN_IMPORT(BotGetChatMessage, BOTLIB_AI_GET_CHAT_MESSAGE), + GEN_IMPORT(BotRemoveFromAvoidGoals, BOTLIB_AI_REMOVE_FROM_AVOID_GOALS), + GEN_IMPORT(BotPredictVisiblePosition, BOTLIB_AI_PREDICT_VISIBLE_POSITION), + GEN_IMPORT(BotSetAvoidGoalTime, BOTLIB_AI_SET_AVOID_GOAL_TIME), + GEN_IMPORT(BotAddAvoidSpot, BOTLIB_AI_ADD_AVOID_SPOT), + GEN_IMPORT(BotLibSetup, BOTLIB_SETUP), + GEN_IMPORT(BotLibShutdown, BOTLIB_SHUTDOWN), + GEN_IMPORT(BotLibVarSet, BOTLIB_LIBVAR_SET), + GEN_IMPORT(BotLibVarGet, BOTLIB_LIBVAR_GET), + GEN_IMPORT(BotLibDefine, BOTLIB_PC_ADD_GLOBAL_DEFINE), + GEN_IMPORT(BotLibStartFrame, BOTLIB_START_FRAME), + GEN_IMPORT(BotLibLoadMap, BOTLIB_LOAD_MAP), + GEN_IMPORT(BotLibUpdateEntity, BOTLIB_UPDATENTITY), + GEN_IMPORT(BotLibTest, BOTLIB_TEST), + GEN_IMPORT(BotGetSnapshotEntity, BOTLIB_GET_SNAPSHOT_ENTITY), + GEN_IMPORT(BotGetServerCommand, BOTLIB_GET_CONSOLE_MESSAGE), + GEN_IMPORT(BotUserCommand, BOTLIB_USER_COMMAND), + GEN_IMPORT(BotUpdateWaypoints, G_BOT_UPDATEWAYPOINTS), + GEN_IMPORT(BotCalculatePaths, G_BOT_CALCULATEPATHS), + GEN_IMPORT(AAS_EnableRoutingArea, BOTLIB_AAS_ENABLE_ROUTING_AREA), + GEN_IMPORT(AAS_BBoxAreas, BOTLIB_AAS_BBOX_AREAS), + GEN_IMPORT(AAS_AreaInfo, BOTLIB_AAS_AREA_INFO), + GEN_IMPORT(AAS_EntityInfo, BOTLIB_AAS_ENTITY_INFO), + GEN_IMPORT(AAS_Initialized, BOTLIB_AAS_INITIALIZED), + GEN_IMPORT(AAS_PresenceTypeBoundingBox, BOTLIB_AAS_PRESENCE_TYPE_BOUNDING_BOX), + GEN_IMPORT(AAS_Time, BOTLIB_AAS_TIME), + GEN_IMPORT(AAS_PointAreaNum, BOTLIB_AAS_POINT_AREA_NUM), + GEN_IMPORT(AAS_TraceAreas, BOTLIB_AAS_TRACE_AREAS), + GEN_IMPORT(AAS_PointContents, BOTLIB_AAS_POINT_CONTENTS), + GEN_IMPORT(AAS_NextBSPEntity, BOTLIB_AAS_NEXT_BSP_ENTITY), + GEN_IMPORT(AAS_ValueForBSPEpairKey, BOTLIB_AAS_VALUE_FOR_BSP_EPAIR_KEY), + GEN_IMPORT(AAS_VectorForBSPEpairKey, BOTLIB_AAS_VECTOR_FOR_BSP_EPAIR_KEY), + GEN_IMPORT(AAS_FloatForBSPEpairKey, BOTLIB_AAS_FLOAT_FOR_BSP_EPAIR_KEY), + GEN_IMPORT(AAS_IntForBSPEpairKey, BOTLIB_AAS_INT_FOR_BSP_EPAIR_KEY), + GEN_IMPORT(AAS_AreaReachability, BOTLIB_AAS_AREA_REACHABILITY), + GEN_IMPORT(AAS_AreaTravelTimeToGoalArea, BOTLIB_AAS_AREA_TRAVEL_TIME_TO_GOAL_AREA), + GEN_IMPORT(AAS_Swimming, BOTLIB_AAS_SWIMMING), + GEN_IMPORT(AAS_PredictClientMovement, BOTLIB_AAS_PREDICT_CLIENT_MOVEMENT), + GEN_IMPORT(AAS_AlternativeRouteGoals, BOTLIB_AAS_ALTERNATIVE_ROUTE_GOAL), + GEN_IMPORT(AAS_PredictRoute, BOTLIB_AAS_PREDICT_ROUTE), + GEN_IMPORT(AAS_PointReachabilityAreaIndex, BOTLIB_AAS_POINT_REACHABILITY_AREA_INDEX), + GEN_IMPORT(EA_Say, BOTLIB_EA_SAY), + GEN_IMPORT(EA_SayTeam, BOTLIB_EA_SAY_TEAM), + GEN_IMPORT(EA_Command, BOTLIB_EA_COMMAND), + GEN_IMPORT(EA_Action, BOTLIB_EA_ACTION), + GEN_IMPORT(EA_Gesture, BOTLIB_EA_GESTURE), + GEN_IMPORT(EA_Talk, BOTLIB_EA_TALK), + GEN_IMPORT(EA_Attack, BOTLIB_EA_ATTACK), + GEN_IMPORT(EA_Alt_Attack, BOTLIB_EA_ALT_ATTACK), + GEN_IMPORT(EA_ForcePower, BOTLIB_EA_FORCEPOWER), + GEN_IMPORT(EA_Use, BOTLIB_EA_USE), + GEN_IMPORT(EA_Respawn, BOTLIB_EA_RESPAWN), + GEN_IMPORT(EA_Crouch, BOTLIB_EA_CROUCH), + GEN_IMPORT(EA_MoveUp, BOTLIB_EA_MOVE_UP), + GEN_IMPORT(EA_MoveDown, BOTLIB_EA_MOVE_DOWN), + GEN_IMPORT(EA_MoveForward, BOTLIB_EA_MOVE_FORWARD), + GEN_IMPORT(EA_MoveBack, BOTLIB_EA_MOVE_BACK), + GEN_IMPORT(EA_MoveLeft, BOTLIB_EA_MOVE_LEFT), + GEN_IMPORT(EA_MoveRight, BOTLIB_EA_MOVE_RIGHT), + GEN_IMPORT(EA_SelectWeapon, BOTLIB_EA_SELECT_WEAPON), + GEN_IMPORT(EA_Jump, BOTLIB_EA_JUMP), + GEN_IMPORT(EA_DelayedJump, BOTLIB_EA_DELAYED_JUMP), + GEN_IMPORT(EA_Move, BOTLIB_EA_MOVE), + GEN_IMPORT(EA_View, BOTLIB_EA_VIEW), + GEN_IMPORT(EA_EndRegular, BOTLIB_EA_END_REGULAR), + GEN_IMPORT(EA_GetInput, BOTLIB_EA_GET_INPUT), + GEN_IMPORT(EA_ResetInput, BOTLIB_EA_RESET_INPUT), + GEN_IMPORT(PC_LoadSource, BOTLIB_PC_LOAD_SOURCE), + GEN_IMPORT(PC_FreeSource, BOTLIB_PC_FREE_SOURCE), + GEN_IMPORT(PC_ReadToken, BOTLIB_PC_READ_TOKEN), + GEN_IMPORT(PC_SourceFileAndLine, BOTLIB_PC_SOURCE_FILE_AND_LINE), + GEN_IMPORT(R_RegisterSkin, G_R_REGISTERSKIN), + GEN_IMPORT(SetActiveSubBSP, G_SET_ACTIVE_SUBBSP), + GEN_IMPORT(CM_RegisterTerrain, G_CM_REGISTER_TERRAIN), + GEN_IMPORT(RMG_Init, G_RMG_INIT), + GEN_IMPORT(G2API_ListModelBones, G_G2_LISTBONES), + GEN_IMPORT(G2API_ListModelSurfaces, G_G2_LISTSURFACES), + GEN_IMPORT(G2API_HaveWeGhoul2Models, G_G2_HAVEWEGHOULMODELS), + GEN_IMPORT(G2API_SetGhoul2ModelIndexes, G_G2_SETMODELS), + GEN_IMPORT(G2API_GetBoltMatrix, G_G2_GETBOLT), + GEN_IMPORT(G2API_GetBoltMatrix_NoReconstruct, G_G2_GETBOLT_NOREC), + GEN_IMPORT(G2API_GetBoltMatrix_NoRecNoRot, G_G2_GETBOLT_NOREC_NOROT), + GEN_IMPORT(G2API_InitGhoul2Model, G_G2_INITGHOUL2MODEL), + GEN_IMPORT(G2API_SetSkin, G_G2_SETSKIN), + GEN_IMPORT(G2API_Ghoul2Size, G_G2_SIZE), + GEN_IMPORT(G2API_AddBolt, G_G2_ADDBOLT), + GEN_IMPORT(G2API_SetBoltInfo, G_G2_SETBOLTINFO), + GEN_IMPORT(G2API_SetBoneAngles, G_G2_ANGLEOVERRIDE), + GEN_IMPORT(G2API_SetBoneAnim, G_G2_PLAYANIM), + GEN_IMPORT(G2API_GetBoneAnim, G_G2_GETBONEANIM), + GEN_IMPORT(G2API_GetGLAName, G_G2_GETGLANAME), + GEN_IMPORT(G2API_CopyGhoul2Instance, G_G2_COPYGHOUL2INSTANCE), + GEN_IMPORT(G2API_CopySpecificGhoul2Model, G_G2_COPYSPECIFICGHOUL2MODEL), + GEN_IMPORT(G2API_DuplicateGhoul2Instance, G_G2_DUPLICATEGHOUL2INSTANCE), + GEN_IMPORT(G2API_HasGhoul2ModelOnIndex, G_G2_HASGHOUL2MODELONINDEX), + GEN_IMPORT(G2API_RemoveGhoul2Model, G_G2_REMOVEGHOUL2MODEL), + GEN_IMPORT(G2API_RemoveGhoul2Models, G_G2_REMOVEGHOUL2MODELS), + GEN_IMPORT(G2API_CleanGhoul2Models, G_G2_CLEANMODELS), + GEN_IMPORT(G2API_CollisionDetect, G_G2_COLLISIONDETECT), + GEN_IMPORT(G2API_CollisionDetectCache, G_G2_COLLISIONDETECTCACHE), + GEN_IMPORT(G2API_SetRootSurface, G_G2_SETROOTSURFACE), + GEN_IMPORT(G2API_SetSurfaceOnOff, G_G2_SETSURFACEONOFF), + GEN_IMPORT(G2API_SetNewOrigin, G_G2_SETNEWORIGIN), + GEN_IMPORT(G2API_DoesBoneExist, G_G2_DOESBONEEXIST), + GEN_IMPORT(G2API_GetSurfaceRenderStatus, G_G2_GETSURFACERENDERSTATUS), + GEN_IMPORT(G2API_AbsurdSmoothing, G_G2_ABSURDSMOOTHING), + GEN_IMPORT(G2API_SetRagDoll, G_G2_SETRAGDOLL), + GEN_IMPORT(G2API_AnimateG2Models, G_G2_ANIMATEG2MODELS), + GEN_IMPORT(G2API_RagPCJConstraint, G_G2_RAGPCJCONSTRAINT), + GEN_IMPORT(G2API_RagPCJGradientSpeed, G_G2_RAGPCJGRADIENTSPEED), + GEN_IMPORT(G2API_RagEffectorGoal, G_G2_RAGEFFECTORGOAL), + GEN_IMPORT(G2API_GetRagBonePos, G_G2_GETRAGBONEPOS), + GEN_IMPORT(G2API_RagEffectorKick, G_G2_RAGEFFECTORKICK), + GEN_IMPORT(G2API_RagForceSolve, G_G2_RAGFORCESOLVE), + GEN_IMPORT(G2API_SetBoneIKState, G_G2_SETBONEIKSTATE), + GEN_IMPORT(G2API_IKMove, G_G2_IKMOVE), + GEN_IMPORT(G2API_RemoveBone, G_G2_REMOVEBONE), + GEN_IMPORT(G2API_AttachInstanceToEntNum, G_G2_ATTACHINSTANCETOENTNUM), + GEN_IMPORT(G2API_ClearAttachedInstance, G_G2_CLEARATTACHEDINSTANCE), + GEN_IMPORT(G2API_CleanEntAttachments, G_G2_CLEANENTATTACHMENTS), + GEN_IMPORT(G2API_OverrideServer, G_G2_OVERRIDESERVER), + GEN_IMPORT(G2API_GetSurfaceName, G_G2_GETSURFACENAME), + }; + + // struct with lambdas that call QMM's vmMain function. this is given to the game engine + game_export_t qmm_export = { + GEN_EXPORT(InitGame, GAME_INIT), + GEN_EXPORT(ShutdownGame, GAME_SHUTDOWN), + GEN_EXPORT(ClientConnect, GAME_CLIENT_CONNECT), + GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), + GEN_EXPORT(ClientUserinfoChanged, GAME_CLIENT_USERINFO_CHANGED), + GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), + GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), + GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), + GEN_EXPORT(RunFrame, GAME_RUN_FRAME), + GEN_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND), + GEN_EXPORT(BotAIStartFrame, BOTAI_START_FRAME), + GEN_EXPORT(ROFF_NotetrackCallback, GAME_ROFF_NOTETRACK_CALLBACK), + GEN_EXPORT(SpawnRMGEntity, GAME_SPAWN_RMG_ENTITY), + GEN_EXPORT(ICARUS_PlaySound, GAME_ICARUS_PLAYSOUND), + GEN_EXPORT(ICARUS_Set, GAME_ICARUS_SET), + GEN_EXPORT(ICARUS_Lerp2Pos, GAME_ICARUS_LERP2POS), + GEN_EXPORT(ICARUS_Lerp2Origin, GAME_ICARUS_LERP2ORIGIN), + GEN_EXPORT(ICARUS_Lerp2Angles, GAME_ICARUS_LERP2ANGLES), + GEN_EXPORT(ICARUS_GetTag, GAME_ICARUS_GETTAG), + GEN_EXPORT(ICARUS_Lerp2Start, GAME_ICARUS_LERP2START), + GEN_EXPORT(ICARUS_Lerp2End, GAME_ICARUS_LERP2END), + GEN_EXPORT(ICARUS_Use, GAME_ICARUS_USE), + GEN_EXPORT(ICARUS_Kill, GAME_ICARUS_KILL), + GEN_EXPORT(ICARUS_Remove, GAME_ICARUS_REMOVE), + GEN_EXPORT(ICARUS_Play, GAME_ICARUS_PLAY), + GEN_EXPORT(ICARUS_GetFloat, GAME_ICARUS_GETFLOAT), + GEN_EXPORT(ICARUS_GetVector, GAME_ICARUS_GETVECTOR), + GEN_EXPORT(ICARUS_GetString, GAME_ICARUS_GETSTRING), + GEN_EXPORT(ICARUS_SoundIndex, GAME_ICARUS_SOUNDINDEX), + GEN_EXPORT(ICARUS_GetSetIDForString, GAME_ICARUS_GETSETIDFORSTRING), + GEN_EXPORT(NAV_ClearPathToPoint, GAME_NAV_CLEARPATHTOPOINT), + GEN_EXPORT(NPC_ClearLOS2, GAME_NAV_CLEARLOS), + GEN_EXPORT(NAVNEW_ClearPathBetweenPoints, GAME_NAV_CLEARPATHBETWEENPOINTS), + GEN_EXPORT(NAV_CheckNodeFailedForEnt, GAME_NAV_CHECKNODEFAILEDFORENT), + GEN_EXPORT(NAV_EntIsUnlockedDoor, GAME_NAV_ENTISUNLOCKEDDOOR), + GEN_EXPORT(NAV_EntIsDoor, GAME_NAV_ENTISDOOR), + GEN_EXPORT(NAV_EntIsBreakable, GAME_NAV_ENTISBREAKABLE), + GEN_EXPORT(NAV_EntIsRemovableUsable, GAME_NAV_ENTISREMOVABLEUSABLE), + GEN_EXPORT(NAV_FindCombatPointWaypoints, GAME_NAV_FINDCOMBATPOINTWAYPOINTS), + GEN_EXPORT(BG_GetItemIndexByTag, GAME_GETITEMINDEXBYTAG), + }; +}; -GEN_GAME_FUNCS(JAMP); +GEN_GAME_OBJ(JAMP); // auto-detection logic for JAMP -static bool JAMP_AutoDetect(api_supportedgame* game, APIType engineapi) { +bool JAMP_GameSupport::AutoDetect(APIType engineapi) { if (engineapi != QMM_API_DLLENTRY && engineapi != QMM_API_GETMODULEAPI) return false; // QMM filename must match default or an OpenJK temp filename (if DLL was pulled from .pk3) - if (!str_striequal(g_gameinfo.qmm_file, game->dllname) + if (!str_striequal(g_gameinfo.qmm_file, DefaultDLLName()) && !str_striequal(g_gameinfo.qmm_file.substr(0, 3), "ojk") - && !str_striequal(path_baseext(g_gameinfo.qmm_file), "tmp")) + && !str_striequal(path_baseext(g_gameinfo.qmm_file), ".tmp")) { return false; } @@ -50,390 +440,14 @@ static bool JAMP_AutoDetect(api_supportedgame* game, APIType engineapi) { } -// original syscall pointer that comes from the game engine -static eng_syscall orig_syscall = nullptr; - -// pointer to vmMain that comes from the mod -static mod_vmMain orig_vmMain = nullptr; - - -// these variables are all for the GGA implementation for OpenJK - -// a copy of the apiversion int that comes from the game engine -static intptr_t orig_apiversion = 0; - -// a copy of the original import struct that comes from the game engine -static game_import_t orig_import; - -// a copy of the original export struct pointer that comes from the mod -static game_export_t* orig_export = nullptr; - -// struct with lambdas that call QMM's syscall function. this is given to the mod -static game_import_t qmm_import = { - GEN_IMPORT(Print, G_PRINT), - GEN_IMPORT(Error, G_ERROR), - GEN_IMPORT(Milliseconds, G_MILLISECONDS), - GEN_IMPORT(PrecisionTimerStart, G_PRECISIONTIMER_START), - GEN_IMPORT(PrecisionTimerEnd, G_PRECISIONTIMER_END), - GEN_IMPORT(SV_RegisterSharedMemory, G_SET_SHARED_BUFFER), - GEN_IMPORT(RealTime, G_REAL_TIME), - GEN_IMPORT(TrueMalloc, G_TRUEMALLOC), - GEN_IMPORT(TrueFree, G_TRUEFREE), - GEN_IMPORT(SnapVector, G_SNAPVECTOR), - GEN_IMPORT(Cvar_Register, G_CVAR_REGISTER), - GEN_IMPORT(Cvar_Set, G_CVAR_SET), - GEN_IMPORT(Cvar_Update, G_CVAR_UPDATE), - GEN_IMPORT(Cvar_VariableIntegerValue, G_CVAR_VARIABLE_INTEGER_VALUE), - GEN_IMPORT(Cvar_VariableStringBuffer, G_CVAR_VARIABLE_STRING_BUFFER), - GEN_IMPORT(Argc, G_ARGC), - GEN_IMPORT(Argv, G_ARGV), - GEN_IMPORT(FS_Close, G_FS_FCLOSE_FILE), - GEN_IMPORT(FS_GetFileList, G_FS_GETFILELIST), - GEN_IMPORT(FS_Open, G_FS_FOPEN_FILE), - GEN_IMPORT(FS_Read, G_FS_READ), - GEN_IMPORT(FS_Write, G_FS_WRITE), - GEN_IMPORT(AdjustAreaPortalState, G_ADJUST_AREA_PORTAL_STATE), - GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), - GEN_IMPORT(DebugPolygonCreate, G_DEBUG_POLYGON_CREATE), - GEN_IMPORT(DebugPolygonDelete, G_DEBUG_POLYGON_DELETE), - GEN_IMPORT(DropClient, G_DROP_CLIENT), - GEN_IMPORT(EntitiesInBox, G_ENTITIES_IN_BOX), - GEN_IMPORT(EntityContact, G_ENTITY_CONTACT), - GEN_IMPORT(GetConfigstring, G_GET_CONFIGSTRING), - GEN_IMPORT(GetEntityToken, G_GET_ENTITY_TOKEN), - GEN_IMPORT(GetServerinfo, G_GET_SERVERINFO), - GEN_IMPORT(GetUsercmd, G_GET_USERCMD), - GEN_IMPORT(GetUserinfo, G_GET_USERINFO), - GEN_IMPORT(InPVS, G_IN_PVS), - GEN_IMPORT(InPVSIgnorePortals, G_IN_PVS_IGNORE_PORTALS), - GEN_IMPORT(LinkEntity, G_LINKENTITY), - GEN_IMPORT(LocateGameData, G_LOCATE_GAME_DATA), - GEN_IMPORT(PointContents, G_POINT_CONTENTS), - GEN_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND), - GEN_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND), - GEN_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL), - GEN_IMPORT(SetConfigstring, G_SET_CONFIGSTRING), - GEN_IMPORT(SetServerCull, G_SET_SERVER_CULL), - GEN_IMPORT(SetUserinfo, G_SET_USERINFO), - GEN_IMPORT(SiegePersSet, G_SIEGEPERSSET), - GEN_IMPORT(SiegePersGet, G_SIEGEPERSGET), - GEN_IMPORT(Trace, G_TRACE), - GEN_IMPORT(UnlinkEntity, G_UNLINKENTITY), - GEN_IMPORT(ROFF_Clean, G_ROFF_CLEAN), - GEN_IMPORT(ROFF_UpdateEntities, G_ROFF_UPDATE_ENTITIES), - GEN_IMPORT(ROFF_Cache, G_ROFF_CACHE), - GEN_IMPORT(ROFF_Play, G_ROFF_PLAY), - GEN_IMPORT(ROFF_Purge_Ent, G_ROFF_PURGE_ENT), - GEN_IMPORT(ICARUS_RunScript, G_ICARUS_RUNSCRIPT), - GEN_IMPORT(ICARUS_RegisterScript, G_ICARUS_REGISTERSCRIPT), - GEN_IMPORT(ICARUS_Init, G_ICARUS_INIT), - GEN_IMPORT(ICARUS_ValidEnt, G_ICARUS_VALIDENT), - GEN_IMPORT(ICARUS_IsInitialized, G_ICARUS_ISINITIALIZED), - GEN_IMPORT(ICARUS_MaintainTaskManager, G_ICARUS_MAINTAINTASKMANAGER), - GEN_IMPORT(ICARUS_IsRunning, G_ICARUS_ISRUNNING), - GEN_IMPORT(ICARUS_TaskIDPending, G_ICARUS_TASKIDPENDING), - GEN_IMPORT(ICARUS_InitEnt, G_ICARUS_INITENT), - GEN_IMPORT(ICARUS_FreeEnt, G_ICARUS_FREEENT), - GEN_IMPORT(ICARUS_AssociateEnt, G_ICARUS_ASSOCIATEENT), - GEN_IMPORT(ICARUS_Shutdown, G_ICARUS_SHUTDOWN), - GEN_IMPORT(ICARUS_TaskIDSet, G_ICARUS_TASKIDSET), - GEN_IMPORT(ICARUS_TaskIDComplete, G_ICARUS_TASKIDCOMPLETE), - GEN_IMPORT(ICARUS_SetVar, G_ICARUS_SETVAR), - GEN_IMPORT(ICARUS_VariableDeclared, G_ICARUS_VARIABLEDECLARED), - GEN_IMPORT(ICARUS_GetFloatVariable, G_ICARUS_GETFLOATVARIABLE), - GEN_IMPORT(ICARUS_GetStringVariable, G_ICARUS_GETSTRINGVARIABLE), - GEN_IMPORT(ICARUS_GetVectorVariable, G_ICARUS_GETVECTORVARIABLE), - GEN_IMPORT(Nav_Init, G_NAV_INIT), - GEN_IMPORT(Nav_Free, G_NAV_FREE), - GEN_IMPORT(Nav_Load, G_NAV_LOAD), - GEN_IMPORT(Nav_Save, G_NAV_SAVE), - GEN_IMPORT(Nav_AddRawPoint, G_NAV_ADDRAWPOINT), - GEN_IMPORT(Nav_CalculatePaths, G_NAV_CALCULATEPATHS), - GEN_IMPORT(Nav_HardConnect, G_NAV_HARDCONNECT), - GEN_IMPORT(Nav_ShowNodes, G_NAV_SHOWNODES), - GEN_IMPORT(Nav_ShowEdges, G_NAV_SHOWEDGES), - GEN_IMPORT(Nav_ShowPath, G_NAV_SHOWPATH), - GEN_IMPORT(Nav_GetNearestNode, G_NAV_GETNEARESTNODE), - GEN_IMPORT(Nav_GetBestNode, G_NAV_GETBESTNODE), - GEN_IMPORT(Nav_GetNodePosition, G_NAV_GETNODEPOSITION), - GEN_IMPORT(Nav_GetNodeNumEdges, G_NAV_GETNODENUMEDGES), - GEN_IMPORT(Nav_GetNodeEdge, G_NAV_GETNODEEDGE), - GEN_IMPORT(Nav_GetNumNodes, G_NAV_GETNUMNODES), - GEN_IMPORT(Nav_Connected, G_NAV_CONNECTED), - GEN_IMPORT(Nav_GetPathCost, G_NAV_GETPATHCOST), - GEN_IMPORT(Nav_GetEdgeCost, G_NAV_GETEDGECOST), - GEN_IMPORT(Nav_GetProjectedNode, G_NAV_GETPROJECTEDNODE), - GEN_IMPORT(Nav_CheckFailedNodes, G_NAV_CHECKFAILEDNODES), - GEN_IMPORT(Nav_AddFailedNode, G_NAV_ADDFAILEDNODE), - GEN_IMPORT(Nav_NodeFailed, G_NAV_NODEFAILED), - GEN_IMPORT(Nav_NodesAreNeighbors, G_NAV_NODESARENEIGHBORS), - GEN_IMPORT(Nav_ClearFailedEdge, G_NAV_CLEARFAILEDEDGE), - GEN_IMPORT(Nav_ClearAllFailedEdges, G_NAV_CLEARALLFAILEDEDGES), - GEN_IMPORT(Nav_EdgeFailed, G_NAV_EDGEFAILED), - GEN_IMPORT(Nav_AddFailedEdge, G_NAV_ADDFAILEDEDGE), - GEN_IMPORT(Nav_CheckFailedEdge, G_NAV_CHECKFAILEDEDGE), - GEN_IMPORT(Nav_CheckAllFailedEdges, G_NAV_CHECKALLFAILEDEDGES), - GEN_IMPORT(Nav_RouteBlocked, G_NAV_ROUTEBLOCKED), - GEN_IMPORT(Nav_GetBestNodeAltRoute, G_NAV_GETBESTNODEALTROUTE), - GEN_IMPORT(Nav_GetBestNodeAltRoute2, G_NAV_GETBESTNODEALT2), - GEN_IMPORT(Nav_GetBestPathBetweenEnts, G_NAV_GETBESTPATHBETWEENENTS), - GEN_IMPORT(Nav_GetNodeRadius, G_NAV_GETNODERADIUS), - GEN_IMPORT(Nav_CheckBlockedEdges, G_NAV_CHECKBLOCKEDEDGES), - GEN_IMPORT(Nav_ClearCheckedNodes, G_NAV_CLEARCHECKEDNODES), - GEN_IMPORT(Nav_CheckedNode, G_NAV_CHECKEDNODE), - GEN_IMPORT(Nav_SetCheckedNode, G_NAV_SETCHECKEDNODE), - GEN_IMPORT(Nav_FlagAllNodes, G_NAV_FLAGALLNODES), - GEN_IMPORT(Nav_GetPathsCalculated, G_NAV_GETPATHSCALCULATED), - GEN_IMPORT(Nav_SetPathsCalculated, G_NAV_SETPATHSCALCULATED), - GEN_IMPORT(BotAllocateClient, G_BOT_ALLOCATE_CLIENT), - GEN_IMPORT(BotFreeClient, G_BOT_FREE_CLIENT), - GEN_IMPORT(BotLoadCharacter, BOTLIB_AI_LOAD_CHARACTER), - GEN_IMPORT(BotFreeCharacter, BOTLIB_AI_FREE_CHARACTER), - GEN_IMPORT(Characteristic_Float, BOTLIB_AI_CHARACTERISTIC_FLOAT), - GEN_IMPORT(Characteristic_BFloat, BOTLIB_AI_CHARACTERISTIC_BFLOAT), - GEN_IMPORT(Characteristic_Integer, BOTLIB_AI_CHARACTERISTIC_INTEGER), - GEN_IMPORT(Characteristic_BInteger, BOTLIB_AI_CHARACTERISTIC_BINTEGER), - GEN_IMPORT(Characteristic_String, BOTLIB_AI_CHARACTERISTIC_STRING), - GEN_IMPORT(BotAllocChatState, BOTLIB_AI_ALLOC_CHAT_STATE), - GEN_IMPORT(BotFreeChatState, BOTLIB_AI_FREE_CHAT_STATE), - GEN_IMPORT(BotQueueConsoleMessage, BOTLIB_AI_QUEUE_CONSOLE_MESSAGE), - GEN_IMPORT(BotRemoveConsoleMessage, BOTLIB_AI_REMOVE_CONSOLE_MESSAGE), - GEN_IMPORT(BotNextConsoleMessage, BOTLIB_AI_NEXT_CONSOLE_MESSAGE), - GEN_IMPORT(BotNumConsoleMessages, BOTLIB_AI_NUM_CONSOLE_MESSAGE), - GEN_IMPORT(BotInitialChat, BOTLIB_AI_INITIAL_CHAT), - GEN_IMPORT(BotReplyChat, BOTLIB_AI_REPLY_CHAT), - GEN_IMPORT(BotChatLength, BOTLIB_AI_CHAT_LENGTH), - GEN_IMPORT(BotEnterChat, BOTLIB_AI_ENTER_CHAT), - GEN_IMPORT(StringContains, BOTLIB_AI_STRING_CONTAINS), - GEN_IMPORT(BotFindMatch, BOTLIB_AI_FIND_MATCH), - GEN_IMPORT(BotMatchVariable, BOTLIB_AI_MATCH_VARIABLE), - GEN_IMPORT(UnifyWhiteSpaces, BOTLIB_AI_UNIFY_WHITE_SPACES), - GEN_IMPORT(BotReplaceSynonyms, BOTLIB_AI_REPLACE_SYNONYMS), - GEN_IMPORT(BotLoadChatFile, BOTLIB_AI_LOAD_CHAT_FILE), - GEN_IMPORT(BotSetChatGender, BOTLIB_AI_SET_CHAT_GENDER), - GEN_IMPORT(BotSetChatName, BOTLIB_AI_SET_CHAT_NAME), - GEN_IMPORT(BotResetGoalState, BOTLIB_AI_RESET_GOAL_STATE), - GEN_IMPORT(BotResetAvoidGoals, BOTLIB_AI_RESET_AVOID_GOALS), - GEN_IMPORT(BotPushGoal, BOTLIB_AI_PUSH_GOAL), - GEN_IMPORT(BotPopGoal, BOTLIB_AI_POP_GOAL), - GEN_IMPORT(BotEmptyGoalStack, BOTLIB_AI_EMPTY_GOAL_STACK), - GEN_IMPORT(BotDumpAvoidGoals, BOTLIB_AI_DUMP_AVOID_GOALS), - GEN_IMPORT(BotDumpGoalStack, BOTLIB_AI_DUMP_GOAL_STACK), - GEN_IMPORT(BotGoalName, BOTLIB_AI_GOAL_NAME), - GEN_IMPORT(BotGetTopGoal, BOTLIB_AI_GET_TOP_GOAL), - GEN_IMPORT(BotGetSecondGoal, BOTLIB_AI_GET_SECOND_GOAL), - GEN_IMPORT(BotChooseLTGItem, BOTLIB_AI_CHOOSE_LTG_ITEM), - GEN_IMPORT(BotChooseNBGItem, BOTLIB_AI_CHOOSE_NBG_ITEM), - GEN_IMPORT(BotTouchingGoal, BOTLIB_AI_TOUCHING_GOAL), - GEN_IMPORT(BotItemGoalInVisButNotVisible, BOTLIB_AI_ITEM_GOAL_IN_VIS_BUT_NOT_VISIBLE), - GEN_IMPORT(BotGetLevelItemGoal, BOTLIB_AI_GET_LEVEL_ITEM_GOAL), - GEN_IMPORT(BotAvoidGoalTime, BOTLIB_AI_AVOID_GOAL_TIME), - GEN_IMPORT(BotInitLevelItems, BOTLIB_AI_INIT_LEVEL_ITEMS), - GEN_IMPORT(BotUpdateEntityItems, BOTLIB_AI_UPDATE_ENTITY_ITEMS), - GEN_IMPORT(BotLoadItemWeights, BOTLIB_AI_LOAD_ITEM_WEIGHTS), - GEN_IMPORT(BotFreeItemWeights, BOTLIB_AI_FREE_ITEM_WEIGHTS), - GEN_IMPORT(BotSaveGoalFuzzyLogic, BOTLIB_AI_SAVE_GOAL_FUZZY_LOGIC), - GEN_IMPORT(BotAllocGoalState, BOTLIB_AI_ALLOC_GOAL_STATE), - GEN_IMPORT(BotFreeGoalState, BOTLIB_AI_FREE_GOAL_STATE), - GEN_IMPORT(BotResetMoveState, BOTLIB_AI_RESET_MOVE_STATE), - GEN_IMPORT(BotMoveToGoal, BOTLIB_AI_MOVE_TO_GOAL), - GEN_IMPORT(BotMoveInDirection, BOTLIB_AI_MOVE_IN_DIRECTION), - GEN_IMPORT(BotResetAvoidReach, BOTLIB_AI_RESET_AVOID_REACH), - GEN_IMPORT(BotResetLastAvoidReach, BOTLIB_AI_RESET_LAST_AVOID_REACH), - GEN_IMPORT(BotReachabilityArea, BOTLIB_AI_REACHABILITY_AREA), - GEN_IMPORT(BotMovementViewTarget, BOTLIB_AI_MOVEMENT_VIEW_TARGET), - GEN_IMPORT(BotAllocMoveState, BOTLIB_AI_ALLOC_MOVE_STATE), - GEN_IMPORT(BotFreeMoveState, BOTLIB_AI_FREE_MOVE_STATE), - GEN_IMPORT(BotInitMoveState, BOTLIB_AI_INIT_MOVE_STATE), - GEN_IMPORT(BotChooseBestFightWeapon, BOTLIB_AI_CHOOSE_BEST_FIGHT_WEAPON), - GEN_IMPORT(BotGetWeaponInfo, BOTLIB_AI_GET_WEAPON_INFO), - GEN_IMPORT(BotLoadWeaponWeights, BOTLIB_AI_LOAD_WEAPON_WEIGHTS), - GEN_IMPORT(BotAllocWeaponState, BOTLIB_AI_ALLOC_WEAPON_STATE), - GEN_IMPORT(BotFreeWeaponState, BOTLIB_AI_FREE_WEAPON_STATE), - GEN_IMPORT(BotResetWeaponState, BOTLIB_AI_RESET_WEAPON_STATE), - GEN_IMPORT(GeneticParentsAndChildSelection, BOTLIB_AI_GENETIC_PARENTS_AND_CHILD_SELECTION), - GEN_IMPORT(BotInterbreedGoalFuzzyLogic, BOTLIB_AI_INTERBREED_GOAL_FUZZY_LOGIC), - GEN_IMPORT(BotMutateGoalFuzzyLogic, BOTLIB_AI_MUTATE_GOAL_FUZZY_LOGIC), - GEN_IMPORT(BotGetNextCampSpotGoal, BOTLIB_AI_GET_NEXT_CAMP_SPOT_GOAL), - GEN_IMPORT(BotGetMapLocationGoal, BOTLIB_AI_GET_MAP_LOCATION_GOAL), - GEN_IMPORT(BotNumInitialChats, BOTLIB_AI_NUM_INITIAL_CHATS), - GEN_IMPORT(BotGetChatMessage, BOTLIB_AI_GET_CHAT_MESSAGE), - GEN_IMPORT(BotRemoveFromAvoidGoals, BOTLIB_AI_REMOVE_FROM_AVOID_GOALS), - GEN_IMPORT(BotPredictVisiblePosition, BOTLIB_AI_PREDICT_VISIBLE_POSITION), - GEN_IMPORT(BotSetAvoidGoalTime, BOTLIB_AI_SET_AVOID_GOAL_TIME), - GEN_IMPORT(BotAddAvoidSpot, BOTLIB_AI_ADD_AVOID_SPOT), - GEN_IMPORT(BotLibSetup, BOTLIB_SETUP), - GEN_IMPORT(BotLibShutdown, BOTLIB_SHUTDOWN), - GEN_IMPORT(BotLibVarSet, BOTLIB_LIBVAR_SET), - GEN_IMPORT(BotLibVarGet, BOTLIB_LIBVAR_GET), - GEN_IMPORT(BotLibDefine, BOTLIB_PC_ADD_GLOBAL_DEFINE), - GEN_IMPORT(BotLibStartFrame, BOTLIB_START_FRAME), - GEN_IMPORT(BotLibLoadMap, BOTLIB_LOAD_MAP), - GEN_IMPORT(BotLibUpdateEntity, BOTLIB_UPDATENTITY), - GEN_IMPORT(BotLibTest, BOTLIB_TEST), - GEN_IMPORT(BotGetSnapshotEntity, BOTLIB_GET_SNAPSHOT_ENTITY), - GEN_IMPORT(BotGetServerCommand, BOTLIB_GET_CONSOLE_MESSAGE), - GEN_IMPORT(BotUserCommand, BOTLIB_USER_COMMAND), - GEN_IMPORT(BotUpdateWaypoints, G_BOT_UPDATEWAYPOINTS), - GEN_IMPORT(BotCalculatePaths, G_BOT_CALCULATEPATHS), - GEN_IMPORT(AAS_EnableRoutingArea, BOTLIB_AAS_ENABLE_ROUTING_AREA), - GEN_IMPORT(AAS_BBoxAreas, BOTLIB_AAS_BBOX_AREAS), - GEN_IMPORT(AAS_AreaInfo, BOTLIB_AAS_AREA_INFO), - GEN_IMPORT(AAS_EntityInfo, BOTLIB_AAS_ENTITY_INFO), - GEN_IMPORT(AAS_Initialized, BOTLIB_AAS_INITIALIZED), - GEN_IMPORT(AAS_PresenceTypeBoundingBox, BOTLIB_AAS_PRESENCE_TYPE_BOUNDING_BOX), - GEN_IMPORT(AAS_Time, BOTLIB_AAS_TIME), - GEN_IMPORT(AAS_PointAreaNum, BOTLIB_AAS_POINT_AREA_NUM), - GEN_IMPORT(AAS_TraceAreas, BOTLIB_AAS_TRACE_AREAS), - GEN_IMPORT(AAS_PointContents, BOTLIB_AAS_POINT_CONTENTS), - GEN_IMPORT(AAS_NextBSPEntity, BOTLIB_AAS_NEXT_BSP_ENTITY), - GEN_IMPORT(AAS_ValueForBSPEpairKey, BOTLIB_AAS_VALUE_FOR_BSP_EPAIR_KEY), - GEN_IMPORT(AAS_VectorForBSPEpairKey, BOTLIB_AAS_VECTOR_FOR_BSP_EPAIR_KEY), - GEN_IMPORT(AAS_FloatForBSPEpairKey, BOTLIB_AAS_FLOAT_FOR_BSP_EPAIR_KEY), - GEN_IMPORT(AAS_IntForBSPEpairKey, BOTLIB_AAS_INT_FOR_BSP_EPAIR_KEY), - GEN_IMPORT(AAS_AreaReachability, BOTLIB_AAS_AREA_REACHABILITY), - GEN_IMPORT(AAS_AreaTravelTimeToGoalArea, BOTLIB_AAS_AREA_TRAVEL_TIME_TO_GOAL_AREA), - GEN_IMPORT(AAS_Swimming, BOTLIB_AAS_SWIMMING), - GEN_IMPORT(AAS_PredictClientMovement, BOTLIB_AAS_PREDICT_CLIENT_MOVEMENT), - GEN_IMPORT(AAS_AlternativeRouteGoals, BOTLIB_AAS_ALTERNATIVE_ROUTE_GOAL), - GEN_IMPORT(AAS_PredictRoute, BOTLIB_AAS_PREDICT_ROUTE), - GEN_IMPORT(AAS_PointReachabilityAreaIndex, BOTLIB_AAS_POINT_REACHABILITY_AREA_INDEX), - GEN_IMPORT(EA_Say, BOTLIB_EA_SAY), - GEN_IMPORT(EA_SayTeam, BOTLIB_EA_SAY_TEAM), - GEN_IMPORT(EA_Command, BOTLIB_EA_COMMAND), - GEN_IMPORT(EA_Action, BOTLIB_EA_ACTION), - GEN_IMPORT(EA_Gesture, BOTLIB_EA_GESTURE), - GEN_IMPORT(EA_Talk, BOTLIB_EA_TALK), - GEN_IMPORT(EA_Attack, BOTLIB_EA_ATTACK), - GEN_IMPORT(EA_Alt_Attack, BOTLIB_EA_ALT_ATTACK), - GEN_IMPORT(EA_ForcePower, BOTLIB_EA_FORCEPOWER), - GEN_IMPORT(EA_Use, BOTLIB_EA_USE), - GEN_IMPORT(EA_Respawn, BOTLIB_EA_RESPAWN), - GEN_IMPORT(EA_Crouch, BOTLIB_EA_CROUCH), - GEN_IMPORT(EA_MoveUp, BOTLIB_EA_MOVE_UP), - GEN_IMPORT(EA_MoveDown, BOTLIB_EA_MOVE_DOWN), - GEN_IMPORT(EA_MoveForward, BOTLIB_EA_MOVE_FORWARD), - GEN_IMPORT(EA_MoveBack, BOTLIB_EA_MOVE_BACK), - GEN_IMPORT(EA_MoveLeft, BOTLIB_EA_MOVE_LEFT), - GEN_IMPORT(EA_MoveRight, BOTLIB_EA_MOVE_RIGHT), - GEN_IMPORT(EA_SelectWeapon, BOTLIB_EA_SELECT_WEAPON), - GEN_IMPORT(EA_Jump, BOTLIB_EA_JUMP), - GEN_IMPORT(EA_DelayedJump, BOTLIB_EA_DELAYED_JUMP), - GEN_IMPORT(EA_Move, BOTLIB_EA_MOVE), - GEN_IMPORT(EA_View, BOTLIB_EA_VIEW), - GEN_IMPORT(EA_EndRegular, BOTLIB_EA_END_REGULAR), - GEN_IMPORT(EA_GetInput, BOTLIB_EA_GET_INPUT), - GEN_IMPORT(EA_ResetInput, BOTLIB_EA_RESET_INPUT), - GEN_IMPORT(PC_LoadSource, BOTLIB_PC_LOAD_SOURCE), - GEN_IMPORT(PC_FreeSource, BOTLIB_PC_FREE_SOURCE), - GEN_IMPORT(PC_ReadToken, BOTLIB_PC_READ_TOKEN), - GEN_IMPORT(PC_SourceFileAndLine, BOTLIB_PC_SOURCE_FILE_AND_LINE), - GEN_IMPORT(R_RegisterSkin, G_R_REGISTERSKIN), - GEN_IMPORT(SetActiveSubBSP, G_SET_ACTIVE_SUBBSP), - GEN_IMPORT(CM_RegisterTerrain, G_CM_REGISTER_TERRAIN), - GEN_IMPORT(RMG_Init, G_RMG_INIT), - GEN_IMPORT(G2API_ListModelBones, G_G2_LISTBONES), - GEN_IMPORT(G2API_ListModelSurfaces, G_G2_LISTSURFACES), - GEN_IMPORT(G2API_HaveWeGhoul2Models, G_G2_HAVEWEGHOULMODELS), - GEN_IMPORT(G2API_SetGhoul2ModelIndexes, G_G2_SETMODELS), - GEN_IMPORT(G2API_GetBoltMatrix, G_G2_GETBOLT), - GEN_IMPORT(G2API_GetBoltMatrix_NoReconstruct, G_G2_GETBOLT_NOREC), - GEN_IMPORT(G2API_GetBoltMatrix_NoRecNoRot, G_G2_GETBOLT_NOREC_NOROT), - GEN_IMPORT(G2API_InitGhoul2Model, G_G2_INITGHOUL2MODEL), - GEN_IMPORT(G2API_SetSkin, G_G2_SETSKIN), - GEN_IMPORT(G2API_Ghoul2Size, G_G2_SIZE), - GEN_IMPORT(G2API_AddBolt, G_G2_ADDBOLT), - GEN_IMPORT(G2API_SetBoltInfo, G_G2_SETBOLTINFO), - GEN_IMPORT(G2API_SetBoneAngles, G_G2_ANGLEOVERRIDE), - GEN_IMPORT(G2API_SetBoneAnim, G_G2_PLAYANIM), - GEN_IMPORT(G2API_GetBoneAnim, G_G2_GETBONEANIM), - GEN_IMPORT(G2API_GetGLAName, G_G2_GETGLANAME), - GEN_IMPORT(G2API_CopyGhoul2Instance, G_G2_COPYGHOUL2INSTANCE), - GEN_IMPORT(G2API_CopySpecificGhoul2Model, G_G2_COPYSPECIFICGHOUL2MODEL), - GEN_IMPORT(G2API_DuplicateGhoul2Instance, G_G2_DUPLICATEGHOUL2INSTANCE), - GEN_IMPORT(G2API_HasGhoul2ModelOnIndex, G_G2_HASGHOUL2MODELONINDEX), - GEN_IMPORT(G2API_RemoveGhoul2Model, G_G2_REMOVEGHOUL2MODEL), - GEN_IMPORT(G2API_RemoveGhoul2Models, G_G2_REMOVEGHOUL2MODELS), - GEN_IMPORT(G2API_CleanGhoul2Models, G_G2_CLEANMODELS), - GEN_IMPORT(G2API_CollisionDetect, G_G2_COLLISIONDETECT), - GEN_IMPORT(G2API_CollisionDetectCache, G_G2_COLLISIONDETECTCACHE), - GEN_IMPORT(G2API_SetRootSurface, G_G2_SETROOTSURFACE), - GEN_IMPORT(G2API_SetSurfaceOnOff, G_G2_SETSURFACEONOFF), - GEN_IMPORT(G2API_SetNewOrigin, G_G2_SETNEWORIGIN), - GEN_IMPORT(G2API_DoesBoneExist, G_G2_DOESBONEEXIST), - GEN_IMPORT(G2API_GetSurfaceRenderStatus, G_G2_GETSURFACERENDERSTATUS), - GEN_IMPORT(G2API_AbsurdSmoothing, G_G2_ABSURDSMOOTHING), - GEN_IMPORT(G2API_SetRagDoll, G_G2_SETRAGDOLL), - GEN_IMPORT(G2API_AnimateG2Models, G_G2_ANIMATEG2MODELS), - GEN_IMPORT(G2API_RagPCJConstraint, G_G2_RAGPCJCONSTRAINT), - GEN_IMPORT(G2API_RagPCJGradientSpeed, G_G2_RAGPCJGRADIENTSPEED), - GEN_IMPORT(G2API_RagEffectorGoal, G_G2_RAGEFFECTORGOAL), - GEN_IMPORT(G2API_GetRagBonePos, G_G2_GETRAGBONEPOS), - GEN_IMPORT(G2API_RagEffectorKick, G_G2_RAGEFFECTORKICK), - GEN_IMPORT(G2API_RagForceSolve, G_G2_RAGFORCESOLVE), - GEN_IMPORT(G2API_SetBoneIKState, G_G2_SETBONEIKSTATE), - GEN_IMPORT(G2API_IKMove, G_G2_IKMOVE), - GEN_IMPORT(G2API_RemoveBone, G_G2_REMOVEBONE), - GEN_IMPORT(G2API_AttachInstanceToEntNum, G_G2_ATTACHINSTANCETOENTNUM), - GEN_IMPORT(G2API_ClearAttachedInstance, G_G2_CLEARATTACHEDINSTANCE), - GEN_IMPORT(G2API_CleanEntAttachments, G_G2_CLEANENTATTACHMENTS), - GEN_IMPORT(G2API_OverrideServer, G_G2_OVERRIDESERVER), - GEN_IMPORT(G2API_GetSurfaceName, G_G2_GETSURFACENAME), -}; - -// struct with lambdas that call QMM's vmMain function. this is given to the game engine -static game_export_t qmm_export = { - GEN_EXPORT(InitGame, GAME_INIT), - GEN_EXPORT(ShutdownGame, GAME_SHUTDOWN), - GEN_EXPORT(ClientConnect, GAME_CLIENT_CONNECT), - GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), - GEN_EXPORT(ClientUserinfoChanged, GAME_CLIENT_USERINFO_CHANGED), - GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), - GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), - GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), - GEN_EXPORT(RunFrame, GAME_RUN_FRAME), - GEN_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND), - GEN_EXPORT(BotAIStartFrame, BOTAI_START_FRAME), - GEN_EXPORT(ROFF_NotetrackCallback, GAME_ROFF_NOTETRACK_CALLBACK), - GEN_EXPORT(SpawnRMGEntity, GAME_SPAWN_RMG_ENTITY), - GEN_EXPORT(ICARUS_PlaySound, GAME_ICARUS_PLAYSOUND), - GEN_EXPORT(ICARUS_Set, GAME_ICARUS_SET), - GEN_EXPORT(ICARUS_Lerp2Pos, GAME_ICARUS_LERP2POS), - GEN_EXPORT(ICARUS_Lerp2Origin, GAME_ICARUS_LERP2ORIGIN), - GEN_EXPORT(ICARUS_Lerp2Angles, GAME_ICARUS_LERP2ANGLES), - GEN_EXPORT(ICARUS_GetTag, GAME_ICARUS_GETTAG), - GEN_EXPORT(ICARUS_Lerp2Start, GAME_ICARUS_LERP2START), - GEN_EXPORT(ICARUS_Lerp2End, GAME_ICARUS_LERP2END), - GEN_EXPORT(ICARUS_Use, GAME_ICARUS_USE), - GEN_EXPORT(ICARUS_Kill, GAME_ICARUS_KILL), - GEN_EXPORT(ICARUS_Remove, GAME_ICARUS_REMOVE), - GEN_EXPORT(ICARUS_Play, GAME_ICARUS_PLAY), - GEN_EXPORT(ICARUS_GetFloat, GAME_ICARUS_GETFLOAT), - GEN_EXPORT(ICARUS_GetVector, GAME_ICARUS_GETVECTOR), - GEN_EXPORT(ICARUS_GetString, GAME_ICARUS_GETSTRING), - GEN_EXPORT(ICARUS_SoundIndex, GAME_ICARUS_SOUNDINDEX), - GEN_EXPORT(ICARUS_GetSetIDForString, GAME_ICARUS_GETSETIDFORSTRING), - GEN_EXPORT(NAV_ClearPathToPoint, GAME_NAV_CLEARPATHTOPOINT), - GEN_EXPORT(NPC_ClearLOS2, GAME_NAV_CLEARLOS), - GEN_EXPORT(NAVNEW_ClearPathBetweenPoints, GAME_NAV_CLEARPATHBETWEENPOINTS), - GEN_EXPORT(NAV_CheckNodeFailedForEnt, GAME_NAV_CHECKNODEFAILEDFORENT), - GEN_EXPORT(NAV_EntIsUnlockedDoor, GAME_NAV_ENTISUNLOCKEDDOOR), - GEN_EXPORT(NAV_EntIsDoor, GAME_NAV_ENTISDOOR), - GEN_EXPORT(NAV_EntIsBreakable, GAME_NAV_ENTISBREAKABLE), - GEN_EXPORT(NAV_EntIsRemovableUsable, GAME_NAV_ENTISREMOVABLEUSABLE), - GEN_EXPORT(NAV_FindCombatPointWaypoints, GAME_NAV_FINDCOMBATPOINTWAYPOINTS), - GEN_EXPORT(BG_GetItemIndexByTag, GAME_GETITEMINDEXBYTAG), -}; - - // wrapper syscall function that calls actual engine func from orig_import or orig_syscall // this is how QMM and plugins will call into the engine -static intptr_t JAMP_syscall(intptr_t cmd, ...) { +intptr_t JAMP_GameSupport::syscall(intptr_t cmd, ...) { QMM_GET_SYSCALL_ARGS(); #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_syscall({} {}) called\n", JAMP_EngMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_GameSupport::syscall({} {}) called\n", EngMsgName(cmd), cmd); #endif // store return value since we do some stuff after the function call is over @@ -805,7 +819,7 @@ static intptr_t JAMP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_syscall({} {}) returning {}\n", JAMP_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_GameSupport::syscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; @@ -814,11 +828,11 @@ static intptr_t JAMP_syscall(intptr_t cmd, ...) { // wrapper vmMain function that calls actual mod func from orig_export // this is how QMM and plugins will call into the mod -static intptr_t JAMP_vmMain(intptr_t cmd, ...) { +intptr_t JAMP_GameSupport::vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_vmMain({} {}) called\n", JAMP_ModMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_GameSupport::vmMain({} {}) called\n", ModMsgName(cmd), cmd); #endif // store return value since we do some stuff after the function call is over @@ -879,15 +893,15 @@ static intptr_t JAMP_vmMain(intptr_t cmd, ...) { } #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_vmMain({} {}) returning {}\n", JAMP_ModMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_GameSupport::vmMain({} {}) returning {}\n", ModMsgName(cmd), cmd, ret); #endif return ret; } -static void* JAMP_Entry(void* arg0, void* arg1, APIType engine) { - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_Entry({}, {}, {}) called\n", arg0, arg1, APIType_Name(engine)); +void* JAMP_GameSupport::Entry(void* arg0, void* arg1, APIType engine) { + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_GameSupport::Entry({}, {}, {}) called\n", arg0, arg1, APIType_Name(engine)); if (engine == QMM_API_GETMODULEAPI) { orig_apiversion = (intptr_t)arg0; @@ -899,13 +913,7 @@ static void* JAMP_Entry(void* arg0, void* arg1, APIType engine) { // fill in variables of our hooked import struct to pass to the mod - // pointer to wrapper vmMain function that calls either orig_vmMain or func in orig_export - g_gameinfo.pfnvmMain = JAMP_vmMain; - - // pointer to wrapper syscall function that calls either orig_syscall or func in orig_import - g_gameinfo.pfnsyscall = JAMP_syscall; - - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_Entry({}, {}, {}) returning {}\n", arg0, arg1, APIType_Name(engine), fmt::ptr(&qmm_export)); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_GameSupport::Entry({}, {}, {}) returning {}\n", arg0, arg1, APIType_Name(engine), fmt::ptr(&qmm_export)); // struct full of export lambdas to QMM's vmMain // this gets returned to the game engine, but we haven't loaded the mod yet. @@ -915,13 +923,7 @@ static void* JAMP_Entry(void* arg0, void* arg1, APIType engine) { // store original syscall from engine orig_syscall = (eng_syscall)arg0; - // pointer to wrapper vmMain function that calls either orig_vmMain or func in orig_export - g_gameinfo.pfnvmMain = JAMP_vmMain; - - // pointer to wrapper syscall function that calls either orig_syscall or func in orig_import - g_gameinfo.pfnsyscall = JAMP_syscall; - - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_Entry({}, {}, {}) returning\n", arg0, arg1, APIType_Name(engine)); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_GameSupport::Entry({}, {}, {}) returning\n", arg0, arg1, APIType_Name(engine)); return nullptr; } @@ -930,7 +932,7 @@ static void* JAMP_Entry(void* arg0, void* arg1, APIType engine) { } -static bool JAMP_ModLoad(void* entry, APIType modapi) { +bool JAMP_GameSupport::ModLoad(void* entry, APIType modapi) { if (modapi == QMM_API_GETMODULEAPI) { mod_GetGameAPI pfnGGA = (mod_GetGameAPI)entry; // api version gets passed before import pointer @@ -949,13 +951,13 @@ static bool JAMP_ModLoad(void* entry, APIType modapi) { } -static void JAMP_ModUnload() { +void JAMP_GameSupport::ModUnload() { orig_export = nullptr; orig_vmMain = nullptr; } -static const char* JAMP_EngMsgName(intptr_t cmd) { +const char* JAMP_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINT); GEN_CASE(G_ERROR); @@ -1296,7 +1298,7 @@ static const char* JAMP_EngMsgName(intptr_t cmd) { } -static const char* JAMP_ModMsgName(intptr_t cmd) { +const char* JAMP_GameSupport::ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_INIT); GEN_CASE(GAME_SHUTDOWN); diff --git a/src/game_jk2mp.cpp b/src/game_jk2mp.cpp index c9861f7..175ad37 100644 --- a/src/game_jk2mp.cpp +++ b/src/game_jk2mp.cpp @@ -22,17 +22,43 @@ Created By: #include "mod.h" #include "util.h" -GEN_GAME_QMM_MSGS(JK2MP); -GEN_GAME_FUNCS_QVM(JK2MP); +struct JK2MP_GameSupport : public GameSupport { + virtual const char* EngMsgName(intptr_t msg); + virtual const char* ModMsgName(intptr_t msg); + virtual bool AutoDetect(APIType engine_api); + virtual void* Entry(void* syscall, void*, APIType engine_api); + virtual bool ModLoad(void* entry, APIType mod_api); + virtual void ModUnload(); + + virtual intptr_t syscall(intptr_t, ...); + virtual intptr_t vmMain(intptr_t, ...); + + virtual const char* DefaultDLLName() { return "jk2mpgame" MOD_DLL; } + virtual const char* DefaultQVMName() { return "vm/jk2mpgame.qvm"; } + virtual const char* DefaultModDir() { return "base"; } + virtual const char* GameName() { return "Jedi Knight 2: Jedi Outcast (MP)"; } + virtual const char* GameCode() { return "JK2MP"; } + + virtual int QVMSyscall(uint8_t* membase, int cmd, int* args); + +private: + eng_syscall orig_syscall = nullptr; + mod_vmMain orig_vmMain = nullptr; + + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = GEN_GAME_QMM_MOD_MSGS(); +}; + +GEN_GAME_OBJ(JK2MP); // auto-detection logic for JK2MP -static bool JK2MP_AutoDetect(api_supportedgame* game, APIType engineapi) { +bool JK2MP_GameSupport::AutoDetect(APIType engineapi) { if (engineapi != QMM_API_DLLENTRY) return false; - if (!str_striequal(g_gameinfo.qmm_file, game->dllname)) + if (!str_striequal(g_gameinfo.qmm_file, DefaultDLLName())) return false; if (!str_stristr(g_gameinfo.exe_file, "jk2mp") && !str_stristr(g_gameinfo.exe_file, "jk2ded")) @@ -42,20 +68,14 @@ static bool JK2MP_AutoDetect(api_supportedgame* game, APIType engineapi) { } -// original syscall pointer that comes from the game engine -static eng_syscall orig_syscall = nullptr; - -// pointer to vmMain that comes from the mod -static mod_vmMain orig_vmMain = nullptr; - // wrapper syscall function that calls actual engine func in orig_syscall // this is how QMM and plugins will call into the engine -static intptr_t JK2MP_syscall(intptr_t cmd, ...) { +intptr_t JK2MP_GameSupport::syscall(intptr_t cmd, ...) { QMM_GET_SYSCALL_ARGS(); #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2MP_syscall({} {}) called\n", JK2MP_EngMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2MP_GameSupport::syscall({} {}) called\n", EngMsgName(cmd), cmd); #endif intptr_t ret = 0; @@ -88,7 +108,7 @@ static intptr_t JK2MP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2MP_syscall({} {}) returning {}\n", JK2MP_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2MP_GameSupport::syscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; @@ -97,11 +117,11 @@ static intptr_t JK2MP_syscall(intptr_t cmd, ...) { // wrapper vmMain function that calls actual mod func in orig_vmMain // this is how QMM and plugins will call into the mod -static intptr_t JK2MP_vmMain(intptr_t cmd, ...) { +intptr_t JK2MP_GameSupport::vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2MP_vmMain({} {}) called\n", JK2MP_ModMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2MP_GameSupport::vmMain({} {}) called\n", ModMsgName(cmd), cmd); #endif if (!orig_vmMain) @@ -120,32 +140,26 @@ static intptr_t JK2MP_vmMain(intptr_t cmd, ...) { } #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2MP_vmMain({} {}) returning {}\n", JK2MP_ModMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2MP_GameSupport::vmMain({} {}) returning {}\n", ModMsgName(cmd), cmd, ret); #endif return ret; } -static void* JK2MP_Entry(void* syscall, void*, APIType) { - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2MP_Entry({}) called\n", syscall); +void* JK2MP_GameSupport::Entry(void* syscall, void*, APIType) { + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2MP_GameSupport::Entry({}) called\n", syscall); // store original syscall from engine orig_syscall = (eng_syscall)syscall; - // pointer to wrapper vmMain function that calls actual mod vmMain func orig_vmMain - g_gameinfo.pfnvmMain = JK2MP_vmMain; - - // pointer to wrapper syscall function that calls actual engine syscall func - g_gameinfo.pfnsyscall = JK2MP_syscall; - - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2MP_Entry({}) returning\n", syscall); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2MP_GameSupport::Entry({}) returning\n", syscall); return nullptr; } -static bool JK2MP_ModLoad(void* entry, APIType modapi) { +bool JK2MP_GameSupport::ModLoad(void* entry, APIType modapi) { if (modapi != QMM_API_DLLENTRY && modapi != QMM_API_QVM) return false; @@ -155,12 +169,12 @@ static bool JK2MP_ModLoad(void* entry, APIType modapi) { } -static void JK2MP_ModUnload() { +void JK2MP_GameSupport::ModUnload() { orig_vmMain = nullptr; } -static const char* JK2MP_EngMsgName(intptr_t cmd) { +const char* JK2MP_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINT); GEN_CASE(G_ERROR); @@ -399,7 +413,7 @@ static const char* JK2MP_EngMsgName(intptr_t cmd) { } -static const char* JK2MP_ModMsgName(intptr_t cmd) { +const char* JK2MP_GameSupport::ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_INIT); GEN_CASE(GAME_SHUTDOWN); @@ -427,9 +441,9 @@ static const char* JK2MP_ModMsgName(intptr_t cmd) { // vec3_t are arrays, so convert them as pointers // do NOT convert the "ghoul" void pointers, treat them as plain ints // for double pointers (gentity_t**, vec3_t*, void**), convert them once with vmptr() -static int JK2MP_QVMSyscall(uint8_t* membase, int cmd, int* args) { +int JK2MP_GameSupport::QVMSyscall(uint8_t* membase, int cmd, int* args) { #ifdef _DEBUG - LOG(QMM_LOG_TRACE, "QMM") << fmt::format("JK2MP_QVMSyscall({} {}) called\n", JK2MP_EngMsgName(cmd), cmd); + LOG(QMM_LOG_TRACE, "QMM") << fmt::format("JK2MP_GameSupport::QVMSyscall({} {}) called\n", EngMsgName(cmd), cmd); #endif int ret = 0; @@ -754,7 +768,7 @@ static int JK2MP_QVMSyscall(uint8_t* membase, int cmd, int* args) { } #ifdef _DEBUG - LOG(QMM_LOG_TRACE, "QMM") << fmt::format("JK2MP_QVMSyscall({} {}) returning {}\n", JK2MP_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_TRACE, "QMM") << fmt::format("JK2MP_GameSupport::QVMSyscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; diff --git a/src/game_q3a.cpp b/src/game_q3a.cpp index 8b22484..40be1f1 100644 --- a/src/game_q3a.cpp +++ b/src/game_q3a.cpp @@ -22,9 +22,8 @@ Created By: #include "mod.h" #include "util.h" -struct Q3A_GameSupport : GameSupport { - virtual int QMMEngMsg(int msg); - virtual int QMMModMsg(int msg); + +struct Q3A_GameSupport : public GameSupport { virtual const char* EngMsgName(intptr_t msg); virtual const char* ModMsgName(intptr_t msg); virtual bool AutoDetect(APIType engine_api); @@ -37,8 +36,8 @@ struct Q3A_GameSupport : GameSupport { virtual const char* DefaultDLLName() { return "qagame" MOD_DLL; } virtual const char* DefaultQVMName() { return "vm/qagame.qvm"; } - virtual const char* DefaultModDir() { return "baseq3"; }; - virtual const char* GameName() { return "Quake 3 Arena"; }; + virtual const char* DefaultModDir() { return "baseq3"; } + virtual const char* GameName() { return "Quake 3 Arena"; } virtual const char* GameCode() { return "Q3A"; } virtual int QVMSyscall(uint8_t* membase, int cmd, int* args); @@ -47,18 +46,19 @@ struct Q3A_GameSupport : GameSupport { eng_syscall orig_syscall = nullptr; mod_vmMain orig_vmMain = nullptr; - GEN_GAME_QMM_ENG_MSGS(); - GEN_GAME_QMM_MOD_MSGS(); + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = GEN_GAME_QMM_MOD_MSGS(); }; GEN_GAME_OBJ(Q3A); + // auto-detection logic for Q3A bool Q3A_GameSupport::AutoDetect(APIType engine_api) { if (engine_api != QMM_API_DLLENTRY) return false; - if (!str_striequal(g_gameinfo.qmm_file, this->DefaultDLLName())) + if (!str_striequal(g_gameinfo.qmm_file, DefaultDLLName())) return false; if (!str_stristr(g_gameinfo.exe_file, "quake3") && !str_stristr(g_gameinfo.exe_file, "q3ded")) @@ -173,16 +173,6 @@ void Q3A_GameSupport::ModUnload() { } -int Q3A_GameSupport::QMMEngMsg(int msg) { - return this->qmm_eng_msgs[msg]; -} - - -int Q3A_GameSupport::QMMModMsg(int msg) { - return this->qmm_mod_msgs[msg]; -} - - const char* Q3A_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINT); diff --git a/src/game_rtcwmp.cpp b/src/game_rtcwmp.cpp index 5f441f6..965d410 100644 --- a/src/game_rtcwmp.cpp +++ b/src/game_rtcwmp.cpp @@ -21,17 +21,42 @@ Created By: #include "main.h" #include "util.h" -GEN_GAME_QMM_MSGS(RTCWMP); +struct RTCWMP_GameSupport : public GameSupport { + virtual const char* EngMsgName(intptr_t msg); + virtual const char* ModMsgName(intptr_t msg); + virtual bool AutoDetect(APIType engine_api); + virtual void* Entry(void* syscall, void*, APIType engine_api); + virtual bool ModLoad(void* entry, APIType mod_api); + virtual void ModUnload(); -GEN_GAME_FUNCS_QVM(RTCWMP); + virtual intptr_t syscall(intptr_t, ...); + virtual intptr_t vmMain(intptr_t, ...); + + virtual const char* DefaultDLLName() { return "qagame" MP_DLL X64_DLL; } + virtual const char* DefaultQVMName() { return "vm/qagame.mp.qvm"; } + virtual const char* DefaultModDir() { return "Main"; } + virtual const char* GameName() { return "Return to Castle Wolfenstein (MP)"; } + virtual const char* GameCode() { return "RTCWMP"; } + + virtual int QVMSyscall(uint8_t* membase, int cmd, int* args); + +private: + eng_syscall orig_syscall = nullptr; + mod_vmMain orig_vmMain = nullptr; + + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = GEN_GAME_QMM_MOD_MSGS(); +}; + +GEN_GAME_OBJ(RTCWMP); // auto-detection logic for RTCWMP -static bool RTCWMP_AutoDetect(api_supportedgame* game, APIType engineapi) { +bool RTCWMP_GameSupport::AutoDetect(APIType engineapi) { if (engineapi != QMM_API_DLLENTRY) return false; - if (!str_striequal(g_gameinfo.qmm_file, game->dllname)) + if (!str_striequal(g_gameinfo.qmm_file, DefaultDLLName())) return false; if (!str_stristr(g_gameinfo.exe_file, "wolfmp") && !str_stristr(g_gameinfo.exe_file, "wolfded")) @@ -41,20 +66,14 @@ static bool RTCWMP_AutoDetect(api_supportedgame* game, APIType engineapi) { } -// original syscall pointer that comes from the game engine -static eng_syscall orig_syscall = nullptr; - -// pointer to vmMain that comes from the mod -static mod_vmMain orig_vmMain = nullptr; - // wrapper syscall function that calls actual engine func in orig_syscall // this is how QMM and plugins will call into the engine -static intptr_t RTCWMP_syscall(intptr_t cmd, ...) { +intptr_t RTCWMP_GameSupport::syscall(intptr_t cmd, ...) { QMM_GET_SYSCALL_ARGS(); #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWMP_syscall({} {}) called\n", RTCWMP_EngMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWMP_GameSupport::syscall({} {}) called\n", EngMsgName(cmd), cmd); #endif intptr_t ret = 0; @@ -87,7 +106,7 @@ static intptr_t RTCWMP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWMP_syscall({} {}) returning {}\n", RTCWMP_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWMP_GameSupport::syscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; @@ -96,11 +115,11 @@ static intptr_t RTCWMP_syscall(intptr_t cmd, ...) { // wrapper vmMain function that calls actual mod func in orig_vmMain // this is how QMM and plugins will call into the mod -static intptr_t RTCWMP_vmMain(intptr_t cmd, ...) { +intptr_t RTCWMP_GameSupport::vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWMP_vmMain({} {}) called\n", RTCWMP_ModMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWMP_GameSupport::vmMain({} {}) called\n", ModMsgName(cmd), cmd); #endif if (!orig_vmMain) @@ -113,32 +132,26 @@ static intptr_t RTCWMP_vmMain(intptr_t cmd, ...) { ret = orig_vmMain(cmd, QMM_PUT_VMMAIN_ARGS()); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWMP_vmMain({} {}) returning {}\n", RTCWMP_ModMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWMP_GameSupport::vmMain({} {}) returning {}\n", ModMsgName(cmd), cmd, ret); #endif return ret; } -static void* RTCWMP_Entry(void* syscall, void*, APIType) { - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWMP_Entry({}) called\n", syscall); +void* RTCWMP_GameSupport::Entry(void* syscall, void*, APIType) { + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWMP_GameSupport::Entry({}) called\n", syscall); // store original syscall from engine orig_syscall = (eng_syscall)syscall; - // pointer to wrapper vmMain function that calls actual mod vmMain func orig_vmMain - g_gameinfo.pfnvmMain = RTCWMP_vmMain; - - // pointer to wrapper syscall function that calls actual engine syscall func - g_gameinfo.pfnsyscall = RTCWMP_syscall; - - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWMP_Entry({}) returning\n", syscall); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWMP_GameSupport::Entry({}) returning\n", syscall); return nullptr; } -static bool RTCWMP_ModLoad(void* entry, APIType modapi) { +bool RTCWMP_GameSupport::ModLoad(void* entry, APIType modapi) { if (modapi != QMM_API_DLLENTRY && modapi != QMM_API_QVM) return false; @@ -148,12 +161,12 @@ static bool RTCWMP_ModLoad(void* entry, APIType modapi) { } -static void RTCWMP_ModUnload() { +void RTCWMP_GameSupport::ModUnload() { orig_vmMain = nullptr; } -static const char* RTCWMP_EngMsgName(intptr_t cmd) { +const char* RTCWMP_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINT); GEN_CASE(G_ERROR); @@ -373,7 +386,7 @@ static const char* RTCWMP_EngMsgName(intptr_t cmd) { } -static const char* RTCWMP_ModMsgName(intptr_t cmd) { +const char* RTCWMP_GameSupport::ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_INIT); GEN_CASE(GAME_SHUTDOWN); @@ -402,9 +415,9 @@ static const char* RTCWMP_ModMsgName(intptr_t cmd) { */ // vec3_t are arrays, so convert them as pointers // for double pointers (gentity_t** and vec3_t*), convert them once with vmptr() -static int RTCWMP_QVMSyscall(uint8_t* membase, int cmd, int* args) { +int RTCWMP_GameSupport::QVMSyscall(uint8_t* membase, int cmd, int* args) { #ifdef _DEBUG - LOG(QMM_LOG_TRACE, "QMM") << fmt::format("RTCWMP_QVMSyscall({} {}) called\n", RTCWMP_EngMsgName(cmd), cmd); + LOG(QMM_LOG_TRACE, "QMM") << fmt::format("RTCWMP_GameSupport::QVMSyscall({} {}) called\n", EngMsgName(cmd), cmd); #endif intptr_t ret = 0; @@ -701,7 +714,7 @@ static int RTCWMP_QVMSyscall(uint8_t* membase, int cmd, int* args) { } #ifdef _DEBUG - LOG(QMM_LOG_TRACE, "QMM") << fmt::format("RTCWMP_QVMSyscall({} {}) returning {}\n", RTCWMP_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_TRACE, "QMM") << fmt::format("RTCWMP_GameSupport::QVMSyscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; diff --git a/src/game_rtcwsp.cpp b/src/game_rtcwsp.cpp index d335599..88681d6 100644 --- a/src/game_rtcwsp.cpp +++ b/src/game_rtcwsp.cpp @@ -22,56 +22,70 @@ Created By: #include "main.h" #include "util.h" -GEN_GAME_QMM_MSGS(RTCWSP); +struct RTCWSP_GameSupport : public GameSupport { + virtual const char* EngMsgName(intptr_t msg); + virtual const char* ModMsgName(intptr_t msg); + virtual bool AutoDetect(APIType engine_api); + virtual void* Entry(void* syscall, void*, APIType engine_api); + virtual bool ModLoad(void* entry, APIType mod_api); + virtual void ModUnload(); -GEN_GAME_FUNCS(RTCWSP); + virtual intptr_t syscall(intptr_t, ...); + virtual intptr_t vmMain(intptr_t, ...); + + virtual const char* DefaultDLLName() { return is_iortcw ? iortcw_dllname : official_dllname; } + virtual const char* DefaultModDir() { /* iortcw runs single player out of main dir */ return is_iortcw ? "main" : "."; } + virtual const char* GameName() { return "Return to Castle Wolfenstein (SP)"; } + virtual const char* GameCode() { return "RTCWSP"; } + +private: + eng_syscall orig_syscall = nullptr; + mod_vmMain orig_vmMain = nullptr; + + // was QMM loaded by the ioRTCW engine? + bool is_iortcw = false; + + const char* const iortcw_dllname = "qagame" SP_DLL X64_DLL; + const char* const official_dllname = "qagame" MOD_DLL; + + // store all the mallocs for G_ALLOC if this isn't running in ioRTCW engine + std::vector alloc_list; + + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = GEN_GAME_QMM_MOD_MSGS(); +}; + +GEN_GAME_OBJ(RTCWSP); -static bool is_iortcw = false; // auto-detection logic for RTCWSP -static bool RTCWSP_AutoDetect(api_supportedgame* game, APIType engineapi) { +bool RTCWSP_GameSupport::AutoDetect(APIType engineapi) { if (engineapi != QMM_API_DLLENTRY) return false; - // game->dllname holds the iortcw filenames, but we also need to check for official engine dll name - if (!str_striequal(g_gameinfo.qmm_file, game->dllname) && !str_striequal(g_gameinfo.qmm_file, "qagame" MOD_DLL)) + // check for either dll name + if (!str_striequal(g_gameinfo.qmm_file, iortcw_dllname) && !str_striequal(g_gameinfo.qmm_file, official_dllname)) return false; if (!str_stristr(g_gameinfo.exe_file, "wolfsp")) return false; // loaded in iortcw? - if (str_striequal(g_gameinfo.qmm_file, game->dllname)) { + if (str_striequal(g_gameinfo.qmm_file, iortcw_dllname)) is_iortcw = true; - // iortcw runs single player out of main dir - game->moddir = "main"; - } - - // set the default dllname to whatever QMM loaded as to load the correct mod DLL - game->dllname = g_gameinfo.qmm_file.c_str(); return true; } -// original syscall pointer that comes from the game engine -static eng_syscall orig_syscall = nullptr; - -// pointer to vmMain that comes from the mod -static mod_vmMain orig_vmMain = nullptr; - -// store all the mallocs for G_ALLOC if this isn't running in ioRTCW engine -static std::vector alloc_list; - - // wrapper syscall function that calls actual engine func in orig_syscall // this is how QMM and plugins will call into the engine -static intptr_t RTCWSP_syscall(intptr_t cmd, ...) { +intptr_t RTCWSP_GameSupport::syscall(intptr_t cmd, ...) { QMM_GET_SYSCALL_ARGS(); #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWSP_syscall({} {}) called\n", RTCWSP_EngMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWSP_GameSupport::syscall({} {}) called\n", EngMsgName(cmd), cmd); #endif intptr_t ret = 0; @@ -116,7 +130,7 @@ static intptr_t RTCWSP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWSP_syscall({} {}) returning {}\n", RTCWSP_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWSP_GameSupport::syscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; @@ -125,11 +139,11 @@ static intptr_t RTCWSP_syscall(intptr_t cmd, ...) { // wrapper vmMain function that calls actual mod func in orig_vmMain // this is how QMM and plugins will call into the mod -static intptr_t RTCWSP_vmMain(intptr_t cmd, ...) { +intptr_t RTCWSP_GameSupport::vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWSP_vmMain({} {}) called\n", RTCWSP_ModMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWSP_GameSupport::vmMain({} {}) called\n", ModMsgName(cmd), cmd); #endif if (!orig_vmMain) @@ -142,32 +156,26 @@ static intptr_t RTCWSP_vmMain(intptr_t cmd, ...) { ret = orig_vmMain(cmd, QMM_PUT_VMMAIN_ARGS()); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWSP_vmMain({} {}) returning {}\n", RTCWSP_ModMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWSP_GameSupport::vmMain({} {}) returning {}\n", ModMsgName(cmd), cmd, ret); #endif return ret; } -static void* RTCWSP_Entry(void* syscall, void*, APIType) { - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWSP_Entry({}) called\n", syscall); +void* RTCWSP_GameSupport::Entry(void* syscall, void*, APIType) { + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWSP_GameSupport::Entry({}) called\n", syscall); // store original syscall from engine orig_syscall = (eng_syscall)syscall; - // pointer to wrapper vmMain function that calls actual mod vmMain func orig_vmMain - g_gameinfo.pfnvmMain = RTCWSP_vmMain; - - // pointer to wrapper syscall function that calls actual engine syscall func - g_gameinfo.pfnsyscall = RTCWSP_syscall; - - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWSP_Entry({}) returning\n", syscall); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("RTCWSP_GameSupport::Entry({}) returning\n", syscall); return nullptr; } -static bool RTCWSP_ModLoad(void* entry, APIType modapi) { +bool RTCWSP_GameSupport::ModLoad(void* entry, APIType modapi) { if (modapi != QMM_API_DLLENTRY) return false; @@ -177,7 +185,7 @@ static bool RTCWSP_ModLoad(void* entry, APIType modapi) { } -static void RTCWSP_ModUnload() { +void RTCWSP_GameSupport::ModUnload() { orig_vmMain = nullptr; // free the G_ALLOC list @@ -188,7 +196,7 @@ static void RTCWSP_ModUnload() { } -static const char* RTCWSP_EngMsgName(intptr_t cmd) { +const char* RTCWSP_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINT); GEN_CASE(G_ERROR); @@ -400,7 +408,7 @@ static const char* RTCWSP_EngMsgName(intptr_t cmd) { } -static const char* RTCWSP_ModMsgName(intptr_t cmd) { +const char* RTCWSP_GameSupport::ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_INIT); GEN_CASE(GAME_SHUTDOWN); diff --git a/src/game_sof2mp.cpp b/src/game_sof2mp.cpp index bf449f8..e17e477 100644 --- a/src/game_sof2mp.cpp +++ b/src/game_sof2mp.cpp @@ -23,17 +23,42 @@ Created By: #include "mod.h" #include "util.h" -GEN_GAME_QMM_MSGS(SOF2MP); +struct SOF2MP_GameSupport : public GameSupport { + virtual const char* EngMsgName(intptr_t msg); + virtual const char* ModMsgName(intptr_t msg); + virtual bool AutoDetect(APIType engine_api); + virtual void* Entry(void* syscall, void*, APIType engine_api); + virtual bool ModLoad(void* entry, APIType mod_api); + virtual void ModUnload(); -GEN_GAME_FUNCS_QVM(SOF2MP); + virtual intptr_t syscall(intptr_t, ...); + virtual intptr_t vmMain(intptr_t, ...); + + virtual const char* DefaultDLLName() { return "sof2mp_game" MOD_DLL; } + virtual const char* DefaultQVMName() { return "vm/sof2mp_game.qvm"; } + virtual const char* DefaultModDir() { return "base/mp"; } + virtual const char* GameName() { return "Soldier of Fortune 2: Double Helix (MP)"; } + virtual const char* GameCode() { return "SOF2MP"; } + + virtual int QVMSyscall(uint8_t* membase, int cmd, int* args); + +private: + eng_syscall orig_syscall = nullptr; + mod_vmMain orig_vmMain = nullptr; + + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = GEN_GAME_QMM_MOD_MSGS(); +}; + +GEN_GAME_OBJ(SOF2MP); // auto-detection logic for SOF2MP -static bool SOF2MP_AutoDetect(api_supportedgame* game, APIType engineapi) { +bool SOF2MP_GameSupport::AutoDetect(APIType engineapi) { if (engineapi != QMM_API_DLLENTRY) return false; - if (!str_striequal(g_gameinfo.qmm_file, game->dllname)) + if (!str_striequal(g_gameinfo.qmm_file, DefaultDLLName())) return false; if (!str_stristr(g_gameinfo.exe_file, "sof2mp") && !str_stristr(g_gameinfo.exe_file, "sof2ded")) @@ -43,20 +68,14 @@ static bool SOF2MP_AutoDetect(api_supportedgame* game, APIType engineapi) { } -// original syscall pointer that comes from the game engine -static eng_syscall orig_syscall = nullptr; - -// pointer to vmMain that comes from the mod -static mod_vmMain orig_vmMain = nullptr; - // wrapper syscall function that calls actual engine func in orig_syscall // this is how QMM and plugins will call into the engine -static intptr_t SOF2MP_syscall(intptr_t cmd, ...) { +intptr_t SOF2MP_GameSupport::syscall(intptr_t cmd, ...) { QMM_GET_SYSCALL_ARGS(); #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2MP_syscall({} {}) called\n", SOF2MP_EngMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2MP_GameSupport::syscall({} {}) called\n", EngMsgName(cmd), cmd); #endif intptr_t ret = 0; @@ -90,7 +109,7 @@ static intptr_t SOF2MP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2MP_syscall({} {}) returning {}\n", SOF2MP_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2MP_GameSupport::syscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; @@ -99,11 +118,11 @@ static intptr_t SOF2MP_syscall(intptr_t cmd, ...) { // wrapper vmMain function that calls actual mod func in orig_vmMain // this is how QMM and plugins will call into the mod -static intptr_t SOF2MP_vmMain(intptr_t cmd, ...) { +intptr_t SOF2MP_GameSupport::vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2MP_vmMain({} {}) called\n", SOF2MP_ModMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2MP_GameSupport::vmMain({} {}) called\n", ModMsgName(cmd), cmd); #endif if (!orig_vmMain) @@ -156,33 +175,27 @@ static intptr_t SOF2MP_vmMain(intptr_t cmd, ...) { } #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2MP_vmMain({} {}) returning {}\n", SOF2MP_ModMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2MP_GameSupport::vmMain({} {}) returning {}\n", ModMsgName(cmd), cmd, ret); #endif return ret; } -static void* SOF2MP_Entry(void* syscall, void*, APIType) { - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2MP_Entry({}) called\n", syscall); +void* SOF2MP_GameSupport::Entry(void* syscall, void*, APIType) { + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2MP_GameSupport::Entry({}) called\n", syscall); // store original syscall from engine orig_syscall = (eng_syscall)syscall; - // pointer to wrapper vmMain function that calls actual mod vmMain func orig_vmMain - g_gameinfo.pfnvmMain = SOF2MP_vmMain; - - // pointer to wrapper syscall function that calls actual engine syscall func - g_gameinfo.pfnsyscall = SOF2MP_syscall; - - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2MP_Entry({}) returning\n", syscall); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2MP_GameSupport::Entry({}) returning\n", syscall); return nullptr; } // get mod's vmMain function pointer from mod.cpp::mod_load -static bool SOF2MP_ModLoad(void* entry, APIType modapi) { +bool SOF2MP_GameSupport::ModLoad(void* entry, APIType modapi) { if (modapi != QMM_API_DLLENTRY && modapi != QMM_API_QVM) return false; @@ -196,12 +209,12 @@ static bool SOF2MP_ModLoad(void* entry, APIType modapi) { } -static void SOF2MP_ModUnload() { +void SOF2MP_GameSupport::ModUnload() { orig_vmMain = nullptr; } -static const char* SOF2MP_EngMsgName(intptr_t cmd) { +const char* SOF2MP_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINT); GEN_CASE(G_ERROR); @@ -474,7 +487,7 @@ static const char* SOF2MP_EngMsgName(intptr_t cmd) { } -static const char* SOF2MP_ModMsgName(intptr_t cmd) { +const char* SOF2MP_GameSupport::ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_INIT); GEN_CASE(GAME_SHUTDOWN); @@ -506,9 +519,9 @@ static const char* SOF2MP_ModMsgName(intptr_t cmd) { // do NOT convert the "ghoul" void pointers, treat them as plain ints // TGPValue, TGPGroup, and TGenericParser2 are void*, but treat them as plain ints // for double pointers (gentity_t**, vec3_t*, void**), convert them once with vmptr() -static int SOF2MP_QVMSyscall(uint8_t* membase, int cmd, int* args) { +int SOF2MP_GameSupport::QVMSyscall(uint8_t* membase, int cmd, int* args) { #ifdef _DEBUG - LOG(QMM_LOG_TRACE, "QMM") << fmt::format("SOF2MP_QVMSyscall({} {}) called\n", SOF2MP_EngMsgName(cmd), cmd); + LOG(QMM_LOG_TRACE, "QMM") << fmt::format("SOF2MP_GameSupport::QVMSyscall({} {}) called\n", EngMsgName(cmd), cmd); #endif int ret = 0; @@ -880,7 +893,7 @@ static int SOF2MP_QVMSyscall(uint8_t* membase, int cmd, int* args) { } #ifdef _DEBUG - LOG(QMM_LOG_TRACE, "QMM") << fmt::format("SOF2MP_QVMSyscall({} {}) returning {}\n", SOF2MP_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_TRACE, "QMM") << fmt::format("SOF2MP_GameSupport::QVMSyscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; diff --git a/src/game_stvoyhm.cpp b/src/game_stvoyhm.cpp index 2c3f4ff..a68aa5b 100644 --- a/src/game_stvoyhm.cpp +++ b/src/game_stvoyhm.cpp @@ -22,17 +22,42 @@ Created By: #include "mod.h" #include "util.h" -GEN_GAME_QMM_MSGS(STVOYHM); +struct STVOYHM_GameSupport : public GameSupport { + virtual const char* EngMsgName(intptr_t msg); + virtual const char* ModMsgName(intptr_t msg); + virtual bool AutoDetect(APIType engine_api); + virtual void* Entry(void* syscall, void*, APIType engine_api); + virtual bool ModLoad(void* entry, APIType mod_api); + virtual void ModUnload(); -GEN_GAME_FUNCS_QVM(STVOYHM); + virtual intptr_t syscall(intptr_t, ...); + virtual intptr_t vmMain(intptr_t, ...); + + virtual const char* DefaultDLLName() { return "qagame" MOD_DLL; } + virtual const char* DefaultQVMName() { return "vm/qagame.qvm"; } + virtual const char* DefaultModDir() { return "baseef"; } + virtual const char* GameName() { return "Star Trek Voyager: Elite Force (Holomatch)"; } + virtual const char* GameCode() { return "STVOYHM"; } + + virtual int QVMSyscall(uint8_t* membase, int cmd, int* args); + +private: + eng_syscall orig_syscall = nullptr; + mod_vmMain orig_vmMain = nullptr; + + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = GEN_GAME_QMM_MOD_MSGS(); +}; + +GEN_GAME_OBJ(STVOYHM); // auto-detection logic for Q3A -static bool STVOYHM_AutoDetect(api_supportedgame* game, APIType engineapi) { +bool STVOYHM_GameSupport::AutoDetect(APIType engineapi) { if (engineapi != QMM_API_DLLENTRY) return false; - if (!str_striequal(g_gameinfo.qmm_file, game->dllname)) + if (!str_striequal(g_gameinfo.qmm_file, DefaultDLLName())) return false; if (!str_stristr(g_gameinfo.exe_file, "stvoyhm")) @@ -42,20 +67,14 @@ static bool STVOYHM_AutoDetect(api_supportedgame* game, APIType engineapi) { } -// original syscall pointer that comes from the game engine -static eng_syscall orig_syscall = nullptr; - -// pointer to vmMain that comes from the mod -static mod_vmMain orig_vmMain = nullptr; - // wrapper syscall function that calls actual engine func in orig_syscall // this is how QMM and plugins will call into the engine -static intptr_t STVOYHM_syscall(intptr_t cmd, ...) { +intptr_t STVOYHM_GameSupport::syscall(intptr_t cmd, ...) { QMM_GET_SYSCALL_ARGS(); #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYHM_syscall({} {}) called\n", STVOYHM_EngMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYHM_GameSupport::syscall({} {}) called\n", EngMsgName(cmd), cmd); #endif intptr_t ret = 0; @@ -88,7 +107,7 @@ static intptr_t STVOYHM_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYHM_syscall({} {}) returning {}\n", STVOYHM_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYHM_GameSupport::syscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; @@ -97,11 +116,11 @@ static intptr_t STVOYHM_syscall(intptr_t cmd, ...) { // wrapper vmMain function that calls actual mod func in orig_vmMain // this is how QMM and plugins will call into the mod -static intptr_t STVOYHM_vmMain(intptr_t cmd, ...) { +intptr_t STVOYHM_GameSupport::vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYHM_vmMain({} {}) called\n", STVOYHM_ModMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYHM_GameSupport::vmMain({} {}) called\n", ModMsgName(cmd), cmd); #endif if (!orig_vmMain) @@ -120,32 +139,26 @@ static intptr_t STVOYHM_vmMain(intptr_t cmd, ...) { } #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYHM_vmMain({} {}) returning {}\n", STVOYHM_ModMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYHM_GameSupport::vmMain({} {}) returning {}\n", ModMsgName(cmd), cmd, ret); #endif return ret; } -static void* STVOYHM_Entry(void* syscall, void*, APIType) { - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYHM_Entry({}) called\n", syscall); +void* STVOYHM_GameSupport::Entry(void* syscall, void*, APIType) { + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYHM_GameSupport::Entry({}) called\n", syscall); // store original syscall from engine orig_syscall = (eng_syscall)syscall; - // pointer to wrapper vmMain function that calls actual mod vmMain func orig_vmMain - g_gameinfo.pfnvmMain = STVOYHM_vmMain; - - // pointer to wrapper syscall function that calls actual engine syscall func - g_gameinfo.pfnsyscall = STVOYHM_syscall; - - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYHM_Entry({}) returning\n", syscall); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYHM_GameSupport::Entry({}) returning\n", syscall); return nullptr; } -static bool STVOYHM_ModLoad(void* entry, APIType modapi) { +bool STVOYHM_GameSupport::ModLoad(void* entry, APIType modapi) { if (modapi != QMM_API_DLLENTRY && modapi != QMM_API_QVM) return false; @@ -155,12 +168,12 @@ static bool STVOYHM_ModLoad(void* entry, APIType modapi) { } -static void STVOYHM_ModUnload() { +void STVOYHM_GameSupport::ModUnload() { orig_vmMain = nullptr; } -static const char* STVOYHM_EngMsgName(intptr_t cmd) { +const char* STVOYHM_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINT); GEN_CASE(G_ERROR); @@ -360,7 +373,7 @@ static const char* STVOYHM_EngMsgName(intptr_t cmd) { } -static const char* STVOYHM_ModMsgName(intptr_t cmd) { +const char* STVOYHM_GameSupport::ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_INIT); GEN_CASE(GAME_SHUTDOWN); @@ -386,9 +399,9 @@ static const char* STVOYHM_ModMsgName(intptr_t cmd) { */ // vec3_t are arrays, so convert them as pointers // for double pointers (gentity_t** and vec3_t*), convert them once with vmptr() -static int STVOYHM_QVMSyscall(uint8_t* membase, int cmd, int* args) { +int STVOYHM_GameSupport::QVMSyscall(uint8_t* membase, int cmd, int* args) { #ifdef _DEBUG - LOG(QMM_LOG_TRACE, "QMM") << fmt::format("STVOYHM_QVMSyscall({} {}) called\n", STVOYHM_EngMsgName(cmd), cmd); + LOG(QMM_LOG_TRACE, "QMM") << fmt::format("STVOYHM_GameSupport::QVMSyscall({} {}) called\n", EngMsgName(cmd), cmd); #endif int ret = 0; @@ -653,7 +666,7 @@ static int STVOYHM_QVMSyscall(uint8_t* membase, int cmd, int* args) { } #ifdef _DEBUG - LOG(QMM_LOG_TRACE, "QMM") << fmt::format("STVOYHM_QVMSyscall({} {}) returning {}\n", STVOYHM_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_TRACE, "QMM") << fmt::format("STVOYHM_GameSupport::QVMSyscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; diff --git a/src/game_wet.cpp b/src/game_wet.cpp index e069ae3..dbfc102 100644 --- a/src/game_wet.cpp +++ b/src/game_wet.cpp @@ -21,17 +21,39 @@ Created By: #include "main.h" #include "util.h" -GEN_GAME_QMM_MSGS(WET); +struct WET_GameSupport : public GameSupport { + virtual const char* EngMsgName(intptr_t msg); + virtual const char* ModMsgName(intptr_t msg); + virtual bool AutoDetect(APIType engine_api); + virtual void* Entry(void* syscall, void*, APIType engine_api); + virtual bool ModLoad(void* entry, APIType mod_api); + virtual void ModUnload(); -GEN_GAME_FUNCS(WET); + virtual intptr_t syscall(intptr_t, ...); + virtual intptr_t vmMain(intptr_t, ...); + + virtual const char* DefaultDLLName() { return "qagame" MP_DLL X64_DLL; } + virtual const char* DefaultModDir() { return "etmain"; } + virtual const char* GameName() { return "Wolfenstein: Enemy Territory"; } + virtual const char* GameCode() { return "WET"; } + +private: + eng_syscall orig_syscall = nullptr; + mod_vmMain orig_vmMain = nullptr; + + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = GEN_GAME_QMM_MOD_MSGS(); +}; + +GEN_GAME_OBJ(WET); // auto-detection logic for WET -static bool WET_AutoDetect(api_supportedgame* game, APIType engineapi) { +bool WET_GameSupport::AutoDetect(APIType engineapi) { if (engineapi != QMM_API_DLLENTRY) return false; - if (!str_striequal(g_gameinfo.qmm_file, game->dllname)) + if (!str_striequal(g_gameinfo.qmm_file, DefaultDLLName())) return false; if (!str_stristr(g_gameinfo.exe_file, "et")) @@ -41,20 +63,14 @@ static bool WET_AutoDetect(api_supportedgame* game, APIType engineapi) { } -// original syscall pointer that comes from the game engine -static eng_syscall orig_syscall = nullptr; - -// pointer to vmMain that comes from the mod -static mod_vmMain orig_vmMain = nullptr; - // wrapper syscall function that calls actual engine func in orig_syscall // this is how QMM and plugins will call into the engine -static intptr_t WET_syscall(intptr_t cmd, ...) { +intptr_t WET_GameSupport::syscall(intptr_t cmd, ...) { QMM_GET_SYSCALL_ARGS(); #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("WET_syscall({} {}) called\n", WET_EngMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("WET_GameSupport::syscall({} {}) called\n", EngMsgName(cmd), cmd); #endif intptr_t ret = 0; @@ -87,7 +103,7 @@ static intptr_t WET_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("WET_syscall({} {}) returning {}\n", WET_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("WET_GameSupport::syscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; @@ -96,11 +112,11 @@ static intptr_t WET_syscall(intptr_t cmd, ...) { // wrapper vmMain function that calls actual mod func in orig_vmMain // this is how QMM and plugins will call into the mod -static intptr_t WET_vmMain(intptr_t cmd, ...) { +intptr_t WET_GameSupport::vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("WET_vmMain({} {}) called\n", WET_ModMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("WET_GameSupport::vmMain({} {}) called\n", ModMsgName(cmd), cmd); #endif if (!orig_vmMain) @@ -113,32 +129,26 @@ static intptr_t WET_vmMain(intptr_t cmd, ...) { ret = orig_vmMain(cmd, QMM_PUT_VMMAIN_ARGS()); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("WET_vmMain({} {}) returning {}\n", WET_ModMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("WET_GameSupport::vmMain({} {}) returning {}\n", ModMsgName(cmd), cmd, ret); #endif return ret; } -static void* WET_Entry(void* syscall, void*, APIType) { - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("WET_Entry({}) called\n", syscall); +void* WET_GameSupport::Entry(void* syscall, void*, APIType) { + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("WET_GameSupport::Entry({}) called\n", syscall); // store original syscall from engine orig_syscall = (eng_syscall)syscall; - // pointer to wrapper vmMain function that calls actual mod vmMain func orig_vmMain - g_gameinfo.pfnvmMain = WET_vmMain; - - // pointer to wrapper syscall function that calls actual engine syscall func - g_gameinfo.pfnsyscall = WET_syscall; - - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("WET_Entry({}) returning\n", syscall); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("WET_GameSupport::Entry({}) returning\n", syscall); return nullptr; } -static bool WET_ModLoad(void* entry, APIType modapi) { +bool WET_GameSupport::ModLoad(void* entry, APIType modapi) { if (modapi != QMM_API_DLLENTRY) return false; @@ -148,12 +158,12 @@ static bool WET_ModLoad(void* entry, APIType modapi) { } -static void WET_ModUnload() { +void WET_GameSupport::ModUnload() { orig_vmMain = nullptr; } -static const char* WET_EngMsgName(intptr_t cmd) { +const char* WET_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINT); GEN_CASE(G_ERROR); @@ -384,7 +394,7 @@ static const char* WET_EngMsgName(intptr_t cmd) { } -static const char* WET_ModMsgName(intptr_t cmd) { +const char* WET_GameSupport::ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_INIT); GEN_CASE(GAME_SHUTDOWN); From 2656dd8409e7fbc22484af2fd9374a8850f0a64e Mon Sep 17 00:00:00 2001 From: Kevin Masterson Date: Wed, 18 Mar 2026 14:28:03 +0200 Subject: [PATCH 3/7] update codmp, codupmp, and jk2sp to new OOP format --- src/game_codmp.cpp | 78 ++++----- src/game_coduomp.cpp | 78 ++++----- src/game_jk2sp.cpp | 368 +++++++++++++++++++++++-------------------- 3 files changed, 275 insertions(+), 249 deletions(-) diff --git a/src/game_codmp.cpp b/src/game_codmp.cpp index 94d2c09..4ee8a55 100644 --- a/src/game_codmp.cpp +++ b/src/game_codmp.cpp @@ -20,27 +20,41 @@ Created By: #include "main.h" #include "util.h" -// GAME_GET_APIVERSION gets called first, which is when QMM has to perform mod/plugin loading, but we -// don't want to make plugins have to use separate code to handle the actual GAME_INIT message, so we -// manually fill out the msg arrays -static int CODMP_qmm_eng_msgs[] = { - G_PRINT, G_ERROR, G_ARGV, G_ARGC, G_SEND_CONSOLE_COMMAND, G_GET_CONFIGSTRING, - G_CVAR_REGISTER, G_CVAR_VARIABLE_STRING_BUFFER, G_CVAR_VARIABLE_INTEGER_VALUE, CVAR_SERVERINFO, CVAR_ROM, - G_FS_FOPEN_FILE, G_FS_READ, G_FS_WRITE, G_FS_FCLOSE_FILE, EXEC_APPEND, FS_READ, -}; -static int CODMP_qmm_mod_msgs[] = { - GAME_GET_APIVERSION, GAME_SHUTDOWN, GAME_CONSOLE_COMMAND, +struct CODMP_GameSupport : public GameSupport { + virtual const char* EngMsgName(intptr_t msg); + virtual const char* ModMsgName(intptr_t msg); + virtual bool AutoDetect(APIType engine_api); + virtual void* Entry(void* syscall, void*, APIType engine_api); + virtual bool ModLoad(void* entry, APIType mod_api); + virtual void ModUnload(); + + virtual intptr_t syscall(intptr_t, ...); + virtual intptr_t vmMain(intptr_t, ...); + + virtual const char* DefaultDLLName() { return "game" MP_DLL MOD_DLL; } + virtual const char* DefaultModDir() { return "Main"; } + virtual const char* GameName() { return "Call of Duty (MP)"; } + virtual const char* GameCode() { return "CODMP"; } + +private: + eng_syscall orig_syscall = nullptr; + mod_vmMain orig_vmMain = nullptr; + + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); + // GAME_GET_APIVERSION gets called first, which is when QMM has to perform mod/plugin loading, but we + // don't want to make plugins have to use separate code to handle the actual GAME_INIT message + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = { GAME_GET_APIVERSION, GAME_SHUTDOWN, GAME_CONSOLE_COMMAND, }; }; -GEN_GAME_FUNCS(CODMP); +GEN_GAME_OBJ(CODMP); // auto-detection logic for CODMP -static bool CODMP_AutoDetect(api_supportedgame* game, APIType engineapi) { +bool CODMP_GameSupport::AutoDetect(APIType engineapi) { if (engineapi != QMM_API_DLLENTRY) return false; - if (!str_striequal(g_gameinfo.qmm_file, game->dllname)) + if (!str_striequal(g_gameinfo.qmm_file, DefaultDLLName())) return false; if (!str_stristr(g_gameinfo.exe_file, "codmp") && !str_stristr(g_gameinfo.exe_file, "cod_lnxded")) @@ -50,20 +64,14 @@ static bool CODMP_AutoDetect(api_supportedgame* game, APIType engineapi) { } -// original syscall pointer that comes from the game engine -static eng_syscall orig_syscall = nullptr; - -// pointer to vmMain that comes from the mod -static mod_vmMain orig_vmMain = nullptr; - // wrapper syscall function that calls actual engine func in orig_syscall // this is how QMM and plugins will call into the engine -static intptr_t CODMP_syscall(intptr_t cmd, ...) { +intptr_t CODMP_GameSupport::syscall(intptr_t cmd, ...) { QMM_GET_SYSCALL_ARGS(); #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODMP_syscall({} {}) called\n", CODMP_EngMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODMP_GameSupport::syscall({} {}) called\n", EngMsgName(cmd), cmd); #endif intptr_t ret = 0; @@ -96,7 +104,7 @@ static intptr_t CODMP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODMP_syscall({} {}) returning {}\n", CODMP_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODMP_GameSupport::syscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; @@ -105,11 +113,11 @@ static intptr_t CODMP_syscall(intptr_t cmd, ...) { // wrapper vmMain function that calls actual mod func in orig_vmMain // this is how QMM and plugins will call into the mod -static intptr_t CODMP_vmMain(intptr_t cmd, ...) { +intptr_t CODMP_GameSupport::vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODMP_vmMain({} {}) called\n", CODMP_ModMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODMP_GameSupport::vmMain({} {}) called\n", ModMsgName(cmd), cmd); #endif if (!orig_vmMain) @@ -122,32 +130,26 @@ static intptr_t CODMP_vmMain(intptr_t cmd, ...) { ret = orig_vmMain(cmd, QMM_PUT_VMMAIN_ARGS()); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODMP_vmMain({} {}) returning {}\n", CODMP_ModMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODMP_GameSupport::vmMain({} {}) returning {}\n", ModMsgName(cmd), cmd, ret); #endif return ret; } -static void* CODMP_Entry(void* syscall, void*, APIType) { - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODMP_Entry({}) called\n", syscall); +void* CODMP_GameSupport::Entry(void* syscall, void*, APIType) { + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODMP_GameSupport::Entry({}) called\n", syscall); // store original syscall from engine orig_syscall = (eng_syscall)syscall; - // pointer to wrapper vmMain function that calls actual mod vmMain func orig_vmMain - g_gameinfo.pfnvmMain = CODMP_vmMain; - - // pointer to wrapper syscall function that calls actual engine syscall func - g_gameinfo.pfnsyscall = CODMP_syscall; - - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODMP_Entry({}) returning\n", syscall); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODMP_GameSupport::Entry({}) returning\n", syscall); return nullptr; } -static bool CODMP_ModLoad(void* entry, APIType modapi) { +bool CODMP_GameSupport::ModLoad(void* entry, APIType modapi) { if (modapi != QMM_API_DLLENTRY) return false; @@ -157,12 +159,12 @@ static bool CODMP_ModLoad(void* entry, APIType modapi) { } -static void CODMP_ModUnload() { +void CODMP_GameSupport::ModUnload() { orig_vmMain = nullptr; } -static const char* CODMP_EngMsgName(intptr_t cmd) { +const char* CODMP_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINTF); GEN_CASE(G_ERROR); @@ -314,7 +316,7 @@ static const char* CODMP_EngMsgName(intptr_t cmd) { } -static const char* CODMP_ModMsgName(intptr_t cmd) { +const char* CODMP_GameSupport::ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_DEFAULT_0); GEN_CASE(GAME_GET_APIVERSION); diff --git a/src/game_coduomp.cpp b/src/game_coduomp.cpp index e047ea0..f60540c 100644 --- a/src/game_coduomp.cpp +++ b/src/game_coduomp.cpp @@ -20,27 +20,41 @@ Created By: #include "main.h" #include "util.h" -// GAME_GET_APIVERSION gets called first, which is when QMM has to perform mod/plugin loading, but we -// don't want to make plugins have to use separate code to handle the actual GAME_INIT message, so we -// manually fill out the msg arrays -static int CODUOMP_qmm_eng_msgs[] = { - G_PRINT, G_ERROR, G_ARGV, G_ARGC, G_SEND_CONSOLE_COMMAND, G_GET_CONFIGSTRING, - G_CVAR_REGISTER, G_CVAR_VARIABLE_STRING_BUFFER, G_CVAR_VARIABLE_INTEGER_VALUE, CVAR_SERVERINFO, CVAR_ROM, - G_FS_FOPEN_FILE, G_FS_READ, G_FS_WRITE, G_FS_FCLOSE_FILE, EXEC_APPEND, FS_READ, -}; -static int CODUOMP_qmm_mod_msgs[] = { - GAME_GET_APIVERSION, GAME_SHUTDOWN, GAME_CONSOLE_COMMAND, +struct CODUOMP_GameSupport : public GameSupport { + virtual const char* EngMsgName(intptr_t msg); + virtual const char* ModMsgName(intptr_t msg); + virtual bool AutoDetect(APIType engine_api); + virtual void* Entry(void* syscall, void*, APIType engine_api); + virtual bool ModLoad(void* entry, APIType mod_api); + virtual void ModUnload(); + + virtual intptr_t syscall(intptr_t, ...); + virtual intptr_t vmMain(intptr_t, ...); + + virtual const char* DefaultDLLName() { return "" UO_DLL MOD_DLL; } + virtual const char* DefaultModDir() { return "uo"; } + virtual const char* GameName() { return "Call of Duty: United Offensive (MP)"; } + virtual const char* GameCode() { return "CODMP"; } + +private: + eng_syscall orig_syscall = nullptr; + mod_vmMain orig_vmMain = nullptr; + + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); + // GAME_GET_APIVERSION gets called first, which is when QMM has to perform mod/plugin loading, but we + // don't want to make plugins have to use separate code to handle the actual GAME_INIT message + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = { GAME_GET_APIVERSION, GAME_SHUTDOWN, GAME_CONSOLE_COMMAND, }; }; -GEN_GAME_FUNCS(CODUOMP); +GEN_GAME_OBJ(CODUOMP); // auto-detection logic for CODUOMP -static bool CODUOMP_AutoDetect(api_supportedgame* game, APIType engineapi) { +bool CODUOMP_GameSupport::AutoDetect(APIType engineapi) { if (engineapi != QMM_API_DLLENTRY) return false; - if (!str_striequal(g_gameinfo.qmm_file, game->dllname)) + if (!str_striequal(g_gameinfo.qmm_file, DefaultDLLName())) return false; if (!str_stristr(g_gameinfo.exe_file, "coduomp") && !str_stristr(g_gameinfo.exe_file, "coduo_lnxded")) @@ -50,20 +64,14 @@ static bool CODUOMP_AutoDetect(api_supportedgame* game, APIType engineapi) { } -// original syscall pointer that comes from the game engine -static eng_syscall orig_syscall = nullptr; - -// pointer to vmMain that comes from the mod -static mod_vmMain orig_vmMain = nullptr; - // wrapper syscall function that calls actual engine func in orig_syscall // this is how QMM and plugins will call into the engine -static intptr_t CODUOMP_syscall(intptr_t cmd, ...) { +intptr_t CODUOMP_GameSupport::syscall(intptr_t cmd, ...) { QMM_GET_SYSCALL_ARGS(); #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODUOMP_syscall({} {}) called\n", CODUOMP_EngMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODUOMP_GameSupport::syscall({} {}) called\n", EngMsgName(cmd), cmd); #endif intptr_t ret = 0; @@ -96,7 +104,7 @@ static intptr_t CODUOMP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODUOMP_syscall({} {}) returning {}\n", CODUOMP_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODUOMP_GameSupport::syscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; @@ -105,11 +113,11 @@ static intptr_t CODUOMP_syscall(intptr_t cmd, ...) { // wrapper vmMain function that calls actual mod func in orig_vmMain // this is how QMM and plugins will call into the mod -static intptr_t CODUOMP_vmMain(intptr_t cmd, ...) { +intptr_t CODUOMP_GameSupport::vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODUOMP_vmMain({} {}) called\n", CODUOMP_ModMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODUOMP_GameSupport::vmMain({} {}) called\n", ModMsgName(cmd), cmd); #endif if (!orig_vmMain) @@ -122,32 +130,26 @@ static intptr_t CODUOMP_vmMain(intptr_t cmd, ...) { ret = orig_vmMain(cmd, QMM_PUT_VMMAIN_ARGS()); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODUOMP_vmMain({} {}) returning {}\n", CODUOMP_ModMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODUOMP_GameSupport::vmMain({} {}) returning {}\n", ModMsgName(cmd), cmd, ret); #endif return ret; } -static void* CODUOMP_Entry(void* syscall, void*, APIType) { - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODUOMP_Entry({}) called\n", syscall); +void* CODUOMP_GameSupport::Entry(void* syscall, void*, APIType) { + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODUOMP_GameSupport::Entry({}) called\n", syscall); // store original syscall from engine orig_syscall = (eng_syscall)syscall; - // pointer to wrapper vmMain function that calls actual mod vmMain func orig_vmMain - g_gameinfo.pfnvmMain = CODUOMP_vmMain; - - // pointer to wrapper syscall function that calls actual engine syscall func - g_gameinfo.pfnsyscall = CODUOMP_syscall; - - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODUOMP_Entry({}) returning\n", syscall); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("CODUOMP_GameSupport::Entry({}) returning\n", syscall); return nullptr; } -static bool CODUOMP_ModLoad(void* entry, APIType modapi) { +bool CODUOMP_GameSupport::ModLoad(void* entry, APIType modapi) { if (modapi != QMM_API_DLLENTRY) return false; @@ -157,12 +159,12 @@ static bool CODUOMP_ModLoad(void* entry, APIType modapi) { } -static void CODUOMP_ModUnload() { +void CODUOMP_GameSupport::ModUnload() { orig_vmMain = nullptr; } -static const char* CODUOMP_EngMsgName(intptr_t cmd) { +const char* CODUOMP_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINTF); GEN_CASE(G_ERROR); @@ -314,7 +316,7 @@ static const char* CODUOMP_EngMsgName(intptr_t cmd) { } -static const char* CODUOMP_ModMsgName(intptr_t cmd) { +const char* CODUOMP_GameSupport::ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_DEFAULT_0); GEN_CASE(GAME_GET_APIVERSION); diff --git a/src/game_jk2sp.cpp b/src/game_jk2sp.cpp index d2190d9..8ac2b90 100644 --- a/src/game_jk2sp.cpp +++ b/src/game_jk2sp.cpp @@ -22,17 +22,183 @@ Created By: #include "util.h" -GEN_GAME_QMM_MSGS(JK2SP); +struct JK2SP_GameSupport : public GameSupport { + virtual const char* EngMsgName(intptr_t msg); + virtual const char* ModMsgName(intptr_t msg); + virtual bool AutoDetect(APIType engine_api); + virtual void* Entry(void* syscall, void*, APIType engine_api); + virtual bool ModLoad(void* entry, APIType mod_api); + virtual void ModUnload(); + + virtual intptr_t syscall(intptr_t, ...); + virtual intptr_t vmMain(intptr_t, ...); + + virtual const char* DefaultDLLName() { return "jk2game" MOD_DLL; } + virtual const char* DefaultModDir() { return "."; } + virtual const char* GameName() { return "Jedi Knight 2: Jedi Outcast (SP)"; } + virtual const char* GameCode() { return "JK2SP"; } + +private: + void update_exports(); + + // track entstrings for our G_GET_ENTITY_TOKEN syscall + // TODO find a way to make this more elegant instead of statics + static std::vector entity_tokens; + static size_t token_counter; + static void Init(const char* mapname, const char* spawntarget, int checkSum, const char* entstring, int levelTime, int randomSeed, int globalTime, SavedGameJustLoaded_e eSavedGameJustLoaded, qboolean qbLoadTransition); + + // a copy of the original import struct that comes from the game engine + game_import_t orig_import = {}; + + // a copy of the original export struct pointer that comes from the mod + game_export_t* orig_export = nullptr; + + // struct with lambdas that call QMM's syscall function. this is given to the mod + game_import_t qmm_import = { + GEN_IMPORT(Printf, G_PRINTF), + GEN_IMPORT(WriteCam, G_WRITECAM), + GEN_IMPORT(Error, G_ERROR), + GEN_IMPORT(Milliseconds, G_MILLISECONDS), + GEN_IMPORT(cvar, G_CVAR), + GEN_IMPORT(cvar_set, G_CVAR_SET), + GEN_IMPORT(Cvar_VariableIntegerValue, G_CVAR_VARIABLE_INTEGER_VALUE), + GEN_IMPORT(Cvar_VariableStringBuffer, G_CVAR_VARIABLE_STRING_BUFFER), + GEN_IMPORT(argc, G_ARGC), + GEN_IMPORT(argv, G_ARGV), + GEN_IMPORT(FS_FOpenFile, G_FS_FOPEN_FILE), + GEN_IMPORT(FS_Read, G_FS_READ), + GEN_IMPORT(FS_Write, G_FS_WRITE), + GEN_IMPORT(FS_FCloseFile, G_FS_FCLOSE_FILE), + GEN_IMPORT(FS_ReadFile, G_FS_READFILE), + GEN_IMPORT(FS_FreeFile, G_FS_FREEFILE), + GEN_IMPORT(FS_GetFileList, G_FS_GETFILELIST), + GEN_IMPORT(AppendToSaveGame, G_APPENDTOSAVEGAME), + GEN_IMPORT(ReadFromSaveGame, G_READFROMSAVEGAME), + GEN_IMPORT(ReadFromSaveGameOptional, G_READFROMSAVEGAMEOPTIONAL), + GEN_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND), + GEN_IMPORT(DropClient, G_DROP_CLIENT), + GEN_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND), + GEN_IMPORT(SetConfigstring, G_SET_CONFIGSTRING), + GEN_IMPORT(GetConfigstring, G_GET_CONFIGSTRING), + GEN_IMPORT(GetUserinfo, G_GET_USERINFO), + GEN_IMPORT(SetUserinfo, G_SET_USERINFO), + GEN_IMPORT(GetServerinfo, G_GET_SERVERINFO), + GEN_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL), + GEN_IMPORT(trace, G_TRACE), + GEN_IMPORT(pointcontents, G_POINT_CONTENTS), + GEN_IMPORT(inPVS, G_IN_PVS), + GEN_IMPORT(inPVSIgnorePortals, G_IN_PVS_IGNOREPORTALS), + GEN_IMPORT(AdjustAreaPortalState, G_ADJUSTAREAPORTALSTATE), + GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), + GEN_IMPORT(linkentity, G_LINKENTITY), + GEN_IMPORT(unlinkentity, G_UNLINKENTITY), + GEN_IMPORT(EntitiesInBox, G_ENTITIES_IN_BOX), + GEN_IMPORT(EntityContact, G_ENTITY_CONTACT), + nullptr, // int* VoiceVolume; + GEN_IMPORT(Malloc, G_MALLOC), // see qcommon/tags.h for choices + GEN_IMPORT(Free, G_FREE), + GEN_IMPORT(G2API_PrecacheGhoul2Model, G_G2API_PRECACHEGHOUL2MODEL), + GEN_IMPORT(G2API_InitGhoul2Model, G_G2API_INITGHOUL2MODEL), + GEN_IMPORT(G2API_SetLodBias, G_G2API_SETLODBIAS), + GEN_IMPORT(G2API_SetSkin, G_G2API_SETSKIN), + GEN_IMPORT(G2API_SetShader, G_G2API_SETSHADER), + GEN_IMPORT(G2API_RemoveGhoul2Model, G_G2API_REMOVEGHOUL2MODEL), + GEN_IMPORT(G2API_SetSurfaceOnOff, G_G2API_SETSURFACEONOFF), + GEN_IMPORT(G2API_SetRootSurface, G_G2API_SETROOTSURFACE), + GEN_IMPORT(G2API_RemoveSurface, G_G2API_REMOVESURFACE), + GEN_IMPORT_6(G2API_AddSurface, G_G2API_ADDSURFACE, int, CGhoul2Info*, int, int, float, float, int), + GEN_IMPORT(G2API_SetBoneAnim, G_G2API_SETBONEANIM), + GEN_IMPORT(G2API_GetBoneAnim, G_G2API_GETBONEANIM), + GEN_IMPORT(G2API_GetBoneAnimIndex, G_G2API_GETBONEANIMINDEX), + GEN_IMPORT(G2API_GetAnimRange, G_G2API_GETANIMRANGE), + GEN_IMPORT(G2API_GetAnimRangeIndex, G_G2API_GETANIMRANGEINDEX), + GEN_IMPORT(G2API_PauseBoneAnim, G_G2API_PAUSEBONEANIM), + GEN_IMPORT(G2API_PauseBoneAnimIndex, G_G2API_PAUSEBONEANIMINDEX), + GEN_IMPORT(G2API_IsPaused, G_G2API_ISPAUSED), + GEN_IMPORT(G2API_StopBoneAnim, G_G2API_STOPBONEANIM), + GEN_IMPORT(G2API_SetBoneAngles, G_G2API_SETBONEANGLES), + GEN_IMPORT(G2API_SetBoneAnglesMatrix, G_G2API_SETBONEANGLESMATRIX), + GEN_IMPORT(G2API_StopBoneAngles, G_G2API_STOPBONEANGLES), + GEN_IMPORT(G2API_RemoveBone, G_G2API_REMOVEBONE), + GEN_IMPORT(G2API_RemoveBolt, G_G2API_REMOVEBOLT), + GEN_IMPORT(G2API_AddBolt, G_G2API_ADDBOLT), + GEN_IMPORT(G2API_AddBoltSurfNum, G_G2API_ADDBOLTSURFNUM), + GEN_IMPORT(G2API_AttachG2Model, G_G2API_ATTACHG2MODEL), + GEN_IMPORT(G2API_DetachG2Model, G_G2API_DETACHG2MODEL), + GEN_IMPORT(G2API_AttachEnt, G_G2API_ATTACHENT), + GEN_IMPORT(G2API_DetachEnt, G_G2API_DETACHENT), + GEN_IMPORT(G2API_GetBoltMatrix, G_G2API_GETBOLTMATRIX), + GEN_IMPORT(G2API_ListSurfaces, G_G2API_LISTSURFACES), + GEN_IMPORT(G2API_ListBones, G_G2API_LISTBONES), + GEN_IMPORT(G2API_HaveWeGhoul2Models, G_G2API_HAVEWEGHOUL2MODELS), + GEN_IMPORT(G2API_SetGhoul2ModelFlags, G_G2API_SETGHOUL2MODELFLAGS), + GEN_IMPORT(G2API_GetGhoul2ModelFlags, G_G2API_GETGHOUL2MODELFLAGS), + GEN_IMPORT(G2API_GetAnimFileName, G_G2API_GETANIMFILENAME), + GEN_IMPORT(G2API_CollisionDetect, G_G2API_COLLISIONDETECT), + GEN_IMPORT(G2API_GiveMeVectorFromMatrix, G_G2API_GIVEMEVECTORFROMMATRIX), + GEN_IMPORT(G2API_CopyGhoul2Instance, G_G2API_COPYGHOUL2INSTANCE), + GEN_IMPORT(G2API_CleanGhoul2Models, G_G2API_CLEANGHOUL2MODELS), + GEN_IMPORT(TheGhoul2InfoArray, G_THEGHOUL2INFOARRAY), + GEN_IMPORT(G2API_GetParentSurface, G_G2API_GETPARENTSURFACE), + GEN_IMPORT(G2API_GetSurfaceIndex, G_G2API_GETSURFACEINDEX), + GEN_IMPORT(G2API_GetSurfaceName, G_G2API_GETSURFACENAME), + GEN_IMPORT(G2API_GetGLAName, G_G2API_GETGLANAME), + GEN_IMPORT(G2API_SetNewOrigin, G_G2API_SETNEWORIGIN), + GEN_IMPORT(G2API_GetBoneIndex, G_G2API_GETBONEINDEX), + GEN_IMPORT(G2API_StopBoneAnglesIndex, G_G2API_STOPBONEANGLESINDEX), + GEN_IMPORT(G2API_StopBoneAnimIndex, G_G2API_STOPBONEANIMINDEX), + GEN_IMPORT(G2API_SetBoneAnglesIndex, G_G2API_SETBONEANGLESINDEX), + GEN_IMPORT(G2API_SetBoneAnglesMatrixIndex, G_G2API_SETBONEANGLESMATRIXINDEX), + GEN_IMPORT(G2API_SetBoneAnimIndex, G_G2API_SETBONEANIMINDEX), + GEN_IMPORT(G2API_SaveGhoul2Models, G_G2API_SAVEGHOUL2MODELS), + GEN_IMPORT(G2API_LoadGhoul2Models, G_G2API_LOADGHOUL2MODELS), + GEN_IMPORT(G2API_LoadSaveCodeDestructGhoul2Info, G_G2API_LOADSAVECODEDESTRUCTGHOUL2INFO), + GEN_IMPORT(G2API_FreeSaveBuffer, G_G2API_FREESAVEBUFFER), + GEN_IMPORT(G2API_GetAnimFileNameIndex, G_G2API_GETANIMFILENAMEINDEX), + GEN_IMPORT(G2API_GetSurfaceRenderStatus, G_G2API_GETSURFACERENDERSTATUS), + GEN_IMPORT(RE_RegisterSkin, G_RE_REGISTERSKIN), + GEN_IMPORT(RE_GetAnimationCFG, G_RE_GETANIMATIONCFG), + }; + + // struct with lambdas that call QMM's vmMain function. this is given to the game engine + game_export_t qmm_export = { + GAME_API_VERSION, // apiversion + JK2SP_GameSupport::Init, + GEN_EXPORT(Shutdown, GAME_SHUTDOWN), + GEN_EXPORT(WriteLevel, GAME_WRITE_LEVEL), + GEN_EXPORT(ReadLevel, GAME_READ_LEVEL), + GEN_EXPORT(GameAllowedToSaveHere, GAME_GAMEALLOWEDTOSAVEHERE), + GEN_EXPORT(ClientConnect, GAME_CLIENT_CONNECT), + GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), + GEN_EXPORT(ClientUserinfoChanged, GAME_CLIENT_USERINFO_CHANGED), + GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), + GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), + GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), + GEN_EXPORT(RunFrame, GAME_RUN_FRAME), + GEN_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND), + GEN_EXPORT(PrintEntClassname, GAME_PRINTENTCLASSNAME), + GEN_EXPORT(ValidateAnimRange, GAME_VALIDATEANIMRANGE), + + // the engine won't use these until after Init, so we can fill these in after each call into the mod's export functions ("vmMain") + nullptr, // gentities + 0, // gentitySize + 0, // num_entities + }; + + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = GEN_GAME_QMM_MOD_MSGS(); +}; + +GEN_GAME_OBJ(JK2SP); -GEN_GAME_FUNCS(JK2SP); // auto-detection logic for JK2SP -static bool JK2SP_AutoDetect(api_supportedgame* game, APIType engineapi) { +bool JK2SP_GameSupport::AutoDetect(APIType engineapi) { if (engineapi != QMM_API_GETGAMEAPI) return false; - if (!str_striequal(g_gameinfo.qmm_file, game->dllname)) + if (!str_striequal(g_gameinfo.qmm_file, DefaultDLLName())) return false; if (!str_stristr(g_gameinfo.exe_file, "jk2sp") && !str_stristr(g_gameinfo.exe_file, "openjo_sp")) @@ -42,163 +208,25 @@ static bool JK2SP_AutoDetect(api_supportedgame* game, APIType engineapi) { } -// a copy of the original import struct that comes from the game engine -static game_import_t orig_import; - -// a copy of the original export struct pointer that comes from the mod -static game_export_t* orig_export = nullptr; -// struct with lambdas that call QMM's syscall function. this is given to the mod -static game_import_t qmm_import = { - GEN_IMPORT(Printf, G_PRINTF), - GEN_IMPORT(WriteCam, G_WRITECAM), - GEN_IMPORT(Error, G_ERROR), - GEN_IMPORT(Milliseconds, G_MILLISECONDS), - GEN_IMPORT(cvar, G_CVAR), - GEN_IMPORT(cvar_set, G_CVAR_SET), - GEN_IMPORT(Cvar_VariableIntegerValue, G_CVAR_VARIABLE_INTEGER_VALUE), - GEN_IMPORT(Cvar_VariableStringBuffer, G_CVAR_VARIABLE_STRING_BUFFER), - GEN_IMPORT(argc, G_ARGC), - GEN_IMPORT(argv, G_ARGV), - GEN_IMPORT(FS_FOpenFile, G_FS_FOPEN_FILE), - GEN_IMPORT(FS_Read, G_FS_READ), - GEN_IMPORT(FS_Write, G_FS_WRITE), - GEN_IMPORT(FS_FCloseFile, G_FS_FCLOSE_FILE), - GEN_IMPORT(FS_ReadFile, G_FS_READFILE), - GEN_IMPORT(FS_FreeFile, G_FS_FREEFILE), - GEN_IMPORT(FS_GetFileList, G_FS_GETFILELIST), - GEN_IMPORT(AppendToSaveGame, G_APPENDTOSAVEGAME), - GEN_IMPORT(ReadFromSaveGame, G_READFROMSAVEGAME), - GEN_IMPORT(ReadFromSaveGameOptional, G_READFROMSAVEGAMEOPTIONAL), - GEN_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND), - GEN_IMPORT(DropClient, G_DROP_CLIENT), - GEN_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND), - GEN_IMPORT(SetConfigstring, G_SET_CONFIGSTRING), - GEN_IMPORT(GetConfigstring, G_GET_CONFIGSTRING), - GEN_IMPORT(GetUserinfo, G_GET_USERINFO), - GEN_IMPORT(SetUserinfo, G_SET_USERINFO), - GEN_IMPORT(GetServerinfo, G_GET_SERVERINFO), - GEN_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL), - GEN_IMPORT(trace, G_TRACE), - GEN_IMPORT(pointcontents, G_POINT_CONTENTS), - GEN_IMPORT(inPVS, G_IN_PVS), - GEN_IMPORT(inPVSIgnorePortals, G_IN_PVS_IGNOREPORTALS), - GEN_IMPORT(AdjustAreaPortalState, G_ADJUSTAREAPORTALSTATE), - GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), - GEN_IMPORT(linkentity, G_LINKENTITY), - GEN_IMPORT(unlinkentity, G_UNLINKENTITY), - GEN_IMPORT(EntitiesInBox, G_ENTITIES_IN_BOX), - GEN_IMPORT(EntityContact, G_ENTITY_CONTACT), - nullptr, // int* VoiceVolume; - GEN_IMPORT(Malloc, G_MALLOC), // see qcommon/tags.h for choices - GEN_IMPORT(Free, G_FREE), - GEN_IMPORT(G2API_PrecacheGhoul2Model, G_G2API_PRECACHEGHOUL2MODEL), - GEN_IMPORT(G2API_InitGhoul2Model, G_G2API_INITGHOUL2MODEL), - GEN_IMPORT(G2API_SetLodBias, G_G2API_SETLODBIAS), - GEN_IMPORT(G2API_SetSkin, G_G2API_SETSKIN), - GEN_IMPORT(G2API_SetShader, G_G2API_SETSHADER), - GEN_IMPORT(G2API_RemoveGhoul2Model, G_G2API_REMOVEGHOUL2MODEL), - GEN_IMPORT(G2API_SetSurfaceOnOff, G_G2API_SETSURFACEONOFF), - GEN_IMPORT(G2API_SetRootSurface, G_G2API_SETROOTSURFACE), - GEN_IMPORT(G2API_RemoveSurface, G_G2API_REMOVESURFACE), - GEN_IMPORT_6(G2API_AddSurface, G_G2API_ADDSURFACE, int, CGhoul2Info*, int, int, float, float, int), - GEN_IMPORT(G2API_SetBoneAnim, G_G2API_SETBONEANIM), - GEN_IMPORT(G2API_GetBoneAnim, G_G2API_GETBONEANIM), - GEN_IMPORT(G2API_GetBoneAnimIndex, G_G2API_GETBONEANIMINDEX), - GEN_IMPORT(G2API_GetAnimRange, G_G2API_GETANIMRANGE), - GEN_IMPORT(G2API_GetAnimRangeIndex, G_G2API_GETANIMRANGEINDEX), - GEN_IMPORT(G2API_PauseBoneAnim, G_G2API_PAUSEBONEANIM), - GEN_IMPORT(G2API_PauseBoneAnimIndex, G_G2API_PAUSEBONEANIMINDEX), - GEN_IMPORT(G2API_IsPaused, G_G2API_ISPAUSED), - GEN_IMPORT(G2API_StopBoneAnim, G_G2API_STOPBONEANIM), - GEN_IMPORT(G2API_SetBoneAngles, G_G2API_SETBONEANGLES), - GEN_IMPORT(G2API_SetBoneAnglesMatrix, G_G2API_SETBONEANGLESMATRIX), - GEN_IMPORT(G2API_StopBoneAngles, G_G2API_STOPBONEANGLES), - GEN_IMPORT(G2API_RemoveBone, G_G2API_REMOVEBONE), - GEN_IMPORT(G2API_RemoveBolt, G_G2API_REMOVEBOLT), - GEN_IMPORT(G2API_AddBolt, G_G2API_ADDBOLT), - GEN_IMPORT(G2API_AddBoltSurfNum, G_G2API_ADDBOLTSURFNUM), - GEN_IMPORT(G2API_AttachG2Model, G_G2API_ATTACHG2MODEL), - GEN_IMPORT(G2API_DetachG2Model, G_G2API_DETACHG2MODEL), - GEN_IMPORT(G2API_AttachEnt, G_G2API_ATTACHENT), - GEN_IMPORT(G2API_DetachEnt, G_G2API_DETACHENT), - GEN_IMPORT(G2API_GetBoltMatrix, G_G2API_GETBOLTMATRIX), - GEN_IMPORT(G2API_ListSurfaces, G_G2API_LISTSURFACES), - GEN_IMPORT(G2API_ListBones, G_G2API_LISTBONES), - GEN_IMPORT(G2API_HaveWeGhoul2Models, G_G2API_HAVEWEGHOUL2MODELS), - GEN_IMPORT(G2API_SetGhoul2ModelFlags, G_G2API_SETGHOUL2MODELFLAGS), - GEN_IMPORT(G2API_GetGhoul2ModelFlags, G_G2API_GETGHOUL2MODELFLAGS), - GEN_IMPORT(G2API_GetAnimFileName, G_G2API_GETANIMFILENAME), - GEN_IMPORT(G2API_CollisionDetect, G_G2API_COLLISIONDETECT), - GEN_IMPORT(G2API_GiveMeVectorFromMatrix, G_G2API_GIVEMEVECTORFROMMATRIX), - GEN_IMPORT(G2API_CopyGhoul2Instance, G_G2API_COPYGHOUL2INSTANCE), - GEN_IMPORT(G2API_CleanGhoul2Models, G_G2API_CLEANGHOUL2MODELS), - GEN_IMPORT(TheGhoul2InfoArray, G_THEGHOUL2INFOARRAY), - GEN_IMPORT(G2API_GetParentSurface, G_G2API_GETPARENTSURFACE), - GEN_IMPORT(G2API_GetSurfaceIndex, G_G2API_GETSURFACEINDEX), - GEN_IMPORT(G2API_GetSurfaceName, G_G2API_GETSURFACENAME), - GEN_IMPORT(G2API_GetGLAName, G_G2API_GETGLANAME), - GEN_IMPORT(G2API_SetNewOrigin, G_G2API_SETNEWORIGIN), - GEN_IMPORT(G2API_GetBoneIndex, G_G2API_GETBONEINDEX), - GEN_IMPORT(G2API_StopBoneAnglesIndex, G_G2API_STOPBONEANGLESINDEX), - GEN_IMPORT(G2API_StopBoneAnimIndex, G_G2API_STOPBONEANIMINDEX), - GEN_IMPORT(G2API_SetBoneAnglesIndex, G_G2API_SETBONEANGLESINDEX), - GEN_IMPORT(G2API_SetBoneAnglesMatrixIndex, G_G2API_SETBONEANGLESMATRIXINDEX), - GEN_IMPORT(G2API_SetBoneAnimIndex, G_G2API_SETBONEANIMINDEX), - GEN_IMPORT(G2API_SaveGhoul2Models, G_G2API_SAVEGHOUL2MODELS), - GEN_IMPORT(G2API_LoadGhoul2Models, G_G2API_LOADGHOUL2MODELS), - GEN_IMPORT(G2API_LoadSaveCodeDestructGhoul2Info, G_G2API_LOADSAVECODEDESTRUCTGHOUL2INFO), - GEN_IMPORT(G2API_FreeSaveBuffer, G_G2API_FREESAVEBUFFER), - GEN_IMPORT(G2API_GetAnimFileNameIndex, G_G2API_GETANIMFILENAMEINDEX), - GEN_IMPORT(G2API_GetSurfaceRenderStatus, G_G2API_GETSURFACERENDERSTATUS), - GEN_IMPORT(RE_RegisterSkin, G_RE_REGISTERSKIN), - GEN_IMPORT(RE_GetAnimationCFG, G_RE_GETANIMATIONCFG), -}; - - // these are "pre" hooks for storing some data for polyfills. // we need these to be called BEFORE plugins' prehooks get called so they have to be done in the qmm_export table // track entstrings for our G_GET_ENTITY_TOKEN syscall -static std::vector s_entity_tokens; -static size_t s_tokencount = 0; -static void JK2SP_Init(const char* mapname, const char* spawntarget, int checkSum, const char* entstring, int levelTime, int randomSeed, int globalTime, SavedGameJustLoaded_e eSavedGameJustLoaded, qboolean qbLoadTransition) { +std::vector JK2SP_GameSupport::entity_tokens; +size_t JK2SP_GameSupport::token_counter = 0; + +void JK2SP_GameSupport::Init(const char* mapname, const char* spawntarget, int checkSum, const char* entstring, int levelTime, int randomSeed, int globalTime, SavedGameJustLoaded_e eSavedGameJustLoaded, qboolean qbLoadTransition) { if (entstring) { - s_entity_tokens = util_parse_entstring(entstring); - s_tokencount = 0; + entity_tokens = util_parse_entstring(entstring); + token_counter = 0; } cgame.is_from_QMM = true; - vmMain(GAME_INIT, mapname, spawntarget, checkSum, entstring, levelTime, randomSeed, globalTime, eSavedGameJustLoaded, qbLoadTransition); + ::vmMain(GAME_INIT, mapname, spawntarget, checkSum, entstring, levelTime, randomSeed, globalTime, eSavedGameJustLoaded, qbLoadTransition); } -// struct with lambdas that call QMM's vmMain function. this is given to the game engine -static game_export_t qmm_export = { - GAME_API_VERSION, // apiversion - JK2SP_Init, - GEN_EXPORT(Shutdown, GAME_SHUTDOWN), - GEN_EXPORT(WriteLevel, GAME_WRITE_LEVEL), - GEN_EXPORT(ReadLevel, GAME_READ_LEVEL), - GEN_EXPORT(GameAllowedToSaveHere, GAME_GAMEALLOWEDTOSAVEHERE), - GEN_EXPORT(ClientConnect, GAME_CLIENT_CONNECT), - GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), - GEN_EXPORT(ClientUserinfoChanged, GAME_CLIENT_USERINFO_CHANGED), - GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), - GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), - GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), - GEN_EXPORT(RunFrame, GAME_RUN_FRAME), - GEN_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND), - GEN_EXPORT(PrintEntClassname, GAME_PRINTENTCLASSNAME), - GEN_EXPORT(ValidateAnimRange, GAME_VALIDATEANIMRANGE), - - // the engine won't use these until after Init, so we can fill these in after each call into the mod's export functions ("vmMain") - nullptr, // gentities - 0, // gentitySize - 0, // num_entities -}; - - // update the export variables from orig_export -static void s_update_export() { +void JK2SP_GameSupport::update_exports() { if (!orig_export) return; @@ -226,16 +254,16 @@ static void s_update_export() { // wrapper syscall function that calls actual engine func from orig_import // this is how QMM and plugins will call into the engine -static intptr_t JK2SP_syscall(intptr_t cmd, ...) { +intptr_t JK2SP_GameSupport::syscall(intptr_t cmd, ...) { QMM_GET_SYSCALL_ARGS(); #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2SP_syscall({} {}) called\n", JK2SP_EngMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2SP_GameSupport::syscall({} {}) called\n", EngMsgName(cmd), cmd); #endif // update export vars before calling into the engine - s_update_export(); + update_exports(); intptr_t ret = 0; @@ -379,7 +407,7 @@ static intptr_t JK2SP_syscall(intptr_t cmd, ...) { } case G_GET_ENTITY_TOKEN: { // qboolean trap_GetEntityToken(char *buffer, int bufferSize); - if (s_tokencount >= s_entity_tokens.size()) { + if (token_counter >= entity_tokens.size()) { ret = qfalse; break; } @@ -387,7 +415,7 @@ static intptr_t JK2SP_syscall(intptr_t cmd, ...) { char* buffer = (char*)args[0]; intptr_t bufferSize = args[1]; - strncpyz(buffer, s_entity_tokens[s_tokencount++].c_str(), (size_t)bufferSize); + strncpyz(buffer, entity_tokens[token_counter++].c_str(), (size_t)bufferSize); ret = qtrue; break; } @@ -413,7 +441,7 @@ static intptr_t JK2SP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2SP_syscall({} {}) returning {}\n", JK2SP_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2SP_GameSupport::syscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; @@ -422,11 +450,11 @@ static intptr_t JK2SP_syscall(intptr_t cmd, ...) { // wrapper vmMain function that calls actual mod func from orig_export // this is how QMM and plugins will call into the mod -static intptr_t JK2SP_vmMain(intptr_t cmd, ...) { +intptr_t JK2SP_GameSupport::vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2SP_vmMain({} {}) called\n", JK2SP_ModMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2SP_GameSupport::vmMain({} {}) called\n", ModMsgName(cmd), cmd); #endif if (!orig_export) @@ -463,18 +491,18 @@ static intptr_t JK2SP_vmMain(intptr_t cmd, ...) { }; // update export vars after returning from the mod - s_update_export(); + update_exports(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2SP_vmMain({} {}) returning {}\n", JK2SP_ModMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2SP_GameSupport::vmMain({} {}) returning {}\n", ModMsgName(cmd), cmd, ret); #endif return ret; } -static void* JK2SP_Entry(void* import, void*, APIType) { - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2SP_Entry({}) called\n", import); +void* JK2SP_GameSupport::Entry(void* import, void*, APIType) { + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2SP_GameSupport::Entry({}) called\n", import); // original import struct from engine // the struct given by the engine goes out of scope after this returns so we have to copy the whole thing @@ -484,13 +512,7 @@ static void* JK2SP_Entry(void* import, void*, APIType) { // fill in variables of our hooked import struct to pass to the mod qmm_import.VoiceVolume = orig_import.VoiceVolume; - // pointer to wrapper vmMain function that calls actual mod func from orig_export - g_gameinfo.pfnvmMain = JK2SP_vmMain; - - // pointer to wrapper syscall function that calls actual engine func from orig_import - g_gameinfo.pfnsyscall = JK2SP_syscall; - - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2SP_Entry({}) returning {}\n", import, fmt::ptr(&qmm_export)); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JK2SP_GameSupport::Entry({}) returning {}\n", import, fmt::ptr(&qmm_export)); // struct full of export lambdas to QMM's vmMain // this gets returned to the game engine, but we haven't loaded the mod yet. @@ -499,7 +521,7 @@ static void* JK2SP_Entry(void* import, void*, APIType) { } -static bool JK2SP_ModLoad(void* entry, APIType modapi) { +bool JK2SP_GameSupport::ModLoad(void* entry, APIType modapi) { if (modapi != QMM_API_GETGAMEAPI) return false; @@ -510,12 +532,12 @@ static bool JK2SP_ModLoad(void* entry, APIType modapi) { } -static void JK2SP_ModUnload() { +void JK2SP_GameSupport::ModUnload() { orig_export = nullptr; } -static const char* JK2SP_EngMsgName(intptr_t cmd) { +const char* JK2SP_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINTF); GEN_CASE(G_WRITECAM); @@ -633,7 +655,7 @@ static const char* JK2SP_EngMsgName(intptr_t cmd) { } -static const char* JK2SP_ModMsgName(intptr_t cmd) { +const char* JK2SP_GameSupport::ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAMEV_APIVERSION); GEN_CASE(GAME_INIT); From 17c809190897ec7fd5f39a98c2ebdef975ae624f Mon Sep 17 00:00:00 2001 From: Kevin Masterson Date: Wed, 18 Mar 2026 21:02:46 +0200 Subject: [PATCH 4/7] updated more games, tweaked some already-done implementations. don't compile/include MOH*, SOF2*, COD*, STVOY*, and STEF2 .h/.cpp in 64-bit --- include/game_cod11mp.h | 7 + include/game_codmp.h | 6 + include/game_coduomp.h | 6 + include/game_mohaa.h | 6 + include/game_mohbt.h | 6 + include/game_mohsh.h | 6 + include/game_sof2mp.h | 6 + include/game_sof2sp.h | 6 + include/game_stef2.h | 6 + include/game_stvoyhm.h | 5 + include/game_stvoysp.h | 6 + src/game_api.cpp | 25 +- src/game_cod11mp.cpp | 10 +- src/game_codmp.cpp | 9 + src/game_coduomp.cpp | 9 + src/game_jamp.cpp | 730 +++++++++++++++++++++-------------------- src/game_jasp.cpp | 522 +++++++++++++++-------------- src/game_jk2mp.cpp | 3 + src/game_jk2sp.cpp | 358 ++++++++++---------- src/game_mohaa.cpp | 659 +++++++++++++++++++------------------ src/game_mohbt.cpp | 676 ++++++++++++++++++++------------------ src/game_mohsh.cpp | 688 ++++++++++++++++++++------------------ src/game_q2r.cpp | 545 +++++++++++++++--------------- src/game_q3a.cpp | 3 + src/game_quake2.cpp | 430 +++++++++++++----------- src/game_rtcwmp.cpp | 3 + src/game_rtcwsp.cpp | 3 + src/game_sin.cpp | 566 +++++++++++++++++--------------- src/game_sof2mp.cpp | 9 + src/game_sof2sp.cpp | 14 +- src/game_stef2.cpp | 26 +- src/game_stvoyhm.cpp | 9 + src/game_stvoysp.cpp | 26 +- src/game_wet.cpp | 3 + 34 files changed, 2884 insertions(+), 2508 deletions(-) diff --git a/include/game_cod11mp.h b/include/game_cod11mp.h index 8230296..b8af293 100644 --- a/include/game_cod11mp.h +++ b/include/game_cod11mp.h @@ -12,9 +12,16 @@ Created By: #ifndef QMM2_GAME_COD11MP_H #define QMM2_GAME_COD11MP_H +#include "version.h" + +#if defined(QMM_ARCH_32) + // these import messages do not have an exact analogue in COD11MP enum { G_ARGS = -100, // char* (void) }; +#endif // QMM_ARCH_32 + #endif // QMM2_GAME_COD11MP_H + diff --git a/include/game_codmp.h b/include/game_codmp.h index 463b501..3861546 100644 --- a/include/game_codmp.h +++ b/include/game_codmp.h @@ -12,9 +12,15 @@ Created By: #ifndef QMM2_GAME_CODMP_H #define QMM2_GAME_CODMP_H +#include "version.h" + +#if defined(QMM_ARCH_32) + // these import messages do not have an exact analogue in CODMP enum { G_ARGS = -100, // char* (void) }; +#endif // QMM_ARCH_32 + #endif // QMM2_GAME_CODMP_H diff --git a/include/game_coduomp.h b/include/game_coduomp.h index f85f47b..ad86d37 100644 --- a/include/game_coduomp.h +++ b/include/game_coduomp.h @@ -12,9 +12,15 @@ Created By: #ifndef QMM2_GAME_CODUOMP_H #define QMM2_GAME_CODUOMP_H +#include "version.h" + +#if defined(QMM_ARCH_32) + // these import messages do not have an exact analogue in CODUOMP enum { G_ARGS = -100, // char* (void) }; +#endif // QMM_ARCH_32 + #endif // QMM2_GAME_CODUOMP_H diff --git a/include/game_mohaa.h b/include/game_mohaa.h index f70ff90..2758079 100644 --- a/include/game_mohaa.h +++ b/include/game_mohaa.h @@ -12,6 +12,10 @@ Created By: #ifndef QMM2_GAME_MOHAA_H #define QMM2_GAME_MOHAA_H +#include "version.h" + +#if defined(QMM_ARCH_32) + // import ("syscall") cmds enum { G_PRINTF, @@ -250,4 +254,6 @@ enum { // we need to store FILE* in fileHandle_t so change the underlying type #define fileHandle_t intptr_t +#endif // QMM_ARCH_32 + #endif // QMM2_GAME_MOHAA_H diff --git a/include/game_mohbt.h b/include/game_mohbt.h index 7e9b7ae..4877c99 100644 --- a/include/game_mohbt.h +++ b/include/game_mohbt.h @@ -12,6 +12,10 @@ Created By: #ifndef QMM2_GAME_MOHBT_H #define QMM2_GAME_MOHBT_H +#include "version.h" + +#if defined(QMM_ARCH_32) + // import ("syscall") cmds enum { G_PRINTF, @@ -256,4 +260,6 @@ enum { // we need to store FILE* in fileHandle_t so change the underlying type #define fileHandle_t intptr_t +#endif // QMM_ARCH_32 + #endif // QMM2_GAME_MOHBT_H diff --git a/include/game_mohsh.h b/include/game_mohsh.h index 8c14ce2..539b2fc 100644 --- a/include/game_mohsh.h +++ b/include/game_mohsh.h @@ -12,6 +12,10 @@ Created By: #ifndef QMM2_GAME_MOHSH_H #define QMM2_GAME_MOHSH_H +#include "version.h" + +#if defined(QMM_ARCH_32) + // import ("syscall") cmds enum { G_PRINTF, @@ -256,4 +260,6 @@ enum { // we need to store FILE* in fileHandle_t so change the underlying type #define fileHandle_t intptr_t +#endif // QMM_ARCH_32 + #endif // QMM2_GAME_MOHSH_H diff --git a/include/game_sof2mp.h b/include/game_sof2mp.h index 7e07ddf..3a3e06b 100644 --- a/include/game_sof2mp.h +++ b/include/game_sof2mp.h @@ -12,9 +12,15 @@ Created By: #ifndef QMM2_GAME_SOF2MP_H #define QMM2_GAME_SOF2MP_H +#include "version.h" + +#if defined(QMM_ARCH_32) + // these import messages do not have an exact analogue in SOF2MP enum { G_ARGS = -100, // char* (void) }; +#endif // QMM_ARCH_32 + #endif // QMM2_GAME_SOF2MP_H diff --git a/include/game_sof2sp.h b/include/game_sof2sp.h index 9e985d7..32ec0e4 100644 --- a/include/game_sof2sp.h +++ b/include/game_sof2sp.h @@ -12,6 +12,10 @@ Created By: #ifndef QMM2_GAME_SOF2SP_H #define QMM2_GAME_SOF2SP_H +#include "version.h" + +#if defined(QMM_ARCH_32) + // import ("syscall") cmds enum { G_PRINTF, @@ -167,4 +171,6 @@ enum { G_ERROR = -100, // void (const char* msg) }; +#endif // QMM_ARCH_32 + #endif // QMM2_GAME_SOF2SP_H diff --git a/include/game_stef2.h b/include/game_stef2.h index 008cb82..c95c63b 100644 --- a/include/game_stef2.h +++ b/include/game_stef2.h @@ -12,6 +12,10 @@ Created By: #ifndef QMM2_GAME_STEF2_H #define QMM2_GAME_STEF2_H +#include "version.h" + +#if defined(QMM_ARCH_32) + // import ("syscall") cmds enum { G_PRINTF, @@ -407,4 +411,6 @@ enum { G_GET_ENTITY_TOKEN, // qboolean (char *buffer, int bufferSize) }; +#endif // QMM_ARCH_32 + #endif // QMM2_GAME_STEF2_H diff --git a/include/game_stvoyhm.h b/include/game_stvoyhm.h index ad5bc09..eecabcc 100644 --- a/include/game_stvoyhm.h +++ b/include/game_stvoyhm.h @@ -12,6 +12,9 @@ Created By: #ifndef QMM2_GAME_STVOYMP_H #define QMM2_GAME_STVOYMP_H +#include "version.h" + +#if defined(QMM_ARCH_32) // these function ids are defined either in the g_syscalls.asm file or in qcommon.h from ioef source, // but they do not appear in the enum in stvoyhm/game/g_public.h @@ -37,4 +40,6 @@ enum { G_ARGS = -100, // char* (void) }; +#endif // QMM_ARCH_32 + #endif // QMM2_GAME_STVOYMP_H diff --git a/include/game_stvoysp.h b/include/game_stvoysp.h index d763f65..0015bbe 100644 --- a/include/game_stvoysp.h +++ b/include/game_stvoysp.h @@ -12,6 +12,10 @@ Created By: #ifndef QMM2_GAME_STVOYSP_H #define QMM2_GAME_STVOYSP_H +#include "version.h" + +#if defined(QMM_ARCH_32) + // import ("syscall") cmds enum { G_PRINTF, @@ -94,4 +98,6 @@ enum { G_ARGS, // char* (void) }; +#endif // QMM_ARCH_32 + #endif // QMM2_GAME_STVOYSP_H diff --git a/src/game_api.cpp b/src/game_api.cpp index b298206..186e068 100644 --- a/src/game_api.cpp +++ b/src/game_api.cpp @@ -84,33 +84,14 @@ std::vector api_supportedgames = { }; #if 0 -// these games don't appear to have an official 64-bit version or source port -#if defined(QMM_ARCH_32) - { "game" MP_DLL MOD_DLL, nullptr, "Main", "Call of Duty (MP)", GEN_GAME_INFO(CODMP), 8, 4 }, - { "" UO_DLL MOD_DLL, nullptr, "uo", "Call of Duty: United Offensive (MP)", GEN_GAME_INFO(CODUOMP), 8, 4 }, -#endif - - // GetGameAPI games - { "jk2game" MOD_DLL, nullptr, ".", "Jedi Knight 2: Jedi Outcast (SP)", GEN_GAME_INFO(JK2SP), 13, 9 }, - { "jagame" MOD_DLL, nullptr, ".", "Jedi Knight: Jedi Academy (SP)", GEN_GAME_INFO(JASP), 13, 9 }, - { "game" MOD_DLL, nullptr, "baseq2", "Quake 2", GEN_GAME_INFO(QUAKE2), 7, 3 }, - { "game" MOD_DLL, nullptr, "base", "SiN", GEN_GAME_INFO(SIN), 10, 3 }, // OpenMOHAA adds 64-bit MoH support but the API is very different, so disable it for now // the rest of the games don't appear to have an official 64-bit version or source port #if defined(QMM_ARCH_32) - { "game" MOD_DLL, nullptr, "main", "Medal of Honor: Allied Assault", GEN_GAME_INFO(MOHAA), 9, 7 }, - { "game" MOD_DLL, nullptr, "mainta", "Medal of Honor: Spearhead", GEN_GAME_INFO(MOHSH), 9, 7 }, - { "game" MOD_DLL, nullptr, "maintt", "Medal of Honor: Breakthrough", GEN_GAME_INFO(MOHBT), 9, 7 }, - { "game" MOD_DLL, nullptr, "base", "Star Trek: Elite Force II", GEN_GAME_INFO(STEF2), 17, 4 }, - { "game" MOD_DLL, nullptr, ".", "Soldier of Fortune 2: Double Helix (SP)", GEN_GAME_INFO(SOF2SP), 0, 0 }, - { "efgame" MOD_DLL, nullptr, ".", "Star Trek Voyager: Elite Force (SP)", GEN_GAME_INFO(STVOYSP), 13, 9 }, + { "game" MOD_DLL, nullptr, "base", "Star Trek: Elite Force II", GEN_GAME_INFO(STEF2), 17, 4 }, + { "game" MOD_DLL, nullptr, ".", "Soldier of Fortune 2: Double Helix (SP)", GEN_GAME_INFO(SOF2SP), 0, 0 }, + { "efgame" MOD_DLL, nullptr, ".", "Star Trek Voyager: Elite Force (SP)", GEN_GAME_INFO(STVOYSP), 13, 9 }, #endif -// Q2R only exists for 64-bit Windows -#if defined(QMM_OS_WINDOWS) && defined(QMM_ARCH_64) - { "game_" X64_DLL, nullptr, "baseq2", "Quake 2 Remastered", GEN_GAME_INFO(Q2R), 9, 6 }, -#endif -}; #endif diff --git a/src/game_cod11mp.cpp b/src/game_cod11mp.cpp index 38d4d1a..544088c 100644 --- a/src/game_cod11mp.cpp +++ b/src/game_cod11mp.cpp @@ -9,6 +9,10 @@ Created By: */ +#include "version.h" + +#if defined(QMM_ARCH_32) + #include #include "game_api.h" @@ -17,7 +21,6 @@ Created By: #include // QMM-specific COD11MP header #include "game_cod11mp.h" -#include "main.h" struct COD11MP_GameSupport : public GameSupport { @@ -37,7 +40,10 @@ struct COD11MP_GameSupport : public GameSupport { virtual const char* GameCode() { return "COD11MP"; } private: + // a copy of the original syscall from the engine eng_syscall orig_syscall = nullptr; + + // a copy of the vmMain function from the mod mod_vmMain orig_vmMain = nullptr; const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); @@ -328,3 +334,5 @@ const char* COD11MP_GameSupport::ModMsgName(intptr_t cmd) { return "unknown"; } } + +#endif // QMM_ARCH_32 diff --git a/src/game_codmp.cpp b/src/game_codmp.cpp index 4ee8a55..44a9270 100644 --- a/src/game_codmp.cpp +++ b/src/game_codmp.cpp @@ -9,6 +9,10 @@ Created By: */ +#include "version.h" + +#if defined(QMM_ARCH_32) + #include #include "game_api.h" @@ -37,7 +41,10 @@ struct CODMP_GameSupport : public GameSupport { virtual const char* GameCode() { return "CODMP"; } private: + // a copy of the original syscall from the engine eng_syscall orig_syscall = nullptr; + + // a copy of the vmMain function from the mod mod_vmMain orig_vmMain = nullptr; const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); @@ -347,3 +354,5 @@ const char* CODMP_GameSupport::ModMsgName(intptr_t cmd) { return "unknown"; } } + +#endif // QMM_ARCH_32 diff --git a/src/game_coduomp.cpp b/src/game_coduomp.cpp index f60540c..e8c29c6 100644 --- a/src/game_coduomp.cpp +++ b/src/game_coduomp.cpp @@ -9,6 +9,10 @@ Created By: */ +#include "version.h" + +#if defined(QMM_ARCH_32) + #include #include "game_api.h" @@ -37,7 +41,10 @@ struct CODUOMP_GameSupport : public GameSupport { virtual const char* GameCode() { return "CODMP"; } private: + // a copy of the original syscall from the engine eng_syscall orig_syscall = nullptr; + + // a copy of the vmMain function from the mod mod_vmMain orig_vmMain = nullptr; const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); @@ -347,3 +354,5 @@ const char* CODUOMP_GameSupport::ModMsgName(intptr_t cmd) { return "unknown"; } } + +#endif // QMM_ARCH_32 diff --git a/src/game_jamp.cpp b/src/game_jamp.cpp index 61a940d..514e1b0 100644 --- a/src/game_jamp.cpp +++ b/src/game_jamp.cpp @@ -38,7 +38,10 @@ struct JAMP_GameSupport : public GameSupport { virtual const char* GameCode() { return "JAMP"; } private: + // a copy of the original syscall from the engine eng_syscall orig_syscall = nullptr; + + // a copy of the vmMain function from the mod mod_vmMain orig_vmMain = nullptr; const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); @@ -47,370 +50,19 @@ struct JAMP_GameSupport : public GameSupport { // these variables are all for the GGA implementation for OpenJK // a copy of the apiversion int that comes from the game engine - intptr_t orig_apiversion = 0; + static intptr_t orig_apiversion; // a copy of the original import struct that comes from the game engine - game_import_t orig_import = {}; + static game_import_t orig_import; // a copy of the original export struct pointer that comes from the mod - game_export_t* orig_export = nullptr; + static game_export_t* orig_export; // struct with lambdas that call QMM's syscall function. this is given to the mod - game_import_t qmm_import = { - GEN_IMPORT(Print, G_PRINT), - GEN_IMPORT(Error, G_ERROR), - GEN_IMPORT(Milliseconds, G_MILLISECONDS), - GEN_IMPORT(PrecisionTimerStart, G_PRECISIONTIMER_START), - GEN_IMPORT(PrecisionTimerEnd, G_PRECISIONTIMER_END), - GEN_IMPORT(SV_RegisterSharedMemory, G_SET_SHARED_BUFFER), - GEN_IMPORT(RealTime, G_REAL_TIME), - GEN_IMPORT(TrueMalloc, G_TRUEMALLOC), - GEN_IMPORT(TrueFree, G_TRUEFREE), - GEN_IMPORT(SnapVector, G_SNAPVECTOR), - GEN_IMPORT(Cvar_Register, G_CVAR_REGISTER), - GEN_IMPORT(Cvar_Set, G_CVAR_SET), - GEN_IMPORT(Cvar_Update, G_CVAR_UPDATE), - GEN_IMPORT(Cvar_VariableIntegerValue, G_CVAR_VARIABLE_INTEGER_VALUE), - GEN_IMPORT(Cvar_VariableStringBuffer, G_CVAR_VARIABLE_STRING_BUFFER), - GEN_IMPORT(Argc, G_ARGC), - GEN_IMPORT(Argv, G_ARGV), - GEN_IMPORT(FS_Close, G_FS_FCLOSE_FILE), - GEN_IMPORT(FS_GetFileList, G_FS_GETFILELIST), - GEN_IMPORT(FS_Open, G_FS_FOPEN_FILE), - GEN_IMPORT(FS_Read, G_FS_READ), - GEN_IMPORT(FS_Write, G_FS_WRITE), - GEN_IMPORT(AdjustAreaPortalState, G_ADJUST_AREA_PORTAL_STATE), - GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), - GEN_IMPORT(DebugPolygonCreate, G_DEBUG_POLYGON_CREATE), - GEN_IMPORT(DebugPolygonDelete, G_DEBUG_POLYGON_DELETE), - GEN_IMPORT(DropClient, G_DROP_CLIENT), - GEN_IMPORT(EntitiesInBox, G_ENTITIES_IN_BOX), - GEN_IMPORT(EntityContact, G_ENTITY_CONTACT), - GEN_IMPORT(GetConfigstring, G_GET_CONFIGSTRING), - GEN_IMPORT(GetEntityToken, G_GET_ENTITY_TOKEN), - GEN_IMPORT(GetServerinfo, G_GET_SERVERINFO), - GEN_IMPORT(GetUsercmd, G_GET_USERCMD), - GEN_IMPORT(GetUserinfo, G_GET_USERINFO), - GEN_IMPORT(InPVS, G_IN_PVS), - GEN_IMPORT(InPVSIgnorePortals, G_IN_PVS_IGNORE_PORTALS), - GEN_IMPORT(LinkEntity, G_LINKENTITY), - GEN_IMPORT(LocateGameData, G_LOCATE_GAME_DATA), - GEN_IMPORT(PointContents, G_POINT_CONTENTS), - GEN_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND), - GEN_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND), - GEN_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL), - GEN_IMPORT(SetConfigstring, G_SET_CONFIGSTRING), - GEN_IMPORT(SetServerCull, G_SET_SERVER_CULL), - GEN_IMPORT(SetUserinfo, G_SET_USERINFO), - GEN_IMPORT(SiegePersSet, G_SIEGEPERSSET), - GEN_IMPORT(SiegePersGet, G_SIEGEPERSGET), - GEN_IMPORT(Trace, G_TRACE), - GEN_IMPORT(UnlinkEntity, G_UNLINKENTITY), - GEN_IMPORT(ROFF_Clean, G_ROFF_CLEAN), - GEN_IMPORT(ROFF_UpdateEntities, G_ROFF_UPDATE_ENTITIES), - GEN_IMPORT(ROFF_Cache, G_ROFF_CACHE), - GEN_IMPORT(ROFF_Play, G_ROFF_PLAY), - GEN_IMPORT(ROFF_Purge_Ent, G_ROFF_PURGE_ENT), - GEN_IMPORT(ICARUS_RunScript, G_ICARUS_RUNSCRIPT), - GEN_IMPORT(ICARUS_RegisterScript, G_ICARUS_REGISTERSCRIPT), - GEN_IMPORT(ICARUS_Init, G_ICARUS_INIT), - GEN_IMPORT(ICARUS_ValidEnt, G_ICARUS_VALIDENT), - GEN_IMPORT(ICARUS_IsInitialized, G_ICARUS_ISINITIALIZED), - GEN_IMPORT(ICARUS_MaintainTaskManager, G_ICARUS_MAINTAINTASKMANAGER), - GEN_IMPORT(ICARUS_IsRunning, G_ICARUS_ISRUNNING), - GEN_IMPORT(ICARUS_TaskIDPending, G_ICARUS_TASKIDPENDING), - GEN_IMPORT(ICARUS_InitEnt, G_ICARUS_INITENT), - GEN_IMPORT(ICARUS_FreeEnt, G_ICARUS_FREEENT), - GEN_IMPORT(ICARUS_AssociateEnt, G_ICARUS_ASSOCIATEENT), - GEN_IMPORT(ICARUS_Shutdown, G_ICARUS_SHUTDOWN), - GEN_IMPORT(ICARUS_TaskIDSet, G_ICARUS_TASKIDSET), - GEN_IMPORT(ICARUS_TaskIDComplete, G_ICARUS_TASKIDCOMPLETE), - GEN_IMPORT(ICARUS_SetVar, G_ICARUS_SETVAR), - GEN_IMPORT(ICARUS_VariableDeclared, G_ICARUS_VARIABLEDECLARED), - GEN_IMPORT(ICARUS_GetFloatVariable, G_ICARUS_GETFLOATVARIABLE), - GEN_IMPORT(ICARUS_GetStringVariable, G_ICARUS_GETSTRINGVARIABLE), - GEN_IMPORT(ICARUS_GetVectorVariable, G_ICARUS_GETVECTORVARIABLE), - GEN_IMPORT(Nav_Init, G_NAV_INIT), - GEN_IMPORT(Nav_Free, G_NAV_FREE), - GEN_IMPORT(Nav_Load, G_NAV_LOAD), - GEN_IMPORT(Nav_Save, G_NAV_SAVE), - GEN_IMPORT(Nav_AddRawPoint, G_NAV_ADDRAWPOINT), - GEN_IMPORT(Nav_CalculatePaths, G_NAV_CALCULATEPATHS), - GEN_IMPORT(Nav_HardConnect, G_NAV_HARDCONNECT), - GEN_IMPORT(Nav_ShowNodes, G_NAV_SHOWNODES), - GEN_IMPORT(Nav_ShowEdges, G_NAV_SHOWEDGES), - GEN_IMPORT(Nav_ShowPath, G_NAV_SHOWPATH), - GEN_IMPORT(Nav_GetNearestNode, G_NAV_GETNEARESTNODE), - GEN_IMPORT(Nav_GetBestNode, G_NAV_GETBESTNODE), - GEN_IMPORT(Nav_GetNodePosition, G_NAV_GETNODEPOSITION), - GEN_IMPORT(Nav_GetNodeNumEdges, G_NAV_GETNODENUMEDGES), - GEN_IMPORT(Nav_GetNodeEdge, G_NAV_GETNODEEDGE), - GEN_IMPORT(Nav_GetNumNodes, G_NAV_GETNUMNODES), - GEN_IMPORT(Nav_Connected, G_NAV_CONNECTED), - GEN_IMPORT(Nav_GetPathCost, G_NAV_GETPATHCOST), - GEN_IMPORT(Nav_GetEdgeCost, G_NAV_GETEDGECOST), - GEN_IMPORT(Nav_GetProjectedNode, G_NAV_GETPROJECTEDNODE), - GEN_IMPORT(Nav_CheckFailedNodes, G_NAV_CHECKFAILEDNODES), - GEN_IMPORT(Nav_AddFailedNode, G_NAV_ADDFAILEDNODE), - GEN_IMPORT(Nav_NodeFailed, G_NAV_NODEFAILED), - GEN_IMPORT(Nav_NodesAreNeighbors, G_NAV_NODESARENEIGHBORS), - GEN_IMPORT(Nav_ClearFailedEdge, G_NAV_CLEARFAILEDEDGE), - GEN_IMPORT(Nav_ClearAllFailedEdges, G_NAV_CLEARALLFAILEDEDGES), - GEN_IMPORT(Nav_EdgeFailed, G_NAV_EDGEFAILED), - GEN_IMPORT(Nav_AddFailedEdge, G_NAV_ADDFAILEDEDGE), - GEN_IMPORT(Nav_CheckFailedEdge, G_NAV_CHECKFAILEDEDGE), - GEN_IMPORT(Nav_CheckAllFailedEdges, G_NAV_CHECKALLFAILEDEDGES), - GEN_IMPORT(Nav_RouteBlocked, G_NAV_ROUTEBLOCKED), - GEN_IMPORT(Nav_GetBestNodeAltRoute, G_NAV_GETBESTNODEALTROUTE), - GEN_IMPORT(Nav_GetBestNodeAltRoute2, G_NAV_GETBESTNODEALT2), - GEN_IMPORT(Nav_GetBestPathBetweenEnts, G_NAV_GETBESTPATHBETWEENENTS), - GEN_IMPORT(Nav_GetNodeRadius, G_NAV_GETNODERADIUS), - GEN_IMPORT(Nav_CheckBlockedEdges, G_NAV_CHECKBLOCKEDEDGES), - GEN_IMPORT(Nav_ClearCheckedNodes, G_NAV_CLEARCHECKEDNODES), - GEN_IMPORT(Nav_CheckedNode, G_NAV_CHECKEDNODE), - GEN_IMPORT(Nav_SetCheckedNode, G_NAV_SETCHECKEDNODE), - GEN_IMPORT(Nav_FlagAllNodes, G_NAV_FLAGALLNODES), - GEN_IMPORT(Nav_GetPathsCalculated, G_NAV_GETPATHSCALCULATED), - GEN_IMPORT(Nav_SetPathsCalculated, G_NAV_SETPATHSCALCULATED), - GEN_IMPORT(BotAllocateClient, G_BOT_ALLOCATE_CLIENT), - GEN_IMPORT(BotFreeClient, G_BOT_FREE_CLIENT), - GEN_IMPORT(BotLoadCharacter, BOTLIB_AI_LOAD_CHARACTER), - GEN_IMPORT(BotFreeCharacter, BOTLIB_AI_FREE_CHARACTER), - GEN_IMPORT(Characteristic_Float, BOTLIB_AI_CHARACTERISTIC_FLOAT), - GEN_IMPORT(Characteristic_BFloat, BOTLIB_AI_CHARACTERISTIC_BFLOAT), - GEN_IMPORT(Characteristic_Integer, BOTLIB_AI_CHARACTERISTIC_INTEGER), - GEN_IMPORT(Characteristic_BInteger, BOTLIB_AI_CHARACTERISTIC_BINTEGER), - GEN_IMPORT(Characteristic_String, BOTLIB_AI_CHARACTERISTIC_STRING), - GEN_IMPORT(BotAllocChatState, BOTLIB_AI_ALLOC_CHAT_STATE), - GEN_IMPORT(BotFreeChatState, BOTLIB_AI_FREE_CHAT_STATE), - GEN_IMPORT(BotQueueConsoleMessage, BOTLIB_AI_QUEUE_CONSOLE_MESSAGE), - GEN_IMPORT(BotRemoveConsoleMessage, BOTLIB_AI_REMOVE_CONSOLE_MESSAGE), - GEN_IMPORT(BotNextConsoleMessage, BOTLIB_AI_NEXT_CONSOLE_MESSAGE), - GEN_IMPORT(BotNumConsoleMessages, BOTLIB_AI_NUM_CONSOLE_MESSAGE), - GEN_IMPORT(BotInitialChat, BOTLIB_AI_INITIAL_CHAT), - GEN_IMPORT(BotReplyChat, BOTLIB_AI_REPLY_CHAT), - GEN_IMPORT(BotChatLength, BOTLIB_AI_CHAT_LENGTH), - GEN_IMPORT(BotEnterChat, BOTLIB_AI_ENTER_CHAT), - GEN_IMPORT(StringContains, BOTLIB_AI_STRING_CONTAINS), - GEN_IMPORT(BotFindMatch, BOTLIB_AI_FIND_MATCH), - GEN_IMPORT(BotMatchVariable, BOTLIB_AI_MATCH_VARIABLE), - GEN_IMPORT(UnifyWhiteSpaces, BOTLIB_AI_UNIFY_WHITE_SPACES), - GEN_IMPORT(BotReplaceSynonyms, BOTLIB_AI_REPLACE_SYNONYMS), - GEN_IMPORT(BotLoadChatFile, BOTLIB_AI_LOAD_CHAT_FILE), - GEN_IMPORT(BotSetChatGender, BOTLIB_AI_SET_CHAT_GENDER), - GEN_IMPORT(BotSetChatName, BOTLIB_AI_SET_CHAT_NAME), - GEN_IMPORT(BotResetGoalState, BOTLIB_AI_RESET_GOAL_STATE), - GEN_IMPORT(BotResetAvoidGoals, BOTLIB_AI_RESET_AVOID_GOALS), - GEN_IMPORT(BotPushGoal, BOTLIB_AI_PUSH_GOAL), - GEN_IMPORT(BotPopGoal, BOTLIB_AI_POP_GOAL), - GEN_IMPORT(BotEmptyGoalStack, BOTLIB_AI_EMPTY_GOAL_STACK), - GEN_IMPORT(BotDumpAvoidGoals, BOTLIB_AI_DUMP_AVOID_GOALS), - GEN_IMPORT(BotDumpGoalStack, BOTLIB_AI_DUMP_GOAL_STACK), - GEN_IMPORT(BotGoalName, BOTLIB_AI_GOAL_NAME), - GEN_IMPORT(BotGetTopGoal, BOTLIB_AI_GET_TOP_GOAL), - GEN_IMPORT(BotGetSecondGoal, BOTLIB_AI_GET_SECOND_GOAL), - GEN_IMPORT(BotChooseLTGItem, BOTLIB_AI_CHOOSE_LTG_ITEM), - GEN_IMPORT(BotChooseNBGItem, BOTLIB_AI_CHOOSE_NBG_ITEM), - GEN_IMPORT(BotTouchingGoal, BOTLIB_AI_TOUCHING_GOAL), - GEN_IMPORT(BotItemGoalInVisButNotVisible, BOTLIB_AI_ITEM_GOAL_IN_VIS_BUT_NOT_VISIBLE), - GEN_IMPORT(BotGetLevelItemGoal, BOTLIB_AI_GET_LEVEL_ITEM_GOAL), - GEN_IMPORT(BotAvoidGoalTime, BOTLIB_AI_AVOID_GOAL_TIME), - GEN_IMPORT(BotInitLevelItems, BOTLIB_AI_INIT_LEVEL_ITEMS), - GEN_IMPORT(BotUpdateEntityItems, BOTLIB_AI_UPDATE_ENTITY_ITEMS), - GEN_IMPORT(BotLoadItemWeights, BOTLIB_AI_LOAD_ITEM_WEIGHTS), - GEN_IMPORT(BotFreeItemWeights, BOTLIB_AI_FREE_ITEM_WEIGHTS), - GEN_IMPORT(BotSaveGoalFuzzyLogic, BOTLIB_AI_SAVE_GOAL_FUZZY_LOGIC), - GEN_IMPORT(BotAllocGoalState, BOTLIB_AI_ALLOC_GOAL_STATE), - GEN_IMPORT(BotFreeGoalState, BOTLIB_AI_FREE_GOAL_STATE), - GEN_IMPORT(BotResetMoveState, BOTLIB_AI_RESET_MOVE_STATE), - GEN_IMPORT(BotMoveToGoal, BOTLIB_AI_MOVE_TO_GOAL), - GEN_IMPORT(BotMoveInDirection, BOTLIB_AI_MOVE_IN_DIRECTION), - GEN_IMPORT(BotResetAvoidReach, BOTLIB_AI_RESET_AVOID_REACH), - GEN_IMPORT(BotResetLastAvoidReach, BOTLIB_AI_RESET_LAST_AVOID_REACH), - GEN_IMPORT(BotReachabilityArea, BOTLIB_AI_REACHABILITY_AREA), - GEN_IMPORT(BotMovementViewTarget, BOTLIB_AI_MOVEMENT_VIEW_TARGET), - GEN_IMPORT(BotAllocMoveState, BOTLIB_AI_ALLOC_MOVE_STATE), - GEN_IMPORT(BotFreeMoveState, BOTLIB_AI_FREE_MOVE_STATE), - GEN_IMPORT(BotInitMoveState, BOTLIB_AI_INIT_MOVE_STATE), - GEN_IMPORT(BotChooseBestFightWeapon, BOTLIB_AI_CHOOSE_BEST_FIGHT_WEAPON), - GEN_IMPORT(BotGetWeaponInfo, BOTLIB_AI_GET_WEAPON_INFO), - GEN_IMPORT(BotLoadWeaponWeights, BOTLIB_AI_LOAD_WEAPON_WEIGHTS), - GEN_IMPORT(BotAllocWeaponState, BOTLIB_AI_ALLOC_WEAPON_STATE), - GEN_IMPORT(BotFreeWeaponState, BOTLIB_AI_FREE_WEAPON_STATE), - GEN_IMPORT(BotResetWeaponState, BOTLIB_AI_RESET_WEAPON_STATE), - GEN_IMPORT(GeneticParentsAndChildSelection, BOTLIB_AI_GENETIC_PARENTS_AND_CHILD_SELECTION), - GEN_IMPORT(BotInterbreedGoalFuzzyLogic, BOTLIB_AI_INTERBREED_GOAL_FUZZY_LOGIC), - GEN_IMPORT(BotMutateGoalFuzzyLogic, BOTLIB_AI_MUTATE_GOAL_FUZZY_LOGIC), - GEN_IMPORT(BotGetNextCampSpotGoal, BOTLIB_AI_GET_NEXT_CAMP_SPOT_GOAL), - GEN_IMPORT(BotGetMapLocationGoal, BOTLIB_AI_GET_MAP_LOCATION_GOAL), - GEN_IMPORT(BotNumInitialChats, BOTLIB_AI_NUM_INITIAL_CHATS), - GEN_IMPORT(BotGetChatMessage, BOTLIB_AI_GET_CHAT_MESSAGE), - GEN_IMPORT(BotRemoveFromAvoidGoals, BOTLIB_AI_REMOVE_FROM_AVOID_GOALS), - GEN_IMPORT(BotPredictVisiblePosition, BOTLIB_AI_PREDICT_VISIBLE_POSITION), - GEN_IMPORT(BotSetAvoidGoalTime, BOTLIB_AI_SET_AVOID_GOAL_TIME), - GEN_IMPORT(BotAddAvoidSpot, BOTLIB_AI_ADD_AVOID_SPOT), - GEN_IMPORT(BotLibSetup, BOTLIB_SETUP), - GEN_IMPORT(BotLibShutdown, BOTLIB_SHUTDOWN), - GEN_IMPORT(BotLibVarSet, BOTLIB_LIBVAR_SET), - GEN_IMPORT(BotLibVarGet, BOTLIB_LIBVAR_GET), - GEN_IMPORT(BotLibDefine, BOTLIB_PC_ADD_GLOBAL_DEFINE), - GEN_IMPORT(BotLibStartFrame, BOTLIB_START_FRAME), - GEN_IMPORT(BotLibLoadMap, BOTLIB_LOAD_MAP), - GEN_IMPORT(BotLibUpdateEntity, BOTLIB_UPDATENTITY), - GEN_IMPORT(BotLibTest, BOTLIB_TEST), - GEN_IMPORT(BotGetSnapshotEntity, BOTLIB_GET_SNAPSHOT_ENTITY), - GEN_IMPORT(BotGetServerCommand, BOTLIB_GET_CONSOLE_MESSAGE), - GEN_IMPORT(BotUserCommand, BOTLIB_USER_COMMAND), - GEN_IMPORT(BotUpdateWaypoints, G_BOT_UPDATEWAYPOINTS), - GEN_IMPORT(BotCalculatePaths, G_BOT_CALCULATEPATHS), - GEN_IMPORT(AAS_EnableRoutingArea, BOTLIB_AAS_ENABLE_ROUTING_AREA), - GEN_IMPORT(AAS_BBoxAreas, BOTLIB_AAS_BBOX_AREAS), - GEN_IMPORT(AAS_AreaInfo, BOTLIB_AAS_AREA_INFO), - GEN_IMPORT(AAS_EntityInfo, BOTLIB_AAS_ENTITY_INFO), - GEN_IMPORT(AAS_Initialized, BOTLIB_AAS_INITIALIZED), - GEN_IMPORT(AAS_PresenceTypeBoundingBox, BOTLIB_AAS_PRESENCE_TYPE_BOUNDING_BOX), - GEN_IMPORT(AAS_Time, BOTLIB_AAS_TIME), - GEN_IMPORT(AAS_PointAreaNum, BOTLIB_AAS_POINT_AREA_NUM), - GEN_IMPORT(AAS_TraceAreas, BOTLIB_AAS_TRACE_AREAS), - GEN_IMPORT(AAS_PointContents, BOTLIB_AAS_POINT_CONTENTS), - GEN_IMPORT(AAS_NextBSPEntity, BOTLIB_AAS_NEXT_BSP_ENTITY), - GEN_IMPORT(AAS_ValueForBSPEpairKey, BOTLIB_AAS_VALUE_FOR_BSP_EPAIR_KEY), - GEN_IMPORT(AAS_VectorForBSPEpairKey, BOTLIB_AAS_VECTOR_FOR_BSP_EPAIR_KEY), - GEN_IMPORT(AAS_FloatForBSPEpairKey, BOTLIB_AAS_FLOAT_FOR_BSP_EPAIR_KEY), - GEN_IMPORT(AAS_IntForBSPEpairKey, BOTLIB_AAS_INT_FOR_BSP_EPAIR_KEY), - GEN_IMPORT(AAS_AreaReachability, BOTLIB_AAS_AREA_REACHABILITY), - GEN_IMPORT(AAS_AreaTravelTimeToGoalArea, BOTLIB_AAS_AREA_TRAVEL_TIME_TO_GOAL_AREA), - GEN_IMPORT(AAS_Swimming, BOTLIB_AAS_SWIMMING), - GEN_IMPORT(AAS_PredictClientMovement, BOTLIB_AAS_PREDICT_CLIENT_MOVEMENT), - GEN_IMPORT(AAS_AlternativeRouteGoals, BOTLIB_AAS_ALTERNATIVE_ROUTE_GOAL), - GEN_IMPORT(AAS_PredictRoute, BOTLIB_AAS_PREDICT_ROUTE), - GEN_IMPORT(AAS_PointReachabilityAreaIndex, BOTLIB_AAS_POINT_REACHABILITY_AREA_INDEX), - GEN_IMPORT(EA_Say, BOTLIB_EA_SAY), - GEN_IMPORT(EA_SayTeam, BOTLIB_EA_SAY_TEAM), - GEN_IMPORT(EA_Command, BOTLIB_EA_COMMAND), - GEN_IMPORT(EA_Action, BOTLIB_EA_ACTION), - GEN_IMPORT(EA_Gesture, BOTLIB_EA_GESTURE), - GEN_IMPORT(EA_Talk, BOTLIB_EA_TALK), - GEN_IMPORT(EA_Attack, BOTLIB_EA_ATTACK), - GEN_IMPORT(EA_Alt_Attack, BOTLIB_EA_ALT_ATTACK), - GEN_IMPORT(EA_ForcePower, BOTLIB_EA_FORCEPOWER), - GEN_IMPORT(EA_Use, BOTLIB_EA_USE), - GEN_IMPORT(EA_Respawn, BOTLIB_EA_RESPAWN), - GEN_IMPORT(EA_Crouch, BOTLIB_EA_CROUCH), - GEN_IMPORT(EA_MoveUp, BOTLIB_EA_MOVE_UP), - GEN_IMPORT(EA_MoveDown, BOTLIB_EA_MOVE_DOWN), - GEN_IMPORT(EA_MoveForward, BOTLIB_EA_MOVE_FORWARD), - GEN_IMPORT(EA_MoveBack, BOTLIB_EA_MOVE_BACK), - GEN_IMPORT(EA_MoveLeft, BOTLIB_EA_MOVE_LEFT), - GEN_IMPORT(EA_MoveRight, BOTLIB_EA_MOVE_RIGHT), - GEN_IMPORT(EA_SelectWeapon, BOTLIB_EA_SELECT_WEAPON), - GEN_IMPORT(EA_Jump, BOTLIB_EA_JUMP), - GEN_IMPORT(EA_DelayedJump, BOTLIB_EA_DELAYED_JUMP), - GEN_IMPORT(EA_Move, BOTLIB_EA_MOVE), - GEN_IMPORT(EA_View, BOTLIB_EA_VIEW), - GEN_IMPORT(EA_EndRegular, BOTLIB_EA_END_REGULAR), - GEN_IMPORT(EA_GetInput, BOTLIB_EA_GET_INPUT), - GEN_IMPORT(EA_ResetInput, BOTLIB_EA_RESET_INPUT), - GEN_IMPORT(PC_LoadSource, BOTLIB_PC_LOAD_SOURCE), - GEN_IMPORT(PC_FreeSource, BOTLIB_PC_FREE_SOURCE), - GEN_IMPORT(PC_ReadToken, BOTLIB_PC_READ_TOKEN), - GEN_IMPORT(PC_SourceFileAndLine, BOTLIB_PC_SOURCE_FILE_AND_LINE), - GEN_IMPORT(R_RegisterSkin, G_R_REGISTERSKIN), - GEN_IMPORT(SetActiveSubBSP, G_SET_ACTIVE_SUBBSP), - GEN_IMPORT(CM_RegisterTerrain, G_CM_REGISTER_TERRAIN), - GEN_IMPORT(RMG_Init, G_RMG_INIT), - GEN_IMPORT(G2API_ListModelBones, G_G2_LISTBONES), - GEN_IMPORT(G2API_ListModelSurfaces, G_G2_LISTSURFACES), - GEN_IMPORT(G2API_HaveWeGhoul2Models, G_G2_HAVEWEGHOULMODELS), - GEN_IMPORT(G2API_SetGhoul2ModelIndexes, G_G2_SETMODELS), - GEN_IMPORT(G2API_GetBoltMatrix, G_G2_GETBOLT), - GEN_IMPORT(G2API_GetBoltMatrix_NoReconstruct, G_G2_GETBOLT_NOREC), - GEN_IMPORT(G2API_GetBoltMatrix_NoRecNoRot, G_G2_GETBOLT_NOREC_NOROT), - GEN_IMPORT(G2API_InitGhoul2Model, G_G2_INITGHOUL2MODEL), - GEN_IMPORT(G2API_SetSkin, G_G2_SETSKIN), - GEN_IMPORT(G2API_Ghoul2Size, G_G2_SIZE), - GEN_IMPORT(G2API_AddBolt, G_G2_ADDBOLT), - GEN_IMPORT(G2API_SetBoltInfo, G_G2_SETBOLTINFO), - GEN_IMPORT(G2API_SetBoneAngles, G_G2_ANGLEOVERRIDE), - GEN_IMPORT(G2API_SetBoneAnim, G_G2_PLAYANIM), - GEN_IMPORT(G2API_GetBoneAnim, G_G2_GETBONEANIM), - GEN_IMPORT(G2API_GetGLAName, G_G2_GETGLANAME), - GEN_IMPORT(G2API_CopyGhoul2Instance, G_G2_COPYGHOUL2INSTANCE), - GEN_IMPORT(G2API_CopySpecificGhoul2Model, G_G2_COPYSPECIFICGHOUL2MODEL), - GEN_IMPORT(G2API_DuplicateGhoul2Instance, G_G2_DUPLICATEGHOUL2INSTANCE), - GEN_IMPORT(G2API_HasGhoul2ModelOnIndex, G_G2_HASGHOUL2MODELONINDEX), - GEN_IMPORT(G2API_RemoveGhoul2Model, G_G2_REMOVEGHOUL2MODEL), - GEN_IMPORT(G2API_RemoveGhoul2Models, G_G2_REMOVEGHOUL2MODELS), - GEN_IMPORT(G2API_CleanGhoul2Models, G_G2_CLEANMODELS), - GEN_IMPORT(G2API_CollisionDetect, G_G2_COLLISIONDETECT), - GEN_IMPORT(G2API_CollisionDetectCache, G_G2_COLLISIONDETECTCACHE), - GEN_IMPORT(G2API_SetRootSurface, G_G2_SETROOTSURFACE), - GEN_IMPORT(G2API_SetSurfaceOnOff, G_G2_SETSURFACEONOFF), - GEN_IMPORT(G2API_SetNewOrigin, G_G2_SETNEWORIGIN), - GEN_IMPORT(G2API_DoesBoneExist, G_G2_DOESBONEEXIST), - GEN_IMPORT(G2API_GetSurfaceRenderStatus, G_G2_GETSURFACERENDERSTATUS), - GEN_IMPORT(G2API_AbsurdSmoothing, G_G2_ABSURDSMOOTHING), - GEN_IMPORT(G2API_SetRagDoll, G_G2_SETRAGDOLL), - GEN_IMPORT(G2API_AnimateG2Models, G_G2_ANIMATEG2MODELS), - GEN_IMPORT(G2API_RagPCJConstraint, G_G2_RAGPCJCONSTRAINT), - GEN_IMPORT(G2API_RagPCJGradientSpeed, G_G2_RAGPCJGRADIENTSPEED), - GEN_IMPORT(G2API_RagEffectorGoal, G_G2_RAGEFFECTORGOAL), - GEN_IMPORT(G2API_GetRagBonePos, G_G2_GETRAGBONEPOS), - GEN_IMPORT(G2API_RagEffectorKick, G_G2_RAGEFFECTORKICK), - GEN_IMPORT(G2API_RagForceSolve, G_G2_RAGFORCESOLVE), - GEN_IMPORT(G2API_SetBoneIKState, G_G2_SETBONEIKSTATE), - GEN_IMPORT(G2API_IKMove, G_G2_IKMOVE), - GEN_IMPORT(G2API_RemoveBone, G_G2_REMOVEBONE), - GEN_IMPORT(G2API_AttachInstanceToEntNum, G_G2_ATTACHINSTANCETOENTNUM), - GEN_IMPORT(G2API_ClearAttachedInstance, G_G2_CLEARATTACHEDINSTANCE), - GEN_IMPORT(G2API_CleanEntAttachments, G_G2_CLEANENTATTACHMENTS), - GEN_IMPORT(G2API_OverrideServer, G_G2_OVERRIDESERVER), - GEN_IMPORT(G2API_GetSurfaceName, G_G2_GETSURFACENAME), - }; + static game_import_t qmm_import; // struct with lambdas that call QMM's vmMain function. this is given to the game engine - game_export_t qmm_export = { - GEN_EXPORT(InitGame, GAME_INIT), - GEN_EXPORT(ShutdownGame, GAME_SHUTDOWN), - GEN_EXPORT(ClientConnect, GAME_CLIENT_CONNECT), - GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), - GEN_EXPORT(ClientUserinfoChanged, GAME_CLIENT_USERINFO_CHANGED), - GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), - GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), - GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), - GEN_EXPORT(RunFrame, GAME_RUN_FRAME), - GEN_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND), - GEN_EXPORT(BotAIStartFrame, BOTAI_START_FRAME), - GEN_EXPORT(ROFF_NotetrackCallback, GAME_ROFF_NOTETRACK_CALLBACK), - GEN_EXPORT(SpawnRMGEntity, GAME_SPAWN_RMG_ENTITY), - GEN_EXPORT(ICARUS_PlaySound, GAME_ICARUS_PLAYSOUND), - GEN_EXPORT(ICARUS_Set, GAME_ICARUS_SET), - GEN_EXPORT(ICARUS_Lerp2Pos, GAME_ICARUS_LERP2POS), - GEN_EXPORT(ICARUS_Lerp2Origin, GAME_ICARUS_LERP2ORIGIN), - GEN_EXPORT(ICARUS_Lerp2Angles, GAME_ICARUS_LERP2ANGLES), - GEN_EXPORT(ICARUS_GetTag, GAME_ICARUS_GETTAG), - GEN_EXPORT(ICARUS_Lerp2Start, GAME_ICARUS_LERP2START), - GEN_EXPORT(ICARUS_Lerp2End, GAME_ICARUS_LERP2END), - GEN_EXPORT(ICARUS_Use, GAME_ICARUS_USE), - GEN_EXPORT(ICARUS_Kill, GAME_ICARUS_KILL), - GEN_EXPORT(ICARUS_Remove, GAME_ICARUS_REMOVE), - GEN_EXPORT(ICARUS_Play, GAME_ICARUS_PLAY), - GEN_EXPORT(ICARUS_GetFloat, GAME_ICARUS_GETFLOAT), - GEN_EXPORT(ICARUS_GetVector, GAME_ICARUS_GETVECTOR), - GEN_EXPORT(ICARUS_GetString, GAME_ICARUS_GETSTRING), - GEN_EXPORT(ICARUS_SoundIndex, GAME_ICARUS_SOUNDINDEX), - GEN_EXPORT(ICARUS_GetSetIDForString, GAME_ICARUS_GETSETIDFORSTRING), - GEN_EXPORT(NAV_ClearPathToPoint, GAME_NAV_CLEARPATHTOPOINT), - GEN_EXPORT(NPC_ClearLOS2, GAME_NAV_CLEARLOS), - GEN_EXPORT(NAVNEW_ClearPathBetweenPoints, GAME_NAV_CLEARPATHBETWEENPOINTS), - GEN_EXPORT(NAV_CheckNodeFailedForEnt, GAME_NAV_CHECKNODEFAILEDFORENT), - GEN_EXPORT(NAV_EntIsUnlockedDoor, GAME_NAV_ENTISUNLOCKEDDOOR), - GEN_EXPORT(NAV_EntIsDoor, GAME_NAV_ENTISDOOR), - GEN_EXPORT(NAV_EntIsBreakable, GAME_NAV_ENTISBREAKABLE), - GEN_EXPORT(NAV_EntIsRemovableUsable, GAME_NAV_ENTISREMOVABLEUSABLE), - GEN_EXPORT(NAV_FindCombatPointWaypoints, GAME_NAV_FINDCOMBATPOINTWAYPOINTS), - GEN_EXPORT(BG_GetItemIndexByTag, GAME_GETITEMINDEXBYTAG), - }; + static game_export_t qmm_export; }; GEN_GAME_OBJ(JAMP); @@ -1345,3 +997,369 @@ const char* JAMP_GameSupport::ModMsgName(intptr_t cmd) { return "unknown"; } } + + +intptr_t JAMP_GameSupport::orig_apiversion = 0; + + +game_import_t JAMP_GameSupport::orig_import = {}; + + +game_export_t* JAMP_GameSupport::orig_export = nullptr; + + +game_import_t JAMP_GameSupport::qmm_import = { + GEN_IMPORT(Print, G_PRINT), + GEN_IMPORT(Error, G_ERROR), + GEN_IMPORT(Milliseconds, G_MILLISECONDS), + GEN_IMPORT(PrecisionTimerStart, G_PRECISIONTIMER_START), + GEN_IMPORT(PrecisionTimerEnd, G_PRECISIONTIMER_END), + GEN_IMPORT(SV_RegisterSharedMemory, G_SET_SHARED_BUFFER), + GEN_IMPORT(RealTime, G_REAL_TIME), + GEN_IMPORT(TrueMalloc, G_TRUEMALLOC), + GEN_IMPORT(TrueFree, G_TRUEFREE), + GEN_IMPORT(SnapVector, G_SNAPVECTOR), + GEN_IMPORT(Cvar_Register, G_CVAR_REGISTER), + GEN_IMPORT(Cvar_Set, G_CVAR_SET), + GEN_IMPORT(Cvar_Update, G_CVAR_UPDATE), + GEN_IMPORT(Cvar_VariableIntegerValue, G_CVAR_VARIABLE_INTEGER_VALUE), + GEN_IMPORT(Cvar_VariableStringBuffer, G_CVAR_VARIABLE_STRING_BUFFER), + GEN_IMPORT(Argc, G_ARGC), + GEN_IMPORT(Argv, G_ARGV), + GEN_IMPORT(FS_Close, G_FS_FCLOSE_FILE), + GEN_IMPORT(FS_GetFileList, G_FS_GETFILELIST), + GEN_IMPORT(FS_Open, G_FS_FOPEN_FILE), + GEN_IMPORT(FS_Read, G_FS_READ), + GEN_IMPORT(FS_Write, G_FS_WRITE), + GEN_IMPORT(AdjustAreaPortalState, G_ADJUST_AREA_PORTAL_STATE), + GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), + GEN_IMPORT(DebugPolygonCreate, G_DEBUG_POLYGON_CREATE), + GEN_IMPORT(DebugPolygonDelete, G_DEBUG_POLYGON_DELETE), + GEN_IMPORT(DropClient, G_DROP_CLIENT), + GEN_IMPORT(EntitiesInBox, G_ENTITIES_IN_BOX), + GEN_IMPORT(EntityContact, G_ENTITY_CONTACT), + GEN_IMPORT(GetConfigstring, G_GET_CONFIGSTRING), + GEN_IMPORT(GetEntityToken, G_GET_ENTITY_TOKEN), + GEN_IMPORT(GetServerinfo, G_GET_SERVERINFO), + GEN_IMPORT(GetUsercmd, G_GET_USERCMD), + GEN_IMPORT(GetUserinfo, G_GET_USERINFO), + GEN_IMPORT(InPVS, G_IN_PVS), + GEN_IMPORT(InPVSIgnorePortals, G_IN_PVS_IGNORE_PORTALS), + GEN_IMPORT(LinkEntity, G_LINKENTITY), + GEN_IMPORT(LocateGameData, G_LOCATE_GAME_DATA), + GEN_IMPORT(PointContents, G_POINT_CONTENTS), + GEN_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND), + GEN_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND), + GEN_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL), + GEN_IMPORT(SetConfigstring, G_SET_CONFIGSTRING), + GEN_IMPORT(SetServerCull, G_SET_SERVER_CULL), + GEN_IMPORT(SetUserinfo, G_SET_USERINFO), + GEN_IMPORT(SiegePersSet, G_SIEGEPERSSET), + GEN_IMPORT(SiegePersGet, G_SIEGEPERSGET), + GEN_IMPORT(Trace, G_TRACE), + GEN_IMPORT(UnlinkEntity, G_UNLINKENTITY), + GEN_IMPORT(ROFF_Clean, G_ROFF_CLEAN), + GEN_IMPORT(ROFF_UpdateEntities, G_ROFF_UPDATE_ENTITIES), + GEN_IMPORT(ROFF_Cache, G_ROFF_CACHE), + GEN_IMPORT(ROFF_Play, G_ROFF_PLAY), + GEN_IMPORT(ROFF_Purge_Ent, G_ROFF_PURGE_ENT), + GEN_IMPORT(ICARUS_RunScript, G_ICARUS_RUNSCRIPT), + GEN_IMPORT(ICARUS_RegisterScript, G_ICARUS_REGISTERSCRIPT), + GEN_IMPORT(ICARUS_Init, G_ICARUS_INIT), + GEN_IMPORT(ICARUS_ValidEnt, G_ICARUS_VALIDENT), + GEN_IMPORT(ICARUS_IsInitialized, G_ICARUS_ISINITIALIZED), + GEN_IMPORT(ICARUS_MaintainTaskManager, G_ICARUS_MAINTAINTASKMANAGER), + GEN_IMPORT(ICARUS_IsRunning, G_ICARUS_ISRUNNING), + GEN_IMPORT(ICARUS_TaskIDPending, G_ICARUS_TASKIDPENDING), + GEN_IMPORT(ICARUS_InitEnt, G_ICARUS_INITENT), + GEN_IMPORT(ICARUS_FreeEnt, G_ICARUS_FREEENT), + GEN_IMPORT(ICARUS_AssociateEnt, G_ICARUS_ASSOCIATEENT), + GEN_IMPORT(ICARUS_Shutdown, G_ICARUS_SHUTDOWN), + GEN_IMPORT(ICARUS_TaskIDSet, G_ICARUS_TASKIDSET), + GEN_IMPORT(ICARUS_TaskIDComplete, G_ICARUS_TASKIDCOMPLETE), + GEN_IMPORT(ICARUS_SetVar, G_ICARUS_SETVAR), + GEN_IMPORT(ICARUS_VariableDeclared, G_ICARUS_VARIABLEDECLARED), + GEN_IMPORT(ICARUS_GetFloatVariable, G_ICARUS_GETFLOATVARIABLE), + GEN_IMPORT(ICARUS_GetStringVariable, G_ICARUS_GETSTRINGVARIABLE), + GEN_IMPORT(ICARUS_GetVectorVariable, G_ICARUS_GETVECTORVARIABLE), + GEN_IMPORT(Nav_Init, G_NAV_INIT), + GEN_IMPORT(Nav_Free, G_NAV_FREE), + GEN_IMPORT(Nav_Load, G_NAV_LOAD), + GEN_IMPORT(Nav_Save, G_NAV_SAVE), + GEN_IMPORT(Nav_AddRawPoint, G_NAV_ADDRAWPOINT), + GEN_IMPORT(Nav_CalculatePaths, G_NAV_CALCULATEPATHS), + GEN_IMPORT(Nav_HardConnect, G_NAV_HARDCONNECT), + GEN_IMPORT(Nav_ShowNodes, G_NAV_SHOWNODES), + GEN_IMPORT(Nav_ShowEdges, G_NAV_SHOWEDGES), + GEN_IMPORT(Nav_ShowPath, G_NAV_SHOWPATH), + GEN_IMPORT(Nav_GetNearestNode, G_NAV_GETNEARESTNODE), + GEN_IMPORT(Nav_GetBestNode, G_NAV_GETBESTNODE), + GEN_IMPORT(Nav_GetNodePosition, G_NAV_GETNODEPOSITION), + GEN_IMPORT(Nav_GetNodeNumEdges, G_NAV_GETNODENUMEDGES), + GEN_IMPORT(Nav_GetNodeEdge, G_NAV_GETNODEEDGE), + GEN_IMPORT(Nav_GetNumNodes, G_NAV_GETNUMNODES), + GEN_IMPORT(Nav_Connected, G_NAV_CONNECTED), + GEN_IMPORT(Nav_GetPathCost, G_NAV_GETPATHCOST), + GEN_IMPORT(Nav_GetEdgeCost, G_NAV_GETEDGECOST), + GEN_IMPORT(Nav_GetProjectedNode, G_NAV_GETPROJECTEDNODE), + GEN_IMPORT(Nav_CheckFailedNodes, G_NAV_CHECKFAILEDNODES), + GEN_IMPORT(Nav_AddFailedNode, G_NAV_ADDFAILEDNODE), + GEN_IMPORT(Nav_NodeFailed, G_NAV_NODEFAILED), + GEN_IMPORT(Nav_NodesAreNeighbors, G_NAV_NODESARENEIGHBORS), + GEN_IMPORT(Nav_ClearFailedEdge, G_NAV_CLEARFAILEDEDGE), + GEN_IMPORT(Nav_ClearAllFailedEdges, G_NAV_CLEARALLFAILEDEDGES), + GEN_IMPORT(Nav_EdgeFailed, G_NAV_EDGEFAILED), + GEN_IMPORT(Nav_AddFailedEdge, G_NAV_ADDFAILEDEDGE), + GEN_IMPORT(Nav_CheckFailedEdge, G_NAV_CHECKFAILEDEDGE), + GEN_IMPORT(Nav_CheckAllFailedEdges, G_NAV_CHECKALLFAILEDEDGES), + GEN_IMPORT(Nav_RouteBlocked, G_NAV_ROUTEBLOCKED), + GEN_IMPORT(Nav_GetBestNodeAltRoute, G_NAV_GETBESTNODEALTROUTE), + GEN_IMPORT(Nav_GetBestNodeAltRoute2, G_NAV_GETBESTNODEALT2), + GEN_IMPORT(Nav_GetBestPathBetweenEnts, G_NAV_GETBESTPATHBETWEENENTS), + GEN_IMPORT(Nav_GetNodeRadius, G_NAV_GETNODERADIUS), + GEN_IMPORT(Nav_CheckBlockedEdges, G_NAV_CHECKBLOCKEDEDGES), + GEN_IMPORT(Nav_ClearCheckedNodes, G_NAV_CLEARCHECKEDNODES), + GEN_IMPORT(Nav_CheckedNode, G_NAV_CHECKEDNODE), + GEN_IMPORT(Nav_SetCheckedNode, G_NAV_SETCHECKEDNODE), + GEN_IMPORT(Nav_FlagAllNodes, G_NAV_FLAGALLNODES), + GEN_IMPORT(Nav_GetPathsCalculated, G_NAV_GETPATHSCALCULATED), + GEN_IMPORT(Nav_SetPathsCalculated, G_NAV_SETPATHSCALCULATED), + GEN_IMPORT(BotAllocateClient, G_BOT_ALLOCATE_CLIENT), + GEN_IMPORT(BotFreeClient, G_BOT_FREE_CLIENT), + GEN_IMPORT(BotLoadCharacter, BOTLIB_AI_LOAD_CHARACTER), + GEN_IMPORT(BotFreeCharacter, BOTLIB_AI_FREE_CHARACTER), + GEN_IMPORT(Characteristic_Float, BOTLIB_AI_CHARACTERISTIC_FLOAT), + GEN_IMPORT(Characteristic_BFloat, BOTLIB_AI_CHARACTERISTIC_BFLOAT), + GEN_IMPORT(Characteristic_Integer, BOTLIB_AI_CHARACTERISTIC_INTEGER), + GEN_IMPORT(Characteristic_BInteger, BOTLIB_AI_CHARACTERISTIC_BINTEGER), + GEN_IMPORT(Characteristic_String, BOTLIB_AI_CHARACTERISTIC_STRING), + GEN_IMPORT(BotAllocChatState, BOTLIB_AI_ALLOC_CHAT_STATE), + GEN_IMPORT(BotFreeChatState, BOTLIB_AI_FREE_CHAT_STATE), + GEN_IMPORT(BotQueueConsoleMessage, BOTLIB_AI_QUEUE_CONSOLE_MESSAGE), + GEN_IMPORT(BotRemoveConsoleMessage, BOTLIB_AI_REMOVE_CONSOLE_MESSAGE), + GEN_IMPORT(BotNextConsoleMessage, BOTLIB_AI_NEXT_CONSOLE_MESSAGE), + GEN_IMPORT(BotNumConsoleMessages, BOTLIB_AI_NUM_CONSOLE_MESSAGE), + GEN_IMPORT(BotInitialChat, BOTLIB_AI_INITIAL_CHAT), + GEN_IMPORT(BotReplyChat, BOTLIB_AI_REPLY_CHAT), + GEN_IMPORT(BotChatLength, BOTLIB_AI_CHAT_LENGTH), + GEN_IMPORT(BotEnterChat, BOTLIB_AI_ENTER_CHAT), + GEN_IMPORT(StringContains, BOTLIB_AI_STRING_CONTAINS), + GEN_IMPORT(BotFindMatch, BOTLIB_AI_FIND_MATCH), + GEN_IMPORT(BotMatchVariable, BOTLIB_AI_MATCH_VARIABLE), + GEN_IMPORT(UnifyWhiteSpaces, BOTLIB_AI_UNIFY_WHITE_SPACES), + GEN_IMPORT(BotReplaceSynonyms, BOTLIB_AI_REPLACE_SYNONYMS), + GEN_IMPORT(BotLoadChatFile, BOTLIB_AI_LOAD_CHAT_FILE), + GEN_IMPORT(BotSetChatGender, BOTLIB_AI_SET_CHAT_GENDER), + GEN_IMPORT(BotSetChatName, BOTLIB_AI_SET_CHAT_NAME), + GEN_IMPORT(BotResetGoalState, BOTLIB_AI_RESET_GOAL_STATE), + GEN_IMPORT(BotResetAvoidGoals, BOTLIB_AI_RESET_AVOID_GOALS), + GEN_IMPORT(BotPushGoal, BOTLIB_AI_PUSH_GOAL), + GEN_IMPORT(BotPopGoal, BOTLIB_AI_POP_GOAL), + GEN_IMPORT(BotEmptyGoalStack, BOTLIB_AI_EMPTY_GOAL_STACK), + GEN_IMPORT(BotDumpAvoidGoals, BOTLIB_AI_DUMP_AVOID_GOALS), + GEN_IMPORT(BotDumpGoalStack, BOTLIB_AI_DUMP_GOAL_STACK), + GEN_IMPORT(BotGoalName, BOTLIB_AI_GOAL_NAME), + GEN_IMPORT(BotGetTopGoal, BOTLIB_AI_GET_TOP_GOAL), + GEN_IMPORT(BotGetSecondGoal, BOTLIB_AI_GET_SECOND_GOAL), + GEN_IMPORT(BotChooseLTGItem, BOTLIB_AI_CHOOSE_LTG_ITEM), + GEN_IMPORT(BotChooseNBGItem, BOTLIB_AI_CHOOSE_NBG_ITEM), + GEN_IMPORT(BotTouchingGoal, BOTLIB_AI_TOUCHING_GOAL), + GEN_IMPORT(BotItemGoalInVisButNotVisible, BOTLIB_AI_ITEM_GOAL_IN_VIS_BUT_NOT_VISIBLE), + GEN_IMPORT(BotGetLevelItemGoal, BOTLIB_AI_GET_LEVEL_ITEM_GOAL), + GEN_IMPORT(BotAvoidGoalTime, BOTLIB_AI_AVOID_GOAL_TIME), + GEN_IMPORT(BotInitLevelItems, BOTLIB_AI_INIT_LEVEL_ITEMS), + GEN_IMPORT(BotUpdateEntityItems, BOTLIB_AI_UPDATE_ENTITY_ITEMS), + GEN_IMPORT(BotLoadItemWeights, BOTLIB_AI_LOAD_ITEM_WEIGHTS), + GEN_IMPORT(BotFreeItemWeights, BOTLIB_AI_FREE_ITEM_WEIGHTS), + GEN_IMPORT(BotSaveGoalFuzzyLogic, BOTLIB_AI_SAVE_GOAL_FUZZY_LOGIC), + GEN_IMPORT(BotAllocGoalState, BOTLIB_AI_ALLOC_GOAL_STATE), + GEN_IMPORT(BotFreeGoalState, BOTLIB_AI_FREE_GOAL_STATE), + GEN_IMPORT(BotResetMoveState, BOTLIB_AI_RESET_MOVE_STATE), + GEN_IMPORT(BotMoveToGoal, BOTLIB_AI_MOVE_TO_GOAL), + GEN_IMPORT(BotMoveInDirection, BOTLIB_AI_MOVE_IN_DIRECTION), + GEN_IMPORT(BotResetAvoidReach, BOTLIB_AI_RESET_AVOID_REACH), + GEN_IMPORT(BotResetLastAvoidReach, BOTLIB_AI_RESET_LAST_AVOID_REACH), + GEN_IMPORT(BotReachabilityArea, BOTLIB_AI_REACHABILITY_AREA), + GEN_IMPORT(BotMovementViewTarget, BOTLIB_AI_MOVEMENT_VIEW_TARGET), + GEN_IMPORT(BotAllocMoveState, BOTLIB_AI_ALLOC_MOVE_STATE), + GEN_IMPORT(BotFreeMoveState, BOTLIB_AI_FREE_MOVE_STATE), + GEN_IMPORT(BotInitMoveState, BOTLIB_AI_INIT_MOVE_STATE), + GEN_IMPORT(BotChooseBestFightWeapon, BOTLIB_AI_CHOOSE_BEST_FIGHT_WEAPON), + GEN_IMPORT(BotGetWeaponInfo, BOTLIB_AI_GET_WEAPON_INFO), + GEN_IMPORT(BotLoadWeaponWeights, BOTLIB_AI_LOAD_WEAPON_WEIGHTS), + GEN_IMPORT(BotAllocWeaponState, BOTLIB_AI_ALLOC_WEAPON_STATE), + GEN_IMPORT(BotFreeWeaponState, BOTLIB_AI_FREE_WEAPON_STATE), + GEN_IMPORT(BotResetWeaponState, BOTLIB_AI_RESET_WEAPON_STATE), + GEN_IMPORT(GeneticParentsAndChildSelection, BOTLIB_AI_GENETIC_PARENTS_AND_CHILD_SELECTION), + GEN_IMPORT(BotInterbreedGoalFuzzyLogic, BOTLIB_AI_INTERBREED_GOAL_FUZZY_LOGIC), + GEN_IMPORT(BotMutateGoalFuzzyLogic, BOTLIB_AI_MUTATE_GOAL_FUZZY_LOGIC), + GEN_IMPORT(BotGetNextCampSpotGoal, BOTLIB_AI_GET_NEXT_CAMP_SPOT_GOAL), + GEN_IMPORT(BotGetMapLocationGoal, BOTLIB_AI_GET_MAP_LOCATION_GOAL), + GEN_IMPORT(BotNumInitialChats, BOTLIB_AI_NUM_INITIAL_CHATS), + GEN_IMPORT(BotGetChatMessage, BOTLIB_AI_GET_CHAT_MESSAGE), + GEN_IMPORT(BotRemoveFromAvoidGoals, BOTLIB_AI_REMOVE_FROM_AVOID_GOALS), + GEN_IMPORT(BotPredictVisiblePosition, BOTLIB_AI_PREDICT_VISIBLE_POSITION), + GEN_IMPORT(BotSetAvoidGoalTime, BOTLIB_AI_SET_AVOID_GOAL_TIME), + GEN_IMPORT(BotAddAvoidSpot, BOTLIB_AI_ADD_AVOID_SPOT), + GEN_IMPORT(BotLibSetup, BOTLIB_SETUP), + GEN_IMPORT(BotLibShutdown, BOTLIB_SHUTDOWN), + GEN_IMPORT(BotLibVarSet, BOTLIB_LIBVAR_SET), + GEN_IMPORT(BotLibVarGet, BOTLIB_LIBVAR_GET), + GEN_IMPORT(BotLibDefine, BOTLIB_PC_ADD_GLOBAL_DEFINE), + GEN_IMPORT(BotLibStartFrame, BOTLIB_START_FRAME), + GEN_IMPORT(BotLibLoadMap, BOTLIB_LOAD_MAP), + GEN_IMPORT(BotLibUpdateEntity, BOTLIB_UPDATENTITY), + GEN_IMPORT(BotLibTest, BOTLIB_TEST), + GEN_IMPORT(BotGetSnapshotEntity, BOTLIB_GET_SNAPSHOT_ENTITY), + GEN_IMPORT(BotGetServerCommand, BOTLIB_GET_CONSOLE_MESSAGE), + GEN_IMPORT(BotUserCommand, BOTLIB_USER_COMMAND), + GEN_IMPORT(BotUpdateWaypoints, G_BOT_UPDATEWAYPOINTS), + GEN_IMPORT(BotCalculatePaths, G_BOT_CALCULATEPATHS), + GEN_IMPORT(AAS_EnableRoutingArea, BOTLIB_AAS_ENABLE_ROUTING_AREA), + GEN_IMPORT(AAS_BBoxAreas, BOTLIB_AAS_BBOX_AREAS), + GEN_IMPORT(AAS_AreaInfo, BOTLIB_AAS_AREA_INFO), + GEN_IMPORT(AAS_EntityInfo, BOTLIB_AAS_ENTITY_INFO), + GEN_IMPORT(AAS_Initialized, BOTLIB_AAS_INITIALIZED), + GEN_IMPORT(AAS_PresenceTypeBoundingBox, BOTLIB_AAS_PRESENCE_TYPE_BOUNDING_BOX), + GEN_IMPORT(AAS_Time, BOTLIB_AAS_TIME), + GEN_IMPORT(AAS_PointAreaNum, BOTLIB_AAS_POINT_AREA_NUM), + GEN_IMPORT(AAS_TraceAreas, BOTLIB_AAS_TRACE_AREAS), + GEN_IMPORT(AAS_PointContents, BOTLIB_AAS_POINT_CONTENTS), + GEN_IMPORT(AAS_NextBSPEntity, BOTLIB_AAS_NEXT_BSP_ENTITY), + GEN_IMPORT(AAS_ValueForBSPEpairKey, BOTLIB_AAS_VALUE_FOR_BSP_EPAIR_KEY), + GEN_IMPORT(AAS_VectorForBSPEpairKey, BOTLIB_AAS_VECTOR_FOR_BSP_EPAIR_KEY), + GEN_IMPORT(AAS_FloatForBSPEpairKey, BOTLIB_AAS_FLOAT_FOR_BSP_EPAIR_KEY), + GEN_IMPORT(AAS_IntForBSPEpairKey, BOTLIB_AAS_INT_FOR_BSP_EPAIR_KEY), + GEN_IMPORT(AAS_AreaReachability, BOTLIB_AAS_AREA_REACHABILITY), + GEN_IMPORT(AAS_AreaTravelTimeToGoalArea, BOTLIB_AAS_AREA_TRAVEL_TIME_TO_GOAL_AREA), + GEN_IMPORT(AAS_Swimming, BOTLIB_AAS_SWIMMING), + GEN_IMPORT(AAS_PredictClientMovement, BOTLIB_AAS_PREDICT_CLIENT_MOVEMENT), + GEN_IMPORT(AAS_AlternativeRouteGoals, BOTLIB_AAS_ALTERNATIVE_ROUTE_GOAL), + GEN_IMPORT(AAS_PredictRoute, BOTLIB_AAS_PREDICT_ROUTE), + GEN_IMPORT(AAS_PointReachabilityAreaIndex, BOTLIB_AAS_POINT_REACHABILITY_AREA_INDEX), + GEN_IMPORT(EA_Say, BOTLIB_EA_SAY), + GEN_IMPORT(EA_SayTeam, BOTLIB_EA_SAY_TEAM), + GEN_IMPORT(EA_Command, BOTLIB_EA_COMMAND), + GEN_IMPORT(EA_Action, BOTLIB_EA_ACTION), + GEN_IMPORT(EA_Gesture, BOTLIB_EA_GESTURE), + GEN_IMPORT(EA_Talk, BOTLIB_EA_TALK), + GEN_IMPORT(EA_Attack, BOTLIB_EA_ATTACK), + GEN_IMPORT(EA_Alt_Attack, BOTLIB_EA_ALT_ATTACK), + GEN_IMPORT(EA_ForcePower, BOTLIB_EA_FORCEPOWER), + GEN_IMPORT(EA_Use, BOTLIB_EA_USE), + GEN_IMPORT(EA_Respawn, BOTLIB_EA_RESPAWN), + GEN_IMPORT(EA_Crouch, BOTLIB_EA_CROUCH), + GEN_IMPORT(EA_MoveUp, BOTLIB_EA_MOVE_UP), + GEN_IMPORT(EA_MoveDown, BOTLIB_EA_MOVE_DOWN), + GEN_IMPORT(EA_MoveForward, BOTLIB_EA_MOVE_FORWARD), + GEN_IMPORT(EA_MoveBack, BOTLIB_EA_MOVE_BACK), + GEN_IMPORT(EA_MoveLeft, BOTLIB_EA_MOVE_LEFT), + GEN_IMPORT(EA_MoveRight, BOTLIB_EA_MOVE_RIGHT), + GEN_IMPORT(EA_SelectWeapon, BOTLIB_EA_SELECT_WEAPON), + GEN_IMPORT(EA_Jump, BOTLIB_EA_JUMP), + GEN_IMPORT(EA_DelayedJump, BOTLIB_EA_DELAYED_JUMP), + GEN_IMPORT(EA_Move, BOTLIB_EA_MOVE), + GEN_IMPORT(EA_View, BOTLIB_EA_VIEW), + GEN_IMPORT(EA_EndRegular, BOTLIB_EA_END_REGULAR), + GEN_IMPORT(EA_GetInput, BOTLIB_EA_GET_INPUT), + GEN_IMPORT(EA_ResetInput, BOTLIB_EA_RESET_INPUT), + GEN_IMPORT(PC_LoadSource, BOTLIB_PC_LOAD_SOURCE), + GEN_IMPORT(PC_FreeSource, BOTLIB_PC_FREE_SOURCE), + GEN_IMPORT(PC_ReadToken, BOTLIB_PC_READ_TOKEN), + GEN_IMPORT(PC_SourceFileAndLine, BOTLIB_PC_SOURCE_FILE_AND_LINE), + GEN_IMPORT(R_RegisterSkin, G_R_REGISTERSKIN), + GEN_IMPORT(SetActiveSubBSP, G_SET_ACTIVE_SUBBSP), + GEN_IMPORT(CM_RegisterTerrain, G_CM_REGISTER_TERRAIN), + GEN_IMPORT(RMG_Init, G_RMG_INIT), + GEN_IMPORT(G2API_ListModelBones, G_G2_LISTBONES), + GEN_IMPORT(G2API_ListModelSurfaces, G_G2_LISTSURFACES), + GEN_IMPORT(G2API_HaveWeGhoul2Models, G_G2_HAVEWEGHOULMODELS), + GEN_IMPORT(G2API_SetGhoul2ModelIndexes, G_G2_SETMODELS), + GEN_IMPORT(G2API_GetBoltMatrix, G_G2_GETBOLT), + GEN_IMPORT(G2API_GetBoltMatrix_NoReconstruct, G_G2_GETBOLT_NOREC), + GEN_IMPORT(G2API_GetBoltMatrix_NoRecNoRot, G_G2_GETBOLT_NOREC_NOROT), + GEN_IMPORT(G2API_InitGhoul2Model, G_G2_INITGHOUL2MODEL), + GEN_IMPORT(G2API_SetSkin, G_G2_SETSKIN), + GEN_IMPORT(G2API_Ghoul2Size, G_G2_SIZE), + GEN_IMPORT(G2API_AddBolt, G_G2_ADDBOLT), + GEN_IMPORT(G2API_SetBoltInfo, G_G2_SETBOLTINFO), + GEN_IMPORT(G2API_SetBoneAngles, G_G2_ANGLEOVERRIDE), + GEN_IMPORT(G2API_SetBoneAnim, G_G2_PLAYANIM), + GEN_IMPORT(G2API_GetBoneAnim, G_G2_GETBONEANIM), + GEN_IMPORT(G2API_GetGLAName, G_G2_GETGLANAME), + GEN_IMPORT(G2API_CopyGhoul2Instance, G_G2_COPYGHOUL2INSTANCE), + GEN_IMPORT(G2API_CopySpecificGhoul2Model, G_G2_COPYSPECIFICGHOUL2MODEL), + GEN_IMPORT(G2API_DuplicateGhoul2Instance, G_G2_DUPLICATEGHOUL2INSTANCE), + GEN_IMPORT(G2API_HasGhoul2ModelOnIndex, G_G2_HASGHOUL2MODELONINDEX), + GEN_IMPORT(G2API_RemoveGhoul2Model, G_G2_REMOVEGHOUL2MODEL), + GEN_IMPORT(G2API_RemoveGhoul2Models, G_G2_REMOVEGHOUL2MODELS), + GEN_IMPORT(G2API_CleanGhoul2Models, G_G2_CLEANMODELS), + GEN_IMPORT(G2API_CollisionDetect, G_G2_COLLISIONDETECT), + GEN_IMPORT(G2API_CollisionDetectCache, G_G2_COLLISIONDETECTCACHE), + GEN_IMPORT(G2API_SetRootSurface, G_G2_SETROOTSURFACE), + GEN_IMPORT(G2API_SetSurfaceOnOff, G_G2_SETSURFACEONOFF), + GEN_IMPORT(G2API_SetNewOrigin, G_G2_SETNEWORIGIN), + GEN_IMPORT(G2API_DoesBoneExist, G_G2_DOESBONEEXIST), + GEN_IMPORT(G2API_GetSurfaceRenderStatus, G_G2_GETSURFACERENDERSTATUS), + GEN_IMPORT(G2API_AbsurdSmoothing, G_G2_ABSURDSMOOTHING), + GEN_IMPORT(G2API_SetRagDoll, G_G2_SETRAGDOLL), + GEN_IMPORT(G2API_AnimateG2Models, G_G2_ANIMATEG2MODELS), + GEN_IMPORT(G2API_RagPCJConstraint, G_G2_RAGPCJCONSTRAINT), + GEN_IMPORT(G2API_RagPCJGradientSpeed, G_G2_RAGPCJGRADIENTSPEED), + GEN_IMPORT(G2API_RagEffectorGoal, G_G2_RAGEFFECTORGOAL), + GEN_IMPORT(G2API_GetRagBonePos, G_G2_GETRAGBONEPOS), + GEN_IMPORT(G2API_RagEffectorKick, G_G2_RAGEFFECTORKICK), + GEN_IMPORT(G2API_RagForceSolve, G_G2_RAGFORCESOLVE), + GEN_IMPORT(G2API_SetBoneIKState, G_G2_SETBONEIKSTATE), + GEN_IMPORT(G2API_IKMove, G_G2_IKMOVE), + GEN_IMPORT(G2API_RemoveBone, G_G2_REMOVEBONE), + GEN_IMPORT(G2API_AttachInstanceToEntNum, G_G2_ATTACHINSTANCETOENTNUM), + GEN_IMPORT(G2API_ClearAttachedInstance, G_G2_CLEARATTACHEDINSTANCE), + GEN_IMPORT(G2API_CleanEntAttachments, G_G2_CLEANENTATTACHMENTS), + GEN_IMPORT(G2API_OverrideServer, G_G2_OVERRIDESERVER), + GEN_IMPORT(G2API_GetSurfaceName, G_G2_GETSURFACENAME), +}; + + +game_export_t JAMP_GameSupport::qmm_export = { + GEN_EXPORT(InitGame, GAME_INIT), + GEN_EXPORT(ShutdownGame, GAME_SHUTDOWN), + GEN_EXPORT(ClientConnect, GAME_CLIENT_CONNECT), + GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), + GEN_EXPORT(ClientUserinfoChanged, GAME_CLIENT_USERINFO_CHANGED), + GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), + GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), + GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), + GEN_EXPORT(RunFrame, GAME_RUN_FRAME), + GEN_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND), + GEN_EXPORT(BotAIStartFrame, BOTAI_START_FRAME), + GEN_EXPORT(ROFF_NotetrackCallback, GAME_ROFF_NOTETRACK_CALLBACK), + GEN_EXPORT(SpawnRMGEntity, GAME_SPAWN_RMG_ENTITY), + GEN_EXPORT(ICARUS_PlaySound, GAME_ICARUS_PLAYSOUND), + GEN_EXPORT(ICARUS_Set, GAME_ICARUS_SET), + GEN_EXPORT(ICARUS_Lerp2Pos, GAME_ICARUS_LERP2POS), + GEN_EXPORT(ICARUS_Lerp2Origin, GAME_ICARUS_LERP2ORIGIN), + GEN_EXPORT(ICARUS_Lerp2Angles, GAME_ICARUS_LERP2ANGLES), + GEN_EXPORT(ICARUS_GetTag, GAME_ICARUS_GETTAG), + GEN_EXPORT(ICARUS_Lerp2Start, GAME_ICARUS_LERP2START), + GEN_EXPORT(ICARUS_Lerp2End, GAME_ICARUS_LERP2END), + GEN_EXPORT(ICARUS_Use, GAME_ICARUS_USE), + GEN_EXPORT(ICARUS_Kill, GAME_ICARUS_KILL), + GEN_EXPORT(ICARUS_Remove, GAME_ICARUS_REMOVE), + GEN_EXPORT(ICARUS_Play, GAME_ICARUS_PLAY), + GEN_EXPORT(ICARUS_GetFloat, GAME_ICARUS_GETFLOAT), + GEN_EXPORT(ICARUS_GetVector, GAME_ICARUS_GETVECTOR), + GEN_EXPORT(ICARUS_GetString, GAME_ICARUS_GETSTRING), + GEN_EXPORT(ICARUS_SoundIndex, GAME_ICARUS_SOUNDINDEX), + GEN_EXPORT(ICARUS_GetSetIDForString, GAME_ICARUS_GETSETIDFORSTRING), + GEN_EXPORT(NAV_ClearPathToPoint, GAME_NAV_CLEARPATHTOPOINT), + GEN_EXPORT(NPC_ClearLOS2, GAME_NAV_CLEARLOS), + GEN_EXPORT(NAVNEW_ClearPathBetweenPoints, GAME_NAV_CLEARPATHBETWEENPOINTS), + GEN_EXPORT(NAV_CheckNodeFailedForEnt, GAME_NAV_CHECKNODEFAILEDFORENT), + GEN_EXPORT(NAV_EntIsUnlockedDoor, GAME_NAV_ENTISUNLOCKEDDOOR), + GEN_EXPORT(NAV_EntIsDoor, GAME_NAV_ENTISDOOR), + GEN_EXPORT(NAV_EntIsBreakable, GAME_NAV_ENTISBREAKABLE), + GEN_EXPORT(NAV_EntIsRemovableUsable, GAME_NAV_ENTISREMOVABLEUSABLE), + GEN_EXPORT(NAV_FindCombatPointWaypoints, GAME_NAV_FINDCOMBATPOINTWAYPOINTS), + GEN_EXPORT(BG_GetItemIndexByTag, GAME_GETITEMINDEXBYTAG), +}; diff --git a/src/game_jasp.cpp b/src/game_jasp.cpp index ad0fb29..f7c4ffd 100644 --- a/src/game_jasp.cpp +++ b/src/game_jasp.cpp @@ -23,17 +23,58 @@ Created By: #include "main.h" #include "util.h" -GEN_GAME_QMM_MSGS(JASP); +struct JASP_GameSupport : public GameSupport { + virtual const char* EngMsgName(intptr_t msg); + virtual const char* ModMsgName(intptr_t msg); + virtual bool AutoDetect(APIType engine_api); + virtual void* Entry(void* syscall, void*, APIType engine_api); + virtual bool ModLoad(void* entry, APIType mod_api); + virtual void ModUnload(); + + virtual intptr_t syscall(intptr_t, ...); + virtual intptr_t vmMain(intptr_t, ...); + + virtual const char* DefaultDLLName() { return "jagame" MOD_DLL; } + virtual const char* DefaultModDir() { return "."; } + virtual const char* GameName() { return "Jedi Knight: Jedi Academy (SP)"; } + virtual const char* GameCode() { return "JASP"; } + +private: + static void update_exports(); + + // track entstrings for our G_GET_ENTITY_TOKEN syscall + // TODO find a way to make this more elegant instead of statics +// track entstrings for our G_GET_ENTITY_TOKEN syscall + static std::map> subbsp_entity_tokens; // -1 = main map entities + static std::map token_counter; + static intptr_t active_subbsp; + static void Init(const char* mapname, const char* spawntarget, int checkSum, const char* entstring, int levelTime, int randomSeed, int globalTime, SavedGameJustLoaded_e eSavedGameJustLoaded, qboolean qbLoadTransition); + + // a copy of the original import struct that comes from the game engine + static game_import_t orig_import; + + // a copy of the original export struct pointer that comes from the mod + static game_export_t* orig_export; -GEN_GAME_FUNCS(JASP); + // struct with lambdas that call QMM's syscall function. this is given to the mod + static game_import_t qmm_import; + + // struct with lambdas that call QMM's vmMain function. this is given to the game engine + static game_export_t qmm_export; + + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = GEN_GAME_QMM_MOD_MSGS(); +}; + +GEN_GAME_OBJ(JASP); // auto-detection logic for JASP -static bool JASP_AutoDetect(api_supportedgame* game, APIType engineapi) { +bool JASP_GameSupport::AutoDetect(APIType engineapi) { if (engineapi != QMM_API_GETGAMEAPI) return false; - if (!str_striequal(g_gameinfo.qmm_file, game->dllname)) + if (!str_striequal(g_gameinfo.qmm_file, DefaultDLLName())) return false; if (!str_stristr(g_gameinfo.exe_file, "jasp") && !str_stristr(g_gameinfo.exe_file, "openjk_sp")) @@ -43,230 +84,18 @@ static bool JASP_AutoDetect(api_supportedgame* game, APIType engineapi) { } -// a copy of the original import struct that comes from the game engine -static game_import_t orig_import; - -// a copy of the original export struct pointer that comes from the mod -static game_export_t* orig_export = nullptr; - -// struct with lambdas that call QMM's syscall function. this is given to the mod -static game_import_t qmm_import = { - GEN_IMPORT(Printf, G_PRINTF), - GEN_IMPORT(WriteCam, G_WRITECAM), - GEN_IMPORT(FlushCamFile, G_FLUSHCAMFILE), - GEN_IMPORT(Error, G_ERROR), - GEN_IMPORT(Milliseconds, G_MILLISECONDS), - GEN_IMPORT(cvar, G_CVAR), - GEN_IMPORT(cvar_set, G_CVAR_SET), - GEN_IMPORT(Cvar_VariableIntegerValue, G_CVAR_VARIABLE_INTEGER_VALUE), - GEN_IMPORT(Cvar_VariableStringBuffer, G_CVAR_VARIABLE_STRING_BUFFER), - GEN_IMPORT(argc, G_ARGC), - GEN_IMPORT(argv, G_ARGV), - GEN_IMPORT(FS_FOpenFile, G_FS_FOPEN_FILE), - GEN_IMPORT(FS_Read, G_FS_READ), - GEN_IMPORT(FS_Write, G_FS_WRITE), - GEN_IMPORT(FS_FCloseFile, G_FS_FCLOSE_FILE), - GEN_IMPORT(FS_ReadFile, G_FS_READFILE), - GEN_IMPORT(FS_FreeFile, G_FS_FREEFILE), - GEN_IMPORT(FS_GetFileList, G_FS_GETFILELIST), - GEN_IMPORT(AppendToSaveGame, G_APPENDTOSAVEGAME), - GEN_IMPORT(ReadFromSaveGame, G_READFROMSAVEGAME), - GEN_IMPORT(ReadFromSaveGameOptional, G_READFROMSAVEGAMEOPTIONAL), - GEN_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND), - GEN_IMPORT(DropClient, G_DROP_CLIENT), - GEN_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND), - GEN_IMPORT(SetConfigstring, G_SET_CONFIGSTRING), - GEN_IMPORT(GetConfigstring, G_GET_CONFIGSTRING), - GEN_IMPORT(GetUserinfo, G_GET_USERINFO), - GEN_IMPORT(SetUserinfo, G_SET_USERINFO), - GEN_IMPORT(GetServerinfo, G_GET_SERVERINFO), - GEN_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL), - GEN_IMPORT_9(trace, G_TRACE, void, trace_t*, const vec3_t, const vec3_t, const vec3_t, const vec3_t, const int, const int, const EG2_Collision, const int), - GEN_IMPORT(pointcontents, G_POINT_CONTENTS), - GEN_IMPORT(totalMapContents, G_TOTALMAPCONTENTS), - GEN_IMPORT(inPVS, G_IN_PVS), - GEN_IMPORT(inPVSIgnorePortals, G_IN_PVS_IGNOREPORTALS), - GEN_IMPORT(AdjustAreaPortalState, G_ADJUSTAREAPORTALSTATE), - GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), - GEN_IMPORT(linkentity, G_LINKENTITY), - GEN_IMPORT(unlinkentity, G_UNLINKENTITY), - GEN_IMPORT(EntitiesInBox, G_ENTITIES_IN_BOX), - GEN_IMPORT(EntityContact, G_ENTITY_CONTACT), - nullptr, // int* VoiceVolume - GEN_IMPORT(Malloc, G_MALLOC), - GEN_IMPORT(Free, G_FREE), - GEN_IMPORT(bIsFromZone, G_BISFROMZONE), - GEN_IMPORT(G2API_PrecacheGhoul2Model, G_G2API_PRECACHEGHOUL2MODEL), - GEN_IMPORT(G2API_InitGhoul2Model, G_G2API_INITGHOUL2MODEL), - GEN_IMPORT(G2API_SetSkin, G_G2API_SETSKIN), - GEN_IMPORT(G2API_SetBoneAnim, G_G2API_SETBONEANIM), - GEN_IMPORT(G2API_SetBoneAngles, G_G2API_SETBONEANGLES), - GEN_IMPORT(G2API_SetBoneAnglesIndex, G_G2API_SETBONEANGLESINDEX), - GEN_IMPORT(G2API_SetBoneAnglesMatrix, G_G2API_SETBONEANGLESMATRIX), - GEN_IMPORT(G2API_CopyGhoul2Instance, G_G2API_COPYGHOUL2INSTANCE), - GEN_IMPORT(G2API_SetBoneAnimIndex, G_G2API_SETBONEANIMINDEX), - GEN_IMPORT(G2API_SetLodBias, G_G2API_SETLODBIAS), - GEN_IMPORT(G2API_SetShader, G_G2API_SETSHADER), - GEN_IMPORT(G2API_RemoveGhoul2Model, G_G2API_REMOVEGHOUL2MODEL), - GEN_IMPORT(G2API_SetSurfaceOnOff, G_G2API_SETSURFACEONOFF), - GEN_IMPORT(G2API_SetRootSurface, G_G2API_SETROOTSURFACE), - GEN_IMPORT(G2API_RemoveSurface, G_G2API_REMOVESURFACE), - GEN_IMPORT_6(G2API_AddSurface, G_G2API_ADDSURFACE, int, CGhoul2Info*, int, int, float, float, int), - GEN_IMPORT(G2API_GetBoneAnim, G_G2API_GETBONEANIM), - GEN_IMPORT(G2API_GetBoneAnimIndex, G_G2API_GETBONEANIMINDEX), - GEN_IMPORT(G2API_GetAnimRange, G_G2API_GETANIMRANGE), - GEN_IMPORT(G2API_GetAnimRangeIndex, G_G2API_GETANIMRANGEINDEX), - GEN_IMPORT(G2API_PauseBoneAnim, G_G2API_PAUSEBONEANIM), - GEN_IMPORT(G2API_PauseBoneAnimIndex, G_G2API_PAUSEBONEANIMINDEX), - GEN_IMPORT(G2API_IsPaused, G_G2API_ISPAUSED), - GEN_IMPORT(G2API_StopBoneAnim, G_G2API_STOPBONEANIM), - GEN_IMPORT(G2API_StopBoneAngles, G_G2API_STOPBONEANGLES), - GEN_IMPORT(G2API_RemoveBone, G_G2API_REMOVEBONE), - GEN_IMPORT(G2API_RemoveBolt, G_G2API_REMOVEBOLT), - GEN_IMPORT(G2API_AddBolt, G_G2API_ADDBOLT), - GEN_IMPORT(G2API_AddBoltSurfNum, G_G2API_ADDBOLTSURFNUM), - GEN_IMPORT(G2API_AttachG2Model, G_G2API_ATTACHG2MODEL), - GEN_IMPORT(G2API_DetachG2Model, G_G2API_DETACHG2MODEL), - GEN_IMPORT(G2API_AttachEnt, G_G2API_ATTACHENT), - GEN_IMPORT(G2API_DetachEnt, G_G2API_DETACHENT), - GEN_IMPORT(G2API_GetBoltMatrix, G_G2API_GETBOLTMATRIX), - GEN_IMPORT(G2API_ListSurfaces, G_G2API_LISTSURFACES), - GEN_IMPORT(G2API_ListBones, G_G2API_LISTBONES), - GEN_IMPORT(G2API_HaveWeGhoul2Models, G_G2API_HAVEWEGHOUL2MODELS), - GEN_IMPORT(G2API_SetGhoul2ModelFlags, G_G2API_SETGHOUL2MODELFLAGS), - GEN_IMPORT(G2API_GetGhoul2ModelFlags, G_G2API_GETGHOUL2MODELFLAGS), - GEN_IMPORT(G2API_GetAnimFileName, G_G2API_GETANIMFILENAME), - GEN_IMPORT(G2API_CollisionDetect, G_G2API_COLLISIONDETECT), - GEN_IMPORT(G2API_GiveMeVectorFromMatrix, G_G2API_GIVEMEVECTORFROMMATRIX), - GEN_IMPORT(G2API_CleanGhoul2Models, G_G2API_CLEANGHOUL2MODELS), - GEN_IMPORT(TheGhoul2InfoArray, G_THEGHOUL2INFOARRAY), - GEN_IMPORT(G2API_GetParentSurface, G_G2API_GETPARENTSURFACE), - GEN_IMPORT(G2API_GetSurfaceIndex, G_G2API_GETSURFACEINDEX), - GEN_IMPORT(G2API_GetSurfaceName, G_G2API_GETSURFACENAME), - GEN_IMPORT(G2API_GetGLAName, G_G2API_GETGLANAME), - GEN_IMPORT(G2API_SetNewOrigin, G_G2API_SETNEWORIGIN), - GEN_IMPORT(G2API_GetBoneIndex, G_G2API_GETBONEINDEX), - GEN_IMPORT(G2API_StopBoneAnglesIndex, G_G2API_STOPBONEANGLESINDEX), - GEN_IMPORT(G2API_StopBoneAnimIndex, G_G2API_STOPBONEANIMINDEX), - GEN_IMPORT(G2API_SetBoneAnglesMatrixIndex, G_G2API_SETBONEANGLESMATRIXINDEX), - GEN_IMPORT(G2API_SetAnimIndex, G_G2API_SETANIMINDEX), - GEN_IMPORT(G2API_GetAnimIndex, G_G2API_GETANIMINDEX), - GEN_IMPORT(G2API_SaveGhoul2Models, G_G2API_SAVEGHOUL2MODELS), - GEN_IMPORT(G2API_LoadGhoul2Models, G_G2API_LOADGHOUL2MODELS), - GEN_IMPORT(G2API_LoadSaveCodeDestructGhoul2Info, G_G2API_LOADSAVECODEDESTRUCTGHOUL2INFO), - GEN_IMPORT(G2API_GetAnimFileNameIndex, G_G2API_GETANIMFILENAMEINDEX), - GEN_IMPORT(G2API_GetAnimFileInternalNameIndex, G_G2API_GETANIMFILEINTERNALNAMEINDEX), - GEN_IMPORT(G2API_GetSurfaceRenderStatus, G_G2API_GETSURFACERENDERSTATUS), - GEN_IMPORT(G2API_SetRagDoll, G_G2API_SETRAGDOLL), - GEN_IMPORT(G2API_AnimateG2Models, G_G2API_ANIMATEG2MODELS), - GEN_IMPORT(G2API_RagPCJConstraint, G_G2API_RAGPCJCONSTRAINT), - GEN_IMPORT_3(G2API_RagPCJGradientSpeed, G_G2API_RAGPCJGRADIENTSPEED, qboolean, CGhoul2Info_v&, const char *, const float), - GEN_IMPORT(G2API_RagEffectorGoal, G_G2API_RAGEFFECTORGOAL), - GEN_IMPORT(G2API_GetRagBonePos, G_G2API_GETRAGBONEPOS), - GEN_IMPORT(G2API_RagEffectorKick, G_G2API_RAGEFFECTORKICK), - GEN_IMPORT(G2API_RagForceSolve, G_G2API_RAGFORCESOLVE), - GEN_IMPORT(G2API_SetBoneIKState, G_G2API_SETBONEIKSTATE), - GEN_IMPORT(G2API_IKMove, G_G2API_IKMOVE), - GEN_IMPORT(G2API_AddSkinGore, G_G2API_ADDSKINGORE), - GEN_IMPORT(G2API_ClearSkinGore, G_G2API_CLEARSKINGORE), - GEN_IMPORT(RMG_Init, G_RMG_INIT), - GEN_IMPORT(CM_RegisterTerrain, G_CM_REGISTERTERRAIN), - GEN_IMPORT(SetActiveSubBSP, G_SET_ACTIVE_SUBBSP), - GEN_IMPORT(RE_RegisterSkin, G_RE_REGISTERSKIN), - GEN_IMPORT(RE_GetAnimationCFG, G_RE_GETANIMATIONCFG), - GEN_IMPORT(WE_GetWindVector, G_WE_GETWINDVECTOR), - GEN_IMPORT(WE_GetWindGusting, G_WE_GETWINDGUSTING), - GEN_IMPORT(WE_IsOutside, G_WE_ISOUTSIDE), - GEN_IMPORT(WE_IsOutsideCausingPain, G_WE_ISOUTSIDECAUSINGPAIN), // todo: returns float - GEN_IMPORT(WE_GetChanceOfSaberFizz, G_WE_GETCHANCEOFSABERFIZZ), // todo: returns float - GEN_IMPORT(WE_IsShaking, G_WE_ISSHAKING), - GEN_IMPORT(WE_AddWeatherZone, G_WE_ADDWEATHERZONE), - GEN_IMPORT(WE_SetTempGlobalFogColor, G_WE_SETTEMPGLOBALFOGCOLOR), -}; - - -// these are "pre" hooks for storing some data for polyfills. -// we need these to be called BEFORE plugins' prehooks get called so they have to be done in the qmm_export table - -// track entstrings for our G_GET_ENTITY_TOKEN syscall -static std::map> s_subbsp_entity_tokens; // -1 = main map entities -static std::map s_tokencount; -static intptr_t s_active_subbsp = -1; -static void JASP_Init(const char* mapname, const char* spawntarget, int checkSum, const char* entstring, int levelTime, int randomSeed, int globalTime, SavedGameJustLoaded_e eSavedGameJustLoaded, qboolean qbLoadTransition) { - if (entstring) { - s_subbsp_entity_tokens[-1] = util_parse_entstring(entstring); - s_tokencount[-1] = 0; - } - cgame.is_from_QMM = true; - vmMain(GAME_INIT, mapname, spawntarget, checkSum, entstring, levelTime, randomSeed, globalTime, eSavedGameJustLoaded, qbLoadTransition); -} - - -// struct with lambdas that call QMM's vmMain function. this is given to the game engine -static game_export_t qmm_export = { - GAME_API_VERSION, // apiversion - JASP_Init, - GEN_EXPORT(Shutdown, GAME_SHUTDOWN), - GEN_EXPORT(WriteLevel, GAME_WRITE_LEVEL), - GEN_EXPORT(ReadLevel, GAME_READ_LEVEL), - GEN_EXPORT(GameAllowedToSaveHere, GAME_GAMEALLOWEDTOSAVEHERE), - GEN_EXPORT(ClientConnect, GAME_CLIENT_CONNECT), - GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), - GEN_EXPORT(ClientUserinfoChanged, GAME_CLIENT_USERINFO_CHANGED), - GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), - GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), - GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), - GEN_EXPORT(RunFrame, GAME_RUN_FRAME), - GEN_EXPORT(ConnectNavs, GAME_CONNECTNAVS), - GEN_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND), - GEN_EXPORT(GameSpawnRMGEntity, GAME_SPAWN_RMG_ENTITY), - - // the engine won't use these until after Init, so we can fill these in after each call into the mod's export functions ("vmMain") - nullptr, // gentities - 0, // gentitySize - 0, // num_entities -}; - - -// update the export variables from orig_export -static void s_update_export() { - if (!orig_export) - return; - - bool changed = false; - - // if entity data changed, we need to send a G_LOCATE_GAME_DATA so plugins can hook it - if (qmm_export.gentities != orig_export->gentities - || qmm_export.gentitySize != orig_export->gentitySize - || qmm_export.num_entities != orig_export->num_entities - ) { - changed = true; - } - - qmm_export.gentities = orig_export->gentities; - qmm_export.gentitySize = orig_export->gentitySize; - qmm_export.num_entities = orig_export->num_entities; - - if (changed) { - // this will trigger this message to be fired to plugins, and then it will be handled - // by the empty "case G_LOCATE_GAME_DATA" in JASP_syscall - qmm_syscall(G_LOCATE_GAME_DATA, (intptr_t)qmm_export.gentities, qmm_export.num_entities, qmm_export.gentitySize, nullptr, 0); - } -} - - // wrapper syscall function that calls actual engine func from orig_import // this is how QMM and plugins will call into the engine -static intptr_t JASP_syscall(intptr_t cmd, ...) { +intptr_t JASP_GameSupport::syscall(intptr_t cmd, ...) { QMM_GET_SYSCALL_ARGS(); #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JASP_syscall({} {}) called\n", JASP_EngMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JASP_GameSupport::syscall({} {}) called\n", EngMsgName(cmd), cmd); #endif // update export vars before calling into the engine - s_update_export(); + update_exports(); intptr_t ret = 0; @@ -438,13 +267,13 @@ static intptr_t JASP_syscall(intptr_t cmd, ...) { } case G_SET_ACTIVE_SUBBSP: { // save current index - s_active_subbsp = args[0]; + active_subbsp = args[0]; // get actual entstring from engine return value - const char* entstring = orig_import.SetActiveSubBSP((int)s_active_subbsp); + const char* entstring = orig_import.SetActiveSubBSP((int)active_subbsp); // if it returns an entstring (-1 won't), parse it - if (s_active_subbsp != -1 && entstring) { - s_subbsp_entity_tokens[s_active_subbsp] = util_parse_entstring(entstring); - s_tokencount[s_active_subbsp] = 0; + if (active_subbsp != -1 && entstring) { + subbsp_entity_tokens[active_subbsp] = util_parse_entstring(entstring); + token_counter[active_subbsp] = 0; } ret = (intptr_t)entstring; break; @@ -453,8 +282,8 @@ static intptr_t JASP_syscall(intptr_t cmd, ...) { // qboolean trap_GetEntityToken(char *buffer, int bufferSize); // get references to the tokencount and tokenlist for this subbsp - size_t& tokencount = s_tokencount[s_active_subbsp]; - std::vector& tokens = s_subbsp_entity_tokens[s_active_subbsp]; + size_t& tokencount = token_counter[active_subbsp]; + std::vector& tokens = subbsp_entity_tokens[active_subbsp]; // if we previously hit the end of the particular entity list, return false if (tokencount >= tokens.size()) { @@ -492,7 +321,7 @@ static intptr_t JASP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JASP_syscall({} {}) returning {}\n", JASP_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JASP_GameSupport::syscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; @@ -501,11 +330,11 @@ static intptr_t JASP_syscall(intptr_t cmd, ...) { // wrapper vmMain function that calls actual mod func from orig_export // this is how QMM and plugins will call into the mod -static intptr_t JASP_vmMain(intptr_t cmd, ...) { +intptr_t JASP_GameSupport::vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JASP_vmMain({} {}) called\n", JASP_ModMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JASP_GameSupport::vmMain({} {}) called\n", ModMsgName(cmd), cmd); #endif if (!orig_export) @@ -542,18 +371,18 @@ static intptr_t JASP_vmMain(intptr_t cmd, ...) { }; // update export vars after returning from the mod - s_update_export(); + update_exports(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JASP_vmMain({} {}) returning {}\n", JASP_ModMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JASP_GameSupport::vmMain({} {}) returning {}\n", ModMsgName(cmd), cmd, ret); #endif return ret; } -static void* JASP_Entry(void* import, void*, APIType) { - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JASP_Entry({}) called\n", import); +void* JASP_GameSupport::Entry(void* import, void*, APIType) { + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JASP_GameSupport::Entry({}) called\n", import); // original import struct from engine // the struct given by the engine goes out of scope after this returns so we have to copy the whole thing @@ -563,13 +392,7 @@ static void* JASP_Entry(void* import, void*, APIType) { // fill in variables of our hooked import struct to pass to the mod qmm_import.VoiceVolume = orig_import.VoiceVolume; - // pointer to wrapper vmMain function that calls actual mod func from orig_export - g_gameinfo.pfnvmMain = JASP_vmMain; - - // pointer to wrapper syscall function that calls actual engine func from orig_import - g_gameinfo.pfnsyscall = JASP_syscall; - - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JASP_Entry({}) returning {}\n", import, fmt::ptr(&qmm_export)); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JASP_GameSupport::Entry({}) returning {}\n", import, fmt::ptr(&qmm_export)); // struct full of export lambdas to QMM's vmMain // this gets returned to the game engine, but we haven't loaded the mod yet. @@ -578,7 +401,7 @@ static void* JASP_Entry(void* import, void*, APIType) { } -static bool JASP_ModLoad(void* entry, APIType modapi) { +bool JASP_GameSupport::ModLoad(void* entry, APIType modapi) { if (modapi != QMM_API_GETGAMEAPI) return false; @@ -589,12 +412,12 @@ static bool JASP_ModLoad(void* entry, APIType modapi) { } -static void JASP_ModUnload() { +void JASP_GameSupport::ModUnload() { orig_export = nullptr; } -static const char* JASP_EngMsgName(intptr_t cmd) { +const char* JASP_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINTF); GEN_CASE(G_WRITECAM); @@ -740,7 +563,7 @@ static const char* JASP_EngMsgName(intptr_t cmd) { } -static const char* JASP_ModMsgName(intptr_t cmd) { +const char* JASP_GameSupport::ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAMEV_APIVERSION); GEN_CASE(GAME_INIT); @@ -765,3 +588,214 @@ static const char* JASP_ModMsgName(intptr_t cmd) { return "unknown"; } } + + +// update the export variables from orig_export +void JASP_GameSupport::update_exports() { + if (!orig_export) + return; + + bool changed = false; + + // if entity data changed, we need to send a G_LOCATE_GAME_DATA so plugins can hook it + if (qmm_export.gentities != orig_export->gentities + || qmm_export.gentitySize != orig_export->gentitySize + || qmm_export.num_entities != orig_export->num_entities + ) { + changed = true; + } + + qmm_export.gentities = orig_export->gentities; + qmm_export.gentitySize = orig_export->gentitySize; + qmm_export.num_entities = orig_export->num_entities; + + if (changed) { + // this will trigger this message to be fired to plugins, and then it will be handled + // by the empty "case G_LOCATE_GAME_DATA" in syscall + qmm_syscall(G_LOCATE_GAME_DATA, (intptr_t)qmm_export.gentities, qmm_export.num_entities, qmm_export.gentitySize, nullptr, 0); + } +} + + +// a copy of the original import struct that comes from the game engine +game_import_t JASP_GameSupport::orig_import = {}; + + +// a copy of the original export struct pointer that comes from the mod +game_export_t* JASP_GameSupport::orig_export = nullptr; + + +// struct with lambdas that call QMM's syscall function. this is given to the mod +game_import_t JASP_GameSupport::qmm_import = { + GEN_IMPORT(Printf, G_PRINTF), + GEN_IMPORT(WriteCam, G_WRITECAM), + GEN_IMPORT(FlushCamFile, G_FLUSHCAMFILE), + GEN_IMPORT(Error, G_ERROR), + GEN_IMPORT(Milliseconds, G_MILLISECONDS), + GEN_IMPORT(cvar, G_CVAR), + GEN_IMPORT(cvar_set, G_CVAR_SET), + GEN_IMPORT(Cvar_VariableIntegerValue, G_CVAR_VARIABLE_INTEGER_VALUE), + GEN_IMPORT(Cvar_VariableStringBuffer, G_CVAR_VARIABLE_STRING_BUFFER), + GEN_IMPORT(argc, G_ARGC), + GEN_IMPORT(argv, G_ARGV), + GEN_IMPORT(FS_FOpenFile, G_FS_FOPEN_FILE), + GEN_IMPORT(FS_Read, G_FS_READ), + GEN_IMPORT(FS_Write, G_FS_WRITE), + GEN_IMPORT(FS_FCloseFile, G_FS_FCLOSE_FILE), + GEN_IMPORT(FS_ReadFile, G_FS_READFILE), + GEN_IMPORT(FS_FreeFile, G_FS_FREEFILE), + GEN_IMPORT(FS_GetFileList, G_FS_GETFILELIST), + GEN_IMPORT(AppendToSaveGame, G_APPENDTOSAVEGAME), + GEN_IMPORT(ReadFromSaveGame, G_READFROMSAVEGAME), + GEN_IMPORT(ReadFromSaveGameOptional, G_READFROMSAVEGAMEOPTIONAL), + GEN_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND), + GEN_IMPORT(DropClient, G_DROP_CLIENT), + GEN_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND), + GEN_IMPORT(SetConfigstring, G_SET_CONFIGSTRING), + GEN_IMPORT(GetConfigstring, G_GET_CONFIGSTRING), + GEN_IMPORT(GetUserinfo, G_GET_USERINFO), + GEN_IMPORT(SetUserinfo, G_SET_USERINFO), + GEN_IMPORT(GetServerinfo, G_GET_SERVERINFO), + GEN_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL), + GEN_IMPORT_9(trace, G_TRACE, void, trace_t*, const vec3_t, const vec3_t, const vec3_t, const vec3_t, const int, const int, const EG2_Collision, const int), + GEN_IMPORT(pointcontents, G_POINT_CONTENTS), + GEN_IMPORT(totalMapContents, G_TOTALMAPCONTENTS), + GEN_IMPORT(inPVS, G_IN_PVS), + GEN_IMPORT(inPVSIgnorePortals, G_IN_PVS_IGNOREPORTALS), + GEN_IMPORT(AdjustAreaPortalState, G_ADJUSTAREAPORTALSTATE), + GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), + GEN_IMPORT(linkentity, G_LINKENTITY), + GEN_IMPORT(unlinkentity, G_UNLINKENTITY), + GEN_IMPORT(EntitiesInBox, G_ENTITIES_IN_BOX), + GEN_IMPORT(EntityContact, G_ENTITY_CONTACT), + nullptr, // int* VoiceVolume + GEN_IMPORT(Malloc, G_MALLOC), + GEN_IMPORT(Free, G_FREE), + GEN_IMPORT(bIsFromZone, G_BISFROMZONE), + GEN_IMPORT(G2API_PrecacheGhoul2Model, G_G2API_PRECACHEGHOUL2MODEL), + GEN_IMPORT(G2API_InitGhoul2Model, G_G2API_INITGHOUL2MODEL), + GEN_IMPORT(G2API_SetSkin, G_G2API_SETSKIN), + GEN_IMPORT(G2API_SetBoneAnim, G_G2API_SETBONEANIM), + GEN_IMPORT(G2API_SetBoneAngles, G_G2API_SETBONEANGLES), + GEN_IMPORT(G2API_SetBoneAnglesIndex, G_G2API_SETBONEANGLESINDEX), + GEN_IMPORT(G2API_SetBoneAnglesMatrix, G_G2API_SETBONEANGLESMATRIX), + GEN_IMPORT(G2API_CopyGhoul2Instance, G_G2API_COPYGHOUL2INSTANCE), + GEN_IMPORT(G2API_SetBoneAnimIndex, G_G2API_SETBONEANIMINDEX), + GEN_IMPORT(G2API_SetLodBias, G_G2API_SETLODBIAS), + GEN_IMPORT(G2API_SetShader, G_G2API_SETSHADER), + GEN_IMPORT(G2API_RemoveGhoul2Model, G_G2API_REMOVEGHOUL2MODEL), + GEN_IMPORT(G2API_SetSurfaceOnOff, G_G2API_SETSURFACEONOFF), + GEN_IMPORT(G2API_SetRootSurface, G_G2API_SETROOTSURFACE), + GEN_IMPORT(G2API_RemoveSurface, G_G2API_REMOVESURFACE), + GEN_IMPORT_6(G2API_AddSurface, G_G2API_ADDSURFACE, int, CGhoul2Info*, int, int, float, float, int), + GEN_IMPORT(G2API_GetBoneAnim, G_G2API_GETBONEANIM), + GEN_IMPORT(G2API_GetBoneAnimIndex, G_G2API_GETBONEANIMINDEX), + GEN_IMPORT(G2API_GetAnimRange, G_G2API_GETANIMRANGE), + GEN_IMPORT(G2API_GetAnimRangeIndex, G_G2API_GETANIMRANGEINDEX), + GEN_IMPORT(G2API_PauseBoneAnim, G_G2API_PAUSEBONEANIM), + GEN_IMPORT(G2API_PauseBoneAnimIndex, G_G2API_PAUSEBONEANIMINDEX), + GEN_IMPORT(G2API_IsPaused, G_G2API_ISPAUSED), + GEN_IMPORT(G2API_StopBoneAnim, G_G2API_STOPBONEANIM), + GEN_IMPORT(G2API_StopBoneAngles, G_G2API_STOPBONEANGLES), + GEN_IMPORT(G2API_RemoveBone, G_G2API_REMOVEBONE), + GEN_IMPORT(G2API_RemoveBolt, G_G2API_REMOVEBOLT), + GEN_IMPORT(G2API_AddBolt, G_G2API_ADDBOLT), + GEN_IMPORT(G2API_AddBoltSurfNum, G_G2API_ADDBOLTSURFNUM), + GEN_IMPORT(G2API_AttachG2Model, G_G2API_ATTACHG2MODEL), + GEN_IMPORT(G2API_DetachG2Model, G_G2API_DETACHG2MODEL), + GEN_IMPORT(G2API_AttachEnt, G_G2API_ATTACHENT), + GEN_IMPORT(G2API_DetachEnt, G_G2API_DETACHENT), + GEN_IMPORT(G2API_GetBoltMatrix, G_G2API_GETBOLTMATRIX), + GEN_IMPORT(G2API_ListSurfaces, G_G2API_LISTSURFACES), + GEN_IMPORT(G2API_ListBones, G_G2API_LISTBONES), + GEN_IMPORT(G2API_HaveWeGhoul2Models, G_G2API_HAVEWEGHOUL2MODELS), + GEN_IMPORT(G2API_SetGhoul2ModelFlags, G_G2API_SETGHOUL2MODELFLAGS), + GEN_IMPORT(G2API_GetGhoul2ModelFlags, G_G2API_GETGHOUL2MODELFLAGS), + GEN_IMPORT(G2API_GetAnimFileName, G_G2API_GETANIMFILENAME), + GEN_IMPORT(G2API_CollisionDetect, G_G2API_COLLISIONDETECT), + GEN_IMPORT(G2API_GiveMeVectorFromMatrix, G_G2API_GIVEMEVECTORFROMMATRIX), + GEN_IMPORT(G2API_CleanGhoul2Models, G_G2API_CLEANGHOUL2MODELS), + GEN_IMPORT(TheGhoul2InfoArray, G_THEGHOUL2INFOARRAY), + GEN_IMPORT(G2API_GetParentSurface, G_G2API_GETPARENTSURFACE), + GEN_IMPORT(G2API_GetSurfaceIndex, G_G2API_GETSURFACEINDEX), + GEN_IMPORT(G2API_GetSurfaceName, G_G2API_GETSURFACENAME), + GEN_IMPORT(G2API_GetGLAName, G_G2API_GETGLANAME), + GEN_IMPORT(G2API_SetNewOrigin, G_G2API_SETNEWORIGIN), + GEN_IMPORT(G2API_GetBoneIndex, G_G2API_GETBONEINDEX), + GEN_IMPORT(G2API_StopBoneAnglesIndex, G_G2API_STOPBONEANGLESINDEX), + GEN_IMPORT(G2API_StopBoneAnimIndex, G_G2API_STOPBONEANIMINDEX), + GEN_IMPORT(G2API_SetBoneAnglesMatrixIndex, G_G2API_SETBONEANGLESMATRIXINDEX), + GEN_IMPORT(G2API_SetAnimIndex, G_G2API_SETANIMINDEX), + GEN_IMPORT(G2API_GetAnimIndex, G_G2API_GETANIMINDEX), + GEN_IMPORT(G2API_SaveGhoul2Models, G_G2API_SAVEGHOUL2MODELS), + GEN_IMPORT(G2API_LoadGhoul2Models, G_G2API_LOADGHOUL2MODELS), + GEN_IMPORT(G2API_LoadSaveCodeDestructGhoul2Info, G_G2API_LOADSAVECODEDESTRUCTGHOUL2INFO), + GEN_IMPORT(G2API_GetAnimFileNameIndex, G_G2API_GETANIMFILENAMEINDEX), + GEN_IMPORT(G2API_GetAnimFileInternalNameIndex, G_G2API_GETANIMFILEINTERNALNAMEINDEX), + GEN_IMPORT(G2API_GetSurfaceRenderStatus, G_G2API_GETSURFACERENDERSTATUS), + GEN_IMPORT(G2API_SetRagDoll, G_G2API_SETRAGDOLL), + GEN_IMPORT(G2API_AnimateG2Models, G_G2API_ANIMATEG2MODELS), + GEN_IMPORT(G2API_RagPCJConstraint, G_G2API_RAGPCJCONSTRAINT), + GEN_IMPORT_3(G2API_RagPCJGradientSpeed, G_G2API_RAGPCJGRADIENTSPEED, qboolean, CGhoul2Info_v&, const char*, const float), + GEN_IMPORT(G2API_RagEffectorGoal, G_G2API_RAGEFFECTORGOAL), + GEN_IMPORT(G2API_GetRagBonePos, G_G2API_GETRAGBONEPOS), + GEN_IMPORT(G2API_RagEffectorKick, G_G2API_RAGEFFECTORKICK), + GEN_IMPORT(G2API_RagForceSolve, G_G2API_RAGFORCESOLVE), + GEN_IMPORT(G2API_SetBoneIKState, G_G2API_SETBONEIKSTATE), + GEN_IMPORT(G2API_IKMove, G_G2API_IKMOVE), + GEN_IMPORT(G2API_AddSkinGore, G_G2API_ADDSKINGORE), + GEN_IMPORT(G2API_ClearSkinGore, G_G2API_CLEARSKINGORE), + GEN_IMPORT(RMG_Init, G_RMG_INIT), + GEN_IMPORT(CM_RegisterTerrain, G_CM_REGISTERTERRAIN), + GEN_IMPORT(SetActiveSubBSP, G_SET_ACTIVE_SUBBSP), + GEN_IMPORT(RE_RegisterSkin, G_RE_REGISTERSKIN), + GEN_IMPORT(RE_GetAnimationCFG, G_RE_GETANIMATIONCFG), + GEN_IMPORT(WE_GetWindVector, G_WE_GETWINDVECTOR), + GEN_IMPORT(WE_GetWindGusting, G_WE_GETWINDGUSTING), + GEN_IMPORT(WE_IsOutside, G_WE_ISOUTSIDE), + GEN_IMPORT(WE_IsOutsideCausingPain, G_WE_ISOUTSIDECAUSINGPAIN), // todo: returns float + GEN_IMPORT(WE_GetChanceOfSaberFizz, G_WE_GETCHANCEOFSABERFIZZ), // todo: returns float + GEN_IMPORT(WE_IsShaking, G_WE_ISSHAKING), + GEN_IMPORT(WE_AddWeatherZone, G_WE_ADDWEATHERZONE), + GEN_IMPORT(WE_SetTempGlobalFogColor, G_WE_SETTEMPGLOBALFOGCOLOR), +}; + + +// track entstrings for our G_GET_ENTITY_TOKEN syscall +std::map> JASP_GameSupport::subbsp_entity_tokens; // -1 = main map entities +std::map JASP_GameSupport::token_counter; +intptr_t JASP_GameSupport::active_subbsp = -1; +void JASP_GameSupport::Init(const char* mapname, const char* spawntarget, int checkSum, const char* entstring, int levelTime, int randomSeed, int globalTime, SavedGameJustLoaded_e eSavedGameJustLoaded, qboolean qbLoadTransition) { + if (entstring) { + subbsp_entity_tokens[-1] = util_parse_entstring(entstring); + token_counter[-1] = 0; + } + cgame.is_from_QMM = true; + ::vmMain(GAME_INIT, mapname, spawntarget, checkSum, entstring, levelTime, randomSeed, globalTime, eSavedGameJustLoaded, qbLoadTransition); +} + + +// struct with lambdas that call QMM's vmMain function. this is given to the game engine +game_export_t JASP_GameSupport::qmm_export = { + GAME_API_VERSION, // apiversion + JASP_GameSupport::Init, + GEN_EXPORT(Shutdown, GAME_SHUTDOWN), + GEN_EXPORT(WriteLevel, GAME_WRITE_LEVEL), + GEN_EXPORT(ReadLevel, GAME_READ_LEVEL), + GEN_EXPORT(GameAllowedToSaveHere, GAME_GAMEALLOWEDTOSAVEHERE), + GEN_EXPORT(ClientConnect, GAME_CLIENT_CONNECT), + GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), + GEN_EXPORT(ClientUserinfoChanged, GAME_CLIENT_USERINFO_CHANGED), + GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), + GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), + GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), + GEN_EXPORT(RunFrame, GAME_RUN_FRAME), + GEN_EXPORT(ConnectNavs, GAME_CONNECTNAVS), + GEN_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND), + GEN_EXPORT(GameSpawnRMGEntity, GAME_SPAWN_RMG_ENTITY), + + // the engine won't use these until after Init, so we can fill these in after each call into the mod's export functions ("vmMain") + nullptr, // gentities + 0, // gentitySize + 0, // num_entities +}; diff --git a/src/game_jk2mp.cpp b/src/game_jk2mp.cpp index 175ad37..4ae4458 100644 --- a/src/game_jk2mp.cpp +++ b/src/game_jk2mp.cpp @@ -43,7 +43,10 @@ struct JK2MP_GameSupport : public GameSupport { virtual int QVMSyscall(uint8_t* membase, int cmd, int* args); private: + // a copy of the original syscall from the engine eng_syscall orig_syscall = nullptr; + + // a copy of the vmMain function from the mod mod_vmMain orig_vmMain = nullptr; const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); diff --git a/src/game_jk2sp.cpp b/src/game_jk2sp.cpp index 8ac2b90..5356022 100644 --- a/src/game_jk2sp.cpp +++ b/src/game_jk2sp.cpp @@ -39,7 +39,7 @@ struct JK2SP_GameSupport : public GameSupport { virtual const char* GameCode() { return "JK2SP"; } private: - void update_exports(); + static void update_exports(); // track entstrings for our G_GET_ENTITY_TOKEN syscall // TODO find a way to make this more elegant instead of statics @@ -48,142 +48,16 @@ struct JK2SP_GameSupport : public GameSupport { static void Init(const char* mapname, const char* spawntarget, int checkSum, const char* entstring, int levelTime, int randomSeed, int globalTime, SavedGameJustLoaded_e eSavedGameJustLoaded, qboolean qbLoadTransition); // a copy of the original import struct that comes from the game engine - game_import_t orig_import = {}; + static game_import_t orig_import; // a copy of the original export struct pointer that comes from the mod - game_export_t* orig_export = nullptr; + static game_export_t* orig_export; // struct with lambdas that call QMM's syscall function. this is given to the mod - game_import_t qmm_import = { - GEN_IMPORT(Printf, G_PRINTF), - GEN_IMPORT(WriteCam, G_WRITECAM), - GEN_IMPORT(Error, G_ERROR), - GEN_IMPORT(Milliseconds, G_MILLISECONDS), - GEN_IMPORT(cvar, G_CVAR), - GEN_IMPORT(cvar_set, G_CVAR_SET), - GEN_IMPORT(Cvar_VariableIntegerValue, G_CVAR_VARIABLE_INTEGER_VALUE), - GEN_IMPORT(Cvar_VariableStringBuffer, G_CVAR_VARIABLE_STRING_BUFFER), - GEN_IMPORT(argc, G_ARGC), - GEN_IMPORT(argv, G_ARGV), - GEN_IMPORT(FS_FOpenFile, G_FS_FOPEN_FILE), - GEN_IMPORT(FS_Read, G_FS_READ), - GEN_IMPORT(FS_Write, G_FS_WRITE), - GEN_IMPORT(FS_FCloseFile, G_FS_FCLOSE_FILE), - GEN_IMPORT(FS_ReadFile, G_FS_READFILE), - GEN_IMPORT(FS_FreeFile, G_FS_FREEFILE), - GEN_IMPORT(FS_GetFileList, G_FS_GETFILELIST), - GEN_IMPORT(AppendToSaveGame, G_APPENDTOSAVEGAME), - GEN_IMPORT(ReadFromSaveGame, G_READFROMSAVEGAME), - GEN_IMPORT(ReadFromSaveGameOptional, G_READFROMSAVEGAMEOPTIONAL), - GEN_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND), - GEN_IMPORT(DropClient, G_DROP_CLIENT), - GEN_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND), - GEN_IMPORT(SetConfigstring, G_SET_CONFIGSTRING), - GEN_IMPORT(GetConfigstring, G_GET_CONFIGSTRING), - GEN_IMPORT(GetUserinfo, G_GET_USERINFO), - GEN_IMPORT(SetUserinfo, G_SET_USERINFO), - GEN_IMPORT(GetServerinfo, G_GET_SERVERINFO), - GEN_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL), - GEN_IMPORT(trace, G_TRACE), - GEN_IMPORT(pointcontents, G_POINT_CONTENTS), - GEN_IMPORT(inPVS, G_IN_PVS), - GEN_IMPORT(inPVSIgnorePortals, G_IN_PVS_IGNOREPORTALS), - GEN_IMPORT(AdjustAreaPortalState, G_ADJUSTAREAPORTALSTATE), - GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), - GEN_IMPORT(linkentity, G_LINKENTITY), - GEN_IMPORT(unlinkentity, G_UNLINKENTITY), - GEN_IMPORT(EntitiesInBox, G_ENTITIES_IN_BOX), - GEN_IMPORT(EntityContact, G_ENTITY_CONTACT), - nullptr, // int* VoiceVolume; - GEN_IMPORT(Malloc, G_MALLOC), // see qcommon/tags.h for choices - GEN_IMPORT(Free, G_FREE), - GEN_IMPORT(G2API_PrecacheGhoul2Model, G_G2API_PRECACHEGHOUL2MODEL), - GEN_IMPORT(G2API_InitGhoul2Model, G_G2API_INITGHOUL2MODEL), - GEN_IMPORT(G2API_SetLodBias, G_G2API_SETLODBIAS), - GEN_IMPORT(G2API_SetSkin, G_G2API_SETSKIN), - GEN_IMPORT(G2API_SetShader, G_G2API_SETSHADER), - GEN_IMPORT(G2API_RemoveGhoul2Model, G_G2API_REMOVEGHOUL2MODEL), - GEN_IMPORT(G2API_SetSurfaceOnOff, G_G2API_SETSURFACEONOFF), - GEN_IMPORT(G2API_SetRootSurface, G_G2API_SETROOTSURFACE), - GEN_IMPORT(G2API_RemoveSurface, G_G2API_REMOVESURFACE), - GEN_IMPORT_6(G2API_AddSurface, G_G2API_ADDSURFACE, int, CGhoul2Info*, int, int, float, float, int), - GEN_IMPORT(G2API_SetBoneAnim, G_G2API_SETBONEANIM), - GEN_IMPORT(G2API_GetBoneAnim, G_G2API_GETBONEANIM), - GEN_IMPORT(G2API_GetBoneAnimIndex, G_G2API_GETBONEANIMINDEX), - GEN_IMPORT(G2API_GetAnimRange, G_G2API_GETANIMRANGE), - GEN_IMPORT(G2API_GetAnimRangeIndex, G_G2API_GETANIMRANGEINDEX), - GEN_IMPORT(G2API_PauseBoneAnim, G_G2API_PAUSEBONEANIM), - GEN_IMPORT(G2API_PauseBoneAnimIndex, G_G2API_PAUSEBONEANIMINDEX), - GEN_IMPORT(G2API_IsPaused, G_G2API_ISPAUSED), - GEN_IMPORT(G2API_StopBoneAnim, G_G2API_STOPBONEANIM), - GEN_IMPORT(G2API_SetBoneAngles, G_G2API_SETBONEANGLES), - GEN_IMPORT(G2API_SetBoneAnglesMatrix, G_G2API_SETBONEANGLESMATRIX), - GEN_IMPORT(G2API_StopBoneAngles, G_G2API_STOPBONEANGLES), - GEN_IMPORT(G2API_RemoveBone, G_G2API_REMOVEBONE), - GEN_IMPORT(G2API_RemoveBolt, G_G2API_REMOVEBOLT), - GEN_IMPORT(G2API_AddBolt, G_G2API_ADDBOLT), - GEN_IMPORT(G2API_AddBoltSurfNum, G_G2API_ADDBOLTSURFNUM), - GEN_IMPORT(G2API_AttachG2Model, G_G2API_ATTACHG2MODEL), - GEN_IMPORT(G2API_DetachG2Model, G_G2API_DETACHG2MODEL), - GEN_IMPORT(G2API_AttachEnt, G_G2API_ATTACHENT), - GEN_IMPORT(G2API_DetachEnt, G_G2API_DETACHENT), - GEN_IMPORT(G2API_GetBoltMatrix, G_G2API_GETBOLTMATRIX), - GEN_IMPORT(G2API_ListSurfaces, G_G2API_LISTSURFACES), - GEN_IMPORT(G2API_ListBones, G_G2API_LISTBONES), - GEN_IMPORT(G2API_HaveWeGhoul2Models, G_G2API_HAVEWEGHOUL2MODELS), - GEN_IMPORT(G2API_SetGhoul2ModelFlags, G_G2API_SETGHOUL2MODELFLAGS), - GEN_IMPORT(G2API_GetGhoul2ModelFlags, G_G2API_GETGHOUL2MODELFLAGS), - GEN_IMPORT(G2API_GetAnimFileName, G_G2API_GETANIMFILENAME), - GEN_IMPORT(G2API_CollisionDetect, G_G2API_COLLISIONDETECT), - GEN_IMPORT(G2API_GiveMeVectorFromMatrix, G_G2API_GIVEMEVECTORFROMMATRIX), - GEN_IMPORT(G2API_CopyGhoul2Instance, G_G2API_COPYGHOUL2INSTANCE), - GEN_IMPORT(G2API_CleanGhoul2Models, G_G2API_CLEANGHOUL2MODELS), - GEN_IMPORT(TheGhoul2InfoArray, G_THEGHOUL2INFOARRAY), - GEN_IMPORT(G2API_GetParentSurface, G_G2API_GETPARENTSURFACE), - GEN_IMPORT(G2API_GetSurfaceIndex, G_G2API_GETSURFACEINDEX), - GEN_IMPORT(G2API_GetSurfaceName, G_G2API_GETSURFACENAME), - GEN_IMPORT(G2API_GetGLAName, G_G2API_GETGLANAME), - GEN_IMPORT(G2API_SetNewOrigin, G_G2API_SETNEWORIGIN), - GEN_IMPORT(G2API_GetBoneIndex, G_G2API_GETBONEINDEX), - GEN_IMPORT(G2API_StopBoneAnglesIndex, G_G2API_STOPBONEANGLESINDEX), - GEN_IMPORT(G2API_StopBoneAnimIndex, G_G2API_STOPBONEANIMINDEX), - GEN_IMPORT(G2API_SetBoneAnglesIndex, G_G2API_SETBONEANGLESINDEX), - GEN_IMPORT(G2API_SetBoneAnglesMatrixIndex, G_G2API_SETBONEANGLESMATRIXINDEX), - GEN_IMPORT(G2API_SetBoneAnimIndex, G_G2API_SETBONEANIMINDEX), - GEN_IMPORT(G2API_SaveGhoul2Models, G_G2API_SAVEGHOUL2MODELS), - GEN_IMPORT(G2API_LoadGhoul2Models, G_G2API_LOADGHOUL2MODELS), - GEN_IMPORT(G2API_LoadSaveCodeDestructGhoul2Info, G_G2API_LOADSAVECODEDESTRUCTGHOUL2INFO), - GEN_IMPORT(G2API_FreeSaveBuffer, G_G2API_FREESAVEBUFFER), - GEN_IMPORT(G2API_GetAnimFileNameIndex, G_G2API_GETANIMFILENAMEINDEX), - GEN_IMPORT(G2API_GetSurfaceRenderStatus, G_G2API_GETSURFACERENDERSTATUS), - GEN_IMPORT(RE_RegisterSkin, G_RE_REGISTERSKIN), - GEN_IMPORT(RE_GetAnimationCFG, G_RE_GETANIMATIONCFG), - }; + static game_import_t qmm_import; // struct with lambdas that call QMM's vmMain function. this is given to the game engine - game_export_t qmm_export = { - GAME_API_VERSION, // apiversion - JK2SP_GameSupport::Init, - GEN_EXPORT(Shutdown, GAME_SHUTDOWN), - GEN_EXPORT(WriteLevel, GAME_WRITE_LEVEL), - GEN_EXPORT(ReadLevel, GAME_READ_LEVEL), - GEN_EXPORT(GameAllowedToSaveHere, GAME_GAMEALLOWEDTOSAVEHERE), - GEN_EXPORT(ClientConnect, GAME_CLIENT_CONNECT), - GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), - GEN_EXPORT(ClientUserinfoChanged, GAME_CLIENT_USERINFO_CHANGED), - GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), - GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), - GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), - GEN_EXPORT(RunFrame, GAME_RUN_FRAME), - GEN_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND), - GEN_EXPORT(PrintEntClassname, GAME_PRINTENTCLASSNAME), - GEN_EXPORT(ValidateAnimRange, GAME_VALIDATEANIMRANGE), - - // the engine won't use these until after Init, so we can fill these in after each call into the mod's export functions ("vmMain") - nullptr, // gentities - 0, // gentitySize - 0, // num_entities - }; + static game_export_t qmm_export; const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = GEN_GAME_QMM_MOD_MSGS(); @@ -208,50 +82,6 @@ bool JK2SP_GameSupport::AutoDetect(APIType engineapi) { } -// these are "pre" hooks for storing some data for polyfills. -// we need these to be called BEFORE plugins' prehooks get called so they have to be done in the qmm_export table - -// track entstrings for our G_GET_ENTITY_TOKEN syscall -std::vector JK2SP_GameSupport::entity_tokens; -size_t JK2SP_GameSupport::token_counter = 0; - -void JK2SP_GameSupport::Init(const char* mapname, const char* spawntarget, int checkSum, const char* entstring, int levelTime, int randomSeed, int globalTime, SavedGameJustLoaded_e eSavedGameJustLoaded, qboolean qbLoadTransition) { - if (entstring) { - entity_tokens = util_parse_entstring(entstring); - token_counter = 0; - } - cgame.is_from_QMM = true; - ::vmMain(GAME_INIT, mapname, spawntarget, checkSum, entstring, levelTime, randomSeed, globalTime, eSavedGameJustLoaded, qbLoadTransition); -} - - -// update the export variables from orig_export -void JK2SP_GameSupport::update_exports() { - if (!orig_export) - return; - - bool changed = false; - - // if entity data changed, we need to send a G_LOCATE_GAME_DATA so plugins can hook it - if (qmm_export.gentities != orig_export->gentities - || qmm_export.gentitySize != orig_export->gentitySize - || qmm_export.num_entities != orig_export->num_entities - ) { - changed = true; - } - - qmm_export.gentities = orig_export->gentities; - qmm_export.gentitySize = orig_export->gentitySize; - qmm_export.num_entities = orig_export->num_entities; - - if (changed) { - // this will trigger this message to be fired to plugins, and then it will be handled - // by the empty "case G_LOCATE_GAME_DATA" in JASP_syscall - qmm_syscall(G_LOCATE_GAME_DATA, (intptr_t)qmm_export.gentities, qmm_export.num_entities, qmm_export.gentitySize, nullptr, 0); - } -} - - // wrapper syscall function that calls actual engine func from orig_import // this is how QMM and plugins will call into the engine intptr_t JK2SP_GameSupport::syscall(intptr_t cmd, ...) { @@ -680,3 +510,181 @@ const char* JK2SP_GameSupport::ModMsgName(intptr_t cmd) { return "unknown"; } } + + +// update the export variables from orig_export +void JK2SP_GameSupport::update_exports() { + if (!orig_export) + return; + + bool changed = false; + + // if entity data changed, we need to send a G_LOCATE_GAME_DATA so plugins can hook it + if (qmm_export.gentities != orig_export->gentities + || qmm_export.gentitySize != orig_export->gentitySize + || qmm_export.num_entities != orig_export->num_entities + ) { + changed = true; + } + + qmm_export.gentities = orig_export->gentities; + qmm_export.gentitySize = orig_export->gentitySize; + qmm_export.num_entities = orig_export->num_entities; + + if (changed) { + // this will trigger this message to be fired to plugins, and then it will be handled + // by the empty "case G_LOCATE_GAME_DATA" in syscall + qmm_syscall(G_LOCATE_GAME_DATA, (intptr_t)qmm_export.gentities, qmm_export.num_entities, qmm_export.gentitySize, nullptr, 0); + } +} + + +game_import_t JK2SP_GameSupport::orig_import = {}; + + +game_export_t* JK2SP_GameSupport::orig_export = nullptr; + + +game_import_t JK2SP_GameSupport::qmm_import = { + GEN_IMPORT(Printf, G_PRINTF), + GEN_IMPORT(WriteCam, G_WRITECAM), + GEN_IMPORT(Error, G_ERROR), + GEN_IMPORT(Milliseconds, G_MILLISECONDS), + GEN_IMPORT(cvar, G_CVAR), + GEN_IMPORT(cvar_set, G_CVAR_SET), + GEN_IMPORT(Cvar_VariableIntegerValue, G_CVAR_VARIABLE_INTEGER_VALUE), + GEN_IMPORT(Cvar_VariableStringBuffer, G_CVAR_VARIABLE_STRING_BUFFER), + GEN_IMPORT(argc, G_ARGC), + GEN_IMPORT(argv, G_ARGV), + GEN_IMPORT(FS_FOpenFile, G_FS_FOPEN_FILE), + GEN_IMPORT(FS_Read, G_FS_READ), + GEN_IMPORT(FS_Write, G_FS_WRITE), + GEN_IMPORT(FS_FCloseFile, G_FS_FCLOSE_FILE), + GEN_IMPORT(FS_ReadFile, G_FS_READFILE), + GEN_IMPORT(FS_FreeFile, G_FS_FREEFILE), + GEN_IMPORT(FS_GetFileList, G_FS_GETFILELIST), + GEN_IMPORT(AppendToSaveGame, G_APPENDTOSAVEGAME), + GEN_IMPORT(ReadFromSaveGame, G_READFROMSAVEGAME), + GEN_IMPORT(ReadFromSaveGameOptional, G_READFROMSAVEGAMEOPTIONAL), + GEN_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND), + GEN_IMPORT(DropClient, G_DROP_CLIENT), + GEN_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND), + GEN_IMPORT(SetConfigstring, G_SET_CONFIGSTRING), + GEN_IMPORT(GetConfigstring, G_GET_CONFIGSTRING), + GEN_IMPORT(GetUserinfo, G_GET_USERINFO), + GEN_IMPORT(SetUserinfo, G_SET_USERINFO), + GEN_IMPORT(GetServerinfo, G_GET_SERVERINFO), + GEN_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL), + GEN_IMPORT(trace, G_TRACE), + GEN_IMPORT(pointcontents, G_POINT_CONTENTS), + GEN_IMPORT(inPVS, G_IN_PVS), + GEN_IMPORT(inPVSIgnorePortals, G_IN_PVS_IGNOREPORTALS), + GEN_IMPORT(AdjustAreaPortalState, G_ADJUSTAREAPORTALSTATE), + GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), + GEN_IMPORT(linkentity, G_LINKENTITY), + GEN_IMPORT(unlinkentity, G_UNLINKENTITY), + GEN_IMPORT(EntitiesInBox, G_ENTITIES_IN_BOX), + GEN_IMPORT(EntityContact, G_ENTITY_CONTACT), + nullptr, // int* VoiceVolume; + GEN_IMPORT(Malloc, G_MALLOC), // see qcommon/tags.h for choices + GEN_IMPORT(Free, G_FREE), + GEN_IMPORT(G2API_PrecacheGhoul2Model, G_G2API_PRECACHEGHOUL2MODEL), + GEN_IMPORT(G2API_InitGhoul2Model, G_G2API_INITGHOUL2MODEL), + GEN_IMPORT(G2API_SetLodBias, G_G2API_SETLODBIAS), + GEN_IMPORT(G2API_SetSkin, G_G2API_SETSKIN), + GEN_IMPORT(G2API_SetShader, G_G2API_SETSHADER), + GEN_IMPORT(G2API_RemoveGhoul2Model, G_G2API_REMOVEGHOUL2MODEL), + GEN_IMPORT(G2API_SetSurfaceOnOff, G_G2API_SETSURFACEONOFF), + GEN_IMPORT(G2API_SetRootSurface, G_G2API_SETROOTSURFACE), + GEN_IMPORT(G2API_RemoveSurface, G_G2API_REMOVESURFACE), + GEN_IMPORT_6(G2API_AddSurface, G_G2API_ADDSURFACE, int, CGhoul2Info*, int, int, float, float, int), + GEN_IMPORT(G2API_SetBoneAnim, G_G2API_SETBONEANIM), + GEN_IMPORT(G2API_GetBoneAnim, G_G2API_GETBONEANIM), + GEN_IMPORT(G2API_GetBoneAnimIndex, G_G2API_GETBONEANIMINDEX), + GEN_IMPORT(G2API_GetAnimRange, G_G2API_GETANIMRANGE), + GEN_IMPORT(G2API_GetAnimRangeIndex, G_G2API_GETANIMRANGEINDEX), + GEN_IMPORT(G2API_PauseBoneAnim, G_G2API_PAUSEBONEANIM), + GEN_IMPORT(G2API_PauseBoneAnimIndex, G_G2API_PAUSEBONEANIMINDEX), + GEN_IMPORT(G2API_IsPaused, G_G2API_ISPAUSED), + GEN_IMPORT(G2API_StopBoneAnim, G_G2API_STOPBONEANIM), + GEN_IMPORT(G2API_SetBoneAngles, G_G2API_SETBONEANGLES), + GEN_IMPORT(G2API_SetBoneAnglesMatrix, G_G2API_SETBONEANGLESMATRIX), + GEN_IMPORT(G2API_StopBoneAngles, G_G2API_STOPBONEANGLES), + GEN_IMPORT(G2API_RemoveBone, G_G2API_REMOVEBONE), + GEN_IMPORT(G2API_RemoveBolt, G_G2API_REMOVEBOLT), + GEN_IMPORT(G2API_AddBolt, G_G2API_ADDBOLT), + GEN_IMPORT(G2API_AddBoltSurfNum, G_G2API_ADDBOLTSURFNUM), + GEN_IMPORT(G2API_AttachG2Model, G_G2API_ATTACHG2MODEL), + GEN_IMPORT(G2API_DetachG2Model, G_G2API_DETACHG2MODEL), + GEN_IMPORT(G2API_AttachEnt, G_G2API_ATTACHENT), + GEN_IMPORT(G2API_DetachEnt, G_G2API_DETACHENT), + GEN_IMPORT(G2API_GetBoltMatrix, G_G2API_GETBOLTMATRIX), + GEN_IMPORT(G2API_ListSurfaces, G_G2API_LISTSURFACES), + GEN_IMPORT(G2API_ListBones, G_G2API_LISTBONES), + GEN_IMPORT(G2API_HaveWeGhoul2Models, G_G2API_HAVEWEGHOUL2MODELS), + GEN_IMPORT(G2API_SetGhoul2ModelFlags, G_G2API_SETGHOUL2MODELFLAGS), + GEN_IMPORT(G2API_GetGhoul2ModelFlags, G_G2API_GETGHOUL2MODELFLAGS), + GEN_IMPORT(G2API_GetAnimFileName, G_G2API_GETANIMFILENAME), + GEN_IMPORT(G2API_CollisionDetect, G_G2API_COLLISIONDETECT), + GEN_IMPORT(G2API_GiveMeVectorFromMatrix, G_G2API_GIVEMEVECTORFROMMATRIX), + GEN_IMPORT(G2API_CopyGhoul2Instance, G_G2API_COPYGHOUL2INSTANCE), + GEN_IMPORT(G2API_CleanGhoul2Models, G_G2API_CLEANGHOUL2MODELS), + GEN_IMPORT(TheGhoul2InfoArray, G_THEGHOUL2INFOARRAY), + GEN_IMPORT(G2API_GetParentSurface, G_G2API_GETPARENTSURFACE), + GEN_IMPORT(G2API_GetSurfaceIndex, G_G2API_GETSURFACEINDEX), + GEN_IMPORT(G2API_GetSurfaceName, G_G2API_GETSURFACENAME), + GEN_IMPORT(G2API_GetGLAName, G_G2API_GETGLANAME), + GEN_IMPORT(G2API_SetNewOrigin, G_G2API_SETNEWORIGIN), + GEN_IMPORT(G2API_GetBoneIndex, G_G2API_GETBONEINDEX), + GEN_IMPORT(G2API_StopBoneAnglesIndex, G_G2API_STOPBONEANGLESINDEX), + GEN_IMPORT(G2API_StopBoneAnimIndex, G_G2API_STOPBONEANIMINDEX), + GEN_IMPORT(G2API_SetBoneAnglesIndex, G_G2API_SETBONEANGLESINDEX), + GEN_IMPORT(G2API_SetBoneAnglesMatrixIndex, G_G2API_SETBONEANGLESMATRIXINDEX), + GEN_IMPORT(G2API_SetBoneAnimIndex, G_G2API_SETBONEANIMINDEX), + GEN_IMPORT(G2API_SaveGhoul2Models, G_G2API_SAVEGHOUL2MODELS), + GEN_IMPORT(G2API_LoadGhoul2Models, G_G2API_LOADGHOUL2MODELS), + GEN_IMPORT(G2API_LoadSaveCodeDestructGhoul2Info, G_G2API_LOADSAVECODEDESTRUCTGHOUL2INFO), + GEN_IMPORT(G2API_FreeSaveBuffer, G_G2API_FREESAVEBUFFER), + GEN_IMPORT(G2API_GetAnimFileNameIndex, G_G2API_GETANIMFILENAMEINDEX), + GEN_IMPORT(G2API_GetSurfaceRenderStatus, G_G2API_GETSURFACERENDERSTATUS), + GEN_IMPORT(RE_RegisterSkin, G_RE_REGISTERSKIN), + GEN_IMPORT(RE_GetAnimationCFG, G_RE_GETANIMATIONCFG), +}; + + +// track entstrings for our G_GET_ENTITY_TOKEN syscall +std::vector JK2SP_GameSupport::entity_tokens; +size_t JK2SP_GameSupport::token_counter = 0; +void JK2SP_GameSupport::Init(const char* mapname, const char* spawntarget, int checkSum, const char* entstring, int levelTime, int randomSeed, int globalTime, SavedGameJustLoaded_e eSavedGameJustLoaded, qboolean qbLoadTransition) { + if (entstring) { + entity_tokens = util_parse_entstring(entstring); + token_counter = 0; + } + cgame.is_from_QMM = true; + ::vmMain(GAME_INIT, mapname, spawntarget, checkSum, entstring, levelTime, randomSeed, globalTime, eSavedGameJustLoaded, qbLoadTransition); +} + + +game_export_t JK2SP_GameSupport::qmm_export = { + GAME_API_VERSION, // apiversion + JK2SP_GameSupport::Init, + GEN_EXPORT(Shutdown, GAME_SHUTDOWN), + GEN_EXPORT(WriteLevel, GAME_WRITE_LEVEL), + GEN_EXPORT(ReadLevel, GAME_READ_LEVEL), + GEN_EXPORT(GameAllowedToSaveHere, GAME_GAMEALLOWEDTOSAVEHERE), + GEN_EXPORT(ClientConnect, GAME_CLIENT_CONNECT), + GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), + GEN_EXPORT(ClientUserinfoChanged, GAME_CLIENT_USERINFO_CHANGED), + GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), + GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), + GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), + GEN_EXPORT(RunFrame, GAME_RUN_FRAME), + GEN_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND), + GEN_EXPORT(PrintEntClassname, GAME_PRINTENTCLASSNAME), + GEN_EXPORT(ValidateAnimRange, GAME_VALIDATEANIMRANGE), + + // the engine won't use these until after Init, so we can fill these in after each call into the mod's export functions ("vmMain") + nullptr, // gentities + 0, // gentitySize + 0, // num_entities +}; diff --git a/src/game_mohaa.cpp b/src/game_mohaa.cpp index ae022bf..45662cd 100644 --- a/src/game_mohaa.cpp +++ b/src/game_mohaa.cpp @@ -9,6 +9,11 @@ Created By: */ +#include "version.h" + +#if defined(QMM_ARCH_32) + + #define _CRT_SECURE_NO_WARNINGS 1 #include #include @@ -26,320 +31,80 @@ Created By: #include "main.h" #include "util.h" -GEN_GAME_QMM_MSGS(MOHAA); +struct MOHAA_GameSupport : public GameSupport { + virtual const char* EngMsgName(intptr_t msg); + virtual const char* ModMsgName(intptr_t msg); + virtual bool AutoDetect(APIType engine_api); + virtual void* Entry(void* syscall, void*, APIType engine_api); + virtual bool ModLoad(void* entry, APIType mod_api); + virtual void ModUnload(); -GEN_GAME_FUNCS(MOHAA); + virtual intptr_t syscall(intptr_t, ...); + virtual intptr_t vmMain(intptr_t, ...); + virtual const char* DefaultDLLName() { return "game" MOD_DLL; } + virtual const char* DefaultModDir() { return "main"; } + virtual const char* GameName() { return "Medal of Honor: Allied Assault"; } + virtual const char* GameCode() { return "MOHAA"; } -// auto-detection logic for MOHAA -static bool MOHAA_AutoDetect(api_supportedgame* game, APIType engineapi) { - if (engineapi != QMM_API_GETGAMEAPI) - return false; +private: + static void update_exports(); - if (!str_striequal(g_gameinfo.qmm_file, game->dllname)) - return false; + // track entstrings for our G_GET_ENTITY_TOKEN syscall + static std::vector entity_tokens; + static size_t token_counter; + static void SpawnEntities(char* entstring, int levelTime); - if (!str_stristr(g_gameinfo.exe_file, "mohaa")) - return false; + static void DebugCircle(float* arg0, float arg1, float arg2, float arg3, float arg4, float arg5, qboolean arg6); - return true; -} + // a copy of the original import struct that comes from the game engine + static game_import_t orig_import; + // a copy of the original export struct pointer that comes from the mod + static game_export_t* orig_export; -// a copy of the original import struct that comes from the game engine -static game_import_t orig_import; + // struct with lambdas that call QMM's syscall function. this is given to the mod + static game_import_t qmm_import; -// a copy of the original export struct pointer that comes from the mod -static game_export_t* orig_export = nullptr; + // struct with lambdas that call QMM's vmMain function. this is given to the game engine + static game_export_t qmm_export; -// struct with lambdas that call QMM's syscall function. this is given to the mod -static game_import_t qmm_import = { - GEN_IMPORT(Printf, G_PRINTF), - GEN_IMPORT(DPrintf, G_DPRINTF), - GEN_IMPORT(DPrintf2, G_DPRINTF2), - GEN_IMPORT(DebugPrintf, G_DEBUGPRINTF), - GEN_IMPORT(Error, G_ERROR), - GEN_IMPORT(Milliseconds, G_MILLISECONDS), - GEN_IMPORT(LV_ConvertString, G_LV_CONVERTSTRING), - GEN_IMPORT(Malloc, G_MALLOC), - GEN_IMPORT(Free, G_FREE), - GEN_IMPORT(Cvar_Get, G_CVAR_GET), - GEN_IMPORT(cvar_set, G_CVAR_SET), - GEN_IMPORT(cvar_set2, G_CVAR_SET2), - GEN_IMPORT(NextCvar, G_NEXTCVAR), - GEN_IMPORT(Argc, G_ARGC), - GEN_IMPORT(Argv, G_ARGV), - GEN_IMPORT(Args, G_ARGS), - GEN_IMPORT(AddCommand, G_ADDCOMMAND), - GEN_IMPORT(FS_ReadFile, G_FS_READFILE), - GEN_IMPORT(FS_FreeFile, G_FS_FREEFILE), - GEN_IMPORT(FS_WriteFile, G_FS_WRITEFILE), - GEN_IMPORT(FS_FOpenFileWrite, G_FS_FOPEN_FILE_WRITE), - GEN_IMPORT(FS_FOpenFileAppend, G_FS_FOPEN_FILE_APPEND), - GEN_IMPORT(FS_PrepFileWrite, G_FS_PREPFILEWRITE), - GEN_IMPORT(FS_Write, G_FS_WRITE), - GEN_IMPORT(FS_Read, G_FS_READ), - GEN_IMPORT(FS_FCloseFile, G_FS_FCLOSE_FILE), - GEN_IMPORT(FS_Tell, G_FS_TELL), - GEN_IMPORT(FS_Seek, G_FS_SEEK), - GEN_IMPORT(FS_Flush, G_FS_FLUSH), - GEN_IMPORT(FS_FileNewer, G_FS_FILENEWER), - GEN_IMPORT(FS_CanonicalFilename, G_FS_CANONICALFILENAME), - GEN_IMPORT(FS_ListFiles, G_FS_LISTFILES), - GEN_IMPORT(FS_FreeFileList, G_FS_FREEFILELIST), - GEN_IMPORT(GetArchiveFileName, G_GETARCHIVEFILENAME), - GEN_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND), - GEN_IMPORT_1(DebugGraph, G_DEBUGGRAPH, void, float), - GEN_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND), - GEN_IMPORT(DropClient, G_DROP_CLIENT), - GEN_IMPORT(MSG_WriteBits, G_MSG_WRITEBITS), - GEN_IMPORT(MSG_WriteChar, G_MSG_WRITECHAR), - GEN_IMPORT(MSG_WriteByte, G_MSG_WRITEBYTE), - GEN_IMPORT(MSG_WriteSVC, G_MSG_WRITESVC), - GEN_IMPORT(MSG_WriteShort, G_MSG_WRITESHORT), - GEN_IMPORT(MSG_WriteLong, G_MSG_WRITELONG), - GEN_IMPORT_1(MSG_WriteFloat, G_MSG_WRITEFLOAT, void, float), - GEN_IMPORT(MSG_WriteString, G_MSG_WRITESTRING), - GEN_IMPORT_1(MSG_WriteAngle8, G_MSG_WRITEANGLE8, void, float), - GEN_IMPORT_1(MSG_WriteAngle16, G_MSG_WRITEANGLE16, void, float), - GEN_IMPORT_1(MSG_WriteCoord, G_MSG_WRITECOORD, void, float), - GEN_IMPORT(MSG_WriteDir, G_MSG_WRITEDIR), - GEN_IMPORT(MSG_StartCGM, G_MSG_STARTCGM), - GEN_IMPORT(MSG_EndCGM, G_MSG_ENDCGM), - GEN_IMPORT(MSG_SetClient, G_MSG_SETCLIENT), - GEN_IMPORT(SetBroadcastVisible, G_SETBROADCASTVISIBLE), - GEN_IMPORT(SetBroadcastHearable, G_SETBROADCASTHEARABLE), - GEN_IMPORT(SetBroadcastAll, G_SETBROADCASTALL), - GEN_IMPORT(setConfigstring, G_SET_CONFIGSTRING), - GEN_IMPORT(getConfigstring, G_GET_CONFIGSTRING), - GEN_IMPORT(SetUserinfo, G_SET_USERINFO), - GEN_IMPORT(GetUserinfo, G_GET_USERINFO), - GEN_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL), - GEN_IMPORT(ModelBoundsFromName, G_MODELBOUNDSFROMNAME), - GEN_IMPORT(SightTraceEntity, G_SIGHTTRACEENTITY), - GEN_IMPORT(SightTrace, G_SIGHTTRACE), - GEN_IMPORT(trace, G_TRACE), - GEN_IMPORT(GetShader, G_GETSHADER), - GEN_IMPORT(pointcontents, G_POINT_CONTENTS), - GEN_IMPORT(PointBrushnum, G_POINTBRUSHNUM), - GEN_IMPORT(AdjustAreaPortalState, G_ADJUSTAREAPORTALSTATE), - GEN_IMPORT(AreaForPoint, G_AREAFORPOINT), - GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), - GEN_IMPORT(InPVS, G_IN_PVS), - GEN_IMPORT(linkentity, G_LINKENTITY), - GEN_IMPORT(unlinkentity, G_UNLINKENTITY), - GEN_IMPORT(AreaEntities, G_AREAENTITIES), - GEN_IMPORT(ClipToEntity, G_CLIPTOENTITY), - GEN_IMPORT(imageindex, G_IMAGEINDEX), - GEN_IMPORT(itemindex, G_ITEMINDEX), - GEN_IMPORT(soundindex, G_SOUNDINDEX), - GEN_IMPORT(TIKI_RegisterModel, G_TIKI_REGISTERMODEL), - GEN_IMPORT(modeltiki, G_MODELTIKI), - GEN_IMPORT(modeltikianim, G_MODELTIKIANIM), - GEN_IMPORT(SetLightStyle, G_SETLIGHTSTYLE), - GEN_IMPORT(GameDir, G_GAMEDIR), - GEN_IMPORT(setmodel, G_SETMODEL), - GEN_IMPORT(clearmodel, G_CLEARMODEL), - GEN_IMPORT(TIKI_NumAnims, G_TIKI_NUMANIMS), - GEN_IMPORT(TIKI_NumSurfaces, G_TIKI_NUMSURFACES), - GEN_IMPORT(TIKI_NumTags, G_TIKI_NUMTAGS), - GEN_IMPORT_4(TIKI_CalculateBounds, G_TIKI_CALCULATEBOUNDS, void, dtiki_t*, float, vec3_t, vec3_t), - GEN_IMPORT(TIKI_GetSkeletor, G_TIKI_GETSKELETOR), - GEN_IMPORT(Anim_NameForNum, G_ANIM_NAMEFORNUM), - GEN_IMPORT(Anim_NumForName, G_ANIM_NUMFORNAME), - GEN_IMPORT(Anim_Random, G_ANIM_RANDOM), - GEN_IMPORT(Anim_NumFrames, G_ANIM_NUMFRAMES), - GEN_IMPORT(Anim_Time, G_ANIM_TIME), - GEN_IMPORT(Anim_Frametime, G_ANIM_FRAMETIME), - GEN_IMPORT(Anim_CrossTime, G_ANIM_CROSSTIME), - GEN_IMPORT(Anim_Delta, G_ANIM_DELTA), - GEN_IMPORT(Anim_HasDelta, G_ANIM_HASDELTA), - GEN_IMPORT_5(Anim_DeltaOverTime, G_ANIM_DELTAOVERTIME, void, dtiki_t*, int, float, float, float*), - GEN_IMPORT(Anim_Flags, G_ANIM_FLAGS), - GEN_IMPORT(Anim_FlagsSkel, G_ANIM_FLAGSSKEL), - GEN_IMPORT(Anim_HasCommands, G_ANIM_HASCOMMANDS), - GEN_IMPORT(NumHeadModels, G_NUMHEADMODELS), - GEN_IMPORT(GetHeadModel, G_GETHEADMODEL), - GEN_IMPORT(NumHeadSkins, G_NUMHEADSKINS), - GEN_IMPORT(GetHeadSkin, G_GETHEADSKIN), - GEN_IMPORT(Frame_Commands, G_FRAME_COMMANDS), - GEN_IMPORT(Surface_NameToNum, G_SURFACE_NAMETONUM), - GEN_IMPORT(Surface_NumToName, G_SURFACE_NUMTONAME), - GEN_IMPORT(Tag_NumForName, G_TAG_NUMFORNAME), - GEN_IMPORT(Tag_NameForNum, G_TAG_NAMEFORNUM), - GEN_IMPORT(TIKI_OrientationInternal, G_TIKI_ORIENTATIONINTERNAL), // todo: change types to actually match float, but also need to return an intptr_t instead of orientation_t - GEN_IMPORT(TIKI_TransformInternal, G_TIKI_TRANSFORMINTERNAL), - GEN_IMPORT_4(TIKI_IsOnGroundInternal, G_TIKI_ISONGROUNDINTERNAL, qboolean, dtiki_t*, int, int, float), - GEN_IMPORT_6(TIKI_SetPoseInternal, G_TIKI_SETPOSEINTERNAL, void, dtiki_t*, int, const frameInfo_t*, int*, vec4_t*, float), - GEN_IMPORT(CM_GetHitLocationInfo, G_CM_GETHITLOCATIONINFO), - GEN_IMPORT(CM_GetHitLocationInfoSecondary, G_CM_GETHITLOCATIONINFOSECONDARY), - GEN_IMPORT(Alias_Add, G_ALIAS_ADD), - GEN_IMPORT(Alias_FindRandom, G_ALIAS_FINDRANDOM), - GEN_IMPORT(Alias_Dump, G_ALIAS_DUMP), - GEN_IMPORT(Alias_Clear, G_ALIAS_CLEAR), - GEN_IMPORT(Alias_UpdateDialog, G_ALIAS_UPDATEDIALOG), - GEN_IMPORT(TIKI_NameForNum, G_TIKI_NAMEFORNUM), - GEN_IMPORT(GlobalAlias_Add, G_GLOBALALIAS_ADD), - GEN_IMPORT(GlobalAlias_FindRandom, G_GLOBALALIAS_FINDRANDOM), - GEN_IMPORT(GlobalAlias_Dump, G_GLOBALALIAS_DUMP), - GEN_IMPORT(GlobalAlias_Clear, G_GLOBALALIAS_CLEAR), - GEN_IMPORT(centerprintf, G_CENTERPRINTF), - GEN_IMPORT(locationprintf, G_LOCATIONPRINTF), - GEN_IMPORT_9(Sound, G_SOUND, void, vec3_t*, int, int, const char*, float, float, float, float, int), - GEN_IMPORT(StopSound, G_STOPSOUND), - GEN_IMPORT(SoundLength, G_SOUNDLENGTH), - GEN_IMPORT(SoundAmplitudes, G_SOUNDAMPLITUDES), - GEN_IMPORT(S_IsSoundPlaying, G_S_ISSOUNDPLAYING), - GEN_IMPORT(CalcCRC, G_CALCCRC), - nullptr, // DebugLines - nullptr, // numDebugLines - nullptr, // DebugStrings - nullptr, // numDebugStrings - GEN_IMPORT(LocateGameData, G_LOCATE_GAME_DATA), - GEN_IMPORT(SetFarPlane, G_SETFARPLANE), - GEN_IMPORT(SetSkyPortal, G_SETSKYPORTAL), - GEN_IMPORT(Popmenu, G_POPMENU), - GEN_IMPORT(Showmenu, G_SHOWMENU), - GEN_IMPORT(Hidemenu, G_HIDEMENU), - GEN_IMPORT(Pushmenu, G_PUSHMENU), - GEN_IMPORT(HideMouseCursor, G_HIDEMOUSECURSOR), - GEN_IMPORT(ShowMouseCursor, G_SHOWMOUSECURSOR), - GEN_IMPORT(MapTime, G_MAPTIME), - GEN_IMPORT(LoadResource, G_LOADRESOURCE), - GEN_IMPORT(ClearResource, G_CLEARRESOURCE), - GEN_IMPORT(Key_StringToKeynum, G_KEY_STRINGTOKEYNUM), - GEN_IMPORT(Key_KeynumToBindString, G_KEY_KEYNUMTOBINDSTRING), - GEN_IMPORT(Key_GetKeysForCommand, G_KEY_GETKEYSFORCOMMAND), - GEN_IMPORT(ArchiveLevel, G_ARCHIVELEVEL), - GEN_IMPORT(AddSvsTimeFixup, G_ADDSVSTIMEFIXUP), - GEN_IMPORT(HudDrawShader, G_HUDDRAWSHADER), - GEN_IMPORT(HudDrawAlign, G_HUDDRAWALIGN), - GEN_IMPORT(HudDrawRect, G_HUDDRAWRECT), - GEN_IMPORT(HudDrawVirtualSize, G_HUDDRAWVIRTUALSIZE), - GEN_IMPORT(HudDrawColor, G_HUDDRAWCOLOR), - GEN_IMPORT_2(HudDrawAlpha, G_HUDDRAWALPHA, void, int, float), - GEN_IMPORT(HudDrawString, G_HUDDRAWSTRING), - GEN_IMPORT(HudDrawFont, G_HUDDRAWFONT), - GEN_IMPORT(SanitizeName, G_SANITIZENAME), - nullptr, //fsDebug + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = GEN_GAME_QMM_MOD_MSGS(); }; +GEN_GAME_OBJ(MOHAA); -// these are "pre" hooks for storing some data for polyfills. -// we need these to be called BEFORE plugins' prehooks get called so they have to be done in the qmm_export table - -// track entstrings for our G_GET_ENTITY_TOKEN syscall -static std::vector s_entity_tokens; -static size_t s_tokencount = 0; -static void MOHAA_SpawnEntities(char* entstring, int levelTime) { - if (entstring) { - s_entity_tokens = util_parse_entstring(entstring); - s_tokencount = 0; - } - cgame.is_from_QMM = true; - vmMain(GAME_SPAWN_ENTITIES, entstring, levelTime); -} +// auto-detection logic for MOHAA +bool MOHAA_GameSupport::AutoDetect(APIType engineapi) { + if (engineapi != QMM_API_GETGAMEAPI) + return false; -// at least one of first four args is a float (see big comment at top of game_q2r.cpp), so use specific types -static void MOHAA_DebugCircle(float* arg0, float arg1, float arg2, float arg3, float arg4, float arg5, qboolean arg6) { - cgame.is_from_QMM = true; - vmMain(GAME_DEBUG_CIRCLE, arg0, arg1, arg2, arg3, arg4, arg5, arg6); -} + if (!str_striequal(g_gameinfo.qmm_file, DefaultDLLName())) + return false; + if (!str_stristr(g_gameinfo.exe_file, "mohaa")) + return false; -// struct with lambdas that call QMM's vmMain function. this is given to the game engine -static game_export_t qmm_export = { - GAME_API_VERSION, // apiversion - GEN_EXPORT(Init, GAME_INIT), - GEN_EXPORT(Shutdown, GAME_SHUTDOWN), - GEN_EXPORT(Cleanup, GAME_CLEANUP), - GEN_EXPORT(Precache, GAME_PRECACHE), - GEN_EXPORT(SetMap, GAME_SETMAP), - GEN_EXPORT(Restart, GAME_RESTART), - GEN_EXPORT(SetTime, GAME_SETTIME), - MOHAA_SpawnEntities, - GEN_EXPORT(ClientConnect, GAME_CLIENT_CONNECT), - GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), - GEN_EXPORT(ClientUserinfoChanged, GAME_CLIENT_USERINFO_CHANGED), - GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), - GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), - GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), - GEN_EXPORT(BotBegin, GAME_BOTBEGIN), - GEN_EXPORT(BotThink, GAME_BOTTHINK), - GEN_EXPORT(PrepFrame, GAME_PREP_FRAME), - GEN_EXPORT(RunFrame, GAME_RUN_FRAME), - GEN_EXPORT(ServerSpawned, GAME_SERVER_SPAWNED), - GEN_EXPORT(RegisterSounds, GAME_REGISTER_SOUNDS), - GEN_EXPORT(AllowPaused, GAME_ALLOW_PAUSED), - GEN_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND), - GEN_EXPORT(ArchivePersistant, GAME_ARCHIVE_PERSISTANT), - GEN_EXPORT(WriteLevel, GAME_WRITE_LEVEL), - GEN_EXPORT(ReadLevel, GAME_READ_LEVEL), - GEN_EXPORT(LevelArchiveValid, GAME_LEVEL_ARCHIVE_VALID), - GEN_EXPORT(ArchiveInteger, GAME_ARCHIVE_INTEGER), - GEN_EXPORT(ArchiveFloat, GAME_ARCHIVE_FLOAT), - GEN_EXPORT(ArchiveString, GAME_ARCHIVE_STRING), - GEN_EXPORT(ArchiveSvsTime, GAME_ARCHIVE_SVSTIME), - GEN_EXPORT(TIKI_Orientation, GAME_TIKI_ORIENTATION), // todo: change types to actually match float, but also need to return an intptr_t instead of orientation_t - MOHAA_DebugCircle, - GEN_EXPORT(SetFrameNumber, GAME_SET_FRAME_NUMBER), - GEN_EXPORT(SoundCallback, GAME_SOUND_CALLBACK), + return true; +} - // the engine won't use these until after Init, so we can fill these in after each call into the mod's export functions ("vmMain") - nullptr, // profStruct - nullptr, // gentities - sizeof(gentity_t), // gentitySize - 0, // num_entities - 0, // max_entities - nullptr, // errorMessage -}; +// wrapper syscall function that calls actual engine func from orig_import +// this is how QMM and plugins will call into the engine +intptr_t MOHAA_GameSupport::syscall(intptr_t cmd, ...) { + QMM_GET_SYSCALL_ARGS(); -// update the export variables from orig_export -static void s_update_export() { - if (!orig_export) - return; +#ifdef _DEBUG + if (cmd != G_PRINT) + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHAA_GameSupport::syscall({} {}) called\n", EngMsgName(cmd), cmd); +#endif - bool changed = false; + // update export vars before calling into the engine + update_exports(); - // if entity data changed, we need to send a G_LOCATE_GAME_DATA so plugins can hook it - if (qmm_export.gentities != orig_export->gentities - || qmm_export.gentitySize != orig_export->gentitySize - || qmm_export.num_entities != orig_export->num_entities - ) { - changed = true; - } - - qmm_export.profStruct = orig_export->profStruct; - qmm_export.gentities = orig_export->gentities; - qmm_export.gentitySize = orig_export->gentitySize; - qmm_export.num_entities = orig_export->num_entities; - qmm_export.max_entities = orig_export->max_entities; - qmm_export.errorMessage = orig_export->errorMessage; - - if (changed) { - // this will trigger this message to be fired to plugins, and then it will be handled - // by the empty "case G_LOCATE_GAME_DATA" in MOHAA_syscall - qmm_syscall(G_LOCATE_GAME_DATA, (intptr_t)qmm_export.gentities, qmm_export.num_entities, qmm_export.gentitySize, nullptr, 0); - } -} - - -// wrapper syscall function that calls actual engine func from orig_import -// this is how QMM and plugins will call into the engine -static intptr_t MOHAA_syscall(intptr_t cmd, ...) { - QMM_GET_SYSCALL_ARGS(); - -#ifdef _DEBUG - if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHAA_syscall({} {}) called\n", MOHAA_EngMsgName(cmd), cmd); -#endif - - // update export vars before calling into the engine - s_update_export(); - - intptr_t ret = 0; + intptr_t ret = 0; switch (cmd) { ROUTE_IMPORT(Printf, G_PRINTF); @@ -639,7 +404,7 @@ static intptr_t MOHAA_syscall(intptr_t cmd, ...) { } case G_GET_ENTITY_TOKEN: { // qboolean trap_GetEntityToken(char *buffer, int bufferSize); - if (s_tokencount >= s_entity_tokens.size()) { + if (token_counter >= entity_tokens.size()) { ret = qfalse; break; } @@ -647,7 +412,7 @@ static intptr_t MOHAA_syscall(intptr_t cmd, ...) { char* buffer = (char*)args[0]; intptr_t bufferSize = args[1]; - strncpyz(buffer, s_entity_tokens[s_tokencount++].c_str(), (size_t)bufferSize); + strncpyz(buffer, entity_tokens[token_counter++].c_str(), (size_t)bufferSize); ret = qtrue; break; } @@ -660,7 +425,7 @@ static intptr_t MOHAA_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHAA_syscall({} {}) returning {}\n", MOHAA_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHAA_GameSupport::syscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; @@ -669,11 +434,11 @@ static intptr_t MOHAA_syscall(intptr_t cmd, ...) { // wrapper vmMain function that calls actual mod func from orig_export // this is how QMM and plugins will call into the mod -static intptr_t MOHAA_vmMain(intptr_t cmd, ...) { +intptr_t MOHAA_GameSupport::vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHAA_vmMain({} {}) called\n", MOHAA_ModMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHAA_GameSupport::vmMain({} {}) called\n", ModMsgName(cmd), cmd); #endif if (!orig_export) @@ -732,18 +497,18 @@ static intptr_t MOHAA_vmMain(intptr_t cmd, ...) { }; // update export vars after returning from the mod - s_update_export(); + update_exports(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHAA_vmMain({} {}) returning {}\n", MOHAA_ModMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHAA_GameSupport::vmMain({} {}) returning {}\n", ModMsgName(cmd), cmd, ret); #endif return ret; } -static void* MOHAA_Entry(void* import, void*, APIType) { - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHAA_Entry({}) called\n", import); +void* MOHAA_GameSupport::Entry(void* import, void*, APIType) { + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHAA_GameSupport::Entry({}) called\n", import); // original import struct from engine // the struct given by the engine goes out of scope after this returns so we have to copy the whole thing @@ -757,13 +522,7 @@ static void* MOHAA_Entry(void* import, void*, APIType) { qmm_import.numDebugStrings = orig_import.numDebugStrings; qmm_import.fsDebug = orig_import.fsDebug; - // pointer to wrapper vmMain function that calls actual mod func from orig_export - g_gameinfo.pfnvmMain = MOHAA_vmMain; - - // pointer to wrapper syscall function that calls actual engine func from orig_import - g_gameinfo.pfnsyscall = MOHAA_syscall; - - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHAA_Entry({}) returning {}\n", import, fmt::ptr(&qmm_export)); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHAA_GameSupport::Entry({}) returning {}\n", import, fmt::ptr(&qmm_export)); // struct full of export lambdas to QMM's vmMain // this gets returned to the game engine, but we haven't loaded the mod yet. @@ -772,7 +531,7 @@ static void* MOHAA_Entry(void* import, void*, APIType) { } -static bool MOHAA_ModLoad(void* entry, APIType modapi) { +bool MOHAA_GameSupport::ModLoad(void* entry, APIType modapi) { if (modapi != QMM_API_GETGAMEAPI) return false; @@ -783,12 +542,12 @@ static bool MOHAA_ModLoad(void* entry, APIType modapi) { } -static void MOHAA_ModUnload() { +void MOHAA_GameSupport::ModUnload() { orig_export = nullptr; } -static const char* MOHAA_EngMsgName(intptr_t cmd) { +const char* MOHAA_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINTF); GEN_CASE(G_DPRINTF); @@ -970,7 +729,7 @@ static const char* MOHAA_EngMsgName(intptr_t cmd) { } -static const char* MOHAA_ModMsgName(intptr_t cmd) { +const char* MOHAA_GameSupport::ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAMEV_APIVERSION); GEN_CASE(GAME_INIT); @@ -1017,3 +776,279 @@ static const char* MOHAA_ModMsgName(intptr_t cmd) { return "unknown"; } } + + +void MOHAA_GameSupport::update_exports() { + if (!orig_export) + return; + + bool changed = false; + + // if entity data changed, we need to send a G_LOCATE_GAME_DATA so plugins can hook it + if (qmm_export.gentities != orig_export->gentities + || qmm_export.gentitySize != orig_export->gentitySize + || qmm_export.num_entities != orig_export->num_entities + ) { + changed = true; + } + + qmm_export.profStruct = orig_export->profStruct; + qmm_export.gentities = orig_export->gentities; + qmm_export.gentitySize = orig_export->gentitySize; + qmm_export.num_entities = orig_export->num_entities; + qmm_export.max_entities = orig_export->max_entities; + qmm_export.errorMessage = orig_export->errorMessage; + + if (changed) { + // this will trigger this message to be fired to plugins, and then it will be handled + // by the empty "case G_LOCATE_GAME_DATA" in MOHAA_syscall + qmm_syscall(G_LOCATE_GAME_DATA, (intptr_t)qmm_export.gentities, qmm_export.num_entities, qmm_export.gentitySize, nullptr, 0); + } +} + + +game_import_t MOHAA_GameSupport::orig_import; + + +game_export_t* MOHAA_GameSupport::orig_export = nullptr; + + +game_import_t MOHAA_GameSupport::qmm_import = { + GEN_IMPORT(Printf, G_PRINTF), + GEN_IMPORT(DPrintf, G_DPRINTF), + GEN_IMPORT(DPrintf2, G_DPRINTF2), + GEN_IMPORT(DebugPrintf, G_DEBUGPRINTF), + GEN_IMPORT(Error, G_ERROR), + GEN_IMPORT(Milliseconds, G_MILLISECONDS), + GEN_IMPORT(LV_ConvertString, G_LV_CONVERTSTRING), + GEN_IMPORT(Malloc, G_MALLOC), + GEN_IMPORT(Free, G_FREE), + GEN_IMPORT(Cvar_Get, G_CVAR_GET), + GEN_IMPORT(cvar_set, G_CVAR_SET), + GEN_IMPORT(cvar_set2, G_CVAR_SET2), + GEN_IMPORT(NextCvar, G_NEXTCVAR), + GEN_IMPORT(Argc, G_ARGC), + GEN_IMPORT(Argv, G_ARGV), + GEN_IMPORT(Args, G_ARGS), + GEN_IMPORT(AddCommand, G_ADDCOMMAND), + GEN_IMPORT(FS_ReadFile, G_FS_READFILE), + GEN_IMPORT(FS_FreeFile, G_FS_FREEFILE), + GEN_IMPORT(FS_WriteFile, G_FS_WRITEFILE), + GEN_IMPORT(FS_FOpenFileWrite, G_FS_FOPEN_FILE_WRITE), + GEN_IMPORT(FS_FOpenFileAppend, G_FS_FOPEN_FILE_APPEND), + GEN_IMPORT(FS_PrepFileWrite, G_FS_PREPFILEWRITE), + GEN_IMPORT(FS_Write, G_FS_WRITE), + GEN_IMPORT(FS_Read, G_FS_READ), + GEN_IMPORT(FS_FCloseFile, G_FS_FCLOSE_FILE), + GEN_IMPORT(FS_Tell, G_FS_TELL), + GEN_IMPORT(FS_Seek, G_FS_SEEK), + GEN_IMPORT(FS_Flush, G_FS_FLUSH), + GEN_IMPORT(FS_FileNewer, G_FS_FILENEWER), + GEN_IMPORT(FS_CanonicalFilename, G_FS_CANONICALFILENAME), + GEN_IMPORT(FS_ListFiles, G_FS_LISTFILES), + GEN_IMPORT(FS_FreeFileList, G_FS_FREEFILELIST), + GEN_IMPORT(GetArchiveFileName, G_GETARCHIVEFILENAME), + GEN_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND), + GEN_IMPORT_1(DebugGraph, G_DEBUGGRAPH, void, float), + GEN_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND), + GEN_IMPORT(DropClient, G_DROP_CLIENT), + GEN_IMPORT(MSG_WriteBits, G_MSG_WRITEBITS), + GEN_IMPORT(MSG_WriteChar, G_MSG_WRITECHAR), + GEN_IMPORT(MSG_WriteByte, G_MSG_WRITEBYTE), + GEN_IMPORT(MSG_WriteSVC, G_MSG_WRITESVC), + GEN_IMPORT(MSG_WriteShort, G_MSG_WRITESHORT), + GEN_IMPORT(MSG_WriteLong, G_MSG_WRITELONG), + GEN_IMPORT_1(MSG_WriteFloat, G_MSG_WRITEFLOAT, void, float), + GEN_IMPORT(MSG_WriteString, G_MSG_WRITESTRING), + GEN_IMPORT_1(MSG_WriteAngle8, G_MSG_WRITEANGLE8, void, float), + GEN_IMPORT_1(MSG_WriteAngle16, G_MSG_WRITEANGLE16, void, float), + GEN_IMPORT_1(MSG_WriteCoord, G_MSG_WRITECOORD, void, float), + GEN_IMPORT(MSG_WriteDir, G_MSG_WRITEDIR), + GEN_IMPORT(MSG_StartCGM, G_MSG_STARTCGM), + GEN_IMPORT(MSG_EndCGM, G_MSG_ENDCGM), + GEN_IMPORT(MSG_SetClient, G_MSG_SETCLIENT), + GEN_IMPORT(SetBroadcastVisible, G_SETBROADCASTVISIBLE), + GEN_IMPORT(SetBroadcastHearable, G_SETBROADCASTHEARABLE), + GEN_IMPORT(SetBroadcastAll, G_SETBROADCASTALL), + GEN_IMPORT(setConfigstring, G_SET_CONFIGSTRING), + GEN_IMPORT(getConfigstring, G_GET_CONFIGSTRING), + GEN_IMPORT(SetUserinfo, G_SET_USERINFO), + GEN_IMPORT(GetUserinfo, G_GET_USERINFO), + GEN_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL), + GEN_IMPORT(ModelBoundsFromName, G_MODELBOUNDSFROMNAME), + GEN_IMPORT(SightTraceEntity, G_SIGHTTRACEENTITY), + GEN_IMPORT(SightTrace, G_SIGHTTRACE), + GEN_IMPORT(trace, G_TRACE), + GEN_IMPORT(GetShader, G_GETSHADER), + GEN_IMPORT(pointcontents, G_POINT_CONTENTS), + GEN_IMPORT(PointBrushnum, G_POINTBRUSHNUM), + GEN_IMPORT(AdjustAreaPortalState, G_ADJUSTAREAPORTALSTATE), + GEN_IMPORT(AreaForPoint, G_AREAFORPOINT), + GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), + GEN_IMPORT(InPVS, G_IN_PVS), + GEN_IMPORT(linkentity, G_LINKENTITY), + GEN_IMPORT(unlinkentity, G_UNLINKENTITY), + GEN_IMPORT(AreaEntities, G_AREAENTITIES), + GEN_IMPORT(ClipToEntity, G_CLIPTOENTITY), + GEN_IMPORT(imageindex, G_IMAGEINDEX), + GEN_IMPORT(itemindex, G_ITEMINDEX), + GEN_IMPORT(soundindex, G_SOUNDINDEX), + GEN_IMPORT(TIKI_RegisterModel, G_TIKI_REGISTERMODEL), + GEN_IMPORT(modeltiki, G_MODELTIKI), + GEN_IMPORT(modeltikianim, G_MODELTIKIANIM), + GEN_IMPORT(SetLightStyle, G_SETLIGHTSTYLE), + GEN_IMPORT(GameDir, G_GAMEDIR), + GEN_IMPORT(setmodel, G_SETMODEL), + GEN_IMPORT(clearmodel, G_CLEARMODEL), + GEN_IMPORT(TIKI_NumAnims, G_TIKI_NUMANIMS), + GEN_IMPORT(TIKI_NumSurfaces, G_TIKI_NUMSURFACES), + GEN_IMPORT(TIKI_NumTags, G_TIKI_NUMTAGS), + GEN_IMPORT_4(TIKI_CalculateBounds, G_TIKI_CALCULATEBOUNDS, void, dtiki_t*, float, vec3_t, vec3_t), + GEN_IMPORT(TIKI_GetSkeletor, G_TIKI_GETSKELETOR), + GEN_IMPORT(Anim_NameForNum, G_ANIM_NAMEFORNUM), + GEN_IMPORT(Anim_NumForName, G_ANIM_NUMFORNAME), + GEN_IMPORT(Anim_Random, G_ANIM_RANDOM), + GEN_IMPORT(Anim_NumFrames, G_ANIM_NUMFRAMES), + GEN_IMPORT(Anim_Time, G_ANIM_TIME), + GEN_IMPORT(Anim_Frametime, G_ANIM_FRAMETIME), + GEN_IMPORT(Anim_CrossTime, G_ANIM_CROSSTIME), + GEN_IMPORT(Anim_Delta, G_ANIM_DELTA), + GEN_IMPORT(Anim_HasDelta, G_ANIM_HASDELTA), + GEN_IMPORT_5(Anim_DeltaOverTime, G_ANIM_DELTAOVERTIME, void, dtiki_t*, int, float, float, float*), + GEN_IMPORT(Anim_Flags, G_ANIM_FLAGS), + GEN_IMPORT(Anim_FlagsSkel, G_ANIM_FLAGSSKEL), + GEN_IMPORT(Anim_HasCommands, G_ANIM_HASCOMMANDS), + GEN_IMPORT(NumHeadModels, G_NUMHEADMODELS), + GEN_IMPORT(GetHeadModel, G_GETHEADMODEL), + GEN_IMPORT(NumHeadSkins, G_NUMHEADSKINS), + GEN_IMPORT(GetHeadSkin, G_GETHEADSKIN), + GEN_IMPORT(Frame_Commands, G_FRAME_COMMANDS), + GEN_IMPORT(Surface_NameToNum, G_SURFACE_NAMETONUM), + GEN_IMPORT(Surface_NumToName, G_SURFACE_NUMTONAME), + GEN_IMPORT(Tag_NumForName, G_TAG_NUMFORNAME), + GEN_IMPORT(Tag_NameForNum, G_TAG_NAMEFORNUM), + GEN_IMPORT(TIKI_OrientationInternal, G_TIKI_ORIENTATIONINTERNAL), // todo: change types to actually match float, but also need to return an intptr_t instead of orientation_t + GEN_IMPORT(TIKI_TransformInternal, G_TIKI_TRANSFORMINTERNAL), + GEN_IMPORT_4(TIKI_IsOnGroundInternal, G_TIKI_ISONGROUNDINTERNAL, qboolean, dtiki_t*, int, int, float), + GEN_IMPORT_6(TIKI_SetPoseInternal, G_TIKI_SETPOSEINTERNAL, void, dtiki_t*, int, const frameInfo_t*, int*, vec4_t*, float), + GEN_IMPORT(CM_GetHitLocationInfo, G_CM_GETHITLOCATIONINFO), + GEN_IMPORT(CM_GetHitLocationInfoSecondary, G_CM_GETHITLOCATIONINFOSECONDARY), + GEN_IMPORT(Alias_Add, G_ALIAS_ADD), + GEN_IMPORT(Alias_FindRandom, G_ALIAS_FINDRANDOM), + GEN_IMPORT(Alias_Dump, G_ALIAS_DUMP), + GEN_IMPORT(Alias_Clear, G_ALIAS_CLEAR), + GEN_IMPORT(Alias_UpdateDialog, G_ALIAS_UPDATEDIALOG), + GEN_IMPORT(TIKI_NameForNum, G_TIKI_NAMEFORNUM), + GEN_IMPORT(GlobalAlias_Add, G_GLOBALALIAS_ADD), + GEN_IMPORT(GlobalAlias_FindRandom, G_GLOBALALIAS_FINDRANDOM), + GEN_IMPORT(GlobalAlias_Dump, G_GLOBALALIAS_DUMP), + GEN_IMPORT(GlobalAlias_Clear, G_GLOBALALIAS_CLEAR), + GEN_IMPORT(centerprintf, G_CENTERPRINTF), + GEN_IMPORT(locationprintf, G_LOCATIONPRINTF), + GEN_IMPORT_9(Sound, G_SOUND, void, vec3_t*, int, int, const char*, float, float, float, float, int), + GEN_IMPORT(StopSound, G_STOPSOUND), + GEN_IMPORT(SoundLength, G_SOUNDLENGTH), + GEN_IMPORT(SoundAmplitudes, G_SOUNDAMPLITUDES), + GEN_IMPORT(S_IsSoundPlaying, G_S_ISSOUNDPLAYING), + GEN_IMPORT(CalcCRC, G_CALCCRC), + nullptr, // DebugLines + nullptr, // numDebugLines + nullptr, // DebugStrings + nullptr, // numDebugStrings + GEN_IMPORT(LocateGameData, G_LOCATE_GAME_DATA), + GEN_IMPORT(SetFarPlane, G_SETFARPLANE), + GEN_IMPORT(SetSkyPortal, G_SETSKYPORTAL), + GEN_IMPORT(Popmenu, G_POPMENU), + GEN_IMPORT(Showmenu, G_SHOWMENU), + GEN_IMPORT(Hidemenu, G_HIDEMENU), + GEN_IMPORT(Pushmenu, G_PUSHMENU), + GEN_IMPORT(HideMouseCursor, G_HIDEMOUSECURSOR), + GEN_IMPORT(ShowMouseCursor, G_SHOWMOUSECURSOR), + GEN_IMPORT(MapTime, G_MAPTIME), + GEN_IMPORT(LoadResource, G_LOADRESOURCE), + GEN_IMPORT(ClearResource, G_CLEARRESOURCE), + GEN_IMPORT(Key_StringToKeynum, G_KEY_STRINGTOKEYNUM), + GEN_IMPORT(Key_KeynumToBindString, G_KEY_KEYNUMTOBINDSTRING), + GEN_IMPORT(Key_GetKeysForCommand, G_KEY_GETKEYSFORCOMMAND), + GEN_IMPORT(ArchiveLevel, G_ARCHIVELEVEL), + GEN_IMPORT(AddSvsTimeFixup, G_ADDSVSTIMEFIXUP), + GEN_IMPORT(HudDrawShader, G_HUDDRAWSHADER), + GEN_IMPORT(HudDrawAlign, G_HUDDRAWALIGN), + GEN_IMPORT(HudDrawRect, G_HUDDRAWRECT), + GEN_IMPORT(HudDrawVirtualSize, G_HUDDRAWVIRTUALSIZE), + GEN_IMPORT(HudDrawColor, G_HUDDRAWCOLOR), + GEN_IMPORT_2(HudDrawAlpha, G_HUDDRAWALPHA, void, int, float), + GEN_IMPORT(HudDrawString, G_HUDDRAWSTRING), + GEN_IMPORT(HudDrawFont, G_HUDDRAWFONT), + GEN_IMPORT(SanitizeName, G_SANITIZENAME), + nullptr, //fsDebug +}; + + +std::vector MOHAA_GameSupport::entity_tokens; +size_t MOHAA_GameSupport::token_counter = 0; +void MOHAA_GameSupport::SpawnEntities(char* entstring, int levelTime) { + if (entstring) { + entity_tokens = util_parse_entstring(entstring); + token_counter = 0; + } + cgame.is_from_QMM = true; + ::vmMain(GAME_SPAWN_ENTITIES, entstring, levelTime); +} + + +// at least one of first four args is a float (see big comment regarding sound in game_q2r.cpp), so use specific types +void MOHAA_GameSupport::DebugCircle(float* arg0, float arg1, float arg2, float arg3, float arg4, float arg5, qboolean arg6) { + cgame.is_from_QMM = true; + ::vmMain(GAME_DEBUG_CIRCLE, arg0, arg1, arg2, arg3, arg4, arg5, arg6); +} + + +// struct with lambdas that call QMM's vmMain function. this is given to the game engine +game_export_t MOHAA_GameSupport::qmm_export = { + GAME_API_VERSION, // apiversion + GEN_EXPORT(Init, GAME_INIT), + GEN_EXPORT(Shutdown, GAME_SHUTDOWN), + GEN_EXPORT(Cleanup, GAME_CLEANUP), + GEN_EXPORT(Precache, GAME_PRECACHE), + GEN_EXPORT(SetMap, GAME_SETMAP), + GEN_EXPORT(Restart, GAME_RESTART), + GEN_EXPORT(SetTime, GAME_SETTIME), + MOHAA_GameSupport::SpawnEntities, + GEN_EXPORT(ClientConnect, GAME_CLIENT_CONNECT), + GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), + GEN_EXPORT(ClientUserinfoChanged, GAME_CLIENT_USERINFO_CHANGED), + GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), + GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), + GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), + GEN_EXPORT(BotBegin, GAME_BOTBEGIN), + GEN_EXPORT(BotThink, GAME_BOTTHINK), + GEN_EXPORT(PrepFrame, GAME_PREP_FRAME), + GEN_EXPORT(RunFrame, GAME_RUN_FRAME), + GEN_EXPORT(ServerSpawned, GAME_SERVER_SPAWNED), + GEN_EXPORT(RegisterSounds, GAME_REGISTER_SOUNDS), + GEN_EXPORT(AllowPaused, GAME_ALLOW_PAUSED), + GEN_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND), + GEN_EXPORT(ArchivePersistant, GAME_ARCHIVE_PERSISTANT), + GEN_EXPORT(WriteLevel, GAME_WRITE_LEVEL), + GEN_EXPORT(ReadLevel, GAME_READ_LEVEL), + GEN_EXPORT(LevelArchiveValid, GAME_LEVEL_ARCHIVE_VALID), + GEN_EXPORT(ArchiveInteger, GAME_ARCHIVE_INTEGER), + GEN_EXPORT(ArchiveFloat, GAME_ARCHIVE_FLOAT), + GEN_EXPORT(ArchiveString, GAME_ARCHIVE_STRING), + GEN_EXPORT(ArchiveSvsTime, GAME_ARCHIVE_SVSTIME), + GEN_EXPORT(TIKI_Orientation, GAME_TIKI_ORIENTATION), // todo: change types to actually match float, but also need to return an intptr_t instead of orientation_t + MOHAA_GameSupport::DebugCircle, + GEN_EXPORT(SetFrameNumber, GAME_SET_FRAME_NUMBER), + GEN_EXPORT(SoundCallback, GAME_SOUND_CALLBACK), + + // the engine won't use these until after Init, so we can fill these in after each call into the mod's export functions ("vmMain") + nullptr, // profStruct + nullptr, // gentities + sizeof(gentity_t), // gentitySize + 0, // num_entities + 0, // max_entities + nullptr, // errorMessage +}; + +#endif // QMM_ARCH_32 diff --git a/src/game_mohbt.cpp b/src/game_mohbt.cpp index 7b4ff65..c0375b2 100644 --- a/src/game_mohbt.cpp +++ b/src/game_mohbt.cpp @@ -9,6 +9,10 @@ Created By: */ +#include "version.h" + +#if defined(QMM_ARCH_32) + #define _CRT_SECURE_NO_WARNINGS 1 #include #include @@ -22,332 +26,84 @@ Created By: #include #include // QMM-specific MOHBT header -#include "game_mohsh.h" +#include "game_mohbt.h" #include "main.h" #include "util.h" -GEN_GAME_QMM_MSGS(MOHBT); +struct MOHBT_GameSupport : public GameSupport { + virtual const char* EngMsgName(intptr_t msg); + virtual const char* ModMsgName(intptr_t msg); + virtual bool AutoDetect(APIType engine_api); + virtual void* Entry(void* syscall, void*, APIType engine_api); + virtual bool ModLoad(void* entry, APIType mod_api); + virtual void ModUnload(); -GEN_GAME_FUNCS(MOHBT); + virtual intptr_t syscall(intptr_t, ...); + virtual intptr_t vmMain(intptr_t, ...); + virtual const char* DefaultDLLName() { return "game" MOD_DLL; } + virtual const char* DefaultModDir() { return "maintt"; } + virtual const char* GameName() { return "Medal of Honor: Breakthrough"; } + virtual const char* GameCode() { return "MOHBT"; } -// auto-detection logic for MOHBT -static bool MOHBT_AutoDetect(api_supportedgame* game, APIType engineapi) { - if (engineapi != QMM_API_GETGAMEAPI) - return false; +private: + static void update_exports(); - if (!str_striequal(g_gameinfo.qmm_file, game->dllname)) - return false; + // track entstrings for our G_GET_ENTITY_TOKEN syscall + static std::vector entity_tokens; + static size_t token_counter; + static void SpawnEntities(char* entstring, int levelTime); - if (!str_stristr(g_gameinfo.exe_file, "break")) - return false; + static void DebugCircle(float* arg0, float arg1, float arg2, float arg3, float arg4, float arg5, qboolean arg6); - return true; -} + // a copy of the original import struct that comes from the game engine + static game_import_t orig_import; + // a copy of the original export struct pointer that comes from the mod + static game_export_t* orig_export; -// a copy of the original import struct that comes from the game engine -static game_import_t orig_import; + // struct with lambdas that call QMM's syscall function. this is given to the mod + static game_import_t qmm_import; -// a copy of the original export struct pointer that comes from the mod -static game_export_t* orig_export = nullptr; + // struct with lambdas that call QMM's vmMain function. this is given to the game engine + static game_export_t qmm_export; -// struct with lambdas that call QMM's syscall function. this is given to the mod -static game_import_t qmm_import = { - GEN_IMPORT(Printf, G_PRINTF), - GEN_IMPORT(DPrintf, G_DPRINTF), - GEN_IMPORT(DPrintf2, G_DPRINTF2), - GEN_IMPORT(DebugPrintf, G_DEBUGPRINTF), - GEN_IMPORT(Error, G_ERROR), - GEN_IMPORT(Milliseconds, G_MILLISECONDS), - GEN_IMPORT(LV_ConvertString, G_LV_CONVERTSTRING), - GEN_IMPORT(CL_LV_ConvertString, G_CL_LV_CONVERTSTRING), - GEN_IMPORT(Malloc, G_MALLOC), - GEN_IMPORT(Free, G_FREE), - GEN_IMPORT(Cvar_Get, G_CVAR_GET), - GEN_IMPORT(cvar_set, G_CVAR_SET), - GEN_IMPORT(cvar_set2, G_CVAR_SET2), - GEN_IMPORT(NextCvar, G_NEXTCVAR), - GEN_IMPORT(Argc, G_ARGC), - GEN_IMPORT(Argv, G_ARGV), - GEN_IMPORT(Args, G_ARGS), - GEN_IMPORT(AddCommand, G_ADDCOMMAND), - GEN_IMPORT(FS_ReadFile, G_FS_READFILE), - GEN_IMPORT(FS_FreeFile, G_FS_FREEFILE), - GEN_IMPORT(FS_WriteFile, G_FS_WRITEFILE), - GEN_IMPORT(FS_FOpenFileWrite, G_FS_FOPEN_FILE_WRITE), - GEN_IMPORT(FS_FOpenFileAppend, G_FS_FOPEN_FILE_APPEND), - GEN_IMPORT(FS_FOpenFile, G_FS_UNKNOWN), - GEN_IMPORT(FS_PrepFileWrite, G_FS_PREPFILEWRITE), - GEN_IMPORT(FS_Write, G_FS_WRITE), - GEN_IMPORT(FS_Read, G_FS_READ), - GEN_IMPORT(FS_FCloseFile, G_FS_FCLOSE_FILE), - GEN_IMPORT(FS_Tell, G_FS_TELL), - GEN_IMPORT(FS_Seek, G_FS_SEEK), - GEN_IMPORT(FS_Flush, G_FS_FLUSH), - GEN_IMPORT(FS_FileNewer, G_FS_FILENEWER), - GEN_IMPORT(FS_CanonicalFilename, G_FS_CANONICALFILENAME), - GEN_IMPORT(FS_ListFiles, G_FS_LISTFILES), - GEN_IMPORT(FS_FreeFileList, G_FS_FREEFILELIST), - GEN_IMPORT(GetArchiveFileName, G_GETARCHIVEFILENAME), - GEN_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND), - GEN_IMPORT(ExecuteConsoleCommand, G_EXECUTE_CONSOLE_COMMAND), - GEN_IMPORT_1(DebugGraph, G_DEBUGGRAPH, void, float), - GEN_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND), - GEN_IMPORT(DropClient, G_DROP_CLIENT), - GEN_IMPORT(MSG_WriteBits, G_MSG_WRITEBITS), - GEN_IMPORT(MSG_WriteChar, G_MSG_WRITECHAR), - GEN_IMPORT(MSG_WriteByte, G_MSG_WRITEBYTE), - GEN_IMPORT(MSG_WriteSVC, G_MSG_WRITESVC), - GEN_IMPORT(MSG_WriteShort, G_MSG_WRITESHORT), - GEN_IMPORT(MSG_WriteLong, G_MSG_WRITELONG), - GEN_IMPORT_1(MSG_WriteFloat, G_MSG_WRITEFLOAT, void, float), - GEN_IMPORT(MSG_WriteString, G_MSG_WRITESTRING), - GEN_IMPORT_1(MSG_WriteAngle8, G_MSG_WRITEANGLE8, void, float), - GEN_IMPORT_1(MSG_WriteAngle16, G_MSG_WRITEANGLE16, void, float), - GEN_IMPORT_1(MSG_WriteCoord, G_MSG_WRITECOORD, void, float), - GEN_IMPORT(MSG_WriteDir, G_MSG_WRITEDIR), - GEN_IMPORT(MSG_StartCGM, G_MSG_STARTCGM), - GEN_IMPORT(MSG_EndCGM, G_MSG_ENDCGM), - GEN_IMPORT(MSG_SetClient, G_MSG_SETCLIENT), - GEN_IMPORT(SetBroadcastVisible, G_SETBROADCASTVISIBLE), - GEN_IMPORT(SetBroadcastHearable, G_SETBROADCASTHEARABLE), - GEN_IMPORT(SetBroadcastAll, G_SETBROADCASTALL), - GEN_IMPORT(setConfigstring, G_SET_CONFIGSTRING), - GEN_IMPORT(getConfigstring, G_GET_CONFIGSTRING), - GEN_IMPORT(SetUserinfo, G_SET_USERINFO), - GEN_IMPORT(GetUserinfo, G_GET_USERINFO), - GEN_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL), - GEN_IMPORT(ModelBoundsFromName, G_MODELBOUNDSFROMNAME), - GEN_IMPORT(SightTraceEntity, G_SIGHTTRACEENTITY), - GEN_IMPORT(SightTrace, G_SIGHTTRACE), - GEN_IMPORT(trace, G_TRACE), - GEN_IMPORT(CM_VisualObfuscation, G_CM_VISUALOBFUSCATION), - GEN_IMPORT(GetShader, G_GETSHADER), - GEN_IMPORT(pointcontents, G_POINT_CONTENTS), - GEN_IMPORT(PointBrushnum, G_POINTBRUSHNUM), - GEN_IMPORT(AdjustAreaPortalState, G_ADJUSTAREAPORTALSTATE), - GEN_IMPORT(AreaForPoint, G_AREAFORPOINT), - GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), - GEN_IMPORT(InPVS, G_IN_PVS), - GEN_IMPORT(linkentity, G_LINKENTITY), - GEN_IMPORT(unlinkentity, G_UNLINKENTITY), - GEN_IMPORT(AreaEntities, G_AREAENTITIES), - GEN_IMPORT(ClipToEntity, G_CLIPTOENTITY), - GEN_IMPORT(HitEntity, G_HITENTITY), - GEN_IMPORT(imageindex, G_IMAGEINDEX), - GEN_IMPORT(itemindex, G_ITEMINDEX), - GEN_IMPORT(soundindex, G_SOUNDINDEX), - GEN_IMPORT(TIKI_RegisterModel, G_TIKI_REGISTERMODEL), - GEN_IMPORT(modeltiki, G_MODELTIKI), - GEN_IMPORT(modeltikianim, G_MODELTIKIANIM), - GEN_IMPORT(SetLightStyle, G_SETLIGHTSTYLE), - GEN_IMPORT(GameDir, G_GAMEDIR), - GEN_IMPORT(setmodel, G_SETMODEL), - GEN_IMPORT(clearmodel, G_CLEARMODEL), - GEN_IMPORT(TIKI_NumAnims, G_TIKI_NUMANIMS), - GEN_IMPORT(TIKI_NumSurfaces, G_TIKI_NUMSURFACES), - GEN_IMPORT(TIKI_NumTags, G_TIKI_NUMTAGS), - GEN_IMPORT_4(TIKI_CalculateBounds, G_TIKI_CALCULATEBOUNDS, void, dtiki_t*, float, vec3_t, vec3_t), - GEN_IMPORT(TIKI_GetSkeletor, G_TIKI_GETSKELETOR), - GEN_IMPORT(Anim_NameForNum, G_ANIM_NAMEFORNUM), - GEN_IMPORT(Anim_NumForName, G_ANIM_NUMFORNAME), - GEN_IMPORT(Anim_Random, G_ANIM_RANDOM), - GEN_IMPORT(Anim_NumFrames, G_ANIM_NUMFRAMES), - GEN_IMPORT(Anim_Time, G_ANIM_TIME), - GEN_IMPORT(Anim_Frametime, G_ANIM_FRAMETIME), - GEN_IMPORT(Anim_CrossTime, G_ANIM_CROSSTIME), - GEN_IMPORT(Anim_Delta, G_ANIM_DELTA), - GEN_IMPORT(Anim_AngularDelta, G_ANIM_ANGULARDELTA), - GEN_IMPORT(Anim_HasDelta, G_ANIM_HASDELTA), - GEN_IMPORT_5(Anim_DeltaOverTime, G_ANIM_DELTAOVERTIME, void, dtiki_t*, int, float, float, float*), - GEN_IMPORT_5(Anim_AngularDeltaOverTime, G_ANIM_ANGULARDELTAOVERTIME, void, dtiki_t*, int, float, float, float*), - GEN_IMPORT(Anim_Flags, G_ANIM_FLAGS), - GEN_IMPORT(Anim_FlagsSkel, G_ANIM_FLAGSSKEL), - GEN_IMPORT(Anim_HasCommands, G_ANIM_HASCOMMANDS), - GEN_IMPORT(NumHeadModels, G_NUMHEADMODELS), - GEN_IMPORT(GetHeadModel, G_GETHEADMODEL), - GEN_IMPORT(NumHeadSkins, G_NUMHEADSKINS), - GEN_IMPORT(GetHeadSkin, G_GETHEADSKIN), - GEN_IMPORT(Frame_Commands, G_FRAME_COMMANDS), - GEN_IMPORT(Surface_NameToNum, G_SURFACE_NAMETONUM), - GEN_IMPORT(Surface_NumToName, G_SURFACE_NUMTONAME), - GEN_IMPORT(Tag_NumForName, G_TAG_NUMFORNAME), - GEN_IMPORT(Tag_NameForNum, G_TAG_NAMEFORNUM), - GEN_IMPORT(TIKI_OrientationInternal, G_TIKI_ORIENTATIONINTERNAL), // todo: change types to actually match float, but also need to return an intptr_t instead of orientation_t - GEN_IMPORT(TIKI_TransformInternal, G_TIKI_TRANSFORMINTERNAL), - GEN_IMPORT_4(TIKI_IsOnGroundInternal, G_TIKI_ISONGROUNDINTERNAL, qboolean, dtiki_t*, int, int, float), - GEN_IMPORT_6(TIKI_SetPoseInternal, G_TIKI_SETPOSEINTERNAL, void, dtiki_t*, int, const frameInfo_t*, int*, vec4_t*, float), - GEN_IMPORT(CM_GetHitLocationInfo, G_CM_GETHITLOCATIONINFO), - GEN_IMPORT(CM_GetHitLocationInfoSecondary, G_CM_GETHITLOCATIONINFOSECONDARY), - GEN_IMPORT(Alias_Add, G_ALIAS_ADD), - GEN_IMPORT(Alias_FindRandom, G_ALIAS_FINDRANDOM), - GEN_IMPORT(Alias_Dump, G_ALIAS_DUMP), - GEN_IMPORT(Alias_Clear, G_ALIAS_CLEAR), - GEN_IMPORT(Alias_UpdateDialog, G_ALIAS_UPDATEDIALOG), - GEN_IMPORT(TIKI_NameForNum, G_TIKI_NAMEFORNUM), - GEN_IMPORT(GlobalAlias_Add, G_GLOBALALIAS_ADD), - GEN_IMPORT(GlobalAlias_FindRandom, G_GLOBALALIAS_FINDRANDOM), - GEN_IMPORT(GlobalAlias_Dump, G_GLOBALALIAS_DUMP), - GEN_IMPORT(GlobalAlias_Clear, G_GLOBALALIAS_CLEAR), - GEN_IMPORT(centerprintf, G_CENTERPRINTF), - GEN_IMPORT(locationprintf, G_LOCATIONPRINTF), - GEN_IMPORT_9(Sound, G_SOUND, void, vec3_t*, int, int, const char*, float, float, float, float, int), - GEN_IMPORT(StopSound, G_STOPSOUND), - GEN_IMPORT(SoundLength, G_SOUNDLENGTH), - GEN_IMPORT(SoundAmplitudes, G_SOUNDAMPLITUDES), - GEN_IMPORT(S_IsSoundPlaying, G_S_ISSOUNDPLAYING), - GEN_IMPORT(CalcCRC, G_CALCCRC), - nullptr, // DebugLines - nullptr, // numDebugLines - nullptr, // DebugStrings - nullptr, // numDebugStrings - GEN_IMPORT(LocateGameData, G_LOCATE_GAME_DATA), - GEN_IMPORT(SetFarPlane, G_SETFARPLANE), - GEN_IMPORT(SetSkyPortal, G_SETSKYPORTAL), - GEN_IMPORT(Popmenu, G_POPMENU), - GEN_IMPORT(Showmenu, G_SHOWMENU), - GEN_IMPORT(Hidemenu, G_HIDEMENU), - GEN_IMPORT(Pushmenu, G_PUSHMENU), - GEN_IMPORT(HideMouseCursor, G_HIDEMOUSECURSOR), - GEN_IMPORT(ShowMouseCursor, G_SHOWMOUSECURSOR), - GEN_IMPORT(MapTime, G_MAPTIME), - GEN_IMPORT(LoadResource, G_LOADRESOURCE), - GEN_IMPORT(ClearResource, G_CLEARRESOURCE), - GEN_IMPORT(Key_StringToKeynum, G_KEY_STRINGTOKEYNUM), - GEN_IMPORT(Key_KeynumToBindString, G_KEY_KEYNUMTOBINDSTRING), - GEN_IMPORT(Key_GetKeysForCommand, G_KEY_GETKEYSFORCOMMAND), - GEN_IMPORT(ArchiveLevel, G_ARCHIVELEVEL), - GEN_IMPORT(AddSvsTimeFixup, G_ADDSVSTIMEFIXUP), - GEN_IMPORT(HudDrawShader, G_HUDDRAWSHADER), - GEN_IMPORT(HudDrawAlign, G_HUDDRAWALIGN), - GEN_IMPORT(HudDrawRect, G_HUDDRAWRECT), - GEN_IMPORT(HudDrawVirtualSize, G_HUDDRAWVIRTUALSIZE), - GEN_IMPORT(HudDrawColor, G_HUDDRAWCOLOR), - GEN_IMPORT_2(HudDrawAlpha, G_HUDDRAWALPHA, void, int, float), - GEN_IMPORT(HudDrawString, G_HUDDRAWSTRING), - GEN_IMPORT(HudDrawFont, G_HUDDRAWFONT), - GEN_IMPORT(SanitizeName, G_SANITIZENAME), - GEN_IMPORT(pvssoundindex, G_PVSSOUNDINDEX), - nullptr, //fsDebug + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = GEN_GAME_QMM_MOD_MSGS(); }; +GEN_GAME_OBJ(MOHBT); -// these are "pre" hooks for storing some data for polyfills. -// we need these to be called BEFORE plugins' prehooks get called so they have to be done in the qmm_export table -// track entstrings for our G_GET_ENTITY_TOKEN syscall -static std::vector s_entity_tokens; -static size_t s_tokencount = 0; -static void MOHBT_SpawnEntities(char* entstring, int levelTime) { - if (entstring) { - s_entity_tokens = util_parse_entstring(entstring); - s_tokencount = 0; - } - cgame.is_from_QMM = true; - vmMain(GAME_SPAWN_ENTITIES, entstring, levelTime); -} +// auto-detection logic for MOHBT +bool MOHBT_GameSupport::AutoDetect(APIType engineapi) { + if (engineapi != QMM_API_GETGAMEAPI) + return false; + if (!str_striequal(g_gameinfo.qmm_file, DefaultDLLName())) + return false; -// at least one of first four args is a float (see big comment at top of game_q2r.cpp), so use specific types -static void MOHBT_DebugCircle(float* arg0, float arg1, float arg2, float arg3, float arg4, float arg5, qboolean arg6) { - cgame.is_from_QMM = true; - vmMain(GAME_DEBUG_CIRCLE, arg0, arg1, arg2, arg3, arg4, arg5, arg6); -} + if (!str_stristr(g_gameinfo.exe_file, "break")) + return false; + return true; +} -// struct with lambdas that call QMM's vmMain function. this is given to the game engine -static game_export_t qmm_export = { - GAME_API_VERSION, // apiversion - GEN_EXPORT(Init, GAME_INIT), - GEN_EXPORT(Shutdown, GAME_SHUTDOWN), - GEN_EXPORT(Cleanup, GAME_CLEANUP), - GEN_EXPORT(Precache, GAME_PRECACHE), - GEN_EXPORT(SetMap, GAME_SETMAP), - GEN_EXPORT(Restart, GAME_RESTART), - GEN_EXPORT(SetTime, GAME_SETTIME), - MOHBT_SpawnEntities, - GEN_EXPORT(ClientConnect, GAME_CLIENT_CONNECT), - GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), - GEN_EXPORT(ClientUserinfoChanged, GAME_CLIENT_USERINFO_CHANGED), - GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), - GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), - GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), - GEN_EXPORT(BotBegin, GAME_BOTBEGIN), - GEN_EXPORT(BotThink, GAME_BOTTHINK), - GEN_EXPORT(PrepFrame, GAME_PREP_FRAME), - GEN_EXPORT(RunFrame, GAME_RUN_FRAME), - GEN_EXPORT(ServerSpawned, GAME_SERVER_SPAWNED), - GEN_EXPORT(RegisterSounds, GAME_REGISTER_SOUNDS), - GEN_EXPORT(AllowPaused, GAME_ALLOW_PAUSED), - GEN_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND), - GEN_EXPORT(ArchivePersistant, GAME_ARCHIVE_PERSISTANT), - GEN_EXPORT(WriteLevel, GAME_WRITE_LEVEL), - GEN_EXPORT(ReadLevel, GAME_READ_LEVEL), - GEN_EXPORT(LevelArchiveValid, GAME_LEVEL_ARCHIVE_VALID), - GEN_EXPORT(ArchiveInteger, GAME_ARCHIVE_INTEGER), - GEN_EXPORT(ArchiveFloat, GAME_ARCHIVE_FLOAT), - GEN_EXPORT(ArchiveString, GAME_ARCHIVE_STRING), - GEN_EXPORT(ArchiveSvsTime, GAME_ARCHIVE_SVSTIME), - GEN_EXPORT(TIKI_Orientation, GAME_TIKI_ORIENTATION), // todo: change types to actually match float, but also need to return an intptr_t instead of orientation_t - MOHBT_DebugCircle, - GEN_EXPORT(SetFrameNumber, GAME_SET_FRAME_NUMBER), - GEN_EXPORT(SoundCallback, GAME_SOUND_CALLBACK), - // the engine won't use these until after Init, so we can fill these in after each call into the mod's export functions ("vmMain") - nullptr, // profStruct - nullptr, // gentities - 0, // gentitySize - 0, // num_entities - 0, // max_entities - nullptr, // errorMessage -}; +// wrapper syscall function that calls actual engine func from orig_import +// this is how QMM and plugins will call into the engine +intptr_t MOHBT_GameSupport::syscall(intptr_t cmd, ...) { + QMM_GET_SYSCALL_ARGS(); +#ifdef _DEBUG + if (cmd != G_PRINT) + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHBT_GameSupport::syscall({} {}) called\n", EngMsgName(cmd), cmd); +#endif -// update the export variables from orig_export -static void s_update_export() { - if (!orig_export) - return; + // update export vars before calling into the engine + update_exports(); - bool changed = false; - - // if entity data changed, we need to send a G_LOCATE_GAME_DATA so plugins can hook it - if (qmm_export.gentities != orig_export->gentities - || qmm_export.gentitySize != orig_export->gentitySize - || qmm_export.num_entities != orig_export->num_entities - ) { - changed = true; - } - - qmm_export.profStruct = orig_export->profStruct; - qmm_export.gentities = orig_export->gentities; - qmm_export.gentitySize = orig_export->gentitySize; - qmm_export.num_entities = orig_export->num_entities; - qmm_export.max_entities = orig_export->max_entities; - qmm_export.errorMessage = orig_export->errorMessage; - - if (changed) { - // this will trigger this message to be fired to plugins, and then it will be handled - // by the empty "case G_LOCATE_GAME_DATA" in MOHAA_syscall - qmm_syscall(G_LOCATE_GAME_DATA, (intptr_t)qmm_export.gentities, qmm_export.num_entities, qmm_export.gentitySize, nullptr, 0); - } -} - - -// wrapper syscall function that calls actual engine func from orig_import -// this is how QMM and plugins will call into the engine -static intptr_t MOHBT_syscall(intptr_t cmd, ...) { - QMM_GET_SYSCALL_ARGS(); - -#ifdef _DEBUG - if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHBT_syscall({} {}) called\n", MOHBT_EngMsgName(cmd), cmd); -#endif - - // update export vars before calling into the engine - s_update_export(); - - intptr_t ret = 0; + intptr_t ret = 0; switch (cmd) { ROUTE_IMPORT(Printf, G_PRINTF); @@ -678,7 +434,7 @@ static intptr_t MOHBT_syscall(intptr_t cmd, ...) { } case G_GET_ENTITY_TOKEN: { // qboolean trap_GetEntityToken(char *buffer, int bufferSize); - if (s_tokencount >= s_entity_tokens.size()) { + if (token_counter >= entity_tokens.size()) { ret = qfalse; break; } @@ -686,7 +442,7 @@ static intptr_t MOHBT_syscall(intptr_t cmd, ...) { char* buffer = (char*)args[0]; intptr_t bufferSize = args[1]; - strncpyz(buffer, s_entity_tokens[s_tokencount++].c_str(), (size_t)bufferSize); + strncpyz(buffer, entity_tokens[token_counter++].c_str(), (size_t)bufferSize); ret = qtrue; break; } @@ -699,7 +455,7 @@ static intptr_t MOHBT_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHBT_syscall({} {}) returning {}\n", MOHBT_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHBT_GameSupport::syscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; @@ -708,11 +464,11 @@ static intptr_t MOHBT_syscall(intptr_t cmd, ...) { // wrapper vmMain function that calls actual mod func from orig_export // this is how QMM and plugins will call into the mod -static intptr_t MOHBT_vmMain(intptr_t cmd, ...) { +intptr_t MOHBT_GameSupport::vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHBT_vmMain({} {}) called\n", MOHBT_ModMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHBT_GameSupport::vmMain({} {}) called\n", ModMsgName(cmd), cmd); #endif if (!orig_export) @@ -771,18 +527,18 @@ static intptr_t MOHBT_vmMain(intptr_t cmd, ...) { }; // update export vars after returning from the mod - s_update_export(); + update_exports(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHBT_vmMain({} {}) returning {}\n", MOHBT_ModMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHBT_GameSupport::vmMain({} {}) returning {}\n", ModMsgName(cmd), cmd, ret); #endif return ret; } -static void* MOHBT_Entry(void* import, void*, APIType) { - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHBT_Entry({}) called\n", import); +void* MOHBT_GameSupport::Entry(void* import, void*, APIType) { + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHBT_GameSupport::Entry({}) called\n", import); // original import struct from engine // the struct given by the engine goes out of scope after this returns so we have to copy the whole thing @@ -796,13 +552,7 @@ static void* MOHBT_Entry(void* import, void*, APIType) { qmm_import.numDebugStrings = orig_import.numDebugStrings; qmm_import.fsDebug = orig_import.fsDebug; - // pointer to wrapper vmMain function that calls actual mod func from orig_export - g_gameinfo.pfnvmMain = MOHBT_vmMain; - - // pointer to wrapper syscall function that calls actual engine func from orig_import - g_gameinfo.pfnsyscall = MOHBT_syscall; - - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHBT_Entry({}) returning {}\n", import, fmt::ptr(&qmm_export)); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHBT_GameSupport::Entry({}) returning {}\n", import, fmt::ptr(&qmm_export)); // struct full of export lambdas to QMM's vmMain // this gets returned to the game engine, but we haven't loaded the mod yet. @@ -811,7 +561,7 @@ static void* MOHBT_Entry(void* import, void*, APIType) { } -static bool MOHBT_ModLoad(void* entry, APIType modapi) { +bool MOHBT_GameSupport::ModLoad(void* entry, APIType modapi) { if (modapi != QMM_API_GETGAMEAPI) return false; @@ -822,12 +572,12 @@ static bool MOHBT_ModLoad(void* entry, APIType modapi) { } -static void MOHBT_ModUnload() { +void MOHBT_GameSupport::ModUnload() { orig_export = nullptr; } -static const char* MOHBT_EngMsgName(intptr_t cmd) { +const char* MOHBT_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINTF); GEN_CASE(G_DPRINTF); @@ -1016,7 +766,7 @@ static const char* MOHBT_EngMsgName(intptr_t cmd) { } -static const char* MOHBT_ModMsgName(intptr_t cmd) { +const char* MOHBT_GameSupport::ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAMEV_APIVERSION); GEN_CASE(GAME_INIT); @@ -1063,3 +813,287 @@ static const char* MOHBT_ModMsgName(intptr_t cmd) { return "unknown"; } } + + +void MOHBT_GameSupport::update_exports() { + if (!orig_export) + return; + + bool changed = false; + + // if entity data changed, we need to send a G_LOCATE_GAME_DATA so plugins can hook it + if (qmm_export.gentities != orig_export->gentities + || qmm_export.gentitySize != orig_export->gentitySize + || qmm_export.num_entities != orig_export->num_entities + ) { + changed = true; + } + + qmm_export.profStruct = orig_export->profStruct; + qmm_export.gentities = orig_export->gentities; + qmm_export.gentitySize = orig_export->gentitySize; + qmm_export.num_entities = orig_export->num_entities; + qmm_export.max_entities = orig_export->max_entities; + qmm_export.errorMessage = orig_export->errorMessage; + + if (changed) { + // this will trigger this message to be fired to plugins, and then it will be handled + // by the empty "case G_LOCATE_GAME_DATA" in MOHAA_syscall + qmm_syscall(G_LOCATE_GAME_DATA, (intptr_t)qmm_export.gentities, qmm_export.num_entities, qmm_export.gentitySize, nullptr, 0); + } +} + + +game_import_t MOHBT_GameSupport::orig_import; + + +game_export_t* MOHBT_GameSupport::orig_export = nullptr; + + +game_import_t MOHBT_GameSupport::qmm_import = { + GEN_IMPORT(Printf, G_PRINTF), + GEN_IMPORT(DPrintf, G_DPRINTF), + GEN_IMPORT(DPrintf2, G_DPRINTF2), + GEN_IMPORT(DebugPrintf, G_DEBUGPRINTF), + GEN_IMPORT(Error, G_ERROR), + GEN_IMPORT(Milliseconds, G_MILLISECONDS), + GEN_IMPORT(LV_ConvertString, G_LV_CONVERTSTRING), + GEN_IMPORT(CL_LV_ConvertString, G_CL_LV_CONVERTSTRING), + GEN_IMPORT(Malloc, G_MALLOC), + GEN_IMPORT(Free, G_FREE), + GEN_IMPORT(Cvar_Get, G_CVAR_GET), + GEN_IMPORT(cvar_set, G_CVAR_SET), + GEN_IMPORT(cvar_set2, G_CVAR_SET2), + GEN_IMPORT(NextCvar, G_NEXTCVAR), + GEN_IMPORT(Argc, G_ARGC), + GEN_IMPORT(Argv, G_ARGV), + GEN_IMPORT(Args, G_ARGS), + GEN_IMPORT(AddCommand, G_ADDCOMMAND), + GEN_IMPORT(FS_ReadFile, G_FS_READFILE), + GEN_IMPORT(FS_FreeFile, G_FS_FREEFILE), + GEN_IMPORT(FS_WriteFile, G_FS_WRITEFILE), + GEN_IMPORT(FS_FOpenFileWrite, G_FS_FOPEN_FILE_WRITE), + GEN_IMPORT(FS_FOpenFileAppend, G_FS_FOPEN_FILE_APPEND), + GEN_IMPORT(FS_FOpenFile, G_FS_UNKNOWN), + GEN_IMPORT(FS_PrepFileWrite, G_FS_PREPFILEWRITE), + GEN_IMPORT(FS_Write, G_FS_WRITE), + GEN_IMPORT(FS_Read, G_FS_READ), + GEN_IMPORT(FS_FCloseFile, G_FS_FCLOSE_FILE), + GEN_IMPORT(FS_Tell, G_FS_TELL), + GEN_IMPORT(FS_Seek, G_FS_SEEK), + GEN_IMPORT(FS_Flush, G_FS_FLUSH), + GEN_IMPORT(FS_FileNewer, G_FS_FILENEWER), + GEN_IMPORT(FS_CanonicalFilename, G_FS_CANONICALFILENAME), + GEN_IMPORT(FS_ListFiles, G_FS_LISTFILES), + GEN_IMPORT(FS_FreeFileList, G_FS_FREEFILELIST), + GEN_IMPORT(GetArchiveFileName, G_GETARCHIVEFILENAME), + GEN_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND), + GEN_IMPORT(ExecuteConsoleCommand, G_EXECUTE_CONSOLE_COMMAND), + GEN_IMPORT_1(DebugGraph, G_DEBUGGRAPH, void, float), + GEN_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND), + GEN_IMPORT(DropClient, G_DROP_CLIENT), + GEN_IMPORT(MSG_WriteBits, G_MSG_WRITEBITS), + GEN_IMPORT(MSG_WriteChar, G_MSG_WRITECHAR), + GEN_IMPORT(MSG_WriteByte, G_MSG_WRITEBYTE), + GEN_IMPORT(MSG_WriteSVC, G_MSG_WRITESVC), + GEN_IMPORT(MSG_WriteShort, G_MSG_WRITESHORT), + GEN_IMPORT(MSG_WriteLong, G_MSG_WRITELONG), + GEN_IMPORT_1(MSG_WriteFloat, G_MSG_WRITEFLOAT, void, float), + GEN_IMPORT(MSG_WriteString, G_MSG_WRITESTRING), + GEN_IMPORT_1(MSG_WriteAngle8, G_MSG_WRITEANGLE8, void, float), + GEN_IMPORT_1(MSG_WriteAngle16, G_MSG_WRITEANGLE16, void, float), + GEN_IMPORT_1(MSG_WriteCoord, G_MSG_WRITECOORD, void, float), + GEN_IMPORT(MSG_WriteDir, G_MSG_WRITEDIR), + GEN_IMPORT(MSG_StartCGM, G_MSG_STARTCGM), + GEN_IMPORT(MSG_EndCGM, G_MSG_ENDCGM), + GEN_IMPORT(MSG_SetClient, G_MSG_SETCLIENT), + GEN_IMPORT(SetBroadcastVisible, G_SETBROADCASTVISIBLE), + GEN_IMPORT(SetBroadcastHearable, G_SETBROADCASTHEARABLE), + GEN_IMPORT(SetBroadcastAll, G_SETBROADCASTALL), + GEN_IMPORT(setConfigstring, G_SET_CONFIGSTRING), + GEN_IMPORT(getConfigstring, G_GET_CONFIGSTRING), + GEN_IMPORT(SetUserinfo, G_SET_USERINFO), + GEN_IMPORT(GetUserinfo, G_GET_USERINFO), + GEN_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL), + GEN_IMPORT(ModelBoundsFromName, G_MODELBOUNDSFROMNAME), + GEN_IMPORT(SightTraceEntity, G_SIGHTTRACEENTITY), + GEN_IMPORT(SightTrace, G_SIGHTTRACE), + GEN_IMPORT(trace, G_TRACE), + GEN_IMPORT(CM_VisualObfuscation, G_CM_VISUALOBFUSCATION), + GEN_IMPORT(GetShader, G_GETSHADER), + GEN_IMPORT(pointcontents, G_POINT_CONTENTS), + GEN_IMPORT(PointBrushnum, G_POINTBRUSHNUM), + GEN_IMPORT(AdjustAreaPortalState, G_ADJUSTAREAPORTALSTATE), + GEN_IMPORT(AreaForPoint, G_AREAFORPOINT), + GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), + GEN_IMPORT(InPVS, G_IN_PVS), + GEN_IMPORT(linkentity, G_LINKENTITY), + GEN_IMPORT(unlinkentity, G_UNLINKENTITY), + GEN_IMPORT(AreaEntities, G_AREAENTITIES), + GEN_IMPORT(ClipToEntity, G_CLIPTOENTITY), + GEN_IMPORT(HitEntity, G_HITENTITY), + GEN_IMPORT(imageindex, G_IMAGEINDEX), + GEN_IMPORT(itemindex, G_ITEMINDEX), + GEN_IMPORT(soundindex, G_SOUNDINDEX), + GEN_IMPORT(TIKI_RegisterModel, G_TIKI_REGISTERMODEL), + GEN_IMPORT(modeltiki, G_MODELTIKI), + GEN_IMPORT(modeltikianim, G_MODELTIKIANIM), + GEN_IMPORT(SetLightStyle, G_SETLIGHTSTYLE), + GEN_IMPORT(GameDir, G_GAMEDIR), + GEN_IMPORT(setmodel, G_SETMODEL), + GEN_IMPORT(clearmodel, G_CLEARMODEL), + GEN_IMPORT(TIKI_NumAnims, G_TIKI_NUMANIMS), + GEN_IMPORT(TIKI_NumSurfaces, G_TIKI_NUMSURFACES), + GEN_IMPORT(TIKI_NumTags, G_TIKI_NUMTAGS), + GEN_IMPORT_4(TIKI_CalculateBounds, G_TIKI_CALCULATEBOUNDS, void, dtiki_t*, float, vec3_t, vec3_t), + GEN_IMPORT(TIKI_GetSkeletor, G_TIKI_GETSKELETOR), + GEN_IMPORT(Anim_NameForNum, G_ANIM_NAMEFORNUM), + GEN_IMPORT(Anim_NumForName, G_ANIM_NUMFORNAME), + GEN_IMPORT(Anim_Random, G_ANIM_RANDOM), + GEN_IMPORT(Anim_NumFrames, G_ANIM_NUMFRAMES), + GEN_IMPORT(Anim_Time, G_ANIM_TIME), + GEN_IMPORT(Anim_Frametime, G_ANIM_FRAMETIME), + GEN_IMPORT(Anim_CrossTime, G_ANIM_CROSSTIME), + GEN_IMPORT(Anim_Delta, G_ANIM_DELTA), + GEN_IMPORT(Anim_AngularDelta, G_ANIM_ANGULARDELTA), + GEN_IMPORT(Anim_HasDelta, G_ANIM_HASDELTA), + GEN_IMPORT_5(Anim_DeltaOverTime, G_ANIM_DELTAOVERTIME, void, dtiki_t*, int, float, float, float*), + GEN_IMPORT_5(Anim_AngularDeltaOverTime, G_ANIM_ANGULARDELTAOVERTIME, void, dtiki_t*, int, float, float, float*), + GEN_IMPORT(Anim_Flags, G_ANIM_FLAGS), + GEN_IMPORT(Anim_FlagsSkel, G_ANIM_FLAGSSKEL), + GEN_IMPORT(Anim_HasCommands, G_ANIM_HASCOMMANDS), + GEN_IMPORT(NumHeadModels, G_NUMHEADMODELS), + GEN_IMPORT(GetHeadModel, G_GETHEADMODEL), + GEN_IMPORT(NumHeadSkins, G_NUMHEADSKINS), + GEN_IMPORT(GetHeadSkin, G_GETHEADSKIN), + GEN_IMPORT(Frame_Commands, G_FRAME_COMMANDS), + GEN_IMPORT(Surface_NameToNum, G_SURFACE_NAMETONUM), + GEN_IMPORT(Surface_NumToName, G_SURFACE_NUMTONAME), + GEN_IMPORT(Tag_NumForName, G_TAG_NUMFORNAME), + GEN_IMPORT(Tag_NameForNum, G_TAG_NAMEFORNUM), + GEN_IMPORT(TIKI_OrientationInternal, G_TIKI_ORIENTATIONINTERNAL), // todo: change types to actually match float, but also need to return an intptr_t instead of orientation_t + GEN_IMPORT(TIKI_TransformInternal, G_TIKI_TRANSFORMINTERNAL), + GEN_IMPORT_4(TIKI_IsOnGroundInternal, G_TIKI_ISONGROUNDINTERNAL, qboolean, dtiki_t*, int, int, float), + GEN_IMPORT_6(TIKI_SetPoseInternal, G_TIKI_SETPOSEINTERNAL, void, dtiki_t*, int, const frameInfo_t*, int*, vec4_t*, float), + GEN_IMPORT(CM_GetHitLocationInfo, G_CM_GETHITLOCATIONINFO), + GEN_IMPORT(CM_GetHitLocationInfoSecondary, G_CM_GETHITLOCATIONINFOSECONDARY), + GEN_IMPORT(Alias_Add, G_ALIAS_ADD), + GEN_IMPORT(Alias_FindRandom, G_ALIAS_FINDRANDOM), + GEN_IMPORT(Alias_Dump, G_ALIAS_DUMP), + GEN_IMPORT(Alias_Clear, G_ALIAS_CLEAR), + GEN_IMPORT(Alias_UpdateDialog, G_ALIAS_UPDATEDIALOG), + GEN_IMPORT(TIKI_NameForNum, G_TIKI_NAMEFORNUM), + GEN_IMPORT(GlobalAlias_Add, G_GLOBALALIAS_ADD), + GEN_IMPORT(GlobalAlias_FindRandom, G_GLOBALALIAS_FINDRANDOM), + GEN_IMPORT(GlobalAlias_Dump, G_GLOBALALIAS_DUMP), + GEN_IMPORT(GlobalAlias_Clear, G_GLOBALALIAS_CLEAR), + GEN_IMPORT(centerprintf, G_CENTERPRINTF), + GEN_IMPORT(locationprintf, G_LOCATIONPRINTF), + GEN_IMPORT_9(Sound, G_SOUND, void, vec3_t*, int, int, const char*, float, float, float, float, int), + GEN_IMPORT(StopSound, G_STOPSOUND), + GEN_IMPORT(SoundLength, G_SOUNDLENGTH), + GEN_IMPORT(SoundAmplitudes, G_SOUNDAMPLITUDES), + GEN_IMPORT(S_IsSoundPlaying, G_S_ISSOUNDPLAYING), + GEN_IMPORT(CalcCRC, G_CALCCRC), + nullptr, // DebugLines + nullptr, // numDebugLines + nullptr, // DebugStrings + nullptr, // numDebugStrings + GEN_IMPORT(LocateGameData, G_LOCATE_GAME_DATA), + GEN_IMPORT(SetFarPlane, G_SETFARPLANE), + GEN_IMPORT(SetSkyPortal, G_SETSKYPORTAL), + GEN_IMPORT(Popmenu, G_POPMENU), + GEN_IMPORT(Showmenu, G_SHOWMENU), + GEN_IMPORT(Hidemenu, G_HIDEMENU), + GEN_IMPORT(Pushmenu, G_PUSHMENU), + GEN_IMPORT(HideMouseCursor, G_HIDEMOUSECURSOR), + GEN_IMPORT(ShowMouseCursor, G_SHOWMOUSECURSOR), + GEN_IMPORT(MapTime, G_MAPTIME), + GEN_IMPORT(LoadResource, G_LOADRESOURCE), + GEN_IMPORT(ClearResource, G_CLEARRESOURCE), + GEN_IMPORT(Key_StringToKeynum, G_KEY_STRINGTOKEYNUM), + GEN_IMPORT(Key_KeynumToBindString, G_KEY_KEYNUMTOBINDSTRING), + GEN_IMPORT(Key_GetKeysForCommand, G_KEY_GETKEYSFORCOMMAND), + GEN_IMPORT(ArchiveLevel, G_ARCHIVELEVEL), + GEN_IMPORT(AddSvsTimeFixup, G_ADDSVSTIMEFIXUP), + GEN_IMPORT(HudDrawShader, G_HUDDRAWSHADER), + GEN_IMPORT(HudDrawAlign, G_HUDDRAWALIGN), + GEN_IMPORT(HudDrawRect, G_HUDDRAWRECT), + GEN_IMPORT(HudDrawVirtualSize, G_HUDDRAWVIRTUALSIZE), + GEN_IMPORT(HudDrawColor, G_HUDDRAWCOLOR), + GEN_IMPORT_2(HudDrawAlpha, G_HUDDRAWALPHA, void, int, float), + GEN_IMPORT(HudDrawString, G_HUDDRAWSTRING), + GEN_IMPORT(HudDrawFont, G_HUDDRAWFONT), + GEN_IMPORT(SanitizeName, G_SANITIZENAME), + GEN_IMPORT(pvssoundindex, G_PVSSOUNDINDEX), + nullptr, //fsDebug +}; + + +std::vector MOHBT_GameSupport::entity_tokens; +size_t MOHBT_GameSupport::token_counter = 0; +void MOHBT_GameSupport::SpawnEntities(char* entstring, int levelTime) { + if (entstring) { + entity_tokens = util_parse_entstring(entstring); + token_counter = 0; + } + cgame.is_from_QMM = true; + ::vmMain(GAME_SPAWN_ENTITIES, entstring, levelTime); +} + + +// at least one of first four args is a float (see big comment at top of game_q2r.cpp), so use specific types +void MOHBT_GameSupport::DebugCircle(float* arg0, float arg1, float arg2, float arg3, float arg4, float arg5, qboolean arg6) { + cgame.is_from_QMM = true; + ::vmMain(GAME_DEBUG_CIRCLE, arg0, arg1, arg2, arg3, arg4, arg5, arg6); +} + + +// struct with lambdas that call QMM's vmMain function. this is given to the game engine +game_export_t MOHBT_GameSupport::qmm_export = { + GAME_API_VERSION, // apiversion + GEN_EXPORT(Init, GAME_INIT), + GEN_EXPORT(Shutdown, GAME_SHUTDOWN), + GEN_EXPORT(Cleanup, GAME_CLEANUP), + GEN_EXPORT(Precache, GAME_PRECACHE), + GEN_EXPORT(SetMap, GAME_SETMAP), + GEN_EXPORT(Restart, GAME_RESTART), + GEN_EXPORT(SetTime, GAME_SETTIME), + MOHBT_GameSupport::SpawnEntities, + GEN_EXPORT(ClientConnect, GAME_CLIENT_CONNECT), + GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), + GEN_EXPORT(ClientUserinfoChanged, GAME_CLIENT_USERINFO_CHANGED), + GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), + GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), + GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), + GEN_EXPORT(BotBegin, GAME_BOTBEGIN), + GEN_EXPORT(BotThink, GAME_BOTTHINK), + GEN_EXPORT(PrepFrame, GAME_PREP_FRAME), + GEN_EXPORT(RunFrame, GAME_RUN_FRAME), + GEN_EXPORT(ServerSpawned, GAME_SERVER_SPAWNED), + GEN_EXPORT(RegisterSounds, GAME_REGISTER_SOUNDS), + GEN_EXPORT(AllowPaused, GAME_ALLOW_PAUSED), + GEN_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND), + GEN_EXPORT(ArchivePersistant, GAME_ARCHIVE_PERSISTANT), + GEN_EXPORT(WriteLevel, GAME_WRITE_LEVEL), + GEN_EXPORT(ReadLevel, GAME_READ_LEVEL), + GEN_EXPORT(LevelArchiveValid, GAME_LEVEL_ARCHIVE_VALID), + GEN_EXPORT(ArchiveInteger, GAME_ARCHIVE_INTEGER), + GEN_EXPORT(ArchiveFloat, GAME_ARCHIVE_FLOAT), + GEN_EXPORT(ArchiveString, GAME_ARCHIVE_STRING), + GEN_EXPORT(ArchiveSvsTime, GAME_ARCHIVE_SVSTIME), + GEN_EXPORT(TIKI_Orientation, GAME_TIKI_ORIENTATION), // todo: change types to actually match float, but also need to return an intptr_t instead of orientation_t + MOHBT_GameSupport::DebugCircle, + GEN_EXPORT(SetFrameNumber, GAME_SET_FRAME_NUMBER), + GEN_EXPORT(SoundCallback, GAME_SOUND_CALLBACK), + + // the engine won't use these until after Init, so we can fill these in after each call into the mod's export functions ("vmMain") + nullptr, // profStruct + nullptr, // gentities + 0, // gentitySize + 0, // num_entities + 0, // max_entities + nullptr, // errorMessage +}; + +#endif // QMM_ARCH_32 diff --git a/src/game_mohsh.cpp b/src/game_mohsh.cpp index 86a10f8..c5b3c0c 100644 --- a/src/game_mohsh.cpp +++ b/src/game_mohsh.cpp @@ -9,6 +9,10 @@ Created By: */ +#include "version.h" + +#if defined(QMM_ARCH_32) + #define _CRT_SECURE_NO_WARNINGS 1 #include #include @@ -26,328 +30,80 @@ Created By: #include "main.h" #include "util.h" -GEN_GAME_QMM_MSGS(MOHSH); +struct MOHSH_GameSupport : public GameSupport { + virtual const char* EngMsgName(intptr_t msg); + virtual const char* ModMsgName(intptr_t msg); + virtual bool AutoDetect(APIType engine_api); + virtual void* Entry(void* syscall, void*, APIType engine_api); + virtual bool ModLoad(void* entry, APIType mod_api); + virtual void ModUnload(); -GEN_GAME_FUNCS(MOHSH); + virtual intptr_t syscall(intptr_t, ...); + virtual intptr_t vmMain(intptr_t, ...); + virtual const char* DefaultDLLName() { return "game" MOD_DLL; } + virtual const char* DefaultModDir() { return "mainta"; } + virtual const char* GameName() { return "Medal of Honor: Spearhead"; } + virtual const char* GameCode() { return "MOHSH"; } -// auto-detection logic for MOHSH -static bool MOHSH_AutoDetect(api_supportedgame* game, APIType engineapi) { - if (engineapi != QMM_API_GETGAMEAPI) - return false; +private: + static void update_exports(); - if (!str_striequal(g_gameinfo.qmm_file, game->dllname)) - return false; + // track entstrings for our G_GET_ENTITY_TOKEN syscall + static std::vector entity_tokens; + static size_t token_counter; + static void SpawnEntities(char* entstring, int levelTime); - if (!str_stristr(g_gameinfo.exe_file, "spear")) - return false; + static void DebugCircle(float* arg0, float arg1, float arg2, float arg3, float arg4, float arg5, qboolean arg6); - return true; -} + // a copy of the original import struct that comes from the game engine + static game_import_t orig_import; + // a copy of the original export struct pointer that comes from the mod + static game_export_t* orig_export; -// a copy of the original import struct that comes from the game engine -static game_import_t orig_import; + // struct with lambdas that call QMM's syscall function. this is given to the mod + static game_import_t qmm_import; -// a copy of the original export struct pointer that comes from the mod -static game_export_t* orig_export = nullptr; + // struct with lambdas that call QMM's vmMain function. this is given to the game engine + static game_export_t qmm_export; -// struct with lambdas that call QMM's syscall function. this is given to the mod -static game_import_t qmm_import = { - GEN_IMPORT(Printf, G_PRINTF), - GEN_IMPORT(DPrintf, G_DPRINTF), - GEN_IMPORT(DPrintf2, G_DPRINTF2), - GEN_IMPORT(DebugPrintf, G_DEBUGPRINTF), - GEN_IMPORT(Error, G_ERROR), - GEN_IMPORT(Milliseconds, G_MILLISECONDS), - GEN_IMPORT(LV_ConvertString, G_LV_CONVERTSTRING), - GEN_IMPORT(CL_LV_ConvertString, G_CL_LV_CONVERTSTRING), - GEN_IMPORT(Malloc, G_MALLOC), - GEN_IMPORT(Free, G_FREE), - GEN_IMPORT(Cvar_Get, G_CVAR_GET), - GEN_IMPORT(cvar_set, G_CVAR_SET), - GEN_IMPORT(cvar_set2, G_CVAR_SET2), - GEN_IMPORT(NextCvar, G_NEXTCVAR), - GEN_IMPORT(Argc, G_ARGC), - GEN_IMPORT(Argv, G_ARGV), - GEN_IMPORT(Args, G_ARGS), - GEN_IMPORT(AddCommand, G_ADDCOMMAND), - GEN_IMPORT(FS_ReadFile, G_FS_READFILE), - GEN_IMPORT(FS_FreeFile, G_FS_FREEFILE), - GEN_IMPORT(FS_WriteFile, G_FS_WRITEFILE), - GEN_IMPORT(FS_FOpenFileWrite, G_FS_FOPEN_FILE_WRITE), - GEN_IMPORT(FS_FOpenFileAppend, G_FS_FOPEN_FILE_APPEND), - GEN_IMPORT(FS_FOpenFile, G_FS_UNKNOWN), - GEN_IMPORT(FS_PrepFileWrite, G_FS_PREPFILEWRITE), - GEN_IMPORT(FS_Write, G_FS_WRITE), - GEN_IMPORT(FS_Read, G_FS_READ), - GEN_IMPORT(FS_FCloseFile, G_FS_FCLOSE_FILE), - GEN_IMPORT(FS_Tell, G_FS_TELL), - GEN_IMPORT(FS_Seek, G_FS_SEEK), - GEN_IMPORT(FS_Flush, G_FS_FLUSH), - GEN_IMPORT(FS_FileNewer, G_FS_FILENEWER), - GEN_IMPORT(FS_CanonicalFilename, G_FS_CANONICALFILENAME), - GEN_IMPORT(FS_ListFiles, G_FS_LISTFILES), - GEN_IMPORT(FS_FreeFileList, G_FS_FREEFILELIST), - GEN_IMPORT(GetArchiveFileName, G_GETARCHIVEFILENAME), - GEN_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND), - GEN_IMPORT(ExecuteConsoleCommand, G_EXECUTE_CONSOLE_COMMAND), - GEN_IMPORT_1(DebugGraph, G_DEBUGGRAPH, void, float), - GEN_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND), - GEN_IMPORT(DropClient, G_DROP_CLIENT), - GEN_IMPORT(MSG_WriteBits, G_MSG_WRITEBITS), - GEN_IMPORT(MSG_WriteChar, G_MSG_WRITECHAR), - GEN_IMPORT(MSG_WriteByte, G_MSG_WRITEBYTE), - GEN_IMPORT(MSG_WriteSVC, G_MSG_WRITESVC), - GEN_IMPORT(MSG_WriteShort, G_MSG_WRITESHORT), - GEN_IMPORT(MSG_WriteLong, G_MSG_WRITELONG), - GEN_IMPORT_1(MSG_WriteFloat, G_MSG_WRITEFLOAT, void, float), - GEN_IMPORT(MSG_WriteString, G_MSG_WRITESTRING), - GEN_IMPORT_1(MSG_WriteAngle8, G_MSG_WRITEANGLE8, void, float), - GEN_IMPORT_1(MSG_WriteAngle16, G_MSG_WRITEANGLE16, void, float), - GEN_IMPORT_1(MSG_WriteCoord, G_MSG_WRITECOORD, void, float), - GEN_IMPORT(MSG_WriteDir, G_MSG_WRITEDIR), - GEN_IMPORT(MSG_StartCGM, G_MSG_STARTCGM), - GEN_IMPORT(MSG_EndCGM, G_MSG_ENDCGM), - GEN_IMPORT(MSG_SetClient, G_MSG_SETCLIENT), - GEN_IMPORT(SetBroadcastVisible, G_SETBROADCASTVISIBLE), - GEN_IMPORT(SetBroadcastHearable, G_SETBROADCASTHEARABLE), - GEN_IMPORT(SetBroadcastAll, G_SETBROADCASTALL), - GEN_IMPORT(setConfigstring, G_SET_CONFIGSTRING), - GEN_IMPORT(getConfigstring, G_GET_CONFIGSTRING), - GEN_IMPORT(SetUserinfo, G_SET_USERINFO), - GEN_IMPORT(GetUserinfo, G_GET_USERINFO), - GEN_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL), - GEN_IMPORT(ModelBoundsFromName, G_MODELBOUNDSFROMNAME), - GEN_IMPORT(SightTraceEntity, G_SIGHTTRACEENTITY), - GEN_IMPORT(SightTrace, G_SIGHTTRACE), - GEN_IMPORT(trace, G_TRACE), - GEN_IMPORT(CM_VisualObfuscation, G_CM_VISUALOBFUSCATION), - GEN_IMPORT(GetShader, G_GETSHADER), - GEN_IMPORT(pointcontents, G_POINT_CONTENTS), - GEN_IMPORT(PointBrushnum, G_POINTBRUSHNUM), - GEN_IMPORT(AdjustAreaPortalState, G_ADJUSTAREAPORTALSTATE), - GEN_IMPORT(AreaForPoint, G_AREAFORPOINT), - GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), - GEN_IMPORT(InPVS, G_IN_PVS), - GEN_IMPORT(linkentity, G_LINKENTITY), - GEN_IMPORT(unlinkentity, G_UNLINKENTITY), - GEN_IMPORT(AreaEntities, G_AREAENTITIES), - GEN_IMPORT(ClipToEntity, G_CLIPTOENTITY), - GEN_IMPORT(HitEntity, G_HITENTITY), - GEN_IMPORT(imageindex, G_IMAGEINDEX), - GEN_IMPORT(itemindex, G_ITEMINDEX), - GEN_IMPORT(soundindex, G_SOUNDINDEX), - GEN_IMPORT(TIKI_RegisterModel, G_TIKI_REGISTERMODEL), - GEN_IMPORT(modeltiki, G_MODELTIKI), - GEN_IMPORT(modeltikianim, G_MODELTIKIANIM), - GEN_IMPORT(SetLightStyle, G_SETLIGHTSTYLE), - GEN_IMPORT(GameDir, G_GAMEDIR), - GEN_IMPORT(setmodel, G_SETMODEL), - GEN_IMPORT(clearmodel, G_CLEARMODEL), - GEN_IMPORT(TIKI_NumAnims, G_TIKI_NUMANIMS), - GEN_IMPORT(TIKI_NumSurfaces, G_TIKI_NUMSURFACES), - GEN_IMPORT(TIKI_NumTags, G_TIKI_NUMTAGS), - GEN_IMPORT_4(TIKI_CalculateBounds, G_TIKI_CALCULATEBOUNDS, void, dtiki_t*, float, vec3_t, vec3_t), - GEN_IMPORT(TIKI_GetSkeletor, G_TIKI_GETSKELETOR), - GEN_IMPORT(Anim_NameForNum, G_ANIM_NAMEFORNUM), - GEN_IMPORT(Anim_NumForName, G_ANIM_NUMFORNAME), - GEN_IMPORT(Anim_Random, G_ANIM_RANDOM), - GEN_IMPORT(Anim_NumFrames, G_ANIM_NUMFRAMES), - GEN_IMPORT(Anim_Time, G_ANIM_TIME), - GEN_IMPORT(Anim_Frametime, G_ANIM_FRAMETIME), - GEN_IMPORT(Anim_CrossTime, G_ANIM_CROSSTIME), - GEN_IMPORT(Anim_Delta, G_ANIM_DELTA), - GEN_IMPORT(Anim_AngularDelta, G_ANIM_ANGULARDELTA), - GEN_IMPORT(Anim_HasDelta, G_ANIM_HASDELTA), - GEN_IMPORT_5(Anim_DeltaOverTime, G_ANIM_DELTAOVERTIME, void, dtiki_t*, int, float, float, float*), - GEN_IMPORT_5(Anim_AngularDeltaOverTime, G_ANIM_ANGULARDELTAOVERTIME, void, dtiki_t*, int, float, float, float*), - GEN_IMPORT(Anim_Flags, G_ANIM_FLAGS), - GEN_IMPORT(Anim_FlagsSkel, G_ANIM_FLAGSSKEL), - GEN_IMPORT(Anim_HasCommands, G_ANIM_HASCOMMANDS), - GEN_IMPORT(NumHeadModels, G_NUMHEADMODELS), - GEN_IMPORT(GetHeadModel, G_GETHEADMODEL), - GEN_IMPORT(NumHeadSkins, G_NUMHEADSKINS), - GEN_IMPORT(GetHeadSkin, G_GETHEADSKIN), - GEN_IMPORT(Frame_Commands, G_FRAME_COMMANDS), - GEN_IMPORT(Surface_NameToNum, G_SURFACE_NAMETONUM), - GEN_IMPORT(Surface_NumToName, G_SURFACE_NUMTONAME), - GEN_IMPORT(Tag_NumForName, G_TAG_NUMFORNAME), - GEN_IMPORT(Tag_NameForNum, G_TAG_NAMEFORNUM), - GEN_IMPORT(TIKI_OrientationInternal, G_TIKI_ORIENTATIONINTERNAL), // todo: change types to actually match float, but also need to return an intptr_t instead of orientation_t - GEN_IMPORT(TIKI_TransformInternal, G_TIKI_TRANSFORMINTERNAL), - GEN_IMPORT_4(TIKI_IsOnGroundInternal, G_TIKI_ISONGROUNDINTERNAL, qboolean, dtiki_t*, int, int, float), - GEN_IMPORT_6(TIKI_SetPoseInternal, G_TIKI_SETPOSEINTERNAL, void, dtiki_t*, int, const frameInfo_t*, int*, vec4_t*, float), - GEN_IMPORT(CM_GetHitLocationInfo, G_CM_GETHITLOCATIONINFO), - GEN_IMPORT(CM_GetHitLocationInfoSecondary, G_CM_GETHITLOCATIONINFOSECONDARY), - GEN_IMPORT(Alias_Add, G_ALIAS_ADD), - GEN_IMPORT(Alias_FindRandom, G_ALIAS_FINDRANDOM), - GEN_IMPORT(Alias_Dump, G_ALIAS_DUMP), - GEN_IMPORT(Alias_Clear, G_ALIAS_CLEAR), - GEN_IMPORT(Alias_UpdateDialog, G_ALIAS_UPDATEDIALOG), - GEN_IMPORT(TIKI_NameForNum, G_TIKI_NAMEFORNUM), - GEN_IMPORT(GlobalAlias_Add, G_GLOBALALIAS_ADD), - GEN_IMPORT(GlobalAlias_FindRandom, G_GLOBALALIAS_FINDRANDOM), - GEN_IMPORT(GlobalAlias_Dump, G_GLOBALALIAS_DUMP), - GEN_IMPORT(GlobalAlias_Clear, G_GLOBALALIAS_CLEAR), - GEN_IMPORT(centerprintf, G_CENTERPRINTF), - GEN_IMPORT(locationprintf, G_LOCATIONPRINTF), - GEN_IMPORT_9(Sound, G_SOUND, void, vec3_t*, int, int, const char*, float, float, float, float, int), - GEN_IMPORT(StopSound, G_STOPSOUND), - GEN_IMPORT(SoundLength, G_SOUNDLENGTH), - GEN_IMPORT(SoundAmplitudes, G_SOUNDAMPLITUDES), - GEN_IMPORT(S_IsSoundPlaying, G_S_ISSOUNDPLAYING), - GEN_IMPORT(CalcCRC, G_CALCCRC), - nullptr, // DebugLines - nullptr, // numDebugLines - nullptr, // DebugStrings - nullptr, // numDebugStrings - GEN_IMPORT(LocateGameData, G_LOCATE_GAME_DATA), - GEN_IMPORT(SetFarPlane, G_SETFARPLANE), - GEN_IMPORT(SetSkyPortal, G_SETSKYPORTAL), - GEN_IMPORT(Popmenu, G_POPMENU), - GEN_IMPORT(Showmenu, G_SHOWMENU), - GEN_IMPORT(Hidemenu, G_HIDEMENU), - GEN_IMPORT(Pushmenu, G_PUSHMENU), - GEN_IMPORT(HideMouseCursor, G_HIDEMOUSECURSOR), - GEN_IMPORT(ShowMouseCursor, G_SHOWMOUSECURSOR), - GEN_IMPORT(MapTime, G_MAPTIME), - GEN_IMPORT(LoadResource, G_LOADRESOURCE), - GEN_IMPORT(ClearResource, G_CLEARRESOURCE), - GEN_IMPORT(Key_StringToKeynum, G_KEY_STRINGTOKEYNUM), - GEN_IMPORT(Key_KeynumToBindString, G_KEY_KEYNUMTOBINDSTRING), - GEN_IMPORT(Key_GetKeysForCommand, G_KEY_GETKEYSFORCOMMAND), - GEN_IMPORT(ArchiveLevel, G_ARCHIVELEVEL), - GEN_IMPORT(AddSvsTimeFixup, G_ADDSVSTIMEFIXUP), - GEN_IMPORT(HudDrawShader, G_HUDDRAWSHADER), - GEN_IMPORT(HudDrawAlign, G_HUDDRAWALIGN), - GEN_IMPORT(HudDrawRect, G_HUDDRAWRECT), - GEN_IMPORT(HudDrawVirtualSize, G_HUDDRAWVIRTUALSIZE), - GEN_IMPORT(HudDrawColor, G_HUDDRAWCOLOR), - GEN_IMPORT_2(HudDrawAlpha, G_HUDDRAWALPHA, void, int, float), - GEN_IMPORT(HudDrawString, G_HUDDRAWSTRING), - GEN_IMPORT(HudDrawFont, G_HUDDRAWFONT), - GEN_IMPORT(SanitizeName, G_SANITIZENAME), - GEN_IMPORT(pvssoundindex, G_PVSSOUNDINDEX), - nullptr, //fsDebug + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = GEN_GAME_QMM_MOD_MSGS(); }; +GEN_GAME_OBJ(MOHSH); -// these are "pre" hooks for storing some data for polyfills. -// we need these to be called BEFORE plugins' prehooks get called so they have to be done in the qmm_export table -// track entstrings for our G_GET_ENTITY_TOKEN syscall -static std::vector s_entity_tokens; -static size_t s_tokencount = 0; -static void MOHSH_SpawnEntities(char* entstring, int levelTime) { - if (entstring) { - s_entity_tokens = util_parse_entstring(entstring); - s_tokencount = 0; - } - cgame.is_from_QMM = true; - vmMain(GAME_SPAWN_ENTITIES, entstring, levelTime); -} +// auto-detection logic for MOHSH +bool MOHSH_GameSupport::AutoDetect(APIType engineapi) { + if (engineapi != QMM_API_GETGAMEAPI) + return false; + + if (!str_striequal(g_gameinfo.qmm_file, DefaultDLLName())) + return false; + if (!str_stristr(g_gameinfo.exe_file, "break")) + return false; -// at least one of first four args is a float (see big comment at top of game_q2r.cpp), so use specific types -static void MOHSH_DebugCircle(float* arg0, float arg1, float arg2, float arg3, float arg4, float arg5, qboolean arg6) { - cgame.is_from_QMM = true; - vmMain(GAME_DEBUG_CIRCLE, arg0, arg1, arg2, arg3, arg4, arg5, arg6); + return true; } -// struct with lambdas that call QMM's vmMain function. this is given to the game engine -static game_export_t qmm_export = { - GAME_API_VERSION, // apiversion - GEN_EXPORT(Init, GAME_INIT), - GEN_EXPORT(Shutdown, GAME_SHUTDOWN), - GEN_EXPORT(Cleanup, GAME_CLEANUP), - GEN_EXPORT(Precache, GAME_PRECACHE), - GEN_EXPORT(SetMap, GAME_SETMAP), - GEN_EXPORT(Restart, GAME_RESTART), - GEN_EXPORT(SetTime, GAME_SETTIME), - MOHSH_SpawnEntities, - GEN_EXPORT(ClientConnect, GAME_CLIENT_CONNECT), - GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), - GEN_EXPORT(ClientUserinfoChanged, GAME_CLIENT_USERINFO_CHANGED), - GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), - GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), - GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), - GEN_EXPORT(BotBegin, GAME_BOTBEGIN), - GEN_EXPORT(BotThink, GAME_BOTTHINK), - GEN_EXPORT(PrepFrame, GAME_PREP_FRAME), - GEN_EXPORT(RunFrame, GAME_RUN_FRAME), - GEN_EXPORT(ServerSpawned, GAME_SERVER_SPAWNED), - GEN_EXPORT(RegisterSounds, GAME_REGISTER_SOUNDS), - GEN_EXPORT(AllowPaused, GAME_ALLOW_PAUSED), - GEN_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND), - GEN_EXPORT(ArchivePersistant, GAME_ARCHIVE_PERSISTANT), - GEN_EXPORT(WriteLevel, GAME_WRITE_LEVEL), - GEN_EXPORT(ReadLevel, GAME_READ_LEVEL), - GEN_EXPORT(LevelArchiveValid, GAME_LEVEL_ARCHIVE_VALID), - GEN_EXPORT(ArchiveInteger, GAME_ARCHIVE_INTEGER), - GEN_EXPORT(ArchiveFloat, GAME_ARCHIVE_FLOAT), - GEN_EXPORT(ArchiveString, GAME_ARCHIVE_STRING), - GEN_EXPORT(ArchiveSvsTime, GAME_ARCHIVE_SVSTIME), - GEN_EXPORT(TIKI_Orientation, GAME_TIKI_ORIENTATION), // todo: change types to actually match float, but also need to return an intptr_t instead of orientation_t - MOHSH_DebugCircle, - GEN_EXPORT(SetFrameNumber, GAME_SET_FRAME_NUMBER), - GEN_EXPORT(SoundCallback, GAME_SOUND_CALLBACK), - - // the engine won't use these until after Init, so we can fill these in after each call into the mod's export functions ("vmMain") - nullptr, // profStruct - nullptr, // gentities - 0, // gentitySize - 0, // num_entities - 0, // max_entities - nullptr, // errorMessage -}; +// wrapper syscall function that calls actual engine func from orig_import +// this is how QMM and plugins will call into the engine +intptr_t MOHSH_GameSupport::syscall(intptr_t cmd, ...) { + QMM_GET_SYSCALL_ARGS(); +#ifdef _DEBUG + if (cmd != G_PRINT) + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHSH_GameSupport::syscall({} {}) called\n", EngMsgName(cmd), cmd); +#endif -// update the export variables from orig_export -static void s_update_export() { - if (!orig_export) - return; + // update export vars before calling into the engine + update_exports(); - bool changed = false; - - // if entity data changed, we need to send a G_LOCATE_GAME_DATA so plugins can hook it - if (qmm_export.gentities != orig_export->gentities - || qmm_export.gentitySize != orig_export->gentitySize - || qmm_export.num_entities != orig_export->num_entities - ) { - changed = true; - } - - qmm_export.profStruct = orig_export->profStruct; - qmm_export.gentities = orig_export->gentities; - qmm_export.gentitySize = orig_export->gentitySize; - qmm_export.num_entities = orig_export->num_entities; - qmm_export.max_entities = orig_export->max_entities; - qmm_export.errorMessage = orig_export->errorMessage; - - if (changed) { - // this will trigger this message to be fired to plugins, and then it will be handled - // by the empty "case G_LOCATE_GAME_DATA" in MOHAA_syscall - qmm_syscall(G_LOCATE_GAME_DATA, (intptr_t)qmm_export.gentities, qmm_export.num_entities, qmm_export.gentitySize, nullptr, 0); - } -} - - -// wrapper syscall function that calls actual engine func from orig_import -// this is how QMM and plugins will call into the engine -static intptr_t MOHSH_syscall(intptr_t cmd, ...) { - QMM_GET_SYSCALL_ARGS(); - -#ifdef _DEBUG - if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHSH_syscall({} {}) called\n", MOHSH_EngMsgName(cmd), cmd); -#endif - - // update export vars before calling into the engine - s_update_export(); - - intptr_t ret = 0; + intptr_t ret = 0; switch (cmd) { ROUTE_IMPORT(Printf, G_PRINTF); @@ -376,7 +132,7 @@ static intptr_t MOHSH_syscall(intptr_t cmd, ...) { ROUTE_IMPORT(FS_FOpenFile, G_FS_UNKNOWN); ROUTE_IMPORT(FS_PrepFileWrite, G_FS_PREPFILEWRITE); // handled below since we do special handling for these for FILE* access - //ROUTE_IMPORT(FS_Write, G_FS_WRITE); + // ROUTE_IMPORT(FS_Write, G_FS_WRITE); // ROUTE_IMPORT(FS_Read, G_FS_READ); // ROUTE_IMPORT(FS_FCloseFile, G_FS_FCLOSE_FILE); ROUTE_IMPORT(FS_Tell, G_FS_TELL); @@ -388,8 +144,8 @@ static intptr_t MOHSH_syscall(intptr_t cmd, ...) { ROUTE_IMPORT(FS_FreeFileList, G_FS_FREEFILELIST); ROUTE_IMPORT(GetArchiveFileName, G_GETARCHIVEFILENAME); // handled below since we do special handling to deal with the "when" argument - // ROUTE_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND); - // ROUTE_IMPORT(ExecuteConsoleCommand, G_EXECUTE_CONSOLE_COMMAND); + //ROUTE_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND); + //ROUTE_IMPORT(ExecuteConsoleCommand, G_EXECUTE_CONSOLE_COMMAND); ROUTE_IMPORT(DebugGraph, G_DEBUGGRAPH); ROUTE_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND); ROUTE_IMPORT(DropClient, G_DROP_CLIENT); @@ -532,7 +288,7 @@ static intptr_t MOHSH_syscall(intptr_t cmd, ...) { // handle special cmds which QMM uses but MOHSH doesn't have an analogue for case G_CVAR_REGISTER: { - // mohsh: cvar_t* (*Cvar_Get)(const char* varName, const char* varValue, int varFlags) + // mohaa: cvar_t* (*Cvar_Get)(const char* varName, const char* varValue, int varFlags) // q3a: void trap_Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags ) // qmm always passes NULL for vmCvar so don't worry about it const char* varName = (const char*)(args[1]); @@ -542,7 +298,7 @@ static intptr_t MOHSH_syscall(intptr_t cmd, ...) { break; } case G_CVAR_VARIABLE_STRING_BUFFER: { - // mohsh: cvar_t *(*Cvar_Get)(const char *varName, const char *varValue, int varFlags) + // mohaa: cvar_t *(*Cvar_Get)(const char *varName, const char *varValue, int varFlags) // q3a: void trap_Cvar_VariableStringBuffer(const char* var_name, char* buffer, int bufsize) const char* varName = (const char*)(args[0]); char* buffer = (char*)(args[1]); @@ -554,7 +310,7 @@ static intptr_t MOHSH_syscall(intptr_t cmd, ...) { break; } case G_CVAR_VARIABLE_INTEGER_VALUE: { - // mohsh: cvar_t *(*Cvar_Get)(const char *varName, const char *varValue, int varFlags) + // mohaa: cvar_t *(*Cvar_Get)(const char *varName, const char *varValue, int varFlags) // q3a: int trap_Cvar_VariableIntegerValue(const char* var_name) const char* varName = (const char*)(args[0]); cvar_t* cvar = orig_import.Cvar_Get(varName, "", 0); @@ -584,7 +340,7 @@ static intptr_t MOHSH_syscall(intptr_t cmd, ...) { // bypassing them entirely when opening. if somehow a real fileHandle_t gets passed to G_FS_READ, // G_FS_WRITE, or G_FS_FCLOSE_FILE, then they will be passed to the real engine functions. otherwise, // they just use FILE* functions - + // int trap_FS_FOpenFile(const char *qpath, fileHandle_t *f, fsMode_t mode); const char* qpath = (const char*)args[0]; fileHandle_t* f = (fileHandle_t*)args[1]; @@ -678,7 +434,7 @@ static intptr_t MOHSH_syscall(intptr_t cmd, ...) { } case G_GET_ENTITY_TOKEN: { // qboolean trap_GetEntityToken(char *buffer, int bufferSize); - if (s_tokencount >= s_entity_tokens.size()) { + if (token_counter >= entity_tokens.size()) { ret = qfalse; break; } @@ -686,7 +442,7 @@ static intptr_t MOHSH_syscall(intptr_t cmd, ...) { char* buffer = (char*)args[0]; intptr_t bufferSize = args[1]; - strncpyz(buffer, s_entity_tokens[s_tokencount++].c_str(), (size_t)bufferSize); + strncpyz(buffer, entity_tokens[token_counter++].c_str(), (size_t)bufferSize); ret = qtrue; break; } @@ -699,7 +455,7 @@ static intptr_t MOHSH_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHSH_syscall({} {}) returning {}\n", MOHSH_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHSH_GameSupport::syscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; @@ -708,11 +464,11 @@ static intptr_t MOHSH_syscall(intptr_t cmd, ...) { // wrapper vmMain function that calls actual mod func from orig_export // this is how QMM and plugins will call into the mod -static intptr_t MOHSH_vmMain(intptr_t cmd, ...) { +intptr_t MOHSH_GameSupport::vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHSH_vmMain({} {}) called\n", MOHSH_ModMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHSH_GameSupport::vmMain({} {}) called\n", ModMsgName(cmd), cmd); #endif if (!orig_export) @@ -771,18 +527,18 @@ static intptr_t MOHSH_vmMain(intptr_t cmd, ...) { }; // update export vars after returning from the mod - s_update_export(); + update_exports(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHSH_vmMain({} {}) returning {}\n", MOHSH_ModMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHSH_GameSupport::vmMain({} {}) returning {}\n", ModMsgName(cmd), cmd, ret); #endif return ret; } -static void* MOHSH_Entry(void* import, void*, APIType) { - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHSH_Entry({}) called\n", import); +void* MOHSH_GameSupport::Entry(void* import, void*, APIType) { + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHSH_GameSupport::Entry({}) called\n", import); // original import struct from engine // the struct given by the engine goes out of scope after this returns so we have to copy the whole thing @@ -796,13 +552,7 @@ static void* MOHSH_Entry(void* import, void*, APIType) { qmm_import.numDebugStrings = orig_import.numDebugStrings; qmm_import.fsDebug = orig_import.fsDebug; - // pointer to wrapper vmMain function that calls actual mod func from orig_export - g_gameinfo.pfnvmMain = MOHSH_vmMain; - - // pointer to wrapper syscall function that calls actual engine func from orig_import - g_gameinfo.pfnsyscall = MOHSH_syscall; - - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHSH_Entry({}) returning {}\n", import, fmt::ptr(&qmm_export)); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("MOHSH_GameSupport::Entry({}) returning {}\n", import, fmt::ptr(&qmm_export)); // struct full of export lambdas to QMM's vmMain // this gets returned to the game engine, but we haven't loaded the mod yet. @@ -811,7 +561,7 @@ static void* MOHSH_Entry(void* import, void*, APIType) { } -static bool MOHSH_ModLoad(void* entry, APIType modapi) { +bool MOHSH_GameSupport::ModLoad(void* entry, APIType modapi) { if (modapi != QMM_API_GETGAMEAPI) return false; @@ -822,12 +572,12 @@ static bool MOHSH_ModLoad(void* entry, APIType modapi) { } -static void MOHSH_ModUnload() { +void MOHSH_GameSupport::ModUnload() { orig_export = nullptr; } -static const char* MOHSH_EngMsgName(intptr_t cmd) { +const char* MOHSH_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINTF); GEN_CASE(G_DPRINTF); @@ -1016,7 +766,7 @@ static const char* MOHSH_EngMsgName(intptr_t cmd) { } -static const char* MOHSH_ModMsgName(intptr_t cmd) { +const char* MOHSH_GameSupport::ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAMEV_APIVERSION); GEN_CASE(GAME_INIT); @@ -1063,3 +813,287 @@ static const char* MOHSH_ModMsgName(intptr_t cmd) { return "unknown"; } } + + +void MOHSH_GameSupport::update_exports() { + if (!orig_export) + return; + + bool changed = false; + + // if entity data changed, we need to send a G_LOCATE_GAME_DATA so plugins can hook it + if (qmm_export.gentities != orig_export->gentities + || qmm_export.gentitySize != orig_export->gentitySize + || qmm_export.num_entities != orig_export->num_entities + ) { + changed = true; + } + + qmm_export.profStruct = orig_export->profStruct; + qmm_export.gentities = orig_export->gentities; + qmm_export.gentitySize = orig_export->gentitySize; + qmm_export.num_entities = orig_export->num_entities; + qmm_export.max_entities = orig_export->max_entities; + qmm_export.errorMessage = orig_export->errorMessage; + + if (changed) { + // this will trigger this message to be fired to plugins, and then it will be handled + // by the empty "case G_LOCATE_GAME_DATA" in MOHAA_syscall + qmm_syscall(G_LOCATE_GAME_DATA, (intptr_t)qmm_export.gentities, qmm_export.num_entities, qmm_export.gentitySize, nullptr, 0); + } +} + + +game_import_t MOHSH_GameSupport::orig_import; + + +game_export_t* MOHSH_GameSupport::orig_export = nullptr; + + +game_import_t MOHSH_GameSupport::qmm_import = { + GEN_IMPORT(Printf, G_PRINTF), + GEN_IMPORT(DPrintf, G_DPRINTF), + GEN_IMPORT(DPrintf2, G_DPRINTF2), + GEN_IMPORT(DebugPrintf, G_DEBUGPRINTF), + GEN_IMPORT(Error, G_ERROR), + GEN_IMPORT(Milliseconds, G_MILLISECONDS), + GEN_IMPORT(LV_ConvertString, G_LV_CONVERTSTRING), + GEN_IMPORT(CL_LV_ConvertString, G_CL_LV_CONVERTSTRING), + GEN_IMPORT(Malloc, G_MALLOC), + GEN_IMPORT(Free, G_FREE), + GEN_IMPORT(Cvar_Get, G_CVAR_GET), + GEN_IMPORT(cvar_set, G_CVAR_SET), + GEN_IMPORT(cvar_set2, G_CVAR_SET2), + GEN_IMPORT(NextCvar, G_NEXTCVAR), + GEN_IMPORT(Argc, G_ARGC), + GEN_IMPORT(Argv, G_ARGV), + GEN_IMPORT(Args, G_ARGS), + GEN_IMPORT(AddCommand, G_ADDCOMMAND), + GEN_IMPORT(FS_ReadFile, G_FS_READFILE), + GEN_IMPORT(FS_FreeFile, G_FS_FREEFILE), + GEN_IMPORT(FS_WriteFile, G_FS_WRITEFILE), + GEN_IMPORT(FS_FOpenFileWrite, G_FS_FOPEN_FILE_WRITE), + GEN_IMPORT(FS_FOpenFileAppend, G_FS_FOPEN_FILE_APPEND), + GEN_IMPORT(FS_FOpenFile, G_FS_UNKNOWN), + GEN_IMPORT(FS_PrepFileWrite, G_FS_PREPFILEWRITE), + GEN_IMPORT(FS_Write, G_FS_WRITE), + GEN_IMPORT(FS_Read, G_FS_READ), + GEN_IMPORT(FS_FCloseFile, G_FS_FCLOSE_FILE), + GEN_IMPORT(FS_Tell, G_FS_TELL), + GEN_IMPORT(FS_Seek, G_FS_SEEK), + GEN_IMPORT(FS_Flush, G_FS_FLUSH), + GEN_IMPORT(FS_FileNewer, G_FS_FILENEWER), + GEN_IMPORT(FS_CanonicalFilename, G_FS_CANONICALFILENAME), + GEN_IMPORT(FS_ListFiles, G_FS_LISTFILES), + GEN_IMPORT(FS_FreeFileList, G_FS_FREEFILELIST), + GEN_IMPORT(GetArchiveFileName, G_GETARCHIVEFILENAME), + GEN_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND), + GEN_IMPORT(ExecuteConsoleCommand, G_EXECUTE_CONSOLE_COMMAND), + GEN_IMPORT_1(DebugGraph, G_DEBUGGRAPH, void, float), + GEN_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND), + GEN_IMPORT(DropClient, G_DROP_CLIENT), + GEN_IMPORT(MSG_WriteBits, G_MSG_WRITEBITS), + GEN_IMPORT(MSG_WriteChar, G_MSG_WRITECHAR), + GEN_IMPORT(MSG_WriteByte, G_MSG_WRITEBYTE), + GEN_IMPORT(MSG_WriteSVC, G_MSG_WRITESVC), + GEN_IMPORT(MSG_WriteShort, G_MSG_WRITESHORT), + GEN_IMPORT(MSG_WriteLong, G_MSG_WRITELONG), + GEN_IMPORT_1(MSG_WriteFloat, G_MSG_WRITEFLOAT, void, float), + GEN_IMPORT(MSG_WriteString, G_MSG_WRITESTRING), + GEN_IMPORT_1(MSG_WriteAngle8, G_MSG_WRITEANGLE8, void, float), + GEN_IMPORT_1(MSG_WriteAngle16, G_MSG_WRITEANGLE16, void, float), + GEN_IMPORT_1(MSG_WriteCoord, G_MSG_WRITECOORD, void, float), + GEN_IMPORT(MSG_WriteDir, G_MSG_WRITEDIR), + GEN_IMPORT(MSG_StartCGM, G_MSG_STARTCGM), + GEN_IMPORT(MSG_EndCGM, G_MSG_ENDCGM), + GEN_IMPORT(MSG_SetClient, G_MSG_SETCLIENT), + GEN_IMPORT(SetBroadcastVisible, G_SETBROADCASTVISIBLE), + GEN_IMPORT(SetBroadcastHearable, G_SETBROADCASTHEARABLE), + GEN_IMPORT(SetBroadcastAll, G_SETBROADCASTALL), + GEN_IMPORT(setConfigstring, G_SET_CONFIGSTRING), + GEN_IMPORT(getConfigstring, G_GET_CONFIGSTRING), + GEN_IMPORT(SetUserinfo, G_SET_USERINFO), + GEN_IMPORT(GetUserinfo, G_GET_USERINFO), + GEN_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL), + GEN_IMPORT(ModelBoundsFromName, G_MODELBOUNDSFROMNAME), + GEN_IMPORT(SightTraceEntity, G_SIGHTTRACEENTITY), + GEN_IMPORT(SightTrace, G_SIGHTTRACE), + GEN_IMPORT(trace, G_TRACE), + GEN_IMPORT(CM_VisualObfuscation, G_CM_VISUALOBFUSCATION), + GEN_IMPORT(GetShader, G_GETSHADER), + GEN_IMPORT(pointcontents, G_POINT_CONTENTS), + GEN_IMPORT(PointBrushnum, G_POINTBRUSHNUM), + GEN_IMPORT(AdjustAreaPortalState, G_ADJUSTAREAPORTALSTATE), + GEN_IMPORT(AreaForPoint, G_AREAFORPOINT), + GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), + GEN_IMPORT(InPVS, G_IN_PVS), + GEN_IMPORT(linkentity, G_LINKENTITY), + GEN_IMPORT(unlinkentity, G_UNLINKENTITY), + GEN_IMPORT(AreaEntities, G_AREAENTITIES), + GEN_IMPORT(ClipToEntity, G_CLIPTOENTITY), + GEN_IMPORT(HitEntity, G_HITENTITY), + GEN_IMPORT(imageindex, G_IMAGEINDEX), + GEN_IMPORT(itemindex, G_ITEMINDEX), + GEN_IMPORT(soundindex, G_SOUNDINDEX), + GEN_IMPORT(TIKI_RegisterModel, G_TIKI_REGISTERMODEL), + GEN_IMPORT(modeltiki, G_MODELTIKI), + GEN_IMPORT(modeltikianim, G_MODELTIKIANIM), + GEN_IMPORT(SetLightStyle, G_SETLIGHTSTYLE), + GEN_IMPORT(GameDir, G_GAMEDIR), + GEN_IMPORT(setmodel, G_SETMODEL), + GEN_IMPORT(clearmodel, G_CLEARMODEL), + GEN_IMPORT(TIKI_NumAnims, G_TIKI_NUMANIMS), + GEN_IMPORT(TIKI_NumSurfaces, G_TIKI_NUMSURFACES), + GEN_IMPORT(TIKI_NumTags, G_TIKI_NUMTAGS), + GEN_IMPORT_4(TIKI_CalculateBounds, G_TIKI_CALCULATEBOUNDS, void, dtiki_t*, float, vec3_t, vec3_t), + GEN_IMPORT(TIKI_GetSkeletor, G_TIKI_GETSKELETOR), + GEN_IMPORT(Anim_NameForNum, G_ANIM_NAMEFORNUM), + GEN_IMPORT(Anim_NumForName, G_ANIM_NUMFORNAME), + GEN_IMPORT(Anim_Random, G_ANIM_RANDOM), + GEN_IMPORT(Anim_NumFrames, G_ANIM_NUMFRAMES), + GEN_IMPORT(Anim_Time, G_ANIM_TIME), + GEN_IMPORT(Anim_Frametime, G_ANIM_FRAMETIME), + GEN_IMPORT(Anim_CrossTime, G_ANIM_CROSSTIME), + GEN_IMPORT(Anim_Delta, G_ANIM_DELTA), + GEN_IMPORT(Anim_AngularDelta, G_ANIM_ANGULARDELTA), + GEN_IMPORT(Anim_HasDelta, G_ANIM_HASDELTA), + GEN_IMPORT_5(Anim_DeltaOverTime, G_ANIM_DELTAOVERTIME, void, dtiki_t*, int, float, float, float*), + GEN_IMPORT_5(Anim_AngularDeltaOverTime, G_ANIM_ANGULARDELTAOVERTIME, void, dtiki_t*, int, float, float, float*), + GEN_IMPORT(Anim_Flags, G_ANIM_FLAGS), + GEN_IMPORT(Anim_FlagsSkel, G_ANIM_FLAGSSKEL), + GEN_IMPORT(Anim_HasCommands, G_ANIM_HASCOMMANDS), + GEN_IMPORT(NumHeadModels, G_NUMHEADMODELS), + GEN_IMPORT(GetHeadModel, G_GETHEADMODEL), + GEN_IMPORT(NumHeadSkins, G_NUMHEADSKINS), + GEN_IMPORT(GetHeadSkin, G_GETHEADSKIN), + GEN_IMPORT(Frame_Commands, G_FRAME_COMMANDS), + GEN_IMPORT(Surface_NameToNum, G_SURFACE_NAMETONUM), + GEN_IMPORT(Surface_NumToName, G_SURFACE_NUMTONAME), + GEN_IMPORT(Tag_NumForName, G_TAG_NUMFORNAME), + GEN_IMPORT(Tag_NameForNum, G_TAG_NAMEFORNUM), + GEN_IMPORT(TIKI_OrientationInternal, G_TIKI_ORIENTATIONINTERNAL), // todo: change types to actually match float, but also need to return an intptr_t instead of orientation_t + GEN_IMPORT(TIKI_TransformInternal, G_TIKI_TRANSFORMINTERNAL), + GEN_IMPORT_4(TIKI_IsOnGroundInternal, G_TIKI_ISONGROUNDINTERNAL, qboolean, dtiki_t*, int, int, float), + GEN_IMPORT_6(TIKI_SetPoseInternal, G_TIKI_SETPOSEINTERNAL, void, dtiki_t*, int, const frameInfo_t*, int*, vec4_t*, float), + GEN_IMPORT(CM_GetHitLocationInfo, G_CM_GETHITLOCATIONINFO), + GEN_IMPORT(CM_GetHitLocationInfoSecondary, G_CM_GETHITLOCATIONINFOSECONDARY), + GEN_IMPORT(Alias_Add, G_ALIAS_ADD), + GEN_IMPORT(Alias_FindRandom, G_ALIAS_FINDRANDOM), + GEN_IMPORT(Alias_Dump, G_ALIAS_DUMP), + GEN_IMPORT(Alias_Clear, G_ALIAS_CLEAR), + GEN_IMPORT(Alias_UpdateDialog, G_ALIAS_UPDATEDIALOG), + GEN_IMPORT(TIKI_NameForNum, G_TIKI_NAMEFORNUM), + GEN_IMPORT(GlobalAlias_Add, G_GLOBALALIAS_ADD), + GEN_IMPORT(GlobalAlias_FindRandom, G_GLOBALALIAS_FINDRANDOM), + GEN_IMPORT(GlobalAlias_Dump, G_GLOBALALIAS_DUMP), + GEN_IMPORT(GlobalAlias_Clear, G_GLOBALALIAS_CLEAR), + GEN_IMPORT(centerprintf, G_CENTERPRINTF), + GEN_IMPORT(locationprintf, G_LOCATIONPRINTF), + GEN_IMPORT_9(Sound, G_SOUND, void, vec3_t*, int, int, const char*, float, float, float, float, int), + GEN_IMPORT(StopSound, G_STOPSOUND), + GEN_IMPORT(SoundLength, G_SOUNDLENGTH), + GEN_IMPORT(SoundAmplitudes, G_SOUNDAMPLITUDES), + GEN_IMPORT(S_IsSoundPlaying, G_S_ISSOUNDPLAYING), + GEN_IMPORT(CalcCRC, G_CALCCRC), + nullptr, // DebugLines + nullptr, // numDebugLines + nullptr, // DebugStrings + nullptr, // numDebugStrings + GEN_IMPORT(LocateGameData, G_LOCATE_GAME_DATA), + GEN_IMPORT(SetFarPlane, G_SETFARPLANE), + GEN_IMPORT(SetSkyPortal, G_SETSKYPORTAL), + GEN_IMPORT(Popmenu, G_POPMENU), + GEN_IMPORT(Showmenu, G_SHOWMENU), + GEN_IMPORT(Hidemenu, G_HIDEMENU), + GEN_IMPORT(Pushmenu, G_PUSHMENU), + GEN_IMPORT(HideMouseCursor, G_HIDEMOUSECURSOR), + GEN_IMPORT(ShowMouseCursor, G_SHOWMOUSECURSOR), + GEN_IMPORT(MapTime, G_MAPTIME), + GEN_IMPORT(LoadResource, G_LOADRESOURCE), + GEN_IMPORT(ClearResource, G_CLEARRESOURCE), + GEN_IMPORT(Key_StringToKeynum, G_KEY_STRINGTOKEYNUM), + GEN_IMPORT(Key_KeynumToBindString, G_KEY_KEYNUMTOBINDSTRING), + GEN_IMPORT(Key_GetKeysForCommand, G_KEY_GETKEYSFORCOMMAND), + GEN_IMPORT(ArchiveLevel, G_ARCHIVELEVEL), + GEN_IMPORT(AddSvsTimeFixup, G_ADDSVSTIMEFIXUP), + GEN_IMPORT(HudDrawShader, G_HUDDRAWSHADER), + GEN_IMPORT(HudDrawAlign, G_HUDDRAWALIGN), + GEN_IMPORT(HudDrawRect, G_HUDDRAWRECT), + GEN_IMPORT(HudDrawVirtualSize, G_HUDDRAWVIRTUALSIZE), + GEN_IMPORT(HudDrawColor, G_HUDDRAWCOLOR), + GEN_IMPORT_2(HudDrawAlpha, G_HUDDRAWALPHA, void, int, float), + GEN_IMPORT(HudDrawString, G_HUDDRAWSTRING), + GEN_IMPORT(HudDrawFont, G_HUDDRAWFONT), + GEN_IMPORT(SanitizeName, G_SANITIZENAME), + GEN_IMPORT(pvssoundindex, G_PVSSOUNDINDEX), + nullptr, //fsDebug +}; + + +std::vector MOHSH_GameSupport::entity_tokens; +size_t MOHSH_GameSupport::token_counter = 0; +void MOHSH_GameSupport::SpawnEntities(char* entstring, int levelTime) { + if (entstring) { + entity_tokens = util_parse_entstring(entstring); + token_counter = 0; + } + cgame.is_from_QMM = true; + ::vmMain(GAME_SPAWN_ENTITIES, entstring, levelTime); +} + + +// at least one of first four args is a float (see big comment at top of game_q2r.cpp), so use specific types +void MOHSH_GameSupport::DebugCircle(float* arg0, float arg1, float arg2, float arg3, float arg4, float arg5, qboolean arg6) { + cgame.is_from_QMM = true; + ::vmMain(GAME_DEBUG_CIRCLE, arg0, arg1, arg2, arg3, arg4, arg5, arg6); +} + + +// struct with lambdas that call QMM's vmMain function. this is given to the game engine +game_export_t MOHSH_GameSupport::qmm_export = { + GAME_API_VERSION, // apiversion + GEN_EXPORT(Init, GAME_INIT), + GEN_EXPORT(Shutdown, GAME_SHUTDOWN), + GEN_EXPORT(Cleanup, GAME_CLEANUP), + GEN_EXPORT(Precache, GAME_PRECACHE), + GEN_EXPORT(SetMap, GAME_SETMAP), + GEN_EXPORT(Restart, GAME_RESTART), + GEN_EXPORT(SetTime, GAME_SETTIME), + MOHSH_GameSupport::SpawnEntities, + GEN_EXPORT(ClientConnect, GAME_CLIENT_CONNECT), + GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), + GEN_EXPORT(ClientUserinfoChanged, GAME_CLIENT_USERINFO_CHANGED), + GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), + GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), + GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), + GEN_EXPORT(BotBegin, GAME_BOTBEGIN), + GEN_EXPORT(BotThink, GAME_BOTTHINK), + GEN_EXPORT(PrepFrame, GAME_PREP_FRAME), + GEN_EXPORT(RunFrame, GAME_RUN_FRAME), + GEN_EXPORT(ServerSpawned, GAME_SERVER_SPAWNED), + GEN_EXPORT(RegisterSounds, GAME_REGISTER_SOUNDS), + GEN_EXPORT(AllowPaused, GAME_ALLOW_PAUSED), + GEN_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND), + GEN_EXPORT(ArchivePersistant, GAME_ARCHIVE_PERSISTANT), + GEN_EXPORT(WriteLevel, GAME_WRITE_LEVEL), + GEN_EXPORT(ReadLevel, GAME_READ_LEVEL), + GEN_EXPORT(LevelArchiveValid, GAME_LEVEL_ARCHIVE_VALID), + GEN_EXPORT(ArchiveInteger, GAME_ARCHIVE_INTEGER), + GEN_EXPORT(ArchiveFloat, GAME_ARCHIVE_FLOAT), + GEN_EXPORT(ArchiveString, GAME_ARCHIVE_STRING), + GEN_EXPORT(ArchiveSvsTime, GAME_ARCHIVE_SVSTIME), + GEN_EXPORT(TIKI_Orientation, GAME_TIKI_ORIENTATION), // todo: change types to actually match float, but also need to return an intptr_t instead of orientation_t + MOHSH_GameSupport::DebugCircle, + GEN_EXPORT(SetFrameNumber, GAME_SET_FRAME_NUMBER), + GEN_EXPORT(SoundCallback, GAME_SOUND_CALLBACK), + + // the engine won't use these until after Init, so we can fill these in after each call into the mod's export functions ("vmMain") + nullptr, // profStruct + nullptr, // gentities + 0, // gentitySize + 0, // num_entities + 0, // max_entities + nullptr, // errorMessage +}; + +#endif // QMM_ARCH_32 diff --git a/src/game_q2r.cpp b/src/game_q2r.cpp index 8cbc19d..b50697d 100644 --- a/src/game_q2r.cpp +++ b/src/game_q2r.cpp @@ -30,27 +30,66 @@ Created By: #include "main.h" #include "util.h" -// GAME_PREINIT gets called first, which is when QMM has to perform mod/plugin loading, but we -// don't want to make plugins have to use separate code to handle the actual GAME_INIT message, -// so we manually fill out the msg arrays -static int Q2R_qmm_eng_msgs[] = { - G_PRINT, G_ERROR, G_ARGV, G_ARGC, G_SEND_CONSOLE_COMMAND, G_GET_CONFIGSTRING, - G_CVAR_REGISTER, G_CVAR_VARIABLE_STRING_BUFFER, G_CVAR_VARIABLE_INTEGER_VALUE, CVAR_SERVERINFO, CVAR_ROM, - G_FS_FOPEN_FILE, G_FS_READ, G_FS_WRITE, G_FS_FCLOSE_FILE, EXEC_APPEND, FS_READ, -}; -static int Q2R_qmm_mod_msgs[] = { - GAME_PREINIT, GAME_SHUTDOWN, GAME_CONSOLE_COMMAND, +struct Q2R_GameSupport : public GameSupport { + virtual const char* EngMsgName(intptr_t msg); + virtual const char* ModMsgName(intptr_t msg); + virtual bool AutoDetect(APIType engine_api); + virtual void* Entry(void* syscall, void*, APIType engine_api); + virtual bool ModLoad(void* entry, APIType mod_api); + virtual void ModUnload(); + + virtual intptr_t syscall(intptr_t, ...); + virtual intptr_t vmMain(intptr_t, ...); + + virtual const char* DefaultDLLName() { return "game_" X64_DLL; } + virtual const char* DefaultModDir() { return "baseq2"; } + virtual const char* GameName() { return "Quake 2 Remastered"; } + virtual const char* GameCode() { return "Q2R"; } + +private: + static void update_exports(); + + // track configstrings for our G_GET_CONFIGSTRING syscall + static std::map configstrings; + static void configstring(int num, const char* configstring); + + // track userinfo for our G_GET_USERINFO syscall + static std::map userinfos; + static bool ClientConnect(edict_t* ent, char* userinfo, const char* social_id, bool isBot); + static void ClientUserinfoChanged(edict_t* ent, const char* userinfo); + + // track entstrings for our G_GET_ENTITY_TOKEN syscall + static std::vector entity_tokens; + static size_t token_counter; + static void SpawnEntities(const char* mapname, const char* entstring, const char* spawnpoint); + + // a copy of the original import struct that comes from the game engine + static game_import_t orig_import; + + // a copy of the original export struct pointer that comes from the mod + static game_export_t* orig_export; + + // struct with lambdas that call QMM's syscall function. this is given to the mod + static game_import_t qmm_import; + + // struct with lambdas that call QMM's vmMain function. this is given to the game engine + static game_export_t qmm_export; + + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); + // GAME_PREINIT gets called first, which is when QMM has to perform mod/plugin loading, but we + // don't want to make plugins have to use separate code to handle the actual GAME_INIT message + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = { GAME_PREINIT, GAME_SHUTDOWN, GAME_CONSOLE_COMMAND, }; }; -GEN_GAME_FUNCS(Q2R); +GEN_GAME_OBJ(Q2R); // auto-detection logic for Q2R -static bool Q2R_AutoDetect(api_supportedgame* game, APIType engineapi) { +bool Q2R_GameSupport::AutoDetect(APIType engineapi) { if (engineapi != QMM_API_GETGAMEAPI) return false; - if (!str_striequal(g_gameinfo.qmm_file, game->dllname)) + if (!str_striequal(g_gameinfo.qmm_file, DefaultDLLName())) return false; if (!str_stristr(g_gameinfo.exe_file, "quake2ex")) @@ -60,239 +99,18 @@ static bool Q2R_AutoDetect(api_supportedgame* game, APIType engineapi) { } -// a copy of the original import struct that comes from the game engine -static game_import_t orig_import; - -// a copy of the original export struct pointer that comes from the mod -static game_export_t* orig_export = nullptr; - -/* - The sound() import gets messed up because the volume float arg doesn't get passed properly. Server-generated - sound (i.e. anything from the world like item pickup or another player attacking) would be silent. After - debugging, the volume argument would appear to come through as ~1e-19 or something exceptionally small. - Assigning qmm_import.sound=orig_import.sound would resolve the issue but obviously not allow for plugin hooking. - turns out the problem is the float arguments and the GEN_IMPORT macro using intptr_t. - - From: https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention#parameter-passing - "Any floating-point and double-precision arguments in the first four parameters are passed in XMM0 - XMM3, - depending on position. Floating-point values are only placed in the integer registers RCX, RDX, R8, and R9 - when there are varargs arguments." - - Since the Q2R engine is 64-bit and does not use varargs, floats in the first 4 args don't get put into the - integer registers. Everything after 4 arguments are passed on the stack in 8-byte-aligned slots. So, we can - pull the correct arguments with matching prototypes, then pass them to the varargs syscall where they get - handled properly. -*/ -// struct with lambdas that call QMM's syscall function. this is given to the mod -static game_import_t qmm_import = { - 0, // tick_rate - 0, // frame_time_s - 0, // frame_time_ms - GEN_IMPORT(Broadcast_Print, G_BROADCAST_PRINT), - GEN_IMPORT(Com_Print, G_COM_PRINT), - GEN_IMPORT(Client_Print, G_CLIENT_PRINT), - GEN_IMPORT(Center_Print, G_CENTERPRINT), - GEN_IMPORT_6(sound, G_SOUND, void, edict_t*, soundchan_t, int, float, float, float), - GEN_IMPORT_7(positioned_sound, G_POSITIONED_SOUND, void, gvec3_cref_t, edict_t*, soundchan_t, int, float, float, float), - GEN_IMPORT_9(local_sound, G_LOCAL_SOUND, void, edict_t*, gvec3_cptr_t, edict_t*, soundchan_t, int, float, float, float, uint32_t), - GEN_IMPORT(configstring, G_CONFIGSTRING), - GEN_IMPORT(get_configstring, G_GET_CONFIGSTRING), - GEN_IMPORT(Com_Error, G_COM_ERROR), - GEN_IMPORT(modelindex, G_MODELINDEX), - GEN_IMPORT(soundindex, G_SOUNDINDEX), - GEN_IMPORT(imageindex, G_IMAGEINDEX), - GEN_IMPORT(setmodel, G_SETMODEL), - GEN_IMPORT(trace, G_TRACE), - GEN_IMPORT(clip, G_CLIP), - GEN_IMPORT(pointcontents, G_POINT_CONTENTS), - GEN_IMPORT(inPVS, G_IN_PVS), - GEN_IMPORT(inPHS, G_IN_PHS), - GEN_IMPORT(SetAreaPortalState, G_SETAREAPORTALSTATE), - GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), - GEN_IMPORT(linkentity, G_LINKENTITY), - GEN_IMPORT(unlinkentity, G_UNLINKENTITY), - GEN_IMPORT(BoxEdicts, G_BOXEDICTS), - GEN_IMPORT(multicast, G_MULTICAST), - GEN_IMPORT(unicast, G_UNICAST), - GEN_IMPORT(WriteChar, G_MSG_WRITECHAR), - GEN_IMPORT(WriteByte, G_MSG_WRITEBYTE), - GEN_IMPORT(WriteShort, G_MSG_WRITESHORT), - GEN_IMPORT(WriteLong, G_MSG_WRITELONG), - GEN_IMPORT_1(WriteFloat, G_MSG_WRITEFLOAT, void, float), - GEN_IMPORT(WriteString, G_MSG_WRITESTRING), - GEN_IMPORT(WritePosition, G_MSG_WRITEPOSITION), - GEN_IMPORT(WriteDir, G_MSG_WRITEDIR), - GEN_IMPORT_1(WriteAngle, G_MSG_WRITEANGLE, void, float), - GEN_IMPORT(WriteEntity, G_MSG_WRITEENTITY), - GEN_IMPORT(TagMalloc, G_TAGMALLOC), - GEN_IMPORT(TagFree, G_TAGFREE), - GEN_IMPORT(FreeTags, G_FREETAGS), - GEN_IMPORT(cvar, G_CVAR), - GEN_IMPORT(cvar_set, G_CVAR_SET), - GEN_IMPORT(cvar_forceset, G_CVAR_FORCESET), - GEN_IMPORT(argc, G_ARGC), - GEN_IMPORT(argv, G_ARGV), - GEN_IMPORT(args, G_ARGS), - GEN_IMPORT(AddCommandString, G_ADDCOMMANDSTRING), - GEN_IMPORT_2(DebugGraph, G_DEBUGGRAPH, void, float, int), - GEN_IMPORT(GetExtension, G_GET_EXTENSION), - GEN_IMPORT(Bot_RegisterEdict, G_BOT_REGISTEREDICT), - GEN_IMPORT(Bot_UnRegisterEdict, G_BOT_UNREGISTEREDICT), - GEN_IMPORT_3(Bot_MoveToPoint, G_BOT_MOVETOPOINT, GoalReturnCode, const edict_t*, gvec3_cref_t, const float), - GEN_IMPORT(Bot_FollowActor, G_BOT_FOLLOWACTOR), - GEN_IMPORT(GetPathToGoal, G_GETPATHTOGOAL), - GEN_IMPORT(Loc_Print, G_LOC_PRINT), - GEN_IMPORT_5(DrawLine, G_DRAW_LINE, void, gvec3_cref_t, gvec3_cref_t, const rgba_t&, float, bool), - GEN_IMPORT_5(DrawPoint, G_DRAW_POINT, void, gvec3_cref_t, float, const rgba_t&, float, bool), - GEN_IMPORT_5(DrawCircle, G_DRAW_CIRCLE, void, gvec3_cref_t, float, const rgba_t&, float, bool), - GEN_IMPORT_5(DrawBounds, G_DRAW_BOUNDS, void, gvec3_cref_t, gvec3_cref_t, const rgba_t&, float, bool), - GEN_IMPORT_5(DrawSphere, G_DRAW_SPHERE, void, gvec3_cref_t, float, const rgba_t&, float, bool), - GEN_IMPORT_6(DrawOrientedWorldText, G_DRAW_ORIENTEDWORLDTEXT, void, gvec3_cref_t, const char*, const rgba_t&, float, float, bool), - GEN_IMPORT_7(DrawStaticWorldText, G_DRAW_STATICWORLDTEXT, void, gvec3_cref_t, gvec3_cref_t, const char*, const rgba_t&, const float, const float, const bool), - GEN_IMPORT_6(DrawCylinder, G_DRAW_CYLINDER, void, gvec3_cref_t, float, float, const rgba_t&, float, bool), - GEN_IMPORT_7(DrawRay, G_DRAW_RAY, void, gvec3_cref_t, gvec3_cref_t, float, float, const rgba_t&, float, bool), - GEN_IMPORT_7(DrawArrow, G_DRAW_ARROW, void, gvec3_cref_t, gvec3_cref_t, float, const rgba_t&, const rgba_t&, float, bool), - GEN_IMPORT(ReportMatchDetails_Multicast, G_REPORTMATCHDETAILS_MULTICAST), - GEN_IMPORT(ServerFrame, G_SERVER_FRAME), - GEN_IMPORT(SendToClipBoard, G_SENDTOCLIPBOARD), - GEN_IMPORT(Info_ValueForKey, G_INFO_VALUEFORKEY), - GEN_IMPORT(Info_RemoveKey, G_INFO_REMOVEKEY), - GEN_IMPORT(Info_SetValueForKey, G_INFO_SETVALUEFORKEY), -}; - - -// these are "pre" hooks for storing some data for polyfills. -// we need these to be called BEFORE plugins' prehooks get called so they have to be done in the qmm_export table - -// track userinfo for our G_GET_USERINFO syscall -static std::map s_userinfo; -static bool Q2R_ClientConnect(edict_t* ent, char* userinfo, const char* social_id, bool isBot) { - // get client number (ent->s.number is not set until CLIENT_BEGIN, so calculate based on edict_t*) - if (orig_export && orig_export->edicts && orig_export->edict_size) { - intptr_t entnum = (intptr_t)(((intptr_t)ent - (intptr_t)orig_export->edicts) / orig_export->edict_size); - intptr_t clientnum = entnum - 1; - // if userinfo is null, remove entry in map. otherwise store in map - if (!userinfo) - s_userinfo.erase(clientnum); - else - s_userinfo[clientnum] = userinfo; - } - cgame.is_from_QMM = true; - return (bool)vmMain(GAME_CLIENT_CONNECT, ent, userinfo, social_id, isBot); -} - - -static void Q2R_ClientUserinfoChanged(edict_t* ent, const char* userinfo) { - // get client number (ent->s.number is not set until CLIENT_BEGIN, so calculate based on edict_t*) - if (orig_export && orig_export->edicts && orig_export->edict_size) { - intptr_t entnum = (intptr_t)(((intptr_t)ent - (intptr_t)orig_export->edicts) / orig_export->edict_size); - intptr_t clientnum = entnum - 1; - // if userinfo is null, remove entry in map. otherwise store in map - if (!userinfo) - s_userinfo.erase(clientnum); - else - s_userinfo[clientnum] = userinfo; - } - cgame.is_from_QMM = true; - vmMain(GAME_CLIENT_USERINFO_CHANGED, ent, userinfo); -} - - -// track entstrings for our G_GET_ENTITY_TOKEN syscall -static std::vector s_entity_tokens; -static size_t s_tokencount = 0; -static void Q2R_SpawnEntities(const char* mapname, const char* entstring, const char* spawnpoint) { - if (entstring) { - s_entity_tokens = util_parse_entstring(entstring); - s_tokencount = 0; - } - cgame.is_from_QMM = true; - vmMain(GAME_SPAWN_ENTITIES, mapname, entstring, spawnpoint); -} - - -// struct with lambdas that call QMM's vmMain function. this is given to the game engine -static game_export_t qmm_export = { - GAME_API_VERSION, // apiversion - GEN_EXPORT(PreInit, GAME_PREINIT), - GEN_EXPORT(Init, GAME_INIT), - GEN_EXPORT(Shutdown, GAME_SHUTDOWN), - Q2R_SpawnEntities, - GEN_EXPORT(WriteGameJson, GAME_WRITE_GAME), - GEN_EXPORT(ReadGameJson, GAME_READ_GAME), - GEN_EXPORT(WriteLevelJson, GAME_WRITE_LEVEL), - GEN_EXPORT(ReadLevelJson, GAME_READ_LEVEL), - GEN_EXPORT(CanSave, GAME_CAN_SAVE), - GEN_EXPORT(ClientChooseSlot, GAME_CLIENT_CHOOSESLOT), - Q2R_ClientConnect, - GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), - Q2R_ClientUserinfoChanged, - GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), - GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), - GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), - GEN_EXPORT(RunFrame, GAME_RUN_FRAME), - GEN_EXPORT(PrepFrame, GAME_PREP_FRAME), - GEN_EXPORT(ServerCommand, GAME_SERVER_COMMAND), - // the engine won't use these until after Init, so we can fill these in after each call into the mod's export functions ("vmMain") - nullptr, // edicts - 0, // edict_size - 0, // num_edicts - 0, // max_edicts - SERVER_FLAGS_NONE, // server_flags (0) - GEN_EXPORT(Pmove, GAME_PMOVE), - GEN_EXPORT(GetExtension, GAME_GET_EXTENSION), - GEN_EXPORT(Bot_SetWeapon, GAME_BOT_SETWEAPON), - GEN_EXPORT(Bot_TriggerEdict, GAME_BOT_TRIGGEREDICT), - GEN_EXPORT(Bot_UseItem, GAME_BOT_USEITEM), - GEN_EXPORT(Bot_GetItemID, GAME_BOT_GETITEMID), - GEN_EXPORT(Edict_ForceLookAtPoint, GAME_EDICT_FORCELOOKATPOINT), - GEN_EXPORT(Bot_PickedUpItem, GAME_BOT_PICKEDUPITEM), - GEN_EXPORT(Entity_IsVisibleToPlayer, GAME_ENTITY_ISVISIBLETOPLAYER), - GEN_EXPORT(GetShadowLightData, GAME_GETSHADOWLIGHTDATA), -}; - - -// update the export variables from orig_export -static void s_update_export() { - if (!orig_export) - return; - - bool changed = false; - - // if entity data changed, we need to send a G_LOCATE_GAME_DATA so plugins can hook it - if (qmm_export.edicts != orig_export->edicts - || qmm_export.edict_size != orig_export->edict_size - || qmm_export.num_edicts != orig_export->num_edicts - ) { - changed = true; - } - - qmm_export.edicts = orig_export->edicts; - qmm_export.edict_size = orig_export->edict_size; - qmm_export.num_edicts = orig_export->num_edicts; - qmm_export.max_edicts = orig_export->max_edicts; - qmm_export.server_flags = orig_export->server_flags; - - if (changed) { - // this will trigger this message to be fired to plugins, and then it will be handled - // by the empty "case G_LOCATE_GAME_DATA" in MOHAA_syscall - qmm_syscall(G_LOCATE_GAME_DATA, (intptr_t)qmm_export.edicts, qmm_export.num_edicts, qmm_export.edict_size, nullptr, 0); - } -} - - // wrapper syscall function that calls actual engine func from orig_import // this is how QMM and plugins will call into the engine -static intptr_t Q2R_syscall(intptr_t cmd, ...) { +intptr_t Q2R_GameSupport::syscall(intptr_t cmd, ...) { QMM_GET_SYSCALL_ARGS(); #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q2R_syscall({} {}) called\n", Q2R_EngMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q2R_GameSupport::syscall({} {}) called\n", EngMsgName(cmd), cmd); #endif // update export vars before calling into the engine - s_update_export(); + update_exports(); intptr_t ret = 0; @@ -500,13 +318,13 @@ static intptr_t Q2R_syscall(intptr_t cmd, ...) { char* buffer = (char*)args[1]; intptr_t bufferSize = args[2]; *buffer = '\0'; - if (s_userinfo.count(num)) - strncpyz(buffer, s_userinfo[num].c_str(), (size_t)bufferSize); + if (userinfos.count(num)) + strncpyz(buffer, userinfos[num].c_str(), (size_t)bufferSize); break; } case G_GET_ENTITY_TOKEN: { // bool trap_GetEntityToken(char *buffer, int bufferSize); - if (s_tokencount >= s_entity_tokens.size()) { + if (token_counter >= entity_tokens.size()) { ret = false; break; } @@ -514,7 +332,7 @@ static intptr_t Q2R_syscall(intptr_t cmd, ...) { char* buffer = (char*)args[0]; intptr_t bufferSize = args[1]; - strncpyz(buffer, s_entity_tokens[s_tokencount++].c_str(), (size_t)bufferSize); + strncpyz(buffer, entity_tokens[token_counter++].c_str(), (size_t)bufferSize); ret = true; break; } @@ -527,7 +345,7 @@ static intptr_t Q2R_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q2R_syscall({} {}) returning {}\n", Q2R_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q2R_GameSupport::syscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; @@ -536,11 +354,11 @@ static intptr_t Q2R_syscall(intptr_t cmd, ...) { // wrapper vmMain function that calls actual mod func from orig_export // this is how QMM and plugins will call into the mod -static intptr_t Q2R_vmMain(intptr_t cmd, ...) { +intptr_t Q2R_GameSupport::vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q2R_vmMain({} {}) called\n", Q2R_ModMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q2R_GameSupport::vmMain({} {}) called\n", ModMsgName(cmd), cmd); #endif if (!orig_export) @@ -592,18 +410,18 @@ static intptr_t Q2R_vmMain(intptr_t cmd, ...) { }; // update export vars after returning from the mod - s_update_export(); + update_exports(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q2R_vmMain({} {}) returning {}\n", Q2R_ModMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q2R_GameSupport::vmMain({} {}) returning {}\n", ModMsgName(cmd), cmd, ret); #endif return ret; } -static void* Q2R_Entry(void* import, void*, APIType) { - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q2R_Entry({}) called\n", import); +void* Q2R_GameSupport::Entry(void* import, void*, APIType) { + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q2R_GameSupport::Entry({}) called\n", import); // original import struct from engine // the struct given by the engine goes out of scope after this returns so we have to copy the whole thing @@ -615,13 +433,7 @@ static void* Q2R_Entry(void* import, void*, APIType) { qmm_import.frame_time_s = orig_import.frame_time_s; qmm_import.frame_time_ms = orig_import.frame_time_ms; - // pointer to wrapper vmMain function that calls actual mod func from orig_export - g_gameinfo.pfnvmMain = Q2R_vmMain; - - // pointer to wrapper syscall function that calls actual engine func from orig_import - g_gameinfo.pfnsyscall = Q2R_syscall; - - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q2R_Entry({}) returning {}\n", import, fmt::ptr(&qmm_export)); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("Q2R_GameSupport::Entry({}) returning {}\n", import, fmt::ptr(&qmm_export)); // struct full of export lambdas to QMM's vmMain // this gets returned to the game engine, but we haven't loaded the mod yet. @@ -630,7 +442,7 @@ static void* Q2R_Entry(void* import, void*, APIType) { } -static bool Q2R_ModLoad(void* entry, APIType modapi) { +bool Q2R_GameSupport::ModLoad(void* entry, APIType modapi) { if (modapi != QMM_API_GETGAMEAPI) return false; @@ -641,12 +453,12 @@ static bool Q2R_ModLoad(void* entry, APIType modapi) { } -static void Q2R_ModUnload() { +void Q2R_GameSupport::ModUnload() { orig_export = nullptr; } -static const char* Q2R_EngMsgName(intptr_t cmd) { +const char* Q2R_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_BROADCAST_PRINT); GEN_CASE(G_COM_PRINT); @@ -741,7 +553,7 @@ static const char* Q2R_EngMsgName(intptr_t cmd) { } -static const char* Q2R_ModMsgName(intptr_t cmd) { +const char* Q2R_GameSupport::ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAMEV_APIVERSION); GEN_CASE(GAME_PREINIT); @@ -784,4 +596,219 @@ static const char* Q2R_ModMsgName(intptr_t cmd) { } } + +// update the export variables from orig_export +void Q2R_GameSupport::update_exports() { + if (!orig_export) + return; + + bool changed = false; + + // if entity data changed, we need to send a G_LOCATE_GAME_DATA so plugins can hook it + if (qmm_export.edicts != orig_export->edicts + || qmm_export.edict_size != orig_export->edict_size + || qmm_export.num_edicts != orig_export->num_edicts + ) { + changed = true; + } + + qmm_export.edicts = orig_export->edicts; + qmm_export.edict_size = orig_export->edict_size; + qmm_export.num_edicts = orig_export->num_edicts; + qmm_export.max_edicts = orig_export->max_edicts; + qmm_export.server_flags = orig_export->server_flags; + + if (changed) { + // this will trigger this message to be fired to plugins, and then it will be handled + // by the empty "case G_LOCATE_GAME_DATA" in MOHAA_syscall + qmm_syscall(G_LOCATE_GAME_DATA, (intptr_t)qmm_export.edicts, qmm_export.num_edicts, qmm_export.edict_size, nullptr, 0); + } +} + + +game_import_t Q2R_GameSupport::orig_import; + + +game_export_t* Q2R_GameSupport::orig_export = nullptr; + + +/* + The sound() import gets messed up because the volume float arg doesn't get passed properly. Server-generated + sound (i.e. anything from the world like item pickup or another player attacking) would be silent. After + debugging, the volume argument would appear to come through as ~1e-19 or something exceptionally small. + Assigning qmm_import.sound=orig_import.sound would resolve the issue but obviously not allow for plugin hooking. + turns out the problem is the float arguments and the GEN_IMPORT macro using intptr_t. + + From: https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention#parameter-passing + "Any floating-point and double-precision arguments in the first four parameters are passed in XMM0 - XMM3, + depending on position. Floating-point values are only placed in the integer registers RCX, RDX, R8, and R9 + when there are varargs arguments." + + Since the Q2R engine is 64-bit and does not use varargs, floats in the first 4 args don't get put into the + integer registers. Everything after 4 arguments are passed on the stack in 8-byte-aligned slots. So, we can + pull the correct arguments with matching prototypes, then pass them to the varargs syscall where they get + handled properly. +*/ +// struct with lambdas that call QMM's syscall function. this is given to the mod +game_import_t Q2R_GameSupport::qmm_import = { + 0, // tick_rate + 0, // frame_time_s + 0, // frame_time_ms + GEN_IMPORT(Broadcast_Print, G_BROADCAST_PRINT), + GEN_IMPORT(Com_Print, G_COM_PRINT), + GEN_IMPORT(Client_Print, G_CLIENT_PRINT), + GEN_IMPORT(Center_Print, G_CENTERPRINT), + GEN_IMPORT_6(sound, G_SOUND, void, edict_t*, soundchan_t, int, float, float, float), + GEN_IMPORT_7(positioned_sound, G_POSITIONED_SOUND, void, gvec3_cref_t, edict_t*, soundchan_t, int, float, float, float), + GEN_IMPORT_9(local_sound, G_LOCAL_SOUND, void, edict_t*, gvec3_cptr_t, edict_t*, soundchan_t, int, float, float, float, uint32_t), + GEN_IMPORT(configstring, G_CONFIGSTRING), + GEN_IMPORT(get_configstring, G_GET_CONFIGSTRING), + GEN_IMPORT(Com_Error, G_COM_ERROR), + GEN_IMPORT(modelindex, G_MODELINDEX), + GEN_IMPORT(soundindex, G_SOUNDINDEX), + GEN_IMPORT(imageindex, G_IMAGEINDEX), + GEN_IMPORT(setmodel, G_SETMODEL), + GEN_IMPORT(trace, G_TRACE), + GEN_IMPORT(clip, G_CLIP), + GEN_IMPORT(pointcontents, G_POINT_CONTENTS), + GEN_IMPORT(inPVS, G_IN_PVS), + GEN_IMPORT(inPHS, G_IN_PHS), + GEN_IMPORT(SetAreaPortalState, G_SETAREAPORTALSTATE), + GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), + GEN_IMPORT(linkentity, G_LINKENTITY), + GEN_IMPORT(unlinkentity, G_UNLINKENTITY), + GEN_IMPORT(BoxEdicts, G_BOXEDICTS), + GEN_IMPORT(multicast, G_MULTICAST), + GEN_IMPORT(unicast, G_UNICAST), + GEN_IMPORT(WriteChar, G_MSG_WRITECHAR), + GEN_IMPORT(WriteByte, G_MSG_WRITEBYTE), + GEN_IMPORT(WriteShort, G_MSG_WRITESHORT), + GEN_IMPORT(WriteLong, G_MSG_WRITELONG), + GEN_IMPORT_1(WriteFloat, G_MSG_WRITEFLOAT, void, float), + GEN_IMPORT(WriteString, G_MSG_WRITESTRING), + GEN_IMPORT(WritePosition, G_MSG_WRITEPOSITION), + GEN_IMPORT(WriteDir, G_MSG_WRITEDIR), + GEN_IMPORT_1(WriteAngle, G_MSG_WRITEANGLE, void, float), + GEN_IMPORT(WriteEntity, G_MSG_WRITEENTITY), + GEN_IMPORT(TagMalloc, G_TAGMALLOC), + GEN_IMPORT(TagFree, G_TAGFREE), + GEN_IMPORT(FreeTags, G_FREETAGS), + GEN_IMPORT(cvar, G_CVAR), + GEN_IMPORT(cvar_set, G_CVAR_SET), + GEN_IMPORT(cvar_forceset, G_CVAR_FORCESET), + GEN_IMPORT(argc, G_ARGC), + GEN_IMPORT(argv, G_ARGV), + GEN_IMPORT(args, G_ARGS), + GEN_IMPORT(AddCommandString, G_ADDCOMMANDSTRING), + GEN_IMPORT_2(DebugGraph, G_DEBUGGRAPH, void, float, int), + GEN_IMPORT(GetExtension, G_GET_EXTENSION), + GEN_IMPORT(Bot_RegisterEdict, G_BOT_REGISTEREDICT), + GEN_IMPORT(Bot_UnRegisterEdict, G_BOT_UNREGISTEREDICT), + GEN_IMPORT_3(Bot_MoveToPoint, G_BOT_MOVETOPOINT, GoalReturnCode, const edict_t*, gvec3_cref_t, const float), + GEN_IMPORT(Bot_FollowActor, G_BOT_FOLLOWACTOR), + GEN_IMPORT(GetPathToGoal, G_GETPATHTOGOAL), + GEN_IMPORT(Loc_Print, G_LOC_PRINT), + GEN_IMPORT_5(DrawLine, G_DRAW_LINE, void, gvec3_cref_t, gvec3_cref_t, const rgba_t&, float, bool), + GEN_IMPORT_5(DrawPoint, G_DRAW_POINT, void, gvec3_cref_t, float, const rgba_t&, float, bool), + GEN_IMPORT_5(DrawCircle, G_DRAW_CIRCLE, void, gvec3_cref_t, float, const rgba_t&, float, bool), + GEN_IMPORT_5(DrawBounds, G_DRAW_BOUNDS, void, gvec3_cref_t, gvec3_cref_t, const rgba_t&, float, bool), + GEN_IMPORT_5(DrawSphere, G_DRAW_SPHERE, void, gvec3_cref_t, float, const rgba_t&, float, bool), + GEN_IMPORT_6(DrawOrientedWorldText, G_DRAW_ORIENTEDWORLDTEXT, void, gvec3_cref_t, const char*, const rgba_t&, float, float, bool), + GEN_IMPORT_7(DrawStaticWorldText, G_DRAW_STATICWORLDTEXT, void, gvec3_cref_t, gvec3_cref_t, const char*, const rgba_t&, const float, const float, const bool), + GEN_IMPORT_6(DrawCylinder, G_DRAW_CYLINDER, void, gvec3_cref_t, float, float, const rgba_t&, float, bool), + GEN_IMPORT_7(DrawRay, G_DRAW_RAY, void, gvec3_cref_t, gvec3_cref_t, float, float, const rgba_t&, float, bool), + GEN_IMPORT_7(DrawArrow, G_DRAW_ARROW, void, gvec3_cref_t, gvec3_cref_t, float, const rgba_t&, const rgba_t&, float, bool), + GEN_IMPORT(ReportMatchDetails_Multicast, G_REPORTMATCHDETAILS_MULTICAST), + GEN_IMPORT(ServerFrame, G_SERVER_FRAME), + GEN_IMPORT(SendToClipBoard, G_SENDTOCLIPBOARD), + GEN_IMPORT(Info_ValueForKey, G_INFO_VALUEFORKEY), + GEN_IMPORT(Info_RemoveKey, G_INFO_REMOVEKEY), + GEN_IMPORT(Info_SetValueForKey, G_INFO_SETVALUEFORKEY), +}; + + +std::map Q2R_GameSupport::userinfos; +bool Q2R_GameSupport::ClientConnect(edict_t* ent, char* userinfo, const char* social_id, bool isBot) { + // get client number (ent->s.number is not set until CLIENT_BEGIN, so calculate based on edict_t*) + if (orig_export && orig_export->edicts && orig_export->edict_size) { + intptr_t entnum = (intptr_t)(((intptr_t)ent - (intptr_t)orig_export->edicts) / orig_export->edict_size); + intptr_t clientnum = entnum - 1; + // if userinfo is null, remove entry in map. otherwise store in map + if (!userinfo) + userinfos.erase(clientnum); + else + userinfos[clientnum] = userinfo; + } + cgame.is_from_QMM = true; + return (bool)::vmMain(GAME_CLIENT_CONNECT, ent, userinfo, social_id, isBot); +} + + +void Q2R_GameSupport::ClientUserinfoChanged(edict_t* ent, const char* userinfo) { + // get client number (ent->s.number is not set until CLIENT_BEGIN, so calculate based on edict_t*) + if (orig_export && orig_export->edicts && orig_export->edict_size) { + intptr_t entnum = (intptr_t)(((intptr_t)ent - (intptr_t)orig_export->edicts) / orig_export->edict_size); + intptr_t clientnum = entnum - 1; + // if userinfo is null, remove entry in map. otherwise store in map + if (!userinfo) + userinfos.erase(clientnum); + else + userinfos[clientnum] = userinfo; + } + cgame.is_from_QMM = true; + ::vmMain(GAME_CLIENT_USERINFO_CHANGED, ent, userinfo); +} + + +std::vector Q2R_GameSupport::entity_tokens; +size_t Q2R_GameSupport::token_counter = 0; +void Q2R_GameSupport::SpawnEntities(const char* mapname, const char* entstring, const char* spawnpoint) { + if (entstring) { + entity_tokens = util_parse_entstring(entstring); + token_counter = 0; + } + cgame.is_from_QMM = true; + ::vmMain(GAME_SPAWN_ENTITIES, mapname, entstring, spawnpoint); +} + + +// struct with lambdas that call QMM's vmMain function. this is given to the game engine +game_export_t Q2R_GameSupport::qmm_export = { + GAME_API_VERSION, // apiversion + GEN_EXPORT(PreInit, GAME_PREINIT), + GEN_EXPORT(Init, GAME_INIT), + GEN_EXPORT(Shutdown, GAME_SHUTDOWN), + Q2R_GameSupport::SpawnEntities, + GEN_EXPORT(WriteGameJson, GAME_WRITE_GAME), + GEN_EXPORT(ReadGameJson, GAME_READ_GAME), + GEN_EXPORT(WriteLevelJson, GAME_WRITE_LEVEL), + GEN_EXPORT(ReadLevelJson, GAME_READ_LEVEL), + GEN_EXPORT(CanSave, GAME_CAN_SAVE), + GEN_EXPORT(ClientChooseSlot, GAME_CLIENT_CHOOSESLOT), + Q2R_GameSupport::ClientConnect, + GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), + Q2R_GameSupport::ClientUserinfoChanged, + GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), + GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), + GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), + GEN_EXPORT(RunFrame, GAME_RUN_FRAME), + GEN_EXPORT(PrepFrame, GAME_PREP_FRAME), + GEN_EXPORT(ServerCommand, GAME_SERVER_COMMAND), + // the engine won't use these until after Init, so we can fill these in after each call into the mod's export functions ("vmMain") + nullptr, // edicts + 0, // edict_size + 0, // num_edicts + 0, // max_edicts + SERVER_FLAGS_NONE, // server_flags (0) + GEN_EXPORT(Pmove, GAME_PMOVE), + GEN_EXPORT(GetExtension, GAME_GET_EXTENSION), + GEN_EXPORT(Bot_SetWeapon, GAME_BOT_SETWEAPON), + GEN_EXPORT(Bot_TriggerEdict, GAME_BOT_TRIGGEREDICT), + GEN_EXPORT(Bot_UseItem, GAME_BOT_USEITEM), + GEN_EXPORT(Bot_GetItemID, GAME_BOT_GETITEMID), + GEN_EXPORT(Edict_ForceLookAtPoint, GAME_EDICT_FORCELOOKATPOINT), + GEN_EXPORT(Bot_PickedUpItem, GAME_BOT_PICKEDUPITEM), + GEN_EXPORT(Entity_IsVisibleToPlayer, GAME_ENTITY_ISVISIBLETOPLAYER), + GEN_EXPORT(GetShadowLightData, GAME_GETSHADOWLIGHTDATA), +}; #endif // QMM_OS_WINDOWS && QMM_ARCH_64 diff --git a/src/game_q3a.cpp b/src/game_q3a.cpp index 40be1f1..0f78378 100644 --- a/src/game_q3a.cpp +++ b/src/game_q3a.cpp @@ -43,7 +43,10 @@ struct Q3A_GameSupport : public GameSupport { virtual int QVMSyscall(uint8_t* membase, int cmd, int* args); private: + // a copy of the original syscall from the engine eng_syscall orig_syscall = nullptr; + + // a copy of the vmMain function from the mod mod_vmMain orig_vmMain = nullptr; const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); diff --git a/src/game_quake2.cpp b/src/game_quake2.cpp index f2a72dd..4e25199 100644 --- a/src/game_quake2.cpp +++ b/src/game_quake2.cpp @@ -25,212 +25,85 @@ Created By: #include "main.h" #include "util.h" -GEN_GAME_QMM_MSGS(QUAKE2); +struct QUAKE2_GameSupport : public GameSupport { + virtual const char* EngMsgName(intptr_t msg); + virtual const char* ModMsgName(intptr_t msg); + virtual bool AutoDetect(APIType engine_api); + virtual void* Entry(void* syscall, void*, APIType engine_api); + virtual bool ModLoad(void* entry, APIType mod_api); + virtual void ModUnload(); -GEN_GAME_FUNCS(QUAKE2); + virtual intptr_t syscall(intptr_t, ...); + virtual intptr_t vmMain(intptr_t, ...); + virtual const char* DefaultDLLName() { return "game" MOD_DLL; } + virtual const char* DefaultModDir() { return "baseq2"; } + virtual const char* GameName() { return "Quake 2"; } + virtual const char* GameCode() { return "QUAKE2"; } -// auto-detection logic for QUAKE2 -static bool QUAKE2_AutoDetect(api_supportedgame* game, APIType engineapi) { - if (engineapi != QMM_API_GETGAMEAPI) - return false; - - if (!str_striequal(g_gameinfo.qmm_file, game->dllname)) - return false; +private: + static void update_exports(); - if (!str_stristr(g_gameinfo.exe_file, "quake2") && !str_stristr(g_gameinfo.exe_file, "q2ded")) - return false; - - return true; -} + // track configstrings for our G_GET_CONFIGSTRING syscall + static std::map configstrings; + static void configstring(int num, char* configstring); + // track userinfo for our G_GET_USERINFO syscall + static std::map userinfos; + static qboolean ClientConnect(edict_t* ent, char* userinfo); + static void ClientUserinfoChanged(edict_t* ent, char* userinfo); -// a copy of the original import struct that comes from the game engine -static game_import_t orig_import; + // track entstrings for our G_GET_ENTITY_TOKEN syscall + static std::vector entity_tokens; + static size_t token_counter; + static void SpawnEntities(char* mapname, char* entstring, char* spawnpoint); -// a copy of the original export struct pointer that comes from the mod -static game_export_t* orig_export = nullptr; + // a copy of the original import struct that comes from the game engine + static game_import_t orig_import; -// these are "pre" hooks for storing some data for polyfills. -// we need these to be called BEFORE plugins' prehooks get called so they have to be done in the qmm_import table + // a copy of the original export struct pointer that comes from the mod + static game_export_t* orig_export; -// track configstrings for our G_GET_CONFIGSTRING syscall -static std::map s_configstrings; -static void QUAKE2_configstring(int num, char* configstring) { - // if configstring is null, remove entry in map. otherwise store in map - if (!configstring) - s_configstrings.erase(num); - else - s_configstrings[num] = configstring; - qmm_syscall(G_CONFIGSTRING, num, configstring); -} + // struct with lambdas that call QMM's syscall function. this is given to the mod + static game_import_t qmm_import; + // struct with lambdas that call QMM's vmMain function. this is given to the game engine + static game_export_t qmm_export; -// struct with lambdas that call QMM's syscall function. this is given to the mod -static game_import_t qmm_import = { - GEN_IMPORT(bprintf, G_BPRINTF), - GEN_IMPORT(dprintf, G_DPRINTF), - GEN_IMPORT(cprintf, G_CPRINTF), - GEN_IMPORT(centerprintf, G_CENTERPRINTF), - GEN_IMPORT_6(sound, G_SOUND, void, edict_t*, int, int, float, float, float), - GEN_IMPORT_7(positioned_sound, G_POSITIONED_SOUND, void, vec3_t, edict_t*, int, int, float, float, float), - QUAKE2_configstring, - GEN_IMPORT(error, G_ERROR), - GEN_IMPORT(modelindex, G_MODELINDEX), - GEN_IMPORT(soundindex, G_SOUNDINDEX), - GEN_IMPORT(imageindex, G_IMAGEINDEX), - GEN_IMPORT(setmodel, G_SETMODEL), - GEN_IMPORT(trace, G_TRACE), - GEN_IMPORT(pointcontents, G_POINT_CONTENTS), - GEN_IMPORT(inPVS, G_IN_PVS), - GEN_IMPORT(inPHS, G_IN_PHS), - GEN_IMPORT(SetAreaPortalState, G_SETAREAPORTALSTATE), - GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), - GEN_IMPORT(linkentity, G_LINKENTITY), - GEN_IMPORT(unlinkentity, G_UNLINKENTITY), - GEN_IMPORT(BoxEdicts, G_BOXEDICTS), - GEN_IMPORT(Pmove, G_PMOVE), - GEN_IMPORT(multicast, G_MULTICAST), - GEN_IMPORT(unicast, G_UNICAST), - GEN_IMPORT(WriteChar, G_MSG_WRITECHAR), - GEN_IMPORT(WriteByte, G_MSG_WRITEBYTE), - GEN_IMPORT(WriteShort, G_MSG_WRITESHORT), - GEN_IMPORT(WriteLong, G_MSG_WRITELONG), - GEN_IMPORT_1(WriteFloat, G_MSG_WRITEFLOAT, void, float), - GEN_IMPORT(WriteString, G_MSG_WRITESTRING), - GEN_IMPORT(WritePosition, G_MSG_WRITEPOSITION), - GEN_IMPORT(WriteDir, G_MSG_WRITEDIR), - GEN_IMPORT_1(WriteAngle, G_MSG_WRITEANGLE, void, float), - GEN_IMPORT(TagMalloc, G_TAGMALLOC), - GEN_IMPORT(TagFree, G_TAGFREE), - GEN_IMPORT(FreeTags, G_FREETAGS), - GEN_IMPORT(cvar, G_CVAR), - GEN_IMPORT(cvar_set, G_CVAR_SET), - GEN_IMPORT(cvar_forceset, G_CVAR_FORCESET), - GEN_IMPORT(argc, G_ARGC), - GEN_IMPORT(argv, G_ARGV), - GEN_IMPORT(args, G_ARGS), - GEN_IMPORT(AddCommandString, G_ADDCOMMANDSTRING), - GEN_IMPORT_2(DebugGraph, G_DEBUGGRAPH, void, float, int), + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = GEN_GAME_QMM_MOD_MSGS(); }; +GEN_GAME_OBJ(QUAKE2); -// these are "pre" hooks for storing some data for polyfills. -// we need these to be called BEFORE plugins' prehooks get called so they have to be done in the qmm_export table -// track userinfo for our G_GET_USERINFO syscall -static std::map s_userinfo; -static qboolean QUAKE2_ClientConnect(edict_t* ent, char* userinfo) { - // get client number (ent->s.number is not set until CLIENT_BEGIN, so calculate based on edict_t*) - if (orig_export && orig_export->edicts && orig_export->edict_size) { - intptr_t entnum = ((intptr_t)ent - (intptr_t)orig_export->edicts) / orig_export->edict_size; - intptr_t clientnum = entnum - 1; - // if userinfo is null, remove entry in map. otherwise store in map - if (!userinfo) - s_userinfo.erase(clientnum); - else - s_userinfo[clientnum] = userinfo; - } - cgame.is_from_QMM = true; - return vmMain(GAME_CLIENT_CONNECT, ent, userinfo); -} - - -static void QUAKE2_ClientUserinfoChanged(edict_t* ent, char* userinfo) { - // get client number (ent->s.number is not set until CLIENT_BEGIN, so calculate based on edict_t*) - if (orig_export && orig_export->edicts && orig_export->edict_size) { - intptr_t entnum = ((intptr_t)ent - (intptr_t)orig_export->edicts) / orig_export->edict_size; - intptr_t clientnum = entnum - 1; - // if userinfo is null, remove entry in map. otherwise store in map - if (!userinfo) - s_userinfo.erase(clientnum); - else - s_userinfo[clientnum] = userinfo; - } - cgame.is_from_QMM = true; - vmMain(GAME_CLIENT_USERINFO_CHANGED, ent, userinfo); -} - - -// track entstrings for our G_GET_ENTITY_TOKEN syscall -static std::vector s_entity_tokens; -static size_t s_tokencount = 0; -static void QUAKE2_SpawnEntities(char* mapname, char* entstring, char* spawnpoint) { - if (entstring) { - s_entity_tokens = util_parse_entstring(entstring); - s_tokencount = 0; - } - cgame.is_from_QMM = true; - vmMain(GAME_SPAWN_ENTITIES, mapname, entstring, spawnpoint); -} - - -// struct with lambdas that call QMM's vmMain function. this is given to the game engine -static game_export_t qmm_export = { - GAME_API_VERSION, // apiversion - GEN_EXPORT(Init, GAME_INIT), - GEN_EXPORT(Shutdown, GAME_SHUTDOWN), - QUAKE2_SpawnEntities, - GEN_EXPORT(WriteGame, GAME_WRITE_GAME), - GEN_EXPORT(ReadGame, GAME_READ_GAME), - GEN_EXPORT(WriteLevel, GAME_WRITE_LEVEL), - GEN_EXPORT(ReadLevel, GAME_READ_LEVEL), - QUAKE2_ClientConnect, - GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), - QUAKE2_ClientUserinfoChanged, - GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), - GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), - GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), - GEN_EXPORT(RunFrame, GAME_RUN_FRAME), - GEN_EXPORT(ServerCommand, GAME_SERVER_COMMAND), - // the engine won't use these until after Init, so we can fill these in after each call into the mod's export functions ("vmMain") - nullptr, // edicts - 0, // edict_size - 0, // num_edicts - 0, // max_edicts -}; - - -// update the export variables from orig_export -static void s_update_export() { - if (!orig_export) - return; - - bool changed = false; +// auto-detection logic for QUAKE2 +bool QUAKE2_GameSupport::AutoDetect(APIType engineapi) { + if (engineapi != QMM_API_GETGAMEAPI) + return false; - // if entity data changed, we need to send a G_LOCATE_GAME_DATA so plugins can hook it - if (qmm_export.edicts != orig_export->edicts - || qmm_export.edict_size != orig_export->edict_size - || qmm_export.num_edicts != orig_export->num_edicts - ) { - changed = true; - } + if (!str_striequal(g_gameinfo.qmm_file, DefaultDLLName())) + return false; - qmm_export.edicts = orig_export->edicts; - qmm_export.edict_size = orig_export->edict_size; - qmm_export.num_edicts = orig_export->num_edicts; - qmm_export.max_edicts = orig_export->max_edicts; + if (!str_stristr(g_gameinfo.exe_file, "quake2") && !str_stristr(g_gameinfo.exe_file, "q2ded")) + return false; - if (changed) { - // this will trigger this message to be fired to plugins, and then it will be handled - // by the empty "case G_LOCATE_GAME_DATA" in MOHAA_syscall - qmm_syscall(G_LOCATE_GAME_DATA, (intptr_t)qmm_export.edicts, qmm_export.num_edicts, qmm_export.edict_size, nullptr, 0); - } + return true; } // wrapper syscall function that calls actual engine func from orig_import // this is how QMM and plugins will call into the engine -static intptr_t QUAKE2_syscall(intptr_t cmd, ...) { +intptr_t QUAKE2_GameSupport::syscall(intptr_t cmd, ...) { QMM_GET_SYSCALL_ARGS(); #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("QUAKE2_syscall({} {}) called\n", QUAKE2_EngMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("QUAKE2_GameSupport::syscall({} {}) called\n", EngMsgName(cmd), cmd); #endif // update export vars before calling into the engine - s_update_export(); + update_exports(); intptr_t ret = 0; @@ -416,13 +289,13 @@ static intptr_t QUAKE2_syscall(intptr_t cmd, ...) { char* buffer = (char*)args[1]; intptr_t bufferSize = args[2]; *buffer = '\0'; - if (s_userinfo.count(num)) - strncpyz(buffer, s_userinfo[num].c_str(), (size_t)bufferSize); + if (userinfos.count(num)) + strncpyz(buffer, userinfos[num].c_str(), (size_t)bufferSize); break; } case G_GET_ENTITY_TOKEN: { // bool trap_GetEntityToken(char *buffer, int bufferSize); - if (s_tokencount >= s_entity_tokens.size()) { + if (token_counter >= entity_tokens.size()) { ret = false; break; } @@ -430,7 +303,7 @@ static intptr_t QUAKE2_syscall(intptr_t cmd, ...) { char* buffer = (char*)args[0]; intptr_t bufferSize = args[1]; - strncpyz(buffer, s_entity_tokens[s_tokencount++].c_str(), (size_t)bufferSize); + strncpyz(buffer, entity_tokens[token_counter++].c_str(), (size_t)bufferSize); ret = true; break; } @@ -438,8 +311,8 @@ static intptr_t QUAKE2_syscall(intptr_t cmd, ...) { // const char* (*get_configstring)(int num); intptr_t num = args[0]; - if (s_configstrings.count(num)) - ret = (intptr_t)s_configstrings[num].c_str(); + if (configstrings.count(num)) + ret = (intptr_t)configstrings[num].c_str(); break; } @@ -455,7 +328,7 @@ static intptr_t QUAKE2_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("QUAKE2_syscall({} {}) returning {}\n", QUAKE2_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("QUAKE2_GameSupport::syscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; @@ -464,11 +337,11 @@ static intptr_t QUAKE2_syscall(intptr_t cmd, ...) { // wrapper vmMain function that calls actual mod func from orig_export // this is how QMM and plugins will call into the mod -static intptr_t QUAKE2_vmMain(intptr_t cmd, ...) { +intptr_t QUAKE2_GameSupport::vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("QUAKE2_vmMain({} {}) called\n", QUAKE2_ModMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("QUAKE2_GameSupport::vmMain({} {}) called\n", ModMsgName(cmd), cmd); #endif if (!orig_export) @@ -506,18 +379,18 @@ static intptr_t QUAKE2_vmMain(intptr_t cmd, ...) { }; // update export vars after returning from the mod - s_update_export(); + update_exports(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("QUAKE2_vmMain({} {}) returning {}\n", QUAKE2_ModMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("QUAKE2_GameSupport::vmMain({} {}) returning {}\n", ModMsgName(cmd), cmd, ret); #endif return ret; } -static void* QUAKE2_Entry(void* import, void*, APIType) { - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("QUAKE2_Entry({}) called\n", import); +void* QUAKE2_GameSupport::Entry(void* import, void*, APIType) { + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("QUAKE2_GameSupport::Entry({}) called\n", import); // original import struct from engine // the struct given by the engine goes out of scope after this returns so we have to copy the whole thing @@ -527,13 +400,7 @@ static void* QUAKE2_Entry(void* import, void*, APIType) { // fill in variables of our hooked import struct to pass to the mod // qmm_import.x = orig_import.x; - // pointer to wrapper vmMain function that calls actual mod func from orig_export - g_gameinfo.pfnvmMain = QUAKE2_vmMain; - - // pointer to wrapper syscall function that calls actual engine func from orig_import - g_gameinfo.pfnsyscall = QUAKE2_syscall; - - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("QUAKE2_Entry({}) returning {}\n", import, fmt::ptr(&qmm_export)); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("QUAKE2_GameSupport::Entry({}) returning {}\n", import, fmt::ptr(&qmm_export)); // struct full of export lambdas to QMM's vmMain // this gets returned to the game engine, but we haven't loaded the mod yet. @@ -542,7 +409,7 @@ static void* QUAKE2_Entry(void* import, void*, APIType) { } -static bool QUAKE2_ModLoad(void* entry, APIType modapi) { +bool QUAKE2_GameSupport::ModLoad(void* entry, APIType modapi) { if (modapi != QMM_API_GETGAMEAPI) return false; @@ -553,12 +420,12 @@ static bool QUAKE2_ModLoad(void* entry, APIType modapi) { } -static void QUAKE2_ModUnload() { +void QUAKE2_GameSupport::ModUnload() { orig_export = nullptr; } -static const char* QUAKE2_EngMsgName(intptr_t cmd) { +const char* QUAKE2_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_BPRINTF); GEN_CASE(G_DPRINTF); @@ -629,7 +496,7 @@ static const char* QUAKE2_EngMsgName(intptr_t cmd) { } -static const char* QUAKE2_ModMsgName(intptr_t cmd) { +const char* QUAKE2_GameSupport::ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAMEV_APIVERSION); GEN_CASE(GAME_INIT); @@ -656,3 +523,166 @@ static const char* QUAKE2_ModMsgName(intptr_t cmd) { return "unknown"; } } + + +void QUAKE2_GameSupport::update_exports() { + if (!orig_export) + return; + + bool changed = false; + + // if entity data changed, we need to send a G_LOCATE_GAME_DATA so plugins can hook it + if (qmm_export.edicts != orig_export->edicts + || qmm_export.edict_size != orig_export->edict_size + || qmm_export.num_edicts != orig_export->num_edicts + ) { + changed = true; + } + + qmm_export.edicts = orig_export->edicts; + qmm_export.edict_size = orig_export->edict_size; + qmm_export.num_edicts = orig_export->num_edicts; + qmm_export.max_edicts = orig_export->max_edicts; + + if (changed) { + // this will trigger this message to be fired to plugins, and then it will be handled + // by the empty "case G_LOCATE_GAME_DATA" in syscall + qmm_syscall(G_LOCATE_GAME_DATA, (intptr_t)qmm_export.edicts, qmm_export.num_edicts, qmm_export.edict_size, nullptr, 0); + } +} + + +game_import_t QUAKE2_GameSupport::orig_import = {}; + +// a copy of the original export struct pointer that comes from the mod +game_export_t* QUAKE2_GameSupport::orig_export = nullptr; + + +std::map QUAKE2_GameSupport::configstrings; +void QUAKE2_GameSupport::configstring(int num, char* configstring) { + // if configstring is null, remove entry in map. otherwise store in map + if (!configstring) + configstrings.erase(num); + else + configstrings[num] = configstring; + qmm_syscall(G_CONFIGSTRING, num, configstring); +} + + +game_import_t QUAKE2_GameSupport::qmm_import = { + GEN_IMPORT(bprintf, G_BPRINTF), + GEN_IMPORT(dprintf, G_DPRINTF), + GEN_IMPORT(cprintf, G_CPRINTF), + GEN_IMPORT(centerprintf, G_CENTERPRINTF), + GEN_IMPORT_6(sound, G_SOUND, void, edict_t*, int, int, float, float, float), + GEN_IMPORT_7(positioned_sound, G_POSITIONED_SOUND, void, vec3_t, edict_t*, int, int, float, float, float), + QUAKE2_GameSupport::configstring, + GEN_IMPORT(error, G_ERROR), + GEN_IMPORT(modelindex, G_MODELINDEX), + GEN_IMPORT(soundindex, G_SOUNDINDEX), + GEN_IMPORT(imageindex, G_IMAGEINDEX), + GEN_IMPORT(setmodel, G_SETMODEL), + GEN_IMPORT(trace, G_TRACE), + GEN_IMPORT(pointcontents, G_POINT_CONTENTS), + GEN_IMPORT(inPVS, G_IN_PVS), + GEN_IMPORT(inPHS, G_IN_PHS), + GEN_IMPORT(SetAreaPortalState, G_SETAREAPORTALSTATE), + GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), + GEN_IMPORT(linkentity, G_LINKENTITY), + GEN_IMPORT(unlinkentity, G_UNLINKENTITY), + GEN_IMPORT(BoxEdicts, G_BOXEDICTS), + GEN_IMPORT(Pmove, G_PMOVE), + GEN_IMPORT(multicast, G_MULTICAST), + GEN_IMPORT(unicast, G_UNICAST), + GEN_IMPORT(WriteChar, G_MSG_WRITECHAR), + GEN_IMPORT(WriteByte, G_MSG_WRITEBYTE), + GEN_IMPORT(WriteShort, G_MSG_WRITESHORT), + GEN_IMPORT(WriteLong, G_MSG_WRITELONG), + GEN_IMPORT_1(WriteFloat, G_MSG_WRITEFLOAT, void, float), + GEN_IMPORT(WriteString, G_MSG_WRITESTRING), + GEN_IMPORT(WritePosition, G_MSG_WRITEPOSITION), + GEN_IMPORT(WriteDir, G_MSG_WRITEDIR), + GEN_IMPORT_1(WriteAngle, G_MSG_WRITEANGLE, void, float), + GEN_IMPORT(TagMalloc, G_TAGMALLOC), + GEN_IMPORT(TagFree, G_TAGFREE), + GEN_IMPORT(FreeTags, G_FREETAGS), + GEN_IMPORT(cvar, G_CVAR), + GEN_IMPORT(cvar_set, G_CVAR_SET), + GEN_IMPORT(cvar_forceset, G_CVAR_FORCESET), + GEN_IMPORT(argc, G_ARGC), + GEN_IMPORT(argv, G_ARGV), + GEN_IMPORT(args, G_ARGS), + GEN_IMPORT(AddCommandString, G_ADDCOMMANDSTRING), + GEN_IMPORT_2(DebugGraph, G_DEBUGGRAPH, void, float, int), +}; + + +std::map QUAKE2_GameSupport::userinfos; +qboolean QUAKE2_GameSupport::ClientConnect(edict_t* ent, char* userinfo) { + // get client number (ent->s.number is not set until CLIENT_BEGIN, so calculate based on edict_t*) + if (orig_export && orig_export->edicts && orig_export->edict_size) { + intptr_t entnum = ((intptr_t)ent - (intptr_t)orig_export->edicts) / orig_export->edict_size; + intptr_t clientnum = entnum - 1; + // if userinfo is null, remove entry in map. otherwise store in map + if (!userinfo) + userinfos.erase(clientnum); + else + userinfos[clientnum] = userinfo; + } + cgame.is_from_QMM = true; + return ::vmMain(GAME_CLIENT_CONNECT, ent, userinfo); +} + + +void QUAKE2_GameSupport::ClientUserinfoChanged(edict_t* ent, char* userinfo) { + // get client number (ent->s.number is not set until CLIENT_BEGIN, so calculate based on edict_t*) + if (orig_export && orig_export->edicts && orig_export->edict_size) { + intptr_t entnum = ((intptr_t)ent - (intptr_t)orig_export->edicts) / orig_export->edict_size; + intptr_t clientnum = entnum - 1; + // if userinfo is null, remove entry in map. otherwise store in map + if (!userinfo) + userinfos.erase(clientnum); + else + userinfos[clientnum] = userinfo; + } + cgame.is_from_QMM = true; + ::vmMain(GAME_CLIENT_USERINFO_CHANGED, ent, userinfo); +} + + +// track entstrings for our G_GET_ENTITY_TOKEN syscall +std::vector QUAKE2_GameSupport::entity_tokens; +size_t QUAKE2_GameSupport::token_counter = 0; +void QUAKE2_GameSupport::SpawnEntities(char* mapname, char* entstring, char* spawnpoint) { + if (entstring) { + entity_tokens = util_parse_entstring(entstring); + token_counter = 0; + } + cgame.is_from_QMM = true; + ::vmMain(GAME_SPAWN_ENTITIES, mapname, entstring, spawnpoint); +} + + +game_export_t QUAKE2_GameSupport::qmm_export = { + GAME_API_VERSION, // apiversion + GEN_EXPORT(Init, GAME_INIT), + GEN_EXPORT(Shutdown, GAME_SHUTDOWN), + QUAKE2_GameSupport::SpawnEntities, + GEN_EXPORT(WriteGame, GAME_WRITE_GAME), + GEN_EXPORT(ReadGame, GAME_READ_GAME), + GEN_EXPORT(WriteLevel, GAME_WRITE_LEVEL), + GEN_EXPORT(ReadLevel, GAME_READ_LEVEL), + QUAKE2_GameSupport::ClientConnect, + GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), + QUAKE2_GameSupport::ClientUserinfoChanged, + GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), + GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), + GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), + GEN_EXPORT(RunFrame, GAME_RUN_FRAME), + GEN_EXPORT(ServerCommand, GAME_SERVER_COMMAND), + // the engine won't use these until after Init, so we can fill these in after each call into the mod's export functions ("vmMain") + nullptr, // edicts + 0, // edict_size + 0, // num_edicts + 0, // max_edicts +}; diff --git a/src/game_rtcwmp.cpp b/src/game_rtcwmp.cpp index 965d410..c747fe5 100644 --- a/src/game_rtcwmp.cpp +++ b/src/game_rtcwmp.cpp @@ -41,7 +41,10 @@ struct RTCWMP_GameSupport : public GameSupport { virtual int QVMSyscall(uint8_t* membase, int cmd, int* args); private: + // a copy of the original syscall from the engine eng_syscall orig_syscall = nullptr; + + // a copy of the vmMain function from the mod mod_vmMain orig_vmMain = nullptr; const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); diff --git a/src/game_rtcwsp.cpp b/src/game_rtcwsp.cpp index 88681d6..62c5114 100644 --- a/src/game_rtcwsp.cpp +++ b/src/game_rtcwsp.cpp @@ -39,7 +39,10 @@ struct RTCWSP_GameSupport : public GameSupport { virtual const char* GameCode() { return "RTCWSP"; } private: + // a copy of the original syscall from the engine eng_syscall orig_syscall = nullptr; + + // a copy of the vmMain function from the mod mod_vmMain orig_vmMain = nullptr; // was QMM loaded by the ioRTCW engine? diff --git a/src/game_sin.cpp b/src/game_sin.cpp index ca6f8ff..be5cf22 100644 --- a/src/game_sin.cpp +++ b/src/game_sin.cpp @@ -25,279 +25,85 @@ Created By: #include "main.h" #include "util.h" -GEN_GAME_QMM_MSGS(SIN); +struct SIN_GameSupport : public GameSupport { + virtual const char* EngMsgName(intptr_t msg); + virtual const char* ModMsgName(intptr_t msg); + virtual bool AutoDetect(APIType engine_api); + virtual void* Entry(void* syscall, void*, APIType engine_api); + virtual bool ModLoad(void* entry, APIType mod_api); + virtual void ModUnload(); -GEN_GAME_FUNCS(SIN); + virtual intptr_t syscall(intptr_t, ...); + virtual intptr_t vmMain(intptr_t, ...); + virtual const char* DefaultDLLName() { return "game" MOD_DLL; } + virtual const char* DefaultModDir() { return "base"; } + virtual const char* GameName() { return "SiN"; } + virtual const char* GameCode() { return "SIN"; } -// auto-detection logic for SIN -static bool SIN_AutoDetect(api_supportedgame* game, APIType engineapi) { - if (engineapi != QMM_API_GETGAMEAPI) - return false; +private: + static void update_exports(); - if (!str_striequal(g_gameinfo.qmm_file, game->dllname)) - return false; + // track configstrings for our G_GET_CONFIGSTRING syscall + static std::map configstrings; + static void configstring(int num, const char* configstring); - if (!str_stristr(g_gameinfo.exe_file, "sin")) - return false; + // track userinfo for our G_GET_USERINFO syscall + static std::map userinfos; + static qboolean ClientConnect(edict_t* ent, const char* userinfo); + static void ClientUserinfoChanged(edict_t* ent, const char* userinfo); - return true; -} - - -// a copy of the original import struct that comes from the game engine -static game_import_t orig_import; + // track entstrings for our G_GET_ENTITY_TOKEN syscall + static std::vector entity_tokens; + static size_t token_counter; + static void SpawnEntities(const char* mapname, const char* entstring, const char* spawnpoint); -// a copy of the original export struct pointer that comes from the mod -static game_export_t* orig_export = nullptr; + // a copy of the original import struct that comes from the game engine + static game_import_t orig_import; -// these are "pre" hooks for storing some data for polyfills. -// we need these to be called BEFORE plugins' prehooks get called so they have to be done in the qmm_import table + // a copy of the original export struct pointer that comes from the mod + static game_export_t* orig_export; -// track configstrings for our G_GET_CONFIGSTRING syscall -static std::map s_configstrings; -static void SIN_configstring(int num, const char* configstring) { - // if configstring is null, remove entry in map. otherwise store in map - if (!configstring) - s_configstrings.erase(num); - else - s_configstrings[num] = configstring; - qmm_syscall(G_CONFIGSTRING, num, configstring); -} + // struct with lambdas that call QMM's syscall function. this is given to the mod + static game_import_t qmm_import; + // struct with lambdas that call QMM's vmMain function. this is given to the game engine + static game_export_t qmm_export; -// struct with lambdas that call QMM's syscall function. this is given to the mod -static game_import_t qmm_import = { - GEN_IMPORT(bprintf, G_BPRINTF), - GEN_IMPORT(dprintf, G_DPRINTF), - GEN_IMPORT(printf, G_PRINTF), - GEN_IMPORT(cprintf, G_CPRINTF), - GEN_IMPORT(centerprintf, G_CENTERPRINTF), - GEN_IMPORT_9(sound, G_SOUND, void, edict_t*, int, int, float, float, float, float, float, int), - GEN_IMPORT_10(positioned_sound, G_POSITIONED_SOUND, void, vec3_t, edict_t*, int, int, float, float, float, float, float, int), - SIN_configstring, - GEN_IMPORT(error, G_ERROR), - GEN_IMPORT(modelindex, G_MODELINDEX), - GEN_IMPORT(soundindex, G_SOUNDINDEX), - GEN_IMPORT(imageindex, G_IMAGEINDEX), - GEN_IMPORT(itemindex, G_ITEMINDEX), - GEN_IMPORT(setmodel, G_SETMODEL), - GEN_IMPORT(trace, G_TRACE), - GEN_IMPORT(fulltrace, G_FULLTRACE), // todo: change types to actually match float, but also need to return an intptr_t instead of trace_t - GEN_IMPORT(pointcontents, G_POINT_CONTENTS), - GEN_IMPORT(inPVS, G_IN_PVS), - GEN_IMPORT(inPHS, G_IN_PHS), - GEN_IMPORT(SetAreaPortalState, G_SETAREAPORTALSTATE), - GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), - GEN_IMPORT(linkentity, G_LINKENTITY), - GEN_IMPORT(unlinkentity, G_UNLINKENTITY), - GEN_IMPORT(BoxEdicts, G_BOXEDICTS), - GEN_IMPORT(Pmove, G_PMOVE), - GEN_IMPORT(multicast, G_MULTICAST), - GEN_IMPORT(unicast, G_UNICAST), - GEN_IMPORT(WriteChar, G_MSG_WRITECHAR), - GEN_IMPORT(WriteByte, G_MSG_WRITEBYTE), - GEN_IMPORT(WriteShort, G_MSG_WRITESHORT), - GEN_IMPORT(WriteLong, G_MSG_WRITELONG), - GEN_IMPORT_1(WriteFloat, G_MSG_WRITEFLOAT, void, float), - GEN_IMPORT(WriteString, G_MSG_WRITESTRING), - GEN_IMPORT(WritePosition, G_MSG_WRITEPOSITION), - GEN_IMPORT(WriteDir, G_MSG_WRITEDIR), - GEN_IMPORT_1(WriteAngle, G_MSG_WRITEANGLE, void, float), - GEN_IMPORT(TagMalloc, G_TAGMALLOC), - GEN_IMPORT(TagFree, G_TAGFREE), - GEN_IMPORT(FreeTags, G_FREETAGS), - GEN_IMPORT(cvar, G_CVAR), - GEN_IMPORT(cvar_set, G_CVAR_SET), - GEN_IMPORT(cvar_forceset, G_CVAR_FORCESET), - GEN_IMPORT(argc, G_ARGC), - GEN_IMPORT(argv, G_ARGV), - GEN_IMPORT(args, G_ARGS), - GEN_IMPORT(AddCommandString, G_ADDCOMMANDSTRING), - GEN_IMPORT_2(DebugGraph, G_DEBUGGRAPH, void, float, int), - GEN_IMPORT(LoadFile, G_LOADFILE), - GEN_IMPORT(GameDir, G_GAMEDIR), - GEN_IMPORT(PlayerDir, G_PLAYERDIR), - GEN_IMPORT(CreatePath, G_CREATEPATH), - GEN_IMPORT(SoundLength, G_SOUNDLENGTH), - GEN_IMPORT(IsModel, G_ISMODEL), - GEN_IMPORT(NumAnims, G_NUMANIMS), - GEN_IMPORT(NumSkins, G_NUMSKINS), - GEN_IMPORT(NumGroups, G_NUMGROUPS), - GEN_IMPORT(InitCommands, G_INITCOMMANDS), - GEN_IMPORT_4(CalculateBounds, G_CALCULATEBOUNDS, void, int, float, vec3_t, vec3_t), - GEN_IMPORT(Anim_NameForNum, G_ANIM_NAMEFORNUM), - GEN_IMPORT(Anim_NumForName, G_ANIM_NUMFORNAME), - GEN_IMPORT(Anim_Random, G_ANIM_RANDOM), - GEN_IMPORT(Anim_NumFrames, G_ANIM_NUMFRAMES), - GEN_IMPORT(Anim_Time, G_ANIM_TIME), - GEN_IMPORT(Anim_Delta, G_ANIM_DELTA), - GEN_IMPORT(Frame_Commands, G_FRAME_COMMANDS), - GEN_IMPORT(Frame_Delta, G_FRAME_DELTA), - GEN_IMPORT(Frame_Time, G_FRAME_TIME), - GEN_IMPORT(Skin_NameForNum, G_SKIN_NAMEFORNUM), - GEN_IMPORT(Skin_NumForName, G_SKIN_NUMFORNAME), - GEN_IMPORT(Group_NameToNum, G_GROUP_NAMETONUM), - GEN_IMPORT(Group_NumToName, G_GROUP_NUMTONAME), - GEN_IMPORT(Group_DamageMultiplier, G_GROUP_DAMAGEMULTIPLIER), - GEN_IMPORT(Group_Flags, G_GROUP_FLAGS), - GEN_IMPORT(GetBoneInfo, G_GETBONEINFO), - GEN_IMPORT(GetBoneGroupName, G_GETBONEGROUPNAME), - GEN_IMPORT(GetBoneTransform, G_GETBONETRANSFORM), - GEN_IMPORT_4(Alias_Add, G_ALIAS_ADD, qboolean, int, const char*, const char*, float), - GEN_IMPORT(Alias_FindRandom, G_ALIAS_FINDRANDOM), - GEN_IMPORT(Alias_Dump, G_ALIAS_DUMP), - GEN_IMPORT(Alias_Clear, G_ALIAS_CLEAR), - GEN_IMPORT_3(GlobalAlias_Add, G_GLOBALALIAS_ADD, qboolean, const char*, const char*, float), - GEN_IMPORT(GlobalAlias_FindRandom, G_GLOBALALIAS_FINDRANDOM), - GEN_IMPORT(GlobalAlias_Dump, G_GLOBALALIAS_DUMP), - GEN_IMPORT(GlobalAlias_Clear, G_GLOBALALIAS_CLEAR), - GEN_IMPORT(CalcCRC , G_CALCCRC), - GEN_IMPORT(Surf_NumSurfaces, G_SURF_NUMSURFACES), - GEN_IMPORT(Surf_Surfaces, G_SURF_SURFACES), - GEN_IMPORT(IncrementStatusCount, G_INCREMENTSTATUSCOUNT), - nullptr, // DebugLines - nullptr, // numDebugLines + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = GEN_GAME_QMM_MOD_MSGS(); }; +GEN_GAME_OBJ(SIN); -// these are "pre" hooks for storing some data for polyfills. -// we need these to be called BEFORE plugins' prehooks get called so they have to be done in the qmm_export table -// track userinfo for our G_GET_USERINFO syscall -static std::map s_userinfo; -static qboolean SIN_ClientConnect(edict_t* ent, const char* userinfo) { - // get client number (ent->s.number is not set until CLIENT_BEGIN, so calculate based on edict_t*) - if (orig_export && orig_export->edicts && orig_export->edict_size) { - intptr_t entnum = ((intptr_t)ent - (intptr_t)orig_export->edicts) / orig_export->edict_size; - intptr_t clientnum = entnum - 1; - // if userinfo is null, remove entry in map. otherwise store in map - if (!userinfo) - s_userinfo.erase(clientnum); - else - s_userinfo[clientnum] = userinfo; - } - cgame.is_from_QMM = true; - return vmMain(GAME_CLIENT_CONNECT, ent, userinfo); -} - - -static void SIN_ClientUserinfoChanged(edict_t* ent, const char* userinfo) { - // get client number (ent->s.number is not set until CLIENT_BEGIN, so calculate based on edict_t*) - if (orig_export && orig_export->edicts && orig_export->edict_size) { - intptr_t entnum = ((intptr_t)ent - (intptr_t)orig_export->edicts) / orig_export->edict_size; - intptr_t clientnum = entnum - 1; - // if userinfo is null, remove entry in map. otherwise store in map - if (!userinfo) - s_userinfo.erase(clientnum); - else - s_userinfo[clientnum] = userinfo; - } - cgame.is_from_QMM = true; - vmMain(GAME_CLIENT_USERINFO_CHANGED, ent, userinfo); -} - - -// track entstrings for our G_GET_ENTITY_TOKEN syscall -static std::vector s_entity_tokens; -static size_t s_tokencount = 0; -static void SIN_SpawnEntities(const char* mapname, const char* entstring, const char* spawnpoint) { - if (entstring) { - s_entity_tokens = util_parse_entstring(entstring); - s_tokencount = 0; - } - cgame.is_from_QMM = true; - vmMain(GAME_SPAWN_ENTITIES, mapname, entstring, spawnpoint); -} - - -// struct with lambdas that call QMM's vmMain function. this is given to the game engine -static game_export_t qmm_export = { - GAME_API_VERSION, // apiversion - GEN_EXPORT(Init, GAME_INIT), - GEN_EXPORT(Shutdown, GAME_SHUTDOWN), - SIN_SpawnEntities, - GEN_EXPORT(WriteGame, GAME_WRITE_GAME), - GEN_EXPORT(ReadGame, GAME_READ_GAME), - GEN_EXPORT(WriteLevel, GAME_WRITE_LEVEL), - GEN_EXPORT(ReadLevel, GAME_READ_LEVEL), - SIN_ClientConnect, - GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), - SIN_ClientUserinfoChanged, - GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), - GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), - GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), - GEN_EXPORT(RunFrame, GAME_RUN_FRAME), - GEN_EXPORT(ServerCommand, GAME_SERVER_COMMAND), - GEN_EXPORT(CreateSurfaces, GAME_CREATESURFACES), - // the engine won't use these until after Init, so we can fill these in after each call into the mod's export functions ("vmMain") - nullptr, // edicts - 0, // edict_size - 0, // num_edicts - 0, // max_edicts - nullptr, // consoles - 0, // console_size - 0, // num_consoles - 0, // max_consoles - nullptr, // conbuffers - 0, // conbuffer_size - nullptr, // surfaces - 0, // surface_size - 0, // max_surfaces - 0, // num_surfaces -}; - - -// update the export variables from orig_export -static void s_update_export() { - if (!orig_export) - return; - - bool changed = false; +// auto-detection logic for SIN +bool SIN_GameSupport::AutoDetect(APIType engineapi) { + if (engineapi != QMM_API_GETGAMEAPI) + return false; - // if entity data changed, we need to send a G_LOCATE_GAME_DATA so plugins can hook it - if (qmm_export.edicts != orig_export->edicts - || qmm_export.edict_size != orig_export->edict_size - || qmm_export.num_edicts != orig_export->num_edicts - ) { - changed = true; - } + if (!str_striequal(g_gameinfo.qmm_file, DefaultDLLName())) + return false; - qmm_export.edicts = orig_export->edicts; - qmm_export.edict_size = orig_export->edict_size; - qmm_export.num_edicts = orig_export->num_edicts; - qmm_export.max_edicts = orig_export->max_edicts; - qmm_export.consoles = orig_export->consoles; - qmm_export.console_size = orig_export->console_size; - qmm_export.num_consoles = orig_export->num_consoles; - qmm_export.max_consoles = orig_export->max_consoles; - qmm_export.conbuffers = orig_export->conbuffers; - qmm_export.conbuffer_size = orig_export->conbuffer_size; - qmm_export.surfaces = orig_export->surfaces; - qmm_export.surface_size = orig_export->surface_size; - qmm_export.max_surfaces = orig_export->max_surfaces; - qmm_export.num_surfaces = orig_export->num_surfaces; + if (!str_stristr(g_gameinfo.exe_file, "sin")) + return false; - if (changed) { - // this will trigger this message to be fired to plugins, and then it will be handled - // by the empty "case G_LOCATE_GAME_DATA" in MOHAA_syscall - qmm_syscall(G_LOCATE_GAME_DATA, (intptr_t)qmm_export.edicts, qmm_export.num_edicts, qmm_export.edict_size, nullptr, 0); - } + return true; } // wrapper syscall function that calls actual engine func from orig_import // this is how QMM and plugins will call into the engine -static intptr_t SIN_syscall(intptr_t cmd, ...) { +intptr_t SIN_GameSupport::syscall(intptr_t cmd, ...) { QMM_GET_SYSCALL_ARGS(); #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SIN_syscall({} {}) called\n", SIN_EngMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SIN_GameSupport::syscall({} {}) called\n", EngMsgName(cmd), cmd); #endif // update export vars before calling into the engine - s_update_export(); + update_exports(); intptr_t ret = 0; @@ -522,13 +328,13 @@ static intptr_t SIN_syscall(intptr_t cmd, ...) { char* buffer = (char*)args[1]; intptr_t bufferSize = args[2]; *buffer = '\0'; - if (s_userinfo.count(num)) - strncpyz(buffer, s_userinfo[num].c_str(), (size_t)bufferSize); + if (userinfos.count(num)) + strncpyz(buffer, userinfos[num].c_str(), (size_t)bufferSize); break; } case G_GET_ENTITY_TOKEN: { // bool trap_GetEntityToken(char *buffer, int bufferSize); - if (s_tokencount >= s_entity_tokens.size()) { + if (token_counter >= entity_tokens.size()) { ret = false; break; } @@ -536,7 +342,7 @@ static intptr_t SIN_syscall(intptr_t cmd, ...) { char* buffer = (char*)args[0]; intptr_t bufferSize = args[1]; - strncpyz(buffer, s_entity_tokens[s_tokencount++].c_str(), (size_t)bufferSize); + strncpyz(buffer, entity_tokens[token_counter++].c_str(), (size_t)bufferSize); ret = true; break; } @@ -544,8 +350,8 @@ static intptr_t SIN_syscall(intptr_t cmd, ...) { // const char* (*get_configstring)(int num); intptr_t num = args[0]; - if (s_configstrings.count(num)) - ret = (intptr_t)s_configstrings[num].c_str(); + if (configstrings.count(num)) + ret = (intptr_t)configstrings[num].c_str(); break; } @@ -561,7 +367,7 @@ static intptr_t SIN_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SIN_syscall({} {}) returning {}\n", SIN_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SIN_GameSupport::syscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; @@ -570,11 +376,11 @@ static intptr_t SIN_syscall(intptr_t cmd, ...) { // wrapper vmMain function that calls actual mod func from orig_export // this is how QMM and plugins will call into the mod -static intptr_t SIN_vmMain(intptr_t cmd, ...) { +intptr_t SIN_GameSupport::vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SIN_vmMain({} {}) called\n", SIN_ModMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SIN_GameSupport::vmMain({} {}) called\n", ModMsgName(cmd), cmd); #endif if (!orig_export) @@ -622,18 +428,18 @@ static intptr_t SIN_vmMain(intptr_t cmd, ...) { }; // update export vars after returning from the mod - s_update_export(); + update_exports(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SIN_vmMain({} {}) returning {}\n", SIN_ModMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SIN_GameSupport::vmMain({} {}) returning {}\n", ModMsgName(cmd), cmd, ret); #endif return ret; } -static void* SIN_Entry(void* import, void*, APIType) { - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SIN_Entry({}) called\n", import); +void* SIN_GameSupport::Entry(void* import, void*, APIType) { + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SIN_GameSupport::Entry({}) called\n", import); // original import struct from engine // the struct given by the engine goes out of scope after this returns so we have to copy the whole thing @@ -644,13 +450,7 @@ static void* SIN_Entry(void* import, void*, APIType) { qmm_import.DebugLines = orig_import.DebugLines; qmm_import.numDebugLines = orig_import.numDebugLines; - // pointer to wrapper vmMain function that calls actual mod func from orig_export - g_gameinfo.pfnvmMain = SIN_vmMain; - - // pointer to wrapper syscall function that calls actual engine func from orig_import - g_gameinfo.pfnsyscall = SIN_syscall; - - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SIN_Entry({}) returning {}\n", import, fmt::ptr(&qmm_export)); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SIN_GameSupport::Entry({}) returning {}\n", import, fmt::ptr(&qmm_export)); // struct full of export lambdas to QMM's vmMain // this gets returned to the game engine, but we haven't loaded the mod yet. @@ -659,7 +459,7 @@ static void* SIN_Entry(void* import, void*, APIType) { } -static bool SIN_ModLoad(void* entry, APIType modapi) { +bool SIN_GameSupport::ModLoad(void* entry, APIType modapi) { if (modapi != QMM_API_GETGAMEAPI) return false; @@ -670,12 +470,12 @@ static bool SIN_ModLoad(void* entry, APIType modapi) { } -static void SIN_ModUnload() { +void SIN_GameSupport::ModUnload() { orig_export = nullptr; } -static const char* SIN_EngMsgName(intptr_t cmd) { +const char* SIN_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_BPRINTF); GEN_CASE(G_DPRINTF); @@ -791,7 +591,7 @@ static const char* SIN_EngMsgName(intptr_t cmd) { } -static const char* SIN_ModMsgName(intptr_t cmd) { +const char* SIN_GameSupport::ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAMEV_APIVERSION); GEN_CASE(GAME_INIT); @@ -828,3 +628,235 @@ static const char* SIN_ModMsgName(intptr_t cmd) { return "unknown"; } } + + +// update the export variables from orig_export +void SIN_GameSupport::update_exports() { + if (!orig_export) + return; + + bool changed = false; + + // if entity data changed, we need to send a G_LOCATE_GAME_DATA so plugins can hook it + if (qmm_export.edicts != orig_export->edicts + || qmm_export.edict_size != orig_export->edict_size + || qmm_export.num_edicts != orig_export->num_edicts + ) { + changed = true; + } + + qmm_export.edicts = orig_export->edicts; + qmm_export.edict_size = orig_export->edict_size; + qmm_export.num_edicts = orig_export->num_edicts; + qmm_export.max_edicts = orig_export->max_edicts; + qmm_export.consoles = orig_export->consoles; + qmm_export.console_size = orig_export->console_size; + qmm_export.num_consoles = orig_export->num_consoles; + qmm_export.max_consoles = orig_export->max_consoles; + qmm_export.conbuffers = orig_export->conbuffers; + qmm_export.conbuffer_size = orig_export->conbuffer_size; + qmm_export.surfaces = orig_export->surfaces; + qmm_export.surface_size = orig_export->surface_size; + qmm_export.max_surfaces = orig_export->max_surfaces; + qmm_export.num_surfaces = orig_export->num_surfaces; + + if (changed) { + // this will trigger this message to be fired to plugins, and then it will be handled + // by the empty "case G_LOCATE_GAME_DATA" in MOHAA_syscall + qmm_syscall(G_LOCATE_GAME_DATA, (intptr_t)qmm_export.edicts, qmm_export.num_edicts, qmm_export.edict_size, nullptr, 0); + } +} + + +game_import_t SIN_GameSupport::orig_import; + +game_export_t* SIN_GameSupport::orig_export = nullptr; + + +std::map SIN_GameSupport::configstrings; +void SIN_GameSupport::configstring(int num, const char* configstring) { + // if configstring is null, remove entry in map. otherwise store in map + if (!configstring) + configstrings.erase(num); + else + configstrings[num] = configstring; + qmm_syscall(G_CONFIGSTRING, num, configstring); +} + + +game_import_t SIN_GameSupport::qmm_import = { + GEN_IMPORT(bprintf, G_BPRINTF), + GEN_IMPORT(dprintf, G_DPRINTF), + GEN_IMPORT(printf, G_PRINTF), + GEN_IMPORT(cprintf, G_CPRINTF), + GEN_IMPORT(centerprintf, G_CENTERPRINTF), + GEN_IMPORT_9(sound, G_SOUND, void, edict_t*, int, int, float, float, float, float, float, int), + GEN_IMPORT_10(positioned_sound, G_POSITIONED_SOUND, void, vec3_t, edict_t*, int, int, float, float, float, float, float, int), + SIN_GameSupport::configstring, + GEN_IMPORT(error, G_ERROR), + GEN_IMPORT(modelindex, G_MODELINDEX), + GEN_IMPORT(soundindex, G_SOUNDINDEX), + GEN_IMPORT(imageindex, G_IMAGEINDEX), + GEN_IMPORT(itemindex, G_ITEMINDEX), + GEN_IMPORT(setmodel, G_SETMODEL), + GEN_IMPORT(trace, G_TRACE), + GEN_IMPORT(fulltrace, G_FULLTRACE), // todo: change types to actually match float, but also need to return an intptr_t instead of trace_t + GEN_IMPORT(pointcontents, G_POINT_CONTENTS), + GEN_IMPORT(inPVS, G_IN_PVS), + GEN_IMPORT(inPHS, G_IN_PHS), + GEN_IMPORT(SetAreaPortalState, G_SETAREAPORTALSTATE), + GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), + GEN_IMPORT(linkentity, G_LINKENTITY), + GEN_IMPORT(unlinkentity, G_UNLINKENTITY), + GEN_IMPORT(BoxEdicts, G_BOXEDICTS), + GEN_IMPORT(Pmove, G_PMOVE), + GEN_IMPORT(multicast, G_MULTICAST), + GEN_IMPORT(unicast, G_UNICAST), + GEN_IMPORT(WriteChar, G_MSG_WRITECHAR), + GEN_IMPORT(WriteByte, G_MSG_WRITEBYTE), + GEN_IMPORT(WriteShort, G_MSG_WRITESHORT), + GEN_IMPORT(WriteLong, G_MSG_WRITELONG), + GEN_IMPORT_1(WriteFloat, G_MSG_WRITEFLOAT, void, float), + GEN_IMPORT(WriteString, G_MSG_WRITESTRING), + GEN_IMPORT(WritePosition, G_MSG_WRITEPOSITION), + GEN_IMPORT(WriteDir, G_MSG_WRITEDIR), + GEN_IMPORT_1(WriteAngle, G_MSG_WRITEANGLE, void, float), + GEN_IMPORT(TagMalloc, G_TAGMALLOC), + GEN_IMPORT(TagFree, G_TAGFREE), + GEN_IMPORT(FreeTags, G_FREETAGS), + GEN_IMPORT(cvar, G_CVAR), + GEN_IMPORT(cvar_set, G_CVAR_SET), + GEN_IMPORT(cvar_forceset, G_CVAR_FORCESET), + GEN_IMPORT(argc, G_ARGC), + GEN_IMPORT(argv, G_ARGV), + GEN_IMPORT(args, G_ARGS), + GEN_IMPORT(AddCommandString, G_ADDCOMMANDSTRING), + GEN_IMPORT_2(DebugGraph, G_DEBUGGRAPH, void, float, int), + GEN_IMPORT(LoadFile, G_LOADFILE), + GEN_IMPORT(GameDir, G_GAMEDIR), + GEN_IMPORT(PlayerDir, G_PLAYERDIR), + GEN_IMPORT(CreatePath, G_CREATEPATH), + GEN_IMPORT(SoundLength, G_SOUNDLENGTH), + GEN_IMPORT(IsModel, G_ISMODEL), + GEN_IMPORT(NumAnims, G_NUMANIMS), + GEN_IMPORT(NumSkins, G_NUMSKINS), + GEN_IMPORT(NumGroups, G_NUMGROUPS), + GEN_IMPORT(InitCommands, G_INITCOMMANDS), + GEN_IMPORT_4(CalculateBounds, G_CALCULATEBOUNDS, void, int, float, vec3_t, vec3_t), + GEN_IMPORT(Anim_NameForNum, G_ANIM_NAMEFORNUM), + GEN_IMPORT(Anim_NumForName, G_ANIM_NUMFORNAME), + GEN_IMPORT(Anim_Random, G_ANIM_RANDOM), + GEN_IMPORT(Anim_NumFrames, G_ANIM_NUMFRAMES), + GEN_IMPORT(Anim_Time, G_ANIM_TIME), + GEN_IMPORT(Anim_Delta, G_ANIM_DELTA), + GEN_IMPORT(Frame_Commands, G_FRAME_COMMANDS), + GEN_IMPORT(Frame_Delta, G_FRAME_DELTA), + GEN_IMPORT(Frame_Time, G_FRAME_TIME), + GEN_IMPORT(Skin_NameForNum, G_SKIN_NAMEFORNUM), + GEN_IMPORT(Skin_NumForName, G_SKIN_NUMFORNAME), + GEN_IMPORT(Group_NameToNum, G_GROUP_NAMETONUM), + GEN_IMPORT(Group_NumToName, G_GROUP_NUMTONAME), + GEN_IMPORT(Group_DamageMultiplier, G_GROUP_DAMAGEMULTIPLIER), + GEN_IMPORT(Group_Flags, G_GROUP_FLAGS), + GEN_IMPORT(GetBoneInfo, G_GETBONEINFO), + GEN_IMPORT(GetBoneGroupName, G_GETBONEGROUPNAME), + GEN_IMPORT(GetBoneTransform, G_GETBONETRANSFORM), + GEN_IMPORT_4(Alias_Add, G_ALIAS_ADD, qboolean, int, const char*, const char*, float), + GEN_IMPORT(Alias_FindRandom, G_ALIAS_FINDRANDOM), + GEN_IMPORT(Alias_Dump, G_ALIAS_DUMP), + GEN_IMPORT(Alias_Clear, G_ALIAS_CLEAR), + GEN_IMPORT_3(GlobalAlias_Add, G_GLOBALALIAS_ADD, qboolean, const char*, const char*, float), + GEN_IMPORT(GlobalAlias_FindRandom, G_GLOBALALIAS_FINDRANDOM), + GEN_IMPORT(GlobalAlias_Dump, G_GLOBALALIAS_DUMP), + GEN_IMPORT(GlobalAlias_Clear, G_GLOBALALIAS_CLEAR), + GEN_IMPORT(CalcCRC , G_CALCCRC), + GEN_IMPORT(Surf_NumSurfaces, G_SURF_NUMSURFACES), + GEN_IMPORT(Surf_Surfaces, G_SURF_SURFACES), + GEN_IMPORT(IncrementStatusCount, G_INCREMENTSTATUSCOUNT), + nullptr, // DebugLines + nullptr, // numDebugLines +}; + + +// track userinfo for our G_GET_USERINFO syscall +std::map SIN_GameSupport::userinfos; +qboolean SIN_GameSupport::ClientConnect(edict_t* ent, const char* userinfo) { + // get client number (ent->s.number is not set until CLIENT_BEGIN, so calculate based on edict_t*) + if (orig_export && orig_export->edicts && orig_export->edict_size) { + intptr_t entnum = ((intptr_t)ent - (intptr_t)orig_export->edicts) / orig_export->edict_size; + intptr_t clientnum = entnum - 1; + // if userinfo is null, remove entry in map. otherwise store in map + if (!userinfo) + userinfos.erase(clientnum); + else + userinfos[clientnum] = userinfo; + } + cgame.is_from_QMM = true; + return ::vmMain(GAME_CLIENT_CONNECT, ent, userinfo); +} + + +void SIN_GameSupport::ClientUserinfoChanged(edict_t* ent, const char* userinfo) { + // get client number (ent->s.number is not set until CLIENT_BEGIN, so calculate based on edict_t*) + if (orig_export && orig_export->edicts && orig_export->edict_size) { + intptr_t entnum = ((intptr_t)ent - (intptr_t)orig_export->edicts) / orig_export->edict_size; + intptr_t clientnum = entnum - 1; + // if userinfo is null, remove entry in map. otherwise store in map + if (!userinfo) + userinfos.erase(clientnum); + else + userinfos[clientnum] = userinfo; + } + cgame.is_from_QMM = true; + ::vmMain(GAME_CLIENT_USERINFO_CHANGED, ent, userinfo); +} + + +// track entstrings for our G_GET_ENTITY_TOKEN syscall +std::vector SIN_GameSupport::entity_tokens; +size_t SIN_GameSupport::token_counter = 0; +void SIN_GameSupport::SpawnEntities(const char* mapname, const char* entstring, const char* spawnpoint) { + if (entstring) { + entity_tokens = util_parse_entstring(entstring); + token_counter = 0; + } + cgame.is_from_QMM = true; + ::vmMain(GAME_SPAWN_ENTITIES, mapname, entstring, spawnpoint); +} + + +// struct with lambdas that call QMM's vmMain function. this is given to the game engine +game_export_t SIN_GameSupport::qmm_export = { + GAME_API_VERSION, // apiversion + GEN_EXPORT(Init, GAME_INIT), + GEN_EXPORT(Shutdown, GAME_SHUTDOWN), + SIN_GameSupport::SpawnEntities, + GEN_EXPORT(WriteGame, GAME_WRITE_GAME), + GEN_EXPORT(ReadGame, GAME_READ_GAME), + GEN_EXPORT(WriteLevel, GAME_WRITE_LEVEL), + GEN_EXPORT(ReadLevel, GAME_READ_LEVEL), + SIN_GameSupport::ClientConnect, + GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), + SIN_GameSupport::ClientUserinfoChanged, + GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), + GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), + GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), + GEN_EXPORT(RunFrame, GAME_RUN_FRAME), + GEN_EXPORT(ServerCommand, GAME_SERVER_COMMAND), + GEN_EXPORT(CreateSurfaces, GAME_CREATESURFACES), + // the engine won't use these until after Init, so we can fill these in after each call into the mod's export functions ("vmMain") + nullptr, // edicts + 0, // edict_size + 0, // num_edicts + 0, // max_edicts + nullptr, // consoles + 0, // console_size + 0, // num_consoles + 0, // max_consoles + nullptr, // conbuffers + 0, // conbuffer_size + nullptr, // surfaces + 0, // surface_size + 0, // max_surfaces + 0, // num_surfaces +}; diff --git a/src/game_sof2mp.cpp b/src/game_sof2mp.cpp index e17e477..71c1c1a 100644 --- a/src/game_sof2mp.cpp +++ b/src/game_sof2mp.cpp @@ -9,6 +9,10 @@ Created By: */ +#include "version.h" + +#if defined(QMM_ARCH_32) + #include #include #include @@ -43,7 +47,10 @@ struct SOF2MP_GameSupport : public GameSupport { virtual int QVMSyscall(uint8_t* membase, int cmd, int* args); private: + // a copy of the original syscall from the engine eng_syscall orig_syscall = nullptr; + + // a copy of the vmMain function from the mod mod_vmMain orig_vmMain = nullptr; const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); @@ -898,3 +905,5 @@ int SOF2MP_GameSupport::QVMSyscall(uint8_t* membase, int cmd, int* args) { return ret; } + +#endif // QMM_ARCH_32 diff --git a/src/game_sof2sp.cpp b/src/game_sof2sp.cpp index 54c7934..5c06422 100644 --- a/src/game_sof2sp.cpp +++ b/src/game_sof2sp.cpp @@ -9,6 +9,10 @@ Created By: */ +#include "version.h" + +#if defined(QMM_ARCH_32) + #include #include "game_api.h" @@ -25,7 +29,7 @@ GEN_GAME_FUNCS(SOF2SP); // auto-detection logic for SOF2SP -static bool SOF2SP_AutoDetect(api_supportedgame* game, APIType engineapi) { +static bool SOF2SP_AutoDetect(APIType engineapi) { if (engineapi != QMM_API_GETGAMEAPI) return false; @@ -198,7 +202,7 @@ static game_export_t qmm_export = { // update the export variables from orig_export -static void s_update_export() { +static void update_exports() { if (!orig_export) return; @@ -218,7 +222,7 @@ static intptr_t SOF2SP_syscall(intptr_t cmd, ...) { #endif // update export vars before calling into the engine - s_update_export(); + update_exports(); intptr_t ret = 0; @@ -432,7 +436,7 @@ static intptr_t SOF2SP_vmMain(intptr_t cmd, ...) { }; // update export vars after returning from the mod - s_update_export(); + update_exports(); #ifdef _DEBUG LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2SP_vmMain({} {}) returning {}\n", SOF2SP_ModMsgName(cmd), cmd, ret); @@ -644,3 +648,5 @@ static const char* SOF2SP_ModMsgName(intptr_t cmd) { return "unknown"; } } + +#endif // QMM_ARCH_32 diff --git a/src/game_stef2.cpp b/src/game_stef2.cpp index e9a2716..c1a0fab 100644 --- a/src/game_stef2.cpp +++ b/src/game_stef2.cpp @@ -9,6 +9,10 @@ Created By: */ +#include "version.h" + +#if defined(QMM_ARCH_32) + #include #define GAME_DLL #include @@ -30,7 +34,7 @@ GEN_GAME_FUNCS(STEF2); // auto-detection logic for STEF2 -static bool STEF2_AutoDetect(api_supportedgame* game, APIType engineapi) { +static bool STEF2_AutoDetect(APIType engineapi) { if (engineapi != QMM_API_GETGAMEAPI) return false; @@ -400,12 +404,12 @@ static game_import_t qmm_import = { // we need these to be called BEFORE plugins' prehooks get called so they have to be done in the qmm_export table // track entstrings for our G_GET_ENTITY_TOKEN syscall -static std::vector s_entity_tokens; -static size_t s_tokencount = 0; +static std::vector entity_tokens; +static size_t token_counter = 0; static void STEF2_SpawnEntities(const char* mapname, const char* entstring, int levelTime) { if (entstring) { - s_entity_tokens = util_parse_entstring(entstring); - s_tokencount = 0; + entity_tokens = util_parse_entstring(entstring); + token_counter = 0; } cgame.is_from_QMM = true; vmMain(GAME_SPAWN_ENTITIES, mapname, entstring, levelTime); @@ -456,7 +460,7 @@ static game_export_t qmm_export = { // update the export variables from orig_export -static void s_update_export() { +static void update_exports() { if (!orig_export) return; @@ -495,7 +499,7 @@ static intptr_t STEF2_syscall(intptr_t cmd, ...) { #endif // update export vars before calling into the engine - s_update_export(); + update_exports(); intptr_t ret = 0; @@ -861,7 +865,7 @@ static intptr_t STEF2_syscall(intptr_t cmd, ...) { } case G_GET_ENTITY_TOKEN: { // qboolean trap_GetEntityToken(char *buffer, int bufferSize); - if (s_tokencount >= s_entity_tokens.size()) { + if (token_counter >= entity_tokens.size()) { ret = qfalse; break; } @@ -869,7 +873,7 @@ static intptr_t STEF2_syscall(intptr_t cmd, ...) { char* buffer = (char*)args[0]; intptr_t bufferSize = args[1]; - strncpyz(buffer, s_entity_tokens[s_tokencount++].c_str(), (size_t)bufferSize); + strncpyz(buffer, entity_tokens[token_counter++].c_str(), (size_t)bufferSize); ret = qtrue; break; } @@ -950,7 +954,7 @@ static intptr_t STEF2_vmMain(intptr_t cmd, ...) { }; // update export vars after returning from the mod - s_update_export(); + update_exports(); #ifdef _DEBUG LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STEF2_vmMain({} {}) returning {}\n", STEF2_ModMsgName(cmd), cmd, ret); @@ -1399,3 +1403,5 @@ static const char* STEF2_ModMsgName(intptr_t cmd) { return "unknown"; } } + +#endif // QMM_ARCH_32 diff --git a/src/game_stvoyhm.cpp b/src/game_stvoyhm.cpp index a68aa5b..14dcbd6 100644 --- a/src/game_stvoyhm.cpp +++ b/src/game_stvoyhm.cpp @@ -9,6 +9,10 @@ Created By: */ +#include "version.h" + +#if defined(QMM_ARCH_32) + #include #include @@ -42,7 +46,10 @@ struct STVOYHM_GameSupport : public GameSupport { virtual int QVMSyscall(uint8_t* membase, int cmd, int* args); private: + // a copy of the original syscall from the engine eng_syscall orig_syscall = nullptr; + + // a copy of the vmMain function from the mod mod_vmMain orig_vmMain = nullptr; const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); @@ -671,3 +678,5 @@ int STVOYHM_GameSupport::QVMSyscall(uint8_t* membase, int cmd, int* args) { return ret; } + +#endif // QMM_ARCH_32 diff --git a/src/game_stvoysp.cpp b/src/game_stvoysp.cpp index 979d25d..925531d 100644 --- a/src/game_stvoysp.cpp +++ b/src/game_stvoysp.cpp @@ -9,6 +9,10 @@ Created By: */ +#include "version.h" + +#if defined(QMM_ARCH_32) + #include #include @@ -28,7 +32,7 @@ GEN_GAME_FUNCS(STVOYSP); // auto-detection logic for STVOYSP -static bool STVOYSP_AutoDetect(api_supportedgame* game, APIType engineapi) { +static bool STVOYSP_AutoDetect(APIType engineapi) { if (engineapi != QMM_API_GETGAMEAPI) return false; @@ -99,12 +103,12 @@ static game_import_t qmm_import = { // we need these to be called BEFORE plugins' prehooks get called so they have to be done in the qmm_export table // track entstrings for our G_GET_ENTITY_TOKEN syscall -static std::vector s_entity_tokens; -static size_t s_tokencount = 0; +static std::vector entity_tokens; +static size_t token_counter = 0; static void STVOYSP_Init(const char* mapname, const char* spawntarget, int checkSum, const char* entstring, int levelTime, int randomSeed, int globalTime, SavedGameJustLoaded_e eSavedGameJustLoaded, qboolean qbLoadTransition) { if (entstring) { - s_entity_tokens = util_parse_entstring(entstring); - s_tokencount = 0; + entity_tokens = util_parse_entstring(entstring); + token_counter = 0; } cgame.is_from_QMM = true; vmMain(GAME_INIT, mapname, spawntarget, checkSum, entstring, levelTime, randomSeed, globalTime, eSavedGameJustLoaded, qbLoadTransition); @@ -136,7 +140,7 @@ static game_export_t qmm_export = { // update the export variables from orig_export -static void s_update_export() { +static void update_exports() { if (!orig_export) return; @@ -173,7 +177,7 @@ static intptr_t STVOYSP_syscall(intptr_t cmd, ...) { #endif // update export vars before calling into the engine - s_update_export(); + update_exports(); intptr_t ret = 0; @@ -256,7 +260,7 @@ static intptr_t STVOYSP_syscall(intptr_t cmd, ...) { } case G_GET_ENTITY_TOKEN: { // qboolean trap_GetEntityToken(char *buffer, int bufferSize); - if (s_tokencount >= s_entity_tokens.size()) { + if (token_counter >= entity_tokens.size()) { ret = qfalse; break; } @@ -264,7 +268,7 @@ static intptr_t STVOYSP_syscall(intptr_t cmd, ...) { char* buffer = (char*)args[0]; intptr_t bufferSize = args[1]; - strncpyz(buffer, s_entity_tokens[s_tokencount++].c_str(), (size_t)bufferSize); + strncpyz(buffer, entity_tokens[token_counter++].c_str(), (size_t)bufferSize); ret = qtrue; break; } @@ -338,7 +342,7 @@ static intptr_t STVOYSP_vmMain(intptr_t cmd, ...) { }; // update export vars after returning from the mod - s_update_export(); + update_exports(); #ifdef _DEBUG LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYSP_vmMain({} {}) returning {}\n", STVOYSP_ModMsgName(cmd), cmd, ret); @@ -470,3 +474,5 @@ static const char* STVOYSP_ModMsgName(intptr_t cmd) { return "unknown"; } } + +#endif // QMM_ARCH_32 diff --git a/src/game_wet.cpp b/src/game_wet.cpp index dbfc102..a1684f2 100644 --- a/src/game_wet.cpp +++ b/src/game_wet.cpp @@ -38,7 +38,10 @@ struct WET_GameSupport : public GameSupport { virtual const char* GameCode() { return "WET"; } private: + // a copy of the original syscall from the engine eng_syscall orig_syscall = nullptr; + + // a copy of the vmMain function from the mod mod_vmMain orig_vmMain = nullptr; const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); From 59e02f3f33516cd385d007be54e01524b7103b79 Mon Sep 17 00:00:00 2001 From: Kevin Masterson Date: Wed, 18 Mar 2026 21:50:19 +0200 Subject: [PATCH 5/7] finished all games --- src/game_api.cpp | 1 - src/game_jasp.cpp | 4 +- src/game_jk2sp.cpp | 3 +- src/game_sof2sp.cpp | 404 +++++++++--------- src/game_stef2.cpp | 965 ++++++++++++++++++++++--------------------- src/game_stvoysp.cpp | 307 +++++++------- 6 files changed, 881 insertions(+), 803 deletions(-) diff --git a/src/game_api.cpp b/src/game_api.cpp index 186e068..e7bdd29 100644 --- a/src/game_api.cpp +++ b/src/game_api.cpp @@ -88,7 +88,6 @@ std::vector api_supportedgames = { // the rest of the games don't appear to have an official 64-bit version or source port #if defined(QMM_ARCH_32) { "game" MOD_DLL, nullptr, "base", "Star Trek: Elite Force II", GEN_GAME_INFO(STEF2), 17, 4 }, - { "game" MOD_DLL, nullptr, ".", "Soldier of Fortune 2: Double Helix (SP)", GEN_GAME_INFO(SOF2SP), 0, 0 }, { "efgame" MOD_DLL, nullptr, ".", "Star Trek Voyager: Elite Force (SP)", GEN_GAME_INFO(STVOYSP), 13, 9 }, #endif diff --git a/src/game_jasp.cpp b/src/game_jasp.cpp index f7c4ffd..aa46531 100644 --- a/src/game_jasp.cpp +++ b/src/game_jasp.cpp @@ -40,11 +40,10 @@ struct JASP_GameSupport : public GameSupport { virtual const char* GameCode() { return "JASP"; } private: + // update the export variables from orig_export static void update_exports(); // track entstrings for our G_GET_ENTITY_TOKEN syscall - // TODO find a way to make this more elegant instead of statics -// track entstrings for our G_GET_ENTITY_TOKEN syscall static std::map> subbsp_entity_tokens; // -1 = main map entities static std::map token_counter; static intptr_t active_subbsp; @@ -590,7 +589,6 @@ const char* JASP_GameSupport::ModMsgName(intptr_t cmd) { } -// update the export variables from orig_export void JASP_GameSupport::update_exports() { if (!orig_export) return; diff --git a/src/game_jk2sp.cpp b/src/game_jk2sp.cpp index 5356022..1ace725 100644 --- a/src/game_jk2sp.cpp +++ b/src/game_jk2sp.cpp @@ -39,10 +39,10 @@ struct JK2SP_GameSupport : public GameSupport { virtual const char* GameCode() { return "JK2SP"; } private: + // update the export variables from orig_export static void update_exports(); // track entstrings for our G_GET_ENTITY_TOKEN syscall - // TODO find a way to make this more elegant instead of statics static std::vector entity_tokens; static size_t token_counter; static void Init(const char* mapname, const char* spawntarget, int checkSum, const char* entstring, int levelTime, int randomSeed, int globalTime, SavedGameJustLoaded_e eSavedGameJustLoaded, qboolean qbLoadTransition); @@ -512,7 +512,6 @@ const char* JK2SP_GameSupport::ModMsgName(intptr_t cmd) { } -// update the export variables from orig_export void JK2SP_GameSupport::update_exports() { if (!orig_export) return; diff --git a/src/game_sof2sp.cpp b/src/game_sof2sp.cpp index 5c06422..db38e86 100644 --- a/src/game_sof2sp.cpp +++ b/src/game_sof2sp.cpp @@ -23,202 +23,71 @@ Created By: #include "main.h" #include "util.h" -GEN_GAME_QMM_MSGS(SOF2SP); +struct SOF2SP_GameSupport : public GameSupport { + virtual const char* EngMsgName(intptr_t msg); + virtual const char* ModMsgName(intptr_t msg); + virtual bool AutoDetect(APIType engine_api); + virtual void* Entry(void* syscall, void*, APIType engine_api); + virtual bool ModLoad(void* entry, APIType mod_api); + virtual void ModUnload(); -GEN_GAME_FUNCS(SOF2SP); + virtual intptr_t syscall(intptr_t, ...); + virtual intptr_t vmMain(intptr_t, ...); + virtual const char* DefaultDLLName() { return "game" MOD_DLL; } + virtual const char* DefaultModDir() { return "."; } + virtual const char* GameName() { return "Soldier of Fortune 2: Double Helix (SP)"; } + virtual const char* GameCode() { return "SOF2SP"; } -// auto-detection logic for SOF2SP -static bool SOF2SP_AutoDetect(APIType engineapi) { - if (engineapi != QMM_API_GETGAMEAPI) - return false; +private: + // update the export variables from orig_export + static void update_exports(); - if (!str_striequal(g_gameinfo.qmm_file, game->dllname)) - return false; + // a copy of the apiversion int that comes from the game engine + static intptr_t orig_apiversion; - if (!str_stristr(g_gameinfo.exe_file, "sof2")) - return false; - - return true; -} + // a copy of the original import struct that comes from the game engine + static game_import_t orig_import; + // a copy of the original export struct pointer that comes from the mod + static game_export_t* orig_export; -// a copy of the apiversion int that comes from the game engine -static intptr_t orig_apiversion = 0; + // struct with lambdas that call QMM's syscall function. this is given to the mod + static game_import_t qmm_import; -// a copy of the original import struct that comes from the game engine -static game_import_t orig_import; + // struct with lambdas that call QMM's vmMain function. this is given to the game engine + static game_export_t qmm_export; -// a copy of the original export struct pointer that comes from the mod -static game_export_t* orig_export = nullptr; - -// struct with lambdas that call QMM's syscall function. this is given to the mod -static game_import_t qmm_import = { - GEN_IMPORT(Printf, G_PRINTF), - GEN_IMPORT(DPrintf, G_DPRINTF), - GEN_IMPORT(DPrintf2, G_DPRINTF2), - GEN_IMPORT(snprintf, G_SNPRINTF), - GEN_IMPORT(ErrorF, G_ERRORF), - GEN_IMPORT(FS_FOpenFile, G_FS_FOPEN_FILE), - GEN_IMPORT(FS_Read, G_FS_READ), - GEN_IMPORT(FS_Write, G_FS_WRITE), - GEN_IMPORT(FS_FCloseFile, G_FS_FCLOSE_FILE), - GEN_IMPORT(FS_ReadFile, G_FS_READFILE), - GEN_IMPORT(FS_FreeFile, G_FS_FREEFILE), - GEN_IMPORT(FS_FileAvailable, G_FS_FILEAVAILABLE), - GEN_IMPORT(FS_ListFiles, G_FS_LISTFILES), - GEN_IMPORT(FS_FreeFileList, G_FS_FREEFILELIST), - GEN_IMPORT(unknown14, G_UNKNOWN14), - GEN_IMPORT(Milliseconds, G_MILLISECONDS), - GEN_IMPORT(unknown16, G_UNKNOWN16), - GEN_IMPORT(unknown17, G_UNKNOWN17), - GEN_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND), - GEN_IMPORT(ExecuteConsoleCommand, G_EXECUTE_CONSOLE_COMMAND), - GEN_IMPORT(Argc, G_ARGC), - GEN_IMPORT(Argv, G_ARGV), - GEN_IMPORT(Args, G_ARGS), - GEN_IMPORT(Cvar_IsModified, G_CVAR_ISMODIFIED), - GEN_IMPORT(Cvar_Register, G_CVAR_REGISTER), - GEN_IMPORT(Cvar_Update, G_CVAR_UPDATE), - GEN_IMPORT(Cvar_Set, G_CVAR_SET), - GEN_IMPORT(Cvar_Get, G_CVAR_GET), - GEN_IMPORT(Cvar_SetValue, G_CVAR_SETVALUE), - GEN_IMPORT(Cvar_VariableIntegerValue, G_CVAR_VARIABLE_INTEGER_VALUE), - GEN_IMPORT(Cvar_VariableFloatValue, G_CVAR_VARIABLE_FLOAT_VALUE), - GEN_IMPORT(Cvar_VariableStringBuffer, G_CVAR_VARIABLE_STRING_BUFFER), - GEN_IMPORT(Malloc, G_MALLOC), - GEN_IMPORT(Free, G_FREE), - GEN_IMPORT(unknown34, G_UNKNOWN34), - GEN_IMPORT(CM_RegisterTerrain, G_CM_REGISTERTERRAIN), - GEN_IMPORT(unknown36, G_UNKNOWN36), - GEN_IMPORT(unknown37, G_UNKNOWN37), - GEN_IMPORT(unknown38, G_UNKNOWN38), - GEN_IMPORT(unknown39, G_UNKNOWN39), - GEN_IMPORT(unknown40, G_UNKNOWN40), - GEN_IMPORT(unknown41, G_UNKNOWN41), - GEN_IMPORT(unknown42, G_UNKNOWN42), - GEN_IMPORT(unknown43, G_UNKNOWN43), - GEN_IMPORT(unknown44, G_UNKNOWN44), - GEN_IMPORT(unknown45, G_UNKNOWN45), - GEN_IMPORT(unknown46, G_UNKNOWN46), - GEN_IMPORT(unknown47, G_UNKNOWN47), - GEN_IMPORT(unknown48, G_UNKNOWN48), - GEN_IMPORT(unknown49, G_UNKNOWN49), - GEN_IMPORT(unknown50, G_UNKNOWN50), - GEN_IMPORT(Trace, G_TRACE), - GEN_IMPORT(unknown52, G_UNKNOWN52), - GEN_IMPORT(unknown53, G_UNKNOWN53), - GEN_IMPORT(LocateGameData, G_LOCATE_GAME_DATA), - GEN_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND), - GEN_IMPORT(unknown56, G_UNKNOWN56), - GEN_IMPORT(unknown57, G_UNKNOWN57), - GEN_IMPORT(unknown58, G_UNKNOWN58), - GEN_IMPORT(unknown59, G_UNKNOWN59), - GEN_IMPORT(unknown60, G_UNKNOWN60), - GEN_IMPORT(unknown61, G_UNKNOWN61), - GEN_IMPORT(unknown62, G_UNKNOWN62), - GEN_IMPORT(unknown63, G_UNKNOWN63), - GEN_IMPORT(unknown64, G_UNKNOWN64), - GEN_IMPORT(unknown65, G_UNKNOWN65), - GEN_IMPORT(unknown66, G_UNKNOWN66), - 0, // unknown67 - 0, // unknown68 - GEN_IMPORT(unknown69, G_UNKNOWN69), - GEN_IMPORT(unknown70, G_UNKNOWN70), - GEN_IMPORT(PointContents, G_POINT_CONTENTS), - GEN_IMPORT(unknown72, G_UNKNOWN72), - GEN_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL), - GEN_IMPORT(SetActiveSubBSP, G_SET_ACTIVE_SUBBSP), - GEN_IMPORT(unknown75, G_UNKNOWN75), - GEN_IMPORT(unknown76, G_UNKNOWN76), - GEN_IMPORT(SetConfigstring, G_SET_CONFIGSTRING), - GEN_IMPORT(GetConfigstring, G_GET_CONFIGSTRING), - GEN_IMPORT(GetServerInfo, G_GET_SERVERINFO), - GEN_IMPORT(AdjustAreaPortalState, G_ADJUSTAREAPORTALSTATE), - GEN_IMPORT(unknown81, G_UNKNOWN81), - GEN_IMPORT(unknown82, G_UNKNOWN82), - GEN_IMPORT(unknown83, G_UNKNOWN83), - GEN_IMPORT(unknown84, G_UNKNOWN84), - GEN_IMPORT(TIKI_RegisterModel, G_TIKI_REGISTERMODEL), - GEN_IMPORT(unknown86, G_UNKNOWN86), - GEN_IMPORT(unknown87, G_UNKNOWN87), - GEN_IMPORT(unknown88, G_UNKNOWN88), - GEN_IMPORT(unknown89, G_UNKNOWN89), - GEN_IMPORT(unknown90, G_UNKNOWN90), - GEN_IMPORT(unknown91, G_UNKNOWN91), - GEN_IMPORT(unknown92, G_UNKNOWN92), - GEN_IMPORT(unknown93, G_UNKNOWN93), - GEN_IMPORT(unknown94, G_UNKNOWN94), - GEN_IMPORT(unknown95, G_UNKNOWN95), - GEN_IMPORT(GetEntityToken, G_GET_ENTITY_TOKEN), - GEN_IMPORT(unknown97, G_UNKNOWN97), - GEN_IMPORT(unknown98, G_UNKNOWN98), - GEN_IMPORT(unknown99, G_UNKNOWN99), - GEN_IMPORT(unknown100, G_UNKNOWN100), - GEN_IMPORT(unknown101, G_UNKNOWN101), - GEN_IMPORT(unknown102, G_UNKNOWN102), - GEN_IMPORT(unknown103, G_UNKNOWN103), - GEN_IMPORT(unknown104, G_UNKNOWN104), - GEN_IMPORT(unknown105, G_UNKNOWN105), - GEN_IMPORT(unknown106, G_UNKNOWN106), - GEN_IMPORT(unknown107, G_UNKNOWN107), - GEN_IMPORT(unknown108, G_UNKNOWN108), - GEN_IMPORT(unknown109, G_UNKNOWN109), - GEN_IMPORT(unknown110, G_UNKNOWN110), - GEN_IMPORT(CM_TM_Upload, G_CM_TM_UPLOAD), - GEN_IMPORT(SaveTerrainImageToDisk, G_SAVETERRAINIMAGETODISK), + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = GEN_GAME_QMM_MOD_MSGS(); }; +GEN_GAME_OBJ(SOF2SP); -// struct with lambdas that call QMM's vmMain function. this is given to the game engine -static game_export_t qmm_export = { - GEN_EXPORT(Init, GAME_INIT), - GEN_EXPORT(Shutdown, GAME_SHUTDOWN), - GEN_EXPORT(ClientConnect, GAME_CLIENT_CONNECT), - GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), - GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), - GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), - GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), - GEN_EXPORT(RunFrame, GAME_RUN_FRAME), - GEN_EXPORT(IsClientActive, GAME_IS_CLIENT_ACTIVE), - GEN_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND), - 0, // unknown10 - GEN_EXPORT(SpawnRMGEntity, GAME_SPAWN_RMG_ENTITY), - GEN_EXPORT(arioche, GAME_ARIOCHE), - GEN_EXPORT(EntityList, GAME_ENTITY_LIST), - GEN_EXPORT(WriteLevel, GAME_WRITE_LEVEL), - 0, // unknown15 - GEN_EXPORT(unknown16, GAME_UNKNOWN16), - GEN_EXPORT(Save, GAME_SAVE), - GEN_EXPORT(GameAllowedToSaveHere, GAME_GAMEALLOWEDTOSAVEHERE), - GEN_EXPORT(CanPlayCinematic, GAME_CAN_PLAY_CINEMATIC), - GEN_EXPORT(unknown20, GAME_UNKNOWN20), - GEN_EXPORT(unknown21, GAME_UNKNOWN21), - GEN_EXPORT(unknown22, GAME_UNKNOWN22), - GEN_EXPORT(unknown23, GAME_UNKNOWN23), - GEN_EXPORT(unknown24, GAME_UNKNOWN24), - GEN_EXPORT(unknown25, GAME_UNKNOWN25), -}; +// auto-detection logic for SOF2SP +bool SOF2SP_GameSupport::AutoDetect(APIType engineapi) { + if (engineapi != QMM_API_GETGAMEAPI) + return false; + + if (!str_striequal(g_gameinfo.qmm_file, DefaultDLLName())) + return false; -// update the export variables from orig_export -static void update_exports() { - if (!orig_export) - return; + if (!str_stristr(g_gameinfo.exe_file, "sof2")) + return false; - qmm_export.unknown10 = orig_export->unknown10; - qmm_export.unknown15 = orig_export->unknown15; + return true; } // wrapper syscall function that calls actual engine func from orig_import // this is how QMM and plugins will call into the engine -static intptr_t SOF2SP_syscall(intptr_t cmd, ...) { +intptr_t SOF2SP_GameSupport::syscall(intptr_t cmd, ...) { QMM_GET_SYSCALL_ARGS(); #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2SP_syscall({} {}) called\n", SOF2SP_EngMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2SP_GameSupport::syscall({} {}) called\n", EngMsgName(cmd), cmd); #endif // update export vars before calling into the engine @@ -380,7 +249,7 @@ static intptr_t SOF2SP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2SP_syscall({} {}) returning {}\n", SOF2SP_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2SP_GameSupport::syscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; @@ -389,11 +258,11 @@ static intptr_t SOF2SP_syscall(intptr_t cmd, ...) { // wrapper vmMain function that calls actual mod func from orig_export // this is how QMM and plugins will call into the mod -static intptr_t SOF2SP_vmMain(intptr_t cmd, ...) { +intptr_t SOF2SP_GameSupport::vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2SP_vmMain({} {}) called\n", SOF2SP_ModMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2SP_GameSupport::vmMain({} {}) called\n", ModMsgName(cmd), cmd); #endif if (!orig_export) @@ -439,16 +308,16 @@ static intptr_t SOF2SP_vmMain(intptr_t cmd, ...) { update_exports(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2SP_vmMain({} {}) returning {}\n", SOF2SP_ModMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2SP_GameSupport::vmMain({} {}) returning {}\n", ModMsgName(cmd), cmd, ret); #endif return ret; } -static void* SOF2SP_Entry(void* apiversion, void* import, APIType) { +void* SOF2SP_GameSupport::Entry(void* apiversion, void* import, APIType) { orig_apiversion = (intptr_t)apiversion; - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2SP_Entry({}, {}) called\n", orig_apiversion, import); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2SP_GameSupport::Entry({}, {}) called\n", orig_apiversion, import); // original import struct from engine // the struct given by the engine goes out of scope after this returns so we have to copy the whole thing @@ -458,12 +327,6 @@ static void* SOF2SP_Entry(void* apiversion, void* import, APIType) { // fill in variables of our hooked import struct to pass to the mod // qmm_import.unknown = orig_import.unknown; - // pointer to wrapper vmMain function that calls actual mod func from orig_export - g_gameinfo.pfnvmMain = SOF2SP_vmMain; - - // pointer to wrapper syscall function that calls actual engine func from orig_import - g_gameinfo.pfnsyscall = SOF2SP_syscall; - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("SOF2SP_Entry({}, {}) returning {}\n", orig_apiversion, import, fmt::ptr(&qmm_export)); // struct full of export lambdas to QMM's vmMain @@ -473,7 +336,7 @@ static void* SOF2SP_Entry(void* apiversion, void* import, APIType) { } -static bool SOF2SP_ModLoad(void* entry, APIType modapi) { +bool SOF2SP_GameSupport::ModLoad(void* entry, APIType modapi) { if (modapi != QMM_API_GETGAMEAPI) return false; @@ -485,12 +348,12 @@ static bool SOF2SP_ModLoad(void* entry, APIType modapi) { } -static void SOF2SP_ModUnload() { +void SOF2SP_GameSupport::ModUnload() { orig_export = nullptr; } -static const char* SOF2SP_EngMsgName(intptr_t cmd) { +const char* SOF2SP_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINTF); GEN_CASE(G_DPRINTF); @@ -615,7 +478,7 @@ static const char* SOF2SP_EngMsgName(intptr_t cmd) { } -static const char* SOF2SP_ModMsgName(intptr_t cmd) { +const char* SOF2SP_GameSupport::ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAME_INIT); GEN_CASE(GAME_SHUTDOWN); @@ -649,4 +512,169 @@ static const char* SOF2SP_ModMsgName(intptr_t cmd) { } } + +void SOF2SP_GameSupport::update_exports() { + if (!orig_export) + return; + + qmm_export.unknown10 = orig_export->unknown10; + qmm_export.unknown15 = orig_export->unknown15; +} + + +intptr_t SOF2SP_GameSupport::orig_apiversion = 0; + + +game_import_t SOF2SP_GameSupport::orig_import; + + +game_export_t* SOF2SP_GameSupport::orig_export = nullptr; + + +game_import_t SOF2SP_GameSupport::qmm_import = { + GEN_IMPORT(Printf, G_PRINTF), + GEN_IMPORT(DPrintf, G_DPRINTF), + GEN_IMPORT(DPrintf2, G_DPRINTF2), + GEN_IMPORT(snprintf, G_SNPRINTF), + GEN_IMPORT(ErrorF, G_ERRORF), + GEN_IMPORT(FS_FOpenFile, G_FS_FOPEN_FILE), + GEN_IMPORT(FS_Read, G_FS_READ), + GEN_IMPORT(FS_Write, G_FS_WRITE), + GEN_IMPORT(FS_FCloseFile, G_FS_FCLOSE_FILE), + GEN_IMPORT(FS_ReadFile, G_FS_READFILE), + GEN_IMPORT(FS_FreeFile, G_FS_FREEFILE), + GEN_IMPORT(FS_FileAvailable, G_FS_FILEAVAILABLE), + GEN_IMPORT(FS_ListFiles, G_FS_LISTFILES), + GEN_IMPORT(FS_FreeFileList, G_FS_FREEFILELIST), + GEN_IMPORT(unknown14, G_UNKNOWN14), + GEN_IMPORT(Milliseconds, G_MILLISECONDS), + GEN_IMPORT(unknown16, G_UNKNOWN16), + GEN_IMPORT(unknown17, G_UNKNOWN17), + GEN_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND), + GEN_IMPORT(ExecuteConsoleCommand, G_EXECUTE_CONSOLE_COMMAND), + GEN_IMPORT(Argc, G_ARGC), + GEN_IMPORT(Argv, G_ARGV), + GEN_IMPORT(Args, G_ARGS), + GEN_IMPORT(Cvar_IsModified, G_CVAR_ISMODIFIED), + GEN_IMPORT(Cvar_Register, G_CVAR_REGISTER), + GEN_IMPORT(Cvar_Update, G_CVAR_UPDATE), + GEN_IMPORT(Cvar_Set, G_CVAR_SET), + GEN_IMPORT(Cvar_Get, G_CVAR_GET), + GEN_IMPORT(Cvar_SetValue, G_CVAR_SETVALUE), + GEN_IMPORT(Cvar_VariableIntegerValue, G_CVAR_VARIABLE_INTEGER_VALUE), + GEN_IMPORT(Cvar_VariableFloatValue, G_CVAR_VARIABLE_FLOAT_VALUE), + GEN_IMPORT(Cvar_VariableStringBuffer, G_CVAR_VARIABLE_STRING_BUFFER), + GEN_IMPORT(Malloc, G_MALLOC), + GEN_IMPORT(Free, G_FREE), + GEN_IMPORT(unknown34, G_UNKNOWN34), + GEN_IMPORT(CM_RegisterTerrain, G_CM_REGISTERTERRAIN), + GEN_IMPORT(unknown36, G_UNKNOWN36), + GEN_IMPORT(unknown37, G_UNKNOWN37), + GEN_IMPORT(unknown38, G_UNKNOWN38), + GEN_IMPORT(unknown39, G_UNKNOWN39), + GEN_IMPORT(unknown40, G_UNKNOWN40), + GEN_IMPORT(unknown41, G_UNKNOWN41), + GEN_IMPORT(unknown42, G_UNKNOWN42), + GEN_IMPORT(unknown43, G_UNKNOWN43), + GEN_IMPORT(unknown44, G_UNKNOWN44), + GEN_IMPORT(unknown45, G_UNKNOWN45), + GEN_IMPORT(unknown46, G_UNKNOWN46), + GEN_IMPORT(unknown47, G_UNKNOWN47), + GEN_IMPORT(unknown48, G_UNKNOWN48), + GEN_IMPORT(unknown49, G_UNKNOWN49), + GEN_IMPORT(unknown50, G_UNKNOWN50), + GEN_IMPORT(Trace, G_TRACE), + GEN_IMPORT(unknown52, G_UNKNOWN52), + GEN_IMPORT(unknown53, G_UNKNOWN53), + GEN_IMPORT(LocateGameData, G_LOCATE_GAME_DATA), + GEN_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND), + GEN_IMPORT(unknown56, G_UNKNOWN56), + GEN_IMPORT(unknown57, G_UNKNOWN57), + GEN_IMPORT(unknown58, G_UNKNOWN58), + GEN_IMPORT(unknown59, G_UNKNOWN59), + GEN_IMPORT(unknown60, G_UNKNOWN60), + GEN_IMPORT(unknown61, G_UNKNOWN61), + GEN_IMPORT(unknown62, G_UNKNOWN62), + GEN_IMPORT(unknown63, G_UNKNOWN63), + GEN_IMPORT(unknown64, G_UNKNOWN64), + GEN_IMPORT(unknown65, G_UNKNOWN65), + GEN_IMPORT(unknown66, G_UNKNOWN66), + 0, // unknown67 + 0, // unknown68 + GEN_IMPORT(unknown69, G_UNKNOWN69), + GEN_IMPORT(unknown70, G_UNKNOWN70), + GEN_IMPORT(PointContents, G_POINT_CONTENTS), + GEN_IMPORT(unknown72, G_UNKNOWN72), + GEN_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL), + GEN_IMPORT(SetActiveSubBSP, G_SET_ACTIVE_SUBBSP), + GEN_IMPORT(unknown75, G_UNKNOWN75), + GEN_IMPORT(unknown76, G_UNKNOWN76), + GEN_IMPORT(SetConfigstring, G_SET_CONFIGSTRING), + GEN_IMPORT(GetConfigstring, G_GET_CONFIGSTRING), + GEN_IMPORT(GetServerInfo, G_GET_SERVERINFO), + GEN_IMPORT(AdjustAreaPortalState, G_ADJUSTAREAPORTALSTATE), + GEN_IMPORT(unknown81, G_UNKNOWN81), + GEN_IMPORT(unknown82, G_UNKNOWN82), + GEN_IMPORT(unknown83, G_UNKNOWN83), + GEN_IMPORT(unknown84, G_UNKNOWN84), + GEN_IMPORT(TIKI_RegisterModel, G_TIKI_REGISTERMODEL), + GEN_IMPORT(unknown86, G_UNKNOWN86), + GEN_IMPORT(unknown87, G_UNKNOWN87), + GEN_IMPORT(unknown88, G_UNKNOWN88), + GEN_IMPORT(unknown89, G_UNKNOWN89), + GEN_IMPORT(unknown90, G_UNKNOWN90), + GEN_IMPORT(unknown91, G_UNKNOWN91), + GEN_IMPORT(unknown92, G_UNKNOWN92), + GEN_IMPORT(unknown93, G_UNKNOWN93), + GEN_IMPORT(unknown94, G_UNKNOWN94), + GEN_IMPORT(unknown95, G_UNKNOWN95), + GEN_IMPORT(GetEntityToken, G_GET_ENTITY_TOKEN), + GEN_IMPORT(unknown97, G_UNKNOWN97), + GEN_IMPORT(unknown98, G_UNKNOWN98), + GEN_IMPORT(unknown99, G_UNKNOWN99), + GEN_IMPORT(unknown100, G_UNKNOWN100), + GEN_IMPORT(unknown101, G_UNKNOWN101), + GEN_IMPORT(unknown102, G_UNKNOWN102), + GEN_IMPORT(unknown103, G_UNKNOWN103), + GEN_IMPORT(unknown104, G_UNKNOWN104), + GEN_IMPORT(unknown105, G_UNKNOWN105), + GEN_IMPORT(unknown106, G_UNKNOWN106), + GEN_IMPORT(unknown107, G_UNKNOWN107), + GEN_IMPORT(unknown108, G_UNKNOWN108), + GEN_IMPORT(unknown109, G_UNKNOWN109), + GEN_IMPORT(unknown110, G_UNKNOWN110), + GEN_IMPORT(CM_TM_Upload, G_CM_TM_UPLOAD), + GEN_IMPORT(SaveTerrainImageToDisk, G_SAVETERRAINIMAGETODISK), +}; + + +game_export_t SOF2SP_GameSupport::qmm_export = { + GEN_EXPORT(Init, GAME_INIT), + GEN_EXPORT(Shutdown, GAME_SHUTDOWN), + GEN_EXPORT(ClientConnect, GAME_CLIENT_CONNECT), + GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), + GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), + GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), + GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), + GEN_EXPORT(RunFrame, GAME_RUN_FRAME), + GEN_EXPORT(IsClientActive, GAME_IS_CLIENT_ACTIVE), + GEN_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND), + 0, // unknown10 + GEN_EXPORT(SpawnRMGEntity, GAME_SPAWN_RMG_ENTITY), + GEN_EXPORT(arioche, GAME_ARIOCHE), + GEN_EXPORT(EntityList, GAME_ENTITY_LIST), + GEN_EXPORT(WriteLevel, GAME_WRITE_LEVEL), + 0, // unknown15 + GEN_EXPORT(unknown16, GAME_UNKNOWN16), + GEN_EXPORT(Save, GAME_SAVE), + GEN_EXPORT(GameAllowedToSaveHere, GAME_GAMEALLOWEDTOSAVEHERE), + GEN_EXPORT(CanPlayCinematic, GAME_CAN_PLAY_CINEMATIC), + GEN_EXPORT(unknown20, GAME_UNKNOWN20), + GEN_EXPORT(unknown21, GAME_UNKNOWN21), + GEN_EXPORT(unknown22, GAME_UNKNOWN22), + GEN_EXPORT(unknown23, GAME_UNKNOWN23), + GEN_EXPORT(unknown24, GAME_UNKNOWN24), + GEN_EXPORT(unknown25, GAME_UNKNOWN25), +}; + #endif // QMM_ARCH_32 diff --git a/src/game_stef2.cpp b/src/game_stef2.cpp index c1a0fab..68f8539 100644 --- a/src/game_stef2.cpp +++ b/src/game_stef2.cpp @@ -28,17 +28,56 @@ Created By: #include "main.h" #include "util.h" -GEN_GAME_QMM_MSGS(STEF2); +struct STEF2_GameSupport : public GameSupport { + virtual const char* EngMsgName(intptr_t msg); + virtual const char* ModMsgName(intptr_t msg); + virtual bool AutoDetect(APIType engine_api); + virtual void* Entry(void* syscall, void*, APIType engine_api); + virtual bool ModLoad(void* entry, APIType mod_api); + virtual void ModUnload(); -GEN_GAME_FUNCS(STEF2); + virtual intptr_t syscall(intptr_t, ...); + virtual intptr_t vmMain(intptr_t, ...); + + virtual const char* DefaultDLLName() { return "game" MOD_DLL; } + virtual const char* DefaultModDir() { return "BASE"; } + virtual const char* GameName() { return "Star Trek: Elite Force II"; } + virtual const char* GameCode() { return "STEF2"; } + +private: + // update the export variables from orig_export + static void update_exports(); + + // track entstrings for our G_GET_ENTITY_TOKEN syscall + static std::vector entity_tokens; + static size_t token_counter; + static void SpawnEntities(const char* mapname, const char* entstring, int levelTime); + + // a copy of the original import struct that comes from the game engine + static game_import_t orig_import; + + // a copy of the original export struct pointer that comes from the mod + static game_export_t* orig_export; + + // struct with lambdas that call QMM's syscall function. this is given to the mod + static game_import_t qmm_import; + + // struct with lambdas that call QMM's vmMain function. this is given to the game engine + static game_export_t qmm_export; + + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = GEN_GAME_QMM_MOD_MSGS(); +}; + +GEN_GAME_OBJ(STEF2); // auto-detection logic for STEF2 -static bool STEF2_AutoDetect(APIType engineapi) { +bool STEF2_GameSupport::AutoDetect(APIType engineapi) { if (engineapi != QMM_API_GETGAMEAPI) return false; - if (!str_striequal(g_gameinfo.qmm_file, game->dllname)) + if (!str_striequal(g_gameinfo.qmm_file, DefaultDLLName())) return false; if (!str_stristr(g_gameinfo.exe_file, "ef")) @@ -48,455 +87,15 @@ static bool STEF2_AutoDetect(APIType engineapi) { } -// a copy of the original import struct that comes from the game engine -static game_import_t orig_import; +// wrapper syscall function that calls actual engine func from orig_import +// this is how QMM and plugins will call into the engine +intptr_t STEF2_GameSupport::syscall(intptr_t cmd, ...) { + QMM_GET_SYSCALL_ARGS(); -// a copy of the original export struct pointer that comes from the mod -static game_export_t* orig_export = nullptr; - -// struct with lambdas that call QMM's syscall function. this is given to the mod -static game_import_t qmm_import = { - GEN_IMPORT(Printf, G_PRINTF), - GEN_IMPORT(DPrintf, G_DPRINTF), - GEN_IMPORT(WPrintf, G_WPRINTF), - GEN_IMPORT(WDPrintf, G_WDPRINTF), - GEN_IMPORT(DebugPrintf, G_DEBUGPRINTF), - GEN_IMPORT(LocalizeFilePath, G_LOCALIZEFILEPATH), - GEN_IMPORT(Error, G_ERROR), - GEN_IMPORT(Milliseconds, G_MILLISECONDS), - GEN_IMPORT(Malloc, G_MALLOC), - GEN_IMPORT(Free, G_FREE), - GEN_IMPORT(cvar, G_CVAR), - GEN_IMPORT(cvar_get, G_CVAR_GET), - GEN_IMPORT(cvar_set, G_CVAR_SET), - GEN_IMPORT(Cvar_VariableStringBuffer, G_CVAR_VARIABLE_STRING_BUFFER), - GEN_IMPORT(Cvar_Register, G_CVAR_REGISTER), - GEN_IMPORT(Cvar_VariableValue, G_CVAR_VARIABLEVALUE), - GEN_IMPORT(Cvar_Update, G_CVAR_UPDATE), - GEN_IMPORT(Cvar_VariableIntegerValue, G_CVAR_VARIABLE_INTEGER_VALUE), - GEN_IMPORT(argc, G_ARGC), - GEN_IMPORT(argv, G_ARGV), - GEN_IMPORT(args, G_ARGS), - GEN_IMPORT(AddCommand, G_ADDCOMMAND), - GEN_IMPORT(FS_ReadFile, G_FS_READFILE), - GEN_IMPORT(FS_Exists, G_FS_EXISTS), - GEN_IMPORT(FS_FreeFile, G_FS_FREEFILE), - GEN_IMPORT(FS_WriteFile, G_FS_WRITEFILE), - GEN_IMPORT(FS_FOpenFileWrite, G_FS_FOPEN_FILE_WRITE), - GEN_IMPORT(FS_FOpenFileAppend, G_FS_FOPEN_FILE_APPEND), - GEN_IMPORT(FS_ListFiles, G_FS_LISTFILES), - GEN_IMPORT(FS_PrepFileWrite, G_FS_PREPFILEWRITE), - GEN_IMPORT(FS_Write, G_FS_WRITE), - GEN_IMPORT(FS_Read, G_FS_READ), - GEN_IMPORT(FS_FCloseFile, G_FS_FCLOSE_FILE), - GEN_IMPORT(FS_FTell, G_FS_FTELL), - GEN_IMPORT(FS_FSeek, G_FS_FSEEK), - GEN_IMPORT(FS_Flush, G_FS_FLUSH), - GEN_IMPORT(FS_DeleteFile, G_FS_DELETEFILE), - GEN_IMPORT(FS_GetFileList, G_FS_GETFILELIST), - GEN_IMPORT(GetArchiveFileName, G_GETARCHIVEFILENAME), - GEN_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND), - GEN_IMPORT_2(DebugGraph, G_DEBUGGRAPH, void, float, int), - GEN_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND), - GEN_IMPORT(GetNumFreeReliableServerCommands, G_GETNUMFREERELIABLESERVERCOMMANDS), - GEN_IMPORT(setConfigstring, G_SET_CONFIGSTRING), - GEN_IMPORT(getConfigstring, G_GET_CONFIGSTRING), - GEN_IMPORT(setUserinfo, G_SET_USERINFO), - GEN_IMPORT(getUserinfo, G_GET_USERINFO), - GEN_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL), - GEN_IMPORT(trace, G_TRACE), - GEN_IMPORT(fulltrace, G_FULLTRACE), - GEN_IMPORT(pointcontents, G_POINT_CONTENTS), - GEN_IMPORT(pointbrushnum, G_POINTBRUSHNUM), - GEN_IMPORT(inPVS, G_IN_PVS), - GEN_IMPORT(inPVSIgnorePortals, G_IN_PVS_IGNOREPORTALS), - GEN_IMPORT(AdjustAreaPortalState, G_ADJUSTAREAPORTALSTATE), - GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), - GEN_IMPORT(GetLightingGroup, G_GETLIGHTINGGROUP), - GEN_IMPORT_2(SetDynamicLight, G_SETDYNAMICLIGHT, void, int, float), - GEN_IMPORT_2(SetDynamicLightDefault, G_SETDYNAMICLIGHTDEFAULT, void, int, float), - GEN_IMPORT(SetWindDirection, G_SETWINDDIRECTION), - GEN_IMPORT_1(SetWindIntensity, G_SETWINDINTENSITY, void, float), - GEN_IMPORT(SetWeatherInfo, G_SETWEATHERINFO), - GEN_IMPORT_1(SetTimeScale, G_SETTIMESCALE, void, float), - GEN_IMPORT(linkentity, G_LINKENTITY), - GEN_IMPORT(unlinkentity, G_UNLINKENTITY), - GEN_IMPORT(AreaEntities, G_AREAENTITIES), - GEN_IMPORT(ClipToEntity, G_CLIPTOENTITY), - GEN_IMPORT(objectivenameindex, G_OBJECTIVENAMEINDEX), - GEN_IMPORT(archetypeindex, G_ARCHETYPEINDEX), - GEN_IMPORT(imageindex, G_IMAGEINDEX), - GEN_IMPORT(failedcondition, G_FAILEDCONDITION), - GEN_IMPORT(itemindex, G_ITEMINDEX), - GEN_IMPORT(soundindex, G_SOUNDINDEX), - GEN_IMPORT(modelindex, G_MODELINDEX), - GEN_IMPORT(SetLightStyle, G_SETLIGHTSTYLE), - GEN_IMPORT(GameDir, G_GAMEDIR), - GEN_IMPORT(IsModel, G_ISMODEL), - GEN_IMPORT(setmodel, G_SETMODEL), - GEN_IMPORT(setviewmodel, G_SETVIEWMODEL), - GEN_IMPORT(NumAnims, G_NUMANIMS), - GEN_IMPORT(NumSkins, G_NUMSKINS), - GEN_IMPORT(NumSurfaces, G_NUMSURFACES), - GEN_IMPORT(NumTags, G_NUMTAGS), - GEN_IMPORT(NumMorphs, G_NUMMORPHS), - GEN_IMPORT(InitCommands, G_INITCOMMANDS), - GEN_IMPORT_4(CalculateBounds, G_CALCULATEBOUNDS, void, int, float, vec3_t, vec3_t), - GEN_IMPORT(TIKI_CacheAnim, G_TIKI_CACHEANIM), - GEN_IMPORT(Anim_NameForNum, G_ANIM_NAMEFORNUM), - GEN_IMPORT(Anim_NumForName, G_ANIM_NUMFORNAME), - GEN_IMPORT(Anim_Random, G_ANIM_RANDOM), - GEN_IMPORT(Anim_NumFrames, G_ANIM_NUMFRAMES), - GEN_IMPORT(Anim_Time, G_ANIM_TIME), - GEN_IMPORT(Anim_Delta, G_ANIM_DELTA), - GEN_IMPORT(Anim_AbsoluteDelta, G_ANIM_ABSOLUTEDELTA), - GEN_IMPORT(Anim_Flags, G_ANIM_FLAGS), - GEN_IMPORT(Anim_HasCommands, G_ANIM_HASCOMMANDS), - GEN_IMPORT(Frame_Commands, G_FRAME_COMMANDS), - GEN_IMPORT(Frame_Delta, G_FRAME_DELTA), - GEN_IMPORT(Frame_Time, G_FRAME_TIME), - GEN_IMPORT_6(Frame_Bounds, G_FRAME_BOUNDS, void, int, int, int, float, vec3_t, vec3_t), - GEN_IMPORT(Surface_NameToNum, G_SURFACE_NAMETONUM), - GEN_IMPORT(Surface_NumToName, G_SURFACE_NUMTONAME), - GEN_IMPORT(Surface_Flags, G_SURFACE_FLAGS), - GEN_IMPORT(Surface_NumSkins, G_SURFACE_NUMSKINS), - GEN_IMPORT(Morph_NumForName, G_MORPH_NUMFORNAME), - GEN_IMPORT(Morph_NameForNum, G_MORPH_NAMEFORNUM), - GEN_IMPORT(GetExpression, G_GETEXPRESSION), - GEN_IMPORT(Tag_NumForName, G_TAG_NUMFORNAME), - GEN_IMPORT(Tag_NameForNum, G_TAG_NAMEFORNUM), - GEN_IMPORT(Tag_Orientation, G_TAG_ORIENTATION), - GEN_IMPORT(Tag_OrientationEx, G_TAG_ORIENTATIONEX), // todo: change types to actually match float, but also need to return an intptr_t instead of orientation_t - GEN_IMPORT(Bone_GetParentNum, G_BONE_GETPARENTNUM), - GEN_IMPORT(Alias_Add, G_ALIAS_ADD), - GEN_IMPORT(Alias_FindRandom, G_ALIAS_FINDRANDOM), - GEN_IMPORT(Alias_Find, G_ALIAS_FIND), - GEN_IMPORT(Alias_Dump, G_ALIAS_DUMP), - GEN_IMPORT(Alias_Clear, G_ALIAS_CLEAR), - GEN_IMPORT(Alias_FindDialog, G_ALIAS_FINDDIALOG), - GEN_IMPORT(Alias_FindSpecificAnim, G_ALIAS_FINDSPECIFICANIM), - GEN_IMPORT(Alias_CheckLoopAnim, G_ALIAS_CHECKLOOPANIM), - GEN_IMPORT(Alias_GetList, G_ALIAS_GETLIST), - GEN_IMPORT(Alias_UpdateDialog, G_ALIAS_UPDATEDIALOG), - GEN_IMPORT(Alias_AddActorDialog, G_ALIAS_ADDACTORDIALOG), - GEN_IMPORT(NameForNum, G_NAMEFORNUM), - GEN_IMPORT(GlobalAlias_Add, G_GLOBALALIAS_ADD), - GEN_IMPORT(GlobalAlias_FindRandom, G_GLOBALALIAS_FINDRANDOM), - GEN_IMPORT(GlobalAlias_Find, G_GLOBALALIAS_FIND), - GEN_IMPORT(GlobalAlias_Dump, G_GLOBALALIAS_DUMP), - GEN_IMPORT(GlobalAlias_Clear, G_GLOBALALIAS_CLEAR), - GEN_IMPORT(isClientActive, G_ISCLIENTACTIVE), - GEN_IMPORT(centerprintf, G_CENTERPRINTF), - GEN_IMPORT(locationprintf, G_LOCATIONPRINTF), - GEN_IMPORT(Sound, G_SOUND), - GEN_IMPORT(StopSound, G_STOPSOUND), - GEN_IMPORT(SoundLength, G_SOUNDLENGTH), - GEN_IMPORT(GetNextMorphTarget, G_GETNEXTMORPHTARGET), - GEN_IMPORT(CalcCRC, G_CALCCRC), - nullptr, // DebugLines - nullptr, // numDebugLines - GEN_IMPORT(LocateGameData, G_LOCATE_GAME_DATA), - GEN_IMPORT(SetFarPlane, G_SETFARPLANE), - GEN_IMPORT(TikiReload, G_TIKIRELOAD), - GEN_IMPORT(TikiLoadFromTS, G_TIKILOADFROMTS), - GEN_IMPORT(ToolServerGetData, G_TOOLSERVERGETDATA), - GEN_IMPORT(SetSkyPortal, G_SETSKYPORTAL), - GEN_IMPORT(WidgetPrintf, G_WIDGETPRINTF), - GEN_IMPORT(ProcessLoadingScreen, G_PROCESSLOADINGSCREEN), - GEN_IMPORT(MObjective_GetDescription, G_MOBJECTIVE_GETDESCRIPTION), - GEN_IMPORT(MObjective_SetDescription, G_MOBJECTIVE_SETDESCRIPTION), - GEN_IMPORT(MObjective_GetShowObjective, G_MOBJECTIVE_GETSHOWOBJECTIVE), - GEN_IMPORT(MObjective_SetShowObjective, G_MOBJECTIVE_SETSHOWOBJECTIVE), - GEN_IMPORT(MObjective_GetObjectiveComplete, G_MOBJECTIVE_GETOBJECTIVECOMPLETE), - GEN_IMPORT(MObjective_SetObjectiveComplete, G_MOBJECTIVE_SETOBJECTIVECOMPLETE), - GEN_IMPORT(MObjective_GetObjectiveFailed, G_MOBJECTIVE_GETOBJECTIVEFAILED), - GEN_IMPORT(MObjective_SetObjectiveFailed, G_MOBJECTIVE_SETOBJECTIVEFAILED), - GEN_IMPORT(MObjective_GetNameFromIndex, G_MOBJECTIVE_GETNAMEFROMINDEX), - GEN_IMPORT(MObjective_GetIndexFromName, G_MOBJECTIVE_GETINDEXFROMNAME), - GEN_IMPORT(MObjective_NewObjective, G_MOBJECTIVE_NEWOBJECTIVE), - GEN_IMPORT(MObjective_ClearObjectiveList, G_MOBJECTIVE_CLEAROBJECTIVELIST), - GEN_IMPORT(MObjective_ParseObjectiveFile, G_MOBJECTIVE_PARSEOBJECTIVEFILE), - GEN_IMPORT(MObjective_Update, G_MOBJECTIVE_UPDATE), - GEN_IMPORT(MObjective_GetNumObjectives, G_MOBJECTIVE_GETNUMOBJECTIVES), - GEN_IMPORT(MObjective_GetNumActiveObjectives, G_MOBJECTIVE_GETNUMACTIVEOBJECTIVES), - GEN_IMPORT(MObjective_GetNumCompleteObjectives, G_MOBJECTIVE_GETNUMCOMPLETEOBJECTIVES), - GEN_IMPORT(MObjective_GetNumFailedObjectives, G_MOBJECTIVE_GETNUMFAILEDOBJECTIVES), - GEN_IMPORT(MObjective_GetNumIncompleteObjectives, G_MOBJECTIVE_GETNUMINCOMPLETEOBJECTIVES), - GEN_IMPORT(MI_GetShader, G_MI_GETSHADER), - GEN_IMPORT(MI_SetShader, G_MI_SETSHADER), - GEN_IMPORT(MI_GetInformationData, G_MI_GETINFORMATIONDATA), - GEN_IMPORT(MI_SetInformationData, G_MI_SETINFORMATIONDATA), - GEN_IMPORT(MI_GetNameFromIndex, G_MI_GETNAMEFROMINDEX), - GEN_IMPORT(MI_GetIndexFromName, G_MI_GETINDEXFROMNAME), - GEN_IMPORT(MI_NewInformation, G_MI_NEWINFORMATION), - GEN_IMPORT(MI_ClearInformationList, G_MI_CLEARINFORMATIONLIST), - GEN_IMPORT(MI_SetShowInformation, G_MI_SETSHOWINFORMATION), - GEN_IMPORT(MI_GetShowInformation, G_MI_GETSHOWINFORMATION), - GEN_IMPORT(SR_InitializeStringResource, G_SR_INITIALIZESTRINGRESOURCE), - GEN_IMPORT(SR_UninitializeStringResource, G_SR_UNINITIALIZESTRINGRESOURCE), - GEN_IMPORT(SR_LoadLevelStrings, G_SR_LOADLEVELSTRINGS), - GEN_IMPORT(GetViewModeMask, G_GETVIEWMODEMASK), - GEN_IMPORT(GetViewModeClassMask, G_GETVIEWMODECLASSMASK), - GEN_IMPORT(GetViewModeSendInMode, G_GETVIEWMODESENDINMODE), - GEN_IMPORT(GetViewModeSendNotInMode, G_GETVIEWMODESENDNOTINMODE), - GEN_IMPORT(GetViewModeScreenBlend, G_GETVIEWMODESCREENBLEND), - GEN_IMPORT(GetLevelDefs, G_GETLEVELDEFS), - GEN_IMPORT(areSublevels, G_ARESUBLEVELS), - GEN_IMPORT(SurfaceTypeToName, G_SURFACETYPETONAME), - GEN_IMPORT(AAS_EntityInfo, G_AAS_ENTITYINFO), - GEN_IMPORT(AAS_Initialized, G_AAS_INITIALIZED), - GEN_IMPORT(AAS_PresenceTypeBoundingBox, G_AAS_PRESENCETYPEBOUNDINGBOX), - GEN_IMPORT(AAS_Time, G_AAS_TIME), - GEN_IMPORT(AAS_PointAreaNum, G_AAS_POINTAREANUM), - GEN_IMPORT(AAS_PointReachabilityAreaIndex, G_AAS_POINTREACHABILITYAREAINDEX), - GEN_IMPORT(AAS_TraceAreas, G_AAS_TRACEAREAS), - GEN_IMPORT(AAS_BBoxAreas, G_AAS_BBOXAREAS), - GEN_IMPORT(AAS_AreaInfo, G_AAS_AREAINFO), - GEN_IMPORT(AAS_PointContents, G_AAS_POINTCONTENTS), - GEN_IMPORT(AAS_NextBSPEntity, G_AAS_NEXTBSPENTITY), - GEN_IMPORT(AAS_ValueForBSPEpairKey, G_AAS_VALUEFORBSPEPAIRKEY), - GEN_IMPORT(AAS_VectorForBSPEpairKey, G_AAS_VECTORFORBSPEPAIRKEY), - GEN_IMPORT(AAS_FloatForBSPEpairKey, G_AAS_FLOATFORBSPEPAIRKEY), - GEN_IMPORT(AAS_IntForBSPEpairKey, G_AAS_INTFORBSPEPAIRKEY), - GEN_IMPORT(AAS_AreaReachability, G_AAS_AREAREACHABILITY), - GEN_IMPORT(AAS_AreaTravelTimeToGoalArea, G_AAS_AREATRAVELTIMETOGOALAREA), - GEN_IMPORT(AAS_EnableRoutingArea, G_AAS_ENABLEROUTINGAREA), - GEN_IMPORT(AAS_PredictRoute, G_AAS_PREDICTROUTE), - GEN_IMPORT(AAS_AlternativeRouteGoals, G_AAS_ALTERNATIVEROUTEGOALS), - GEN_IMPORT(AAS_Swimming, G_AAS_SWIMMING), - GEN_IMPORT_13(AAS_PredictClientMovement, G_AAS_PREDICTCLIENTMOVEMENT, int, struct aas_clientmove_s*, int, vec3_t, int, int, vec3_t, vec3_t, int, int, float, int, int, int), - GEN_IMPORT(EA_Command, G_EA_COMMAND), - GEN_IMPORT(EA_Say, G_EA_SAY), - GEN_IMPORT(EA_SayTeam, G_EA_SAYTEAM), - GEN_IMPORT(EA_Action, G_EA_ACTION), - GEN_IMPORT(EA_Gesture, G_EA_GESTURE), - GEN_IMPORT(EA_Talk, G_EA_TALK), - GEN_IMPORT(EA_ToggleFireState, G_EA_TOGGLEFIRESTATE), - GEN_IMPORT(EA_Attack, G_EA_ATTACK), - GEN_IMPORT(EA_Use, G_EA_USE), - GEN_IMPORT(EA_Respawn, G_EA_RESPAWN), - GEN_IMPORT(EA_MoveUp, G_EA_MOVEUP), - GEN_IMPORT(EA_MoveDown, G_EA_MOVEDOWN), - GEN_IMPORT(EA_MoveForward, G_EA_MOVEFORWARD), - GEN_IMPORT(EA_MoveBack, G_EA_MOVEBACK), - GEN_IMPORT(EA_MoveLeft, G_EA_MOVELEFT), - GEN_IMPORT(EA_MoveRight, G_EA_MOVERIGHT), - GEN_IMPORT(EA_Crouch, G_EA_CROUCH), - GEN_IMPORT(EA_SelectWeapon, G_EA_SELECTWEAPON), - GEN_IMPORT(EA_Jump, G_EA_JUMP), - GEN_IMPORT(EA_DelayedJump, G_EA_DELAYEDJUMP), - GEN_IMPORT_3(EA_Move, G_EA_MOVE, void, int, vec3_t, float), - GEN_IMPORT(EA_View, G_EA_VIEW), - GEN_IMPORT_2(EA_EndRegular, G_EA_ENDREGULAR, void, int, float), - GEN_IMPORT_3(EA_GetInput, G_EA_GETINPUT, void, int, float, bot_input_t*), - GEN_IMPORT(EA_ResetInput, G_EA_RESETINPUT), - GEN_IMPORT_2(BotLoadCharacter, G_BOTLOADCHARACTER, int, char*, float), - GEN_IMPORT(BotFreeCharacter, G_BOTFREECHARACTER), - GEN_IMPORT(Characteristic_Float, G_CHARACTERISTIC_FLOAT), - GEN_IMPORT(Characteristic_BFloat, G_CHARACTERISTIC_BFLOAT), - GEN_IMPORT(Characteristic_Integer, G_CHARACTERISTIC_INTEGER), - GEN_IMPORT(Characteristic_BInteger, G_CHARACTERISTIC_BINTEGER), - GEN_IMPORT(Characteristic_String, G_CHARACTERISTIC_STRING), - GEN_IMPORT(BotAllocChatState, G_BOTALLOCCHATSTATE), - GEN_IMPORT(BotFreeChatState, G_BOTFREECHATSTATE), - GEN_IMPORT(BotQueueConsoleMessage, G_BOTQUEUECONSOLEMESSAGE), - GEN_IMPORT(BotRemoveConsoleMessage, G_BOTREMOVECONSOLEMESSAGE), - GEN_IMPORT(BotNextConsoleMessage, G_BOTNEXTCONSOLEMESSAGE), - GEN_IMPORT(BotNumConsoleMessages, G_BOTNUMCONSOLEMESSAGES), - GEN_IMPORT(BotInitialChat, G_BOTINITIALCHAT), - GEN_IMPORT(BotNumInitialChats, G_BOTNUMINITIALCHATS), - GEN_IMPORT(BotReplyChat, G_BOTREPLYCHAT), - GEN_IMPORT(BotChatLength, G_BOTCHATLENGTH), - GEN_IMPORT(BotEnterChat, G_BOTENTERCHAT), - GEN_IMPORT(BotGetChatMessage, G_BOTGETCHATMESSAGE), - GEN_IMPORT(StringContains, G_STRINGCONTAINS), - GEN_IMPORT(BotFindMatch, G_BOTFINDMATCH), - GEN_IMPORT(BotMatchVariable, G_BOTMATCHVARIABLE), - GEN_IMPORT(UnifyWhiteSpaces, G_UNIFYWHITESPACES), - GEN_IMPORT(BotReplaceSynonyms, G_BOTREPLACESYNONYMS), - GEN_IMPORT(BotLoadChatFile, G_BOTLOADCHATFILE), - GEN_IMPORT(BotSetChatGender, G_BOTSETCHATGENDER), - GEN_IMPORT(BotSetChatName, G_BOTSETCHATNAME), - GEN_IMPORT(BotResetGoalState, G_BOTRESETGOALSTATE), - GEN_IMPORT(BotResetAvoidGoals, G_BOTRESETAVOIDGOALS), - GEN_IMPORT(BotRemoveFromAvoidGoals, G_BOTREMOVEFROMAVOIDGOALS), - GEN_IMPORT(BotPushGoal, G_BOTPUSHGOAL), - GEN_IMPORT(BotPopGoal, G_BOTPOPGOAL), - GEN_IMPORT(BotEmptyGoalStack, G_BOTEMPTYGOALSTACK), - GEN_IMPORT(BotDumpAvoidGoals, G_BOTDUMPAVOIDGOALS), - GEN_IMPORT(BotDumpGoalStack, G_BOTDUMPGOALSTACK), - GEN_IMPORT(BotGoalName, G_BOTGOALNAME), - GEN_IMPORT(BotGetTopGoal, G_BOTGETTOPGOAL), - GEN_IMPORT(BotGetSecondGoal, G_BOTGETSECONDGOAL), - GEN_IMPORT(BotChooseLTGItem, G_BOTCHOOSELTGITEM), - GEN_IMPORT_6(BotChooseNBGItem, G_BOTCHOOSENBGITEM, int, int, vec3_t, int*, int, struct bot_goal_s*, float), - GEN_IMPORT(BotTouchingGoal, G_BOTTOUCHINGGOAL), - GEN_IMPORT(BotItemGoalInVisButNotVisible, G_BOTITEMGOALINVISBUTNOTVISIBLE), - GEN_IMPORT(BotGetLevelItemGoal, G_BOTGETLEVELITEMGOAL), - GEN_IMPORT(BotGetNextCampSpotGoal, G_BOTGETNEXTCAMPSPOTGOAL), - GEN_IMPORT(BotGetMapLocationGoal, G_BOTGETMAPLOCATIONGOAL), - GEN_IMPORT(BotAvoidGoalTime, G_BOTAVOIDGOALTIME), - GEN_IMPORT_3(BotSetAvoidGoalTime, G_BOTSETAVOIDGOALTIME, void, int, int, float), - GEN_IMPORT(BotInitLevelItems, G_BOTINITLEVELITEMS), - GEN_IMPORT(BotUpdateEntityItems, G_BOTUPDATEENTITYITEMS), - GEN_IMPORT(BotLoadItemWeights, G_BOTLOADITEMWEIGHTS), - GEN_IMPORT(BotFreeItemWeights, G_BOTFREEITEMWEIGHTS), - GEN_IMPORT(BotInterbreedGoalFuzzyLogic, G_BOTINTERBREEDGOALFUZZYLOGIC), - GEN_IMPORT(BotSaveGoalFuzzyLogic, G_BOTSAVEGOALFUZZYLOGIC), - GEN_IMPORT_2(BotMutateGoalFuzzyLogic, G_BOTMUTATEGOALFUZZYLOGIC, void, int, float), - GEN_IMPORT(BotAllocGoalState, G_BOTALLOCGOALSTATE), - GEN_IMPORT(BotFreeGoalState, G_BOTFREEGOALSTATE), - GEN_IMPORT(BotResetMoveState, G_BOTRESETMOVESTATE), - GEN_IMPORT(BotMoveToGoal, G_BOTMOVETOGOAL), - GEN_IMPORT_4(BotMoveInDirection, G_BOTMOVEINDIRECTION, int, int, vec3_t, float, int), - GEN_IMPORT(BotResetAvoidReach, G_BOTRESETAVOIDREACH), - GEN_IMPORT(BotResetLastAvoidReach, G_BOTRESETLASTAVOIDREACH), - GEN_IMPORT(BotReachabilityArea, G_BOTREACHABILITYAREA), - GEN_IMPORT_5(BotMovementViewTarget, G_BOTMOVEMENTVIEWTARGET, int, int, struct bot_goal_s*, int, float, vec3_t), - GEN_IMPORT(BotPredictVisiblePosition, G_BOTPREDICTVISIBLEPOSITION), - GEN_IMPORT(BotAllocMoveState, G_BOTALLOCMOVESTATE), - GEN_IMPORT(BotFreeMoveState, G_BOTFREEMOVESTATE), - GEN_IMPORT(BotInitMoveState, G_BOTINITMOVESTATE), - GEN_IMPORT_4(BotAddAvoidSpot, G_BOTADDAVOIDSPOT, void, int, vec3_t, float, int), - GEN_IMPORT(BotChooseBestFightWeapon, G_BOTCHOOSEBESTFIGHTWEAPON), - GEN_IMPORT(BotGetWeaponInfo, G_BOTGETWEAPONINFO), - GEN_IMPORT(BotLoadWeaponWeights, G_BOTLOADWEAPONWEIGHTS), - GEN_IMPORT(BotAllocWeaponState, G_BOTALLOCWEAPONSTATE), - GEN_IMPORT(BotFreeWeaponState, G_BOTFREEWEAPONSTATE), - GEN_IMPORT(BotResetWeaponState, G_BOTRESETWEAPONSTATE), - GEN_IMPORT(GeneticParentsAndChildSelection, G_GENETICPARENTSANDCHILDSELECTION), - GEN_IMPORT(Print, G_BOTPRINT), - GEN_IMPORT(PointContents, G_BOTPOINTCONTENTS), - GEN_IMPORT(BSPEntityData, G_BSPENTITYDATA), - GEN_IMPORT(BSPModelMinsMaxsOrigin, G_BSPMODELMINSMAXSORIGIN), - GEN_IMPORT(BotClientCommand, G_BOTCLIENTCOMMAND), - GEN_IMPORT(AvailableMemory, G_AVAILABLEMEMORY), - GEN_IMPORT(HunkAlloc, G_HUNKALLOC), - GEN_IMPORT(FS_FOpenFile, G_FS_FOPEN_FILE), - GEN_IMPORT(FS_Seek, G_FS_SEEK), - GEN_IMPORT(DebugLineCreate, G_DEBUGLINECREATE), - GEN_IMPORT(DebugLineDelete, G_DEBUGLINEDELETE), - GEN_IMPORT(DebugLineShow, G_DEBUGLINESHOW), - GEN_IMPORT(DebugPolygonCreate, G_DEBUGPOLYGONCREATE), - GEN_IMPORT(DebugPolygonDelete, G_DEBUGPOLYGONDELETE), - GEN_IMPORT(DropClient, G_DROP_CLIENT), - GEN_IMPORT(SV_GetServerinfo, G_SV_GETSERVERINFO), - GEN_IMPORT(BotAllocateClient, G_BOTALLOCATECLIENT), - GEN_IMPORT(BotGetSnapshotEntity, G_BOTGETSNAPSHOTENTITY), - GEN_IMPORT(BotGetConsoleMessage, G_BOTGETCONSOLEMESSAGE), - GEN_IMPORT(BotLibSetup, G_BOTLIBSETUP), - GEN_IMPORT(BotLibShutdown, G_BOTLIBSHUTDOWN), - GEN_IMPORT(BotLibVarSet, G_BOTLIBVARSET), - GEN_IMPORT(BotLibVarGet, G_BOTLIBVARGET), - GEN_IMPORT(PC_AddGlobalDefine, G_PC_ADDGLOBALDEFINE), - GEN_IMPORT(PC_LoadSourceHandle, G_PC_LOADSOURCEHANDLE), - GEN_IMPORT(PC_FreeSourceHandle, G_PC_FREESOURCEHANDLE), - GEN_IMPORT(PC_SourceFileAndLine, G_PC_SOURCEFILEANDLINE), - GEN_IMPORT_1(BotLibStartFrame, G_BOTLIBSTARTFRAME, int, float), - GEN_IMPORT(BotLibLoadMap, G_BOTLIBLOADMAP), - GEN_IMPORT(BotLibUpdateEntity, G_BOTLIBUPDATEENTITY), - GEN_IMPORT(Test, G_TEST), - GEN_IMPORT(BotUserCommand, G_BOTUSERCOMMAND), -}; - - -// these are "pre" hooks for storing some data for polyfills. -// we need these to be called BEFORE plugins' prehooks get called so they have to be done in the qmm_export table - -// track entstrings for our G_GET_ENTITY_TOKEN syscall -static std::vector entity_tokens; -static size_t token_counter = 0; -static void STEF2_SpawnEntities(const char* mapname, const char* entstring, int levelTime) { - if (entstring) { - entity_tokens = util_parse_entstring(entstring); - token_counter = 0; - } - cgame.is_from_QMM = true; - vmMain(GAME_SPAWN_ENTITIES, mapname, entstring, levelTime); -} - - -// struct with lambdas that call QMM's vmMain function. this is given to the game engine -static game_export_t qmm_export = { - GAME_API_VERSION, // apiversion - GEN_EXPORT(Init, GAME_INIT), - GEN_EXPORT(Shutdown, GAME_SHUTDOWN), - GEN_EXPORT(Cleanup, GAME_CLEANUP), - STEF2_SpawnEntities, - GEN_EXPORT(PostLoad, GAME_POSTLOAD), - GEN_EXPORT(PostSublevelLoad, GAME_POSTSUBLEVELLOAD), - GEN_EXPORT(ClientConnect, GAME_CLIENT_CONNECT), - GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), - GEN_EXPORT(ClientUserinfoChanged, GAME_CLIENT_USERINFO_CHANGED), - GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), - GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), - GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), - GEN_EXPORT(PrepFrame, GAME_PREP_FRAME), - GEN_EXPORT(RunFrame, GAME_RUN_FRAME), - GEN_EXPORT(SendEntity, GAME_SEND_ENTITY), - GEN_EXPORT(UpdateEntityStateForClient, GAME_UPDATE_ENTITYSTATE_FOR_CLIENT), - GEN_EXPORT(UpdatePlayerStateForClient, GAME_UPDATE_PLAYERSTATE_FOR_CLIENT), - GEN_EXPORT(ExtraEntitiesToSend, GAME_EXTRA_ENTITIES_TO_SEND), - GEN_EXPORT(GetEntityCurrentAnimFrame, GAME_GETENTITY_CURRENT_ANIMFRAME), - GEN_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND), - GEN_EXPORT(WritePersistant, GAME_WRITE_PERSISTANT), - GEN_EXPORT(ReadPersistant, GAME_READ_PERSISTANT), - GEN_EXPORT(WriteLevel, GAME_WRITE_LEVEL), - GEN_EXPORT(ReadLevel, GAME_READ_LEVEL), - GEN_EXPORT(LevelArchiveValid, GAME_LEVEL_ARCHIVE_VALID), - GEN_EXPORT(inMultiplayerGame, GAME_IN_MULTIPLAYER_GAME), - GEN_EXPORT(isDefined, GAME_IS_DEFINED), - GEN_EXPORT(getDefine, GAME_GET_DEFINE), - GEN_EXPORT(BotAIStartFrame, BOTAI_START_FRAME), - GEN_EXPORT(AddBot_f, GAME_ADDBOT_F), - GEN_EXPORT(GetTotalGameFrames, GAME_GETTOTALGAMEFRAMES), - // the engine won't use these until after Init, so we can fill these in after each call into the mod's export functions ("vmMain") - nullptr, // gentities - 0, // gentitySize - 0, // num_entities - 0, // max_entities - nullptr, // errorMessage -}; - - -// update the export variables from orig_export -static void update_exports() { - if (!orig_export) - return; - - bool changed = false; - - // if entity data changed, we need to send a G_LOCATE_GAME_DATA so plugins can hook it - if (qmm_export.gentities != orig_export->gentities - || qmm_export.gentitySize != orig_export->gentitySize - || qmm_export.num_entities != orig_export->num_entities - ) { - changed = true; - } - - qmm_export.gentities = orig_export->gentities; - qmm_export.gentitySize = orig_export->gentitySize; - qmm_export.num_entities = orig_export->num_entities; - qmm_export.max_entities = orig_export->max_entities; - qmm_export.error_message = orig_export->error_message; - - if (changed) { - // this will trigger this message to be fired to plugins, and then it will be handled - // by the empty "case G_LOCATE_GAME_DATA" in MOHAA_syscall - qmm_syscall(G_LOCATE_GAME_DATA, (intptr_t)qmm_export.gentities, qmm_export.num_entities, qmm_export.gentitySize, nullptr, 0); - } -} - - -// wrapper syscall function that calls actual engine func from orig_import -// this is how QMM and plugins will call into the engine -static intptr_t STEF2_syscall(intptr_t cmd, ...) { - QMM_GET_SYSCALL_ARGS(); - -#ifdef _DEBUG - if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STEF2_syscall({} {}) called\n", STEF2_EngMsgName(cmd), cmd); -#endif +#ifdef _DEBUG + if (cmd != G_PRINT) + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STEF2_GameSupport::syscall({} {}) called\n", EngMsgName(cmd), cmd); +#endif // update export vars before calling into the engine update_exports(); @@ -886,7 +485,7 @@ static intptr_t STEF2_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STEF2_syscall({} {}) returning {}\n", STEF2_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STEF2_GameSupport::syscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; @@ -895,11 +494,11 @@ static intptr_t STEF2_syscall(intptr_t cmd, ...) { // wrapper vmMain function that calls actual mod func from orig_export // this is how QMM and plugins will call into the mod -static intptr_t STEF2_vmMain(intptr_t cmd, ...) { +intptr_t STEF2_GameSupport::vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STEF2_vmMain({} {}) called\n", STEF2_ModMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STEF2_GameSupport::vmMain({} {}) called\n", ModMsgName(cmd), cmd); #endif if (!orig_export) @@ -957,15 +556,15 @@ static intptr_t STEF2_vmMain(intptr_t cmd, ...) { update_exports(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STEF2_vmMain({} {}) returning {}\n", STEF2_ModMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STEF2_GameSupport::vmMain({} {}) returning {}\n", ModMsgName(cmd), cmd, ret); #endif return ret; } -static void* STEF2_Entry(void* import, void*, APIType) { - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STEF2_Entry({}) called\n", import); +void* STEF2_GameSupport::Entry(void* import, void*, APIType) { + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STEF2_GameSupport::Entry({}) called\n", import); // original import struct from engine // the struct given by the engine goes out of scope after this returns so we have to copy the whole thing @@ -976,13 +575,7 @@ static void* STEF2_Entry(void* import, void*, APIType) { qmm_import.DebugLines = orig_import.DebugLines; qmm_import.numDebugLines = orig_import.numDebugLines; - // pointer to wrapper vmMain function that calls actual mod func from orig_export - g_gameinfo.pfnvmMain = STEF2_vmMain; - - // pointer to wrapper syscall function that calls actual engine func from orig_import - g_gameinfo.pfnsyscall = STEF2_syscall; - - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STEF2_Entry({}) returning {}\n", import, fmt::ptr(&qmm_export)); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STEF2_GameSupport::Entry({}) returning {}\n", import, fmt::ptr(&qmm_export)); // struct full of export lambdas to QMM's vmMain // this gets returned to the game engine, but we haven't loaded the mod yet. @@ -991,7 +584,7 @@ static void* STEF2_Entry(void* import, void*, APIType) { } -static bool STEF2_ModLoad(void* entry, APIType modapi) { +bool STEF2_GameSupport::ModLoad(void* entry, APIType modapi) { if (modapi != QMM_API_GETGAMEAPI) return false; @@ -1001,13 +594,12 @@ static bool STEF2_ModLoad(void* entry, APIType modapi) { return !!orig_export; } - -static void STEF2_ModUnload() { +void STEF2_GameSupport::ModUnload() { orig_export = nullptr; } -static const char* STEF2_EngMsgName(intptr_t cmd) { +const char* STEF2_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINTF); GEN_CASE(G_DPRINTF); @@ -1360,7 +952,7 @@ static const char* STEF2_EngMsgName(intptr_t cmd) { } -static const char* STEF2_ModMsgName(intptr_t cmd) { +const char* STEF2_GameSupport::ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAMEV_APIVERSION); GEN_CASE(GAME_INIT); @@ -1404,4 +996,437 @@ static const char* STEF2_ModMsgName(intptr_t cmd) { } } + +void STEF2_GameSupport::update_exports() { + if (!orig_export) + return; + + bool changed = false; + + // if entity data changed, we need to send a G_LOCATE_GAME_DATA so plugins can hook it + if (qmm_export.gentities != orig_export->gentities + || qmm_export.gentitySize != orig_export->gentitySize + || qmm_export.num_entities != orig_export->num_entities + ) { + changed = true; + } + + qmm_export.gentities = orig_export->gentities; + qmm_export.gentitySize = orig_export->gentitySize; + qmm_export.num_entities = orig_export->num_entities; + qmm_export.max_entities = orig_export->max_entities; + qmm_export.error_message = orig_export->error_message; + + if (changed) { + // this will trigger this message to be fired to plugins, and then it will be handled + // by the empty "case G_LOCATE_GAME_DATA" in MOHAA_syscall + qmm_syscall(G_LOCATE_GAME_DATA, (intptr_t)qmm_export.gentities, qmm_export.num_entities, qmm_export.gentitySize, nullptr, 0); + } +} + + +game_import_t STEF2_GameSupport::orig_import; + + +game_export_t* STEF2_GameSupport::orig_export = nullptr; + + +game_import_t STEF2_GameSupport::qmm_import = { + GEN_IMPORT(Printf, G_PRINTF), + GEN_IMPORT(DPrintf, G_DPRINTF), + GEN_IMPORT(WPrintf, G_WPRINTF), + GEN_IMPORT(WDPrintf, G_WDPRINTF), + GEN_IMPORT(DebugPrintf, G_DEBUGPRINTF), + GEN_IMPORT(LocalizeFilePath, G_LOCALIZEFILEPATH), + GEN_IMPORT(Error, G_ERROR), + GEN_IMPORT(Milliseconds, G_MILLISECONDS), + GEN_IMPORT(Malloc, G_MALLOC), + GEN_IMPORT(Free, G_FREE), + GEN_IMPORT(cvar, G_CVAR), + GEN_IMPORT(cvar_get, G_CVAR_GET), + GEN_IMPORT(cvar_set, G_CVAR_SET), + GEN_IMPORT(Cvar_VariableStringBuffer, G_CVAR_VARIABLE_STRING_BUFFER), + GEN_IMPORT(Cvar_Register, G_CVAR_REGISTER), + GEN_IMPORT(Cvar_VariableValue, G_CVAR_VARIABLEVALUE), + GEN_IMPORT(Cvar_Update, G_CVAR_UPDATE), + GEN_IMPORT(Cvar_VariableIntegerValue, G_CVAR_VARIABLE_INTEGER_VALUE), + GEN_IMPORT(argc, G_ARGC), + GEN_IMPORT(argv, G_ARGV), + GEN_IMPORT(args, G_ARGS), + GEN_IMPORT(AddCommand, G_ADDCOMMAND), + GEN_IMPORT(FS_ReadFile, G_FS_READFILE), + GEN_IMPORT(FS_Exists, G_FS_EXISTS), + GEN_IMPORT(FS_FreeFile, G_FS_FREEFILE), + GEN_IMPORT(FS_WriteFile, G_FS_WRITEFILE), + GEN_IMPORT(FS_FOpenFileWrite, G_FS_FOPEN_FILE_WRITE), + GEN_IMPORT(FS_FOpenFileAppend, G_FS_FOPEN_FILE_APPEND), + GEN_IMPORT(FS_ListFiles, G_FS_LISTFILES), + GEN_IMPORT(FS_PrepFileWrite, G_FS_PREPFILEWRITE), + GEN_IMPORT(FS_Write, G_FS_WRITE), + GEN_IMPORT(FS_Read, G_FS_READ), + GEN_IMPORT(FS_FCloseFile, G_FS_FCLOSE_FILE), + GEN_IMPORT(FS_FTell, G_FS_FTELL), + GEN_IMPORT(FS_FSeek, G_FS_FSEEK), + GEN_IMPORT(FS_Flush, G_FS_FLUSH), + GEN_IMPORT(FS_DeleteFile, G_FS_DELETEFILE), + GEN_IMPORT(FS_GetFileList, G_FS_GETFILELIST), + GEN_IMPORT(GetArchiveFileName, G_GETARCHIVEFILENAME), + GEN_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND), + GEN_IMPORT_2(DebugGraph, G_DEBUGGRAPH, void, float, int), + GEN_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND), + GEN_IMPORT(GetNumFreeReliableServerCommands, G_GETNUMFREERELIABLESERVERCOMMANDS), + GEN_IMPORT(setConfigstring, G_SET_CONFIGSTRING), + GEN_IMPORT(getConfigstring, G_GET_CONFIGSTRING), + GEN_IMPORT(setUserinfo, G_SET_USERINFO), + GEN_IMPORT(getUserinfo, G_GET_USERINFO), + GEN_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL), + GEN_IMPORT(trace, G_TRACE), + GEN_IMPORT(fulltrace, G_FULLTRACE), + GEN_IMPORT(pointcontents, G_POINT_CONTENTS), + GEN_IMPORT(pointbrushnum, G_POINTBRUSHNUM), + GEN_IMPORT(inPVS, G_IN_PVS), + GEN_IMPORT(inPVSIgnorePortals, G_IN_PVS_IGNOREPORTALS), + GEN_IMPORT(AdjustAreaPortalState, G_ADJUSTAREAPORTALSTATE), + GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), + GEN_IMPORT(GetLightingGroup, G_GETLIGHTINGGROUP), + GEN_IMPORT_2(SetDynamicLight, G_SETDYNAMICLIGHT, void, int, float), + GEN_IMPORT_2(SetDynamicLightDefault, G_SETDYNAMICLIGHTDEFAULT, void, int, float), + GEN_IMPORT(SetWindDirection, G_SETWINDDIRECTION), + GEN_IMPORT_1(SetWindIntensity, G_SETWINDINTENSITY, void, float), + GEN_IMPORT(SetWeatherInfo, G_SETWEATHERINFO), + GEN_IMPORT_1(SetTimeScale, G_SETTIMESCALE, void, float), + GEN_IMPORT(linkentity, G_LINKENTITY), + GEN_IMPORT(unlinkentity, G_UNLINKENTITY), + GEN_IMPORT(AreaEntities, G_AREAENTITIES), + GEN_IMPORT(ClipToEntity, G_CLIPTOENTITY), + GEN_IMPORT(objectivenameindex, G_OBJECTIVENAMEINDEX), + GEN_IMPORT(archetypeindex, G_ARCHETYPEINDEX), + GEN_IMPORT(imageindex, G_IMAGEINDEX), + GEN_IMPORT(failedcondition, G_FAILEDCONDITION), + GEN_IMPORT(itemindex, G_ITEMINDEX), + GEN_IMPORT(soundindex, G_SOUNDINDEX), + GEN_IMPORT(modelindex, G_MODELINDEX), + GEN_IMPORT(SetLightStyle, G_SETLIGHTSTYLE), + GEN_IMPORT(GameDir, G_GAMEDIR), + GEN_IMPORT(IsModel, G_ISMODEL), + GEN_IMPORT(setmodel, G_SETMODEL), + GEN_IMPORT(setviewmodel, G_SETVIEWMODEL), + GEN_IMPORT(NumAnims, G_NUMANIMS), + GEN_IMPORT(NumSkins, G_NUMSKINS), + GEN_IMPORT(NumSurfaces, G_NUMSURFACES), + GEN_IMPORT(NumTags, G_NUMTAGS), + GEN_IMPORT(NumMorphs, G_NUMMORPHS), + GEN_IMPORT(InitCommands, G_INITCOMMANDS), + GEN_IMPORT_4(CalculateBounds, G_CALCULATEBOUNDS, void, int, float, vec3_t, vec3_t), + GEN_IMPORT(TIKI_CacheAnim, G_TIKI_CACHEANIM), + GEN_IMPORT(Anim_NameForNum, G_ANIM_NAMEFORNUM), + GEN_IMPORT(Anim_NumForName, G_ANIM_NUMFORNAME), + GEN_IMPORT(Anim_Random, G_ANIM_RANDOM), + GEN_IMPORT(Anim_NumFrames, G_ANIM_NUMFRAMES), + GEN_IMPORT(Anim_Time, G_ANIM_TIME), + GEN_IMPORT(Anim_Delta, G_ANIM_DELTA), + GEN_IMPORT(Anim_AbsoluteDelta, G_ANIM_ABSOLUTEDELTA), + GEN_IMPORT(Anim_Flags, G_ANIM_FLAGS), + GEN_IMPORT(Anim_HasCommands, G_ANIM_HASCOMMANDS), + GEN_IMPORT(Frame_Commands, G_FRAME_COMMANDS), + GEN_IMPORT(Frame_Delta, G_FRAME_DELTA), + GEN_IMPORT(Frame_Time, G_FRAME_TIME), + GEN_IMPORT_6(Frame_Bounds, G_FRAME_BOUNDS, void, int, int, int, float, vec3_t, vec3_t), + GEN_IMPORT(Surface_NameToNum, G_SURFACE_NAMETONUM), + GEN_IMPORT(Surface_NumToName, G_SURFACE_NUMTONAME), + GEN_IMPORT(Surface_Flags, G_SURFACE_FLAGS), + GEN_IMPORT(Surface_NumSkins, G_SURFACE_NUMSKINS), + GEN_IMPORT(Morph_NumForName, G_MORPH_NUMFORNAME), + GEN_IMPORT(Morph_NameForNum, G_MORPH_NAMEFORNUM), + GEN_IMPORT(GetExpression, G_GETEXPRESSION), + GEN_IMPORT(Tag_NumForName, G_TAG_NUMFORNAME), + GEN_IMPORT(Tag_NameForNum, G_TAG_NAMEFORNUM), + GEN_IMPORT(Tag_Orientation, G_TAG_ORIENTATION), + GEN_IMPORT(Tag_OrientationEx, G_TAG_ORIENTATIONEX), // todo: change types to actually match float, but also need to return an intptr_t instead of orientation_t + GEN_IMPORT(Bone_GetParentNum, G_BONE_GETPARENTNUM), + GEN_IMPORT(Alias_Add, G_ALIAS_ADD), + GEN_IMPORT(Alias_FindRandom, G_ALIAS_FINDRANDOM), + GEN_IMPORT(Alias_Find, G_ALIAS_FIND), + GEN_IMPORT(Alias_Dump, G_ALIAS_DUMP), + GEN_IMPORT(Alias_Clear, G_ALIAS_CLEAR), + GEN_IMPORT(Alias_FindDialog, G_ALIAS_FINDDIALOG), + GEN_IMPORT(Alias_FindSpecificAnim, G_ALIAS_FINDSPECIFICANIM), + GEN_IMPORT(Alias_CheckLoopAnim, G_ALIAS_CHECKLOOPANIM), + GEN_IMPORT(Alias_GetList, G_ALIAS_GETLIST), + GEN_IMPORT(Alias_UpdateDialog, G_ALIAS_UPDATEDIALOG), + GEN_IMPORT(Alias_AddActorDialog, G_ALIAS_ADDACTORDIALOG), + GEN_IMPORT(NameForNum, G_NAMEFORNUM), + GEN_IMPORT(GlobalAlias_Add, G_GLOBALALIAS_ADD), + GEN_IMPORT(GlobalAlias_FindRandom, G_GLOBALALIAS_FINDRANDOM), + GEN_IMPORT(GlobalAlias_Find, G_GLOBALALIAS_FIND), + GEN_IMPORT(GlobalAlias_Dump, G_GLOBALALIAS_DUMP), + GEN_IMPORT(GlobalAlias_Clear, G_GLOBALALIAS_CLEAR), + GEN_IMPORT(isClientActive, G_ISCLIENTACTIVE), + GEN_IMPORT(centerprintf, G_CENTERPRINTF), + GEN_IMPORT(locationprintf, G_LOCATIONPRINTF), + GEN_IMPORT(Sound, G_SOUND), + GEN_IMPORT(StopSound, G_STOPSOUND), + GEN_IMPORT(SoundLength, G_SOUNDLENGTH), + GEN_IMPORT(GetNextMorphTarget, G_GETNEXTMORPHTARGET), + GEN_IMPORT(CalcCRC, G_CALCCRC), + nullptr, // DebugLines + nullptr, // numDebugLines + GEN_IMPORT(LocateGameData, G_LOCATE_GAME_DATA), + GEN_IMPORT(SetFarPlane, G_SETFARPLANE), + GEN_IMPORT(TikiReload, G_TIKIRELOAD), + GEN_IMPORT(TikiLoadFromTS, G_TIKILOADFROMTS), + GEN_IMPORT(ToolServerGetData, G_TOOLSERVERGETDATA), + GEN_IMPORT(SetSkyPortal, G_SETSKYPORTAL), + GEN_IMPORT(WidgetPrintf, G_WIDGETPRINTF), + GEN_IMPORT(ProcessLoadingScreen, G_PROCESSLOADINGSCREEN), + GEN_IMPORT(MObjective_GetDescription, G_MOBJECTIVE_GETDESCRIPTION), + GEN_IMPORT(MObjective_SetDescription, G_MOBJECTIVE_SETDESCRIPTION), + GEN_IMPORT(MObjective_GetShowObjective, G_MOBJECTIVE_GETSHOWOBJECTIVE), + GEN_IMPORT(MObjective_SetShowObjective, G_MOBJECTIVE_SETSHOWOBJECTIVE), + GEN_IMPORT(MObjective_GetObjectiveComplete, G_MOBJECTIVE_GETOBJECTIVECOMPLETE), + GEN_IMPORT(MObjective_SetObjectiveComplete, G_MOBJECTIVE_SETOBJECTIVECOMPLETE), + GEN_IMPORT(MObjective_GetObjectiveFailed, G_MOBJECTIVE_GETOBJECTIVEFAILED), + GEN_IMPORT(MObjective_SetObjectiveFailed, G_MOBJECTIVE_SETOBJECTIVEFAILED), + GEN_IMPORT(MObjective_GetNameFromIndex, G_MOBJECTIVE_GETNAMEFROMINDEX), + GEN_IMPORT(MObjective_GetIndexFromName, G_MOBJECTIVE_GETINDEXFROMNAME), + GEN_IMPORT(MObjective_NewObjective, G_MOBJECTIVE_NEWOBJECTIVE), + GEN_IMPORT(MObjective_ClearObjectiveList, G_MOBJECTIVE_CLEAROBJECTIVELIST), + GEN_IMPORT(MObjective_ParseObjectiveFile, G_MOBJECTIVE_PARSEOBJECTIVEFILE), + GEN_IMPORT(MObjective_Update, G_MOBJECTIVE_UPDATE), + GEN_IMPORT(MObjective_GetNumObjectives, G_MOBJECTIVE_GETNUMOBJECTIVES), + GEN_IMPORT(MObjective_GetNumActiveObjectives, G_MOBJECTIVE_GETNUMACTIVEOBJECTIVES), + GEN_IMPORT(MObjective_GetNumCompleteObjectives, G_MOBJECTIVE_GETNUMCOMPLETEOBJECTIVES), + GEN_IMPORT(MObjective_GetNumFailedObjectives, G_MOBJECTIVE_GETNUMFAILEDOBJECTIVES), + GEN_IMPORT(MObjective_GetNumIncompleteObjectives, G_MOBJECTIVE_GETNUMINCOMPLETEOBJECTIVES), + GEN_IMPORT(MI_GetShader, G_MI_GETSHADER), + GEN_IMPORT(MI_SetShader, G_MI_SETSHADER), + GEN_IMPORT(MI_GetInformationData, G_MI_GETINFORMATIONDATA), + GEN_IMPORT(MI_SetInformationData, G_MI_SETINFORMATIONDATA), + GEN_IMPORT(MI_GetNameFromIndex, G_MI_GETNAMEFROMINDEX), + GEN_IMPORT(MI_GetIndexFromName, G_MI_GETINDEXFROMNAME), + GEN_IMPORT(MI_NewInformation, G_MI_NEWINFORMATION), + GEN_IMPORT(MI_ClearInformationList, G_MI_CLEARINFORMATIONLIST), + GEN_IMPORT(MI_SetShowInformation, G_MI_SETSHOWINFORMATION), + GEN_IMPORT(MI_GetShowInformation, G_MI_GETSHOWINFORMATION), + GEN_IMPORT(SR_InitializeStringResource, G_SR_INITIALIZESTRINGRESOURCE), + GEN_IMPORT(SR_UninitializeStringResource, G_SR_UNINITIALIZESTRINGRESOURCE), + GEN_IMPORT(SR_LoadLevelStrings, G_SR_LOADLEVELSTRINGS), + GEN_IMPORT(GetViewModeMask, G_GETVIEWMODEMASK), + GEN_IMPORT(GetViewModeClassMask, G_GETVIEWMODECLASSMASK), + GEN_IMPORT(GetViewModeSendInMode, G_GETVIEWMODESENDINMODE), + GEN_IMPORT(GetViewModeSendNotInMode, G_GETVIEWMODESENDNOTINMODE), + GEN_IMPORT(GetViewModeScreenBlend, G_GETVIEWMODESCREENBLEND), + GEN_IMPORT(GetLevelDefs, G_GETLEVELDEFS), + GEN_IMPORT(areSublevels, G_ARESUBLEVELS), + GEN_IMPORT(SurfaceTypeToName, G_SURFACETYPETONAME), + GEN_IMPORT(AAS_EntityInfo, G_AAS_ENTITYINFO), + GEN_IMPORT(AAS_Initialized, G_AAS_INITIALIZED), + GEN_IMPORT(AAS_PresenceTypeBoundingBox, G_AAS_PRESENCETYPEBOUNDINGBOX), + GEN_IMPORT(AAS_Time, G_AAS_TIME), + GEN_IMPORT(AAS_PointAreaNum, G_AAS_POINTAREANUM), + GEN_IMPORT(AAS_PointReachabilityAreaIndex, G_AAS_POINTREACHABILITYAREAINDEX), + GEN_IMPORT(AAS_TraceAreas, G_AAS_TRACEAREAS), + GEN_IMPORT(AAS_BBoxAreas, G_AAS_BBOXAREAS), + GEN_IMPORT(AAS_AreaInfo, G_AAS_AREAINFO), + GEN_IMPORT(AAS_PointContents, G_AAS_POINTCONTENTS), + GEN_IMPORT(AAS_NextBSPEntity, G_AAS_NEXTBSPENTITY), + GEN_IMPORT(AAS_ValueForBSPEpairKey, G_AAS_VALUEFORBSPEPAIRKEY), + GEN_IMPORT(AAS_VectorForBSPEpairKey, G_AAS_VECTORFORBSPEPAIRKEY), + GEN_IMPORT(AAS_FloatForBSPEpairKey, G_AAS_FLOATFORBSPEPAIRKEY), + GEN_IMPORT(AAS_IntForBSPEpairKey, G_AAS_INTFORBSPEPAIRKEY), + GEN_IMPORT(AAS_AreaReachability, G_AAS_AREAREACHABILITY), + GEN_IMPORT(AAS_AreaTravelTimeToGoalArea, G_AAS_AREATRAVELTIMETOGOALAREA), + GEN_IMPORT(AAS_EnableRoutingArea, G_AAS_ENABLEROUTINGAREA), + GEN_IMPORT(AAS_PredictRoute, G_AAS_PREDICTROUTE), + GEN_IMPORT(AAS_AlternativeRouteGoals, G_AAS_ALTERNATIVEROUTEGOALS), + GEN_IMPORT(AAS_Swimming, G_AAS_SWIMMING), + GEN_IMPORT_13(AAS_PredictClientMovement, G_AAS_PREDICTCLIENTMOVEMENT, int, struct aas_clientmove_s*, int, vec3_t, int, int, vec3_t, vec3_t, int, int, float, int, int, int), + GEN_IMPORT(EA_Command, G_EA_COMMAND), + GEN_IMPORT(EA_Say, G_EA_SAY), + GEN_IMPORT(EA_SayTeam, G_EA_SAYTEAM), + GEN_IMPORT(EA_Action, G_EA_ACTION), + GEN_IMPORT(EA_Gesture, G_EA_GESTURE), + GEN_IMPORT(EA_Talk, G_EA_TALK), + GEN_IMPORT(EA_ToggleFireState, G_EA_TOGGLEFIRESTATE), + GEN_IMPORT(EA_Attack, G_EA_ATTACK), + GEN_IMPORT(EA_Use, G_EA_USE), + GEN_IMPORT(EA_Respawn, G_EA_RESPAWN), + GEN_IMPORT(EA_MoveUp, G_EA_MOVEUP), + GEN_IMPORT(EA_MoveDown, G_EA_MOVEDOWN), + GEN_IMPORT(EA_MoveForward, G_EA_MOVEFORWARD), + GEN_IMPORT(EA_MoveBack, G_EA_MOVEBACK), + GEN_IMPORT(EA_MoveLeft, G_EA_MOVELEFT), + GEN_IMPORT(EA_MoveRight, G_EA_MOVERIGHT), + GEN_IMPORT(EA_Crouch, G_EA_CROUCH), + GEN_IMPORT(EA_SelectWeapon, G_EA_SELECTWEAPON), + GEN_IMPORT(EA_Jump, G_EA_JUMP), + GEN_IMPORT(EA_DelayedJump, G_EA_DELAYEDJUMP), + GEN_IMPORT_3(EA_Move, G_EA_MOVE, void, int, vec3_t, float), + GEN_IMPORT(EA_View, G_EA_VIEW), + GEN_IMPORT_2(EA_EndRegular, G_EA_ENDREGULAR, void, int, float), + GEN_IMPORT_3(EA_GetInput, G_EA_GETINPUT, void, int, float, bot_input_t*), + GEN_IMPORT(EA_ResetInput, G_EA_RESETINPUT), + GEN_IMPORT_2(BotLoadCharacter, G_BOTLOADCHARACTER, int, char*, float), + GEN_IMPORT(BotFreeCharacter, G_BOTFREECHARACTER), + GEN_IMPORT(Characteristic_Float, G_CHARACTERISTIC_FLOAT), + GEN_IMPORT(Characteristic_BFloat, G_CHARACTERISTIC_BFLOAT), + GEN_IMPORT(Characteristic_Integer, G_CHARACTERISTIC_INTEGER), + GEN_IMPORT(Characteristic_BInteger, G_CHARACTERISTIC_BINTEGER), + GEN_IMPORT(Characteristic_String, G_CHARACTERISTIC_STRING), + GEN_IMPORT(BotAllocChatState, G_BOTALLOCCHATSTATE), + GEN_IMPORT(BotFreeChatState, G_BOTFREECHATSTATE), + GEN_IMPORT(BotQueueConsoleMessage, G_BOTQUEUECONSOLEMESSAGE), + GEN_IMPORT(BotRemoveConsoleMessage, G_BOTREMOVECONSOLEMESSAGE), + GEN_IMPORT(BotNextConsoleMessage, G_BOTNEXTCONSOLEMESSAGE), + GEN_IMPORT(BotNumConsoleMessages, G_BOTNUMCONSOLEMESSAGES), + GEN_IMPORT(BotInitialChat, G_BOTINITIALCHAT), + GEN_IMPORT(BotNumInitialChats, G_BOTNUMINITIALCHATS), + GEN_IMPORT(BotReplyChat, G_BOTREPLYCHAT), + GEN_IMPORT(BotChatLength, G_BOTCHATLENGTH), + GEN_IMPORT(BotEnterChat, G_BOTENTERCHAT), + GEN_IMPORT(BotGetChatMessage, G_BOTGETCHATMESSAGE), + GEN_IMPORT(StringContains, G_STRINGCONTAINS), + GEN_IMPORT(BotFindMatch, G_BOTFINDMATCH), + GEN_IMPORT(BotMatchVariable, G_BOTMATCHVARIABLE), + GEN_IMPORT(UnifyWhiteSpaces, G_UNIFYWHITESPACES), + GEN_IMPORT(BotReplaceSynonyms, G_BOTREPLACESYNONYMS), + GEN_IMPORT(BotLoadChatFile, G_BOTLOADCHATFILE), + GEN_IMPORT(BotSetChatGender, G_BOTSETCHATGENDER), + GEN_IMPORT(BotSetChatName, G_BOTSETCHATNAME), + GEN_IMPORT(BotResetGoalState, G_BOTRESETGOALSTATE), + GEN_IMPORT(BotResetAvoidGoals, G_BOTRESETAVOIDGOALS), + GEN_IMPORT(BotRemoveFromAvoidGoals, G_BOTREMOVEFROMAVOIDGOALS), + GEN_IMPORT(BotPushGoal, G_BOTPUSHGOAL), + GEN_IMPORT(BotPopGoal, G_BOTPOPGOAL), + GEN_IMPORT(BotEmptyGoalStack, G_BOTEMPTYGOALSTACK), + GEN_IMPORT(BotDumpAvoidGoals, G_BOTDUMPAVOIDGOALS), + GEN_IMPORT(BotDumpGoalStack, G_BOTDUMPGOALSTACK), + GEN_IMPORT(BotGoalName, G_BOTGOALNAME), + GEN_IMPORT(BotGetTopGoal, G_BOTGETTOPGOAL), + GEN_IMPORT(BotGetSecondGoal, G_BOTGETSECONDGOAL), + GEN_IMPORT(BotChooseLTGItem, G_BOTCHOOSELTGITEM), + GEN_IMPORT_6(BotChooseNBGItem, G_BOTCHOOSENBGITEM, int, int, vec3_t, int*, int, struct bot_goal_s*, float), + GEN_IMPORT(BotTouchingGoal, G_BOTTOUCHINGGOAL), + GEN_IMPORT(BotItemGoalInVisButNotVisible, G_BOTITEMGOALINVISBUTNOTVISIBLE), + GEN_IMPORT(BotGetLevelItemGoal, G_BOTGETLEVELITEMGOAL), + GEN_IMPORT(BotGetNextCampSpotGoal, G_BOTGETNEXTCAMPSPOTGOAL), + GEN_IMPORT(BotGetMapLocationGoal, G_BOTGETMAPLOCATIONGOAL), + GEN_IMPORT(BotAvoidGoalTime, G_BOTAVOIDGOALTIME), + GEN_IMPORT_3(BotSetAvoidGoalTime, G_BOTSETAVOIDGOALTIME, void, int, int, float), + GEN_IMPORT(BotInitLevelItems, G_BOTINITLEVELITEMS), + GEN_IMPORT(BotUpdateEntityItems, G_BOTUPDATEENTITYITEMS), + GEN_IMPORT(BotLoadItemWeights, G_BOTLOADITEMWEIGHTS), + GEN_IMPORT(BotFreeItemWeights, G_BOTFREEITEMWEIGHTS), + GEN_IMPORT(BotInterbreedGoalFuzzyLogic, G_BOTINTERBREEDGOALFUZZYLOGIC), + GEN_IMPORT(BotSaveGoalFuzzyLogic, G_BOTSAVEGOALFUZZYLOGIC), + GEN_IMPORT_2(BotMutateGoalFuzzyLogic, G_BOTMUTATEGOALFUZZYLOGIC, void, int, float), + GEN_IMPORT(BotAllocGoalState, G_BOTALLOCGOALSTATE), + GEN_IMPORT(BotFreeGoalState, G_BOTFREEGOALSTATE), + GEN_IMPORT(BotResetMoveState, G_BOTRESETMOVESTATE), + GEN_IMPORT(BotMoveToGoal, G_BOTMOVETOGOAL), + GEN_IMPORT_4(BotMoveInDirection, G_BOTMOVEINDIRECTION, int, int, vec3_t, float, int), + GEN_IMPORT(BotResetAvoidReach, G_BOTRESETAVOIDREACH), + GEN_IMPORT(BotResetLastAvoidReach, G_BOTRESETLASTAVOIDREACH), + GEN_IMPORT(BotReachabilityArea, G_BOTREACHABILITYAREA), + GEN_IMPORT_5(BotMovementViewTarget, G_BOTMOVEMENTVIEWTARGET, int, int, struct bot_goal_s*, int, float, vec3_t), + GEN_IMPORT(BotPredictVisiblePosition, G_BOTPREDICTVISIBLEPOSITION), + GEN_IMPORT(BotAllocMoveState, G_BOTALLOCMOVESTATE), + GEN_IMPORT(BotFreeMoveState, G_BOTFREEMOVESTATE), + GEN_IMPORT(BotInitMoveState, G_BOTINITMOVESTATE), + GEN_IMPORT_4(BotAddAvoidSpot, G_BOTADDAVOIDSPOT, void, int, vec3_t, float, int), + GEN_IMPORT(BotChooseBestFightWeapon, G_BOTCHOOSEBESTFIGHTWEAPON), + GEN_IMPORT(BotGetWeaponInfo, G_BOTGETWEAPONINFO), + GEN_IMPORT(BotLoadWeaponWeights, G_BOTLOADWEAPONWEIGHTS), + GEN_IMPORT(BotAllocWeaponState, G_BOTALLOCWEAPONSTATE), + GEN_IMPORT(BotFreeWeaponState, G_BOTFREEWEAPONSTATE), + GEN_IMPORT(BotResetWeaponState, G_BOTRESETWEAPONSTATE), + GEN_IMPORT(GeneticParentsAndChildSelection, G_GENETICPARENTSANDCHILDSELECTION), + GEN_IMPORT(Print, G_BOTPRINT), + GEN_IMPORT(PointContents, G_BOTPOINTCONTENTS), + GEN_IMPORT(BSPEntityData, G_BSPENTITYDATA), + GEN_IMPORT(BSPModelMinsMaxsOrigin, G_BSPMODELMINSMAXSORIGIN), + GEN_IMPORT(BotClientCommand, G_BOTCLIENTCOMMAND), + GEN_IMPORT(AvailableMemory, G_AVAILABLEMEMORY), + GEN_IMPORT(HunkAlloc, G_HUNKALLOC), + GEN_IMPORT(FS_FOpenFile, G_FS_FOPEN_FILE), + GEN_IMPORT(FS_Seek, G_FS_SEEK), + GEN_IMPORT(DebugLineCreate, G_DEBUGLINECREATE), + GEN_IMPORT(DebugLineDelete, G_DEBUGLINEDELETE), + GEN_IMPORT(DebugLineShow, G_DEBUGLINESHOW), + GEN_IMPORT(DebugPolygonCreate, G_DEBUGPOLYGONCREATE), + GEN_IMPORT(DebugPolygonDelete, G_DEBUGPOLYGONDELETE), + GEN_IMPORT(DropClient, G_DROP_CLIENT), + GEN_IMPORT(SV_GetServerinfo, G_SV_GETSERVERINFO), + GEN_IMPORT(BotAllocateClient, G_BOTALLOCATECLIENT), + GEN_IMPORT(BotGetSnapshotEntity, G_BOTGETSNAPSHOTENTITY), + GEN_IMPORT(BotGetConsoleMessage, G_BOTGETCONSOLEMESSAGE), + GEN_IMPORT(BotLibSetup, G_BOTLIBSETUP), + GEN_IMPORT(BotLibShutdown, G_BOTLIBSHUTDOWN), + GEN_IMPORT(BotLibVarSet, G_BOTLIBVARSET), + GEN_IMPORT(BotLibVarGet, G_BOTLIBVARGET), + GEN_IMPORT(PC_AddGlobalDefine, G_PC_ADDGLOBALDEFINE), + GEN_IMPORT(PC_LoadSourceHandle, G_PC_LOADSOURCEHANDLE), + GEN_IMPORT(PC_FreeSourceHandle, G_PC_FREESOURCEHANDLE), + GEN_IMPORT(PC_SourceFileAndLine, G_PC_SOURCEFILEANDLINE), + GEN_IMPORT_1(BotLibStartFrame, G_BOTLIBSTARTFRAME, int, float), + GEN_IMPORT(BotLibLoadMap, G_BOTLIBLOADMAP), + GEN_IMPORT(BotLibUpdateEntity, G_BOTLIBUPDATEENTITY), + GEN_IMPORT(Test, G_TEST), + GEN_IMPORT(BotUserCommand, G_BOTUSERCOMMAND), +}; + + +std::vector STEF2_GameSupport::entity_tokens; +size_t STEF2_GameSupport::token_counter = 0; +void STEF2_GameSupport::SpawnEntities(const char* mapname, const char* entstring, int levelTime) { + if (entstring) { + entity_tokens = util_parse_entstring(entstring); + token_counter = 0; + } + cgame.is_from_QMM = true; + ::vmMain(GAME_SPAWN_ENTITIES, mapname, entstring, levelTime); +} + + +game_export_t STEF2_GameSupport::qmm_export = { + GAME_API_VERSION, // apiversion + GEN_EXPORT(Init, GAME_INIT), + GEN_EXPORT(Shutdown, GAME_SHUTDOWN), + GEN_EXPORT(Cleanup, GAME_CLEANUP), + STEF2_GameSupport::SpawnEntities, + GEN_EXPORT(PostLoad, GAME_POSTLOAD), + GEN_EXPORT(PostSublevelLoad, GAME_POSTSUBLEVELLOAD), + GEN_EXPORT(ClientConnect, GAME_CLIENT_CONNECT), + GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), + GEN_EXPORT(ClientUserinfoChanged, GAME_CLIENT_USERINFO_CHANGED), + GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), + GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), + GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), + GEN_EXPORT(PrepFrame, GAME_PREP_FRAME), + GEN_EXPORT(RunFrame, GAME_RUN_FRAME), + GEN_EXPORT(SendEntity, GAME_SEND_ENTITY), + GEN_EXPORT(UpdateEntityStateForClient, GAME_UPDATE_ENTITYSTATE_FOR_CLIENT), + GEN_EXPORT(UpdatePlayerStateForClient, GAME_UPDATE_PLAYERSTATE_FOR_CLIENT), + GEN_EXPORT(ExtraEntitiesToSend, GAME_EXTRA_ENTITIES_TO_SEND), + GEN_EXPORT(GetEntityCurrentAnimFrame, GAME_GETENTITY_CURRENT_ANIMFRAME), + GEN_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND), + GEN_EXPORT(WritePersistant, GAME_WRITE_PERSISTANT), + GEN_EXPORT(ReadPersistant, GAME_READ_PERSISTANT), + GEN_EXPORT(WriteLevel, GAME_WRITE_LEVEL), + GEN_EXPORT(ReadLevel, GAME_READ_LEVEL), + GEN_EXPORT(LevelArchiveValid, GAME_LEVEL_ARCHIVE_VALID), + GEN_EXPORT(inMultiplayerGame, GAME_IN_MULTIPLAYER_GAME), + GEN_EXPORT(isDefined, GAME_IS_DEFINED), + GEN_EXPORT(getDefine, GAME_GET_DEFINE), + GEN_EXPORT(BotAIStartFrame, BOTAI_START_FRAME), + GEN_EXPORT(AddBot_f, GAME_ADDBOT_F), + GEN_EXPORT(GetTotalGameFrames, GAME_GETTOTALGAMEFRAMES), + // the engine won't use these until after Init, so we can fill these in after each call into the mod's export functions ("vmMain") + nullptr, // gentities + 0, // gentitySize + 0, // num_entities + 0, // max_entities + nullptr, // errorMessage +}; + #endif // QMM_ARCH_32 diff --git a/src/game_stvoysp.cpp b/src/game_stvoysp.cpp index 925531d..9e0e3ce 100644 --- a/src/game_stvoysp.cpp +++ b/src/game_stvoysp.cpp @@ -26,154 +26,75 @@ Created By: #include "main.h" #include "util.h" -GEN_GAME_QMM_MSGS(STVOYSP); +struct STVOYSP_GameSupport : public GameSupport { + virtual const char* EngMsgName(intptr_t msg); + virtual const char* ModMsgName(intptr_t msg); + virtual bool AutoDetect(APIType engine_api); + virtual void* Entry(void* syscall, void*, APIType engine_api); + virtual bool ModLoad(void* entry, APIType mod_api); + virtual void ModUnload(); -GEN_GAME_FUNCS(STVOYSP); + virtual intptr_t syscall(intptr_t, ...); + virtual intptr_t vmMain(intptr_t, ...); + virtual const char* DefaultDLLName() { return "efgame" MOD_DLL; } + virtual const char* DefaultModDir() { return "."; } + virtual const char* GameName() { return "Star Trek Voyager: Elite Force (SP)"; } + virtual const char* GameCode() { return "STVOYSP"; } -// auto-detection logic for STVOYSP -static bool STVOYSP_AutoDetect(APIType engineapi) { - if (engineapi != QMM_API_GETGAMEAPI) - return false; +private: + // update the export variables from orig_export + static void update_exports(); - if (!str_striequal(g_gameinfo.qmm_file, game->dllname)) - return false; - - if (!str_stristr(g_gameinfo.exe_file, "stvoy")) - return false; + // track entstrings for our G_GET_ENTITY_TOKEN syscall + static std::vector entity_tokens; + static size_t token_counter; + static void Init(const char* mapname, const char* spawntarget, int checkSum, const char* entstring, int levelTime, int randomSeed, int globalTime, SavedGameJustLoaded_e eSavedGameJustLoaded, qboolean qbLoadTransition); - return true; -} + // a copy of the original import struct that comes from the game engine + static game_import_t orig_import; + // a copy of the original export struct pointer that comes from the mod + static game_export_t* orig_export; -// a copy of the original import struct that comes from the game engine -static game_import_t orig_import; + // struct with lambdas that call QMM's syscall function. this is given to the mod + static game_import_t qmm_import; -// a copy of the original export struct pointer that comes from the mod -static game_export_t* orig_export = nullptr; + // struct with lambdas that call QMM's vmMain function. this is given to the game engine + static game_export_t qmm_export; -// struct with lambdas that call QMM's syscall function. this is given to the mod -static game_import_t qmm_import = { - GEN_IMPORT(Printf, G_PRINTF), - GEN_IMPORT(WriteCam, G_WRITECAM), - GEN_IMPORT(Error, G_ERROR), - GEN_IMPORT(Milliseconds, G_MILLISECONDS), - GEN_IMPORT(cvar, G_CVAR), - GEN_IMPORT(cvar_set, G_CVAR_SET), - GEN_IMPORT(Cvar_VariableIntegerValue, G_CVAR_VARIABLE_INTEGER_VALUE), - GEN_IMPORT(Cvar_VariableStringBuffer, G_CVAR_VARIABLE_STRING_BUFFER), - GEN_IMPORT(argc, G_ARGC), - GEN_IMPORT(argv, G_ARGV), - GEN_IMPORT(FS_FOpenFile, G_FS_FOPEN_FILE), - GEN_IMPORT(FS_Read, G_FS_READ), - GEN_IMPORT(FS_Write, G_FS_WRITE), - GEN_IMPORT(FS_FCloseFile, G_FS_FCLOSE_FILE), - GEN_IMPORT(FS_ReadFile, G_FS_READFILE), - GEN_IMPORT(FS_FreeFile, G_FS_FREEFILE), - GEN_IMPORT(FS_GetFileList, G_FS_GETFILELIST), - GEN_IMPORT(AppendToSaveGame, G_APPENDTOSAVEGAME), - GEN_IMPORT(ReadFromSaveGame, G_READFROMSAVEGAME), - GEN_IMPORT(ReadFromSaveGameOptional, G_READFROMSAVEGAMEOPTIONAL), - GEN_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND), - GEN_IMPORT(DropClient, G_DROP_CLIENT), - GEN_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND), - GEN_IMPORT(SetConfigstring, G_SET_CONFIGSTRING), - GEN_IMPORT(GetConfigstring, G_GET_CONFIGSTRING), - GEN_IMPORT(GetUserinfo, G_GET_USERINFO), - GEN_IMPORT(SetUserinfo, G_SET_USERINFO), - GEN_IMPORT(GetServerinfo, G_GET_SERVERINFO), - GEN_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL), - GEN_IMPORT(trace, G_TRACE), - GEN_IMPORT(pointcontents, G_POINT_CONTENTS), - GEN_IMPORT(inPVS, G_IN_PVS), - GEN_IMPORT(inPVSIgnorePortals, G_IN_PVS_IGNOREPORTALS), - GEN_IMPORT(AdjustAreaPortalState, G_ADJUSTAREAPORTALSTATE), - GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), - GEN_IMPORT(linkentity, G_LINKENTITY), - GEN_IMPORT(unlinkentity, G_UNLINKENTITY), - GEN_IMPORT(EntitiesInBox, G_ENTITIES_IN_BOX), - GEN_IMPORT(EntityContact, G_ENTITY_CONTACT), - nullptr, // int* S_Override - GEN_IMPORT(Malloc, G_MALLOC), - GEN_IMPORT(Free, G_FREE), + const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = GEN_GAME_QMM_ENG_MSGS(); + const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = GEN_GAME_QMM_MOD_MSGS(); }; +GEN_GAME_OBJ(STVOYSP); -// these are "pre" hooks for storing some data for polyfills. -// we need these to be called BEFORE plugins' prehooks get called so they have to be done in the qmm_export table - -// track entstrings for our G_GET_ENTITY_TOKEN syscall -static std::vector entity_tokens; -static size_t token_counter = 0; -static void STVOYSP_Init(const char* mapname, const char* spawntarget, int checkSum, const char* entstring, int levelTime, int randomSeed, int globalTime, SavedGameJustLoaded_e eSavedGameJustLoaded, qboolean qbLoadTransition) { - if (entstring) { - entity_tokens = util_parse_entstring(entstring); - token_counter = 0; - } - cgame.is_from_QMM = true; - vmMain(GAME_INIT, mapname, spawntarget, checkSum, entstring, levelTime, randomSeed, globalTime, eSavedGameJustLoaded, qbLoadTransition); -} - - -// struct with lambdas that call QMM's vmMain function. this is given to the game engine -static game_export_t qmm_export = { - GAME_API_VERSION, // apiversion - STVOYSP_Init, - GEN_EXPORT(Shutdown, GAME_SHUTDOWN), - GEN_EXPORT(WriteLevel, GAME_WRITE_LEVEL), - GEN_EXPORT(ReadLevel, GAME_READ_LEVEL), - GEN_EXPORT(GameAllowedToSaveHere, GAME_GAMEALLOWEDTOSAVEHERE), - GEN_EXPORT(ClientConnect, GAME_CLIENT_CONNECT), - GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), - GEN_EXPORT(ClientUserinfoChanged, GAME_CLIENT_USERINFO_CHANGED), - GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), - GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), - GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), - GEN_EXPORT(RunFrame, GAME_RUN_FRAME), - GEN_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND), - - // the engine won't use these until after Init, so we can fill these in after each call into the mod's export functions ("vmMain") - nullptr, // gentities - 0, // gentitySize - 0, // num_entities -}; +// auto-detection logic for STVOYSP +bool STVOYSP_GameSupport::AutoDetect(APIType engineapi) { + if (engineapi != QMM_API_GETGAMEAPI) + return false; -// update the export variables from orig_export -static void update_exports() { - if (!orig_export) - return; + if (!str_striequal(g_gameinfo.qmm_file, DefaultDLLName())) + return false; - bool changed = false; + if (!str_stristr(g_gameinfo.exe_file, "stvoy")) + return false; - // if entity data changed, we need to send a G_LOCATE_GAME_DATA so plugins can hook it - if (qmm_export.gentities != orig_export->gentities - || qmm_export.gentitySize != orig_export->gentitySize - || qmm_export.num_entities != orig_export->num_entities - ) { - changed = true; - } + return true; +} - qmm_export.gentities = orig_export->gentities; - qmm_export.gentitySize = orig_export->gentitySize; - qmm_export.num_entities = orig_export->num_entities; - if (changed) { - // this will trigger this message to be fired to plugins, and then it will be handled - // by the empty "case G_LOCATE_GAME_DATA" in MOHAA_syscall - qmm_syscall(G_LOCATE_GAME_DATA, (intptr_t)qmm_export.gentities, qmm_export.num_entities, qmm_export.gentitySize, nullptr, 0); - } -} // wrapper syscall function that calls actual engine func from orig_import // this is how QMM and plugins will call into the engine -static intptr_t STVOYSP_syscall(intptr_t cmd, ...) { +intptr_t STVOYSP_GameSupport::syscall(intptr_t cmd, ...) { QMM_GET_SYSCALL_ARGS(); #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYSP_syscall({} {}) called\n", STVOYSP_EngMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYSP_GameSupport::syscall({} {}) called\n", EngMsgName(cmd), cmd); #endif // update export vars before calling into the engine @@ -294,7 +215,7 @@ static intptr_t STVOYSP_syscall(intptr_t cmd, ...) { #ifdef _DEBUG if (cmd != G_PRINT) - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYSP_syscall({} {}) returning {}\n", STVOYSP_EngMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYSP_GameSupport::syscall({} {}) returning {}\n", EngMsgName(cmd), cmd, ret); #endif return ret; @@ -303,11 +224,11 @@ static intptr_t STVOYSP_syscall(intptr_t cmd, ...) { // wrapper vmMain function that calls actual mod func from orig_export // this is how QMM and plugins will call into the mod -static intptr_t STVOYSP_vmMain(intptr_t cmd, ...) { +intptr_t STVOYSP_GameSupport::vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYSP_vmMain({} {}) called\n", STVOYSP_ModMsgName(cmd), cmd); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYSP_GameSupport::vmMain({} {}) called\n", ModMsgName(cmd), cmd); #endif if (!orig_export) @@ -345,15 +266,15 @@ static intptr_t STVOYSP_vmMain(intptr_t cmd, ...) { update_exports(); #ifdef _DEBUG - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYSP_vmMain({} {}) returning {}\n", STVOYSP_ModMsgName(cmd), cmd, ret); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYSP_GameSupport::vmMain({} {}) returning {}\n", ModMsgName(cmd), cmd, ret); #endif return ret; } -static void* STVOYSP_Entry(void* import, void*, APIType) { - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYSP_Entry({}) called\n", import); +void* STVOYSP_GameSupport::Entry(void* import, void*, APIType) { + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYSP_GameSupport::Entry({}) called\n", import); // original import struct from engine // the struct given by the engine goes out of scope after this returns so we have to copy the whole thing @@ -363,13 +284,7 @@ static void* STVOYSP_Entry(void* import, void*, APIType) { // fill in variables of our hooked import struct to pass to the mod qmm_import.S_Override = orig_import.S_Override; - // pointer to wrapper vmMain function that calls actual mod func from orig_export - g_gameinfo.pfnvmMain = STVOYSP_vmMain; - - // pointer to wrapper syscall function that calls actual engine func from orig_import - g_gameinfo.pfnsyscall = STVOYSP_syscall; - - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYSP_Entry({}) returning {}\n", import, fmt::ptr(&qmm_export)); + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("STVOYSP_GameSupport::Entry({}) returning {}\n", import, fmt::ptr(&qmm_export)); // struct full of export lambdas to QMM's vmMain // this gets returned to the game engine, but we haven't loaded the mod yet. @@ -378,7 +293,7 @@ static void* STVOYSP_Entry(void* import, void*, APIType) { } -static bool STVOYSP_ModLoad(void* entry, APIType modapi) { +bool STVOYSP_GameSupport::ModLoad(void* entry, APIType modapi) { if (modapi != QMM_API_GETGAMEAPI) return false; @@ -389,12 +304,12 @@ static bool STVOYSP_ModLoad(void* entry, APIType modapi) { } -static void STVOYSP_ModUnload() { +void STVOYSP_GameSupport::ModUnload() { orig_export = nullptr; } -static const char* STVOYSP_EngMsgName(intptr_t cmd) { +const char* STVOYSP_GameSupport::EngMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(G_PRINTF); GEN_CASE(G_WRITECAM); @@ -451,7 +366,7 @@ static const char* STVOYSP_EngMsgName(intptr_t cmd) { } -static const char* STVOYSP_ModMsgName(intptr_t cmd) { +const char* STVOYSP_GameSupport::ModMsgName(intptr_t cmd) { switch (cmd) { GEN_CASE(GAMEV_APIVERSION); GEN_CASE(GAME_INIT); @@ -475,4 +390,118 @@ static const char* STVOYSP_ModMsgName(intptr_t cmd) { } } + +void STVOYSP_GameSupport::update_exports() { + if (!orig_export) + return; + + bool changed = false; + + // if entity data changed, we need to send a G_LOCATE_GAME_DATA so plugins can hook it + if (qmm_export.gentities != orig_export->gentities + || qmm_export.gentitySize != orig_export->gentitySize + || qmm_export.num_entities != orig_export->num_entities + ) { + changed = true; + } + + qmm_export.gentities = orig_export->gentities; + qmm_export.gentitySize = orig_export->gentitySize; + qmm_export.num_entities = orig_export->num_entities; + + if (changed) { + // this will trigger this message to be fired to plugins, and then it will be handled + // by the empty "case G_LOCATE_GAME_DATA" in MOHAA_syscall + qmm_syscall(G_LOCATE_GAME_DATA, (intptr_t)qmm_export.gentities, qmm_export.num_entities, qmm_export.gentitySize, nullptr, 0); + } +} + + +game_import_t STVOYSP_GameSupport::orig_import; + + +game_export_t* STVOYSP_GameSupport::orig_export = nullptr; + + +game_import_t STVOYSP_GameSupport::qmm_import = { + GEN_IMPORT(Printf, G_PRINTF), + GEN_IMPORT(WriteCam, G_WRITECAM), + GEN_IMPORT(Error, G_ERROR), + GEN_IMPORT(Milliseconds, G_MILLISECONDS), + GEN_IMPORT(cvar, G_CVAR), + GEN_IMPORT(cvar_set, G_CVAR_SET), + GEN_IMPORT(Cvar_VariableIntegerValue, G_CVAR_VARIABLE_INTEGER_VALUE), + GEN_IMPORT(Cvar_VariableStringBuffer, G_CVAR_VARIABLE_STRING_BUFFER), + GEN_IMPORT(argc, G_ARGC), + GEN_IMPORT(argv, G_ARGV), + GEN_IMPORT(FS_FOpenFile, G_FS_FOPEN_FILE), + GEN_IMPORT(FS_Read, G_FS_READ), + GEN_IMPORT(FS_Write, G_FS_WRITE), + GEN_IMPORT(FS_FCloseFile, G_FS_FCLOSE_FILE), + GEN_IMPORT(FS_ReadFile, G_FS_READFILE), + GEN_IMPORT(FS_FreeFile, G_FS_FREEFILE), + GEN_IMPORT(FS_GetFileList, G_FS_GETFILELIST), + GEN_IMPORT(AppendToSaveGame, G_APPENDTOSAVEGAME), + GEN_IMPORT(ReadFromSaveGame, G_READFROMSAVEGAME), + GEN_IMPORT(ReadFromSaveGameOptional, G_READFROMSAVEGAMEOPTIONAL), + GEN_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND), + GEN_IMPORT(DropClient, G_DROP_CLIENT), + GEN_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND), + GEN_IMPORT(SetConfigstring, G_SET_CONFIGSTRING), + GEN_IMPORT(GetConfigstring, G_GET_CONFIGSTRING), + GEN_IMPORT(GetUserinfo, G_GET_USERINFO), + GEN_IMPORT(SetUserinfo, G_SET_USERINFO), + GEN_IMPORT(GetServerinfo, G_GET_SERVERINFO), + GEN_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL), + GEN_IMPORT(trace, G_TRACE), + GEN_IMPORT(pointcontents, G_POINT_CONTENTS), + GEN_IMPORT(inPVS, G_IN_PVS), + GEN_IMPORT(inPVSIgnorePortals, G_IN_PVS_IGNOREPORTALS), + GEN_IMPORT(AdjustAreaPortalState, G_ADJUSTAREAPORTALSTATE), + GEN_IMPORT(AreasConnected, G_AREAS_CONNECTED), + GEN_IMPORT(linkentity, G_LINKENTITY), + GEN_IMPORT(unlinkentity, G_UNLINKENTITY), + GEN_IMPORT(EntitiesInBox, G_ENTITIES_IN_BOX), + GEN_IMPORT(EntityContact, G_ENTITY_CONTACT), + nullptr, // int* S_Override + GEN_IMPORT(Malloc, G_MALLOC), + GEN_IMPORT(Free, G_FREE), +}; + + +std::vector STVOYSP_GameSupport::entity_tokens; +size_t STVOYSP_GameSupport::token_counter = 0; +void STVOYSP_GameSupport::Init(const char* mapname, const char* spawntarget, int checkSum, const char* entstring, int levelTime, int randomSeed, int globalTime, SavedGameJustLoaded_e eSavedGameJustLoaded, qboolean qbLoadTransition) { + if (entstring) { + entity_tokens = util_parse_entstring(entstring); + token_counter = 0; + } + cgame.is_from_QMM = true; + ::vmMain(GAME_INIT, mapname, spawntarget, checkSum, entstring, levelTime, randomSeed, globalTime, eSavedGameJustLoaded, qbLoadTransition); +} + + +// struct with lambdas that call QMM's vmMain function. this is given to the game engine +game_export_t STVOYSP_GameSupport::qmm_export = { + GAME_API_VERSION, // apiversion + STVOYSP_GameSupport::Init, + GEN_EXPORT(Shutdown, GAME_SHUTDOWN), + GEN_EXPORT(WriteLevel, GAME_WRITE_LEVEL), + GEN_EXPORT(ReadLevel, GAME_READ_LEVEL), + GEN_EXPORT(GameAllowedToSaveHere, GAME_GAMEALLOWEDTOSAVEHERE), + GEN_EXPORT(ClientConnect, GAME_CLIENT_CONNECT), + GEN_EXPORT(ClientBegin, GAME_CLIENT_BEGIN), + GEN_EXPORT(ClientUserinfoChanged, GAME_CLIENT_USERINFO_CHANGED), + GEN_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT), + GEN_EXPORT(ClientCommand, GAME_CLIENT_COMMAND), + GEN_EXPORT(ClientThink, GAME_CLIENT_THINK), + GEN_EXPORT(RunFrame, GAME_RUN_FRAME), + GEN_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND), + + // the engine won't use these until after Init, so we can fill these in after each call into the mod's export functions ("vmMain") + nullptr, // gentities + 0, // gentitySize + 0, // num_entities +}; + #endif // QMM_ARCH_32 From 3efd605d7d937acf63f509333bb69a789cbb912b Mon Sep 17 00:00:00 2001 From: Kevin Masterson Date: Thu, 19 Mar 2026 01:00:27 +0200 Subject: [PATCH 6/7] implement QMMEngMsg and QMMModMsg in each derived game support class. also in main_load_mod, if the "mod" setting is a relative path to a .qvm file, it places it as-is first in the mod search list --- include/game_api.h | 38 +++++++++++++++++++++----------------- include/version.h | 4 ++-- src/game_api.cpp | 10 ---------- src/game_cod11mp.cpp | 2 ++ src/game_codmp.cpp | 2 ++ src/game_coduomp.cpp | 2 ++ src/game_jamp.cpp | 2 ++ src/game_jasp.cpp | 2 ++ src/game_jk2mp.cpp | 2 ++ src/game_jk2sp.cpp | 2 ++ src/game_mohaa.cpp | 3 +++ src/game_mohbt.cpp | 3 +++ src/game_mohsh.cpp | 3 +++ src/game_q2r.cpp | 4 +++- src/game_q3a.cpp | 4 +++- src/game_quake2.cpp | 3 +++ src/game_rtcwmp.cpp | 2 ++ src/game_rtcwsp.cpp | 12 ++++++++++-- src/game_sin.cpp | 4 +++- src/game_sof2mp.cpp | 2 ++ src/game_sof2sp.cpp | 2 ++ src/game_stef2.cpp | 2 ++ src/game_stvoyhm.cpp | 2 ++ src/game_stvoysp.cpp | 2 ++ src/game_wet.cpp | 2 ++ src/main.cpp | 8 +++++--- 26 files changed, 87 insertions(+), 37 deletions(-) diff --git a/include/game_api.h b/include/game_api.h index 2356b1b..2721bb1 100644 --- a/include/game_api.h +++ b/include/game_api.h @@ -86,6 +86,7 @@ enum { // files QMM_G_FS_FOPEN_FILE, QMM_G_FS_READ, QMM_G_FS_WRITE, QMM_G_FS_FCLOSE_FILE, QMM_EXEC_APPEND, QMM_FS_READ, + // array size QMM_ENGINE_MSG_COUNT, }; @@ -93,18 +94,36 @@ enum { enum { QMM_GAME_INIT, QMM_GAME_SHUTDOWN, QMM_GAME_CONSOLE_COMMAND, + // array size QMM_MOD_MSG_COUNT, }; +// macros to output game-specific message values to match the QMM_ enums above +#define GEN_GAME_QMM_ENG_MSGS() \ + { \ + G_PRINT, G_ERROR, G_ARGV, G_ARGC, G_SEND_CONSOLE_COMMAND, G_GET_CONFIGSTRING, \ + G_CVAR_REGISTER, G_CVAR_VARIABLE_STRING_BUFFER, G_CVAR_VARIABLE_INTEGER_VALUE, CVAR_SERVERINFO, CVAR_ROM, \ + G_FS_FOPEN_FILE, G_FS_READ, G_FS_WRITE, G_FS_FCLOSE_FILE, EXEC_APPEND, FS_READ, \ + } +#define GEN_GAME_QMM_MOD_MSGS() \ + { \ + GAME_INIT, GAME_SHUTDOWN, GAME_CONSOLE_COMMAND, \ + } + +// pure virtual base class for game support +// need to implement all functions except: +// * DefaultQVMName - only need if the game supports QVMs. default will return nullptr (this is how QMM determines support) +// * QVMSyscall - only need if the game supports QVMs. default returns 0 +// need to implement qmm_eng_msgs and qmm_mod_msgs (use GEN_GAME_QMM_ENG_MSGS() and GEN_GAME_QMM_MOD_MSGS() macros) struct GameSupport { - virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } - virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual const char* EngMsgName(intptr_t) = 0; virtual const char* ModMsgName(intptr_t) = 0; virtual bool AutoDetect(APIType) = 0; virtual void* Entry(void*, void*, APIType) = 0; virtual bool ModLoad(void*, APIType) = 0; virtual void ModUnload() = 0; + virtual int QMMEngMsg(int msg) = 0; + virtual int QMMModMsg(int msg) = 0; virtual intptr_t syscall(intptr_t, ...) = 0; virtual intptr_t vmMain(intptr_t, ...) = 0; @@ -116,9 +135,6 @@ struct GameSupport { virtual const char* GameCode() = 0; virtual int QVMSyscall(uint8_t*, int, int*) { return 0; } -private: - const int qmm_eng_msgs[QMM_ENGINE_MSG_COUNT] = {}; - const int qmm_mod_msgs[QMM_MOD_MSG_COUNT] = {}; }; extern std::vector api_supportedgames; @@ -137,18 +153,6 @@ extern std::vector api_supportedgames; // generate a case/string line for use in the *MsgNames functions #define GEN_CASE(x) case x: return #x -// macros to output game-specific message values to match the QMM_ enums above -#define GEN_GAME_QMM_ENG_MSGS() \ - { \ - G_PRINT, G_ERROR, G_ARGV, G_ARGC, G_SEND_CONSOLE_COMMAND, G_GET_CONFIGSTRING, \ - G_CVAR_REGISTER, G_CVAR_VARIABLE_STRING_BUFFER, G_CVAR_VARIABLE_INTEGER_VALUE, CVAR_SERVERINFO, CVAR_ROM, \ - G_FS_FOPEN_FILE, G_FS_READ, G_FS_WRITE, G_FS_FCLOSE_FILE, EXEC_APPEND, FS_READ, \ - } -#define GEN_GAME_QMM_MOD_MSGS() \ - { \ - GAME_INIT, GAME_SHUTDOWN, GAME_CONSOLE_COMMAND, \ - } - // cache some dynamic message values that get evaluated a lot extern intptr_t msg_G_PRINT, msg_GAME_INIT, msg_GAME_CONSOLE_COMMAND, msg_GAME_SHUTDOWN; diff --git a/include/version.h b/include/version.h index 21ce0b0..35f389d 100644 --- a/include/version.h +++ b/include/version.h @@ -16,8 +16,8 @@ Created By: #define STRINGIFY2(x) #x #define QMM_VERSION_MAJOR 2 -#define QMM_VERSION_MINOR 4 -#define QMM_VERSION_REV 9 +#define QMM_VERSION_MINOR 5 +#define QMM_VERSION_REV 0 #define QMM_VERSION STRINGIFY(QMM_VERSION_MAJOR) "." STRINGIFY(QMM_VERSION_MINOR) "." STRINGIFY(QMM_VERSION_REV) diff --git a/src/game_api.cpp b/src/game_api.cpp index e7bdd29..c6bc618 100644 --- a/src/game_api.cpp +++ b/src/game_api.cpp @@ -83,16 +83,6 @@ std::vector api_supportedgames = { #endif }; -#if 0 -// OpenMOHAA adds 64-bit MoH support but the API is very different, so disable it for now -// the rest of the games don't appear to have an official 64-bit version or source port -#if defined(QMM_ARCH_32) - { "game" MOD_DLL, nullptr, "base", "Star Trek: Elite Force II", GEN_GAME_INFO(STEF2), 17, 4 }, - { "efgame" MOD_DLL, nullptr, ".", "Star Trek Voyager: Elite Force (SP)", GEN_GAME_INFO(STVOYSP), 13, 9 }, -#endif - - -#endif const char* APIType_Name(APIType api) { switch (api) { diff --git a/src/game_cod11mp.cpp b/src/game_cod11mp.cpp index 544088c..3a30f91 100644 --- a/src/game_cod11mp.cpp +++ b/src/game_cod11mp.cpp @@ -30,6 +30,8 @@ struct COD11MP_GameSupport : public GameSupport { virtual void* Entry(void* syscall, void*, APIType engine_api); virtual bool ModLoad(void* entry, APIType mod_api); virtual void ModUnload(); + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual intptr_t syscall(intptr_t, ...); virtual intptr_t vmMain(intptr_t, ...); diff --git a/src/game_codmp.cpp b/src/game_codmp.cpp index 44a9270..7cd015b 100644 --- a/src/game_codmp.cpp +++ b/src/game_codmp.cpp @@ -31,6 +31,8 @@ struct CODMP_GameSupport : public GameSupport { virtual void* Entry(void* syscall, void*, APIType engine_api); virtual bool ModLoad(void* entry, APIType mod_api); virtual void ModUnload(); + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual intptr_t syscall(intptr_t, ...); virtual intptr_t vmMain(intptr_t, ...); diff --git a/src/game_coduomp.cpp b/src/game_coduomp.cpp index e8c29c6..85217db 100644 --- a/src/game_coduomp.cpp +++ b/src/game_coduomp.cpp @@ -31,6 +31,8 @@ struct CODUOMP_GameSupport : public GameSupport { virtual void* Entry(void* syscall, void*, APIType engine_api); virtual bool ModLoad(void* entry, APIType mod_api); virtual void ModUnload(); + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual intptr_t syscall(intptr_t, ...); virtual intptr_t vmMain(intptr_t, ...); diff --git a/src/game_jamp.cpp b/src/game_jamp.cpp index 514e1b0..ebcb67e 100644 --- a/src/game_jamp.cpp +++ b/src/game_jamp.cpp @@ -28,6 +28,8 @@ struct JAMP_GameSupport : public GameSupport { virtual void* Entry(void* syscall, void*, APIType engine_api); virtual bool ModLoad(void* entry, APIType mod_api); virtual void ModUnload(); + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual intptr_t syscall(intptr_t, ...); virtual intptr_t vmMain(intptr_t, ...); diff --git a/src/game_jasp.cpp b/src/game_jasp.cpp index aa46531..03c67c8 100644 --- a/src/game_jasp.cpp +++ b/src/game_jasp.cpp @@ -30,6 +30,8 @@ struct JASP_GameSupport : public GameSupport { virtual void* Entry(void* syscall, void*, APIType engine_api); virtual bool ModLoad(void* entry, APIType mod_api); virtual void ModUnload(); + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual intptr_t syscall(intptr_t, ...); virtual intptr_t vmMain(intptr_t, ...); diff --git a/src/game_jk2mp.cpp b/src/game_jk2mp.cpp index 4ae4458..1422386 100644 --- a/src/game_jk2mp.cpp +++ b/src/game_jk2mp.cpp @@ -30,6 +30,8 @@ struct JK2MP_GameSupport : public GameSupport { virtual void* Entry(void* syscall, void*, APIType engine_api); virtual bool ModLoad(void* entry, APIType mod_api); virtual void ModUnload(); + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual intptr_t syscall(intptr_t, ...); virtual intptr_t vmMain(intptr_t, ...); diff --git a/src/game_jk2sp.cpp b/src/game_jk2sp.cpp index 1ace725..3c44bd5 100644 --- a/src/game_jk2sp.cpp +++ b/src/game_jk2sp.cpp @@ -29,6 +29,8 @@ struct JK2SP_GameSupport : public GameSupport { virtual void* Entry(void* syscall, void*, APIType engine_api); virtual bool ModLoad(void* entry, APIType mod_api); virtual void ModUnload(); + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual intptr_t syscall(intptr_t, ...); virtual intptr_t vmMain(intptr_t, ...); diff --git a/src/game_mohaa.cpp b/src/game_mohaa.cpp index 45662cd..6d8ff25 100644 --- a/src/game_mohaa.cpp +++ b/src/game_mohaa.cpp @@ -38,6 +38,8 @@ struct MOHAA_GameSupport : public GameSupport { virtual void* Entry(void* syscall, void*, APIType engine_api); virtual bool ModLoad(void* entry, APIType mod_api); virtual void ModUnload(); + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual intptr_t syscall(intptr_t, ...); virtual intptr_t vmMain(intptr_t, ...); @@ -48,6 +50,7 @@ struct MOHAA_GameSupport : public GameSupport { virtual const char* GameCode() { return "MOHAA"; } private: + // update the export variables from orig_export static void update_exports(); // track entstrings for our G_GET_ENTITY_TOKEN syscall diff --git a/src/game_mohbt.cpp b/src/game_mohbt.cpp index c0375b2..a4b59bf 100644 --- a/src/game_mohbt.cpp +++ b/src/game_mohbt.cpp @@ -37,6 +37,8 @@ struct MOHBT_GameSupport : public GameSupport { virtual void* Entry(void* syscall, void*, APIType engine_api); virtual bool ModLoad(void* entry, APIType mod_api); virtual void ModUnload(); + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual intptr_t syscall(intptr_t, ...); virtual intptr_t vmMain(intptr_t, ...); @@ -47,6 +49,7 @@ struct MOHBT_GameSupport : public GameSupport { virtual const char* GameCode() { return "MOHBT"; } private: + // update the export variables from orig_export static void update_exports(); // track entstrings for our G_GET_ENTITY_TOKEN syscall diff --git a/src/game_mohsh.cpp b/src/game_mohsh.cpp index c5b3c0c..cb540ed 100644 --- a/src/game_mohsh.cpp +++ b/src/game_mohsh.cpp @@ -37,6 +37,8 @@ struct MOHSH_GameSupport : public GameSupport { virtual void* Entry(void* syscall, void*, APIType engine_api); virtual bool ModLoad(void* entry, APIType mod_api); virtual void ModUnload(); + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual intptr_t syscall(intptr_t, ...); virtual intptr_t vmMain(intptr_t, ...); @@ -47,6 +49,7 @@ struct MOHSH_GameSupport : public GameSupport { virtual const char* GameCode() { return "MOHSH"; } private: + // update the export variables from orig_export static void update_exports(); // track entstrings for our G_GET_ENTITY_TOKEN syscall diff --git a/src/game_q2r.cpp b/src/game_q2r.cpp index b50697d..4bb2563 100644 --- a/src/game_q2r.cpp +++ b/src/game_q2r.cpp @@ -37,6 +37,8 @@ struct Q2R_GameSupport : public GameSupport { virtual void* Entry(void* syscall, void*, APIType engine_api); virtual bool ModLoad(void* entry, APIType mod_api); virtual void ModUnload(); + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual intptr_t syscall(intptr_t, ...); virtual intptr_t vmMain(intptr_t, ...); @@ -47,6 +49,7 @@ struct Q2R_GameSupport : public GameSupport { virtual const char* GameCode() { return "Q2R"; } private: + // update the export variables from orig_export static void update_exports(); // track configstrings for our G_GET_CONFIGSTRING syscall @@ -597,7 +600,6 @@ const char* Q2R_GameSupport::ModMsgName(intptr_t cmd) { } -// update the export variables from orig_export void Q2R_GameSupport::update_exports() { if (!orig_export) return; diff --git a/src/game_q3a.cpp b/src/game_q3a.cpp index 0f78378..8e05ee0 100644 --- a/src/game_q3a.cpp +++ b/src/game_q3a.cpp @@ -30,7 +30,9 @@ struct Q3A_GameSupport : public GameSupport { virtual void* Entry(void* syscall, void*, APIType engine_api); virtual bool ModLoad(void* entry, APIType mod_api); virtual void ModUnload(); - + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } + virtual intptr_t syscall(intptr_t, ...); virtual intptr_t vmMain(intptr_t, ...); diff --git a/src/game_quake2.cpp b/src/game_quake2.cpp index 4e25199..9fea744 100644 --- a/src/game_quake2.cpp +++ b/src/game_quake2.cpp @@ -32,6 +32,8 @@ struct QUAKE2_GameSupport : public GameSupport { virtual void* Entry(void* syscall, void*, APIType engine_api); virtual bool ModLoad(void* entry, APIType mod_api); virtual void ModUnload(); + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual intptr_t syscall(intptr_t, ...); virtual intptr_t vmMain(intptr_t, ...); @@ -42,6 +44,7 @@ struct QUAKE2_GameSupport : public GameSupport { virtual const char* GameCode() { return "QUAKE2"; } private: + // update the export variables from orig_export static void update_exports(); // track configstrings for our G_GET_CONFIGSTRING syscall diff --git a/src/game_rtcwmp.cpp b/src/game_rtcwmp.cpp index c747fe5..0aa9cbf 100644 --- a/src/game_rtcwmp.cpp +++ b/src/game_rtcwmp.cpp @@ -28,6 +28,8 @@ struct RTCWMP_GameSupport : public GameSupport { virtual void* Entry(void* syscall, void*, APIType engine_api); virtual bool ModLoad(void* entry, APIType mod_api); virtual void ModUnload(); + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual intptr_t syscall(intptr_t, ...); virtual intptr_t vmMain(intptr_t, ...); diff --git a/src/game_rtcwsp.cpp b/src/game_rtcwsp.cpp index 62c5114..68a38da 100644 --- a/src/game_rtcwsp.cpp +++ b/src/game_rtcwsp.cpp @@ -29,12 +29,20 @@ struct RTCWSP_GameSupport : public GameSupport { virtual void* Entry(void* syscall, void*, APIType engine_api); virtual bool ModLoad(void* entry, APIType mod_api); virtual void ModUnload(); + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual intptr_t syscall(intptr_t, ...); virtual intptr_t vmMain(intptr_t, ...); - virtual const char* DefaultDLLName() { return is_iortcw ? iortcw_dllname : official_dllname; } - virtual const char* DefaultModDir() { /* iortcw runs single player out of main dir */ return is_iortcw ? "main" : "."; } + virtual const char* DefaultDLLName() { + // ioRTCW has a different DLL name + return is_iortcw ? iortcw_dllname : official_dllname; + } + virtual const char* DefaultModDir() { + // iortcw runs single player out of main dir + return is_iortcw ? "main" : "."; + } virtual const char* GameName() { return "Return to Castle Wolfenstein (SP)"; } virtual const char* GameCode() { return "RTCWSP"; } diff --git a/src/game_sin.cpp b/src/game_sin.cpp index be5cf22..4e7f835 100644 --- a/src/game_sin.cpp +++ b/src/game_sin.cpp @@ -32,6 +32,8 @@ struct SIN_GameSupport : public GameSupport { virtual void* Entry(void* syscall, void*, APIType engine_api); virtual bool ModLoad(void* entry, APIType mod_api); virtual void ModUnload(); + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual intptr_t syscall(intptr_t, ...); virtual intptr_t vmMain(intptr_t, ...); @@ -42,6 +44,7 @@ struct SIN_GameSupport : public GameSupport { virtual const char* GameCode() { return "SIN"; } private: + // update the export variables from orig_export static void update_exports(); // track configstrings for our G_GET_CONFIGSTRING syscall @@ -630,7 +633,6 @@ const char* SIN_GameSupport::ModMsgName(intptr_t cmd) { } -// update the export variables from orig_export void SIN_GameSupport::update_exports() { if (!orig_export) return; diff --git a/src/game_sof2mp.cpp b/src/game_sof2mp.cpp index 71c1c1a..3c50cef 100644 --- a/src/game_sof2mp.cpp +++ b/src/game_sof2mp.cpp @@ -34,6 +34,8 @@ struct SOF2MP_GameSupport : public GameSupport { virtual void* Entry(void* syscall, void*, APIType engine_api); virtual bool ModLoad(void* entry, APIType mod_api); virtual void ModUnload(); + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual intptr_t syscall(intptr_t, ...); virtual intptr_t vmMain(intptr_t, ...); diff --git a/src/game_sof2sp.cpp b/src/game_sof2sp.cpp index db38e86..6beac66 100644 --- a/src/game_sof2sp.cpp +++ b/src/game_sof2sp.cpp @@ -30,6 +30,8 @@ struct SOF2SP_GameSupport : public GameSupport { virtual void* Entry(void* syscall, void*, APIType engine_api); virtual bool ModLoad(void* entry, APIType mod_api); virtual void ModUnload(); + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual intptr_t syscall(intptr_t, ...); virtual intptr_t vmMain(intptr_t, ...); diff --git a/src/game_stef2.cpp b/src/game_stef2.cpp index 68f8539..33b8095 100644 --- a/src/game_stef2.cpp +++ b/src/game_stef2.cpp @@ -35,6 +35,8 @@ struct STEF2_GameSupport : public GameSupport { virtual void* Entry(void* syscall, void*, APIType engine_api); virtual bool ModLoad(void* entry, APIType mod_api); virtual void ModUnload(); + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual intptr_t syscall(intptr_t, ...); virtual intptr_t vmMain(intptr_t, ...); diff --git a/src/game_stvoyhm.cpp b/src/game_stvoyhm.cpp index 14dcbd6..9855aac 100644 --- a/src/game_stvoyhm.cpp +++ b/src/game_stvoyhm.cpp @@ -33,6 +33,8 @@ struct STVOYHM_GameSupport : public GameSupport { virtual void* Entry(void* syscall, void*, APIType engine_api); virtual bool ModLoad(void* entry, APIType mod_api); virtual void ModUnload(); + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual intptr_t syscall(intptr_t, ...); virtual intptr_t vmMain(intptr_t, ...); diff --git a/src/game_stvoysp.cpp b/src/game_stvoysp.cpp index 9e0e3ce..a12d5f0 100644 --- a/src/game_stvoysp.cpp +++ b/src/game_stvoysp.cpp @@ -33,6 +33,8 @@ struct STVOYSP_GameSupport : public GameSupport { virtual void* Entry(void* syscall, void*, APIType engine_api); virtual bool ModLoad(void* entry, APIType mod_api); virtual void ModUnload(); + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual intptr_t syscall(intptr_t, ...); virtual intptr_t vmMain(intptr_t, ...); diff --git a/src/game_wet.cpp b/src/game_wet.cpp index a1684f2..d926bdc 100644 --- a/src/game_wet.cpp +++ b/src/game_wet.cpp @@ -28,6 +28,8 @@ struct WET_GameSupport : public GameSupport { virtual void* Entry(void* syscall, void*, APIType engine_api); virtual bool ModLoad(void* entry, APIType mod_api); virtual void ModUnload(); + virtual int QMMEngMsg(int msg) { return qmm_eng_msgs[msg]; } + virtual int QMMModMsg(int msg) { return qmm_mod_msgs[msg]; } virtual intptr_t syscall(intptr_t, ...); virtual intptr_t vmMain(intptr_t, ...); diff --git a/src/main.cpp b/src/main.cpp index 14555ce..74fe281 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -262,7 +262,7 @@ C_DLLEXPORT intptr_t vmMain(intptr_t cmd, ...) { g_gameinfo.is_shutdown = true; // if syscall passed to dllEntry was null, revert to std::exit because *shrug* if (!g_gameinfo.syscall) { - + printf("\nFatal QMM Error:\nQMM was unable to determine the game engine.\nPlease set the \"game\" option in qmm2.json.\nRefer to the documentation for more information.\n"); std::exit(-1); } g_gameinfo.syscall(QMM_FAIL_G_ERROR, "\nFatal QMM Error:\nQMM was unable to determine the game engine.\nPlease set the \"game\" option in qmm2.json.\nRefer to the documentation for more information.\n"); @@ -485,7 +485,7 @@ static void* main_handle_entry(void* import, void* extra, APIType engine) { msg_GAME_CONSOLE_COMMAND = QMM_MOD_MSG(QMM_GAME_CONSOLE_COMMAND); msg_GAME_SHUTDOWN = QMM_MOD_MSG(QMM_GAME_SHUTDOWN); - // call the game-specific entry handler (e.g. Q3A_Entry) which will set up the internals to interact + // call the game-specific entry handler (e.g. Q3A_GameSupport::Entry) which will set up the internals to interact // the engine and the mod void* ret = g_gameinfo.game->Entry(import, extra, engine); @@ -612,7 +612,9 @@ static bool main_load_mod(std::string cfg_mod) { if (g_gameinfo.game->DefaultQVMName()) try_paths.push_back(g_gameinfo.game->DefaultQVMName()); } - // if "mod" config setting was a relative path, do nothing special + // if "mod" config setting was a relative path, do nothing special unless QVM + if (str_striequal(path_baseext(cfg_mod), EXT_QVM) && g_gameinfo.game->DefaultQVMName()) + try_paths.push_back(cfg_mod); try_paths.push_back(fmt::format("{}/{}", g_gameinfo.qmm_dir, cfg_mod)); try_paths.push_back(fmt::format("{}/{}/{}", g_gameinfo.exe_dir, g_gameinfo.mod_dir, cfg_mod)); for (std::string& try_path : try_paths) { From 84a773ba423ad709ec226a5430e788a34c0e6393 Mon Sep 17 00:00:00 2001 From: Kevin Masterson Date: Thu, 19 Mar 2026 01:06:43 +0200 Subject: [PATCH 7/7] add (void) cast to ::vmMain calls in game support code --- include/game_api.h | 1 - src/game_jasp.cpp | 2 +- src/game_jk2sp.cpp | 2 +- src/game_mohaa.cpp | 4 ++-- src/game_mohbt.cpp | 4 ++-- src/game_mohsh.cpp | 4 ++-- src/game_q2r.cpp | 4 ++-- src/game_quake2.cpp | 4 ++-- src/game_sin.cpp | 4 ++-- src/game_stef2.cpp | 2 +- src/game_stvoysp.cpp | 2 +- 11 files changed, 16 insertions(+), 17 deletions(-) diff --git a/include/game_api.h b/include/game_api.h index 2721bb1..95f4421 100644 --- a/include/game_api.h +++ b/include/game_api.h @@ -17,7 +17,6 @@ Created By: #include #include #include "qmmapi.h" -#include "qvm.h" // mod suffixes and extensions diff --git a/src/game_jasp.cpp b/src/game_jasp.cpp index 03c67c8..f5dc071 100644 --- a/src/game_jasp.cpp +++ b/src/game_jasp.cpp @@ -771,7 +771,7 @@ void JASP_GameSupport::Init(const char* mapname, const char* spawntarget, int ch token_counter[-1] = 0; } cgame.is_from_QMM = true; - ::vmMain(GAME_INIT, mapname, spawntarget, checkSum, entstring, levelTime, randomSeed, globalTime, eSavedGameJustLoaded, qbLoadTransition); + (void)::vmMain(GAME_INIT, mapname, spawntarget, checkSum, entstring, levelTime, randomSeed, globalTime, eSavedGameJustLoaded, qbLoadTransition); } diff --git a/src/game_jk2sp.cpp b/src/game_jk2sp.cpp index 3c44bd5..079a95f 100644 --- a/src/game_jk2sp.cpp +++ b/src/game_jk2sp.cpp @@ -662,7 +662,7 @@ void JK2SP_GameSupport::Init(const char* mapname, const char* spawntarget, int c token_counter = 0; } cgame.is_from_QMM = true; - ::vmMain(GAME_INIT, mapname, spawntarget, checkSum, entstring, levelTime, randomSeed, globalTime, eSavedGameJustLoaded, qbLoadTransition); + (void)::vmMain(GAME_INIT, mapname, spawntarget, checkSum, entstring, levelTime, randomSeed, globalTime, eSavedGameJustLoaded, qbLoadTransition); } diff --git a/src/game_mohaa.cpp b/src/game_mohaa.cpp index 6d8ff25..166b95d 100644 --- a/src/game_mohaa.cpp +++ b/src/game_mohaa.cpp @@ -996,14 +996,14 @@ void MOHAA_GameSupport::SpawnEntities(char* entstring, int levelTime) { token_counter = 0; } cgame.is_from_QMM = true; - ::vmMain(GAME_SPAWN_ENTITIES, entstring, levelTime); + (void)::vmMain(GAME_SPAWN_ENTITIES, entstring, levelTime); } // at least one of first four args is a float (see big comment regarding sound in game_q2r.cpp), so use specific types void MOHAA_GameSupport::DebugCircle(float* arg0, float arg1, float arg2, float arg3, float arg4, float arg5, qboolean arg6) { cgame.is_from_QMM = true; - ::vmMain(GAME_DEBUG_CIRCLE, arg0, arg1, arg2, arg3, arg4, arg5, arg6); + (void)::vmMain(GAME_DEBUG_CIRCLE, arg0, arg1, arg2, arg3, arg4, arg5, arg6); } diff --git a/src/game_mohbt.cpp b/src/game_mohbt.cpp index a4b59bf..e08fc79 100644 --- a/src/game_mohbt.cpp +++ b/src/game_mohbt.cpp @@ -1041,14 +1041,14 @@ void MOHBT_GameSupport::SpawnEntities(char* entstring, int levelTime) { token_counter = 0; } cgame.is_from_QMM = true; - ::vmMain(GAME_SPAWN_ENTITIES, entstring, levelTime); + (void)::vmMain(GAME_SPAWN_ENTITIES, entstring, levelTime); } // at least one of first four args is a float (see big comment at top of game_q2r.cpp), so use specific types void MOHBT_GameSupport::DebugCircle(float* arg0, float arg1, float arg2, float arg3, float arg4, float arg5, qboolean arg6) { cgame.is_from_QMM = true; - ::vmMain(GAME_DEBUG_CIRCLE, arg0, arg1, arg2, arg3, arg4, arg5, arg6); + (void)::vmMain(GAME_DEBUG_CIRCLE, arg0, arg1, arg2, arg3, arg4, arg5, arg6); } diff --git a/src/game_mohsh.cpp b/src/game_mohsh.cpp index cb540ed..2afbc34 100644 --- a/src/game_mohsh.cpp +++ b/src/game_mohsh.cpp @@ -1041,14 +1041,14 @@ void MOHSH_GameSupport::SpawnEntities(char* entstring, int levelTime) { token_counter = 0; } cgame.is_from_QMM = true; - ::vmMain(GAME_SPAWN_ENTITIES, entstring, levelTime); + (void)::vmMain(GAME_SPAWN_ENTITIES, entstring, levelTime); } // at least one of first four args is a float (see big comment at top of game_q2r.cpp), so use specific types void MOHSH_GameSupport::DebugCircle(float* arg0, float arg1, float arg2, float arg3, float arg4, float arg5, qboolean arg6) { cgame.is_from_QMM = true; - ::vmMain(GAME_DEBUG_CIRCLE, arg0, arg1, arg2, arg3, arg4, arg5, arg6); + (void)::vmMain(GAME_DEBUG_CIRCLE, arg0, arg1, arg2, arg3, arg4, arg5, arg6); } diff --git a/src/game_q2r.cpp b/src/game_q2r.cpp index 4bb2563..d2b0f9e 100644 --- a/src/game_q2r.cpp +++ b/src/game_q2r.cpp @@ -758,7 +758,7 @@ void Q2R_GameSupport::ClientUserinfoChanged(edict_t* ent, const char* userinfo) userinfos[clientnum] = userinfo; } cgame.is_from_QMM = true; - ::vmMain(GAME_CLIENT_USERINFO_CHANGED, ent, userinfo); + (void)::vmMain(GAME_CLIENT_USERINFO_CHANGED, ent, userinfo); } @@ -770,7 +770,7 @@ void Q2R_GameSupport::SpawnEntities(const char* mapname, const char* entstring, token_counter = 0; } cgame.is_from_QMM = true; - ::vmMain(GAME_SPAWN_ENTITIES, mapname, entstring, spawnpoint); + (void)::vmMain(GAME_SPAWN_ENTITIES, mapname, entstring, spawnpoint); } diff --git a/src/game_quake2.cpp b/src/game_quake2.cpp index 9fea744..ed3d489 100644 --- a/src/game_quake2.cpp +++ b/src/game_quake2.cpp @@ -649,7 +649,7 @@ void QUAKE2_GameSupport::ClientUserinfoChanged(edict_t* ent, char* userinfo) { userinfos[clientnum] = userinfo; } cgame.is_from_QMM = true; - ::vmMain(GAME_CLIENT_USERINFO_CHANGED, ent, userinfo); + (void)::vmMain(GAME_CLIENT_USERINFO_CHANGED, ent, userinfo); } @@ -662,7 +662,7 @@ void QUAKE2_GameSupport::SpawnEntities(char* mapname, char* entstring, char* spa token_counter = 0; } cgame.is_from_QMM = true; - ::vmMain(GAME_SPAWN_ENTITIES, mapname, entstring, spawnpoint); + (void)::vmMain(GAME_SPAWN_ENTITIES, mapname, entstring, spawnpoint); } diff --git a/src/game_sin.cpp b/src/game_sin.cpp index 4e7f835..7867c27 100644 --- a/src/game_sin.cpp +++ b/src/game_sin.cpp @@ -810,7 +810,7 @@ void SIN_GameSupport::ClientUserinfoChanged(edict_t* ent, const char* userinfo) userinfos[clientnum] = userinfo; } cgame.is_from_QMM = true; - ::vmMain(GAME_CLIENT_USERINFO_CHANGED, ent, userinfo); + (void)::vmMain(GAME_CLIENT_USERINFO_CHANGED, ent, userinfo); } @@ -823,7 +823,7 @@ void SIN_GameSupport::SpawnEntities(const char* mapname, const char* entstring, token_counter = 0; } cgame.is_from_QMM = true; - ::vmMain(GAME_SPAWN_ENTITIES, mapname, entstring, spawnpoint); + (void)::vmMain(GAME_SPAWN_ENTITIES, mapname, entstring, spawnpoint); } diff --git a/src/game_stef2.cpp b/src/game_stef2.cpp index 33b8095..f4721de 100644 --- a/src/game_stef2.cpp +++ b/src/game_stef2.cpp @@ -1386,7 +1386,7 @@ void STEF2_GameSupport::SpawnEntities(const char* mapname, const char* entstring token_counter = 0; } cgame.is_from_QMM = true; - ::vmMain(GAME_SPAWN_ENTITIES, mapname, entstring, levelTime); + (void)::vmMain(GAME_SPAWN_ENTITIES, mapname, entstring, levelTime); } diff --git a/src/game_stvoysp.cpp b/src/game_stvoysp.cpp index a12d5f0..c1ad9e6 100644 --- a/src/game_stvoysp.cpp +++ b/src/game_stvoysp.cpp @@ -479,7 +479,7 @@ void STVOYSP_GameSupport::Init(const char* mapname, const char* spawntarget, int token_counter = 0; } cgame.is_from_QMM = true; - ::vmMain(GAME_INIT, mapname, spawntarget, checkSum, entstring, levelTime, randomSeed, globalTime, eSavedGameJustLoaded, qbLoadTransition); + (void)::vmMain(GAME_INIT, mapname, spawntarget, checkSum, entstring, levelTime, randomSeed, globalTime, eSavedGameJustLoaded, qbLoadTransition); }