/
grp.c
407 lines (321 loc) · 11.3 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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*
* 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 ...)
*
* As it was never a concern in the DOS-based Duke Nukem days, I treat these
* archives as CASE-INSENSITIVE. Opening "myfile.txt" and "MYFILE.TXT" both
* work, and get the same file.
*
* Please see the file LICENSE in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include "physfs.h"
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#if (!defined PHYSFS_SUPPORTS_GRP)
#error PHYSFS_SUPPORTS_GRP must be defined.
#endif
46
47
/* !!! FIXME: Using the same file handle for all reads is a RACE CONDITION! */
48
49
50
typedef struct
{
FILE *handle;
51
PHYSFS_uint32 totalEntries;
52
53
54
55
} GRPinfo;
typedef struct
{
56
57
58
PHYSFS_uint32 startPos;
PHYSFS_uint32 curPos;
PHYSFS_uint32 size;
59
60
61
} GRPfileinfo;
62
static void GRP_dirClose(DirHandle *h);
63
64
static PHYSFS_sint64 GRP_read(FileHandle *handle, void *buffer,
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount);
65
static int GRP_eof(FileHandle *handle);
66
67
68
static PHYSFS_sint64 GRP_tell(FileHandle *handle);
static int GRP_seek(FileHandle *handle, PHYSFS_uint64 offset);
static PHYSFS_sint64 GRP_fileLength(FileHandle *handle);
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
static int GRP_fileClose(FileHandle *handle);
static int GRP_isArchive(const char *filename, int forWriting);
static DirHandle *GRP_openArchive(const char *name, int forWriting);
static LinkedStringList *GRP_enumerateFiles(DirHandle *h,
const char *dirname,
int omitSymLinks);
static int GRP_exists(DirHandle *h, const char *name);
static int GRP_isDirectory(DirHandle *h, const char *name);
static int GRP_isSymLink(DirHandle *h, const char *name);
static FileHandle *GRP_openRead(DirHandle *h, const char *name);
static const FileFunctions __PHYSFS_FileFunctions_GRP =
{
GRP_read, /* read() method */
NULL, /* write() method */
GRP_eof, /* eof() method */
GRP_tell, /* tell() method */
GRP_seek, /* seek() method */
GRP_fileLength, /* fileLength() method */
GRP_fileClose /* fileClose() method */
};
const DirFunctions __PHYSFS_DirFunctions_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_openRead, /* openRead() method */
NULL, /* openWrite() method */
NULL, /* openAppend() method */
NULL, /* remove() method */
NULL, /* mkdir() method */
GRP_dirClose /* dirClose() method */
};
const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_GRP =
{
"GRP",
"Build engine Groupfile format",
112
"Ryan C. Gordon <icculus@clutteredmind.org>",
113
114
115
116
117
118
119
120
121
122
123
"http://www.icculus.org/physfs/",
};
static void GRP_dirClose(DirHandle *h)
{
fclose( ((GRPinfo *) h->opaque)->handle );
free(h->opaque);
free(h);
} /* GRP_dirClose */
124
125
126
127
static PHYSFS_sint64 GRP_read(FileHandle *handle, void *buffer,
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
128
129
130
{
GRPfileinfo *finfo = (GRPfileinfo *) (handle->opaque);
FILE *fh = (FILE *) (((GRPinfo *) (handle->dirHandle->opaque))->handle);
131
132
PHYSFS_uint32 bytesLeft = (finfo->startPos + finfo->size) - finfo->curPos;
PHYSFS_uint32 objsLeft = bytesLeft / objSize;
133
size_t retval = 0;
134
135
136
137
138
139
140
141
142
if (objsLeft < objCount)
objCount = objsLeft;
errno = 0;
BAIL_IF_MACRO(fseek(fh,finfo->curPos,SEEK_SET) == -1,strerror(errno),-1);
errno = 0;
retval = fread(buffer, objSize, objCount, fh);
143
finfo->curPos += (retval * objSize);
144
BAIL_IF_MACRO((retval < (size_t) objCount) && (ferror(fh)),
145
strerror(errno), (PHYSFS_sint64) retval);
146
147
return((PHYSFS_sint64) retval);
148
149
150
151
152
153
154
155
156
157
} /* GRP_read */
static int GRP_eof(FileHandle *handle)
{
GRPfileinfo *finfo = (GRPfileinfo *) (handle->opaque);
return(finfo->curPos >= finfo->startPos + finfo->size);
} /* GRP_eof */
158
static PHYSFS_sint64 GRP_tell(FileHandle *handle)
159
160
161
162
163
164
{
GRPfileinfo *finfo = (GRPfileinfo *) (handle->opaque);
return(finfo->curPos - finfo->startPos);
} /* GRP_tell */
165
static int GRP_seek(FileHandle *handle, PHYSFS_uint64 offset)
166
167
{
GRPfileinfo *finfo = (GRPfileinfo *) (handle->opaque);
168
int newPos = finfo->startPos + offset;
169
170
171
172
173
174
175
176
BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
BAIL_IF_MACRO(newPos > finfo->startPos + finfo->size, ERR_PAST_EOF, 0);
finfo->curPos = newPos;
return(1);
} /* GRP_seek */
177
static PHYSFS_sint64 GRP_fileLength(FileHandle *handle)
178
179
180
181
182
183
{
GRPfileinfo *finfo = (GRPfileinfo *) (handle->opaque);
return(finfo->size);
} /* GRP_fileLength */
184
185
186
187
188
189
190
191
static int GRP_fileClose(FileHandle *handle)
{
free(handle->opaque);
free(handle);
return(1);
} /* GRP_fileClose */
192
static int openGrp(const char *filename, int forWriting, FILE **fh, PHYSFS_sint32 *count)
193
194
195
196
197
198
199
200
201
202
203
204
{
char buf[12];
*fh = NULL;
BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
errno = 0;
*fh = fopen(filename, "rb");
BAIL_IF_MACRO(*fh == NULL, strerror(errno), 0);
errno = 0;
BAIL_IF_MACRO(fread(buf, 12, 1, *fh) != 1, strerror(errno), 0);
205
BAIL_IF_MACRO(memcmp(buf, "KenSilverman", 12) != 0,
206
207
ERR_UNSUPPORTED_ARCHIVE, 0);
208
if (fread(count, sizeof (PHYSFS_sint32), 1, *fh) != 1)
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
*count = 0;
return(1);
} /* openGrp */
static int GRP_isArchive(const char *filename, int forWriting)
{
FILE *fh;
int fileCount;
int retval = openGrp(filename, forWriting, &fh, &fileCount);
if (fh != NULL)
fclose(fh);
return(retval);
} /* GRP_isArchive */
static DirHandle *GRP_openArchive(const char *name, int forWriting)
{
FILE *fh;
int fileCount;
DirHandle *retval = malloc(sizeof (DirHandle));
BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
retval->opaque = malloc(sizeof (GRPinfo));
if (retval->opaque == NULL)
{
free(retval);
BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, NULL);
} /* if */
if (!openGrp(name, forWriting, &fh, &fileCount))
{
if (fh != NULL)
fclose(fh);
free(retval->opaque);
free(retval);
} /* if */
((GRPinfo *) retval->opaque)->handle = fh;
((GRPinfo *) retval->opaque)->totalEntries = fileCount;
retval->funcs = &__PHYSFS_DirFunctions_GRP;
return(retval);
} /* GRP_openArchive */
257
258
259
static LinkedStringList *GRP_enumerateFiles(DirHandle *h,
const char *dirname,
int omitSymLinks)
260
261
262
263
264
265
266
267
268
{
char buf[16];
GRPinfo *g = (GRPinfo *) (h->opaque);
FILE *fh = g->handle;
int i;
LinkedStringList *retval = NULL;
LinkedStringList *l = NULL;
LinkedStringList *prev = NULL;
269
270
271
if (*dirname != '\0') /* no directories in GRP files. */
return(NULL);
272
273
274
275
276
277
278
279
280
281
282
283
/* jump to first file entry... */
errno = 0;
BAIL_IF_MACRO(fseek(fh, 16, SEEK_SET) == -1, strerror(errno), NULL);
for (i = 0; i < g->totalEntries; i++)
{
errno = 0;
BAIL_IF_MACRO(fread(buf, 16, 1, fh) != 1, strerror(errno), retval);
buf[12] = '\0'; /* FILENAME.EXT is all you get. */
l = (LinkedStringList *) malloc(sizeof (LinkedStringList));
284
if (l == NULL)
285
286
287
288
289
290
291
292
293
break;
l->str = (char *) malloc(strlen(buf) + 1);
if (l->str == NULL)
{
free(l);
break;
} /* if */
294
295
strcpy(l->str, buf);
296
297
298
299
300
301
302
303
304
305
306
307
308
if (retval == NULL)
retval = l;
else
prev->next = l;
prev = l;
l->next = NULL;
} /* for */
return(retval);
} /* GRP_enumerateFiles */
309
310
static PHYSFS_sint32 getFileEntry(DirHandle *h, const char *name,
PHYSFS_sint32 *size)
311
312
313
314
315
316
{
char buf[16];
GRPinfo *g = (GRPinfo *) (h->opaque);
FILE *fh = g->handle;
int i;
char *ptr;
317
int retval = (g->totalEntries + 1) * 16; /* offset of raw file data */
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
/* Rule out filenames to avoid unneeded file i/o... */
if (strchr(name, '/') != NULL) /* no directories in groupfiles. */
return(-1);
ptr = strchr(name, '.');
if ((ptr) && (strlen(ptr) > 4)) /* 3 char extension at most. */
return(-1);
if (strlen(name) > 12)
return(-1);
/* jump to first file entry... */
errno = 0;
BAIL_IF_MACRO(fseek(fh, 16, SEEK_SET) == -1, strerror(errno), -1);
for (i = 0; i < g->totalEntries; i++)
{
336
PHYSFS_sint32 l = 0;
337
338
errno = 0;
339
BAIL_IF_MACRO(fread(buf, 12, 1, fh) != 1, strerror(errno), -1);
340
341
342
errno = 0;
BAIL_IF_MACRO(fread(&l, sizeof (l), 1, fh) != 1, strerror(errno), -1);
343
344
345
346
347
348
buf[12] = '\0'; /* FILENAME.EXT is all you get. */
if (__PHYSFS_platformStricmp(buf, name) == 0)
{
if (size != NULL)
349
*size = l;
350
351
352
return(retval);
} /* if */
353
retval += l;
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
} /* for */
return(-1); /* not found. */
} /* getFileEntry */
static int GRP_exists(DirHandle *h, const char *name)
{
return(getFileEntry(h, name, NULL) != -1);
} /* GRP_exists */
static int GRP_isDirectory(DirHandle *h, const char *name)
{
return(0); /* never directories in a groupfile. */
} /* GRP_isDirectory */
static int GRP_isSymLink(DirHandle *h, const char *name)
{
return(0); /* never symlinks in a groupfile. */
} /* GRP_isSymLink */
static FileHandle *GRP_openRead(DirHandle *h, const char *name)
{
FileHandle *retval;
GRPfileinfo *finfo;
382
383
PHYSFS_sint32 size;
PHYSFS_sint32 offset;
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
offset = getFileEntry(h, name, &size);
BAIL_IF_MACRO(offset == -1, ERR_NO_SUCH_FILE, NULL);
retval = (FileHandle *) malloc(sizeof (FileHandle));
BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
finfo = (GRPfileinfo *) malloc(sizeof (GRPfileinfo));
if (finfo == NULL)
{
free(retval);
BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, NULL);
} /* if */
finfo->startPos = offset;
finfo->curPos = offset;
finfo->size = size;
retval->opaque = (void *) finfo;
retval->funcs = &__PHYSFS_FileFunctions_GRP;
retval->dirHandle = h;
return(retval);
} /* GRP_openRead */
/* end of grp.c ... */