Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * OS/2 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.  
  9. #define __PHYSICSFS_INTERNAL__
  10. #include "physfs_platforms.h"
  11.  
  12. #ifdef PHYSFS_PLATFORM_OS2
  13.  
  14. #define INCL_DOSMODULEMGR
  15. #define INCL_DOSSEMAPHORES
  16. #define INCL_DOSDATETIME
  17. #define INCL_DOSFILEMGR
  18. #define INCL_DOSMODULEMGR
  19. #define INCL_DOSERRORS
  20. #define INCL_DOSPROCESS
  21. #define INCL_DOSDEVICES
  22. #define INCL_DOSDEVIOCTL
  23. #define INCL_DOSMISC
  24. #include <os2.h>
  25. #include <uconv.h>
  26.  
  27. #include <errno.h>
  28. #include <time.h>
  29. #include <ctype.h>
  30.  
  31. #include "physfs_internal.h"
  32.  
  33. static HMODULE uconvdll = 0;
  34. static UconvObject uconv = 0;
  35. static int (_System *pUniCreateUconvObject)(UniChar *, UconvObject *) = NULL;
  36. static int (_System *pUniFreeUconvObject)(UconvObject *) = NULL;
  37. static int (_System *pUniUconvToUcs)(UconvObject,void **,size_t *, UniChar**, size_t *, size_t *) = NULL;
  38. static int (_System *pUniUconvFromUcs)(UconvObject,UniChar **,size_t *,void **,size_t *,size_t *) = NULL;
  39.  
  40. static PHYSFS_ErrorCode errcodeFromAPIRET(const APIRET rc)
  41. {
  42.     switch (rc)
  43.     {
  44.         case NO_ERROR: return PHYSFS_ERR_OK;  /* not an error. */
  45.         case ERROR_INTERRUPT: return PHYSFS_ERR_OK;  /* not an error. */
  46.         case ERROR_TIMEOUT: return PHYSFS_ERR_OK;  /* not an error. */
  47.         case ERROR_NOT_ENOUGH_MEMORY: return PHYSFS_ERR_OUT_OF_MEMORY;
  48.         case ERROR_FILE_NOT_FOUND: return PHYSFS_ERR_NOT_FOUND;
  49.         case ERROR_PATH_NOT_FOUND: return PHYSFS_ERR_NOT_FOUND;
  50.         case ERROR_ACCESS_DENIED: return PHYSFS_ERR_PERMISSION;
  51.         case ERROR_NOT_DOS_DISK: return PHYSFS_ERR_NOT_FOUND;
  52.         case ERROR_SHARING_VIOLATION: return PHYSFS_ERR_PERMISSION;
  53.         case ERROR_CANNOT_MAKE: return PHYSFS_ERR_IO;  /* maybe this is wrong? */
  54.         case ERROR_DEVICE_IN_USE: return PHYSFS_ERR_BUSY;
  55.         case ERROR_OPEN_FAILED: return PHYSFS_ERR_IO;  /* maybe this is wrong? */
  56.         case ERROR_DISK_FULL: return PHYSFS_ERR_NO_SPACE;
  57.         case ERROR_PIPE_BUSY: return PHYSFS_ERR_BUSY;
  58.         case ERROR_SHARING_BUFFER_EXCEEDED: return PHYSFS_ERR_IO;
  59.         case ERROR_FILENAME_EXCED_RANGE: return PHYSFS_ERR_BAD_FILENAME;
  60.         case ERROR_META_EXPANSION_TOO_LONG: return PHYSFS_ERR_BAD_FILENAME;
  61.         case ERROR_TOO_MANY_HANDLES: return PHYSFS_ERR_IO;
  62.         case ERROR_TOO_MANY_OPEN_FILES: return PHYSFS_ERR_IO;
  63.         case ERROR_NO_MORE_SEARCH_HANDLES: return PHYSFS_ERR_IO;
  64.         case ERROR_SEEK_ON_DEVICE: return PHYSFS_ERR_IO;
  65.         case ERROR_NEGATIVE_SEEK: return PHYSFS_ERR_INVALID_ARGUMENT;
  66.         case ERROR_WRITE_PROTECT: return PHYSFS_ERR_PERMISSION;
  67.         case ERROR_WRITE_FAULT: return PHYSFS_ERR_IO;
  68.         case ERROR_UNCERTAIN_MEDIA: return PHYSFS_ERR_IO;
  69.         case ERROR_PROTECTION_VIOLATION: return PHYSFS_ERR_IO;
  70.         case ERROR_BROKEN_PIPE: return PHYSFS_ERR_IO;
  71.  
  72.         /* !!! FIXME: some of these might be PHYSFS_ERR_BAD_FILENAME, etc */
  73.         case ERROR_LOCK_VIOLATION:
  74.         case ERROR_GEN_FAILURE:
  75.         case ERROR_INVALID_PARAMETER:
  76.         case ERROR_INVALID_NAME:
  77.         case ERROR_INVALID_DRIVE:
  78.         case ERROR_INVALID_HANDLE:
  79.         case ERROR_INVALID_FUNCTION:
  80.         case ERROR_INVALID_LEVEL:
  81.         case ERROR_INVALID_CATEGORY:
  82.         case ERROR_DUPLICATE_NAME:
  83.         case ERROR_BUFFER_OVERFLOW:
  84.         case ERROR_BAD_LENGTH:
  85.         case ERROR_BAD_DRIVER_LEVEL:
  86.         case ERROR_DIRECT_ACCESS_HANDLE:
  87.         case ERROR_NOT_OWNER:
  88.             return PHYSFS_ERR_OS_ERROR;
  89.  
  90.         default: break;
  91.     } /* switch */
  92.  
  93.     return PHYSFS_ERR_OTHER_ERROR;
  94. } /* errcodeFromAPIRET */
  95.  
  96. static char *cvtUtf8ToCodepage(const char *utf8str)
  97. {
  98.     const size_t len = strlen(utf8str) + 1;
  99.     const size_t uc2buflen = len * sizeof (UniChar);
  100.     UniChar *uc2ptr = (UniChar *) __PHYSFS_smallAlloc(uc2buflen);
  101.     UniChar *uc2str = uc2ptr;
  102.     char *cpptr = NULL;
  103.     char *cpstr = NULL;
  104.     size_t subs = 0;
  105.     size_t unilen;
  106.  
  107.     BAIL_IF(!uc2str, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  108.     PHYSFS_utf8ToUcs2(utf8str, (PHYSFS_uint16 *) uc2str, uc2buflen);
  109.     for (unilen = 0; uc2str[unilen]; unilen++) { /* spin */ }
  110.     unilen++;  /* null terminator. */
  111.  
  112.     if (!uconvdll)
  113.     {
  114.         /* There's really not much we can do on older OS/2s except pray this
  115.            is latin1-compatible. */
  116.         size_t i;
  117.         cpptr = (char *) allocator.Malloc(unilen);
  118.         cpstr = cpptr;
  119.         GOTO_IF(!cpptr, PHYSFS_ERR_OUT_OF_MEMORY, failed);
  120.         for (i = 0; i < unilen; i++)
  121.         {
  122.             const UniChar ch = uc2str[i];
  123.             GOTO_IF(ch > 0xFF, PHYSFS_ERR_BAD_FILENAME, failed);
  124.             cpptr[i] = (char) ((unsigned char) ch);
  125.         } /* for */
  126.  
  127.         __PHYSFS_smallFree(uc2ptr);
  128.         return cpstr;
  129.     } /* if */
  130.     else
  131.     {
  132.         int rc;
  133.         size_t cplen = unilen * 4; /* overallocate, just in case. */
  134.         cpptr = (char *) allocator.Malloc(cplen);
  135.         GOTO_IF(!cpptr, PHYSFS_ERR_OUT_OF_MEMORY, failed);
  136.         cpstr = cpptr;
  137.  
  138.         rc = pUniUconvFromUcs(uconv, &uc2str, &unilen, (void **) &cpstr, &cplen, &subs);
  139.         GOTO_IF(rc != ULS_SUCCESS, PHYSFS_ERR_BAD_FILENAME, failed);
  140.         GOTO_IF(subs > 0, PHYSFS_ERR_BAD_FILENAME, failed);
  141.         assert(unilen == 0);
  142.  
  143.         __PHYSFS_smallFree(uc2ptr);
  144.         return cpptr;
  145.     } /* else */
  146.  
  147. failed:
  148.     __PHYSFS_smallFree(uc2ptr);
  149.     allocator.Free(cpptr);
  150.  
  151.     return NULL;
  152. } /* cvtUtf8ToCodepage */
  153.  
  154. static char *cvtCodepageToUtf8(const char *cpstr)
  155. {
  156.     const size_t len = strlen(cpstr) + 1;
  157.     char *retvalbuf = (char *) allocator.Malloc(len * 4);
  158.     char *retval = NULL;
  159.  
  160.     BAIL_IF(!retvalbuf, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  161.  
  162.     if (!uconvdll)
  163.     {
  164.         /* There's really not much we can do on older OS/2s except pray this
  165.            is latin1-compatible. */
  166.         retval = retvalbuf;
  167.         PHYSFS_utf8FromLatin1(cpstr, retval, len * 4);
  168.     } /* if */
  169.     else
  170.     {
  171.         int rc;
  172.         size_t cplen = len;
  173.         size_t unilen = len;
  174.         size_t subs = 0;
  175.         UniChar *uc2ptr = __PHYSFS_smallAlloc(len * sizeof (UniChar));
  176.         UniChar *uc2str = uc2ptr;
  177.  
  178.         BAIL_IF(!uc2ptr, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  179.         rc = pUniUconvToUcs(uconv, (void **) &cpstr, &cplen, &uc2str, &unilen, &subs);
  180.         GOTO_IF(rc != ULS_SUCCESS, PHYSFS_ERR_BAD_FILENAME, done);
  181.         GOTO_IF(subs > 0, PHYSFS_ERR_BAD_FILENAME, done);
  182.         assert(cplen == 0);
  183.         retval = retvalbuf;
  184.         PHYSFS_utf8FromUcs2((const PHYSFS_uint16 *) uc2ptr, retval, len * 4);
  185.         done:
  186.         __PHYSFS_smallFree(uc2ptr);
  187.     } /* else */
  188.  
  189.     return retval;
  190. } /* cvtCodepageToUtf8 */
  191.  
  192.  
  193. /* (be gentle, this function isn't very robust.) */
  194. static char *cvtPathToCorrectCase(char *buf)
  195. {
  196.     char *retval = buf;
  197.     char *fname = buf + 3;            /* point to first element. */
  198.     char *ptr = strchr(fname, '\\');  /* find end of first element. */
  199.  
  200.     buf[0] = toupper(buf[0]);  /* capitalize drive letter. */
  201.  
  202.     /*
  203.      * Go through each path element, and enumerate its parent dir until
  204.      *  a case-insensitive match is found. If one is (and it SHOULD be)
  205.      *  then overwrite the original element with the correct case.
  206.      * If there's an error, or the path has vanished for some reason, it
  207.      *  won't hurt to have the original case, so we just keep going.
  208.      */
  209.     while ((fname != NULL) && (*fname != '\0'))
  210.     {
  211.         char spec[CCHMAXPATH];
  212.         FILEFINDBUF3 fb;
  213.         HDIR hdir = HDIR_CREATE;
  214.         ULONG count = 1;
  215.         APIRET rc;
  216.  
  217.         *(fname - 1) = '\0';  /* isolate parent dir string. */
  218.  
  219.         strcpy(spec, buf);      /* copy isolated parent dir... */
  220.         strcat(spec, "\\*.*");  /*  ...and add wildcard search spec. */
  221.  
  222.         if (ptr != NULL)  /* isolate element to find (fname is the start). */
  223.             *ptr = '\0';
  224.  
  225.         rc = DosFindFirst((unsigned char *) spec, &hdir, FILE_DIRECTORY,
  226.                           &fb, sizeof (fb), &count, FIL_STANDARD);
  227.         if (rc == NO_ERROR)
  228.         {
  229.             while (count == 1)  /* while still entries to enumerate... */
  230.             {
  231.                 int cmp;
  232.                 char *utf8 = cvtCodepageToUtf8(fb.achName);
  233.                 if (!utf8) /* ugh, maybe we'll get lucky with the C runtime. */
  234.                     cmp = stricmp(fb.achName, fname);
  235.                 else
  236.                 {
  237.                     cmp = PHYSFS_utf8stricmp(utf8, fname);
  238.                     allocator.Free(utf8);
  239.                 } /* else */
  240.  
  241.                 if (cmp == 0)
  242.                 {
  243.                     strcpy(fname, fb.achName);
  244.                     break;  /* there it is. Overwrite and stop searching. */
  245.                 } /* if */
  246.  
  247.                 DosFindNext(hdir, &fb, sizeof (fb), &count);
  248.             } /* while */
  249.             DosFindClose(hdir);
  250.         } /* if */
  251.  
  252.         *(fname - 1) = '\\';   /* unisolate parent dir. */
  253.         fname = ptr;           /* point to next element. */
  254.         if (ptr != NULL)
  255.         {
  256.             *ptr = '\\';       /* unisolate element. */
  257.             ptr = strchr(++fname, '\\');  /* find next element. */
  258.         } /* if */
  259.     } /* while */
  260.  
  261.     return retval;
  262. } /* cvtPathToCorrectCase */
  263.  
  264. static void prepUnicodeSupport(void)
  265. {
  266.     /* really old OS/2 might not have Unicode support _at all_, so load
  267.        the system library and do without if it doesn't exist. */
  268.     int ok = 0;                                                  
  269.     char buf[CCHMAXPATH];
  270.     UniChar defstr[] = { 0 };
  271.     if (DosLoadModule(buf, sizeof (buf) - 1, "uconv", &uconvdll) == NO_ERROR)
  272.     {
  273.         #define LOAD(x) (DosQueryProcAddr(uconvdll,0,#x,(PFN*)&p##x)==NO_ERROR)
  274.         ok = LOAD(UniCreateUconvObject) &&
  275.              LOAD(UniFreeUconvObject) &&
  276.              LOAD(UniUconvToUcs) &&
  277.              LOAD(UniUconvFromUcs);
  278.         #undef LOAD
  279.     } /* else */
  280.  
  281.     if (!ok || (pUniCreateUconvObject(defstr, &uconv) != ULS_SUCCESS))
  282.     {
  283.         /* oh well, live without it. */
  284.         if (uconvdll)
  285.         {
  286.             if (uconv)
  287.                 pUniFreeUconvObject(uconv);
  288.             DosFreeModule(uconvdll);
  289.             uconvdll = 0;
  290.         } /* if */
  291.     } /* if */
  292. } /* prepUnicodeSupport */
  293.  
  294.  
  295. int __PHYSFS_platformInit(void)
  296. {
  297.     prepUnicodeSupport();
  298.     return 1;  /* ready to go! */
  299. } /* __PHYSFS_platformInit */
  300.  
  301.  
  302. void __PHYSFS_platformDeinit(void)
  303. {
  304.     if (uconvdll)
  305.     {
  306.         pUniFreeUconvObject(uconv);
  307.         uconv = 0;
  308.         DosFreeModule(uconvdll);
  309.         uconvdll = 0;
  310.     } /* if */
  311. } /* __PHYSFS_platformDeinit */
  312.  
  313.  
  314. static int discIsInserted(ULONG drive)
  315. {
  316.     int rc;
  317.     char buf[20];
  318.     DosError(FERR_DISABLEHARDERR | FERR_DISABLEEXCEPTION);
  319.     rc = DosQueryFSInfo(drive + 1, FSIL_VOLSER, buf, sizeof (buf));
  320.     DosError(FERR_ENABLEHARDERR | FERR_ENABLEEXCEPTION);
  321.     return (rc == NO_ERROR);
  322. } /* is_cdrom_inserted */
  323.  
  324.  
  325. /* looks like "CD01" in ASCII (littleendian)...used for an ioctl. */
  326. #define CD01 0x31304443
  327.  
  328. static int isCdRomDrive(ULONG drive)
  329. {
  330.     PHYSFS_uint32 param, data;
  331.     ULONG ul1, ul2;
  332.     APIRET rc;
  333.     HFILE hfile = NULLHANDLE;
  334.     unsigned char drivename[3] = { 0, ':', '\0' };
  335.  
  336.     drivename[0] = 'A' + drive;
  337.  
  338.     rc = DosOpen(drivename, &hfile, &ul1, 0, 0,
  339.                  OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
  340.                  OPEN_FLAGS_DASD | OPEN_FLAGS_FAIL_ON_ERROR |
  341.                  OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYNONE, NULL);
  342.     if (rc != NO_ERROR)
  343.         return 0;
  344.  
  345.     data = 0;
  346.     param = PHYSFS_swapULE32(CD01);
  347.     ul1 = ul2 = sizeof (PHYSFS_uint32);
  348.     rc = DosDevIOCtl(hfile, IOCTL_CDROMDISK, CDROMDISK_GETDRIVER,
  349.                      &param, sizeof (param), &ul1, &data, sizeof (data), &ul2);
  350.  
  351.     DosClose(hfile);
  352.     return ((rc == NO_ERROR) && (PHYSFS_swapULE32(data) == CD01));
  353. } /* isCdRomDrive */
  354.  
  355.  
  356. void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
  357. {
  358.     ULONG dummy = 0;
  359.     ULONG drivemap = 0;
  360.     ULONG i, bit;
  361.     const APIRET rc = DosQueryCurrentDisk(&dummy, &drivemap);
  362.     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc),);
  363.  
  364.     for (i = 0, bit = 1; i < 26; i++, bit <<= 1)
  365.     {
  366.         if (drivemap & bit)  /* this logical drive exists. */
  367.         {
  368.             if ((isCdRomDrive(i)) && (discIsInserted(i)))
  369.             {
  370.                 char drive[4] = "x:\\";
  371.                 drive[0] = ('A' + i);
  372.                 cb(data, drive);
  373.             } /* if */
  374.         } /* if */
  375.     } /* for */
  376. } /* __PHYSFS_platformDetectAvailableCDs */
  377.  
  378.  
  379. char *__PHYSFS_platformCalcBaseDir(const char *argv0)
  380. {
  381.     char *retval = NULL;
  382.     char buf[CCHMAXPATH];
  383.     APIRET rc;
  384.     PTIB ptib;
  385.     PPIB ppib;
  386.     PHYSFS_sint32 len;
  387.  
  388.     rc = DosGetInfoBlocks(&ptib, &ppib);
  389.     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
  390.     rc = DosQueryModuleName(ppib->pib_hmte, sizeof (buf), (PCHAR) buf);
  391.     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
  392.     retval = cvtCodepageToUtf8(buf);
  393.     BAIL_IF_ERRPASS(!retval, NULL);
  394.  
  395.     /* chop off filename, leave path. */
  396.     for (len = strlen(retval) - 1; len >= 0; len--)
  397.     {
  398.         if (retval[len] == '\\')
  399.         {
  400.             retval[len + 1] = '\0';
  401.             break;
  402.         } /* if */
  403.     } /* for */
  404.  
  405.     assert(len > 0);  /* should have been a "x:\\" on the front on string. */
  406.  
  407.     /* The string is capitalized! Figure out the REAL case... */
  408.     return cvtPathToCorrectCase(retval);
  409. } /* __PHYSFS_platformCalcBaseDir */
  410.  
  411. char *__PHYSFS_platformCalcUserDir(void)
  412. {
  413.     return __PHYSFS_platformCalcBaseDir(NULL);  /* !!! FIXME: ? */
  414. } /* __PHYSFS_platformCalcUserDir */
  415.  
  416. char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
  417. {
  418.     return __PHYSFS_platformCalcBaseDir(NULL);  /* !!! FIXME: ? */
  419. } /* __PHYSFS_platformCalcPrefDir */
  420.  
  421. PHYSFS_EnumerateCallbackResult __PHYSFS_platformEnumerate(const char *dirname,
  422.                                PHYSFS_EnumerateCallback callback,
  423.                                const char *origdir, void *callbackdata)
  424. {                                        
  425.     PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK;
  426.     size_t utf8len = strlen(dirname);
  427.     char *utf8 = (char *) __PHYSFS_smallAlloc(utf8len + 5);
  428.     char *cpspec = NULL;
  429.     FILEFINDBUF3 fb;
  430.     HDIR hdir = HDIR_CREATE;
  431.     ULONG count = 1;
  432.     APIRET rc;
  433.  
  434.     BAIL_IF(!utf8, PHYSFS_ERR_OUT_OF_MEMORY, PHYSFS_ENUM_ERROR);
  435.  
  436.     strcpy(utf8, dirname);
  437.     if (utf8[utf8len - 1] != '\\')
  438.         strcpy(utf8 + utf8len, "\\*.*");
  439.     else
  440.         strcpy(utf8 + utf8len, "*.*");
  441.  
  442.     cpspec = cvtUtf8ToCodepage(utf8);
  443.     __PHYSFS_smallFree(utf8);
  444.     BAIL_IF_ERRPASS(!cpspec, PHYSFS_ENUM_ERROR);
  445.  
  446.     rc = DosFindFirst((unsigned char *) cpspec, &hdir,
  447.                       FILE_DIRECTORY | FILE_ARCHIVED |
  448.                       FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM,
  449.                       &fb, sizeof (fb), &count, FIL_STANDARD);
  450.     allocator.Free(cpspec);
  451.  
  452.     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), PHYSFS_ENUM_ERROR);
  453.  
  454.     while (count == 1)
  455.     {
  456.         if ((strcmp(fb.achName, ".") != 0) && (strcmp(fb.achName, "..") != 0))
  457.         {
  458.             utf8 = cvtCodepageToUtf8(fb.achName);
  459.             if (!utf8)
  460.                 retval = PHYSFS_ENUM_ERROR;
  461.             else
  462.             {
  463.                 retval = callback(callbackdata, origdir, utf8);
  464.                 allocator.Free(utf8);
  465.                 if (retval == PHYSFS_ENUM_ERROR)
  466.                     PHYSFS_setErrorCode(PHYSFS_ERR_APP_CALLBACK);
  467.             } /* else */
  468.         } /* if */
  469.  
  470.         if (retval != PHYSFS_ENUM_OK)
  471.             break;
  472.  
  473.         DosFindNext(hdir, &fb, sizeof (fb), &count);
  474.     } /* while */
  475.  
  476.     DosFindClose(hdir);
  477.  
  478.     return retval;
  479. } /* __PHYSFS_platformEnumerate */
  480.  
  481.  
  482. char *__PHYSFS_platformCurrentDir(void)
  483. {
  484.     char *retval;
  485.     char *cpstr;
  486.     char *utf8;
  487.     ULONG currentDisk;
  488.     ULONG dummy;
  489.     ULONG pathSize = 0;
  490.     APIRET rc;
  491.     BYTE byte;
  492.  
  493.     rc = DosQueryCurrentDisk(&currentDisk, &dummy);
  494.     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), NULL);
  495.  
  496.     /* The first call just tells us how much space we need for the string. */
  497.     rc = DosQueryCurrentDir(currentDisk, &byte, &pathSize);
  498.     pathSize++; /* Add space for null terminator. */
  499.     cpstr = (char *) __PHYSFS_smallAlloc(pathSize);
  500.     BAIL_IF(cpstr == NULL, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  501.  
  502.     /* Actually get the string this time. */
  503.     rc = DosQueryCurrentDir(currentDisk, (PBYTE) cpstr, &pathSize);
  504.     if (rc != NO_ERROR)
  505.     {
  506.         __PHYSFS_smallFree(cpstr);
  507.         BAIL(errcodeFromAPIRET(rc), NULL);
  508.     } /* if */
  509.  
  510.     utf8 = cvtCodepageToUtf8(cpstr);
  511.     __PHYSFS_smallFree(cpstr);
  512.     BAIL_IF_ERRPASS(utf8 == NULL, NULL);
  513.  
  514.     /* +4 for "x:\\" drive selector and null terminator. */
  515.     retval = (char *) allocator.Malloc(strlen(utf8) + 4);
  516.     if (retval == NULL)
  517.     {
  518.         allocator.Free(utf8);
  519.         BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  520.     } /* if */
  521.  
  522.     retval[0] = ('A' + (currentDisk - 1));
  523.     retval[1] = ':';
  524.     retval[2] = '\\';
  525.     strcpy(retval + 3, utf8);
  526.  
  527.     allocator.Free(utf8);
  528.  
  529.     return retval;
  530. } /* __PHYSFS_platformCurrentDir */
  531.  
  532.  
  533. int __PHYSFS_platformMkDir(const char *filename)
  534. {
  535.     APIRET rc;
  536.     char *cpstr = cvtUtf8ToCodepage(filename);
  537.     BAIL_IF_ERRPASS(!cpstr, 0);
  538.     rc = DosCreateDir((unsigned char *) cpstr, NULL);
  539.     allocator.Free(cpstr);
  540.     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
  541.     return 1;
  542. } /* __PHYSFS_platformMkDir */
  543.  
  544.  
  545. static HFILE openFile(const char *filename, const ULONG flags, const ULONG mode)
  546. {
  547.     char *cpfname = cvtUtf8ToCodepage(filename);
  548.     ULONG action = 0;
  549.     HFILE hfile = NULLHANDLE;
  550.     APIRET rc;
  551.  
  552.     BAIL_IF_ERRPASS(!cpfname, 0);
  553.  
  554.     rc = DosOpen(cpfname, &hfile, &action, 0, FILE_NORMAL, flags, mode, NULL);
  555.     allocator.Free(cpfname);
  556.     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
  557.  
  558.     return hfile;
  559. } /* openFile */
  560.  
  561. void *__PHYSFS_platformOpenRead(const char *filename)
  562. {
  563.     /*
  564.      * File must be opened SHARE_DENYWRITE and ACCESS_READONLY, otherwise
  565.      *  DosQueryFileInfo() will fail if we try to get a file length, etc.
  566.      */
  567.     return (void *) openFile(filename,
  568.                         OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
  569.                         OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY |
  570.                         OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE |
  571.                         OPEN_ACCESS_READONLY);
  572. } /* __PHYSFS_platformOpenRead */
  573.  
  574.  
  575. void *__PHYSFS_platformOpenWrite(const char *filename)
  576. {
  577.     return (void *) openFile(filename,
  578.                         OPEN_ACTION_REPLACE_IF_EXISTS |
  579.                         OPEN_ACTION_CREATE_IF_NEW,
  580.                         OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY |
  581.                         OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE);
  582. } /* __PHYSFS_platformOpenWrite */
  583.  
  584.  
  585. void *__PHYSFS_platformOpenAppend(const char *filename)
  586. {
  587.     APIRET rc;
  588.     ULONG dummy = 0;
  589.     HFILE hfile;
  590.  
  591.     /*
  592.      * File must be opened SHARE_DENYWRITE and ACCESS_READWRITE, otherwise
  593.      *  DosQueryFileInfo() will fail if we try to get a file length, etc.
  594.      */
  595.     hfile = openFile(filename,
  596.                         OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW,
  597.                         OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY |
  598.                         OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE |
  599.                         OPEN_ACCESS_READWRITE);
  600.     BAIL_IF_ERRPASS(!hfile, NULL);
  601.  
  602.     rc = DosSetFilePtr(hfile, 0, FILE_END, &dummy);
  603.     if (rc != NO_ERROR)
  604.     {
  605.         DosClose(hfile);
  606.         BAIL(errcodeFromAPIRET(rc), NULL);
  607.     } /* if */
  608.  
  609.     return ((void *) hfile);
  610. } /* __PHYSFS_platformOpenAppend */
  611.  
  612.  
  613. PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buf, PHYSFS_uint64 len)
  614. {
  615.     ULONG br = 0;
  616.     APIRET rc;
  617.     BAIL_IF(!__PHYSFS_ui64FitsAddressSpace(len),PHYSFS_ERR_INVALID_ARGUMENT,-1);
  618.     rc = DosRead((HFILE) opaque, buf, (ULONG) len, &br);
  619.     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), (br > 0) ? ((PHYSFS_sint64) br) : -1);
  620.     return (PHYSFS_sint64) br;
  621. } /* __PHYSFS_platformRead */
  622.  
  623.  
  624. PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buf,
  625.                                      PHYSFS_uint64 len)
  626. {
  627.     ULONG bw = 0;
  628.     APIRET rc;
  629.     BAIL_IF(!__PHYSFS_ui64FitsAddressSpace(len),PHYSFS_ERR_INVALID_ARGUMENT,-1);
  630.     rc = DosWrite((HFILE) opaque, (void *) buf, (ULONG) len, &bw);    
  631.     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), (bw > 0) ? ((PHYSFS_sint64) bw) : -1);
  632.     return (PHYSFS_sint64) bw;
  633. } /* __PHYSFS_platformWrite */
  634.  
  635.  
  636. int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
  637. {
  638.     ULONG dummy;
  639.     HFILE hfile = (HFILE) opaque;
  640.     LONG dist = (LONG) pos;
  641.     APIRET rc;
  642.  
  643.     /* hooray for 32-bit filesystem limits!  :) */
  644.     BAIL_IF((PHYSFS_uint64) dist != pos, PHYSFS_ERR_INVALID_ARGUMENT, 0);
  645.     rc = DosSetFilePtr(hfile, dist, FILE_BEGIN, &dummy);
  646.     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
  647.     return 1;
  648. } /* __PHYSFS_platformSeek */
  649.  
  650.  
  651. PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
  652. {
  653.     ULONG pos;
  654.     HFILE hfile = (HFILE) opaque;
  655.     const APIRET rc = DosSetFilePtr(hfile, 0, FILE_CURRENT, &pos);
  656.     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), -1);
  657.     return ((PHYSFS_sint64) pos);
  658. } /* __PHYSFS_platformTell */
  659.  
  660.  
  661. PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
  662. {
  663.     FILESTATUS3 fs;
  664.     HFILE hfile = (HFILE) opaque;
  665.     const APIRET rc = DosQueryFileInfo(hfile, FIL_STANDARD, &fs, sizeof (fs));
  666.     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), -1);
  667.     return ((PHYSFS_sint64) fs.cbFile);
  668. } /* __PHYSFS_platformFileLength */
  669.  
  670.  
  671. int __PHYSFS_platformFlush(void *opaque)
  672. {
  673.     const APIRET rc = DosResetBuffer((HFILE) opaque);
  674.     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
  675.     return 1;
  676. } /* __PHYSFS_platformFlush */
  677.  
  678.  
  679. void __PHYSFS_platformClose(void *opaque)
  680. {
  681.     DosClose((HFILE) opaque);  /* ignore errors. You should have flushed! */
  682. } /* __PHYSFS_platformClose */
  683.  
  684.  
  685. int __PHYSFS_platformDelete(const char *path)
  686. {
  687.     char *cppath = cvtUtf8ToCodepage(path);
  688.     FILESTATUS3 fs;
  689.     APIRET rc;
  690.     int retval = 0;
  691.  
  692.     BAIL_IF_ERRPASS(!cppath, 0);
  693.     rc = DosQueryPathInfo(cppath, FIL_STANDARD, &fs, sizeof (fs));
  694.     GOTO_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), done);
  695.     rc = (fs.attrFile & FILE_DIRECTORY) ? DosDeleteDir(path) : DosDelete(path);
  696.     GOTO_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), done);
  697.     retval = 1;  /* success */
  698.  
  699. done:
  700.     allocator.Free(cppath);
  701.     return retval;
  702. } /* __PHYSFS_platformDelete */
  703.  
  704.  
  705. /* Convert to a format PhysicsFS can grok... */
  706. PHYSFS_sint64 os2TimeToUnixTime(const FDATE *date, const FTIME *time)
  707. {
  708.     struct tm tm;
  709.  
  710.     tm.tm_sec = ((PHYSFS_uint32) time->twosecs) * 2;                        
  711.     tm.tm_min = time->minutes;
  712.     tm.tm_hour = time->hours;
  713.     tm.tm_mday = date->day;
  714.     tm.tm_mon = date->month;
  715.     tm.tm_year = ((PHYSFS_uint32) date->year) + 80;
  716.     tm.tm_wday = -1 /*st_localtz.wDayOfWeek*/;
  717.     tm.tm_yday = -1;
  718.     tm.tm_isdst = -1;
  719.  
  720.     return (PHYSFS_sint64) mktime(&tm);
  721. } /* os2TimeToUnixTime */
  722.  
  723.  
  724. int __PHYSFS_platformStat(const char *filename, PHYSFS_Stat *stat, const int follow)
  725. {
  726.     char *cpfname = cvtUtf8ToCodepage(filename);
  727.     FILESTATUS3 fs;
  728.     int retval = 0;
  729.     APIRET rc;
  730.  
  731.     BAIL_IF_ERRPASS(!cpfname, 0);
  732.  
  733.     rc = DosQueryPathInfo(cpfname, FIL_STANDARD, &fs, sizeof (fs));
  734.     GOTO_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), done);
  735.  
  736.     if (fs.attrFile & FILE_DIRECTORY)
  737.     {
  738.         stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
  739.         stat->filesize = 0;
  740.     } /* if */
  741.     else
  742.     {
  743.         stat->filetype = PHYSFS_FILETYPE_REGULAR;
  744.         stat->filesize = fs.cbFile;
  745.     } /* else */
  746.  
  747.     stat->modtime = os2TimeToUnixTime(&fs.fdateLastWrite, &fs.ftimeLastWrite);
  748.     if (stat->modtime < 0)
  749.         stat->modtime = 0;
  750.  
  751.     stat->accesstime = os2TimeToUnixTime(&fs.fdateLastAccess, &fs.ftimeLastAccess);
  752.     if (stat->accesstime < 0)
  753.         stat->accesstime = 0;
  754.  
  755.     stat->createtime = os2TimeToUnixTime(&fs.fdateCreation, &fs.ftimeCreation);
  756.     if (stat->createtime < 0)
  757.         stat->createtime = 0;
  758.  
  759.     stat->readonly = ((fs.attrFile & FILE_READONLY) == FILE_READONLY);
  760.     return 1;  /* success */
  761.  
  762. done:
  763.     allocator.Free(cpfname);
  764.     return retval;
  765. } /* __PHYSFS_platformStat */
  766.  
  767.  
  768. void *__PHYSFS_platformGetThreadID(void)
  769. {
  770.     PTIB ptib;
  771.     PPIB ppib;
  772.  
  773.     /*
  774.      * Allegedly, this API never fails, but we'll punt and return a
  775.      *  default value (zero might as well do) if it does.
  776.      */
  777.     const APIRET rc = DosGetInfoBlocks(&ptib, &ppib);
  778.     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
  779.     return ((void *) ptib->tib_ordinal);
  780. } /* __PHYSFS_platformGetThreadID */
  781.  
  782.  
  783. void *__PHYSFS_platformCreateMutex(void)
  784. {
  785.     HMTX hmtx = NULLHANDLE;
  786.     const APIRET rc = DosCreateMutexSem(NULL, &hmtx, 0, 0);
  787.     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), NULL);
  788.     return ((void *) hmtx);
  789. } /* __PHYSFS_platformCreateMutex */
  790.  
  791.  
  792. void __PHYSFS_platformDestroyMutex(void *mutex)
  793. {
  794.     DosCloseMutexSem((HMTX) mutex);
  795. } /* __PHYSFS_platformDestroyMutex */
  796.  
  797.  
  798. int __PHYSFS_platformGrabMutex(void *mutex)
  799. {
  800.     /* Do _NOT_ set the physfs error message in here! */
  801.     return (DosRequestMutexSem((HMTX) mutex, SEM_INDEFINITE_WAIT) == NO_ERROR);
  802. } /* __PHYSFS_platformGrabMutex */
  803.  
  804.  
  805. void __PHYSFS_platformReleaseMutex(void *mutex)
  806. {
  807.     DosReleaseMutexSem((HMTX) mutex);
  808. } /* __PHYSFS_platformReleaseMutex */
  809.  
  810. #endif  /* PHYSFS_PLATFORM_OS2 */
  811.  
  812. /* end of physfs_platform_os2.c ... */
  813.