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