/
grp.c
471 lines (366 loc) · 12.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
* GRP support routines for PhysicsFS.
*
* This driver handles BUILD engine archives ("groupfiles"). This format
* (but not this driver) was put together by Ken Silverman.
*
* The format is simple enough. In Ken's words:
*
* What's the .GRP file format?
*
* The ".grp" file format is just a collection of a lot of files stored
* into 1 big one. I tried to make the format as simple as possible: The
* first 12 bytes contains my name, "KenSilverman". The next 4 bytes is
* the number of files that were compacted into the group file. Then for
* each file, there is a 16 byte structure, where the first 12 bytes are
* the filename, and the last 4 bytes are the file's size. The rest of
* the group file is just the raw data packed one after the other in the
* same order as the list of files.
*
* (That info is from http://www.advsys.net/ken/build.htm ...)
*
* Please see the file LICENSE in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
27
28
29
30
31
32
#if HAVE_CONFIG_H
# include <config.h>
#endif
#if (defined PHYSFS_SUPPORTS_GRP)
33
34
35
36
37
38
39
40
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "physfs.h"
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
41
42
43
typedef struct
{
char name[13];
44
45
PHYSFS_uint32 startPos;
PHYSFS_uint32 size;
46
47
} GRPentry;
48
49
typedef struct
{
50
char *filename;
51
52
53
PHYSFS_sint64 last_mod_time;
PHYSFS_uint32 entryCount;
GRPentry *entries;
54
55
56
57
} GRPinfo;
typedef struct
{
58
void *handle;
59
GRPentry *entry;
60
PHYSFS_uint32 curPos;
61
62
63
} GRPfileinfo;
64
static void GRP_dirClose(dvoid *opaque)
66
GRPinfo *info = ((GRPinfo *) opaque);
67
68
69
allocator.Free(info->filename);
allocator.Free(info->entries);
allocator.Free(info);
70
} /* GRP_dirClose */
73
static PHYSFS_sint64 GRP_read(fvoid *opaque, void *buffer,
74
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
76
GRPfileinfo *finfo = (GRPfileinfo *) opaque;
77
GRPentry *entry = finfo->entry;
78
79
PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos;
PHYSFS_uint32 objsLeft = (bytesLeft / objSize);
80
PHYSFS_sint64 rc;
81
82
if (objsLeft < objCount)
83
objCount = objsLeft;
85
86
rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount);
if (rc > 0)
87
finfo->curPos += (PHYSFS_uint32) (rc * objSize);
88
89
return(rc);
90
91
92
} /* GRP_read */
93
static PHYSFS_sint64 GRP_write(fvoid *opaque, const void *buffer,
94
95
96
97
98
99
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
} /* GRP_write */
100
static int GRP_eof(fvoid *opaque)
102
GRPfileinfo *finfo = (GRPfileinfo *) opaque;
103
GRPentry *entry = finfo->entry;
104
return(finfo->curPos >= entry->size);
105
106
107
} /* GRP_eof */
108
static PHYSFS_sint64 GRP_tell(fvoid *opaque)
110
return(((GRPfileinfo *) opaque)->curPos);
111
112
113
} /* GRP_tell */
114
static int GRP_seek(fvoid *opaque, PHYSFS_uint64 offset)
116
GRPfileinfo *finfo = (GRPfileinfo *) opaque;
117
118
GRPentry *entry = finfo->entry;
int rc;
119
120
BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
121
122
BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0);
rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset);
124
finfo->curPos = (PHYSFS_uint32) offset;
125
126
return(rc);
127
128
129
} /* GRP_seek */
130
static PHYSFS_sint64 GRP_fileLength(fvoid *opaque)
132
GRPfileinfo *finfo = (GRPfileinfo *) opaque;
133
return((PHYSFS_sint64) finfo->entry->size);
134
135
136
} /* GRP_fileLength */
137
static int GRP_fileClose(fvoid *opaque)
139
GRPfileinfo *finfo = (GRPfileinfo *) opaque;
140
BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
141
allocator.Free(finfo);
142
143
144
145
return(1);
} /* GRP_fileClose */
146
147
static int grp_open(const char *filename, int forWriting,
void **fh, PHYSFS_uint32 *count)
149
PHYSFS_uint8 buf[12];
150
151
152
153
*fh = NULL;
BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
154
155
*fh = __PHYSFS_platformOpenRead(filename);
BAIL_IF_MACRO(*fh == NULL, NULL, 0);
157
158
159
160
161
162
163
164
if (__PHYSFS_platformRead(*fh, buf, 12, 1) != 1)
goto openGrp_failed;
if (memcmp(buf, "KenSilverman", 12) != 0)
{
__PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
goto openGrp_failed;
} /* if */
166
if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1)
167
goto openGrp_failed;
169
*count = PHYSFS_swapULE32(*count);
171
return(1);
172
173
174
175
176
177
178
179
openGrp_failed:
if (*fh != NULL)
__PHYSFS_platformClose(*fh);
*count = -1;
*fh = NULL;
return(0);
180
} /* grp_open */
181
182
183
184
static int GRP_isArchive(const char *filename, int forWriting)
{
186
187
PHYSFS_uint32 fileCount;
int retval = grp_open(filename, forWriting, &fh, &fileCount);
188
189
if (fh != NULL)
190
__PHYSFS_platformClose(fh);
191
192
193
194
195
return(retval);
} /* GRP_isArchive */
196
static int grp_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
198
199
200
GRPentry *a = (GRPentry *) _a;
return(strcmp(a[one].name, a[two].name));
} /* grp_entry_cmp */
203
static void grp_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
204
205
{
GRPentry tmp;
206
207
208
209
210
211
GRPentry *first = &(((GRPentry *) _a)[one]);
GRPentry *second = &(((GRPentry *) _a)[two]);
memcpy(&tmp, first, sizeof (GRPentry));
memcpy(first, second, sizeof (GRPentry));
memcpy(second, &tmp, sizeof (GRPentry));
} /* grp_entry_swap */
212
213
214
static int grp_load_entries(const char *name, int forWriting, GRPinfo *info)
216
void *fh = NULL;
217
PHYSFS_uint32 fileCount;
218
PHYSFS_uint32 location = 16; /* sizeof sig. */
219
220
221
222
223
GRPentry *entry;
char *ptr;
BAIL_IF_MACRO(!grp_open(name, forWriting, &fh, &fileCount), NULL, 0);
info->entryCount = fileCount;
224
info->entries = (GRPentry *) allocator.Malloc(sizeof(GRPentry)*fileCount);
225
226
227
228
229
230
if (info->entries == NULL)
{
__PHYSFS_platformClose(fh);
BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
} /* if */
231
232
location += (16 * fileCount);
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
for (entry = info->entries; fileCount > 0; fileCount--, entry++)
{
if (__PHYSFS_platformRead(fh, &entry->name, 12, 1) != 1)
{
__PHYSFS_platformClose(fh);
return(0);
} /* if */
entry->name[12] = '\0'; /* name isn't null-terminated in file. */
if ((ptr = strchr(entry->name, ' ')) != NULL)
*ptr = '\0'; /* trim extra spaces. */
if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1)
{
__PHYSFS_platformClose(fh);
return(0);
} /* if */
entry->size = PHYSFS_swapULE32(entry->size);
entry->startPos = location;
253
location += entry->size;
254
255
256
257
} /* for */
__PHYSFS_platformClose(fh);
258
259
__PHYSFS_sort(info->entries, info->entryCount,
grp_entry_cmp, grp_entry_swap);
260
261
262
263
return(1);
} /* grp_load_entries */
264
static void *GRP_openArchive(const char *name, int forWriting)
265
266
{
PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name);
267
GRPinfo *info = (GRPinfo *) allocator.Malloc(sizeof (GRPinfo));
269
BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, 0);
271
memset(info, '\0', sizeof (GRPinfo));
272
info->filename = (char *) allocator.Malloc(strlen(name) + 1);
273
GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, GRP_openArchive_failed);
275
if (!grp_load_entries(name, forWriting, info))
276
277
goto GRP_openArchive_failed;
278
279
strcpy(info->filename, name);
info->last_mod_time = modtime;
280
281
return(info);
282
283
GRP_openArchive_failed:
284
if (info != NULL)
286
if (info->filename != NULL)
287
allocator.Free(info->filename);
288
if (info->entries != NULL)
289
290
allocator.Free(info->entries);
allocator.Free(info);
291
292
293
} /* if */
return(NULL);
294
295
296
} /* GRP_openArchive */
297
298
299
static void GRP_enumerateFiles(dvoid *opaque, const char *dname,
int omitSymLinks, PHYSFS_StringCallback cb,
void *callbackdata)
301
/* no directories in GRP files. */
302
303
304
305
306
307
if (*dname != '\0')
{
GRPinfo *info = (GRPinfo *) opaque;
GRPentry *entry = info->entries;
PHYSFS_uint32 max = info->entryCount;
PHYSFS_uint32 i;
309
310
311
for (i = 0; i < max; i++, entry++)
cb(callbackdata, entry->name);
} /* if */
312
313
314
} /* GRP_enumerateFiles */
315
static GRPentry *grp_find_entry(GRPinfo *info, const char *name)
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
char *ptr = strchr(name, '.');
GRPentry *a = info->entries;
PHYSFS_sint32 lo = 0;
PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
PHYSFS_sint32 middle;
int rc;
/*
* Rule out filenames to avoid unneeded processing...no dirs,
* big filenames, or extensions > 3 chars.
*/
BAIL_IF_MACRO((ptr) && (strlen(ptr) > 4), ERR_NO_SUCH_FILE, NULL);
BAIL_IF_MACRO(strlen(name) > 12, ERR_NO_SUCH_FILE, NULL);
BAIL_IF_MACRO(strchr(name, '/') != NULL, ERR_NO_SUCH_FILE, NULL);
while (lo <= hi)
334
335
336
337
338
339
340
341
342
middle = lo + ((hi - lo) / 2);
rc = strcmp(name, a[middle].name);
if (rc == 0) /* found it! */
return(&a[middle]);
else if (rc > 0)
lo = middle + 1;
else
hi = middle - 1;
} /* while */
344
345
BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
} /* grp_find_entry */
348
static int GRP_exists(dvoid *opaque, const char *name)
350
return(grp_find_entry((GRPinfo *) opaque, name) != NULL);
351
352
353
} /* GRP_exists */
354
static int GRP_isDirectory(dvoid *opaque, const char *name, int *fileExists)
356
*fileExists = GRP_exists(opaque, name);
357
358
359
360
return(0); /* never directories in a groupfile. */
} /* GRP_isDirectory */
361
static int GRP_isSymLink(dvoid *opaque, const char *name, int *fileExists)
363
*fileExists = GRP_exists(opaque, name);
364
365
366
367
return(0); /* never symlinks in a groupfile. */
} /* GRP_isSymLink */
368
static PHYSFS_sint64 GRP_getLastModTime(dvoid *opaque,
369
370
const char *name,
int *fileExists)
372
GRPinfo *info = (GRPinfo *) opaque;
373
PHYSFS_sint64 retval = -1;
375
376
*fileExists = (grp_find_entry(info, name) != NULL);
if (*fileExists) /* use time of GRP itself in the physical filesystem. */
377
retval = info->last_mod_time;
378
379
return(retval);
380
381
382
} /* GRP_getLastModTime */
383
static fvoid *GRP_openRead(dvoid *opaque, const char *fnm, int *fileExists)
385
GRPinfo *info = (GRPinfo *) opaque;
386
GRPfileinfo *finfo;
387
GRPentry *entry;
389
390
entry = grp_find_entry(info, fnm);
*fileExists = (entry != NULL);
391
BAIL_IF_MACRO(entry == NULL, NULL, NULL);
393
finfo = (GRPfileinfo *) allocator.Malloc(sizeof (GRPfileinfo));
394
BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
396
finfo->handle = __PHYSFS_platformOpenRead(info->filename);
397
if ( (finfo->handle == NULL) ||
398
(!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
400
allocator.Free(finfo);
401
402
403
return(NULL);
} /* if */
404
405
finfo->curPos = 0;
finfo->entry = entry;
406
return(finfo);
407
408
} /* GRP_openRead */
410
static fvoid *GRP_openWrite(dvoid *opaque, const char *name)
411
412
413
414
415
{
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
} /* GRP_openWrite */
416
static fvoid *GRP_openAppend(dvoid *opaque, const char *name)
417
418
419
420
421
{
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
} /* GRP_openAppend */
422
static int GRP_remove(dvoid *opaque, const char *name)
423
424
425
426
427
{
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
} /* GRP_remove */
428
static int GRP_mkdir(dvoid *opaque, const char *name)
429
430
431
432
{
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
} /* GRP_mkdir */
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_GRP =
{
"GRP",
GRP_ARCHIVE_DESCRIPTION,
"Ryan C. Gordon <icculus@clutteredmind.org>",
"http://icculus.org/physfs/",
};
const PHYSFS_Archiver __PHYSFS_Archiver_GRP =
{
&__PHYSFS_ArchiveInfo_GRP,
GRP_isArchive, /* isArchive() method */
GRP_openArchive, /* openArchive() method */
GRP_enumerateFiles, /* enumerateFiles() method */
GRP_exists, /* exists() method */
GRP_isDirectory, /* isDirectory() method */
GRP_isSymLink, /* isSymLink() method */
GRP_getLastModTime, /* getLastModTime() method */
GRP_openRead, /* openRead() method */
GRP_openWrite, /* openWrite() method */
GRP_openAppend, /* openAppend() method */
GRP_remove, /* remove() method */
GRP_mkdir, /* mkdir() method */
GRP_dirClose, /* dirClose() method */
GRP_read, /* read() method */
GRP_write, /* write() method */
GRP_eof, /* eof() method */
GRP_tell, /* tell() method */
GRP_seek, /* seek() method */
GRP_fileLength, /* fileLength() method */
GRP_fileClose /* fileClose() method */
};
468
469
#endif /* defined PHYSFS_SUPPORTS_GRP */
470
/* end of grp.c ... */