~ubuntu-branches/ubuntu/trusty/hedgewars/trusty-proposed

« back to all changes in this revision

Viewing changes to misc/libphysfs/archiver_unpacked.c

  • Committer: Package Import Robot
  • Author(s): Gianfranco Costamagna
  • Date: 2013-06-04 17:53:08 UTC
  • mfrom: (19.1.2 sid)
  • Revision ID: package-import@ubuntu.com-20130604175308-chcg1js1sci92vxw
Tags: 0.9.19.2-1
New upstream tarball.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * High-level PhysicsFS archiver for simple unpacked file formats.
 
3
 *
 
4
 * This is a framework that basic archivers build on top of. It's for simple
 
5
 *  formats that can just hand back a list of files and the offsets of their
 
6
 *  uncompressed data. There are an alarming number of formats like this.
 
7
 *
 
8
 * RULES: Archive entries must be uncompressed, must not have separate subdir
 
9
 *  entries (but can have subdirs), must be case insensitive LOW ASCII
 
10
 *  filenames <= 56 bytes. No symlinks, etc. We can relax some of these rules
 
11
 *  as necessary.
 
12
 *
 
13
 * Please see the file LICENSE.txt in the source's root directory.
 
14
 *
 
15
 *  This file written by Ryan C. Gordon.
 
16
 */
 
17
 
 
18
#define __PHYSICSFS_INTERNAL__
 
19
#include "physfs_internal.h"
 
20
 
 
21
typedef struct
 
22
{
 
23
    PHYSFS_Io *io;
 
24
    PHYSFS_uint32 entryCount;
 
25
    UNPKentry *entries;
 
26
} UNPKinfo;
 
27
 
 
28
 
 
29
typedef struct
 
30
{
 
31
    PHYSFS_Io *io;
 
32
    UNPKentry *entry;
 
33
    PHYSFS_uint32 curPos;
 
34
} UNPKfileinfo;
 
35
 
 
36
 
 
37
void UNPK_closeArchive(PHYSFS_Dir *opaque)
 
38
{
 
39
    UNPKinfo *info = ((UNPKinfo *) opaque);
 
40
    info->io->destroy(info->io);
 
41
    allocator.Free(info->entries);
 
42
    allocator.Free(info);
 
43
} /* UNPK_closeArchive */
 
44
 
 
45
 
 
46
static PHYSFS_sint64 UNPK_read(PHYSFS_Io *io, void *buffer, PHYSFS_uint64 len)
 
47
{
 
48
    UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
 
49
    const UNPKentry *entry = finfo->entry;
 
50
    const PHYSFS_uint64 bytesLeft = (PHYSFS_uint64)(entry->size-finfo->curPos);
 
51
    PHYSFS_sint64 rc;
 
52
 
 
53
    if (bytesLeft < len)
 
54
        len = bytesLeft;
 
55
 
 
56
    rc = finfo->io->read(finfo->io, buffer, len);
 
57
    if (rc > 0)
 
58
        finfo->curPos += (PHYSFS_uint32) rc;
 
59
 
 
60
    return rc;
 
61
} /* UNPK_read */
 
62
 
 
63
 
 
64
static PHYSFS_sint64 UNPK_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
 
65
{
 
66
    BAIL_MACRO(PHYSFS_ERR_READ_ONLY, -1);
 
67
} /* UNPK_write */
 
68
 
 
69
 
 
70
static PHYSFS_sint64 UNPK_tell(PHYSFS_Io *io)
 
71
{
 
72
    return ((UNPKfileinfo *) io->opaque)->curPos;
 
73
} /* UNPK_tell */
 
74
 
 
75
 
 
76
static int UNPK_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
 
77
{
 
78
    UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
 
79
    const UNPKentry *entry = finfo->entry;
 
80
    int rc;
 
81
 
 
82
    BAIL_IF_MACRO(offset >= entry->size, PHYSFS_ERR_PAST_EOF, 0);
 
83
    rc = finfo->io->seek(finfo->io, entry->startPos + offset);
 
84
    if (rc)
 
85
        finfo->curPos = (PHYSFS_uint32) offset;
 
86
 
 
87
    return rc;
 
88
} /* UNPK_seek */
 
