/
mix.c
452 lines (358 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
24
25
26
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/*
* MIX support routines for PhysicsFS.
*
* This driver handles old archives used in the famous games
* Command&Conquer Tiberium Dawn and Command&Conquer Red Alert.
*
* Newer MIX files as they are used in C&C Tiberium Sun and C&C Red Alert 2
* aren't supported yet. Keep your eyes open for future updates.
*
* A MIX file has three parts:
* (1) Header
* 16bit integer -> number of files stored in this MIX
* 32bit integer -> filesize
* (2) "Directory"
* 32bit integer -> hash of the filename
* 32bit integer -> starting offset in the MIX
* 32bit integer -> end offset in the MIX
* (3) Data (BODY)
* All data comes here
*
* NOTES:
* The offsets are relative to the body. So offset 0 is directly after
* the directory.
*
* Filenames only exist as hashes. So enumerate_files() will only report all
* hashes. Searching a filename in hashes is extremly quick so I decided not
* to include any sorting routines after then opening of the archive.
*
*
* I found the structure of MIX files here:
* http://www.geocities.com/SiliconValley/8682/cncmap1f.txt
*
*
* Please see the file LICENSE in the source's root directory.
*
* This file written by Sebastian Steinhauer <steini@steini-welt.de>
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#if (defined PHYSFS_SUPPORTS_MIX)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "physfs.h"
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
typedef struct
{
PHYSFS_uint16 num_files;
PHYSFS_uint32 filesize;
} MIXheader;
typedef struct
{
PHYSFS_uint32 hash;
PHYSFS_uint32 start_offset;
PHYSFS_uint32 end_offset;
} MIXentry;
typedef struct
{
char *filename; /* filename of the archive */
MIXentry *entry; /* list of entries */
MIXheader header; /* the header of the MIX file */
PHYSFS_uint32 delta; /* size of header + entries */
} MIXinfo;
typedef struct
{
PHYSFS_uint64 size; /* filesize */
PHYSFS_uint64 cur_pos; /* position in this file */
MIXentry *entry; /* pointer to the MIX entry */
MIXinfo *info; /* pointer to our MIXinfo */
void *handle; /* filehandle */
} MIXfileinfo;
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
112
113
114
115
static PHYSFS_uint32 MIX_hash(const char *name)
{
PHYSFS_uint32 id = 0;
PHYSFS_uint32 a = 0;
PHYSFS_uint32 i = 0;
PHYSFS_uint32 l;
PHYSFS_uint32 j;
l = strlen(name);
while (i < l)
{
a = 0;
for(j = 0; j < 4; j++)
{
a >>= 8;
if (i < l)
{
a += (unsigned int) (name[i]) << 24;
i++;
} /* if */
} /* for */
id = (id << 1 | id >> 31) + a;
} /* while */
/* a bit debuggin :)
/printf("Filename %s -> %X\n",name,id); */
return(id);
} /* MIX_hash */
116
static void MIX_dirClose(dvoid *opaque)
117
{
118
MIXinfo *info = ((MIXinfo *) opaque);
119
120
allocator.Free(info->entry);
allocator.Free(info->filename);
121
122
123
} /* MIX_dirClose */
124
static PHYSFS_sint64 MIX_read(fvoid *opaque, void *buffer,
125
126
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
{
127
MIXfileinfo *finfo = (MIXfileinfo *) opaque;
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
MIXentry *entry = finfo->entry;
PHYSFS_uint32 read;
/* set position in the archive */
__PHYSFS_platformSeek(finfo->handle,
finfo->info->delta +
entry->start_offset +
finfo->cur_pos);
/* read n bytes */
read = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount);
/* keep filepointer up to date */
if (read)
finfo->cur_pos += read * objSize;
return(read);
} /* MIX_read */
148
static PHYSFS_sint64 MIX_write(fvoid *opaque, const void *buffer,
149
150
151
152
153
154
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
{
BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
} /* MIX_write */
155
static int MIX_eof(fvoid *opaque)
156
{
157
MIXfileinfo *fifo = (MIXfileinfo *) opaque;
158
159
160
161
return(fifo->cur_pos >= fifo->size);
} /* MIX_eof */
162
static PHYSFS_sint64 MIX_tell(fvoid *opaque)
163
{
164
return(((MIXfileinfo *) opaque)->cur_pos);
165
166
167
} /* MIX_tell */
168
static int MIX_seek(fvoid *opaque, PHYSFS_uint64 offset)
169
{
170
MIXfileinfo *h = (MIXfileinfo *) opaque;
171
172
173
174
175
176
177
178
BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
BAIL_IF_MACRO(offset >= h->size, ERR_PAST_EOF, 0);
h->cur_pos = offset;
return(1);
} /* MIX_seek */
179
static PHYSFS_sint64 MIX_fileLength(fvoid *opaque)
180
{
181
return (((MIXfileinfo *) opaque)->size);
182
183
184
} /* MIX_fileLength */
185
static int MIX_fileClose(fvoid *opaque)
186
{
187
MIXfileinfo *finfo = (MIXfileinfo *) opaque;
188
__PHYSFS_platformClose(finfo->handle);
189
allocator.Free(finfo);
190
191
192
193
194
195
return(1);
} /* MIX_fileClose */
static int MIX_isArchive(const char *filename, int forWriting)
{
196
/* !!! FIXME:
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
write a simple detection routine for MIX files.
Unfortunaly MIX files have no ID in the header.
*/
return(1);
} /* MIX_isArchive */
/*
* Read an unsigned 32-bit int and swap to native byte order.
*/
static int readui32(void *in, PHYSFS_uint32 *val)
{
PHYSFS_uint32 v;
BAIL_IF_MACRO(__PHYSFS_platformRead(in, &v, sizeof (v), 1) != 1, NULL, 0);
*val = PHYSFS_swapULE32(v);
return(1);
} /* readui32 */
/*
* Read an unsigned 16-bit int and swap to native byte order.
*/
static int readui16(void *in, PHYSFS_uint16 *val)
{
PHYSFS_uint16 v;
BAIL_IF_MACRO(__PHYSFS_platformRead(in, &v, sizeof (v), 1) != 1, NULL, 0);
*val = PHYSFS_swapULE16(v);
return(1);
} /* readui16 */
228
static void *MIX_openArchive(const char *name, int forWriting)
229
{
230
231
232
PHYSFS_uint32 i = 0;
MIXinfo *info = NULL;
void *handle = NULL;
233
234
info = (MIXinfo *) allocator.Malloc(sizeof (MIXinfo));
235
236
BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, 0);
memset(info, '\0', sizeof (MIXinfo));
237
238
info->filename = (char *) allocator.Malloc(strlen(name) + 1);
239
240
GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, MIX_openArchive_failed);
241
/* store filename */
242
strcpy(info->filename, name);
243
244
245
246
/* open the file */
handle = __PHYSFS_platformOpenRead(name);
if (!handle)
247
248
goto MIX_openArchive_failed;
249
/* read the MIX header */
250
251
252
253
254
if ( (!readui16(handle, &info->header.num_files)) ||
(!readui32(handle, &info->header.filesize)) )
goto MIX_openArchive_failed;
info->delta = 6 + (info->header.num_files * 12);
255
256
/* allocate space for the entries and read the entries */
257
info->entry = allocator.Malloc(sizeof (MIXentry) * info->header.num_files);
258
259
GOTO_IF_MACRO(!info->entry, ERR_OUT_OF_MEMORY, MIX_openArchive_failed);
260
261
262
/* read the directory list */
for (i = 0; i < header.num_files; i++)
{
263
264
265
266
if ( (!readui32(handle, &info->entry[i].hash)) ||
(!readui32(handle, &info->entry[i].start_offset)) ||
(!readui32(handle, &info->entry[i].end_offset)) )
goto MIX_openArchive_failed;
267
268
269
270
} /* for */
__PHYSFS_platformClose(handle);
271
272
273
274
275
276
return(info);
MIX_openArchive_failed:
if (info != NULL)
{
if (info->filename != NULL)
277
allocator.Free(info->filename);
278
if (info->entry != NULL)
279
280
allocator.Free(info->entry);
allocator.Free(info);
281
282
283
284
285
286
} /* if */
if (handle != NULL)
__PHYSFS_platformClose(handle);
return(NULL);
287
288
289
} /* MIX_openArchive */
290
static void MIX_enumerateFiles(dvoid *opaque, const char *dname,
291
292
int omitSymLinks, PHYSFS_EnumFilesCallback cb,
const char *origdir, void *callbackdata)
293
{
294
295
/* no directories in MIX files. */
if (*dirname != '\0')
296
{
297
298
299
300
301
302
303
304
MIXinfo *info = (MIXinfo*) opaque;
MIXentry *entry = info->entry;
int i;
char buffer[32];
for (i = 0; i < info->header.num_files; i++, entry++)
{
sprintf(buffer, "%X", entry->hash);
305
cb(callbackdata, origdir, buffer);
306
307
} /* for */
} /* if */
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
} /* MIX_enumerateFiles */
static MIXentry *MIX_find_entry(MIXinfo *info, const char *name)
{
MIXentry *entry = info->entry;
PHYSFS_uint32 i, id;
/* create hash */
id = MIX_hash(name);
/* look for this hash */
for (i = 0; i < info->header.num_files; i++, entry++)
{
if (entry->hash == id)
return(entry);
} /* for */
/* nothing found... :( */
return(NULL);
} /* MIX_find_entry */
331
static int MIX_exists(dvoid *opaque, const char *name)
332
{
333
return(MIX_find_entry(((MIXinfo *) opaque), name) != NULL);
334
335
336
} /* MIX_exists */
337
static int MIX_isDirectory(dvoid *opaque, const char *name, int *fileExists)
338
{
339
*fileExists = MIX_exists(opaque, name);
340
341
342
343
return(0); /* never directories in a MIX */
} /* MIX_isDirectory */
344
static int MIX_isSymLink(dvoid *opaque, const char *name, int *fileExists)
345
{
346
*fileExists = MIX_exists(opaque, name);
347
348
349
350
return(0); /* never symlinks in a MIX. */
} /* MIX_isSymLink */
351
static PHYSFS_sint64 MIX_getLastModTime(dvoid *opaque,
352
353
354
const char *name,
int *fileExists)
{
355
BAIL_MACRO(ERR_NOT_SUPPORTED, 0); /* !!! FIXME: return .MIX's modtime. */
356
357
358
} /* MIX_getLastModTime */
359
static fvoid *MIX_openRead(dvoid *opaque, const char *fnm, int *fileExists)
360
{
361
MIXinfo *info = ((MIXinfo*) opaque);
362
363
364
365
366
367
368
369
MIXfileinfo *finfo;
MIXentry *entry;
/* try to find this file */
entry = MIX_find_entry(info,fnm);
BAIL_IF_MACRO(entry == NULL, ERR_NO_SUCH_FILE, NULL);
/* allocate a MIX handle */
370
finfo = (MIXfileinfo *) allocator.Malloc(sizeof (MIXfileinfo));
371
372
373
374
375
376
BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
/* open the archive */
finfo->handle = __PHYSFS_platformOpenRead(info->filename);
if(!finfo->handle)
{
377
allocator.Free(finfo);
378
return(NULL);
379
} /* if */
380
381
382
383
384
385
/* setup structures */
finfo->cur_pos = 0;
finfo->info = info;
finfo->entry = entry;
finfo->size = entry->end_offset - entry->start_offset;
386
387
return(finfo);
388
389
390
} /* MIX_openRead */
391
static fvoid *MIX_openWrite(dvoid *opaque, const char *name)
392
393
394
395
396
{
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
} /* MIX_openWrite */
397
static fvoid *MIX_openAppend(dvoid *opaque, const char *name)
398
399
400
401
402
{
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
} /* MIX_openAppend */
403
static int MIX_remove(dvoid *opaque, const char *name)
404
405
406
407
408
{
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
} /* MIX_remove */
409
static int MIX_mkdir(dvoid *opaque, const char *name)
410
411
412
413
{
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
} /* MIX_mkdir */
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_MIX =
{
"MIX",
"Westwood archive (Tiberian Dawn / Red Alert)",
"Sebastian Steinhauer <steini@steini-welt.de>",
"http://icculus.org/physfs/",
};
const PHYSFS_Archiver __PHYSFS_Archiver_MIX =
{
&__PHYSFS_ArchiveInfo_MIX,
MIX_isArchive, /* isArchive() method */
MIX_openArchive, /* openArchive() method */
MIX_enumerateFiles, /* enumerateFiles() method */
MIX_exists, /* exists() method */
MIX_isDirectory, /* isDirectory() method */
MIX_isSymLink, /* isSymLink() method */
MIX_getLastModTime, /* getLastModTime() method */
MIX_openRead, /* openRead() method */
MIX_openWrite, /* openWrite() method */
MIX_openAppend, /* openAppend() method */
MIX_remove, /* remove() method */
MIX_mkdir, /* mkdir() method */
MIX_dirClose, /* dirClose() method */
MIX_read, /* read() method */
MIX_write, /* write() method */
MIX_eof, /* eof() method */
MIX_tell, /* tell() method */
MIX_seek, /* seek() method */
MIX_fileLength, /* fileLength() method */
MIX_fileClose /* fileClose() method */
};
449
450
451
#endif /* defined PHYSFS_SUPPORTS_MIX */
/* end of mix.c ... */