diff --git a/metamod/msvc/metamod.vcxproj b/metamod/msvc/metamod.vcxproj index f93cbe2..674f10a 100644 --- a/metamod/msvc/metamod.vcxproj +++ b/metamod/msvc/metamod.vcxproj @@ -77,7 +77,7 @@ $(OutDir)$(TargetName).lib MachineX86 false - %(AdditionalDependencies) + psapi.lib;%(AdditionalDependencies) _DEBUG;%(PreprocessorDefinitions) @@ -140,7 +140,7 @@ $(OutDir)$(TargetName).lib MachineX86 false - %(AdditionalDependencies) + psapi.lib;%(AdditionalDependencies) NDEBUG;%(PreprocessorDefinitions) diff --git a/metamod/src/dllapi.cpp b/metamod/src/dllapi.cpp index 24574b0..1605f53 100644 --- a/metamod/src/dllapi.cpp +++ b/metamod/src/dllapi.cpp @@ -216,7 +216,7 @@ void compile_dllfunc_callbacks() { jitdata_t jitdata; jitdata.plugins = g_plugins ? g_plugins->plist : nullptr; - jitdata.plugins_count = g_plugins ? g_plugins->endlist : 0; + jitdata.plugins_count = g_plugins ? g_plugins->max_loaded_count : 0; jitdata.table_offset = offsetof(MPlugin, dllapi_table); jitdata.post_table_offset = offsetof(MPlugin, dllapi_post_table); @@ -237,7 +237,7 @@ void compile_newdllfunc_callbacks() { jitdata_t jitdata; jitdata.plugins = g_plugins ? g_plugins->plist : nullptr; - jitdata.plugins_count = g_plugins ? g_plugins->endlist : 0; + jitdata.plugins_count = g_plugins ? g_plugins->max_loaded_count : 0; jitdata.table_offset = offsetof(MPlugin, newapi_table); jitdata.post_table_offset = offsetof(MPlugin, newapi_post_table); diff --git a/metamod/src/engine_api.cpp b/metamod/src/engine_api.cpp index e656923..e927a78 100644 --- a/metamod/src/engine_api.cpp +++ b/metamod/src/engine_api.cpp @@ -240,7 +240,7 @@ void compile_engfuncs_callbacks() { jitdata_t jitdata; jitdata.plugins = g_plugins ? g_plugins->plist : nullptr; - jitdata.plugins_count = g_plugins ? g_plugins->endlist : 0; + jitdata.plugins_count = g_plugins ? g_plugins->max_loaded_count : 0; jitdata.table_offset = offsetof(MPlugin, engine_table); jitdata.post_table_offset = offsetof(MPlugin, engine_post_table); diff --git a/metamod/src/linkent.cpp b/metamod/src/linkent.cpp index 4eb1b95..e46eb37 100644 --- a/metamod/src/linkent.cpp +++ b/metamod/src/linkent.cpp @@ -14,12 +14,12 @@ void do_link_ent(ENTITY_FN *pfnEntity, int *missing, const char *entStr, entvars if (!*pfnEntity) { META_DEBUG(9, ("Looking up game entity '%s'", entStr)); - *pfnEntity = (ENTITY_FN)DLSYM(GameDLL.handle, entStr); + *pfnEntity = (ENTITY_FN)GameDLL.sys_module.getsym(entStr); } if (!*pfnEntity) { - META_ERROR("Couldn't find game entity '%s' in game DLL '%s': %s", entStr, GameDLL.name, DLERROR()); + META_ERROR("Couldn't find game entity '%s' in game DLL '%s': %s", entStr, GameDLL.name, CSysModule::getloaderror()); *missing = 1; return; } diff --git a/metamod/src/metamod.cpp b/metamod/src/metamod.cpp index 42469ca..540891e 100644 --- a/metamod/src/metamod.cpp +++ b/metamod/src/metamod.cpp @@ -292,9 +292,9 @@ bool meta_load_gamedll(void) } // open the game DLL - if (!(GameDLL.handle = DLOPEN(GameDLL.pathname))) + if (!GameDLL.sys_module.load(GameDLL.pathname)) { - META_ERROR("dll: Couldn't load game DLL %s: %s", GameDLL.pathname, DLERROR()); + META_ERROR("dll: Couldn't load game DLL %s: %s", GameDLL.pathname, CSysModule::getloaderror()); RETURN_ERRNO(false, ME_DLOPEN); } @@ -305,20 +305,21 @@ bool meta_load_gamedll(void) // wanted to catch one of the functions, but now that plugins are // dynamically loadable at any time, we have to always pass our table, // so that any plugin loaded later can catch what they need to. - if ((pfn_give_engfuncs = (GIVE_ENGINE_FUNCTIONS_FN) DLSYM(GameDLL.handle, "GiveFnptrsToDll"))) + if ((pfn_give_engfuncs = (GIVE_ENGINE_FUNCTIONS_FN)GameDLL.sys_module.getsym("GiveFnptrsToDll"))) { pfn_give_engfuncs(&meta_engfuncs, gpGlobals); META_DEBUG(3, ("dll: Game '%s': Called GiveFnptrsToDll", GameDLL.name)); } else { - META_ERROR("dll: Couldn't find GiveFnptrsToDll() in game DLL '%s': %s", GameDLL.name, DLERROR()); + META_ERROR("dll: Couldn't find GiveFnptrsToDll() in game DLL '%s'", GameDLL.name); RETURN_ERRNO(false, ME_DLMISSING); } + // TODO // Yes...another macro. #define GET_FUNC_TABLE_FROM_GAME(gamedll, pfnGetFuncs, STR_GetFuncs, struct_field, API_TYPE, TABLE_TYPE, vers_pass, vers_int, vers_want, gotit) \ - if ((pfnGetFuncs = (API_TYPE) DLSYM(gamedll.handle, STR_GetFuncs))) { \ + if ((pfnGetFuncs = (API_TYPE)gamedll.sys_module.getsym(STR_GetFuncs))) { \ gamedll.funcs.struct_field = (TABLE_TYPE *)Q_calloc(1, sizeof(TABLE_TYPE)); \ if (!gamedll.funcs.struct_field) {\ META_ERROR("malloc failed for gamedll struct_field: %s", STR_GetFuncs); \ diff --git a/metamod/src/metamod.h b/metamod/src/metamod.h index 547d93b..07b0f7f 100644 --- a/metamod/src/metamod.h +++ b/metamod/src/metamod.h @@ -36,7 +36,7 @@ struct gamedll_t char pathname[PATH_MAX]; // ie "/home/willday/half-life/cstrike/dlls/cs_i386.so" char const *file; // ie "cs_i386.so" char real_pathname[PATH_MAX]; // in case pathname overridden by bot, etc - DLHANDLE handle; + CSysModule sys_module; gamedll_funcs_t funcs; // dllapi_table, newapi_table }; diff --git a/metamod/src/mlist.cpp b/metamod/src/mlist.cpp index bf2f651..848bf80 100644 --- a/metamod/src/mlist.cpp +++ b/metamod/src/mlist.cpp @@ -1,7 +1,7 @@ #include "precompiled.h" // Constructor -MPluginList::MPluginList(const char* ifile) : size(MAX_PLUGINS), endlist(0) +MPluginList::MPluginList(const char* ifile) : max_loaded_count(0) { // store filename of ini file Q_strncpy(inifile, ifile, sizeof inifile - 1); @@ -9,28 +9,28 @@ MPluginList::MPluginList(const char* ifile) : size(MAX_PLUGINS), endlist(0) // initialize array Q_memset(plist, 0, sizeof(plist)); - for (int i = 0; i < size; i++) + for (int i = 0; i < MAX_PLUGINS; i++) { plist[i].index = i + 1; // 1-based } - endlist = 0; + max_loaded_count = 0; } // Find a plugin based on the plugin handle. // meta_errno values: // - ME_ARGUMENT invalid pindex // - ME_NOTFOUND couldn't find a matching plugin -MPlugin *MPluginList::find(DLHANDLE handle) +MPlugin *MPluginList::find(module_handle_t handle) { if (!handle) RETURN_ERRNO(NULL, ME_ARGUMENT); - for (int i = 0; i < endlist; i++) + for (int i = 0; i < max_loaded_count; i++) { if (plist[i].status < PL_VALID) continue; - if (plist[i].handle == handle) + if (handle == plist[i].sys_module.gethandle()) return &plist[i]; } @@ -62,7 +62,7 @@ MPlugin *MPluginList::find(plid_t id) if (!id) RETURN_ERRNO(NULL, ME_ARGUMENT); - for (int i = 0; i < endlist; i++) + for (int i = 0; i < max_loaded_count; i++) { if (plist[i].status < PL_VALID) continue; @@ -85,7 +85,7 @@ MPlugin *MPluginList::find(const char* findpath) META_DEBUG(8, ("Looking for loaded plugin with dlfnamepath: %s", findpath)); - for (int i = 0; i < endlist; i++) + for (int i = 0; i < max_loaded_count; i++) { META_DEBUG(9, ("Looking at: plugin %s loadedpath: %s", plist[i].file, plist[i].pathname)); @@ -104,25 +104,16 @@ MPlugin *MPluginList::find(const char* findpath) } // Find a plugin that uses the given memory location. -// meta_errno values: -// - ME_ARGUMENT null memptr -// - ME_NOTFOUND couldn't find a matching plugin -// - errno's from DLFNAME() MPlugin *MPluginList::find_memloc(void *memptr) { - const char* dlfile; + for (int i = 0; i < max_loaded_count; i++) { + auto iplug = &plist[i]; - if (!memptr) - RETURN_ERRNO(NULL, ME_ARGUMENT); - - if (!(dlfile = DLFNAME(memptr))) - { - META_DEBUG(8, ("DLFNAME failed to find memloc %d", memptr)); - // meta_errno should be already set in DLFNAME - return NULL; + if (iplug->sys_module.contain(memptr)) + return iplug; } - return find(dlfile); + return nullptr; } // Find a plugin with non-ambiguous prefix string matching desc, file, @@ -145,7 +136,7 @@ MPlugin *MPluginList::find_match(const char *prefix) pfound = NULL; len = Q_strlen(prefix); Q_snprintf(buf, sizeof(buf), "mm_%s", prefix); - for (i = 0; i < endlist; i++) + for (i = 0; i < max_loaded_count; i++) { iplug = &plist[i]; if (iplug->status < PL_VALID) @@ -214,7 +205,7 @@ MPlugin *MPluginList::find_match(MPlugin* pmatch) } pfound = NULL; - for (i = 0; i < endlist; i++) + for (i = 0; i < max_loaded_count; i++) { iplug = &plist[i]; if (pmatch->platform_match(iplug)) @@ -292,33 +283,34 @@ MPlugin* MPluginList::plugin_addload(plid_t plid, const char* fname, PLUG_LOADTI return pl_added; } +MPlugin* MPluginList::find_empty_slot() +{ + for (int i = 0; i < MAX_PLUGINS; i++) { + if (plist[i].status == PL_EMPTY) { + if (i > max_loaded_count) + max_loaded_count = i + 1; + + return &plist[i]; + } + } + + return nullptr; +} + // Add a plugin to the list. // meta_errno values: // - ME_MAXREACHED reached max plugins MPlugin* MPluginList::add(MPlugin* padd) { - int i; - MPlugin* iplug; - - // Find either: - // - a slot in the list that's not being used - // - the end of the list - for (i = 0; i < endlist && plist[i].status != PL_EMPTY; i++) - ; + auto iplug = find_empty_slot(); // couldn't find a slot to use - if (i == size) + if (!iplug) { - META_ERROR("Couldn't add plugin '%s' to list; reached max plugins (%d)", padd->file, i); + META_ERROR("Couldn't add plugin '%s' to list; reached max plugins (%d)", padd->file, MAX_PLUGINS); RETURN_ERRNO(NULL, ME_MAXREACHED); } - // if we found the end of the list, advance end marker - if (i == endlist) - endlist++; - - iplug = &plist[i]; - // copy filename into this free slot Q_strncpy(iplug->filename, padd->filename, sizeof iplug->filename - 1); iplug->filename[sizeof iplug->filename - 1] = '\0'; @@ -337,13 +329,8 @@ MPlugin* MPluginList::add(MPlugin* padd) iplug->pathname[sizeof iplug->pathname - 1] = '\0'; normalize_pathname(iplug->pathname); - // copy source iplug->source = padd->source; - - // copy status iplug->status = padd->status; - - //copy other things iplug->source_plugin_index = padd->source_plugin_index; return iplug; @@ -375,7 +362,7 @@ bool MPluginList::ini_startup() } META_LOG("ini: Begin reading plugins list: %s", inifile); - for (n = 0 , ln = 1; !feof(fp) && fgets(line, sizeof(line), fp) && n < size; ln++) + for (n = 0 , ln = 1; !feof(fp) && fgets(line, sizeof(line), fp) && n < MAX_PLUGINS; ln++) { // Remove line terminations. char* cp; @@ -420,7 +407,7 @@ bool MPluginList::ini_startup() plist[n].action = PA_LOAD; META_LOG("ini: Read plugin config for: %s", plist[n].desc); n++; - endlist = n; // mark end of list + max_loaded_count = n; // mark end of list } META_LOG("ini: Finished reading plugins list: %s; Found %d plugins to load", inifile, n); @@ -452,7 +439,7 @@ bool MPluginList::ini_refresh() } META_LOG("ini: Begin re-reading plugins list: %s", inifile); - for (n = 0 , ln = 1; !feof(fp) && fgets(line, sizeof(line), fp) && n < size; ln++) + for (n = 0 , ln = 1; !feof(fp) && fgets(line, sizeof(line), fp) && n < MAX_PLUGINS; ln++) { // Remove line terminations. char *cp; @@ -655,7 +642,7 @@ bool MPluginList::load() } META_LOG("dll: Loading plugins..."); - for (int i = 0; i < endlist; i++) + for (int i = 0; i < max_loaded_count; i++) { if (plist[i].status < PL_VALID) continue; @@ -690,7 +677,7 @@ bool MPluginList::refresh(PLUG_LOADTIME now) } META_LOG("dll: Updating plugins..."); - for (i = 0; i < endlist; i++) + for (i = 0; i < max_loaded_count; i++) { iplug = &plist[i]; if (iplug->status < PL_VALID) @@ -767,7 +754,7 @@ bool MPluginList::refresh(PLUG_LOADTIME now) // - none void MPluginList::unpause_all(void) { - for (int i = 0; i < endlist; i++) + for (int i = 0; i < max_loaded_count; i++) { auto iplug = &plist[i]; if (iplug->status == PL_PAUSED) @@ -781,7 +768,7 @@ void MPluginList::unpause_all(void) // - none void MPluginList::retry_all(PLUG_LOADTIME now) { - for (int i = 0; i < endlist; i++) + for (int i = 0; i < max_loaded_count; i++) { auto iplug = &plist[i]; if (iplug->action != PA_NONE) @@ -806,7 +793,7 @@ void MPluginList::show(int source_index) META_CONS(" %*s %-*s %-4s %-4s %-*s v%-*s %-*s %-5s %-5s", WIDTH_MAX_PLUGINS, "", sizeof(desc) - 1, "description", "stat", "pend", sizeof(file) - 1, "file", sizeof(vers) - 1, "ers", 2 + WIDTH_MAX_PLUGINS, "src", "load ", "unlod"); - for (int i = 0; i < endlist; i++) + for (int i = 0; i < max_loaded_count; i++) { pl = &plist[i]; if (pl->status < PL_VALID) @@ -857,7 +844,7 @@ void MPluginList::show_client(edict_t *pEntity) MPlugin *pl; META_CLIENT(pEntity, "Currently running plugins:"); - for (int i = 0; i < endlist; i++) + for (int i = 0; i < max_loaded_count; i++) { pl = &plist[i]; if (pl->status != PL_RUNNING || !pl->info) @@ -880,7 +867,7 @@ bool MPluginList::found_child_plugins(int source_index) const if (source_index <= 0) return false; - for (int i = 0; i < endlist; i++) + for (int i = 0; i < max_loaded_count; i++) { if (plist[i].status < PL_VALID) continue; @@ -897,7 +884,7 @@ void MPluginList::clear_source_plugin_index(int source_index) if (source_index <= 0) return; - for (int i = 0; i < endlist; i++) + for (int i = 0; i < max_loaded_count; i++) { if (plist[i].status < PL_VALID) continue; diff --git a/metamod/src/mlist.h b/metamod/src/mlist.h index fb2a36d..9ffbbed 100644 --- a/metamod/src/mlist.h +++ b/metamod/src/mlist.h @@ -23,7 +23,8 @@ public: MPlugin *find_memloc(void *memptr); // find by memory location MPlugin *find_match(const char *prefix); // find by partial prefix match MPlugin *find_match(MPlugin *pmatch); // find by platform_match() - MPlugin *find(DLHANDLE handle); // find by handle + MPlugin *find(module_handle_t handle); // find by handle + MPlugin *find_empty_slot(); MPlugin *add(MPlugin *padd); bool found_child_plugins(int source_index) const; @@ -42,8 +43,7 @@ public: void clear_source_plugin_index(int source_index); public: - int loaded_count; // index of last used entry - int size; // size of list, ie MAX_PLUGINS + int max_loaded_count; // index of last used entry char inifile[PATH_MAX]; // full pathname MPlugin plist[MAX_PLUGINS]; // array of plugins }; diff --git a/metamod/src/mplugin.cpp b/metamod/src/mplugin.cpp index 0383121..ea7a6d9 100644 --- a/metamod/src/mplugin.cpp +++ b/metamod/src/mplugin.cpp @@ -382,7 +382,7 @@ char *MPlugin::resolve_dirs(char *path) // dir/file // meta_errno values: // - none -char *MPlugin::resolve_prefix(char *path) +char *MPlugin::resolve_prefix(char *path) const { struct stat st; char *cp, *fname; @@ -432,7 +432,7 @@ char *MPlugin::resolve_prefix(char *path) // path_i386.so, path_i486.so, etc (if linux) // meta_errno values: // - none -char *MPlugin::resolve_suffix(char *path) +char *MPlugin::resolve_suffix(char *path) const { struct stat st; static char buf[PATH_MAX ]; @@ -595,12 +595,10 @@ bool MPlugin::load(PLUG_LOADTIME now) META_ERROR("dll: Skipping plugin '%s'; couldn't query", desc); if (meta_errno != ME_DLOPEN) { - if (DLCLOSE(handle) != 0) + if (!sys_module.unload()) { - META_ERROR("dll: Couldn't close plugin file '%s': %s", file, DLERROR()); + META_ERROR("dll: Couldn't close plugin file '%s': %s", file, "invalid handle"); } - else - handle = NULL; } status = PL_BADFILE; info = NULL; //prevent crash @@ -682,10 +680,9 @@ bool MPlugin::query(void) META_QUERY_FN pfn_query; // open the plugin DLL - if (!(handle = DLOPEN(pathname))) + if (!sys_module.load(pathname)) { - META_ERROR("dll: Failed query plugin '%s'; Couldn't open file '%s': %s", - desc, pathname, DLERROR()); + META_ERROR("dll: Failed query plugin '%s'; Couldn't open file '%s': %s", desc, pathname, sys_module.getloaderror()); RETURN_ERRNO(false, ME_DLOPEN); } @@ -699,10 +696,10 @@ bool MPlugin::query(void) // GiveFnptrsToDll before Meta_Query, because the latter typically uses // engine functions like AlertMessage, which have to be passed along via // GiveFnptrsToDll. - pfn_query = (META_QUERY_FN) DLSYM(handle, "Meta_Query"); + pfn_query = (META_QUERY_FN)sys_module.getsym("Meta_Query"); if (!pfn_query) { - META_ERROR("dll: Failed query plugin '%s'; Couldn't find Meta_Query(): %s", desc, DLERROR()); + META_ERROR("dll: Failed query plugin '%s'; Couldn't find Meta_Query(): %s", desc, "function not found"); // caller will dlclose() RETURN_ERRNO(false, ME_DLMISSING); } @@ -721,7 +718,7 @@ bool MPlugin::query(void) // This passes nothing and returns nothing, and the routine in the // plugin can NOT use any g_engine functions, as they haven't been // provided yet (done next, in GiveFnptrsToDll). - pfn_init = (META_INIT_FN) DLSYM(handle, "Meta_Init"); + pfn_init = (META_INIT_FN)sys_module.getsym("Meta_Init"); if (pfn_init) { pfn_init(); @@ -734,10 +731,10 @@ bool MPlugin::query(void) } // pass on engine function table and globals to plugin - if (!(pfn_give_engfuncs = (GIVE_ENGINE_FUNCTIONS_FN) DLSYM(handle, "GiveFnptrsToDll"))) + if (!(pfn_give_engfuncs = (GIVE_ENGINE_FUNCTIONS_FN)sys_module.getsym("GiveFnptrsToDll"))) { // META_ERROR("dll: Couldn't find GiveFnptrsToDll() in plugin '%s': %s", desc, DLERROR()); - META_ERROR("dll: Failed query plugin '%s'; Couldn't find GiveFnptrsToDll(): %s", desc, DLERROR()); + META_ERROR("dll: Failed query plugin '%s'; Couldn't find GiveFnptrsToDll(): %s", desc, "function not found"); // caller will dlclose() RETURN_ERRNO(false, ME_DLMISSING); } @@ -880,9 +877,9 @@ bool MPlugin::attach(PLUG_LOADTIME now) } static_cast(gamedll_funcs.newapi_table)->set_from(GameDLL.funcs.newapi_table); } - if (!(pfn_attach = (META_ATTACH_FN) DLSYM(handle, "Meta_Attach"))) + if (!(pfn_attach = (META_ATTACH_FN)sys_module.getsym("Meta_Attach"))) { - META_ERROR("dll: Failed attach plugin '%s': Couldn't find Meta_Attach(): %s", desc, DLERROR()); + META_ERROR("dll: Failed attach plugin '%s': Couldn't find Meta_Attach(): %s", desc, "function not found"); // caller will dlclose() RETURN_ERRNO(false, ME_DLMISSING); } @@ -1082,13 +1079,12 @@ bool MPlugin::unload(PLUG_LOADTIME now, PL_UNLOAD_REASON reason, PL_UNLOAD_REASO // Close the file. Note: after this, attempts to reference any memory // locations in the file will produce a segfault. - if (DLCLOSE(handle) != 0) + if (!sys_module.unload()) { // If DLL cannot be closed, OS is badly broken or we are giving invalid handle. // So we don't return here but instead remove plugin from our listings. - META_WARNING("dll: Couldn't close plugin file '%s': %s", file, DLERROR()); + META_WARNING("dll: Couldn't close plugin file '%s': %s", file, "invalid handle"); } - handle = NULL; if (action == PA_UNLOAD) { @@ -1118,12 +1114,12 @@ bool MPlugin::detach(PLUG_LOADTIME now, PL_UNLOAD_REASON reason) // If we have no handle, i.e. no dll loaded, we return true because the // dll is obviously detached. We shouldn't call DLSYM() with a NULL // handle since this will DLSYM() ourself. - if (!handle) + if (!sys_module.gethandle()) return true; - if (!(pfn_detach = (META_DETACH_FN) DLSYM(handle, "Meta_Detach"))) + if (!(pfn_detach = (META_DETACH_FN)sys_module.getsym("Meta_Detach"))) { - META_ERROR("dll: Error detach plugin '%s': Couldn't find Meta_Detach(): %s", desc, DLERROR()); + META_ERROR("dll: Error detach plugin '%s': Couldn't find Meta_Detach(): %s", desc, "function not found"); // caller will dlclose() RETURN_ERRNO(false, ME_DLMISSING); } @@ -1279,13 +1275,12 @@ bool MPlugin::clear(void) } // If file is open, close the file. Note: after this, attempts to // reference any memory locations in the file will produce a segfault. - if (handle && DLCLOSE(handle) != 0) + if (!sys_module.unload()) { - META_ERROR("dll: Couldn't close plugin file '%s': %s", file, DLERROR()); + META_ERROR("dll: Couldn't close plugin file '%s': %s", file, "invalid handle"); status = PL_FAILED; RETURN_ERRNO(false, ME_DLERROR); } - handle = NULL; if (gamedll_funcs.dllapi_table) Q_free(gamedll_funcs.dllapi_table); if (gamedll_funcs.newapi_table) Q_free(gamedll_funcs.newapi_table); @@ -1298,7 +1293,6 @@ bool MPlugin::clear(void) status = PL_EMPTY; action = PA_NULL; - handle = NULL; info = NULL; time_loaded = 0; dllapi_table = NULL; diff --git a/metamod/src/mplugin.h b/metamod/src/mplugin.h index 34f7416..198c950 100644 --- a/metamod/src/mplugin.h +++ b/metamod/src/mplugin.h @@ -5,9 +5,9 @@ // Flags to indicate current "load" state of plugin. // NOTE: order is important, as greater/less comparisons are made. -enum PLUG_STATUS +enum PLUG_STATUS : uint8 { - PL_EMPTY = 0, // empty slot + PL_EMPTY = 0, // empty slot PL_VALID, // has valid info in it PL_BADFILE, // nonexistent file (open failed), or not a valid plugin file (query failed) PL_OPENED, // dlopened and queried @@ -17,7 +17,7 @@ enum PLUG_STATUS }; // Action to take for plugin at next opportunity. -enum PLUG_ACTION +enum PLUG_ACTION : uint8 { PA_NULL = 0, PA_NONE, // no action needed right now @@ -29,7 +29,7 @@ enum PLUG_ACTION }; // Flags to indicate from where the plugin was loaded. -enum PLOAD_SOURCE +enum PLOAD_SOURCE : uint8 { PS_INI = 0, // was loaded from the plugins.ini PS_CMD, // was loaded via a server command @@ -37,7 +37,7 @@ enum PLOAD_SOURCE }; // Flags for how to word description of plugin loadtime. -enum STR_LOADTIME +enum STR_LOADTIME : uint8 { SL_SIMPLE = 0, // single word SL_SHOW, // for "show" output, 5 chars @@ -46,21 +46,21 @@ enum STR_LOADTIME }; // Flags for how to format description of status. -enum STR_STATUS +enum STR_STATUS : uint8 { ST_SIMPLE = 0, // single word ST_SHOW, // for "show" output, 4 chars }; // Flags for how to format description of action. -enum STR_ACTION +enum STR_ACTION : uint8 { SA_SIMPLE = 0, // single word SA_SHOW, // for "show" output, 4 chars }; // Flags for how to format description of source. -enum STR_SOURCE +enum STR_SOURCE : uint8 { SO_SIMPLE = 0, // two words SO_SHOW, // for "list" output, 3 chars @@ -68,26 +68,13 @@ enum STR_SOURCE // An individual plugin. class MPlugin { -private: - bool query(); - bool attach(PLUG_LOADTIME now); - bool detach(PLUG_LOADTIME now, PL_UNLOAD_REASON reason); - gamedll_funcs_t gamedll_funcs; - mutil_funcs_t mutil_funcs; - public: - int index; // 1-based - char filename[PATH_MAX]; // ie "dlls/mm_test_i386.so", from inifile - char *file; // ie "mm_test_i386.so", ptr from filename - char desc[MAX_DESC_LEN]; // ie "Test metamod plugin", from inifile - char pathname[PATH_MAX]; // UNIQUE, ie "/home/willday/half-life/cstrike/dlls/mm_test_i386.so", built with GameDLL.gamedir - int pfspecific; // level of specific platform affinity, used during load time PLUG_STATUS status; // current status of plugin (loaded, etc) PLUG_ACTION action; // what to do with plugin (load, unload, etc) PLOAD_SOURCE source; // source of the request to load the plugin - - DLHANDLE handle; // handle for dlopen, dlsym, etc + int index; // 1-based plugin_info_t *info; // information plugin provides about itself + CSysModule sys_module; time_t time_loaded; // when plugin was loaded int source_plugin_index; // who loaded this plugin int unloader_index; @@ -100,6 +87,15 @@ public: enginefuncs_t *engine_table; enginefuncs_t *engine_post_table; + gamedll_funcs_t gamedll_funcs; + mutil_funcs_t mutil_funcs; + + char filename[PATH_MAX]; // ie "dlls/mm_test_i386.so", from inifile + char *file; // ie "mm_test_i386.so", ptr from filename + char desc[MAX_DESC_LEN]; // ie "Test metamod plugin", from inifile + char pathname[PATH_MAX]; // UNIQUE, ie "/home/willday/half-life/cstrike/dlls/mm_test_i386.so", built with GameDLL.gamedir + int pfspecific; // level of specific platform affinity, used during load time + bool ini_parseline(char *line); // parse line from inifile bool cmd_parseline(const char *line); // parse from console command bool plugin_parseline(const char *fname, int loader_index); // parse from plugin @@ -107,8 +103,8 @@ public: bool resolve(); // find a matching file on disk char *resolve_dirs(char *path); - char *resolve_prefix(char *path); - char *resolve_suffix(char *path); + char *resolve_prefix(char *path) const; + char *resolve_suffix(char *path) const; bool platform_match(MPlugin* plugin); @@ -151,6 +147,11 @@ public: if (info) return str_loadtime(info->unloadable, fmt); else return " -"; }; + +private: + bool query(); + bool attach(PLUG_LOADTIME now); + bool detach(PLUG_LOADTIME now, PL_UNLOAD_REASON reason); }; // Macros used by MPlugin::show(), to list the functions that the plugin diff --git a/metamod/src/mreg.cpp b/metamod/src/mreg.cpp index 22ae49d..3d053e9 100644 --- a/metamod/src/mreg.cpp +++ b/metamod/src/mreg.cpp @@ -172,14 +172,6 @@ void MRegCmdList::show(int plugin_id) const int n = 0; MRegCmd *icmd; - // If OS doesn't support DLFNAME, then we can't know what the plugin's - // registered cvars are. - DLFNAME(NULL); - if (meta_errno == ME_OSNOTSUP) - { - META_CONS("Registered commands: unknown (can't get info under this OS)"); - return; - } META_CONS("Registered commands:"); for (int i = 0; i < endlist; i++) { @@ -375,15 +367,6 @@ void MRegCvarList::show(int plugin_id) const MRegCvar *icvar; char bname[30 + 1], bval[15 + 1]; // +1 for term null - // If OS doesn't support DLFNAME, then we can't know what the plugin's - // registered cvars are. - DLFNAME(NULL); - if (meta_errno == ME_OSNOTSUP) - { - META_CONS("Registered cvars: unknown (can't get info under this OS)"); - return; - } - META_CONS("%-*s %*s %s", sizeof(bname) - 1, "Registered cvars:", sizeof(bval) - 1, "float value", "string value"); for (int i = 0; i < endlist; i++) { diff --git a/metamod/src/mreg.h b/metamod/src/mreg.h index 9395a51..eb8193f 100644 --- a/metamod/src/mreg.h +++ b/metamod/src/mreg.h @@ -128,4 +128,4 @@ private: MRegMsg mlist[MAX_REG_MSGS]; // array of registered msgs int size; // size of list, ie MAX_REG_MSGS int endlist; // index of last used entry -}; \ No newline at end of file +}; diff --git a/metamod/src/mutil.cpp b/metamod/src/mutil.cpp index 6b80875..3604191 100644 --- a/metamod/src/mutil.cpp +++ b/metamod/src/mutil.cpp @@ -119,7 +119,7 @@ qboolean EXT_FUNC mutil_CallGameEntity(plid_t plid, const char *entStr, entvars_ { plugin_info_t *plinfo = (plugin_info_t *)plid; META_DEBUG(8, ("Looking up game entity '%s' for plugin '%s'", entStr, plinfo->name)); - ENTITY_FN pfnEntity = (ENTITY_FN) DLSYM(GameDLL.handle, entStr); + ENTITY_FN pfnEntity = (ENTITY_FN)GameDLL.sys_module.getsym(entStr); if (!pfnEntity) { META_ERROR("Couldn't find game entity '%s' in game DLL '%s' for plugin '%s'", entStr, GameDLL.name, plinfo->name); @@ -268,7 +268,7 @@ int EXT_FUNC mutil_LoadMetaPlugin(plid_t plid, const char* fname, PLUG_LOADTIME else { if (plugin_handle) - *plugin_handle = (void *)pl_loaded->handle; + *plugin_handle = (void *)pl_loaded->sys_module.gethandle(); return 0; } } @@ -310,7 +310,7 @@ int EXT_FUNC mutil_UnloadMetaPluginByHandle(plid_t plid, void *plugin_handle, PL return ME_ARGUMENT; } - if (!(findp = g_plugins->find((DLHANDLE)plugin_handle))) + if (!(findp = g_plugins->find((module_handle_t)plugin_handle))) return ME_NOTFOUND; meta_errno = ME_NOERROR; diff --git a/metamod/src/osdep.cpp b/metamod/src/osdep.cpp index e7965f6..591da38 100644 --- a/metamod/src/osdep.cpp +++ b/metamod/src/osdep.cpp @@ -1,36 +1,128 @@ #include "precompiled.h" -bool dlclose_handle_invalid; +CSysModule::CSysModule() : m_handle(0), m_base(0), m_size(0) +{ +} #ifdef _WIN32 -// Since windows doesn't provide a verison of strtok_r(), we include one -// here. This may or may not operate exactly like strtok_r(), but does -// what we need it it do. -char *my_strtok_r(char *s, const char *delim, char **ptrptr) +module_handle_t CSysModule::load(const char* filepath) { - char *begin = nullptr; - char *end = nullptr; - char *rest = nullptr; - if (s) - begin = s; - else - begin = *ptrptr; - if (!begin) - return nullptr; + m_handle = LoadLibrary(filepath); - end = strpbrk(begin, delim); - if (end) - { - *end = '\0'; - rest = end + 1; - *ptrptr = rest + strspn(rest, delim); + MODULEINFO module_info; + if (GetModuleInformation(GetCurrentProcess(), m_handle, &module_info, sizeof(module_info))) { + m_base = (uintptr_t)module_info.lpBaseOfDll; + m_size = module_info.SizeOfImage; } - else - *ptrptr = nullptr; - return begin; + return m_handle; +} + +bool CSysModule::unload() +{ + bool ret = false; + + if (m_handle) { + ret = FreeLibrary(m_handle) != ERROR; + m_handle = 0; + m_base = 0; + m_size = 0; + } + + return ret; +} + +void* CSysModule::getsym(const char* name) const +{ + return GetProcAddress(m_handle, name); +} +#else +static ElfW(Addr) dlsize(void* base) +{ + ElfW(Ehdr) *ehdr; + ElfW(Phdr) *phdr; + ElfW(Addr) end; + + ehdr = (ElfW(Ehdr) *)base; + + /* Find the first program header */ + phdr = (ElfW(Phdr)*)((ElfW(Addr))ehdr + ehdr->e_phoff); + + /* Find the final PT_LOAD segment's extent */ + for (int i = 0; i < ehdr->e_phnum; ++i) + if (phdr[i].p_type == PT_LOAD) + end = phdr[i].p_vaddr + phdr[i].p_memsz; + + /* The start (virtual) address is always zero, so just return end.*/ + return end; +} + +module_handle_t CSysModule::load(const char* filepath) +{ + m_handle = dlopen(filepath, RTLD_NOW); + + char buf[1024], dummy[1024], path[260]; + sprintf(buf, "/proc/%i/maps", getpid()); + + FILE* fp = fopen(buf, "r"); + + while (fgets(buf, sizeof(buf), fp)) { + uintptr_t start, end; + + int args = sscanf(buf, "%x-%x %128s %128s %128s %128s %255s", &start, &end, dummy, dummy, dummy, dummy, path); + if (args != 7) { + continue; + } + + if (!Q_stricmp(path, filepath)) { + m_base = start; + m_size = end - start; + break; + } + } + + fclose(fp); + return m_handle; +} + +bool CSysModule::unload() +{ + bool ret = false; + + if (m_handle) { + ret = dlclose(m_handle) != 0; + m_handle = 0; + m_base = 0; + m_size = 0; + } + + return ret; +} + +void* CSysModule::getsym(const char* name) const +{ + return dlsym(m_handle, name); +} +#endif + +module_handle_t CSysModule::gethandle() const +{ + return m_handle; +} + +bool CSysModule::contain(void* addr) const +{ + return addr && uintptr_t(addr) > m_base && uintptr_t(addr) < m_base + m_size; +} + +const char* CSysModule::getloaderror() +{ +#ifdef _WIN32 + return str_GetLastError(); +#else + return dlerror; +#endif } -#endif // _WIN32 #ifdef _WIN32 // Windows doesn't provide a functon analagous to dlerr() that returns a @@ -44,70 +136,16 @@ const char *str_GetLastError() FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&buf, MAX_STRBUF_LEN - 1, NULL); return buf; } + +const char* str_os_error() +{ +#ifdef _WIN32 + return str_GetLastError(); +#else + return strerror(errno); #endif - -// Find the filename of the DLL/shared-lib where the given memory location -// exists. -#if defined(linux) || defined(__APPLE__) -// Errno values: -// - ME_NOTFOUND couldn't find a sharedlib that contains memory location -const char *DLFNAME(void *memptr) -{ - Dl_info dli; - Q_memset(&dli, 0, sizeof(dli)); - if (dladdr(memptr, &dli)) - return dli.dli_fname; - else - RETURN_ERRNO(NULL, ME_NOTFOUND); } -#elif defined(_WIN32) -// Implementation for win32 provided by Jussi Kivilinna : -// -// 1. Get memory location info on memptr with VirtualQuery. -// 2. Check if memory location info is valid and use MBI.AllocationBase -// as module start point. -// 3. Get module file name with GetModuleFileName. -// -// Simple and should work pretty much same way as 'dladdr' in linux. -// VirtualQuery and GetModuleFileName work even with win32s. -// -// Note that GetModuleFileName returns longfilenames rather than 8.3. -// -// Note also, the returned filename is local static storage, and should be -// copied by caller if it needs to keep it around. -// -// Also note, normalize_pathname() should really be done by the caller, but -// is done here to preserve "const char *" return consistent with linux -// version. -// -// Errno values: -// - ME_NOTFOUND couldn't find a DLL that contains memory location -const char *DLFNAME(void *memptr) -{ - MEMORY_BASIC_INFORMATION MBI; - static char fname[PATH_MAX]; - - if (!VirtualQuery(memptr, &MBI, sizeof(MBI))) - RETURN_ERRNO(NULL, ME_NOTFOUND); - - if (MBI.State != MEM_COMMIT) - RETURN_ERRNO(NULL, ME_NOTFOUND); - - if (!MBI.AllocationBase) - RETURN_ERRNO(NULL, ME_NOTFOUND); - - // MSDN indicates that GetModuleFileName will leave string - // null-terminated, even if it's truncated because buffer is too small. - if (!GetModuleFileName((HMODULE)MBI.AllocationBase, fname, sizeof(fname) - 1)) - RETURN_ERRNO(NULL, ME_NOTFOUND); - if (!fname[0]) - RETURN_ERRNO(NULL, ME_NOTFOUND); - - normalize_pathname(fname); - return fname; -} -#endif // _WIN32 - +#endif // Determine whether the given memory location is valid (ie whether we // should expect to be able to reference strings or functions at this diff --git a/metamod/src/osdep.h b/metamod/src/osdep.h index 6c51dfc..a162e82 100644 --- a/metamod/src/osdep.h +++ b/metamod/src/osdep.h @@ -1,10 +1,5 @@ #pragma once -// Various differences between WIN32 and Linux. -#include "types_meta.h" // bool -#include "mreg.h" // REG_CMD_FN, etc -#include "log_meta.h" // LOG_ERROR, etc - // String describing platform/DLL-type, for matching lines in plugins.ini. #ifdef _WIN32 #define UNUSED /**/ @@ -20,75 +15,31 @@ #define PLATFORM_DLEXT ".so" #endif -extern bool dlclose_handle_invalid; +#include "mreg.h" -// Functions & types for DLL open/close/etc operations. #ifdef _WIN32 - typedef HINSTANCE DLHANDLE; - typedef FARPROC DLFUNC; - inline DLHANDLE DLOPEN(const char *filename) - { - return LoadLibraryA(filename); - } - inline DLFUNC DLSYM(DLHANDLE handle, const char *string) - { - return GetProcAddress(handle, string); - } - inline int DLCLOSE(DLHANDLE handle) - { - if (!handle) - { - dlclose_handle_invalid = true; - return 1; - } - - dlclose_handle_invalid = false; - // NOTE: Windows FreeLibrary returns success=nonzero, fail=zero, - // which is the opposite of the unix convention, thus the '!'. - return !FreeLibrary(handle); - } - // Windows doesn't provide a function corresponding to dlerror(), so - // we make our own. - const char *str_GetLastError(); - inline const char *DLERROR() - { - if (dlclose_handle_invalid) - return "Invalid handle."; - - return str_GetLastError(); - } +typedef HINSTANCE module_handle_t; #else - typedef void *DLHANDLE; - typedef void *DLFUNC; - inline DLHANDLE DLOPEN(const char *filename) - { - return dlopen(filename, RTLD_NOW); - } - inline DLFUNC DLSYM(DLHANDLE handle, const char *string) - { - return dlsym(handle, string); - } - // dlclose crashes if handle is null. - inline int DLCLOSE(DLHANDLE handle) - { - if (!handle) - { - dlclose_handle_invalid = true; - return 1; - } - dlclose_handle_invalid = false; - return dlclose(handle); - } - inline const char *DLERROR() - { - if (dlclose_handle_invalid) - return "Invalid handle."; - - return dlerror(); - } +typedef void* module_handle_t; #endif -const char *DLFNAME(void *memptr); +class CSysModule +{ +public: + CSysModule(); + module_handle_t load(const char* filename); + bool unload(); + void* getsym(const char* name) const; + module_handle_t gethandle() const; + bool contain(void* addr) const; + static const char* getloaderror(); + +private: + module_handle_t m_handle; + uintptr_t m_base; + uintptr_t m_size; +}; + bool IS_VALID_PTR(void *memptr); // Attempt to call the given function pointer, without segfaulting. @@ -96,15 +47,9 @@ bool os_safe_call(REG_CMD_FN pfn); // Windows doesn't have an strtok_r() routine, so we write our own. #ifdef _WIN32 - #define strtok_r(s, delim, ptrptr) my_strtok_r(s, delim, ptrptr) - char *my_strtok_r(char *s, const char *delim, char **ptrptr); -#else -// Linux doesn't have an strlwr() routine, so we write our own. - #define strlwr(s) my_strlwr(s) - char *my_strlwr(char *s); + #define strtok_r(s, delim, ptrptr) mm_strtok_r(s, delim, ptrptr) #endif // _WIN32 - // Set filename and pathname maximum lengths. Note some windows compilers // provide a which is incomplete and/or causes problems; see // doc/windows_notes.txt for more information. @@ -165,89 +110,8 @@ bool os_safe_call(REG_CMD_FN pfn); #ifndef S_IWGRP #define S_IWGRP S_IWUSR #endif + + const char *str_GetLastError(); #endif // _WIN32 -// Normalize/standardize a pathname. -// - For win32, this involves: -// - Turning backslashes (\) into slashes (/), so that config files and -// Metamod internal code can be simpler and just use slashes (/). -// - Turning upper/mixed case into lowercase, since windows is -// non-case-sensitive. -// - For linux, this requires no work, as paths uses slashes (/) natively, -// and pathnames are case-sensitive. -#if defined(__linux) || defined(__APPLE__) -#define normalize_pathname(a) -#elif defined(_WIN32) -inline void normalize_pathname(char *path) -{ - char *cp; - - META_DEBUG(8, ("normalize: %s", path)); - for (cp = path; *cp; cp++) - { - if (isupper(*cp)) *cp = tolower(*cp); - if (*cp == '\\') *cp = '/'; - } - - META_DEBUG(8, ("normalized: %s", path)); -} -#endif // _WIN32 - -// Indicate if pathname appears to be an absolute-path. Under linux this -// is a leading slash (/). Under win32, this can be: -// - a drive-letter path (ie "D:blah" or "C:\blah") -// - a toplevel path (ie "\blah") -// - a UNC network address (ie "\\srv1\blah"). -// Also, handle both native and normalized pathnames. -inline int is_absolute_path(const char *path) -{ - if (path[0] == '/') return TRUE; -#ifdef _WIN32 - if (path[1] == ':') return TRUE; - if (path[0] == '\\') return TRUE; -#endif // _WIN32 - return FALSE; -} - -#ifdef _WIN32 -// Buffer pointed to by resolved_name is assumed to be able to store a -// string of PATH_MAX length. -inline char *realpath(const char *file_name, char *resolved_name) -{ - int ret; - ret = GetFullPathName(file_name, PATH_MAX, resolved_name, NULL); - if (ret > PATH_MAX) - { - errno = ENAMETOOLONG; - return NULL; - } - else if (ret > 0) - { - HANDLE handle; - WIN32_FIND_DATA find_data; - handle = FindFirstFile(resolved_name, &find_data); - if (INVALID_HANDLE_VALUE == handle) - { - errno = ENOENT; - return NULL; - } - - FindClose(handle); - normalize_pathname(resolved_name); - return resolved_name; - } - - return NULL; -} -#endif // _WIN32 - -// Generic "error string" from a recent OS call. For linux, this is based -// on errno. For win32, it's based on GetLastError. -inline const char *str_os_error() -{ -#ifdef _WIN32 - return str_GetLastError(); -#else - return strerror(errno); -#endif -} +const char* str_os_error(); diff --git a/metamod/src/precompiled.h b/metamod/src/precompiled.h index acdd63b..797db0f 100644 --- a/metamod/src/precompiled.h +++ b/metamod/src/precompiled.h @@ -2,6 +2,8 @@ #include "version/appversion.h" +#define PSAPI_VERSION 1 + #if defined(linux) || defined(__APPLE__) // enable extra routines in system header files, like dladdr #ifndef _GNU_SOURCE diff --git a/metamod/src/reg_support.cpp b/metamod/src/reg_support.cpp index 349b8b8..6ffb60e 100644 --- a/metamod/src/reg_support.cpp +++ b/metamod/src/reg_support.cpp @@ -80,13 +80,12 @@ void EXT_FUNC meta_command_handler() // to a generic command-handler function (see above). void EXT_FUNC meta_AddServerCommand(char *cmd_name, void (*function)()) { - MPlugin *iplug = NULL; - MRegCmd *icmd = NULL; + MPlugin *iplug = g_plugins->find_memloc(function); META_DEBUG(4, ("called: meta_AddServerCommand; cmd_name=%s, function=%d", cmd_name, function)); // try to find which plugin is registering this command - if (!(iplug = g_plugins->find_memloc((void *)function))) + if (!iplug) { // if this isn't supported on this OS, don't log an error if (meta_errno != ME_OSNOTSUP) @@ -94,7 +93,7 @@ void EXT_FUNC meta_AddServerCommand(char *cmd_name, void (*function)()) } // See if this command was previously registered, ie a "reloaded" plugin. - icmd = g_regCmds->find(cmd_name); + auto icmd = g_regCmds->find(cmd_name); if (!icmd) { // If not found, add. @@ -134,27 +133,18 @@ void EXT_FUNC meta_AddServerCommand(char *cmd_name, void (*function)()) // it will fail to work properly. void EXT_FUNC meta_CVarRegister(cvar_t *pCvar) { - MPlugin *iplug = nullptr; - MRegCvar *icvar = nullptr; + MPlugin *iplug = g_plugins->find_memloc(pCvar); META_DEBUG(4, ("called: meta_CVarRegister; name=%s", pCvar->name)); - if (!strncmp(pCvar->name, "iz_", 3)) - __asm int 3; - // try to find which plugin is registering this cvar - if (!(iplug = g_plugins->find_memloc((void *)pCvar))) + if (!iplug) { - // if this isn't supported on this OS, don't log an error - if (meta_errno != ME_OSNOTSUP) - // Note: if cvar_t was malloc'd by the plugin, we can't - // determine the calling plugin. Thus, this becomes a Debug - // rather than Error message. META_DEBUG(1, ("Failed to find memloc for regcvar '%s'", pCvar->name)); } // See if this cvar was previously registered, ie a "reloaded" plugin. - icvar = g_regCvars->find(pCvar->name); + auto icvar = g_regCvars->find(pCvar->name); if (!icvar) { // If not found, add. diff --git a/metamod/src/studioapi.cpp b/metamod/src/studioapi.cpp index b33f4f3..d088255 100644 --- a/metamod/src/studioapi.cpp +++ b/metamod/src/studioapi.cpp @@ -44,12 +44,11 @@ C_DLLEXPORT int Server_GetBlendingInterface(int version, if (!getblend) { META_DEBUG(6, ("Looking up Server_GetBlendingInterface")); - getblend = (GETBLENDAPI_FN) DLSYM(GameDLL.handle, - "Server_GetBlendingInterface"); + getblend = (GETBLENDAPI_FN)GameDLL.sys_module.getsym("Server_GetBlendingInterface"); } if (!getblend) { - META_DEBUG(6, ("Couldn't find Server_GetBlendingInterface in game DLL '%s': %s", GameDLL.name, DLERROR())); + META_DEBUG(6, ("Couldn't find Server_GetBlendingInterface in game DLL '%s': %s", GameDLL.name, "function not found")); missing = 1; return 0; } diff --git a/metamod/src/utils.cpp b/metamod/src/utils.cpp index 9211eec..2ce8c7e 100644 --- a/metamod/src/utils.cpp +++ b/metamod/src/utils.cpp @@ -58,3 +58,83 @@ void execmem_allocator::allocate_page() m_used = 0; m_pages.push_back(page); } + +#ifdef _WIN32 +// Since windows doesn't provide a verison of strtok_r(), we include one +// here. This may or may not operate exactly like strtok_r(), but does +// what we need it it do. +char *mm_strtok_r(char *s, const char *delim, char **ptrptr) +{ + char *begin = nullptr; + char *end = nullptr; + char *rest = nullptr; + if (s) + begin = s; + else + begin = *ptrptr; + if (!begin) + return nullptr; + + end = strpbrk(begin, delim); + if (end) { + *end = '\0'; + rest = end + 1; + *ptrptr = rest + strspn(rest, delim); + } + else + *ptrptr = nullptr; + + return begin; +} +#endif // _WIN32 + +void normalize_pathname(char *path) +{ +#ifdef _WIN32 + char *cp; + + META_DEBUG(8, ("normalize: %s", path)); + for (cp = path; *cp; cp++) { + if (isupper(*cp)) *cp = tolower(*cp); + if (*cp == '\\') *cp = '/'; + } + + META_DEBUG(8, ("normalized: %s", path)); +#endif +} + +bool is_absolute_path(const char *path) +{ + if (path[0] == '/') return true; +#ifdef _WIN32 + if (path[1] == ':') return true; + if (path[0] == '\\') return true; +#endif // _WIN32 + return false; +} + +char *realpath(const char *file_name, char *resolved_name) +{ + int ret = GetFullPathName(file_name, PATH_MAX, resolved_name, NULL); + + if (ret > PATH_MAX) { + errno = ENAMETOOLONG; + return NULL; + } + + if (ret > 0) { + HANDLE handle; + WIN32_FIND_DATA find_data; + handle = FindFirstFile(resolved_name, &find_data); + if (INVALID_HANDLE_VALUE == handle) { + errno = ENOENT; + return NULL; + } + + FindClose(handle); + normalize_pathname(resolved_name); + return resolved_name; + } + + return NULL; +} diff --git a/metamod/src/utils.h b/metamod/src/utils.h index 72d6f1b..dfb1351 100644 --- a/metamod/src/utils.h +++ b/metamod/src/utils.h @@ -23,3 +23,11 @@ bool is_yes(const char* str); bool is_no(const char* str); const char* LOCALINFO(char* key); + +#ifdef _WIN32 +char *mm_strtok_r(char *s, const char *delim, char **ptrptr); +#endif + +void normalize_pathname(char *path); +bool is_absolute_path(const char *path); +char *realpath(const char *file_name, char *resolved_name);