89
 
 
90
 
 
91
static PHYSFS_sint64 UNPK_length(PHYSFS_Io *io)
 
92
{
 
93
    const UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
 
94
    return ((PHYSFS_sint64) finfo->entry->size);
 
95
} /* UNPK_length */
 
96
 
 
97
 
 
98
static PHYSFS_Io *UNPK_duplicate(PHYSFS_Io *_io)
 
99
{
 
100
    UNPKfileinfo *origfinfo = (UNPKfileinfo *) _io->opaque;
 
101
    PHYSFS_Io *io = NULL;
 
102
    PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
 
103
    UNPKfileinfo *finfo = (UNPKfileinfo *) allocator.Malloc(sizeof (UNPKfileinfo));
 
104
    GOTO_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_duplicate_failed);
 
105
    GOTO_IF_MACRO(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_duplicate_failed);
 
106
 
 
107
    io = origfinfo->io->duplicate(origfinfo->io);
 
108
    if (!io) goto UNPK_duplicate_failed;
 
109
    finfo->io = io;
 
110
    finfo->entry = origfinfo->entry;
 
111
    finfo->curPos = 0;
 
112
    memcpy(retval, _io, sizeof (PHYSFS_Io));
 
113
    retval->opaque = finfo;
 
114
    return retval;
 
115
 
 
116
UNPK_duplicate_failed:
 
117
    if (finfo != NULL) allocator.Free(finfo);
 
118
    if (retval != NULL) allocator.Free(retval);
 
119
    if (io != NULL) io->destroy(io);
 
120
    return NULL;
 
121
} /* UNPK_duplicate */
 
122
 
 
123
static int UNPK_flush(PHYSFS_Io *io) { return 1;  /* no write support. */ }
 
124
 
 
125
static void UNPK_destroy(PHYSFS_Io *io)
 
126
{
 
127
    UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
 
128
    finfo->io->destroy(finfo->io);
 
129
    allocator.Free(finfo);
 
130
    allocator.Free(io);
 
131
} /* UNPK_destroy */
 
132
 
 
133
 
 
134
static const PHYSFS_Io UNPK_Io =
 
135
{
 
136
    CURRENT_PHYSFS_IO_API_VERSION, NULL,
 
137
    UNPK_read,
 
138
    UNPK_write,
 
139
    UNPK_seek,
 
140
    UNPK_tell,
 
141
    UNPK_length,
 
142
    UNPK_duplicate,
 
143
    UNPK_flush,
 
144
    UNPK_destroy
 
145
};
 
146
 
 
147
 
 
148
static int entryCmp(void *_a, size_t one, size_t two)
 
149
{
 
150
    if (one != two)
 
151
    {
 
152
        const UNPKentry *a = (const UNPKentry *) _a;
 
153
        return __PHYSFS_stricmpASCII(a[one].name, a[two].name);
 
154
    } /* if */
 
155
 
 
156
    return 0;
 
157
} /* entryCmp */
 
158
 
 
159
 
 
160
static void entrySwap(void *_a, size_t one, size_t two)
 
161
{
 
162
    if (one != two)
 
163
    {
 
164
        UNPKentry tmp;
 
165
        UNPKentry *first = &(((UNPKentry *) _a)[one]);
 
166
        UNPKentry *second = &(((UNPKentry *) _a)[two]);
 
167
        memcpy(&tmp, first, sizeof (UNPKentry));
 
168
        memcpy(first, second, sizeof (UNPKentry));
 
169
        memcpy(second, &tmp, sizeof (UNPKentry));
 
170
    } /* if */
 
171
} /* entrySwap */
 
172
 
 
173
 
 
174
static PHYSFS_sint32 findStartOfDir(UNPKinfo *info, const char *path,
 
175
                                    int stop_on_first_find)
 
