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 ... */ |