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