diff --git a/include/game_api.h b/include/game_api.h index c7ea0d4..f58151c 100644 --- a/include/game_api.h +++ b/include/game_api.h @@ -30,7 +30,7 @@ struct supportedgame_funcs { qvm_syscall pfnqvmsyscall; // pointer to a function that handles mod->engine calls from a QVM (NULL = not supported) mod_dllEntry pfndllEntry; // pointer to a function that handles dllEntry entry for a game (NULL = not supported) mod_GetGameAPI pfnGetGameAPI; // pointer to a function that handles GetGameAPI entry for a game (NULL = not supported) - bool(*pfnModLoad)(void*); // pointer to a function that handles mod loading logic after a DLL is loaded + bool(*pfnModLoad)(void*, bool); // pointer to a function that handles mod loading logic after a DLL is loaded void(*pfnModUnload)(); // pointer to a function that handles mod unloading logic before a DLL is unloaded }; @@ -76,7 +76,7 @@ static intptr_t game##_syscall(intptr_t cmd, ...); \ static intptr_t game##_vmMain(intptr_t cmd, ...); \ static int game##_qvmsyscall(uint8_t*, int, int*); \ static void game##_dllEntry(eng_syscall); \ -static bool game##_mod_load(void*); \ +static bool game##_mod_load(void*, bool); \ static void game##_mod_unload(); \ supportedgame_funcs game##_funcs = { \ game##_qmm_eng_msgs, game##_qmm_mod_msgs, game##_eng_msg_names, game##_mod_msg_names, \ @@ -92,7 +92,7 @@ static bool game##_autodetect(bool, supportedgame*); \ static intptr_t game##_syscall(intptr_t cmd, ...); \ static intptr_t game##_vmMain(intptr_t cmd, ...); \ static void game##_dllEntry(eng_syscall); \ -static bool game##_mod_load(void*); \ +static bool game##_mod_load(void*, bool); \ static void game##_mod_unload(); \ supportedgame_funcs game##_funcs = { \ game##_qmm_eng_msgs, game##_qmm_mod_msgs, game##_eng_msg_names, game##_mod_msg_names, \ @@ -108,7 +108,7 @@ static bool game##_autodetect(bool, supportedgame*); \ static intptr_t game##_syscall(intptr_t cmd, ...); \ static intptr_t game##_vmMain(intptr_t cmd, ...); \ static void* game##_GetGameAPI(void*, void*); \ -static bool game##_mod_load(void*); \ +static bool game##_mod_load(void*, bool); \ static void game##_mod_unload(); \ supportedgame_funcs game##_funcs = { \ game##_qmm_eng_msgs, game##_qmm_mod_msgs, game##_eng_msg_names, game##_mod_msg_names, \ diff --git a/include/game_jamp.h b/include/game_jamp.h index bd97b8f..075d047 100644 --- a/include/game_jamp.h +++ b/include/game_jamp.h @@ -12,6 +12,11 @@ Created By: #ifndef QMM2_GAME_JAMP_H #define QMM2_GAME_JAMP_H +// meh +enum { + G_FS_LISTFILES = G_FS_GETFILELIST, +}; + // these import messages do not have an exact analogue in JAMP enum { G_ARGS = -100, // char* (void) diff --git a/src/game_cod11mp.cpp b/src/game_cod11mp.cpp index 8451cda..8db53a6 100644 --- a/src/game_cod11mp.cpp +++ b/src/game_cod11mp.cpp @@ -20,23 +20,13 @@ Created By: GEN_QMM_MSGS(COD11MP); GEN_EXTS(COD11MP); -static const char* COD11MP_eng_msg_names(intptr_t); -static const char* COD11MP_mod_msg_names(intptr_t); -static void COD11MP_dllEntry(eng_syscall); -static bool COD11MP_mod_load(void*); -static void COD11MP_mod_unload(); -supportedgame_funcs COD11MP_funcs = { - COD11MP_qmm_eng_msgs, - COD11MP_qmm_mod_msgs, - COD11MP_eng_msg_names, - COD11MP_mod_msg_names, - nullptr, // COD11MP_autodetect - nullptr, // COD11MP_qvmsyscall - COD11MP_dllEntry, - nullptr, // COD11MP_GetGameAPI - COD11MP_mod_load, - COD11MP_mod_unload -}; +GEN_DLL(COD11MP); + + +// auto-detection logic for COD11MP (never auto-detect) +static bool COD11MP_autodetect(bool, supportedgame*) { + return false; +} // original syscall pointer that comes from the game engine @@ -45,7 +35,7 @@ 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 from orig_import +// 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, ...) { QMM_GET_SYSCALL_ARGS(); @@ -66,7 +56,7 @@ static intptr_t COD11MP_syscall(intptr_t cmd, ...) { s = ""; int i = 1; while (i < orig_syscall(G_ARGC)) { - orig_syscall(G_ARGV, buf, sizeof(buf)); + orig_syscall(G_ARGV, i, buf, sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; if (i != 1) s += " "; @@ -92,7 +82,7 @@ static intptr_t COD11MP_syscall(intptr_t cmd, ...) { } -// wrapper vmMain function that calls actual mod func from orig_export +// 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, ...) { QMM_GET_VMMAIN_ARGS(); @@ -134,7 +124,7 @@ static void COD11MP_dllEntry(eng_syscall syscall) { } -static bool COD11MP_mod_load(void* entry) { +static bool COD11MP_mod_load(void* entry, bool) { orig_vmMain = (mod_vmMain)entry; return !!orig_vmMain; diff --git a/src/game_codmp.cpp b/src/game_codmp.cpp index 17cb6d2..55841b7 100644 --- a/src/game_codmp.cpp +++ b/src/game_codmp.cpp @@ -50,7 +50,7 @@ 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 from orig_import +// 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, ...) { QMM_GET_SYSCALL_ARGS(); @@ -71,7 +71,7 @@ static intptr_t CODMP_syscall(intptr_t cmd, ...) { s = ""; int i = 1; while (i < orig_syscall(G_ARGC)) { - orig_syscall(G_ARGV, buf, sizeof(buf)); + orig_syscall(G_ARGV, i, buf, sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; if (i != 1) s += " "; @@ -97,7 +97,7 @@ static intptr_t CODMP_syscall(intptr_t cmd, ...) { } -// wrapper vmMain function that calls actual mod func from orig_export +// 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, ...) { QMM_GET_VMMAIN_ARGS(); @@ -139,7 +139,7 @@ static void CODMP_dllEntry(eng_syscall syscall) { } -static bool CODMP_mod_load(void* entry) { +static bool CODMP_mod_load(void* entry, bool) { orig_vmMain = (mod_vmMain)entry; return !!orig_vmMain; diff --git a/src/game_coduomp.cpp b/src/game_coduomp.cpp index 07164b4..fecbb3e 100644 --- a/src/game_coduomp.cpp +++ b/src/game_coduomp.cpp @@ -50,7 +50,7 @@ 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 from orig_import +// 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, ...) { QMM_GET_SYSCALL_ARGS(); @@ -71,7 +71,7 @@ static intptr_t CODUOMP_syscall(intptr_t cmd, ...) { s = ""; int i = 1; while (i < orig_syscall(G_ARGC)) { - orig_syscall(G_ARGV, buf, sizeof(buf)); + orig_syscall(G_ARGV, i, buf, sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; if (i != 1) s += " "; @@ -97,7 +97,7 @@ static intptr_t CODUOMP_syscall(intptr_t cmd, ...) { } -// wrapper vmMain function that calls actual mod func from orig_export +// 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, ...) { QMM_GET_VMMAIN_ARGS(); @@ -139,7 +139,7 @@ static void CODUOMP_dllEntry(eng_syscall syscall) { } -static bool CODUOMP_mod_load(void* entry) { +static bool CODUOMP_mod_load(void* entry, bool) { orig_vmMain = (mod_vmMain)entry; return !!orig_vmMain; diff --git a/src/game_jamp.cpp b/src/game_jamp.cpp index b2207d1..15e77af 100644 --- a/src/game_jamp.cpp +++ b/src/game_jamp.cpp @@ -22,14 +22,32 @@ Created By: GEN_QMM_MSGS(JAMP); GEN_EXTS(JAMP); -GEN_DLL(JAMP); +// do not use macro since this game supports both dllEntry and GetGameAPI entry points +static const char* JAMP_eng_msg_names(intptr_t); +static const char* JAMP_mod_msg_names(intptr_t); +static bool JAMP_autodetect(bool, supportedgame*); +static intptr_t JAMP_syscall(intptr_t cmd, ...); +static intptr_t JAMP_vmMain(intptr_t cmd, ...); +static void JAMP_dllEntry(eng_syscall); +static void* JAMP_GetGameAPI(void*, void*); +static bool JAMP_mod_load(void*, bool); +static void JAMP_mod_unload(); +supportedgame_funcs JAMP_funcs = { + JAMP_qmm_eng_msgs, + JAMP_qmm_mod_msgs, + JAMP_eng_msg_names, + JAMP_mod_msg_names, + JAMP_autodetect, + nullptr, // JAMP_qvmsyscall + JAMP_dllEntry, + JAMP_GetGameAPI, + JAMP_mod_load, + JAMP_mod_unload, +}; // auto-detection logic for JAMP static bool JAMP_autodetect(bool is_GetGameAPI, supportedgame* game) { - if (is_GetGameAPI) - 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) && !str_striequal(g_gameinfo.qmm_file.substr(0, 3), "ojk") @@ -49,7 +67,377 @@ 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 from orig_import + +// 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, ...) { QMM_GET_SYSCALL_ARGS(); @@ -59,30 +447,369 @@ static intptr_t JAMP_syscall(intptr_t cmd, ...) { LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_syscall({} {}) called\n", JAMP_eng_msg_names(cmd), cmd); #endif + // store return value since we do some stuff after the function call is over intptr_t ret = 0; - switch (cmd) { - // handle special cmds which QMM uses but JAMP doesn't have an analogue for - case G_ARGS: { - // quake2: char* (*args)(void); - static std::string s; - static char buf[MAX_STRING_CHARS]; - s = ""; - int i = 1; - while (i < orig_syscall(G_ARGC)) { - orig_syscall(G_ARGV, buf, sizeof(buf)); - buf[sizeof(buf) - 1] = '\0'; - if (i != 1) - s += " "; - s += buf; + // if QMM was loaded with the official JAMP or OpenJK "legacy" API (which shouldn't happen, but whatever) + if (orig_syscall) { + switch (cmd) { + // handle special cmds which QMM uses but JAMP doesn't have an analogue for + case G_ARGS: { + // quake2: char* (*args)(void); + static std::string s; + static char buf[MAX_STRING_CHARS]; + s = ""; + int i = 1; + while (i < orig_syscall(G_ARGC)) { + orig_syscall(G_ARGV, i, buf, sizeof(buf)); + buf[sizeof(buf) - 1] = '\0'; + if (i != 1) + s += " "; + s += buf; + } + ret = (intptr_t)s.c_str(); + break; + } + + default: + // all normal engine functions go to engine + ret = orig_syscall(cmd, QMM_PUT_SYSCALL_ARGS()); } - ret = (intptr_t)s.c_str(); - break; } + // if QMM was loaded with the OpenJK "new" API + else if (orig_import.Print) { + switch (cmd) { + ROUTE_IMPORT(Print, G_PRINT); + ROUTE_IMPORT(Error, G_ERROR); + ROUTE_IMPORT(Milliseconds, G_MILLISECONDS); + ROUTE_IMPORT(PrecisionTimerStart, G_PRECISIONTIMER_START); + ROUTE_IMPORT(PrecisionTimerEnd, G_PRECISIONTIMER_END); + ROUTE_IMPORT(SV_RegisterSharedMemory, G_SET_SHARED_BUFFER); + ROUTE_IMPORT(RealTime, G_REAL_TIME); + ROUTE_IMPORT(TrueMalloc, G_TRUEMALLOC); + ROUTE_IMPORT(TrueFree, G_TRUEFREE); + ROUTE_IMPORT(SnapVector, G_SNAPVECTOR); + ROUTE_IMPORT(Cvar_Register, G_CVAR_REGISTER); + ROUTE_IMPORT(Cvar_Set, G_CVAR_SET); + ROUTE_IMPORT(Cvar_Update, G_CVAR_UPDATE); + ROUTE_IMPORT(Cvar_VariableIntegerValue, G_CVAR_VARIABLE_INTEGER_VALUE); + ROUTE_IMPORT(Cvar_VariableStringBuffer, G_CVAR_VARIABLE_STRING_BUFFER); + ROUTE_IMPORT(Argc, G_ARGC); + ROUTE_IMPORT(Argv, G_ARGV); + ROUTE_IMPORT(FS_Close, G_FS_FCLOSE_FILE); + ROUTE_IMPORT(FS_GetFileList, G_FS_GETFILELIST); + ROUTE_IMPORT(FS_Open, G_FS_FOPEN_FILE); + ROUTE_IMPORT(FS_Read, G_FS_READ); + ROUTE_IMPORT(FS_Write, G_FS_WRITE); + ROUTE_IMPORT(AdjustAreaPortalState, G_ADJUST_AREA_PORTAL_STATE); + ROUTE_IMPORT(AreasConnected, G_AREAS_CONNECTED); + ROUTE_IMPORT(DebugPolygonCreate, G_DEBUG_POLYGON_CREATE); + ROUTE_IMPORT(DebugPolygonDelete, G_DEBUG_POLYGON_DELETE); + ROUTE_IMPORT(DropClient, G_DROP_CLIENT); + ROUTE_IMPORT(EntitiesInBox, G_ENTITIES_IN_BOX); + ROUTE_IMPORT(EntityContact, G_ENTITY_CONTACT); + ROUTE_IMPORT(GetConfigstring, G_GET_CONFIGSTRING); + ROUTE_IMPORT(GetEntityToken, G_GET_ENTITY_TOKEN); + ROUTE_IMPORT(GetServerinfo, G_GET_SERVERINFO); + ROUTE_IMPORT(GetUsercmd, G_GET_USERCMD); + ROUTE_IMPORT(GetUserinfo, G_GET_USERINFO); + ROUTE_IMPORT(InPVS, G_IN_PVS); + ROUTE_IMPORT(InPVSIgnorePortals, G_IN_PVS_IGNORE_PORTALS); + ROUTE_IMPORT(LinkEntity, G_LINKENTITY); + ROUTE_IMPORT(LocateGameData, G_LOCATE_GAME_DATA); + ROUTE_IMPORT(PointContents, G_POINT_CONTENTS); + ROUTE_IMPORT(SendConsoleCommand, G_SEND_CONSOLE_COMMAND); + ROUTE_IMPORT(SendServerCommand, G_SEND_SERVER_COMMAND); + ROUTE_IMPORT(SetBrushModel, G_SET_BRUSH_MODEL); + ROUTE_IMPORT(SetConfigstring, G_SET_CONFIGSTRING); + ROUTE_IMPORT(SetServerCull, G_SET_SERVER_CULL); + ROUTE_IMPORT(SetUserinfo, G_SET_USERINFO); + ROUTE_IMPORT(SiegePersSet, G_SIEGEPERSSET); + ROUTE_IMPORT(SiegePersGet, G_SIEGEPERSGET); + ROUTE_IMPORT(Trace, G_TRACE); + ROUTE_IMPORT(UnlinkEntity, G_UNLINKENTITY); + ROUTE_IMPORT(ROFF_Clean, G_ROFF_CLEAN); + ROUTE_IMPORT(ROFF_UpdateEntities, G_ROFF_UPDATE_ENTITIES); + ROUTE_IMPORT(ROFF_Cache, G_ROFF_CACHE); + ROUTE_IMPORT(ROFF_Play, G_ROFF_PLAY); + ROUTE_IMPORT(ROFF_Purge_Ent, G_ROFF_PURGE_ENT); + ROUTE_IMPORT(ICARUS_RunScript, G_ICARUS_RUNSCRIPT); + ROUTE_IMPORT(ICARUS_RegisterScript, G_ICARUS_REGISTERSCRIPT); + ROUTE_IMPORT(ICARUS_Init, G_ICARUS_INIT); + ROUTE_IMPORT(ICARUS_ValidEnt, G_ICARUS_VALIDENT); + ROUTE_IMPORT(ICARUS_IsInitialized, G_ICARUS_ISINITIALIZED); + ROUTE_IMPORT(ICARUS_MaintainTaskManager, G_ICARUS_MAINTAINTASKMANAGER); + ROUTE_IMPORT(ICARUS_IsRunning, G_ICARUS_ISRUNNING); + ROUTE_IMPORT(ICARUS_TaskIDPending, G_ICARUS_TASKIDPENDING); + ROUTE_IMPORT(ICARUS_InitEnt, G_ICARUS_INITENT); + ROUTE_IMPORT(ICARUS_FreeEnt, G_ICARUS_FREEENT); + ROUTE_IMPORT(ICARUS_AssociateEnt, G_ICARUS_ASSOCIATEENT); + ROUTE_IMPORT(ICARUS_Shutdown, G_ICARUS_SHUTDOWN); + ROUTE_IMPORT(ICARUS_TaskIDSet, G_ICARUS_TASKIDSET); + ROUTE_IMPORT(ICARUS_TaskIDComplete, G_ICARUS_TASKIDCOMPLETE); + ROUTE_IMPORT(ICARUS_SetVar, G_ICARUS_SETVAR); + ROUTE_IMPORT(ICARUS_VariableDeclared, G_ICARUS_VARIABLEDECLARED); + ROUTE_IMPORT(ICARUS_GetFloatVariable, G_ICARUS_GETFLOATVARIABLE); + ROUTE_IMPORT(ICARUS_GetStringVariable, G_ICARUS_GETSTRINGVARIABLE); + ROUTE_IMPORT(ICARUS_GetVectorVariable, G_ICARUS_GETVECTORVARIABLE); + ROUTE_IMPORT(Nav_Init, G_NAV_INIT); + ROUTE_IMPORT(Nav_Free, G_NAV_FREE); + ROUTE_IMPORT(Nav_Load, G_NAV_LOAD); + ROUTE_IMPORT(Nav_Save, G_NAV_SAVE); + ROUTE_IMPORT(Nav_AddRawPoint, G_NAV_ADDRAWPOINT); + ROUTE_IMPORT(Nav_CalculatePaths, G_NAV_CALCULATEPATHS); + ROUTE_IMPORT(Nav_HardConnect, G_NAV_HARDCONNECT); + ROUTE_IMPORT(Nav_ShowNodes, G_NAV_SHOWNODES); + ROUTE_IMPORT(Nav_ShowEdges, G_NAV_SHOWEDGES); + ROUTE_IMPORT(Nav_ShowPath, G_NAV_SHOWPATH); + ROUTE_IMPORT(Nav_GetNearestNode, G_NAV_GETNEARESTNODE); + ROUTE_IMPORT(Nav_GetBestNode, G_NAV_GETBESTNODE); + ROUTE_IMPORT(Nav_GetNodePosition, G_NAV_GETNODEPOSITION); + ROUTE_IMPORT(Nav_GetNodeNumEdges, G_NAV_GETNODENUMEDGES); + ROUTE_IMPORT(Nav_GetNodeEdge, G_NAV_GETNODEEDGE); + ROUTE_IMPORT(Nav_GetNumNodes, G_NAV_GETNUMNODES); + ROUTE_IMPORT(Nav_Connected, G_NAV_CONNECTED); + ROUTE_IMPORT(Nav_GetPathCost, G_NAV_GETPATHCOST); + ROUTE_IMPORT(Nav_GetEdgeCost, G_NAV_GETEDGECOST); + ROUTE_IMPORT(Nav_GetProjectedNode, G_NAV_GETPROJECTEDNODE); + ROUTE_IMPORT(Nav_CheckFailedNodes, G_NAV_CHECKFAILEDNODES); + ROUTE_IMPORT(Nav_AddFailedNode, G_NAV_ADDFAILEDNODE); + ROUTE_IMPORT(Nav_NodeFailed, G_NAV_NODEFAILED); + ROUTE_IMPORT(Nav_NodesAreNeighbors, G_NAV_NODESARENEIGHBORS); + ROUTE_IMPORT(Nav_ClearFailedEdge, G_NAV_CLEARFAILEDEDGE); + ROUTE_IMPORT(Nav_ClearAllFailedEdges, G_NAV_CLEARALLFAILEDEDGES); + ROUTE_IMPORT(Nav_EdgeFailed, G_NAV_EDGEFAILED); + ROUTE_IMPORT(Nav_AddFailedEdge, G_NAV_ADDFAILEDEDGE); + ROUTE_IMPORT(Nav_CheckFailedEdge, G_NAV_CHECKFAILEDEDGE); + ROUTE_IMPORT(Nav_CheckAllFailedEdges, G_NAV_CHECKALLFAILEDEDGES); + ROUTE_IMPORT(Nav_RouteBlocked, G_NAV_ROUTEBLOCKED); + ROUTE_IMPORT(Nav_GetBestNodeAltRoute, G_NAV_GETBESTNODEALTROUTE); + ROUTE_IMPORT(Nav_GetBestNodeAltRoute2, G_NAV_GETBESTNODEALT2); + ROUTE_IMPORT(Nav_GetBestPathBetweenEnts, G_NAV_GETBESTPATHBETWEENENTS); + ROUTE_IMPORT(Nav_GetNodeRadius, G_NAV_GETNODERADIUS); + ROUTE_IMPORT(Nav_CheckBlockedEdges, G_NAV_CHECKBLOCKEDEDGES); + ROUTE_IMPORT(Nav_ClearCheckedNodes, G_NAV_CLEARCHECKEDNODES); + ROUTE_IMPORT(Nav_CheckedNode, G_NAV_CHECKEDNODE); + ROUTE_IMPORT(Nav_SetCheckedNode, G_NAV_SETCHECKEDNODE); + ROUTE_IMPORT(Nav_FlagAllNodes, G_NAV_FLAGALLNODES); + ROUTE_IMPORT(Nav_GetPathsCalculated, G_NAV_GETPATHSCALCULATED); + ROUTE_IMPORT(Nav_SetPathsCalculated, G_NAV_SETPATHSCALCULATED); + ROUTE_IMPORT(BotAllocateClient, G_BOT_ALLOCATE_CLIENT); + ROUTE_IMPORT(BotFreeClient, G_BOT_FREE_CLIENT); + ROUTE_IMPORT(BotLoadCharacter, BOTLIB_AI_LOAD_CHARACTER); + ROUTE_IMPORT(BotFreeCharacter, BOTLIB_AI_FREE_CHARACTER); + ROUTE_IMPORT(Characteristic_Float, BOTLIB_AI_CHARACTERISTIC_FLOAT); + ROUTE_IMPORT(Characteristic_BFloat, BOTLIB_AI_CHARACTERISTIC_BFLOAT); + ROUTE_IMPORT(Characteristic_Integer, BOTLIB_AI_CHARACTERISTIC_INTEGER); + ROUTE_IMPORT(Characteristic_BInteger, BOTLIB_AI_CHARACTERISTIC_BINTEGER); + ROUTE_IMPORT(Characteristic_String, BOTLIB_AI_CHARACTERISTIC_STRING); + ROUTE_IMPORT(BotAllocChatState, BOTLIB_AI_ALLOC_CHAT_STATE); + ROUTE_IMPORT(BotFreeChatState, BOTLIB_AI_FREE_CHAT_STATE); + ROUTE_IMPORT(BotQueueConsoleMessage, BOTLIB_AI_QUEUE_CONSOLE_MESSAGE); + ROUTE_IMPORT(BotRemoveConsoleMessage, BOTLIB_AI_REMOVE_CONSOLE_MESSAGE); + ROUTE_IMPORT(BotNextConsoleMessage, BOTLIB_AI_NEXT_CONSOLE_MESSAGE); + ROUTE_IMPORT(BotNumConsoleMessages, BOTLIB_AI_NUM_CONSOLE_MESSAGE); + ROUTE_IMPORT(BotInitialChat, BOTLIB_AI_INITIAL_CHAT); + ROUTE_IMPORT(BotReplyChat, BOTLIB_AI_REPLY_CHAT); + ROUTE_IMPORT(BotChatLength, BOTLIB_AI_CHAT_LENGTH); + ROUTE_IMPORT(BotEnterChat, BOTLIB_AI_ENTER_CHAT); + ROUTE_IMPORT(StringContains, BOTLIB_AI_STRING_CONTAINS); + ROUTE_IMPORT(BotFindMatch, BOTLIB_AI_FIND_MATCH); + ROUTE_IMPORT(BotMatchVariable, BOTLIB_AI_MATCH_VARIABLE); + ROUTE_IMPORT(UnifyWhiteSpaces, BOTLIB_AI_UNIFY_WHITE_SPACES); + ROUTE_IMPORT(BotReplaceSynonyms, BOTLIB_AI_REPLACE_SYNONYMS); + ROUTE_IMPORT(BotLoadChatFile, BOTLIB_AI_LOAD_CHAT_FILE); + ROUTE_IMPORT(BotSetChatGender, BOTLIB_AI_SET_CHAT_GENDER); + ROUTE_IMPORT(BotSetChatName, BOTLIB_AI_SET_CHAT_NAME); + ROUTE_IMPORT(BotResetGoalState, BOTLIB_AI_RESET_GOAL_STATE); + ROUTE_IMPORT(BotResetAvoidGoals, BOTLIB_AI_RESET_AVOID_GOALS); + ROUTE_IMPORT(BotPushGoal, BOTLIB_AI_PUSH_GOAL); + ROUTE_IMPORT(BotPopGoal, BOTLIB_AI_POP_GOAL); + ROUTE_IMPORT(BotEmptyGoalStack, BOTLIB_AI_EMPTY_GOAL_STACK); + ROUTE_IMPORT(BotDumpAvoidGoals, BOTLIB_AI_DUMP_AVOID_GOALS); + ROUTE_IMPORT(BotDumpGoalStack, BOTLIB_AI_DUMP_GOAL_STACK); + ROUTE_IMPORT(BotGoalName, BOTLIB_AI_GOAL_NAME); + ROUTE_IMPORT(BotGetTopGoal, BOTLIB_AI_GET_TOP_GOAL); + ROUTE_IMPORT(BotGetSecondGoal, BOTLIB_AI_GET_SECOND_GOAL); + ROUTE_IMPORT(BotChooseLTGItem, BOTLIB_AI_CHOOSE_LTG_ITEM); + ROUTE_IMPORT(BotChooseNBGItem, BOTLIB_AI_CHOOSE_NBG_ITEM); + ROUTE_IMPORT(BotTouchingGoal, BOTLIB_AI_TOUCHING_GOAL); + ROUTE_IMPORT(BotItemGoalInVisButNotVisible, BOTLIB_AI_ITEM_GOAL_IN_VIS_BUT_NOT_VISIBLE); + ROUTE_IMPORT(BotGetLevelItemGoal, BOTLIB_AI_GET_LEVEL_ITEM_GOAL); + ROUTE_IMPORT(BotAvoidGoalTime, BOTLIB_AI_AVOID_GOAL_TIME); + ROUTE_IMPORT(BotInitLevelItems, BOTLIB_AI_INIT_LEVEL_ITEMS); + ROUTE_IMPORT(BotUpdateEntityItems, BOTLIB_AI_UPDATE_ENTITY_ITEMS); + ROUTE_IMPORT(BotLoadItemWeights, BOTLIB_AI_LOAD_ITEM_WEIGHTS); + ROUTE_IMPORT(BotFreeItemWeights, BOTLIB_AI_FREE_ITEM_WEIGHTS); + ROUTE_IMPORT(BotSaveGoalFuzzyLogic, BOTLIB_AI_SAVE_GOAL_FUZZY_LOGIC); + ROUTE_IMPORT(BotAllocGoalState, BOTLIB_AI_ALLOC_GOAL_STATE); + ROUTE_IMPORT(BotFreeGoalState, BOTLIB_AI_FREE_GOAL_STATE); + ROUTE_IMPORT(BotResetMoveState, BOTLIB_AI_RESET_MOVE_STATE); + ROUTE_IMPORT(BotMoveToGoal, BOTLIB_AI_MOVE_TO_GOAL); + ROUTE_IMPORT(BotMoveInDirection, BOTLIB_AI_MOVE_IN_DIRECTION); + ROUTE_IMPORT(BotResetAvoidReach, BOTLIB_AI_RESET_AVOID_REACH); + ROUTE_IMPORT(BotResetLastAvoidReach, BOTLIB_AI_RESET_LAST_AVOID_REACH); + ROUTE_IMPORT(BotReachabilityArea, BOTLIB_AI_REACHABILITY_AREA); + ROUTE_IMPORT(BotMovementViewTarget, BOTLIB_AI_MOVEMENT_VIEW_TARGET); + ROUTE_IMPORT(BotAllocMoveState, BOTLIB_AI_ALLOC_MOVE_STATE); + ROUTE_IMPORT(BotFreeMoveState, BOTLIB_AI_FREE_MOVE_STATE); + ROUTE_IMPORT(BotInitMoveState, BOTLIB_AI_INIT_MOVE_STATE); + ROUTE_IMPORT(BotChooseBestFightWeapon, BOTLIB_AI_CHOOSE_BEST_FIGHT_WEAPON); + ROUTE_IMPORT(BotGetWeaponInfo, BOTLIB_AI_GET_WEAPON_INFO); + ROUTE_IMPORT(BotLoadWeaponWeights, BOTLIB_AI_LOAD_WEAPON_WEIGHTS); + ROUTE_IMPORT(BotAllocWeaponState, BOTLIB_AI_ALLOC_WEAPON_STATE); + ROUTE_IMPORT(BotFreeWeaponState, BOTLIB_AI_FREE_WEAPON_STATE); + ROUTE_IMPORT(BotResetWeaponState, BOTLIB_AI_RESET_WEAPON_STATE); + ROUTE_IMPORT(GeneticParentsAndChildSelection, BOTLIB_AI_GENETIC_PARENTS_AND_CHILD_SELECTION); + ROUTE_IMPORT(BotInterbreedGoalFuzzyLogic, BOTLIB_AI_INTERBREED_GOAL_FUZZY_LOGIC); + ROUTE_IMPORT(BotMutateGoalFuzzyLogic, BOTLIB_AI_MUTATE_GOAL_FUZZY_LOGIC); + ROUTE_IMPORT(BotGetNextCampSpotGoal, BOTLIB_AI_GET_NEXT_CAMP_SPOT_GOAL); + ROUTE_IMPORT(BotGetMapLocationGoal, BOTLIB_AI_GET_MAP_LOCATION_GOAL); + ROUTE_IMPORT(BotNumInitialChats, BOTLIB_AI_NUM_INITIAL_CHATS); + ROUTE_IMPORT(BotGetChatMessage, BOTLIB_AI_GET_CHAT_MESSAGE); + ROUTE_IMPORT(BotRemoveFromAvoidGoals, BOTLIB_AI_REMOVE_FROM_AVOID_GOALS); + ROUTE_IMPORT(BotPredictVisiblePosition, BOTLIB_AI_PREDICT_VISIBLE_POSITION); + ROUTE_IMPORT(BotSetAvoidGoalTime, BOTLIB_AI_SET_AVOID_GOAL_TIME); + ROUTE_IMPORT(BotAddAvoidSpot, BOTLIB_AI_ADD_AVOID_SPOT); + ROUTE_IMPORT(BotLibSetup, BOTLIB_SETUP); + ROUTE_IMPORT(BotLibShutdown, BOTLIB_SHUTDOWN); + ROUTE_IMPORT(BotLibVarSet, BOTLIB_LIBVAR_SET); + ROUTE_IMPORT(BotLibVarGet, BOTLIB_LIBVAR_GET); + ROUTE_IMPORT(BotLibDefine, BOTLIB_PC_ADD_GLOBAL_DEFINE); + ROUTE_IMPORT(BotLibStartFrame, BOTLIB_START_FRAME); + ROUTE_IMPORT(BotLibLoadMap, BOTLIB_LOAD_MAP); + ROUTE_IMPORT(BotLibUpdateEntity, BOTLIB_UPDATENTITY); + ROUTE_IMPORT(BotLibTest, BOTLIB_TEST); + ROUTE_IMPORT(BotGetSnapshotEntity, BOTLIB_GET_SNAPSHOT_ENTITY); + ROUTE_IMPORT(BotGetServerCommand, BOTLIB_GET_CONSOLE_MESSAGE); + ROUTE_IMPORT(BotUserCommand, BOTLIB_USER_COMMAND); + ROUTE_IMPORT(BotUpdateWaypoints, G_BOT_UPDATEWAYPOINTS); + ROUTE_IMPORT(BotCalculatePaths, G_BOT_CALCULATEPATHS); + ROUTE_IMPORT(AAS_EnableRoutingArea, BOTLIB_AAS_ENABLE_ROUTING_AREA); + ROUTE_IMPORT(AAS_BBoxAreas, BOTLIB_AAS_BBOX_AREAS); + ROUTE_IMPORT(AAS_AreaInfo, BOTLIB_AAS_AREA_INFO); + ROUTE_IMPORT(AAS_EntityInfo, BOTLIB_AAS_ENTITY_INFO); + ROUTE_IMPORT(AAS_Initialized, BOTLIB_AAS_INITIALIZED); + ROUTE_IMPORT(AAS_PresenceTypeBoundingBox, BOTLIB_AAS_PRESENCE_TYPE_BOUNDING_BOX); + ROUTE_IMPORT(AAS_Time, BOTLIB_AAS_TIME); + ROUTE_IMPORT(AAS_PointAreaNum, BOTLIB_AAS_POINT_AREA_NUM); + ROUTE_IMPORT(AAS_TraceAreas, BOTLIB_AAS_TRACE_AREAS); + ROUTE_IMPORT(AAS_PointContents, BOTLIB_AAS_POINT_CONTENTS); + ROUTE_IMPORT(AAS_NextBSPEntity, BOTLIB_AAS_NEXT_BSP_ENTITY); + ROUTE_IMPORT(AAS_ValueForBSPEpairKey, BOTLIB_AAS_VALUE_FOR_BSP_EPAIR_KEY); + ROUTE_IMPORT(AAS_VectorForBSPEpairKey, BOTLIB_AAS_VECTOR_FOR_BSP_EPAIR_KEY); + ROUTE_IMPORT(AAS_FloatForBSPEpairKey, BOTLIB_AAS_FLOAT_FOR_BSP_EPAIR_KEY); + ROUTE_IMPORT(AAS_IntForBSPEpairKey, BOTLIB_AAS_INT_FOR_BSP_EPAIR_KEY); + ROUTE_IMPORT(AAS_AreaReachability, BOTLIB_AAS_AREA_REACHABILITY); + ROUTE_IMPORT(AAS_AreaTravelTimeToGoalArea, BOTLIB_AAS_AREA_TRAVEL_TIME_TO_GOAL_AREA); + ROUTE_IMPORT(AAS_Swimming, BOTLIB_AAS_SWIMMING); + ROUTE_IMPORT(AAS_PredictClientMovement, BOTLIB_AAS_PREDICT_CLIENT_MOVEMENT); + ROUTE_IMPORT(AAS_AlternativeRouteGoals, BOTLIB_AAS_ALTERNATIVE_ROUTE_GOAL); + ROUTE_IMPORT(AAS_PredictRoute, BOTLIB_AAS_PREDICT_ROUTE); + ROUTE_IMPORT(AAS_PointReachabilityAreaIndex, BOTLIB_AAS_POINT_REACHABILITY_AREA_INDEX); + ROUTE_IMPORT(EA_Say, BOTLIB_EA_SAY); + ROUTE_IMPORT(EA_SayTeam, BOTLIB_EA_SAY_TEAM); + ROUTE_IMPORT(EA_Command, BOTLIB_EA_COMMAND); + ROUTE_IMPORT(EA_Action, BOTLIB_EA_ACTION); + ROUTE_IMPORT(EA_Gesture, BOTLIB_EA_GESTURE); + ROUTE_IMPORT(EA_Talk, BOTLIB_EA_TALK); + ROUTE_IMPORT(EA_Attack, BOTLIB_EA_ATTACK); + ROUTE_IMPORT(EA_Alt_Attack, BOTLIB_EA_ALT_ATTACK); + ROUTE_IMPORT(EA_ForcePower, BOTLIB_EA_FORCEPOWER); + ROUTE_IMPORT(EA_Use, BOTLIB_EA_USE); + ROUTE_IMPORT(EA_Respawn, BOTLIB_EA_RESPAWN); + ROUTE_IMPORT(EA_Crouch, BOTLIB_EA_CROUCH); + ROUTE_IMPORT(EA_MoveUp, BOTLIB_EA_MOVE_UP); + ROUTE_IMPORT(EA_MoveDown, BOTLIB_EA_MOVE_DOWN); + ROUTE_IMPORT(EA_MoveForward, BOTLIB_EA_MOVE_FORWARD); + ROUTE_IMPORT(EA_MoveBack, BOTLIB_EA_MOVE_BACK); + ROUTE_IMPORT(EA_MoveLeft, BOTLIB_EA_MOVE_LEFT); + ROUTE_IMPORT(EA_MoveRight, BOTLIB_EA_MOVE_RIGHT); + ROUTE_IMPORT(EA_SelectWeapon, BOTLIB_EA_SELECT_WEAPON); + ROUTE_IMPORT(EA_Jump, BOTLIB_EA_JUMP); + ROUTE_IMPORT(EA_DelayedJump, BOTLIB_EA_DELAYED_JUMP); + ROUTE_IMPORT(EA_Move, BOTLIB_EA_MOVE); + ROUTE_IMPORT(EA_View, BOTLIB_EA_VIEW); + ROUTE_IMPORT(EA_EndRegular, BOTLIB_EA_END_REGULAR); + ROUTE_IMPORT(EA_GetInput, BOTLIB_EA_GET_INPUT); + ROUTE_IMPORT(EA_ResetInput, BOTLIB_EA_RESET_INPUT); + ROUTE_IMPORT(PC_LoadSource, BOTLIB_PC_LOAD_SOURCE); + ROUTE_IMPORT(PC_FreeSource, BOTLIB_PC_FREE_SOURCE); + ROUTE_IMPORT(PC_ReadToken, BOTLIB_PC_READ_TOKEN); + ROUTE_IMPORT(PC_SourceFileAndLine, BOTLIB_PC_SOURCE_FILE_AND_LINE); + ROUTE_IMPORT(R_RegisterSkin, G_R_REGISTERSKIN); + ROUTE_IMPORT(SetActiveSubBSP, G_SET_ACTIVE_SUBBSP); + ROUTE_IMPORT(CM_RegisterTerrain, G_CM_REGISTER_TERRAIN); + ROUTE_IMPORT(RMG_Init, G_RMG_INIT); + ROUTE_IMPORT(G2API_ListModelBones, G_G2_LISTBONES); + ROUTE_IMPORT(G2API_ListModelSurfaces, G_G2_LISTSURFACES); + ROUTE_IMPORT(G2API_HaveWeGhoul2Models, G_G2_HAVEWEGHOULMODELS); + ROUTE_IMPORT(G2API_SetGhoul2ModelIndexes, G_G2_SETMODELS); + ROUTE_IMPORT(G2API_GetBoltMatrix, G_G2_GETBOLT); + ROUTE_IMPORT(G2API_GetBoltMatrix_NoReconstruct, G_G2_GETBOLT_NOREC); + ROUTE_IMPORT(G2API_GetBoltMatrix_NoRecNoRot, G_G2_GETBOLT_NOREC_NOROT); + ROUTE_IMPORT(G2API_InitGhoul2Model, G_G2_INITGHOUL2MODEL); + ROUTE_IMPORT(G2API_SetSkin, G_G2_SETSKIN); + ROUTE_IMPORT(G2API_Ghoul2Size, G_G2_SIZE); + ROUTE_IMPORT(G2API_AddBolt, G_G2_ADDBOLT); + ROUTE_IMPORT(G2API_SetBoltInfo, G_G2_SETBOLTINFO); + ROUTE_IMPORT(G2API_SetBoneAngles, G_G2_ANGLEOVERRIDE); + ROUTE_IMPORT(G2API_SetBoneAnim, G_G2_PLAYANIM); + ROUTE_IMPORT(G2API_GetBoneAnim, G_G2_GETBONEANIM); + ROUTE_IMPORT(G2API_GetGLAName, G_G2_GETGLANAME); + ROUTE_IMPORT(G2API_CopyGhoul2Instance, G_G2_COPYGHOUL2INSTANCE); + ROUTE_IMPORT(G2API_CopySpecificGhoul2Model, G_G2_COPYSPECIFICGHOUL2MODEL); + ROUTE_IMPORT(G2API_DuplicateGhoul2Instance, G_G2_DUPLICATEGHOUL2INSTANCE); + ROUTE_IMPORT(G2API_HasGhoul2ModelOnIndex, G_G2_HASGHOUL2MODELONINDEX); + ROUTE_IMPORT(G2API_RemoveGhoul2Model, G_G2_REMOVEGHOUL2MODEL); + ROUTE_IMPORT(G2API_RemoveGhoul2Models, G_G2_REMOVEGHOUL2MODELS); + ROUTE_IMPORT(G2API_CleanGhoul2Models, G_G2_CLEANMODELS); + ROUTE_IMPORT(G2API_CollisionDetect, G_G2_COLLISIONDETECT); + ROUTE_IMPORT(G2API_CollisionDetectCache, G_G2_COLLISIONDETECTCACHE); + ROUTE_IMPORT(G2API_SetRootSurface, G_G2_SETROOTSURFACE); + ROUTE_IMPORT(G2API_SetSurfaceOnOff, G_G2_SETSURFACEONOFF); + ROUTE_IMPORT(G2API_SetNewOrigin, G_G2_SETNEWORIGIN); + ROUTE_IMPORT(G2API_DoesBoneExist, G_G2_DOESBONEEXIST); + ROUTE_IMPORT(G2API_GetSurfaceRenderStatus, G_G2_GETSURFACERENDERSTATUS); + ROUTE_IMPORT(G2API_AbsurdSmoothing, G_G2_ABSURDSMOOTHING); + ROUTE_IMPORT(G2API_SetRagDoll, G_G2_SETRAGDOLL); + ROUTE_IMPORT(G2API_AnimateG2Models, G_G2_ANIMATEG2MODELS); + ROUTE_IMPORT(G2API_RagPCJConstraint, G_G2_RAGPCJCONSTRAINT); + ROUTE_IMPORT(G2API_RagPCJGradientSpeed, G_G2_RAGPCJGRADIENTSPEED); + ROUTE_IMPORT(G2API_RagEffectorGoal, G_G2_RAGEFFECTORGOAL); + ROUTE_IMPORT(G2API_GetRagBonePos, G_G2_GETRAGBONEPOS); + ROUTE_IMPORT(G2API_RagEffectorKick, G_G2_RAGEFFECTORKICK); + ROUTE_IMPORT(G2API_RagForceSolve, G_G2_RAGFORCESOLVE); + ROUTE_IMPORT(G2API_SetBoneIKState, G_G2_SETBONEIKSTATE); + ROUTE_IMPORT(G2API_IKMove, G_G2_IKMOVE); + ROUTE_IMPORT(G2API_RemoveBone, G_G2_REMOVEBONE); + ROUTE_IMPORT(G2API_AttachInstanceToEntNum, G_G2_ATTACHINSTANCETOENTNUM); + ROUTE_IMPORT(G2API_ClearAttachedInstance, G_G2_CLEARATTACHEDINSTANCE); + ROUTE_IMPORT(G2API_CleanEntAttachments, G_G2_CLEANENTATTACHMENTS); + ROUTE_IMPORT(G2API_OverrideServer, G_G2_OVERRIDESERVER); + ROUTE_IMPORT(G2API_GetSurfaceName, G_G2_GETSURFACENAME); - default: - // all normal engine functions go to engine - ret = orig_syscall(cmd, QMM_PUT_SYSCALL_ARGS()); + // handle special cmds which QMM uses but JAMP doesn't have an analogue for + case G_ARGS: { + // quake2: char* (*args)(void); + static std::string s; + static char buf[MAX_STRING_CHARS]; + s = ""; + int i = 1; + while (i < orig_import.Argc()) { + orig_import.Argv(i, buf, sizeof(buf)); + buf[sizeof(buf) - 1] = '\0'; + if (i != 1) + s += " "; + s += buf; + } + ret = (intptr_t)s.c_str(); + break; + } + + default: + break; + }; } // do anything that needs to be done after function call here @@ -105,14 +832,62 @@ static intptr_t JAMP_vmMain(intptr_t cmd, ...) { LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_vmMain({} {}) called\n", JAMP_mod_msg_names(cmd), cmd); #endif - if (!orig_vmMain) - return 0; - // store return value since we do some stuff after the function call is over intptr_t ret = 0; - // all normal mod functions go to mod - ret = orig_vmMain(cmd, QMM_PUT_VMMAIN_ARGS()); + // if QMM was loaded with the official JAMP or OpenJK "legacy" API (which shouldn't happen, but whatever) + if (orig_vmMain) { + // all normal mod functions go to mod + ret = orig_vmMain(cmd, QMM_PUT_VMMAIN_ARGS()); + } + // if QMM was loaded with the OpenJK "new" API + else if (orig_export) { + switch (cmd) { + ROUTE_EXPORT(InitGame, GAME_INIT); + ROUTE_EXPORT(ShutdownGame, GAME_SHUTDOWN); + ROUTE_EXPORT(ClientConnect, GAME_CLIENT_CONNECT); + ROUTE_EXPORT(ClientBegin, GAME_CLIENT_BEGIN); + ROUTE_EXPORT(ClientUserinfoChanged, GAME_CLIENT_USERINFO_CHANGED); + ROUTE_EXPORT(ClientDisconnect, GAME_CLIENT_DISCONNECT); + ROUTE_EXPORT(ClientCommand, GAME_CLIENT_COMMAND); + ROUTE_EXPORT(ClientThink, GAME_CLIENT_THINK); + ROUTE_EXPORT(RunFrame, GAME_RUN_FRAME); + ROUTE_EXPORT(ConsoleCommand, GAME_CONSOLE_COMMAND); + ROUTE_EXPORT(BotAIStartFrame, BOTAI_START_FRAME); + ROUTE_EXPORT(ROFF_NotetrackCallback, GAME_ROFF_NOTETRACK_CALLBACK); + ROUTE_EXPORT(SpawnRMGEntity, GAME_SPAWN_RMG_ENTITY); + ROUTE_EXPORT(ICARUS_PlaySound, GAME_ICARUS_PLAYSOUND); + ROUTE_EXPORT(ICARUS_Set, GAME_ICARUS_SET); + ROUTE_EXPORT(ICARUS_Lerp2Pos, GAME_ICARUS_LERP2POS); + ROUTE_EXPORT(ICARUS_Lerp2Origin, GAME_ICARUS_LERP2ORIGIN); + ROUTE_EXPORT(ICARUS_Lerp2Angles, GAME_ICARUS_LERP2ANGLES); + ROUTE_EXPORT(ICARUS_GetTag, GAME_ICARUS_GETTAG); + ROUTE_EXPORT(ICARUS_Lerp2Start, GAME_ICARUS_LERP2START); + ROUTE_EXPORT(ICARUS_Lerp2End, GAME_ICARUS_LERP2END); + ROUTE_EXPORT(ICARUS_Use, GAME_ICARUS_USE); + ROUTE_EXPORT(ICARUS_Kill, GAME_ICARUS_KILL); + ROUTE_EXPORT(ICARUS_Remove, GAME_ICARUS_REMOVE); + ROUTE_EXPORT(ICARUS_Play, GAME_ICARUS_PLAY); + ROUTE_EXPORT(ICARUS_GetFloat, GAME_ICARUS_GETFLOAT); + ROUTE_EXPORT(ICARUS_GetVector, GAME_ICARUS_GETVECTOR); + ROUTE_EXPORT(ICARUS_GetString, GAME_ICARUS_GETSTRING); + ROUTE_EXPORT(ICARUS_SoundIndex, GAME_ICARUS_SOUNDINDEX); + ROUTE_EXPORT(ICARUS_GetSetIDForString, GAME_ICARUS_GETSETIDFORSTRING); + ROUTE_EXPORT(NAV_ClearPathToPoint, GAME_NAV_CLEARPATHTOPOINT); + ROUTE_EXPORT(NPC_ClearLOS2, GAME_NAV_CLEARLOS); + ROUTE_EXPORT(NAVNEW_ClearPathBetweenPoints, GAME_NAV_CLEARPATHBETWEENPOINTS); + ROUTE_EXPORT(NAV_CheckNodeFailedForEnt, GAME_NAV_CHECKNODEFAILEDFORENT); + ROUTE_EXPORT(NAV_EntIsUnlockedDoor, GAME_NAV_ENTISUNLOCKEDDOOR); + ROUTE_EXPORT(NAV_EntIsDoor, GAME_NAV_ENTISDOOR); + ROUTE_EXPORT(NAV_EntIsBreakable, GAME_NAV_ENTISBREAKABLE); + ROUTE_EXPORT(NAV_EntIsRemovableUsable, GAME_NAV_ENTISREMOVABLEUSABLE); + ROUTE_EXPORT(NAV_FindCombatPointWaypoints, GAME_NAV_FINDCOMBATPOINTWAYPOINTS); + ROUTE_EXPORT(BG_GetItemIndexByTag, GAME_GETITEMINDEXBYTAG); + + default: + break; + }; + } #ifdef _DEBUG LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_vmMain({} {}) returning {}\n", JAMP_mod_msg_names(cmd), cmd, ret); @@ -128,24 +903,60 @@ static void JAMP_dllEntry(eng_syscall syscall) { // store original syscall from engine orig_syscall = syscall; - // pointer to wrapper vmMain function that calls actual mod vmMain func orig_vmMain + // 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 actual engine syscall func + // 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_dllEntry({}) returning\n", (void*)syscall); } -static bool JAMP_mod_load(void* entry) { - orig_vmMain = (mod_vmMain)entry; +static void* JAMP_GetGameAPI(void* apiversion, void* import) { + orig_apiversion = (intptr_t)apiversion; + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("JAMP_GetGameAPI({}, {}) called\n", orig_apiversion, import); - return !!orig_vmMain; + // 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 + game_import_t* gi = (game_import_t*)import; + orig_import = *gi; + + // 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_GetGameAPI({}, {}) returning {}\n", orig_apiversion, import, (void*)&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. + // the only thing in this struct the engine uses before calling Init is the apiversion + return &qmm_export; + +} + + +static bool JAMP_mod_load(void* entry, bool is_GetGameAPI) { + if (is_GetGameAPI) { + mod_GetGameAPI pfnGGA = (mod_GetGameAPI)entry; + // api version gets passed before import pointer + orig_export = (game_export_t*)pfnGGA((void*)orig_apiversion, &qmm_import); + + return !!orig_export; + } + else { + orig_vmMain = (mod_vmMain)entry; + return !!orig_vmMain; + } } static void JAMP_mod_unload() { + orig_export = nullptr; orig_vmMain = nullptr; } @@ -482,6 +1293,9 @@ static const char* JAMP_eng_msg_names(intptr_t cmd) { GEN_CASE(G_RMG_INIT); GEN_CASE(G_BOT_UPDATEWAYPOINTS); GEN_CASE(G_BOT_CALCULATEPATHS); + + // polyfills + GEN_CASE(G_ARGS); default: return "unknown"; } @@ -531,9 +1345,6 @@ static const char* JAMP_mod_msg_names(intptr_t cmd) { GEN_CASE(GAME_NAV_FINDCOMBATPOINTWAYPOINTS); GEN_CASE(GAME_GETITEMINDEXBYTAG); - // polyfills - GEN_CASE(G_ARGS); - default: return "unknown"; } diff --git a/src/game_jasp.cpp b/src/game_jasp.cpp index 4dc33b3..11334dc 100644 --- a/src/game_jasp.cpp +++ b/src/game_jasp.cpp @@ -575,7 +575,7 @@ static void* JASP_GetGameAPI(void* import, void*) { } -static bool JASP_mod_load(void* entry) { +static bool JASP_mod_load(void* entry, bool) { mod_GetGameAPI pfnGGA = (mod_GetGameAPI)entry; orig_export = (game_export_t*)pfnGGA(&qmm_import, nullptr); diff --git a/src/game_jk2mp.cpp b/src/game_jk2mp.cpp index dda158c..0c80757 100644 --- a/src/game_jk2mp.cpp +++ b/src/game_jk2mp.cpp @@ -47,7 +47,7 @@ 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 from orig_import +// 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, ...) { QMM_GET_SYSCALL_ARGS(); @@ -68,7 +68,7 @@ static intptr_t JK2MP_syscall(intptr_t cmd, ...) { s = ""; int i = 1; while (i < orig_syscall(G_ARGC)) { - orig_syscall(G_ARGV, buf, sizeof(buf)); + orig_syscall(G_ARGV, i, buf, sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; if (i != 1) s += " "; @@ -94,7 +94,7 @@ static intptr_t JK2MP_syscall(intptr_t cmd, ...) { } -// wrapper vmMain function that calls actual mod func from orig_export +// 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, ...) { QMM_GET_VMMAIN_ARGS(); @@ -142,7 +142,7 @@ static void JK2MP_dllEntry(eng_syscall syscall) { } -static bool JK2MP_mod_load(void* entry) { +static bool JK2MP_mod_load(void* entry, bool) { orig_vmMain = (mod_vmMain)entry; return !!orig_vmMain; diff --git a/src/game_jk2sp.cpp b/src/game_jk2sp.cpp index 2896bb3..57af74a 100644 --- a/src/game_jk2sp.cpp +++ b/src/game_jk2sp.cpp @@ -497,7 +497,7 @@ static void* JK2SP_GetGameAPI(void* import, void*) { } -static bool JK2SP_mod_load(void* entry) { +static bool JK2SP_mod_load(void* entry, bool) { mod_GetGameAPI pfnGGA = (mod_GetGameAPI)entry; orig_export = (game_export_t*)pfnGGA(&qmm_import, nullptr); diff --git a/src/game_mohaa.cpp b/src/game_mohaa.cpp index b9c0f38..0e16fb6 100644 --- a/src/game_mohaa.cpp +++ b/src/game_mohaa.cpp @@ -771,7 +771,7 @@ static void* MOHAA_GetGameAPI(void* import, void*) { } -static bool MOHAA_mod_load(void* entry) { +static bool MOHAA_mod_load(void* entry, bool) { mod_GetGameAPI pfnGGA = (mod_GetGameAPI)entry; orig_export = (game_export_t*)pfnGGA(&qmm_import, nullptr); diff --git a/src/game_mohbt.cpp b/src/game_mohbt.cpp index 503ec00..14de074 100644 --- a/src/game_mohbt.cpp +++ b/src/game_mohbt.cpp @@ -810,7 +810,7 @@ static void* MOHBT_GetGameAPI(void* import, void*) { } -static bool MOHBT_mod_load(void* entry) { +static bool MOHBT_mod_load(void* entry, bool) { mod_GetGameAPI pfnGGA = (mod_GetGameAPI)entry; orig_export = (game_export_t*)pfnGGA(&qmm_import, nullptr); diff --git a/src/game_mohsh.cpp b/src/game_mohsh.cpp index 6aea6de..d1d106c 100644 --- a/src/game_mohsh.cpp +++ b/src/game_mohsh.cpp @@ -810,7 +810,7 @@ static void* MOHSH_GetGameAPI(void* import, void*) { } -static bool MOHSH_mod_load(void* entry) { +static bool MOHSH_mod_load(void* entry, bool) { mod_GetGameAPI pfnGGA = (mod_GetGameAPI)entry; orig_export = (game_export_t*)pfnGGA(&qmm_import, nullptr); diff --git a/src/game_q2r.cpp b/src/game_q2r.cpp index 55b62b1..e60e99a 100644 --- a/src/game_q2r.cpp +++ b/src/game_q2r.cpp @@ -623,7 +623,7 @@ static void* Q2R_GetGameAPI(void* import, void*) { } -static bool Q2R_mod_load(void* entry) { +static bool Q2R_mod_load(void* entry, bool) { mod_GetGameAPI pfnGGA = (mod_GetGameAPI)entry; orig_export = (game_export_t*)pfnGGA(&qmm_import, nullptr); diff --git a/src/game_q3a.cpp b/src/game_q3a.cpp index 97f6c0e..1011782 100644 --- a/src/game_q3a.cpp +++ b/src/game_q3a.cpp @@ -47,7 +47,7 @@ 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 from orig_import +// 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, ...) { QMM_GET_SYSCALL_ARGS(); @@ -68,7 +68,7 @@ static intptr_t Q3A_syscall(intptr_t cmd, ...) { s = ""; int i = 1; while (i < orig_syscall(G_ARGC)) { - orig_syscall(G_ARGV, buf, sizeof(buf)); + orig_syscall(G_ARGV, i, buf, sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; if (i != 1) s += " "; @@ -93,7 +93,7 @@ static intptr_t Q3A_syscall(intptr_t cmd, ...) { } -// wrapper vmMain function that calls actual mod func from orig_export +// 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, ...) { QMM_GET_VMMAIN_ARGS(); @@ -141,7 +141,7 @@ static void Q3A_dllEntry(eng_syscall syscall) { } -static bool Q3A_mod_load(void* entry) { +static bool Q3A_mod_load(void* entry, bool) { orig_vmMain = (mod_vmMain)entry; return !!orig_vmMain; diff --git a/src/game_quake2.cpp b/src/game_quake2.cpp index 5f922dd..845322e 100644 --- a/src/game_quake2.cpp +++ b/src/game_quake2.cpp @@ -540,7 +540,7 @@ static void* QUAKE2_GetGameAPI(void* import, void*) { } -static bool QUAKE2_mod_load(void* entry) { +static bool QUAKE2_mod_load(void* entry, bool) { mod_GetGameAPI pfnGGA = (mod_GetGameAPI)entry; orig_export = (game_export_t*)pfnGGA(&qmm_import, nullptr); diff --git a/src/game_rtcwmp.cpp b/src/game_rtcwmp.cpp index 5f75a01..a7a8e7b 100644 --- a/src/game_rtcwmp.cpp +++ b/src/game_rtcwmp.cpp @@ -46,7 +46,7 @@ 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 from orig_import +// 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, ...) { QMM_GET_SYSCALL_ARGS(); @@ -67,7 +67,7 @@ static intptr_t RTCWMP_syscall(intptr_t cmd, ...) { s = ""; int i = 1; while (i < orig_syscall(G_ARGC)) { - orig_syscall(G_ARGV, buf, sizeof(buf)); + orig_syscall(G_ARGV, i, buf, sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; if (i != 1) s += " "; @@ -93,7 +93,7 @@ static intptr_t RTCWMP_syscall(intptr_t cmd, ...) { } -// wrapper vmMain function that calls actual mod func from orig_export +// 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, ...) { QMM_GET_VMMAIN_ARGS(); @@ -135,7 +135,7 @@ static void RTCWMP_dllEntry(eng_syscall syscall) { } -static bool RTCWMP_mod_load(void* entry) { +static bool RTCWMP_mod_load(void* entry, bool) { orig_vmMain = (mod_vmMain)entry; return !!orig_vmMain; diff --git a/src/game_rtcwsp.cpp b/src/game_rtcwsp.cpp index 81d63de..55fec7d 100644 --- a/src/game_rtcwsp.cpp +++ b/src/game_rtcwsp.cpp @@ -46,7 +46,7 @@ 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 from orig_import +// 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, ...) { QMM_GET_SYSCALL_ARGS(); @@ -67,7 +67,7 @@ static intptr_t RTCWSP_syscall(intptr_t cmd, ...) { s = ""; int i = 1; while (i < orig_syscall(G_ARGC)) { - orig_syscall(G_ARGV, buf, sizeof(buf)); + orig_syscall(G_ARGV, i, buf, sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; if (i != 1) s += " "; @@ -93,7 +93,7 @@ static intptr_t RTCWSP_syscall(intptr_t cmd, ...) { } -// wrapper vmMain function that calls actual mod func from orig_export +// 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, ...) { QMM_GET_VMMAIN_ARGS(); @@ -135,7 +135,7 @@ static void RTCWSP_dllEntry(eng_syscall syscall) { } -static bool RTCWSP_mod_load(void* entry) { +static bool RTCWSP_mod_load(void* entry, bool) { orig_vmMain = (mod_vmMain)entry; return !!orig_vmMain; diff --git a/src/game_sin.cpp b/src/game_sin.cpp index 3c44a5c..decdc5e 100644 --- a/src/game_sin.cpp +++ b/src/game_sin.cpp @@ -657,7 +657,7 @@ static void* SIN_GetGameAPI(void* import, void*) { } -static bool SIN_mod_load(void* entry) { +static bool SIN_mod_load(void* entry, bool) { mod_GetGameAPI pfnGGA = (mod_GetGameAPI)entry; orig_export = (game_export_t*)pfnGGA(&qmm_import, nullptr); diff --git a/src/game_sof2mp.cpp b/src/game_sof2mp.cpp index ec2638a..6e3f16a 100644 --- a/src/game_sof2mp.cpp +++ b/src/game_sof2mp.cpp @@ -48,7 +48,7 @@ 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 from orig_import +// 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, ...) { QMM_GET_SYSCALL_ARGS(); @@ -69,7 +69,7 @@ static intptr_t SOF2MP_syscall(intptr_t cmd, ...) { s = ""; int i = 1; while (i < orig_syscall(G_ARGC)) { - orig_syscall(G_ARGV, buf, sizeof(buf)); + orig_syscall(G_ARGV, i, buf, sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; if (i != 1) s += " "; @@ -96,7 +96,7 @@ static intptr_t SOF2MP_syscall(intptr_t cmd, ...) { } -// wrapper vmMain function that calls actual mod func from orig_export +// 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, ...) { QMM_GET_VMMAIN_ARGS(); @@ -179,7 +179,7 @@ static void SOF2MP_dllEntry(eng_syscall syscall) { // get mod's vmMain function pointer from mod.cpp::mod_load -static bool SOF2MP_mod_load(void* entry) { +static bool SOF2MP_mod_load(void* entry, bool) { orig_vmMain = (mod_vmMain)entry; // we cannot verify data in the QVM since this engine both provides malloc functionality and has the gametype module, diff --git a/src/game_sof2sp.cpp b/src/game_sof2sp.cpp index 3c05041..8bee164 100644 --- a/src/game_sof2sp.cpp +++ b/src/game_sof2sp.cpp @@ -469,7 +469,7 @@ static void* SOF2SP_GetGameAPI(void* apiversion, void* import) { } -static bool SOF2SP_mod_load(void* entry) { +static bool SOF2SP_mod_load(void* entry, bool) { mod_GetGameAPI pfnGGA = (mod_GetGameAPI)entry; // api version gets passed before import pointer orig_export = (game_export_t*)pfnGGA((void*)orig_apiversion, &qmm_import); diff --git a/src/game_stef2.cpp b/src/game_stef2.cpp index 8f1ec7c..82c998b 100644 --- a/src/game_stef2.cpp +++ b/src/game_stef2.cpp @@ -985,7 +985,7 @@ static void* STEF2_GetGameAPI(void* import, void*) { } -static bool STEF2_mod_load(void* entry) { +static bool STEF2_mod_load(void* entry, bool) { mod_GetGameAPI pfnGGA = (mod_GetGameAPI)entry; orig_export = (game_export_t*)pfnGGA(&qmm_import, nullptr); diff --git a/src/game_stvoyhm.cpp b/src/game_stvoyhm.cpp index 9fb6849..fb24c5d 100644 --- a/src/game_stvoyhm.cpp +++ b/src/game_stvoyhm.cpp @@ -47,7 +47,7 @@ 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 from orig_import +// 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, ...) { QMM_GET_SYSCALL_ARGS(); @@ -68,7 +68,7 @@ static intptr_t STVOYHM_syscall(intptr_t cmd, ...) { s = ""; int i = 1; while (i < orig_syscall(G_ARGC)) { - orig_syscall(G_ARGV, buf, sizeof(buf)); + orig_syscall(G_ARGV, i, buf, sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; if (i != 1) s += " "; @@ -94,7 +94,7 @@ static intptr_t STVOYHM_syscall(intptr_t cmd, ...) { } -// wrapper vmMain function that calls actual mod func from orig_export +// 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, ...) { QMM_GET_VMMAIN_ARGS(); @@ -142,7 +142,7 @@ static void STVOYHM_dllEntry(eng_syscall syscall) { } -static bool STVOYHM_mod_load(void* entry) { +static bool STVOYHM_mod_load(void* entry, bool) { orig_vmMain = (mod_vmMain)entry; return !!orig_vmMain; diff --git a/src/game_stvoysp.cpp b/src/game_stvoysp.cpp index 545c7c2..e20d550 100644 --- a/src/game_stvoysp.cpp +++ b/src/game_stvoysp.cpp @@ -372,7 +372,7 @@ static void* STVOYSP_GetGameAPI(void* import, void*) { } -static bool STVOYSP_mod_load(void* entry) { +static bool STVOYSP_mod_load(void* entry, bool) { mod_GetGameAPI pfnGGA = (mod_GetGameAPI)entry; orig_export = (game_export_t*)pfnGGA(&qmm_import, nullptr); diff --git a/src/game_wet.cpp b/src/game_wet.cpp index 0582f5c..93dd6f1 100644 --- a/src/game_wet.cpp +++ b/src/game_wet.cpp @@ -46,7 +46,7 @@ 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 from orig_import +// wrapper syscall function that calls actual engine func in orig_syscall // this is how QMM and plugins will call into the engine intptr_t WET_syscall(intptr_t cmd, ...) { QMM_GET_SYSCALL_ARGS(); @@ -67,7 +67,7 @@ intptr_t WET_syscall(intptr_t cmd, ...) { s = ""; int i = 1; while (i < orig_syscall(G_ARGC)) { - orig_syscall(G_ARGV, buf, sizeof(buf)); + orig_syscall(G_ARGV, i, buf, sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; if (i != 1) s += " "; @@ -93,7 +93,7 @@ intptr_t WET_syscall(intptr_t cmd, ...) { } -// wrapper vmMain function that calls actual mod func from orig_export +// wrapper vmMain function that calls actual mod func in orig_vmMain // this is how QMM and plugins will call into the mod intptr_t WET_vmMain(intptr_t cmd, ...) { QMM_GET_VMMAIN_ARGS(); @@ -135,7 +135,7 @@ void WET_dllEntry(eng_syscall syscall) { } -bool WET_mod_load(void* entry) { +bool WET_mod_load(void* entry, bool) { orig_vmMain = (mod_vmMain)entry; return !!orig_vmMain; diff --git a/src/main.cpp b/src/main.cpp index 69b1c97..5fc1e11 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -124,17 +124,17 @@ cgameinfo cgame = { do is store the syscall, load the config file, and attempt to figure out what game engine we are in. This is either determined by the config file, or by getting the filename of the QMM DLL itself. */ -C_DLLEXPORT void dllEntry(eng_syscall syscall) { +C_DLLEXPORT void dllEntry(void* syscall) { // cgame passthrough hack: // QMM is already loaded, so this is a cgame passthrough situation. since the mod DLL isn't loaded yet, we can // just store the syscall pointer and pass it to the mod once it's loaded in vmMain(GAME_INIT) if (g_gameinfo.game) { - cgame.syscall = syscall; - LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("QMM passthrough_syscall = {}\n", (void*)cgame.syscall); + cgame.syscall = (eng_syscall)syscall; + LOG(QMM_LOG_DEBUG, "QMM") << fmt::format("QMM passthrough_syscall = {}\n", syscall); return; } - main_handle_entry((void*)syscall, nullptr, false); // false = !is_GetGameAPI + main_handle_entry(syscall, nullptr, false); // false = !is_GetGameAPI return; } @@ -190,7 +190,13 @@ C_DLLEXPORT void dllEntry(eng_syscall syscall) { to the proper function pointer in the struct. */ C_DLLEXPORT void* GetGameAPI(void* import, void* extra) { - return main_handle_entry(import, extra, true); // true = is_GetGameAPI + return main_handle_entry(import, extra, true); // true = is_GetGameAPI +} + + +// this is the same as the 2-arg GetGameAPI but OpenJK renamed it +C_DLLEXPORT void* GetModuleAPI(void* import, void* extra) { + return main_handle_entry(import, extra, true); // true = is_GetGameAPI } @@ -514,6 +520,11 @@ static void main_detect_env() { else { g_gameinfo.mod_dir = path_basename(g_gameinfo.qmm_dir); } + + // hack for OpenJK + if (str_striequal(g_gameinfo.mod_dir, "temp")) { + g_gameinfo.mod_dir = "base"; + } } diff --git a/src/mod.cpp b/src/mod.cpp index e5e98b4..bf8dda8 100644 --- a/src/mod.cpp +++ b/src/mod.cpp @@ -28,8 +28,6 @@ mod g_mod; static intptr_t s_mod_qvm_vmmain(intptr_t cmd, ...); static int s_mod_qvm_syscall(uint8_t* membase, int cmd, int* args); static bool s_mod_load_qvm(mod& mod); -static bool s_mod_load_vmmain(mod& mod); -static bool s_mod_load_getgameapi(mod& mod); bool mod_load(mod& mod, std::string file) { @@ -54,19 +52,68 @@ bool mod_load(mod& mod, std::string file) { } // if this DLL is the same as QMM, cancel - if ((void*)mod.dll == g_gameinfo.qmm_module_ptr) { + if (mod.dll == g_gameinfo.qmm_module_ptr) { LOG(QMM_LOG_ERROR, "QMM") << fmt::format("mod_load(\"{}\"): DLL is actually QMM?\n", file); - mod_unload(mod); + dlclose(mod.dll); return false; } - // pass off to engine-specific loading function - if (g_gameinfo.game->funcs->pfnGetGameAPI) - return s_mod_load_getgameapi(mod); - else - return s_mod_load_vmmain(mod); + mod.vmbase = 0; + + // if game supports GetGameAPI, look for GetGameAPI function + if (g_gameinfo.game->funcs->pfnGetGameAPI) { + mod_GetGameAPI pfnGGA = (mod_GetGameAPI)dlsym(mod.dll, "GetGameAPI"); + + // try for "GetModuleAPI", which is what OpenJK uses + if (!pfnGGA) { + pfnGGA = (mod_GetGameAPI)dlsym(mod.dll, "GetModuleAPI"); + } + + if (pfnGGA) { + // pass the GetGameAPI function pointer to the game-specific mod load handler + if (g_gameinfo.game->funcs->pfnModLoad && + g_gameinfo.game->funcs->pfnModLoad((void*)pfnGGA, true)) { // true = is_GetGameAPI + return true; + } + else { + LOG(QMM_LOG_ERROR, "QMM") << fmt::format("mod_load(\"{}\"): \"GetGameAPI\" function failed\n", mod.path); + } + } + else { + LOG(QMM_LOG_ERROR, "QMM") << fmt::format("mod_load(\"{}\"): Unable to find \"GetGameAPI\" function\n", mod.path); + } + } + + // if game supports dllEntry, look for dllEntry function + if (g_gameinfo.game->funcs->pfndllEntry) { + mod_dllEntry pfndllEntry = (mod_dllEntry)dlsym(mod.dll, "dllEntry"); + mod_vmMain pfnvmMain = (mod_vmMain)dlsym(mod.dll, "vmMain"); + + if (pfndllEntry && pfnvmMain) { + if (g_gameinfo.game->funcs->pfnModLoad) { + // pass the vmMain function pointer to the game-specific mod load handler + if (g_gameinfo.game->funcs->pfnModLoad((void*)pfnvmMain, false)) { // false = !is_GetGameAPI + // pass qmm_syscall to mod's dllEntry function + pfndllEntry(qmm_syscall); + return true; + } + else { + LOG(QMM_LOG_ERROR, "QMM") << fmt::format("mod_load(\"{}\"): Mod load failed?\n", mod.path); + } + } + // hack in case there isn't a game-specific dllEntry or ModLoad function + else if (!g_gameinfo.pfnvmMain) { + g_gameinfo.pfnvmMain = pfnvmMain; + } + } + else { + LOG(QMM_LOG_ERROR, "QMM") << fmt::format("mod_load(\"{}\"): Unable to find \"dllEntry\" and/or \"vmMain\" function\n", mod.path); + } + } } + mod_unload(mod); + LOG(QMM_LOG_ERROR, "QMM") << fmt::format("mod_load(\"{}\"): Unknown file format\n", file); return false; } @@ -171,7 +218,9 @@ static bool s_mod_load_qvm(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 && !g_gameinfo.game->funcs->pfnModLoad((void*)s_mod_qvm_vmmain)) { + if (g_gameinfo.game->funcs->pfnModLoad && + !g_gameinfo.game->funcs->pfnModLoad((void*)s_mod_qvm_vmmain, false)) // false = !is_GetGameAPI + { LOG(QMM_LOG_ERROR, "QMM") << fmt::format("mod_load(\"{}\"): Mod load failed?\n", mod.path); goto fail; } @@ -187,70 +236,3 @@ static bool s_mod_load_qvm(mod& mod) { mod_unload(mod); return false; } - - -// load a GetGameAPI DLL mod -static bool s_mod_load_getgameapi(mod& mod) { - // look for GetGameAPI function - mod_GetGameAPI pfnGGA = (mod_GetGameAPI)dlsym(mod.dll, "GetGameAPI"); - - if (!pfnGGA) { - LOG(QMM_LOG_ERROR, "QMM") << fmt::format("mod_load(\"{}\"): Unable to find \"GetGameAPI\" function\n", mod.path); - goto fail; - } - - mod.vmbase = 0; - - // pass the GetGameAPI function pointer to the game-specific mod load handler - if (!g_gameinfo.game->funcs->pfnModLoad || !g_gameinfo.game->funcs->pfnModLoad((void*)pfnGGA)) { - LOG(QMM_LOG_ERROR, "QMM") << fmt::format("mod_load(\"{}\"): \"GetGameAPI\" function failed\n", mod.path); - goto fail; - } - - return true; - -fail: - mod_unload(mod); - return false; -} - - -// load a vmMain DLL mod -static bool s_mod_load_vmmain(mod& mod) { - mod_dllEntry pfndllEntry = nullptr; - mod_vmMain pfnvmMain = nullptr; - - // look for dllEntry function - if (!(pfndllEntry = (mod_dllEntry)dlsym(mod.dll, "dllEntry"))) { - LOG(QMM_LOG_ERROR, "QMM") << fmt::format("mod_load(\"{}\"): Unable to find \"dllEntry\" function\n", mod.path); - goto fail; - } - - // look for vmMain function - if (!(pfnvmMain = (mod_vmMain)dlsym(mod.dll, "vmMain"))) { - LOG(QMM_LOG_ERROR, "QMM") << fmt::format("mod_load(\"{}\"): Unable to find \"vmMain\" function\n", mod.path); - goto fail; - } - - // pass qmm_syscall to mod's dllEntry function - pfndllEntry(qmm_syscall); - - mod.vmbase = 0; - - // pass the vmMain function pointer to the game-specific mod load handler - if (g_gameinfo.game->funcs->pfnModLoad && !g_gameinfo.game->funcs->pfnModLoad((void*)pfnvmMain)) { - LOG(QMM_LOG_ERROR, "QMM") << fmt::format("mod_load(\"{}\"): Mod load failed?\n", mod.path); - goto fail; - } - - // hack in case there isn't a game-specific dllEntry or ModLoad function - if (!g_gameinfo.pfnvmMain) { - g_gameinfo.pfnvmMain = pfnvmMain; - } - - return true; - -fail: - mod_unload(mod); - return false; -}