summaryrefslogtreecommitdiffhomepage
path: root/src/sst.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sst.c')
-rw-r--r--src/sst.c115
1 files changed, 70 insertions, 45 deletions
diff --git a/src/sst.c b/src/sst.c
index 9dc8829..e00451a 100644
--- a/src/sst.c
+++ b/src/sst.c
@@ -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;
}