Selaa lähdekoodia

dos: Added MS-DOS support via djgpp.

Ryan C. Gordon 1 viikko sitten
vanhempi
commit
f674b2dc15
6 muutettua tiedostoa jossa 207 lisäystä ja 6 poistoa
  1. 8 1
      CMakeLists.txt
  2. 1 1
      src/physfs.h
  3. 2 2
      src/physfs_internal.h
  4. 178 0
      src/physfs_platform_dos.c
  5. 15 2
      src/physfs_platform_posix.c
  6. 3 0
      src/physfs_platforms.h

+ 8 - 1
CMakeLists.txt

@@ -85,6 +85,7 @@ set(PHYSFS_SRCS
     src/physfs_unicode.c
     src/physfs_platform_cafe.c
     src/physfs_platform_ctr.c
+    src/physfs_platform_dos.c
     src/physfs_platform_posix.c
     src/physfs_platform_unix.c
     src/physfs_platform_windows.c
@@ -218,7 +219,10 @@ if(PHYSFS_BUILD_STATIC)
     list(APPEND PHYSFS_INSTALL_TARGETS "physfs-static")
 endif()
 
-option(PHYSFS_BUILD_SHARED "Build shared library" TRUE)
+if (NOT DOS)
+    option(PHYSFS_BUILD_SHARED "Build shared library" TRUE)
+endif()
+
 if(PHYSFS_BUILD_SHARED)
     add_library(physfs-shared SHARED ${PHYSFS_SRCS})
     add_library(PhysFS::PhysFS-shared ALIAS physfs-shared)
@@ -258,6 +262,9 @@ if(PHYSFS_BUILD_TEST)
     add_executable(test_physfs test/test_physfs.c)
     target_link_libraries(test_physfs PRIVATE PhysFS::PhysFS)
     sdl_add_warning_options(test_physfs WARNING_AS_ERROR ${PHYSFS_WERROR})
+    if(DOS)
+        set_target_properties(test_physfs PROPERTIES OUTPUT_NAME "TESTPHYS")   # 8.3 file names!
+    endif()
 
     find_path(READLINE_H readline/readline.h)
     find_path(HISTORY_H readline/history.h)

+ 1 - 1
src/physfs.h

@@ -233,7 +233,7 @@ extern "C" {
 #define PHYSFS_DECL __declspec(dllexport)
 #elif defined(__SUNPRO_C)
 #define PHYSFS_DECL __global
-#elif ((__GNUC__ >= 3) && (!defined(__EMX__)) && (!defined(sun)))
+#elif ((__GNUC__ >= 3) && (!defined(__EMX__)) && (!defined(DJGPP)) && (!defined(sun)))
 #define PHYSFS_DECL __attribute__((visibility("default")))
 #else
 #define PHYSFS_DECL

+ 2 - 2
src/physfs_internal.h

@@ -69,7 +69,7 @@ extern "C" {
    All file-private symbols need to be marked "static".
    Everything shared between PhysicsFS sources needs to be in this
    file between the visibility pragma blocks. */
-#if !defined(_WIN32) && (PHYSFS_MINIMUM_GCC_VERSION(4,0) || defined(__clang__))
+#if !defined(_WIN32) && !defined(DJGPP) && (PHYSFS_MINIMUM_GCC_VERSION(4,0) || defined(__clang__))
 #define PHYSFS_HAVE_PRAGMA_VISIBILITY 1
 #endif
 
@@ -477,7 +477,7 @@ void __PHYSFS_DirTreeDeinit(__PHYSFS_DirTree *dt);
  *  Obviously, this isn't a function. If you need more than one char for this,
  *  you'll need to pull some old pieces of PhysicsFS out of revision control.
  */
-#if defined(PHYSFS_PLATFORM_WINDOWS) || defined(PHYSFS_PLATFORM_OS2)
+#if defined(PHYSFS_PLATFORM_DOS) || defined(PHYSFS_PLATFORM_WINDOWS) || defined(PHYSFS_PLATFORM_OS2)
 #define __PHYSFS_platformDirSeparator '\\'
 #else
 #define __PHYSFS_STANDARD_DIRSEP 1

+ 178 - 0
src/physfs_platform_dos.c

@@ -0,0 +1,178 @@
+/*
+ * MS-DOS support routines for PhysicsFS.
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ *  This file written by Ryan C. Gordon.
+ */
+
+#define __PHYSICSFS_INTERNAL__
+#include "physfs_platforms.h"
+
+#ifdef PHYSFS_PLATFORM_DOS
+
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/farptr.h>
+#include <dpmi.h>
+#include <go32.h>
+
+#include "physfs_internal.h"
+
+int __PHYSFS_platformInit(const char *argv0)
+{
+    return 1;  /* always succeed. */
+} /* __PHYSFS_platformInit */
+
+
+void __PHYSFS_platformDeinit(void)
+{
+    /* no-op */
+} /* __PHYSFS_platformDeinit */
+
+
+void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
+{
+#if (defined PHYSFS_NO_CDROM_SUPPORT)
+    /* no-op. */
+#else
+    /* MSCDEX API calls go through interrupt 0x2F with AH=0x15: https://www.phatcode.net/res/220/files/mscdex21.txt */
+    __dpmi_regs regs;
+    regs.x.ax = 0x1500;  /* get number of CD drive letters */
+    regs.x.bx = 0x0000;
+    __dpmi_int(0x2F, &regs);
+
+    const int num_drives = (int) regs.x.bx;  /* if zero, no drives or no MSCDEX installed. */
+    if (num_drives > 0) {
+        regs.x.ax = 0x150D;  /* get CD drive letters */
+        regs.x.es = __tb >> 4;     /* the transfer buffer is at least 2k large, if all possible drives were CD-ROMs, we'd need 26 bytes. */
+        regs.x.bx = __tb & 0x0f;
+        __dpmi_int(0x2F, &regs);
+        char drives[26];
+        dosmemget(__tb, num_drives, drives);
+        for (int i = 0; i < num_drives; i++) {
+            const char path[] = { 'A' + drives[i], ':', '\\', '\0' };
+            DIR *dirp = opendir(path);  /* if we can open the drive's root dir, there's a filesystem there. */
+            if (dirp) {
+                closedir(dirp);
+                cb(data, path);
+            }
+        }
+    }
+#endif
+} /* __PHYSFS_platformDetectAvailableCDs */
+
+
+char *__PHYSFS_platformCalcBaseDir(const char *argv0)
+{
+    /* 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, since 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 number of CD drive letters */
+    regs.x.bx = 0x0000;
+    __dpmi_int(0x21, &regs);
+
+    /* https://www.delorie.com/djgpp/doc/ug/dpmi/farptr-intro.html.en */
+    const unsigned long pspseg = (unsigned long) regs.x.bx;
+    const unsigned long envsel = (unsigned long) _farpeekw(_dos_ds, (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;
+        }
+    }
+
+    if (zero_count != 2) {
+        return NULL;  /* uhoh */
+    }
+
+    /* 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. */
+    offset += 2;
+
+    int slen = 0;
+    for (unsigned long i = offset; _farpeekb(envsel, i) != 0; i++) {
+        slen++;
+    }
+
+    slen++;  /* count the null terminator. */
+
+    char *lastbackslash = NULL;
+    char *retval = (char *) allocator.Malloc(slen);
+    if (retval) {
+        int i = 0;
+        do {
+            const char ch = (char) _farpeekb(envsel, offset + i);
+            retval[i] = ch;
+            if (ch == '\\') {
+                lastbackslash = &retval[i];
+            }
+            i++;
+        } while (i < slen);
+    }
+
+    if (lastbackslash) {
+        lastbackslash[1] = '\0';  /* chop off exe name, just leave path */
+    }
+
+    return retval;
+} /* __PHYSFS_platformCalcBaseDir */
+
+
+char *__PHYSFS_platformCalcUserDir(void)
+{
+    return __PHYSFS_platformCalcBaseDir(NULL);  /* !!! FIXME: ? */
+}
+
+char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
+{
+    return __PHYSFS_platformCalcBaseDir(NULL);  /* !!! FIXME: ? */
+} /* __PHYSFS_platformCalcPrefDir */
+
+
+void *__PHYSFS_platformGetThreadID(void)
+{
+    return (void *) 0x1;
+} /* __PHYSFS_platformGetThreadID */
+
+
+void *__PHYSFS_platformCreateMutex(void)
+{
+    return (void *) 0x1;
+} /* __PHYSFS_platformCreateMutex */
+
+
+void __PHYSFS_platformDestroyMutex(void *mutex)
+{
+} /* __PHYSFS_platformDestroyMutex */
+
+
+int __PHYSFS_platformGrabMutex(void *mutex)
+{
+    return (mutex == ((void *) 0x1));
+} /* __PHYSFS_platformGrabMutex */
+
+
+void __PHYSFS_platformReleaseMutex(void *mutex)
+{
+} /* __PHYSFS_platformReleaseMutex */
+
+#endif /* PHYSFS_PLATFORM_DOS */
+
+/* end of physfs_platform_dos.c ... */
+

+ 15 - 2
src/physfs_platform_posix.c

@@ -19,7 +19,10 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+
+#ifndef PHYSFS_PLATFORM_DOS
 #include <pthread.h>
+#endif
 
 #include "physfs_internal.h"
 
@@ -31,7 +34,6 @@ static PHYSFS_ErrorCode errcodeFromErrnoError(const int err)
         case 0: return PHYSFS_ERR_OK;
         case EACCES: return PHYSFS_ERR_PERMISSION;
         case EPERM: return PHYSFS_ERR_PERMISSION;
-        case EDQUOT: return PHYSFS_ERR_NO_SPACE;
         case EIO: return PHYSFS_ERR_IO;
         case ELOOP: return PHYSFS_ERR_SYMLINK_LOOP;
         case EMLINK: return PHYSFS_ERR_NO_SPACE;
@@ -41,10 +43,13 @@ static PHYSFS_ErrorCode errcodeFromErrnoError(const int err)
         case ENOTDIR: return PHYSFS_ERR_NOT_FOUND;
         case EISDIR: return PHYSFS_ERR_NOT_A_FILE;
         case EROFS: return PHYSFS_ERR_READ_ONLY;
-        case ETXTBSY: return PHYSFS_ERR_BUSY;
         case EBUSY: return PHYSFS_ERR_BUSY;
         case ENOMEM: return PHYSFS_ERR_OUT_OF_MEMORY;
         case ENOTEMPTY: return PHYSFS_ERR_DIR_NOT_EMPTY;
+        #ifndef PHYSFS_PLATFORM_DOS  /* djgpp doesn't have these. */
+        case EDQUOT: return PHYSFS_ERR_NO_SPACE;
+        case ETXTBSY: return PHYSFS_ERR_BUSY;
+        #endif
         default: return PHYSFS_ERR_OS_ERROR;
     } /* switch */
 } /* errcodeFromErrnoError */
@@ -56,6 +61,7 @@ static inline PHYSFS_ErrorCode errcodeFromErrno(void)
 } /* errcodeFromErrno */
 
 