176
{
 
177
    PHYSFS_sint32 lo = 0;
 
178
    PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
 
179
    PHYSFS_sint32 middle;
 
180
    PHYSFS_uint32 dlen = (PHYSFS_uint32) strlen(path);
 
181
    PHYSFS_sint32 retval = -1;
 
182
    const char *name;
 
183
    int rc;
 
184
 
 
185
    if (*path == '\0')  /* root dir? */
 
186
        return 0;
 
187
 
 
188
    if ((dlen > 0) && (path[dlen - 1] == '/')) /* ignore trailing slash. */
 
189
        dlen--;
 
190
 
 
191
    while (lo <= hi)
 
192
    {
 
193
        middle = lo + ((hi - lo) / 2);
 
194
        name = info->entries[middle].name;
 
195
        rc = __PHYSFS_strnicmpASCII(path, name, dlen);
 
196
        if (rc == 0)
 
197
        {
 
198
            char ch = name[dlen];
 
199
            if (ch < '/') /* make sure this isn't just a substr match. */
 
200
                rc = -1;
 
201
            else if (ch > '/')
 
202
                rc = 1;
 
203
            else 
 
204
            {
 
205
                if (stop_on_first_find) /* Just checking dir's existance? */
 
206
                    return middle;
 
207
 
 
208
                if (name[dlen + 1] == '\0') /* Skip initial dir entry. */
 
209
                    return (middle + 1);
 
210
 
 
211
                /* there might be more entries earlier in the list. */
 
212
                retval = middle;
 
213
                hi = middle - 1;
 
214
            } /* else */
 
215
        } /* if */
 
216
 
 
217
        if (rc > 0)
 
218
            lo = middle + 1;
 
219
        else
 
220
            hi = middle - 1;
 
221
    } /* while */
 
222
 
 
223
    return retval;
 
224
} /* findStartOfDir */
 
225
 
 
226
 
 
227
/*
 
228
 * Moved to seperate function so we can use alloca then immediately throw
 
229
 *  away the allocated stack space...
 
230
 */
 
231
static void doEnumCallback(PHYSFS_EnumFilesCallback cb, void *callbackdata,
 
232
                           const char *odir, const char *str, PHYSFS_sint32 ln)
 
233
{
 
234
    char *newstr = __PHYSFS_smallAlloc(ln + 1);
 
235
    if (newstr == NULL)
 
236
        return;
 
237
 
 
238
    memcpy(newstr, str, ln);
 
239
    newstr[ln] = '\0';
 
240
    cb(callbackdata, odir, newstr);
 
241
    __PHYSFS_smallFree(newstr);
 
242
} /* doEnumCallback */
 
243
 
 
244
 
 
245
void UNPK_enumerateFiles(PHYSFS_Dir *opaque, const char *dname,
 
246
                         int omitSymLinks, PHYSFS_EnumFilesCallback cb,
 
247
                         const char *origdir, void *callbackdata)
 
248
{
 
249
    UNPKinfo *info = ((UNPKinfo *) opaque);
 
250
    PHYSFS_sint32 dlen, dlen_inc, max, i;
 
251
 
 
252
    i = findStartOfDir(info, dname, 0);
 
253
    if (i == -1)  /* no such directory. */
 
254
        return;
 
255
 
 
256
    dlen = (PHYSFS_sint32) strlen(dname);
 
257
    if ((dlen > 0) && (dname[dlen - 1] == '/')) /* ignore trailing slash. */
 
258
        dlen--;
 
259
 
 
260
    dlen_inc = ((dlen > 0) ? 1 : 0) + dlen;
 
261
    max = (PHYSFS_sint32) info->entryCount;
 
262
    while (i < max)
 
263
    {
 
264
        char *add;
 
265
        char *ptr;
 
266
        PHYSFS_sint32 ln;
 
267
        char *e = info->entries[i].name;
 
268
        if ((dlen) &&
 
269
            ((__PHYSFS_strnicmpASCII(e, dname, dlen)) || (e[dlen] != '/')))
 
270
        {
 
271
            break;  /* past end of this dir; we're done. */
 
272
        } /* if */
 
273
 
 
274
        add = e + dlen_inc;
 
275
        ptr = strchr(add, '/');
 
276
        ln = (PHYSFS_sint32) ((ptr) ? ptr-add : strlen(add));
 
277
        doEnumCallback(cb, callbackdata, origdir, add, ln);
 
278
        ln += dlen_inc;  /* point past entry to children... */
 
279
 
 
280
        /* increment counter and skip children of subdirs... */
 
281
        while ((++i < max) && (ptr != NULL))
 
282
        {
 
283
            char *e_new = info->entries[i].name;
 
284
            if ((__PHYSFS_strnicmpASCII(e, e_new, ln) != 0) ||
 
285
                (e_new[ln] != '/'))
 
286
            {
 
287
                break;
 
288
            } /* if */
 
289
        } /* while */
 
290
    } /* while */
 
291
} /* UNPK_enumerateFiles */
 
