diff --git a/src/filesystem/dos/SDL_sysfilesystem.c b/src/filesystem/dos/SDL_sysfilesystem.c
index 77a4ef4d24..fbc897dae7 100644
--- a/src/filesystem/dos/SDL_sysfilesystem.c
+++ b/src/filesystem/dos/SDL_sysfilesystem.c
@@ -22,9 +22,10 @@
#ifdef SDL_FILESYSTEM_DOS
-#include
#include
+#include "../../core/dos/SDL_dos.h"
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// System dependent filesystem routines
@@ -32,33 +33,75 @@
char *SDL_SYS_GetBasePath(void)
{
- extern const char *SDL_argv0; // from src/main/dos/SDL_sysmain_runapp.c
- char *searched = searchpath(SDL_argv0);
- if (!searched) {
- SDL_SetError("argv[0] not found by searchpath");
- return NULL;
- }
+ /* As of MS-DOS 3.0, you can get the full path to the EXE from the very end of the
+ environment table, which is discovered through the PSP:
+ https://en.wikipedia.org/wiki/Program_Segment_Prefix
- char *fullpath = SDL_strdup(searched);
- if (!fullpath) {
- return NULL;
- }
+ An EXE is launched with the PSP's segment in DS, but we'll use a DOS API to obtain it
+ later, in case we don't control startup. https://stanislavs.org/helppc/int_21-62.html
- // I don't know if this is a good idea. Drop DOS path separators, use Unix style instead.
- char *ptr;
- for (ptr = fullpath; *ptr; ptr++) {
- if (*ptr == '\\') {
- *ptr = '/';
+ The table pointed to by the word at offset 0x2C in the PSP is just a collection
+ of ASCIZ strings, with a blank string signifying the end. The EXE path is
+ after that.
+
+ https://stackoverflow.com/questions/60928997/how-to-get-environment-variables-in-a-dos-assembly-program
+ */
+ __dpmi_regs regs;
+ regs.x.ax = 0x6200; /* get PSP address */
+ regs.x.bx = 0x0000;
+ __dpmi_int(0x21, ®s);
+
+ const Uint32 pspseg = (Uint32) regs.x.bx;
+ const Uint32 envsel = (Uint32) DOS_PeekUint16((pspseg << 16) | 0x2C);
+
+ int zero_count = 0;
+ int offset;
+ for (offset = 0; (offset < 0xFFFF) && (zero_count < 2); offset++) {
+ const char ch = (char) _farpeekb(envsel, offset);
+ if (ch == 0) {
+ zero_count++;
+ } else {
+ zero_count = 0;
}
}
- // drop the .exe name.
- ptr = SDL_strrchr(fullpath, '/');
- if (ptr) {
- ptr[1] = '\0';
+ if (zero_count != 2) {
+ return NULL; // uhoh
+ } else if (_farpeekw(envsel, offset) < 1) { // there's a Uint16 here that represents number of extension strings. In practice it's always 1 and that one string is the path we need.
+ return NULL; // uhoh
}
- return fullpath;
+ offset += 2;
+
+ int slen = 0;
+ for (unsigned long i = (unsigned long) offset; _farpeekb(envsel, i) != 0; i++) {
+ slen++;
+ }
+
+ slen++; /* count the null terminator. */
+
+ char *lastbackslash = NULL;
+ char *retval = (char *) SDL_malloc(slen);
+ if (retval) {
+ for (int i = 0; i < slen; i++) {
+ const char ch = (char) _farpeekb(envsel, offset + i);
+ if (ch == '\\') {
+ retval[i] = '/'; // I don't know if this is a good idea. Drop DOS path separators, use Unix style instead.
+ lastbackslash = &retval[i];
+ } else {
+ retval[i] = ch;
+ }
+ }
+ }
+
+ if (lastbackslash) {
+ lastbackslash[1] = '\0'; /* chop off exe name, just leave path */
+ } else { // uh...should have been a full path...?!
+ SDL_free(retval);
+ retval = NULL;
+ }
+
+ return retval;
}
char *SDL_SYS_GetPrefPath(const char *org, const char *app)
diff --git a/src/main/dos/SDL_sysmain_runapp.c b/src/main/dos/SDL_sysmain_runapp.c
index e41628cfce..da110b37aa 100644
--- a/src/main/dos/SDL_sysmain_runapp.c
+++ b/src/main/dos/SDL_sysmain_runapp.c
@@ -31,8 +31,6 @@
#include
int _crt0_startup_flags = _CRT0_FLAG_LOCK_MEMORY | _CRT0_FLAG_NONMOVE_SBRK;
-const char *SDL_argv0 = NULL;
-
int SDL_RunApp(int argc, char *argv[], SDL_main_func mainFunction, void *reserved)
{
(void)reserved;
@@ -43,8 +41,6 @@ int SDL_RunApp(int argc, char *argv[], SDL_main_func mainFunction, void *reserve
return 1;
}
- SDL_argv0 = argv ? argv[0] : NULL;
-
return SDL_CallMainFunction(argc, argv, mainFunction);
}