diff options
Diffstat (limited to 'src/sst.c')
-rw-r--r-- | src/sst.c | 115 |
1 files changed, 70 insertions, 45 deletions
@@ -20,7 +20,6 @@ #include <shlwapi.h> #endif -#include "ac.h" #include "con_.h" #include "engineapi.h" #include "errmsg.h" @@ -70,22 +69,63 @@ static inline void *ownhandle(void) { #define VDFBASENAME "SourceSpeedrunTools" +#ifdef _WIN32 +// not a proper check, just a short-circuit check to avoid doing more work. +static inline bool checksamedrive(const ushort *restrict path1, + const ushort *restrict path2) { + bool ret = (path1[0] | 32) == (path2[0] | 32); + if (!ret) errmsg_errorx("game and plugin must be on the same drive\n"); + return ret; +} +#endif + DEF_CCMD_HERE(sst_autoload_enable, "Register SST to load on game startup", 0) { - // note: gamedir doesn't account for if the dll is in a base mod's - // directory, although it will yield a valid/working relative path anyway. - const os_char *searchdir = ifacever == 3 ? - gameinfo_gamedir : gameinfo_bindir; os_char path[PATH_MAX]; if (os_dlfile(ownhandle(), path, sizeof(path) / sizeof(*path)) == -1) { // hopefully by this point this won't happen, but, like, never know errmsg_errordl("failed to get path to plugin"); return; } + os_char _startdir[PATH_MAX]; + const os_char *startdir; + if (ifacever == 2) { + startdir = _startdir; + os_getcwd(_startdir, PATH_MAX); // if this fails, OS devs are all fired. +#ifdef _WIN32 + // note: strictly speaking we *could* allow this with an absolute path + // since old builds allow absolute plugin_load paths but since it's less + // reliable if e.g. a disk is removed, and also doesn't work for all + // games, just rule it out entirely to keep things simple. + if (!checksamedrive(path, startdir)) return; +#endif + int len = os_strlen(startdir); + if (len + sizeof("/bin") >= PATH_MAX) { + errmsg_errorx("path to game is too long"); + return; + } + memcpy(_startdir + len, OS_LIT("/bin"), 5 * sizeof(os_char)); + } + else /* ifacever == 3 */ { + // newer games load from the mod dir instead of engine bin, and search + // in inherited search paths too, although we don't bother with those as + // the actual VDF is only read from the mod itself so it's always enough + // to make the path relative to that (and that makes the actual plugin + // search fast too as it should find it in the first place it looks). + // we *still* refuse to autoload across different drives even if some + // obscure gameinfo.txt arrangement could technically allow that to work + startdir = gameinfo_gamedir; +#ifdef _WIN32 + if (!checksamedrive(path, startdir)) return; +#endif + } os_char relpath[PATH_MAX]; #ifdef _WIN32 - if (!PathRelativePathToW(relpath, searchdir, FILE_ATTRIBUTE_DIRECTORY, + // note: dll isn't actually in gamedir if it's in a base mod directory + // note: gamedir doesn't account for if the dll is in a base mod's + // directory, although it will yield a valid/working relative path anyway. + if (!PathRelativePathToW(relpath, startdir, FILE_ATTRIBUTE_DIRECTORY, path, 0)) { - errmsg_errorsys("couldn't compute a relative path for some reason"); + errmsg_errorsys("couldn't compute a relative path"); return; } // arbitrary aesthetic judgement @@ -113,9 +153,8 @@ DEF_CCMD_HERE(sst_autoload_enable, "Register SST to load on game startup", 0) { errmsg_errorstd("couldn't open %" fS, path); return; } - // XXX: oh, crap, we're clobbering unicode again. welp, let's hope the - // theory that the engine is just as bad if not worse is true so that it - // doesn't matter. + // XXX: oh crap, we're clobbering unicode again. welp, let's continue + // relying on the theory that the engine would fail to deal with it anyway. if (fprintf(f, "Plugin { file \"%" fS "\" }\n", relpath) < 0 || fflush(f) == -1) { errmsg_errorstd("couldn't write to %" fS, path); @@ -183,6 +222,27 @@ static const char *updatenotes = "\ #include <featureinit.gen.h> // generated by build/codegen.c static void do_featureinit(void) { + // load libs that might not be there early (...at least on Linux???) + clientlib = os_dlhandle(OS_LIT("client") OS_LIT(OS_DLSUFFIX)); + if (!clientlib) { + errmsg_warndl("couldn't get the game's client library"); + } + else if (!(factory_client = (ifacefactory)os_dlsym(clientlib, + "CreateInterface"))) { + errmsg_warndl("couldn't get client's CreateInterface"); + } + void *inputsystemlib = os_dlhandle(OS_LIT("bin/") OS_LIT(OS_DLPREFIX) + OS_LIT("inputsystem") OS_LIT(OS_DLSUFFIX)); + if (!inputsystemlib) { + errmsg_warndl("couldn't get the input system library"); + } + else if (!(factory_inputsystem = (ifacefactory)os_dlsym(inputsystemlib, + "CreateInterface"))) { + errmsg_warndl("couldn't get input system's CreateInterface"); + } + inputsystem = factory_inputsystem("InputSystemVersion001", 0); + if (!inputsystem) errmsg_warnx("missing input system interface"); + // ... and now for the real magic! initfeatures(); // if we're autoloaded and the external autoupdate script downloaded a new @@ -254,42 +314,8 @@ static bool do_load(ifacefactory enginef, ifacefactory serverf) { errmsg_warnsys("couldn't set up memory for function hooking"); return false; } - factory_engine = enginef; factory_server = serverf; -#ifdef _WIN32 - void *inputsystemlib = GetModuleHandleW(L"inputsystem.dll"); -#else - // TODO(linux): assuming the above doesn't apply to this; check if it does! - // ... actually, there's a good chance this assumption is now wrong! - void *inputsystemlib = dlopen("bin/libinputsystem.so", - RTLD_NOW | RLTD_NOLOAD); - if (inputsystemlib) dlclose(inputsystemlib); // blegh -#endif - if (!inputsystemlib) { - errmsg_warndl("couldn't get the input system library"); - } - else if (!(factory_inputsystem = (ifacefactory)os_dlsym(inputsystemlib, - "CreateInterface"))) { - errmsg_warndl("couldn't get input system's CreateInterface"); - } if (!engineapi_init(ifacever)) return false; -#ifdef _WIN32 - clientlib = GetModuleHandleW(gameinfo_clientlib); -#else - // Apparently on Linux, the client library isn't actually loaded yet here, - // so RTLD_NOLOAD won't actually find it. We have to just dlopen it - // normally - and then remember to decrement the refcount again later in - // do_unload() so nothing gets leaked! - clientlib = dlopen(gameinfo_clientlib, RTLD_NOW); -#endif - if (!clientlib) { - errmsg_warndl("couldn't get the game's client library"); - } - else if (!(factory_client = (ifacefactory)os_dlsym(clientlib, - "CreateInterface"))) { - errmsg_warndl("couldn't get client's CreateInterface"); - } - const void **p = vtable_firstdiff; if (GAMETYPE_MATCHES(Portal2)) *p++ = (void *)&nop_p_v; // ClientFullyConnect *p++ = (void *)&nop_p_v; // ClientDisconnect @@ -303,7 +329,6 @@ static bool do_load(ifacefactory enginef, ifacefactory serverf) { *p++ = (void *)&nop_ipipp_v; // OnQueryCvarValueFinished (002+) *p++ = (void *)&nop_p_v; // OnEdictAllocated *p = (void *)&nop_p_v; // OnEdictFreed - if (!deferinit()) { do_featureinit(); fixes_apply(); } return true; } |