summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/3p/chibicc/tokenize.c4
-rw-r--r--src/ac.c11
-rw-r--r--src/build/cmeta.c22
-rw-r--r--src/build/cmeta.h4
-rw-r--r--src/build/mkentprops.c8
-rw-r--r--src/build/mkgamedata.c8
-rw-r--r--src/build/skiplist.h3
-rw-r--r--src/errmsg.h20
-rw-r--r--src/gameinfo.c5
-rw-r--r--src/hook.c7
-rw-r--r--src/nomute.c11
-rw-r--r--src/os-unix.h125
-rw-r--r--src/os-win32.h131
-rw-r--r--src/os.c204
-rw-r--r--src/os.h260
-rw-r--r--src/sst.c82
-rw-r--r--src/stubs/BCryptPrimitives.c6
-rw-r--r--src/stubs/bcryptprimitives.def7
18 files changed, 557 insertions, 361 deletions
diff --git a/src/3p/chibicc/tokenize.c b/src/3p/chibicc/tokenize.c
index b13edd5..6738206 100644
--- a/src/3p/chibicc/tokenize.c
+++ b/src/3p/chibicc/tokenize.c
@@ -1,5 +1,9 @@
#include "chibicc.h"
+#ifdef _WIN32
+#define strncasecmp _strnicmp
+#endif
+
// Input file
static File *current_file;
diff --git a/src/ac.c b/src/ac.c
index 8b163b9..8c98cfb 100644
--- a/src/ac.c
+++ b/src/ac.c
@@ -17,6 +17,13 @@
#include <stdlib.h>
+#ifdef _WIN32
+#include <Windows.h>
+#include <werapi.h>
+#else
+#include <sys/mman.h>
+#endif
+
#include "alias.h"
#include "bind.h"
#include "chunklets/fastspin.h"
@@ -41,10 +48,6 @@
#include "x86.h"
#include "x86util.h"
-#ifdef _WIN32
-#include <werapi.h> // must be after Windows.h (via os.h)
-#endif
-
FEATURE()
REQUIRE(bind)
REQUIRE(democustom)
diff --git a/src/build/cmeta.c b/src/build/cmeta.c
index 0c9b3ca..8a2416d 100644
--- a/src/build/cmeta.c
+++ b/src/build/cmeta.c
@@ -1,5 +1,5 @@
/*
- * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com>
+ * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -102,15 +102,15 @@ static void die(const char *s) {
exit(100);
}
-static char *readsource(const os_char *f) {
- int fd = os_open(f, O_RDONLY);
- if (fd == -1) return 0;
+static char *readsource(const os_char *path) {
+ int f = os_open_read(path);
+ if (f == -1) return 0;
uint bufsz = 8192;
char *buf = malloc(bufsz);
if (!buf) die("couldn't allocate memory");
int nread;
int off = 0;
- while ((nread = read(fd, buf + off, bufsz - off)) > 0) {
+ while ((nread = os_read(f, buf + off, bufsz - off)) > 0) {
off += nread;
if (off == bufsz) {
bufsz *= 2;
@@ -122,24 +122,24 @@ static char *readsource(const os_char *f) {
}
if (nread == -1) die("couldn't read file");
buf[off] = 0;
- close(fd);
+ os_close(f);
return buf;
}
// as per cmeta.h this is totally opaque; it's actually just a Token in disguise
struct cmeta;
-const struct cmeta *cmeta_loadfile(const os_char *f) {
- char *buf = readsource(f);
+const struct cmeta *cmeta_loadfile(const os_char *path) {
+ char *buf = readsource(path);
if (!buf) return 0;
#ifdef _WIN32
- char *realname = malloc(wcslen(f) + 1);
+ char *realname = malloc(wcslen(path) + 1);
if (!realname) die("couldn't allocate memory");
// XXX: being lazy about Unicode right now; a general purpose tool should
// implement WTF8 or something. SST itself doesn't have any unicode paths
// though, so don't really care as much.
- *realname = *f;
- for (const ushort *p = f + 1; p[-1]; ++p) realname[p - f] = *p;
+ *realname = *path;
+ for (const ushort *p = path + 1; p[-1]; ++p) realname[p - path] = *p;
#else
const char *realname = f;
#endif
diff --git a/src/build/cmeta.h b/src/build/cmeta.h
index 48a2d6d..86ae0ec 100644
--- a/src/build/cmeta.h
+++ b/src/build/cmeta.h
@@ -1,5 +1,5 @@
/*
- * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com>
+ * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -21,7 +21,7 @@
struct cmeta;
-const struct cmeta *cmeta_loadfile(const os_char *f);
+const struct cmeta *cmeta_loadfile(const os_char *path);
/*
* Iterates through all the #include directives in a file, passing each one in
diff --git a/src/build/mkentprops.c b/src/build/mkentprops.c
index 0781f25..2043a9b 100644
--- a/src/build/mkentprops.c
+++ b/src/build/mkentprops.c
@@ -174,13 +174,13 @@ _( "}")
int OS_MAIN(int argc, os_char *argv[]) {
for (++argv; *argv; ++argv) {
- int fd = os_open(*argv, O_RDONLY);
- if (fd == -1) die("couldn't open file");
+ int f = os_open_read(*argv);
+ if (f == -1) die("couldn't open file");
struct kv_parser kv = {0};
struct parsestate state = {*argv, &kv};
char buf[1024];
int nread;
- while (nread = read(fd, buf, sizeof(buf))) {
+ while (nread = os_read(f, buf, sizeof(buf))) {
if (nread == -1) die("couldn't read file");
if (!kv_parser_feed(&kv, buf, nread, &kv_cb, &state)) goto ep;
}
@@ -189,7 +189,7 @@ ep: fprintf(stderr, "mkentprops: %" fS ":%d:%d: bad syntax: %s\n",
*argv, kv.line, kv.col, kv.errmsg);
exit(1);
}
- close(fd);
+ os_close(f);
}
FILE *out = fopen(".build/include/entprops.gen.h", "wb");
diff --git a/src/build/mkgamedata.c b/src/build/mkgamedata.c
index d49eef5..bf359b2 100644
--- a/src/build/mkgamedata.c
+++ b/src/build/mkgamedata.c
@@ -1,5 +1,5 @@
/*
- * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com>
+ * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -211,13 +211,13 @@ _( "}")
int OS_MAIN(int argc, os_char *argv[]) {
for (++argv; *argv; ++argv) {
- int fd = os_open(*argv, O_RDONLY);
+ int fd = os_open_read(*argv);
if (fd == -1) die("couldn't open file");
struct kv_parser kv = {0};
struct parsestate state = {*argv, &kv, &root};
char buf[1024];
int nread;
- while (nread = read(fd, buf, sizeof(buf))) {
+ while (nread = os_read(fd, buf, sizeof(buf))) {
if (nread == -1) die("couldn't read file");
if (!kv_parser_feed(&kv, buf, nread, &kv_cb, &state)) goto ep;
}
@@ -226,7 +226,7 @@ ep: fprintf(stderr, "mkgamedata: %" fS ":%d:%d: bad syntax: %s\n",
*argv, kv.line, kv.col, kv.errmsg);
exit(1);
}
- close(fd);
+ os_close(fd);
}
FILE *out = fopen(".build/include/gamedata.gen.h", "wb");
diff --git a/src/build/skiplist.h b/src/build/skiplist.h
index b747d89..ab6a920 100644
--- a/src/build/skiplist.h
+++ b/src/build/skiplist.h
@@ -1,5 +1,5 @@
/*
- * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com>
+ * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -24,6 +24,7 @@
#ifdef _WIN32
static inline int _skiplist_ffs(uint x) {
+ uchar _BitScanForward(ulong *idx, ulong mask);
uint ret;
// on Windows, sizeof(ulong) == sizeof(uint)
if (_BitScanForward((ulong *)&ret, x)) return ret + 1; else return 0;
diff --git a/src/errmsg.h b/src/errmsg.h
index 17a4457..cdf5095 100644
--- a/src/errmsg.h
+++ b/src/errmsg.h
@@ -1,5 +1,5 @@
/*
- * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com>
+ * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -17,6 +17,15 @@
#ifndef INC_ERRMSG_H
#define INC_ERRMSG_H
+#ifdef _WIN32
+#include <stdarg.h>
+
+// note: redundant declspec avoids warnings if Windows.h was also included
+__declspec(dllimport) unsigned long __stdcall FormatMessageA(unsigned long,
+ const void *, unsigned long, unsigned long, char *, unsigned long,
+ va_list *);
+#endif
+
#include "con_.h"
#include "os.h"
@@ -48,14 +57,17 @@ extern const char msg_note[], msg_warn[], msg_error[];
#ifdef _WIN32
#define _errmsg_sys(msg, ...) do { \
- char _warnsys_buf[512]; \
- OS_WINDOWS_ERROR(_warnsys_buf); \
- _errmsg_msg(_ERRMSG_STR(MODULE_NAME), msg, __VA_ARGS__, _warnsys_buf); \
+ char _errmsg_buf[512]; \
+ FormatMessageA(/*FORMAT_MESSAGE_FROM_SYSTEM*/ 4096, 0, os_lasterror(), \
+ /*MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)*/ 1024, _errmsg_buf, \
+ sizeof(_errmsg_buf), 0); \
+ _errmsg_msg(_ERRMSG_STR(MODULE_NAME), msg, __VA_ARGS__, _errmsg_buf); \
} while (0)
#define _errmsg_dl _errmsg_sys
#else
#define _errmsg_sys _errmsg_std
static inline const char *_errmsg_dlerror(void) {
+ char *dlerror(void);
const char *e = dlerror();
if (!e) return "Unknown error"; // just in case, better avoid weirdness!
return e;
diff --git a/src/gameinfo.c b/src/gameinfo.c
index 85220a8..d535a44 100644
--- a/src/gameinfo.c
+++ b/src/gameinfo.c
@@ -1,5 +1,5 @@
/*
- * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com>
+ * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -15,6 +15,9 @@
*/
#include <string.h>
+#ifdef _WIN32
+#include <Windows.h> // MultiByteToWideChar()
+#endif
#include "engineapi.h"
#include "errmsg.h"
diff --git a/src/hook.c b/src/hook.c
index 9a6e885..fe311cc 100644
--- a/src/hook.c
+++ b/src/hook.c
@@ -23,6 +23,13 @@
#include "os.h"
#include "x86.h"
+#ifdef _WIN32
+// try to avoid pulling in all of Windows.h for this... (redundant dllimport
+// avoids warnings in hook.test.c where Windows.h is included via test.h)
+__declspec(dllimport) int __stdcall FlushInstructionCache(
+ void *, const void *, usize);
+#endif
+
// Warning: half-arsed hacky implementation (because that's all we really need)
// Almost certainly breaks in some weird cases. Oh well! Most of the time,
// vtable hooking is more reliable, this is only for, uh, emergencies.
diff --git a/src/nomute.c b/src/nomute.c
index 93cfcd2..6a42d46 100644
--- a/src/nomute.c
+++ b/src/nomute.c
@@ -1,6 +1,6 @@
/*
* Copyright © 2023 Willian Henrique <wsimanbrazil@yahoo.com.br>
- * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com>
+ * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -18,16 +18,17 @@
// TODO(linux): this is currently only built on Windows, but a linux
// implementation would be also useful for some games e.g. L4D2
+// NOTE: all three of these headers must be in this order. annoyingly.
+#include <Windows.h>
+#include <mmeapi.h>
+#include <dsound.h>
+
#include "con_.h"
#include "errmsg.h"
#include "feature.h"
#include "os.h"
#include "sst.h"
-// these have to come after Windows.h (via os.h) and in this order
-#include <mmeapi.h>
-#include <dsound.h>
-
FEATURE("inactive window audio control")
DEF_CVAR_UNREG(snd_mute_losefocus,
diff --git a/src/os-unix.h b/src/os-unix.h
deleted file mode 100644
index 097d300..0000000
--- a/src/os-unix.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
- * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
- * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
- * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-
-/* Unix-specific definitions for os.h - don't include this directly! */
-
-#ifdef __GNUC__
-#define EXPORT __attribute__((visibility("default")))
-#else
-#define EXPORT int novis[-!!"visibility attribute requires Clang or GCC"];
-#endif
-#define IMPORT
-
-typedef char os_char;
-#define OS_LIT(x) x
-#define os_snprintf snprintf
-#define os_strchr strchr
-#define os_strcmp strcmp
-#define os_strcpy strcpy
-#define os_strlen strlen
-#define os_fopen fopen
-#define os_open open
-#define os_access access
-#define os_stat stat
-#define os_mkdir(f) mkdir(f, 0755) // use real mkdir(2) if the mode matters
-#define os_unlink unlink
-#define os_getenv getenv
-#define os_getcwd getcwd
-
-#define OS_DLPREFIX "lib"
-#define OS_DLSUFFIX ".so"
-
-#define OS_MAIN main
-
-static inline void *os_dlopen(const char *name) {
- return dlopen(name, RTLD_NOW);
-}
-#define os_dlsym dlsym
-
-#ifdef __linux__
-// note: this is glibc-specific. it shouldn't be used in build-time code, just
-// the plugin itself (that really shouldn't be a problem).
-
-// private struct hidden behind _GNU_SOURCE. see dlinfo(3) or <link.h>
-struct gnu_link_map {
- unsigned long l_addr;
- const char *l_name;
- void *l_ld;
- struct gnu_link_map *l_next, *l_prev;
- // [more private stuff below]
-};
-
-static inline void *os_dlhandle(const char *name) {
- extern struct gnu_link_map *_os_lmbase; // note: defined in sst.c for now
- if (!_os_lmbase) { // IMPORTANT: not thread safe. don't forget later!
- _os_lmbase = (struct gnu_link_map *)dlopen("libc.so.6",
- RTLD_LAZY | RTLD_NOLOAD);
- dlclose(_os_lmbase); // assume success
- while (_os_lmbase->l_prev) _os_lmbase = _os_lmbase->l_prev;
- }
- // this is a tiny bit crude, but basically okay. we just want to find
- // something that roughly matches the basename, rather than needing an exact
- // path, in a manner vaguely similar to Windows' GetModuleHandle(). that way
- // we can just look up client.so or something without having to figure out
- // where exactly that is.
- for (struct gnu_link_map *lm = _os_lmbase; lm; lm = lm->l_next) {
- if (name[0] == '/') {
- if (!strcmp(name, lm->l_name)) return lm;
- continue;
- }
- int namelen = strlen(lm->l_name);
- int sublen = strlen(name);
- if (sublen >= namelen) continue;
- if (lm->l_name[namelen - sublen - 1] == '/' && !memcmp(
- lm->l_name + namelen - sublen, name, sublen)) {
- return lm;
- }
- }
- return 0;
-}
-
-static inline int os_dlfile(void *m, char *buf, int sz) {
- struct gnu_link_map *lm = m;
- int ssz = strlen(lm->l_name) + 1;
- if (ssz > sz) { errno = ENAMETOOLONG; return -1; }
- memcpy(buf, lm->l_name, ssz);
- return ssz;
-}
-
-#endif
-
-// unix mprot flags are much nicer but cannot be defined in terms of the windows
-// ones, so we use the windows ones and define them in terms of the unix ones.
-// another victory for stupid!
-#define PAGE_NOACCESS 0
-#define PAGE_READONLY PROT_READ
-#define PAGE_READWRITE PROT_READ | PROT_WRITE
-#define PAGE_EXECUTE_READ PROT_READ | PROT_EXEC
-#define PAGE_EXECUTE_READWRITE PROT_READ | PROT_WRITE | PROT_EXEC
-
-static inline bool os_mprot(void *addr, int len, int fl) {
- // round down address and round up size
- addr = (void *)((unsigned long)addr & ~(4095));
- len = len + 4095 & ~(4095);
- return mprotect(addr, len, fl) != -1;
-}
-
-static inline void os_randombytes(void *buf, int sz) {
- // if this call fails, the system is doomed, so loop until success and pray.
- while (getentropy(buf, sz) == -1);
-}
-
-// vi: sw=4 ts=4 noet tw=80 cc=80
diff --git a/src/os-win32.h b/src/os-win32.h
deleted file mode 100644
index db20964..0000000
--- a/src/os-win32.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
- * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
- * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
- * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-
-/* Windows-specific definitions for os.h - don't include this directly! */
-
-#define IMPORT __declspec(dllimport) // only needed for variables
-#define EXPORT __declspec(dllexport)
-
-typedef unsigned short os_char;
-#define _OS_CAT(L, x) L##x
-#define OS_LIT(x) _OS_CAT(L, x)
-
-#define os_snprintf _snwprintf
-#define os_strchr wcschr
-#define os_strcmp wcscmp
-#define os_strcpy wcscpy
-#define os_strlen wcslen
-#define strncasecmp _strnicmp // stupid!
-
-#define os_fopen _wfopen
-// yuck :(
-#define _os_open3(path, flags, mode) _wopen((path), (flags) | _O_BINARY, (mode))
-#define _os_open2(path, flags) _wopen((path), (flags) | _O_BINARY)
-#define _os_open(a, b, c, x, ...) x
-#define os_open(...) _os_open(__VA_ARGS__, _os_open3, _os_open2, _)(__VA_ARGS__)
-#define os_access _waccess
-// ucrt defines __stat64 to _stat64. we want _wstat64 to be the actual function
-#define _stat64(path, buf) _wstat64(path, buf)
-#define os_stat _stat64
-#define os_mkdir _wmkdir
-#define os_unlink _wunlink
-#define os_getenv _wgetenv
-#define os_getcwd _wgetcwd
-
-#define OS_DLPREFIX ""
-#define OS_DLSUFFIX ".dll"
-
-#define OS_MAIN wmain
-
-static inline void *os_dlopen(const unsigned short *name) {
- return LoadLibraryW(name);
-}
-static inline void *os_dlhandle(const unsigned short *name) {
- return GetModuleHandleW(name);
-}
-static inline void *os_dlsym(void *m, const char *s) {
- return (void *)GetProcAddress(m, s);
-}
-
-static inline int os_dlfile(void *m, unsigned short *buf, int sz) {
- unsigned int n = GetModuleFileNameW(m, buf, sz);
- if (n == 0 || n == sz) return -1;
- // get the canonical capitalisation, as for some reason GetModuleFileName()
- // returns all lowercase. this doesn't really matter except it looks nicer
- GetLongPathNameW(buf, buf, n + 1);
- // the drive letter will also be lower case, if it is an actual drive letter
- // of course. it should be; I'm not gonna lose sleep over UNC paths and such
- if (buf[0] >= L'a' && buf[0] <= L'z' && buf[1] == L':') buf[0] &= ~32u;
- return n;
-}
-
-static inline bool os_mprot(void *addr, int len, int fl) {
- unsigned long old;
- return !!VirtualProtect(addr, len, fl, &old);
-}
-
-// SystemFunction036 is the *real* name of "RtlGenRandom," and is also
-// incorrectly defined in system headers. Yay.
-int __stdcall SystemFunction036(void *buf, unsigned long sz);
-
-static inline void os_randombytes(void *buf, int sz) {
- // if this call fails, the system is doomed, so loop until success and pray.
- while (!SystemFunction036(buf, sz));
-}
-
-/* Further Windows-specific workarounds because Windows is great */
-
-#ifndef PATH_MAX
-// XXX: win32/crt has this dumb 260 limit even though the actual kernel imposes
-// no limit (though apparently NTFS has a limit of 65535). Theoretically we
-// could do some memes with UNC paths to extend it to at least have parity with
-// Unix PATH_MAX (4096), but for now we just kind of accept that Windows is a
-// disaster.
-#define PATH_MAX MAX_PATH
-#endif
-
-// why does Windows prefix so many things with underscores?
-#define alloca _alloca
-#define read _read
-#define write _write
-#define close _close
-#define fdopen _fdopen
-#define dup _dup
-#define dup2 _dup2
-#define strdup _strdup
-#define fstat _fstat64
-#define lseek _lseeki64
-#define O_RDONLY _O_RDONLY
-#define O_RDWR _O_RDWR
-#define O_CLOEXEC _O_NOINHERIT // and why did they rename this!?
-#define O_CREAT _O_CREAT
-#define O_EXCL _O_EXCL
-// missing access() flags
-#define F_OK 0
-#define R_OK 4
-#define W_OK 2
-#define X_OK R_OK // there's no actual X bit, just pretend
-
-// windows doesn't define this for some reason!? note: add others as needed...
-#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
-
-// just dump this boilerplate here as well, I spose
-#define OS_WINDOWS_ERROR(arrayname) \
- FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), \
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), arrayname, \
- sizeof(arrayname), 0)
-
-// vi: sw=4 ts=4 noet tw=80 cc=80
diff --git a/src/os.c b/src/os.c
new file mode 100644
index 0000000..49cab63
--- /dev/null
+++ b/src/os.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#ifdef _WIN32
+#include <Windows.h>
+#else
+#include <errno.h>
+#include <dlfcn.h>
+#include <limits.h>
+#include <link.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#endif
+
+#include "intdefs.h"
+
+#ifdef _WIN32
+
+int os_lasterror(void) { return GetLastError(); }
+
+// N.B. file handle values are 32-bit, even in 64-bit builds. I'm not crazy!
+
+int os_open_read(const ushort *path) {
+ return (int)(ssize)CreateFileW(path, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, 0);
+}
+int os_open_write(const ushort *file) {
+ return (int)(ssize)CreateFileW(file, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, 0);
+}
+int os_open_writetrunc(const ushort *file) {
+ return (int)(ssize)CreateFileW(file, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, 0);
+}
+
+int os_read(int f, void *buf, int max) {
+ ulong n;
+ return ReadFile((void *)(ssize)f, buf, max, &n, 0) ? n : -1;
+}
+int os_write(int f, const void *buf, int len) {
+ ulong n;
+ return WriteFile((void *)(ssize)f, buf, len, &n, 0) ? n : -1;
+}
+
+vlong os_fsize(int f) {
+ vlong ret;
+ if (!GetFileSizeEx((void *)(ssize)f, (LARGE_INTEGER *)&ret)) return -1;
+ return ret;
+}
+
+void os_close(int f) {
+ CloseHandle((void *)(ssize)f);
+}
+
+void os_getcwd(ushort buf[static 260]) { GetCurrentDirectoryW(260, buf); }
+
+bool os_mkdir(const ushort *path) { return CreateDirectoryW(path, 0); }
+bool os_unlink(const ushort *path) { return DeleteFileW(path); }
+bool os_rmdir(const ushort *path) { return RemoveDirectoryW(path); }
+
+int __stdcall ProcessPrng(char *data, usize sz); // from bcryptprimitives.dll
+void os_randombytes(void *buf, int sz) { ProcessPrng(buf, sz); }
+
+void *os_dlhandle(const ushort *name) {
+ return GetModuleHandleW(name);
+}
+void *os_dlsym(void *restrict lib, const char *restrict s) {
+ return (void *)GetProcAddress(lib, s);
+}
+int os_dlfile(void *lib, ushort *buf, int sz) {
+ unsigned int n = GetModuleFileNameW(lib, buf, sz);
+ if (n == 0 || n == sz) return -1;
+ // get the canonical capitalisation, as for some reason GetModuleFileName()
+ // returns all lowercase. this doesn't really matter except it looks nicer
+ GetLongPathNameW(buf, buf, n + 1);
+ // the drive letter will also be lower case, if it is an actual drive letter
+ // of course. it should be; I'm not gonna lose sleep over UNC paths and such
+ if (buf[0] >= L'a' && buf[0] <= L'z' && buf[1] == L':') buf[0] &= ~32u;
+ return n;
+}
+
+bool os_mprot(void *addr, int len, int mode) {
+ ulong old;
+ return !!VirtualProtect(addr, len, mode, &old);
+}
+
+#else
+
+int os_lasterror(void) { return errno; }
+
+int os_open_read(const char *path) {
+ return open(path, O_RDONLY | O_CLOEXEC);
+}
+int os_open_write(const char *path) {
+ return open(path, O_RDWR | O_CLOEXEC | O_CREAT, 0644);
+}
+int os_open_writetrunc(const char *path) {
+ return open(path, O_RDWR | O_CLOEXEC | O_CREAT | O_TRUNC, 0644);
+}
+int os_read(int f, void *buf, int max) { return read(f, buf, max); }
+int os_write(int f, const void *buf, int max) { return write(f, buf, max); }
+void os_close(int f) { close(f); }
+
+vlong os_fsize(int f) {
+ struct stat s;
+ if (stat(f, &s) == -1) return -1;
+ return s.st_size;
+}
+
+void os_getcwd(char buf[PATH_MAX]) { getcwd(buf, PATH_MAX); }
+
+bool os_mkdir(const char *path) { return mkdir(path, 0555) != -1; }
+bool os_unlink(const char *path) { return unlink(path) != -1; }
+bool os_rmdir(const char *path) { return rmdir(path) != -1; }
+
+void *os_dlsym(void *restrict lib, const char *restrict name) {
+ return dlsym(lib, name);
+}
+
+bool os_mprot(void *addr, int len, int mode) {
+ // round down address and round up size
+ addr = (void *)((ulong)addr & ~(4095));
+ len = len + 4095 & ~(4095);
+ return mprotect(addr, len, mode) != -1;
+}
+
+void os_randombytes(void *buf, int sz) { while (getentropy(buf, sz) == -1); }
+
+#endif
+
+#ifdef __linux__
+// glibc-specific stuff here. it shouldn't be used in build-time code, just
+// the plugin itself (that really shouldn't be a problem).
+
+// private struct hidden behind _GNU_SOURCE. see dlinfo(3) or <link.h>
+struct link_map {
+ ulong l_addr;
+ const char *l_name;
+ void *l_ld;
+ struct link_map *l_next, *l_prev;
+ // [more private stuff below]
+};
+
+static struct link_map *lmbase = 0;
+
+void *os_dlhandle(const char *name) {
+ extern struct link_map *lmbase; // note: defined in sst.c for now
+ if (!lmbase) { // IMPORTANT: not thread safe. don't forget later!
+ lmbase = (struct link_map *)dlopen("libc.so.6", RTLD_LAZY | RTLD_NOLOAD);
+ dlclose(lmbase); // assume success
+ while (lmbase->l_prev) lmbase = lmbase->l_prev;
+ }
+ // this is a tiny bit crude, but basically okay. we just want to find
+ // something that roughly matches the basename, rather than needing an exact
+ // path, in a manner vaguely similar to Windows' GetModuleHandle(). that way
+ // we can just look up client.so or something without having to figure out
+ // where exactly that is.
+ for (struct link_map *lm = lmbase; lm; lm = lm->l_next) {
+ if (name[0] == '/') {
+ if (!strcmp(name, lm->l_name)) return lm;
+ continue;
+ }
+ int namelen = strlen(lm->l_name);
+ int sublen = strlen(name);
+ if (sublen >= namelen) continue;
+ if (lm->l_name[namelen - sublen - 1] == '/' && !memcmp(
+ lm->l_name + namelen - sublen, name, sublen)) {
+ return lm;
+ }
+ }
+ return 0;
+}
+
+int os_dlfile(void *lib, char *buf, int sz) {
+ struct link_map *lm = lib;
+ int ssz = strlen(lm->l_name) + 1;
+ if (ssz > sz) { errno = ENAMETOOLONG; return -1; }
+ memcpy(buf, lm->l_name, ssz);
+ return ssz;
+}
+#endif
+
+// vi: sw=4 ts=4 noet tw=80 cc=80
diff --git a/src/os.h b/src/os.h
index c954d1a..b599523 100644
--- a/src/os.h
+++ b/src/os.h
@@ -1,5 +1,5 @@
/*
- * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com>
+ * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -17,48 +17,238 @@
#ifndef INC_OS_H
#define INC_OS_H
-/*
- * Here we declare an absolute ton of wrappers, macros, compatibility shims,
- * reimplementations and so on to try in vain to sweep the inconsistencies
- * between Windows and not-Windows under the rug.
- */
+#include <string.h>
+#include <sys/stat.h> // XXX: try abstracting stat() and avoiding ucrt dep here
-#include <errno.h>
-#include <fcntl.h>
#ifdef _WIN32
-#include <direct.h>
-#include <io.h>
-#include <wchar.h>
-// DUMB HACK: noreturn.h is alphabetically before os.h so including it after
-// looks weird and inconsistent. including it before breaks Windows.h though
-// because of __declspec(noreturn). Undef for now, and restore at the end of
-// this header.
-#undef noreturn
-#include <Windows.h>
-#include <winternl.h>
-#else
-#include <dlfcn.h>
-#include <limits.h>
-#include <link.h>
-#include <stdio.h>
-#include <string.h>
-#include <strings.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <unistd.h>
+
+#include <wchar.h> // XXX: there's kind of a lot of junk in this header!
+
+#define IMPORT __declspec(dllimport) // only needed for variables
+#define EXPORT __declspec(dllexport)
+
+typedef unsigned short os_char;
+#define _OS_CAT(x, y) x##y
+#define OS_LIT(x) _OS_CAT(L, x)
+#define os_strcmp wcscmp
+#define os_strlen wcslen
+// ucrt defines __stat64 to _stat64. we want _wstat64 to be the actual function
+#define _stat64(path, buf) _wstat64(path, buf)
+#define os_stat _stat64
+
+#define OS_DLPREFIX ""
+#define OS_DLSUFFIX ".dll"
+
+#define OS_MAIN wmain
+
+// add to these as needed...
+#define OS_EEXIST /*ERROR_ALREADY_EXISTS*/ 183
+#define OS_ENOENT /*ERROR_PATH_NOT_FOUND*/ 3
+
+// Further Windows-specific workarounds because Windows is great...
+
+#ifndef PATH_MAX
+// XXX: win32/crt has this dumb 260 limit even though the actual kernel imposes
+// no limit (though apparently NTFS has a limit of 65535). Theoretically we
+// could do some memes with UNC paths to extend it to at least have parity with
+// Unix PATH_MAX (4096), but for now we just kind of accept that Windows is a
+// disaster.
+#define PATH_MAX 260
#endif
-// Things were getting unwieldy so they're now split into these headers...
-#ifdef _WIN32
-#include "os-win32.h"
-// DUMB HACK part 2: restore what the #including source file had asked for
-#ifdef INC_NORETURN_H
-#define noreturn _Noreturn void
+// windows doesn't define this for some reason!? note: add others as needed...
+#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
+
+#define alloca _alloca // ugh!
+
+// one last thing: mprot constants are part of os.h API, whether or not
+// Windows.h was included. this is a bit of a hack, but whatever!
+#ifndef _INC_WINDOWS
+#define PAGE_NOACCESS 1
+#define PAGE_READONLY 2
+#define PAGE_READWRITE 4
+#define PAGE_EXECUTE_READ 32
+#define PAGE_EXECUTE_READWRITE 64
#endif
+
#else
-#include "os-unix.h"
+
+#include <errno.h> // meh
+
+#define IMPORT
+#ifdef __GNUC__
+#define EXPORT __attribute__((visibility("default"))
+#else
+#define EXPORT int exp[-!!"compiler needs a way to export symbols!"];
+#endif
+
+// trying to avoid pulling in unnecessary headers as much as possible: define
+// our own constants for os_mprot() / mprotect()
+#if defined(__linux__) // apparently linux is pretty much the sole oddball here!
+#define _OS_PROT_READ 4
+#define _OS_PROT_WRITE 2
+#define _OS_PROT_EXEC 1
+#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || \
+ defined(__DragonFly__) || defined(__sun) || defined(__serenity)
+#define _OS_PROT_READ 1
+#define _OS_PROT_WRITE 2
+#define _OS_PROT_EXEC 4
+#elif !defined(_WIN32) // what else could this possibly even be!?
+#include <sys/mman.h> // just fall back on this. not the end of the world...
+#define _OS_PROT_READ PROT_READ
+#define _OS_PROT_WRITE PROT_WRITE
+#define _OS_PROT_EXEC PROT_EXEC
#endif
+typedef char os_char;
+#define OS_LIT(x) x
+#define os_strcmp strcmp
+#define os_strlen strlen
+#define os_stat stat
+
+#define OS_DLPREFIX "lib"
+#define OS_DLSUFFIX ".so"
+
+#define OS_MAIN main
+
+// unix mprotect flags are much nicer but cannot be defined in terms of the
+// windows ones, so we use the windows ones and define them in terms of the unix
+// ones. another victory for stupid!
+#define PAGE_NOACCESS 0
+#define PAGE_READONLY _OS_PROT_READ
+#define PAGE_READWRITE _OS_PROT_READ | _OS_PROT_WRITE
+#define PAGE_EXECUTE_READ _OS_PROT_READ | _OS_PROT_EXEC
+#define PAGE_EXECUTE_READWRITE _OS_PROT_READ | _OS_PROT_WRITE | _OS_PROT_EXEC
+
+#define OS_EEXIST EEXIST
+#define OS_ENOENT ENOENT
+
+#endif
+
+/* Copies n characters from src to dest, using the OS-specific char type. */
+static inline void os_spancopy(os_char *restrict dest,
+ const os_char *restrict src, int n) {
+ memcpy(dest, src, n * sizeof(os_char));
+}
+
+/*
+ * Returns the last error code from an OS function - equivalent to calling
+ * GetLastError() in Windows and reading errno in Unix-like operating systems.
+ * For standard libc functions (implemented by UCRT on Windows), the value of
+ * errno should be used directly instead.
+ */
+int os_lasterror(void);
+
+/*
+ * Opens a file for reading. Returns an OS-specific file handle, or -1 on error.
+ */
+int os_open_read(const os_char *path);
+
+/*
+ * Opens a file for writing, creating it if required. Returns an OS-specific
+ * file handle, or -1 on error.
+ */
+int os_open_write(const os_char *path);
+
+/*
+ * Opens a file for writing, creating it if required, or truncating it if it
+ * already exists. Returns an OS-specific file handle, or -1 on error.
+ */
+int os_open_writetrunc(const os_char *path);
+
+/*
+ * Reads up to max bytes from OS-specific file handle f into buf. Returns the
+ * number of bytes read, or -1 on error.
+ */
+int os_read(int f, void *buf, int max);
+
+/*
+ * Reads up to len bytes from buf to OS-specific file handle f. Returns the
+ * number of bytes written, or -1 on error. Generally the number of bytes
+ * written will be len, unless writing to a pipe/socket, which has a limited
+ * internal buffer, or possibly being preempted by a signal handler.
+ */
+int os_write(int f, const void *buf, int len);
+
+/*
+ * Returns the length of the on-disk file referred to by OS-specific file handle
+ * f, or -1 on error (the most likely error would be that the file is not
+ * actually on disk and instead refers to a pipe or something).
+ */
+long long os_fsize(int f);
+
+/*
+ * Closes the OS-specific file handle f. On Windows, this causes pending writes
+ * to be flushed; on Unix-likes, this generally happens asynchronously. If
+ * blocking is a concern when writing files to disk, some sort of thread pool
+ * should always be used.
+ */
+void os_close(int f);
+
+/*
+ * Gets the current working directory, which may be up to PATH_MAX characters in
+ * length (using the OS-specific character type).
+ */
+void os_getcwd(os_char buf[static PATH_MAX]);
+
+/*
+ * Tries to create a directory at path. Returns true on success, false on
+ * failure. One may wish to ignore a failure if the directory already exists;
+ * this can be done by checking if os_lasterror() returns OS_EEXIST.
+ */
+bool os_mkdir(const os_char *path);
+
+/*
+ * Tries to delete a file(name) at path. Returns true on success, false on
+ * failure. One may wish to ignore a failure if the file already doesn't exist;
+ * this can be done by checking if os_lasterror() returns OS_ENOENT.
+ *
+ * On some Unix-like operating systems, this may work on empty directories, but
+ * for portably reliable results, one should call os_rmdir() for that instead.
+ */
+bool os_unlink(const os_char *path);
+
+/*
+ * Tries to delete a directory at path. Returns true on success, false on
+ * failure. One may wish to ignore a failure if the directory already doesn't
+ * exist; this can be done by checking if os_lasterror() returns OS_ENOENT.
+ */
+bool os_rmdir(const os_char *path);
+
+/*
+ * Tries to look up the symbol sym from the shared library handle lib. Returns
+ * an opaque pointer on success, or null on failure.
+ */
+void *os_dlsym(void *restrict lib, const char *restrict sym);
+
+#if defined(_WIN32) || defined(__linux__)
+/*
+ * Tries to get a handle to an already-loaded shared library matching name.
+ * Returns the library handle on success, or null on failure.
+ */
+void *os_dlhandle(const os_char *name);
+
+/*
+ * Tries to determine the file path to the shared library handle lib, and stores
+ * it in buf (with max length given by sz). Returns the length of the resulting
+ * string, or -1 on failure.
+ */
+int os_dlfile(void *lib, os_char *buf, int sz);
+#endif
+
+/*
+ * Changes memory protection for the address range given by addr and len, using
+ * one of the Win32-style PAGE_* flags specified above. Returns true on success
+ * and false on failure.
+ */
+bool os_mprot(void *addr, int len, int mode);
+
+/*
+ * Fills buf with up to sz cryptographically random bytes. sz has an OS-specific
+ * upper limit - a safe value across all major operating systems is 256.
+ */
+void os_randombytes(void *buf, int sz);
+
#endif
// vi: sw=4 ts=4 noet tw=80 cc=80
diff --git a/src/sst.c b/src/sst.c
index de04b88..ce80032 100644
--- a/src/sst.c
+++ b/src/sst.c
@@ -14,13 +14,14 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef _WIN32
-#include <stdlib.h> // unsetenv
-#endif
#include <string.h>
#ifdef _WIN32
+#include <Windows.h>
#include <shlwapi.h>
+#else
+#include <stdlib.h> // unsetenv
+#include <sys/uio.h>
#endif
#include "con_.h"
@@ -74,8 +75,6 @@ static void *ownhandle(void) {
}
return cached;
}
-
-struct gnu_link_map *_os_lmbase = 0; // XXX: stupid place to put this, oh well
#endif
#ifdef _WIN32
@@ -99,7 +98,7 @@ DEF_CCMD_HERE(sst_autoload_enable, "Register SST to load on game startup", 0) {
const os_char *startdir;
if (ifacever == 2) {
startdir = _startdir;
- os_getcwd(_startdir, PATH_MAX); // if this fails, OS devs are all fired.
+ os_getcwd(_startdir);
#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
@@ -112,13 +111,12 @@ DEF_CCMD_HERE(sst_autoload_enable, "Register SST to load on game startup", 0) {
errmsg_errorx("path to game is too long");
return;
}
- memcpy(_startdir + len,
#ifdef _WIN32
- L"\\bin", // PathRelativePathToW actually NEEDS a backslash, UGH
+ // PathRelativePathToW actually NEEDS a backslash, UGH
+ os_spancopy(_startdir + len, L"\\bin", 5);
#else
- "/bin",
+ os_spancopy(_startdir + len, L"/bin", 5);
#endif
- 5 * sizeof(os_char));
}
else /* ifacever == 3 */ {
// newer games load from the mod dir instead of engine bin, and search
@@ -143,8 +141,17 @@ DEF_CCMD_HERE(sst_autoload_enable, "Register SST to load on game startup", 0) {
errmsg_errorsys("couldn't compute a relative path");
return;
}
- // arbitrary aesthetic judgement
- for (ushort *p = relpath; *p; ++p) if (*p == L'\\') *p = L'/';
+ // arbitrary aesthetic judgement - use forward slashes. while we're at it,
+ // also make sure there's no unicode in there, just in case...
+ int rellen = 0;
+ for (ushort *p = relpath; *p; ++p, ++rellen) {
+ if (*p > 127) {
+ errmsg_errorx("mod dir contains Unicode characters which Source "
+ "doesn't handle well - autoload file not created");
+ return;
+ }
+ if (*p == L'\\') *p = L'/';
+ }
#else
const char *p = path, *q = startdir;
int slash = 0;
@@ -177,27 +184,34 @@ c: memcpy(r, p + slash + 1, rellen);
errmsg_errorx("path to VDF is too long");
return;
}
- memcpy(path, gameinfo_gamedir, len * sizeof(*gameinfo_gamedir));
- memcpy(path + len, OS_LIT("/addons"), 8 * sizeof(os_char));
- if (os_mkdir(path) == -1 && errno != EEXIST) {
- errmsg_errorstd("couldn't create %" fS, path);
+ os_spancopy(path, gameinfo_gamedir, len);
+ os_spancopy(path + len, OS_LIT("/addons"), 8);
+ if (!os_mkdir(path) && os_lasterror() != OS_EEXIST) {
+ errmsg_errorsys("couldn't create %" fS, path);
return;
}
- memcpy(path + len + sizeof("/addons") - 1,
+ os_spancopy(path + len + sizeof("/addons") - 1,
OS_LIT("/") OS_LIT(VDFBASENAME) OS_LIT(".vdf"),
- sizeof("/" VDFBASENAME ".vdf") * sizeof(os_char));
- FILE *f = os_fopen(path, OS_LIT("wb"));
- if (!f) {
- errmsg_errorstd("couldn't open %" fS, path);
- return;
- }
- // 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);
+ sizeof("/" VDFBASENAME ".vdf"));
+ int f = os_open_write(path);
+ if (f == -1) { errmsg_errorsys("couldn't open %" fS, path); return; }
+#ifdef _WIN32
+ char buf[19 + PATH_MAX];
+ memcpy(buf, "Plugin { file \"", 15);
+ for (int i = 0; i < rellen; ++i) buf[i + 15] = relpath[i];
+ memcpy(buf + 15 + rellen, "\" }\n", 4);
+ if (os_write(f, buf, rellen + 19) == -1) { // blegh
+#else
+ struct iovec iov[3] = {
+ {"Plugin { file \"", 15},
+ {relpath, rellen},
+ {"\" }\n", 4}
+ };
+ if (writev(fd, &iov, 3) == -1) {
+#endif
+ errmsg_errorsys("couldn't write to %" fS, path);
}
- fclose(f);
+ os_close(f);
}
DEF_CCMD_HERE(sst_autoload_disable, "Stop loading SST on game startup", 0) {
@@ -208,11 +222,11 @@ DEF_CCMD_HERE(sst_autoload_disable, "Stop loading SST on game startup", 0) {
errmsg_errorx("path to VDF is too long");
return;
}
- memcpy(path, gameinfo_gamedir, len * sizeof(*gameinfo_gamedir));
- memcpy(path + len, OS_LIT("/addons/") OS_LIT(VDFBASENAME) OS_LIT(".vdf"),
- sizeof("/addons/" VDFBASENAME ".vdf") * sizeof(os_char));
- if (os_unlink(path) == -1 && errno != ENOENT) {
- errmsg_warnstd("couldn't delete %" fS, path);
+ os_spancopy(path, gameinfo_gamedir, len);
+ os_spancopy(path + len, OS_LIT("/addons/") OS_LIT(VDFBASENAME) OS_LIT(".vdf"),
+ sizeof("/addons/" VDFBASENAME ".vdf"));
+ if (!os_unlink(path) && os_lasterror() != OS_ENOENT) {
+ errmsg_warnsys("couldn't delete %" fS, path);
}
}
diff --git a/src/stubs/BCryptPrimitives.c b/src/stubs/BCryptPrimitives.c
new file mode 100644
index 0000000..afcaa7e
--- /dev/null
+++ b/src/stubs/BCryptPrimitives.c
@@ -0,0 +1,6 @@
+/* This file is dedicated to the public domain. */
+
+// We have to define a real function with the right number of arguments to get
+// the mangled symbol _ProcessPrng@8, which the .def file apparently turns
+// into an "undecorate" .lib entry through some undocumented magic (of course!).
+int __stdcall ProcessPrng(void *x, unsigned int y) { return 0; }
diff --git a/src/stubs/bcryptprimitives.def b/src/stubs/bcryptprimitives.def
new file mode 100644
index 0000000..9021d7c
--- /dev/null
+++ b/src/stubs/bcryptprimitives.def
@@ -0,0 +1,7 @@
+; This file is dedicated to the public domain.
+
+LIBRARY bcryptprimitives
+EXPORTS
+ ProcessPrng
+
+; vi: sw=4 ts=4 noet tw=80 cc=80