292
 
 
293
 
 
294
/*
 
295
 * This will find the UNPKentry associated with a path in platform-independent
 
296
 *  notation. Directories don't have UNPKentries associated with them, but 
 
297
 *  (*isDir) will be set to non-zero if a dir was hit.
 
298
 */
 
299
static UNPKentry *findEntry(const UNPKinfo *info, const char *path, int *isDir)
 
300
{
 
301
    UNPKentry *a = info->entries;
 
302
    PHYSFS_sint32 pathlen = (PHYSFS_sint32) strlen(path);
 
303
    PHYSFS_sint32 lo = 0;
 
304
    PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
 
305
    PHYSFS_sint32 middle;
 
306
    const char *thispath = NULL;
 
307
    int rc;
 
308
 
 
309
    while (lo <= hi)
 
310
    {
 
311
        middle = lo + ((hi - lo) / 2);
 
312
        thispath = a[middle].name;
 
313
        rc = __PHYSFS_strnicmpASCII(path, thispath, pathlen);
 
314
 
 
315
        if (rc > 0)
 
316
            lo = middle + 1;
 
317
 
 
318
        else if (rc < 0)
 
319
            hi = middle - 1;
 
320
 
 
321
        else /* substring match...might be dir or entry or nothing. */
 
322
        {
 
323
            if (isDir != NULL)
 
324
            {
 
325
                *isDir = (thispath[pathlen] == '/');
 
326
                if (*isDir)
 
327
                    return NULL;
 
328
            } /* if */
 
329
 
 
330
            if (thispath[pathlen] == '\0') /* found entry? */
 
331
                return &a[middle];
 
332
            /* adjust search params, try again. */
 
333
            else if (thispath[pathlen] > '/')
 
334
                hi = middle - 1;
 
335
            else
 
336
                lo = middle + 1;
 
337
        } /* if */
 
338
    } /* while */
 
339
 
 
340
    if (isDir != NULL)
 
341
        *isDir = 0;
 
342
 
 
343
    BAIL_MACRO(PHYSFS_ERR_NO_SUCH_PATH, NULL);
 
344
} /* findEntry */
 
345
 
 
346
 
 
347
PHYSFS_Io *UNPK_openRead(PHYSFS_Dir *opaque, const char *fnm, int *fileExists)
 
348
{
 
349
    PHYSFS_Io *retval = NULL;
 
350
    UNPKinfo *info = (UNPKinfo *) opaque;
 
351
    UNPKfileinfo *finfo = NULL;
 
352
    int isdir = 0;
 
353
    UNPKentry *entry = findEntry(info, fnm, &isdir);
 
354
 
 
355
    *fileExists = (entry != NULL);
 
356
    GOTO_IF_MACRO(isdir, PHYSFS_ERR_NOT_A_FILE, UNPK_openRead_failed);
 
357
    GOTO_IF_MACRO(!entry, ERRPASS, UNPK_openRead_failed);
 
358
 
 
359
    retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
 
360
    GOTO_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_openRead_failed);
 
361
 
 
362
    finfo = (UNPKfileinfo *) allocator.Malloc(sizeof (UNPKfileinfo));
 
