Subversion Repositories Games.Descent

Rev

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