physfs_platform_dos.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. /*
  2. * MS-DOS support routines for PhysicsFS.
  3. *
  4. * Please see the file LICENSE.txt in the source's root directory.
  5. *
  6. * This file written by Ryan C. Gordon.
  7. */
  8. #define __PHYSICSFS_INTERNAL__
  9. #include "physfs_platforms.h"
  10. #ifdef PHYSFS_PLATFORM_DOS
  11. #include <dirent.h>
  12. #include <sys/types.h>
  13. #include <sys/farptr.h>
  14. #include <dpmi.h>
  15. #include <go32.h>
  16. #include "physfs_internal.h"
  17. int __PHYSFS_platformInit(const char *argv0)
  18. {
  19. return 1; /* always succeed. */
  20. } /* __PHYSFS_platformInit */
  21. void __PHYSFS_platformDeinit(void)
  22. {
  23. /* no-op */
  24. } /* __PHYSFS_platformDeinit */
  25. void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
  26. {
  27. #if (defined PHYSFS_NO_CDROM_SUPPORT)
  28. /* no-op. */
  29. #else
  30. /* MSCDEX API calls go through interrupt 0x2F with AH=0x15: https://www.phatcode.net/res/220/files/mscdex21.txt */
  31. __dpmi_regs regs;
  32. int num_drives;
  33. regs.x.ax = 0x1500; /* get number of CD drive letters */
  34. regs.x.bx = 0x0000;
  35. __dpmi_int(0x2F, &regs);
  36. num_drives = (int) regs.x.bx; /* if zero, no drives or no MSCDEX installed. */
  37. if (num_drives > 0) {
  38. char drives[26];
  39. int i;
  40. regs.x.ax = 0x150D; /* get CD drive letters */
  41. 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. */
  42. regs.x.bx = __tb & 0x0f;
  43. __dpmi_int(0x2F, &regs);
  44. dosmemget(__tb, num_drives, drives);
  45. for (i = 0; i < num_drives; i++) {
  46. const char path[] = { 'A' + drives[i], ':', '\\', '\0' };
  47. DIR *dirp = opendir(path); /* if we can open the drive's root dir, there's a filesystem there. */
  48. if (dirp) {
  49. closedir(dirp);
  50. cb(data, path);
  51. }
  52. }
  53. }
  54. #endif
  55. } /* __PHYSFS_platformDetectAvailableCDs */
  56. char *__PHYSFS_platformCalcBaseDir(const char *argv0)
  57. {
  58. /* As of MS-DOS 3.0, you can get the full path to the EXE from the very end of the
  59. environment table, which is discovered through the PSP:
  60. https://en.wikipedia.org/wiki/Program_Segment_Prefix
  61. An EXE is launched with the PSP's segment in DS, but we'll use a DOS API to obtain it
  62. later, since we don't control startup. https://stanislavs.org/helppc/int_21-62.html
  63. The table pointed to by the word at offset 0x2C in the PSP is just a collection
  64. of ASCIZ strings, with a blank string signifying the end. The EXE path is
  65. after that.
  66. https://stackoverflow.com/questions/60928997/how-to-get-environment-variables-in-a-dos-assembly-program
  67. */
  68. __dpmi_regs regs;
  69. char *lastbackslash;
  70. char *retval;
  71. unsigned long pspseg;
  72. unsigned long envsel;
  73. unsigned long i;
  74. int zero_count = 0;
  75. int slen = 0;
  76. int offset;
  77. regs.x.ax = 0x6200; /* get number of CD drive letters */
  78. regs.x.bx = 0x0000;
  79. __dpmi_int(0x21, &regs);
  80. /* https://www.delorie.com/djgpp/doc/ug/dpmi/farptr-intro.html.en */
  81. pspseg = (unsigned long) regs.x.bx;
  82. envsel = (unsigned long) _farpeekw(_dos_ds, (pspseg * 16) + 0x2C);
  83. for (offset = 0; (offset < 0xFFFF) && (zero_count < 2); offset++) {
  84. const char ch = (char) _farpeekb(envsel, offset);
  85. if (ch == 0) {
  86. zero_count++;
  87. } else {
  88. zero_count = 0;
  89. }
  90. }
  91. if (zero_count != 2) {
  92. return NULL; /* uhoh */
  93. }
  94. /* 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. */
  95. offset += 2;
  96. for (i = offset; _farpeekb(envsel, i) != 0; i++) {
  97. slen++;
  98. }
  99. slen++; /* count the null terminator. */
  100. lastbackslash = NULL;
  101. retval = (char *) allocator.Malloc(slen);
  102. if (retval) {
  103. int i = 0;
  104. do {
  105. const char ch = (char) _farpeekb(envsel, offset + i);
  106. retval[i] = ch;
  107. if (ch == '\\') {
  108. lastbackslash = &retval[i];
  109. }
  110. i++;
  111. } while (i < slen);
  112. }
  113. if (lastbackslash) {
  114. lastbackslash[1] = '\0'; /* chop off exe name, just leave path */
  115. }
  116. return retval;
  117. } /* __PHYSFS_platformCalcBaseDir */
  118. char *__PHYSFS_platformCalcUserDir(void)
  119. {
  120. return __PHYSFS_platformCalcBaseDir(NULL); /* !!! FIXME: ? */
  121. }
  122. char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
  123. {
  124. return __PHYSFS_platformCalcBaseDir(NULL); /* !!! FIXME: ? */
  125. } /* __PHYSFS_platformCalcPrefDir */
  126. void *__PHYSFS_platformGetThreadID(void)
  127. {
  128. return (void *) 0x1;
  129. } /* __PHYSFS_platformGetThreadID */
  130. void *__PHYSFS_platformCreateMutex(void)
  131. {
  132. return (void *) 0x1;
  133. } /* __PHYSFS_platformCreateMutex */
  134. void __PHYSFS_platformDestroyMutex(void *mutex)
  135. {
  136. } /* __PHYSFS_platformDestroyMutex */
  137. int __PHYSFS_platformGrabMutex(void *mutex)
  138. {
  139. return (mutex == ((void *) 0x1));
  140. } /* __PHYSFS_platformGrabMutex */
  141. void __PHYSFS_platformReleaseMutex(void *mutex)
  142. {
  143. } /* __PHYSFS_platformReleaseMutex */
  144. #endif /* PHYSFS_PLATFORM_DOS */
  145. /* end of physfs_platform_dos.c ... */