/
archiver_mvl.c
458 lines (357 loc) · 11.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
* MVL support routines for PhysicsFS.
*
* This driver handles Descent II Movielib archives.
*
* The file format of MVL is quite easy...
*
* //MVL File format - Written by Heiko Herrmann
* char sig[4] = {'D','M', 'V', 'L'}; // "DMVL"=Descent MoVie Library
*
* int num_files; // the number of files in this MVL
*
* struct {
* char file_name[13]; // Filename, padded to 13 bytes with 0s
* int file_size; // filesize in bytes
* }DIR_STRUCT[num_files];
*
* struct {
* char data[file_size]; // The file data
* }FILE_STRUCT[num_files];
*
* (That info is from http://www.descent2.com/ddn/specs/mvl/)
*
24
* Please see the file LICENSE.txt in the source's root directory.
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
*
* This file written by Bradley Bell.
* Based on grp.c by Ryan C. Gordon.
*/
#if (defined PHYSFS_SUPPORTS_MVL)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "physfs.h"
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
typedef struct
{
char name[13];
PHYSFS_uint32 startPos;
PHYSFS_uint32 size;
} MVLentry;
typedef struct
{
49
PHYSFS_Io *io;
50
51
52
53
54
55
PHYSFS_uint32 entryCount;
MVLentry *entries;
} MVLinfo;
typedef struct
{
56
PHYSFS_Io *io;
57
58
59
60
61
MVLentry *entry;
PHYSFS_uint32 curPos;
} MVLfileinfo;
62
63
64
65
66
67
static inline int readAll(PHYSFS_Io *io, void *buf, const PHYSFS_uint64 len)
{
return (io->read(io, buf, len) == len);
} /* readAll */
68
static void MVL_dirClose(dvoid *opaque)
69
{
70
MVLinfo *info = ((MVLinfo *) opaque);
71
info->io->destroy(info->io);
72
73
allocator.Free(info->entries);
allocator.Free(info);
74
75
76
} /* MVL_dirClose */
77
static PHYSFS_sint64 MVL_read(PHYSFS_Io *io, void *buffer, PHYSFS_uint64 len)
78
{
79
MVLfileinfo *finfo = (MVLfileinfo *) io->opaque;
80
81
const MVLentry *entry = finfo->entry;
const PHYSFS_uint64 bytesLeft = (PHYSFS_uint64)(entry->size-finfo->curPos);
82
83
PHYSFS_sint64 rc;
84
85
if (bytesLeft < len)
len = bytesLeft;
86
87
rc = finfo->io->read(finfo->io, buffer, len);
88
if (rc > 0)
89
finfo->curPos += (PHYSFS_uint32) rc;
90
91
return rc;
92
93
94
} /* MVL_read */
95
static PHYSFS_sint64 MVL_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
96
97
98
99
100
{
BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
} /* MVL_write */
101
static PHYSFS_sint64 MVL_tell(PHYSFS_Io *io)
102
{
103
return ((MVLfileinfo *) io->opaque)->curPos;
104
105
106
} /* MVL_tell */
107
static int MVL_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
108
{
109
110
MVLfileinfo *finfo = (MVLfileinfo *) io->opaque;
const MVLentry *entry = finfo->entry;
111
112
113
int rc;
BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0);
114
rc = finfo->io->seek(finfo->io, entry->startPos + offset);
115
116
117
if (rc)
finfo->curPos = (PHYSFS_uint32) offset;
118
return rc;
119
120
121
} /* MVL_seek */
122
static PHYSFS_sint64 MVL_length(PHYSFS_Io *io)
123
{
124
const MVLfileinfo *finfo = (MVLfileinfo *) io->opaque;
125
return ((PHYSFS_sint64) finfo->entry->size);
126
} /* MVL_length */
127
128
129
static PHYSFS_Io *MVL_duplicate(PHYSFS_Io *_io)
130
{
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
MVLfileinfo *origfinfo = (MVLfileinfo *) _io->opaque;
PHYSFS_Io *io = NULL;
PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
MVLfileinfo *finfo = (MVLfileinfo *) allocator.Malloc(sizeof (MVLfileinfo));
GOTO_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, MVL_duplicate_failed);
GOTO_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, MVL_duplicate_failed);
io = origfinfo->io->duplicate(origfinfo->io);
GOTO_IF_MACRO(io == NULL, NULL, MVL_duplicate_failed);
finfo->io = io;
finfo->entry = origfinfo->entry;
finfo->curPos = 0;
memcpy(retval, _io, sizeof (PHYSFS_Io));
retval->opaque = finfo;
return retval;
MVL_duplicate_failed:
if (finfo != NULL) allocator.Free(finfo);
if (retval != NULL) allocator.Free(retval);
if (io != NULL) io->destroy(io);
return NULL;
} /* MVL_duplicate */
153
154
static int MVL_flush(PHYSFS_Io *io) { return 1; /* no write support. */ }
155
156
static void MVL_destroy(PHYSFS_Io *io)
157
{
158
159
160
161
162
MVLfileinfo *finfo = (MVLfileinfo *) io->opaque;
finfo->io->destroy(finfo->io);
allocator.Free(finfo);
allocator.Free(io);
} /* MVL_destroy */
163
164
165
166
167
168
169
170
171
172
173
174
175
176
static const PHYSFS_Io MVL_Io =
{
MVL_read,
MVL_write,
MVL_seek,
MVL_tell,
MVL_length,
MVL_duplicate,
MVL_flush,
MVL_destroy,
NULL
};
177
178
179
static int mvlEntryCmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
180
{
181
182
183
if (one != two)
{
const MVLentry *a = (const MVLentry *) _a;
184
return __PHYSFS_stricmpASCII(a[one].name, a[two].name);
185
186
187
} /* if */
return 0;
188
} /* mvlEntryCmp */
189
190
191
static void mvlEntrySwap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
192
{
193
194
195
196
197
198
199
200
201
if (one != two)
{
MVLentry tmp;
MVLentry *first = &(((MVLentry *) _a)[one]);
MVLentry *second = &(((MVLentry *) _a)[two]);
memcpy(&tmp, first, sizeof (MVLentry));
memcpy(first, second, sizeof (MVLentry));
memcpy(second, &tmp, sizeof (MVLentry));
} /* if */
202
} /* mvlEntrySwap */
203
204
205
static int mvl_load_entries(PHYSFS_Io *io, MVLinfo *info)
206
{
207
PHYSFS_uint32 fileCount = info->entryCount;
208
209
210
PHYSFS_uint32 location = 8; /* sizeof sig. */
MVLentry *entry;
211
info->entries = (MVLentry *) allocator.Malloc(sizeof(MVLentry)*fileCount);
212
BAIL_IF_MACRO(info->entries == NULL, ERR_OUT_OF_MEMORY, 0);
213
214
215
216
217
location += (17 * fileCount);
for (entry = info->entries; fileCount > 0; fileCount--, entry++)
{
218
219
BAIL_IF_MACRO(!readAll(io, &entry->name, 13), NULL, 0);
BAIL_IF_MACRO(!readAll(io, &entry->size, 4), NULL, 0);
220
221
222
223
224
entry->size = PHYSFS_swapULE32(entry->size);
entry->startPos = location;
location += entry->size;
} /* for */
225
__PHYSFS_sort(info->entries, info->entryCount, mvlEntryCmp, mvlEntrySwap);
226
return 1;
227
228
229
} /* mvl_load_entries */
230
static void *MVL_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
231
{
232
233
234
PHYSFS_uint8 buf[4];
MVLinfo *info = NULL;
PHYSFS_uint32 val = 0;
235
236
237
238
239
240
241
242
243
244
245
assert(io != NULL); /* shouldn't ever happen. */
BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
BAIL_IF_MACRO(!readAll(io, buf, 4), NULL, NULL);
if (memcmp(buf, "DMVL", 4) != 0)
GOTO_MACRO(ERR_NOT_AN_ARCHIVE, MVL_openArchive_failed);
info = (MVLinfo *) allocator.Malloc(sizeof (MVLinfo));
GOTO_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, MVL_openArchive_failed);
246
memset(info, '\0', sizeof (MVLinfo));
247
248
249
250
info->io = io;
GOTO_IF_MACRO(!readAll(io,&val,sizeof(val)), NULL, MVL_openArchive_failed);
info->entryCount = PHYSFS_swapULE32(val);
251
252
GOTO_IF_MACRO(!mvl_load_entries(io, info), NULL, MVL_openArchive_failed);
253
254
return info;
255
256
MVL_openArchive_failed:
257
if (info != NULL)
258
{
259
if (info->entries != NULL)
260
261
allocator.Free(info->entries);
allocator.Free(info);
262
263
} /* if */
264
return NULL;
265
266
267
} /* MVL_openArchive */
268
static void MVL_enumerateFiles(dvoid *opaque, const char *dname,
269
270
int omitSymLinks, PHYSFS_EnumFilesCallback cb,
const char *origdir, void *callbackdata)
271
272
{
/* no directories in MVL files. */
273
if (*dname == '\0')
274
275
276
277
278
{
MVLinfo *info = ((MVLinfo *) opaque);
MVLentry *entry = info->entries;
PHYSFS_uint32 max = info->entryCount;
PHYSFS_uint32 i;
279
280
for (i = 0; i < max; i++, entry++)
281
cb(callbackdata, origdir, entry->name);
282
} /* if */
283
284
285
} /* MVL_enumerateFiles */
286
static MVLentry *mvl_find_entry(const MVLinfo *info, const char *name)
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
{
char *ptr = strchr(name, '.');
MVLentry *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);
306
rc = __PHYSFS_stricmpASCII(name, a[middle].name);
307
if (rc == 0) /* found it! */
308
return &a[middle];
309
310
311
312
313
314
315
316
317
318
else if (rc > 0)
lo = middle + 1;
else
hi = middle - 1;
} /* while */
BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
} /* mvl_find_entry */
319
static int MVL_exists(dvoid *opaque, const char *name)
320
{
321
return (mvl_find_entry((MVLinfo *) opaque, name) != NULL);
322
323
324
} /* MVL_exists */
325
static int MVL_isDirectory(dvoid *opaque, const char *name, int *fileExists)
326
{
327
*fileExists = MVL_exists(opaque, name);
328
return 0; /* never directories in a groupfile. */
329
330
331
} /* MVL_isDirectory */
332
static int MVL_isSymLink(dvoid *opaque, const char *name, int *fileExists)
333
{
334
*fileExists = MVL_exists(opaque, name);
335
return 0; /* never symlinks in a groupfile. */
336
337
338
} /* MVL_isSymLink */
339
static PHYSFS_Io *MVL_openRead(dvoid *opaque, const char *fnm, int *fileExists)
340
{
341
342
PHYSFS_Io *retval = NULL;
MVLinfo *info = (MVLinfo *) opaque;
343
344
345
346
347
MVLfileinfo *finfo;
MVLentry *entry;
entry = mvl_find_entry(info, fnm);
*fileExists = (entry != NULL);
348
349
350
351
GOTO_IF_MACRO(entry == NULL, NULL, MVL_openRead_failed);
retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
GOTO_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, MVL_openRead_failed);
352
353
finfo = (MVLfileinfo *) allocator.Malloc(sizeof (MVLfileinfo));
354
355
356
357
358
359
360
361
362
363
364
365
366
367
GOTO_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, MVL_openRead_failed);
finfo->io = info->io->duplicate(info->io);
GOTO_IF_MACRO(finfo->io == NULL, NULL, MVL_openRead_failed);
if (!finfo->io->seek(finfo->io, entry->startPos))
GOTO_MACRO(NULL, MVL_openRead_failed);
finfo->curPos = 0;
finfo->entry = entry;
memcpy(retval, &MVL_Io, sizeof (*retval));
retval->opaque = finfo;
return retval;
368
369
370
MVL_openRead_failed:
if (finfo != NULL)
371
{
372
373
if (finfo->io != NULL)
finfo->io->destroy(finfo->io);
374
allocator.Free(finfo);
375
376
} /* if */
377
378
379
380
if (retval != NULL)
allocator.Free(retval);
return NULL;
381
382
383
} /* MVL_openRead */
384
static PHYSFS_Io *MVL_openWrite(dvoid *opaque, const char *name)
385
386
387
388
389
{
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
} /* MVL_openWrite */
390
static PHYSFS_Io *MVL_openAppend(dvoid *opaque, const char *name)
391
392
393
394
395
{
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
} /* MVL_openAppend */
396
static int MVL_remove(dvoid *opaque, const char *name)
397
398
399
400
401
{
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
} /* MVL_remove */
402
static int MVL_mkdir(dvoid *opaque, const char *name)
403
404
405
406
{
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
} /* MVL_mkdir */
407
408
static int MVL_stat(dvoid *opaque, const char *filename, int *exists,
409
410
411
412
413
414
415
416
417
418
419
PHYSFS_Stat *stat)
{
const MVLinfo *info = (const MVLinfo *) opaque;
const MVLentry *entry = mvl_find_entry(info, filename);
*exists = (entry != 0);
if (!entry)
return 0;
stat->filesize = entry->size;
stat->filetype = PHYSFS_FILETYPE_REGULAR;
420
421
422
stat->modtime = -1;
stat->createtime = -1;
stat->accesstime = -1;
423
424
stat->readonly = 1;
425
return 1;
426
427
428
} /* MVL_stat */
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_MVL =
{
"MVL",
MVL_ARCHIVE_DESCRIPTION,
"Bradley Bell <btb@icculus.org>",
"http://icculus.org/physfs/",
};
const PHYSFS_Archiver __PHYSFS_Archiver_MVL =
{
&__PHYSFS_ArchiveInfo_MVL,
MVL_openArchive, /* openArchive() method */
MVL_enumerateFiles, /* enumerateFiles() method */
MVL_exists, /* exists() method */
MVL_isDirectory, /* isDirectory() method */
MVL_isSymLink, /* isSymLink() method */
MVL_openRead, /* openRead() method */
MVL_openWrite, /* openWrite() method */
MVL_openAppend, /* openAppend() method */
MVL_remove, /* remove() method */
MVL_mkdir, /* mkdir() method */
MVL_dirClose, /* dirClose() method */
452
MVL_stat /* stat() method */
453
454
};
455
456
457
#endif /* defined PHYSFS_SUPPORTS_MVL */
/* end of mvl.c ... */