Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 1 | pmbaty | 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 | ¶m, 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(¤tDisk, &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 ... */ |