363
    GOTO_IF_MACRO(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_openRead_failed);
 
364
 
 
365
    finfo->io = info->io->duplicate(info->io);
 
366
    GOTO_IF_MACRO(!finfo->io, ERRPASS, UNPK_openRead_failed);
 
367
 
 
368
    if (!finfo->io->seek(finfo->io, entry->startPos))
 
369
        goto UNPK_openRead_failed;
 
370
 
 
371
    finfo->curPos = 0;
 
372
    finfo->entry = entry;
 
373
 
 
374
    memcpy(retval, &UNPK_Io, sizeof (*retval));
 
375
    retval->opaque = finfo;
 
376
    return retval;
 
377
 
 
378
UNPK_openRead_failed:
 
379
    if (finfo != NULL)
 
380
    {
 
381
        if (finfo->io != NULL)
 
382
            finfo->io->destroy(finfo->io);
 
383
        allocator.Free(finfo);
 
384
    } /* if */
 
385
 
 
386
    if (retval != NULL)
 
387
        allocator.Free(retval);
 
388
 
 
389
    return NULL;
 
390
} /* UNPK_openRead */
 
391
 
 
392
 
 
393
PHYSFS_Io *UNPK_openWrite(PHYSFS_Dir *opaque, const char *name)
 
394
{
 
395
    BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL);
 
396
} /* UNPK_openWrite */
 
397
 
 
398
 
 
399
PHYSFS_Io *UNPK_openAppend(PHYSFS_Dir *opaque, const char *name)
 
400
{
 
401
    BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL);
 
402
} /* UNPK_openAppend */
 
403
 
 
404
 
 
405
int UNPK_remove(PHYSFS_Dir *opaque, const char *name)
 
406
{
 
407
    BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0);
 
408
} /* UNPK_remove */
 
409
 
 
410
 
 
411
int UNPK_mkdir(PHYSFS_Dir *opaque, const char *name)
 
412
{
 
413
    BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0);
 
414
} /* UNPK_mkdir */
 
415
 
 
416
 
 
417
int UNPK_stat(PHYSFS_Dir *opaque, const char *filename,
 
418
              int *exists, PHYSFS_Stat *stat)
 
419
{
 
420
    int isDir = 0;
 
421
    const UNPKinfo *info = (const UNPKinfo *) opaque;
 
422
    const UNPKentry *entry = findEntry(info, filename, &isDir);
 
423
 
 
424
    if (isDir)
 
425
    {
 
426
        *exists = 1;
 
427
        stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
 
428
        stat->filesize = 0;
 
429
    } /* if */
 
430
    else if (entry != NULL)
 
431
    {
 
432
        *exists = 1;
 
433
        stat->filetype = PHYSFS_FILETYPE_REGULAR;
 
434
        stat->filesize = entry->size;
 
435
    } /* else if */
 
436
    else
 
437
    {
 
438
        *exists = 0;
 
439
        return 0;
 
440
    } /* else */
 
441
 
 
442
    stat->modtime = -1;
 
443
    stat->createtime = -1;
 
444
    stat->accesstime = -1;
 
445
    stat->readonly = 1;
 
446
 
 
447
    return 1;
 
448
} /* UNPK_stat */
 
449
 
 
450
 
 
451
PHYSFS_Dir *UNPK_openArchive(PHYSFS_Io *io, UNPKentry *e,
 
452
                             const PHYSFS_uint32 num)
 
453
{
 
454
    UNPKinfo *info = (UNPKinfo *) allocator.Malloc(sizeof (UNPKinfo));
 
455
    if (info == NULL)
 
456
    {
 
457
        allocator.Free(e);
 
458
        BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
 
459
    } /* if */
 
460
 
 
461
    __PHYSFS_sort(e, (size_t) num, entryCmp, entrySwap);
 
462
    info->io = io;
 
463
    info->entryCount = num;
 
464
    info->entries = e;
 
465
 
 
466
    return info;
 
467
} /* UNPK_openArchive */
 
468
 
 
469
/* end of archiver_unpacked.c ... */
 
470