Преглед изворни кода

dos: Use INT 0x21, operation 0x62 to calculate basepath.

Previously this captured argv[0] and pushed it through searchpath(). It's
probably better to go right to the source to find this info, though.
Ryan C. Gordon пре 11 часа
родитељ
комит
6249bac891
2 измењених фајлова са 62 додато и 23 уклоњено
  1. 62 19
      src/filesystem/dos/SDL_sysfilesystem.c
  2. 0 4
      src/main/dos/SDL_sysmain_runapp.c

+ 62 - 19
src/filesystem/dos/SDL_sysfilesystem.c

@@ -22,9 +22,10 @@
 
 #ifdef SDL_FILESYSTEM_DOS
 
-#include <dir.h>
 #include <sys/stat.h>
 
+#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
+
+       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
+
+       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, &regs);
+
+    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;
+        }
     }
 
-    char *fullpath = SDL_strdup(searched);
-    if (!fullpath) {
-        return NULL;
+    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
+    }
+
+    offset += 2;
+
+    int slen = 0;
+    for (unsigned long i = (unsigned long) offset; _farpeekb(envsel, i) != 0; i++) {
+        slen++;
     }
 
-    // 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 = '/';
+    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;
+            }
         }
     }
 
-    // drop the .exe name.
-    ptr = SDL_strrchr(fullpath, '/');
-    if (ptr) {
-        ptr[1] = '\0';
+    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 fullpath;
+    return retval;
 }
 
 char *SDL_SYS_GetPrefPath(const char *org, const char *app)

+ 0 - 4
src/main/dos/SDL_sysmain_runapp.c

@@ -31,8 +31,6 @@
 #include <crt0.h>
 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);
 }