+#ifndef PHYSFS_PLATFORM_DOS
 static char *getUserDirByUID(void)
 {
     uid_t uid = getuid();
@@ -114,6 +120,7 @@ char *__PHYSFS_platformCalcUserDir(void)
 
     return retval;
 } /* __PHYSFS_platformCalcUserDir */
+#endif
 
 
 PHYSFS_EnumerateCallbackResult __PHYSFS_platformEnumerate(const char *dirname,
@@ -176,6 +183,10 @@ static void *doOpen(const char *filename, int mode)
     /* O_APPEND doesn't actually behave as we'd like. */
     mode &= ~O_APPEND;
 
+#ifdef PHYSFS_PLATFORM_DOS
+    mode |= O_BINARY;
+#endif
+
 #ifdef O_CLOEXEC
     /* Add O_CLOEXEC if defined */
     mode |= O_CLOEXEC;
@@ -367,6 +378,7 @@ int __PHYSFS_platformStat(const char *fname, PHYSFS_Stat *st, const int follow)
 } /* __PHYSFS_platformStat */
 
 
+#ifndef PHYSFS_PLATFORM_DOS
 typedef struct
 {
     pthread_mutex_t mutex;
@@ -442,6 +454,7 @@ void __PHYSFS_platformReleaseMutex(void *mutex)
         } /* if */
     } /* if */
 } /* __PHYSFS_platformReleaseMutex */
+#endif  /* !PHYSFS_PLATFORM_DOS */
 
 #endif  /* PHYSFS_PLATFORM_POSIX */
 

+ 3 - 0
src/physfs_platforms.h

@@ -15,6 +15,9 @@
 #if defined(TARGET_EXTENSION) && (defined(TARGET_PLAYDATE) || defined(TARGET_SIMULATOR))
 #  define PHYSFS_PLATFORM_PLAYDATE 1
 #  define PHYSFS_NO_CRUNTIME_MALLOC 1
+#elif (defined DJGPP)
+#  define PHYSFS_PLATFORM_DOS 1
+#  define PHYSFS_PLATFORM_POSIX 1  /* close enough with djgpp */
 #elif (defined __LIBRETRO__)
 #  define PHYSFS_PLATFORM_LIBRETRO 1
 #  define PHYSFS_NO_CDROM_SUPPORT 1