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(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 | 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(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(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 | |