7768
+ − 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;
10017
+ − 203
else
7768
+ − 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
10017
+ − 296
* notation. Directories don't have UNPKentries associated with them, but
7768
+ − 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