/
archiver_hog.c
477 lines (374 loc) · 12.4 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
/*
* HOG support routines for PhysicsFS.
*
* This driver handles Descent I/II HOG archives.
*
* The format is very simple:
*
* The file always starts with the 3-byte signature "DHF" (Descent
* HOG file). After that the files of a HOG are just attached after
* another, divided by a 17 bytes header, which specifies the name
* and length (in bytes) of the forthcoming file! So you just read
* the header with its information of how big the following file is,
* and then skip exact that number of bytes to get to the next file
* in that HOG.
*
* char sig[3] = {'D', 'H', 'F'}; // "DHF"=Descent HOG File
*
* struct {
* char file_name[13]; // Filename, padded to 13 bytes with 0s
* int file_size; // filesize in bytes
* char data[file_size]; // The file data
* } FILE_STRUCT; // Repeated until the end of the file.
*
* (That info is from http://www.descent2.com/ddn/specs/hog/)
*
26
* Please see the file LICENSE.txt in the source's root directory.
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
*
* This file written by Bradley Bell.
* Based on grp.c by Ryan C. Gordon.
*/
#if (defined PHYSFS_SUPPORTS_HOG)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "physfs.h"
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
/*
* One HOGentry is kept for each file in an open HOG archive.
*/
typedef struct
{
char name[13];
PHYSFS_uint32 startPos;
PHYSFS_uint32 size;
} HOGentry;
/*
* One HOGinfo is kept for each open HOG archive.
*/
typedef struct
{
57
PHYSFS_Io *io;
58
59
60
61
62
63
64
65
66
PHYSFS_uint32 entryCount;
HOGentry *entries;
} HOGinfo;
/*
* One HOGfileinfo is kept for each open file in a HOG archive.
*/
typedef struct
{
67
PHYSFS_Io *io;
68
69
70
71
72
HOGentry *entry;
PHYSFS_uint32 curPos;
} HOGfileinfo;
73
static inline int readAll(PHYSFS_Io *io, void *buf, const PHYSFS_uint64 len)
74
{
75
return (io->read(io, buf, len) == len);
76
77
78
} /* readAll */
79
static void HOG_dirClose(dvoid *opaque)
80
{
81
HOGinfo *info = ((HOGinfo *) opaque);
82
info->io->destroy(info->io);
83
84
allocator.Free(info->entries);
allocator.Free(info);
85
86
87
} /* HOG_dirClose */
88
static PHYSFS_sint64 HOG_read(PHYSFS_Io *io, void *buffer, PHYSFS_uint64 len)
89
{
90
HOGfileinfo *finfo = (HOGfileinfo *) io->opaque;
91
92
const HOGentry *entry = finfo->entry;
const PHYSFS_uint64 bytesLeft = (PHYSFS_uint64)(entry->size-finfo->curPos);
93
94
PHYSFS_sint64 rc;
95
96
if (bytesLeft < len)
len = bytesLeft;
97
98
rc = finfo->io->read(finfo->io, buffer, len);
99
if (rc > 0)
100
finfo->curPos += (PHYSFS_uint32) rc;
101
102
return rc;
103
104
105
} /* HOG_read */
106
static PHYSFS_sint64 HOG_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
107
108
109
110
111
{
BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
} /* HOG_write */
112
static PHYSFS_sint64 HOG_tell(PHYSFS_Io *io)
113
{
114
return ((HOGfileinfo *) io->opaque)->curPos;
115
116
117
} /* HOG_tell */
118
static int HOG_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
119
{
120
121
HOGfileinfo *finfo = (HOGfileinfo *) io->opaque;
const HOGentry *entry = finfo->entry;
122
123
124
int rc;
BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0);
125
rc = finfo->io->seek(finfo->io, entry->startPos + offset);
126
127
128
if (rc)
finfo->curPos = (PHYSFS_uint32) offset;
129
return rc;
130
131
132
} /* HOG_seek */
133
static PHYSFS_sint64 HOG_length(PHYSFS_Io *io)
134
{
135
const HOGfileinfo *finfo = (HOGfileinfo *) io->opaque;
136
return ((PHYSFS_sint64) finfo->entry->size);
137
} /* HOG_length */
138
139
140
static PHYSFS_Io *HOG_duplicate(PHYSFS_Io *_io)
141
{
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
HOGfileinfo *origfinfo = (HOGfileinfo *) _io->opaque;
PHYSFS_Io *io = NULL;
PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
HOGfileinfo *finfo = (HOGfileinfo *) allocator.Malloc(sizeof (HOGfileinfo));
GOTO_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, HOG_duplicate_failed);
GOTO_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, HOG_duplicate_failed);
io = origfinfo->io->duplicate(origfinfo->io);
GOTO_IF_MACRO(io == NULL, NULL, HOG_duplicate_failed);
finfo->io = io;
finfo->entry = origfinfo->entry;
finfo->curPos = 0;
memcpy(retval, _io, sizeof (PHYSFS_Io));
retval->opaque = finfo;
return retval;
HOG_duplicate_failed:
if (finfo != NULL) allocator.Free(finfo);
if (retval != NULL) allocator.Free(retval);
if (io != NULL) io->destroy(io);
return NULL;
} /* HOG_duplicate */
164
165
static int HOG_flush(PHYSFS_Io *io) { return 1; /* no write support. */ }
166
167
static void HOG_destroy(PHYSFS_Io *io)
168
{
169
170
171
172
173
HOGfileinfo *finfo = (HOGfileinfo *) io->opaque;
finfo->io->destroy(finfo->io);
allocator.Free(finfo);
allocator.Free(io);
} /* HOG_destroy */
174
175
176
177
178
179
180
181
182
183
184
185
186
187
static const PHYSFS_Io HOG_Io =
{
HOG_read,
HOG_write,
HOG_seek,
HOG_tell,
HOG_length,
HOG_duplicate,
HOG_flush,
HOG_destroy,
NULL
};
188
189
190
191
static int hogEntryCmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
192
{
193
194
195
if (one != two)
{
const HOGentry *a = (const HOGentry *) _a;
196
return __PHYSFS_stricmpASCII(a[one].name, a[two].name);
197
198
199
} /* if */
return 0;
200
} /* hogEntryCmp */
201
202
203
static void hogEntrySwap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
204
{
205
206
207
208
209
210
211
212
213
if (one != two)
{
HOGentry tmp;
HOGentry *first = &(((HOGentry *) _a)[one]);
HOGentry *second = &(((HOGentry *) _a)[two]);
memcpy(&tmp, first, sizeof (HOGentry));
memcpy(first, second, sizeof (HOGentry));
memcpy(second, &tmp, sizeof (HOGentry));
} /* if */
214
} /* hogEntrySwap */
215
216
217
static int hog_load_entries(PHYSFS_Io *io, HOGinfo *info)
218
{
219
220
221
222
223
224
225
226
const PHYSFS_uint64 iolen = io->length(io);
PHYSFS_uint32 entCount = 0;
void *ptr = NULL;
HOGentry *entry = NULL;
PHYSFS_uint32 size = 0;
PHYSFS_uint32 pos = 3;
while (pos < iolen)
227
{
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
entCount++;
ptr = allocator.Realloc(ptr, sizeof (HOGentry) * entCount);
BAIL_IF_MACRO(ptr == NULL, ERR_OUT_OF_MEMORY, 0);
info->entries = (HOGentry *) ptr;
entry = &info->entries[entCount-1];
BAIL_IF_MACRO(!readAll(io, &entry->name, 13), NULL, 0);
pos += 13;
BAIL_IF_MACRO(!readAll(io, &size, 4), NULL, 0);
pos += 4;
entry->size = PHYSFS_swapULE32(size);
entry->startPos = pos;
pos += size;
BAIL_IF_MACRO(!io->seek(io, pos), NULL, 0); /* skip over entry */
244
245
} /* for */
246
info->entryCount = entCount;
247
248
__PHYSFS_sort(info->entries, entCount, hogEntryCmp, hogEntrySwap);
249
return 1;
250
251
252
} /* hog_load_entries */
253
static void *HOG_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
254
{
255
256
PHYSFS_uint8 buf[3];
HOGinfo *info = NULL;
257
258
assert(io != NULL); /* shouldn't ever happen. */
259
260
BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
261
262
263
264
265
266
267
268
269
270
271
BAIL_IF_MACRO(!readAll(io, buf, 3), NULL, 0);
if (memcmp(buf, "DHF", 3) != 0)
GOTO_MACRO(ERR_NOT_AN_ARCHIVE, HOG_openArchive_failed);
info = (HOGinfo *) allocator.Malloc(sizeof (HOGinfo));
GOTO_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, HOG_openArchive_failed);
memset(info, '\0', sizeof (HOGinfo));
info->io = io;
GOTO_IF_MACRO(!hog_load_entries(io, info), NULL, HOG_openArchive_failed);
272
273
return info;
274
275
HOG_openArchive_failed:
276
if (info != NULL)
277
{
278
if (info->entries != NULL)
279
280
allocator.Free(info->entries);
allocator.Free(info);
281
282
} /* if */
283
return NULL;
284
285
286
} /* HOG_openArchive */
287
static void HOG_enumerateFiles(dvoid *opaque, const char *dname,
288
289
int omitSymLinks, PHYSFS_EnumFilesCallback cb,
const char *origdir, void *callbackdata)
290
291
{
/* no directories in HOG files. */
292
if (*dname == '\0')
293
294
295
296
297
{
HOGinfo *info = (HOGinfo *) opaque;
HOGentry *entry = info->entries;
PHYSFS_uint32 max = info->entryCount;
PHYSFS_uint32 i;
298
299
for (i = 0; i < max; i++, entry++)
300
cb(callbackdata, origdir, entry->name);
301
} /* if */
302
303
304
} /* HOG_enumerateFiles */
305
static HOGentry *hog_find_entry(const HOGinfo *info, const char *name)
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
{
char *ptr = strchr(name, '.');
HOGentry *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)
{
middle = lo + ((hi - lo) / 2);
325
rc = __PHYSFS_stricmpASCII(name, a[middle].name);
326
if (rc == 0) /* found it! */
327
return &a[middle];
328
329
330
331
332
333
334
335
336
337
else if (rc > 0)
lo = middle + 1;
else
hi = middle - 1;
} /* while */
BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
} /* hog_find_entry */
338
static int HOG_exists(dvoid *opaque, const char *name)
339
{
340
return (hog_find_entry((HOGinfo *) opaque, name) != NULL);
341
342
343
} /* HOG_exists */
344
static int HOG_isDirectory(dvoid *opaque, const char *name, int *fileExists)
345
{
346
*fileExists = HOG_exists(opaque, name);
347
return 0; /* never directories in a groupfile. */
348
349
350
} /* HOG_isDirectory */
351
static int HOG_isSymLink(dvoid *opaque, const char *name, int *fileExists)
352
{
353
*fileExists = HOG_exists(opaque, name);
354
return 0; /* never symlinks in a groupfile. */
355
356
357
} /* HOG_isSymLink */
358
static PHYSFS_Io *HOG_openRead(dvoid *opaque, const char *fnm, int *fileExists)
359
{
360
361
PHYSFS_Io *retval = NULL;
HOGinfo *info = (HOGinfo *) opaque;
362
363
364
365
366
HOGfileinfo *finfo;
HOGentry *entry;
entry = hog_find_entry(info, fnm);
*fileExists = (entry != NULL);
367
368
369
370
GOTO_IF_MACRO(entry == NULL, NULL, HOG_openRead_failed);
retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
GOTO_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, HOG_openRead_failed);
371
372
finfo = (HOGfileinfo *) allocator.Malloc(sizeof (HOGfileinfo));
373
374
375
376
GOTO_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, HOG_openRead_failed);
finfo->io = info->io->duplicate(info->io);
GOTO_IF_MACRO(finfo->io == NULL, NULL, HOG_openRead_failed);
377
378
379
380
381
382
383
384
385
386
387
388
389
if (!finfo->io->seek(finfo->io, entry->startPos))
GOTO_MACRO(NULL, HOG_openRead_failed);
finfo->curPos = 0;
finfo->entry = entry;
memcpy(retval, &HOG_Io, sizeof (*retval));
retval->opaque = finfo;
return retval;
HOG_openRead_failed:
if (finfo != NULL)
390
{
391
392
if (finfo->io != NULL)
finfo->io->destroy(finfo->io);
393
allocator.Free(finfo);
394
395
} /* if */
396
397
398
399
if (retval != NULL)
allocator.Free(retval);
return NULL;
400
401
402
} /* HOG_openRead */
403
static PHYSFS_Io *HOG_openWrite(dvoid *opaque, const char *name)
404
405
406
407
408
{
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
} /* HOG_openWrite */
409
static PHYSFS_Io *HOG_openAppend(dvoid *opaque, const char *name)
410
411
412
413
414
{
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
} /* HOG_openAppend */
415
static int HOG_remove(dvoid *opaque, const char *name)
416
417
418
419
420
{
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
} /* HOG_remove */
421
static int HOG_mkdir(dvoid *opaque, const char *name)
422
423
424
425
{
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
} /* HOG_mkdir */
426
427
static int HOG_stat(dvoid *opaque, const char *filename, int *exists,
428
429
430
431
432
433
434
435
436
437
438
PHYSFS_Stat *stat)
{
const HOGinfo *info = (const HOGinfo *) opaque;
const HOGentry *entry = hog_find_entry(info, filename);
*exists = (entry != 0);
if (!entry)
return 0;
stat->filesize = entry->size;
stat->filetype = PHYSFS_FILETYPE_REGULAR;
439
440
stat->modtime = -1;
stat->createtime = -1;
441
442
443
stat->accesstime = -1;
stat->readonly = 1;
444
return 1;
445
446
447
} /* HOG_stat */
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_HOG =
{
"HOG",
HOG_ARCHIVE_DESCRIPTION,
"Bradley Bell <btb@icculus.org>",
"http://icculus.org/physfs/",
};
const PHYSFS_Archiver __PHYSFS_Archiver_HOG =
{
&__PHYSFS_ArchiveInfo_HOG,
HOG_openArchive, /* openArchive() method */
HOG_enumerateFiles, /* enumerateFiles() method */
HOG_exists, /* exists() method */
HOG_isDirectory, /* isDirectory() method */
HOG_isSymLink, /* isSymLink() method */
HOG_openRead, /* openRead() method */
HOG_openWrite, /* openWrite() method */
HOG_openAppend, /* openAppend() method */
HOG_remove, /* remove() method */
HOG_mkdir, /* mkdir() method */
HOG_dirClose, /* dirClose() method */
471
HOG_stat /* stat() method */
472
473
};
474
475
476
#endif /* defined PHYSFS_SUPPORTS_HOG */
/* end of hog.c ... */