Initial integration, lots of broken stuff
[supertux.git] / src / unison / physfs-1.1.1 / archivers / grp.c
1 /*
2  * GRP support routines for PhysicsFS.
3  *
4  * This driver handles BUILD engine archives ("groupfiles"). This format
5  *  (but not this driver) was put together by Ken Silverman.
6  *
7  * The format is simple enough. In Ken's words:
8  *
9  *    What's the .GRP file format?
10  *
11  *     The ".grp" file format is just a collection of a lot of files stored
12  *     into 1 big one. I tried to make the format as simple as possible: The
13  *     first 12 bytes contains my name, "KenSilverman". The next 4 bytes is
14  *     the number of files that were compacted into the group file. Then for
15  *     each file, there is a 16 byte structure, where the first 12 bytes are
16  *     the filename, and the last 4 bytes are the file's size. The rest of
17  *     the group file is just the raw data packed one after the other in the
18  *     same order as the list of files.
19  *
20  * (That info is from http://www.advsys.net/ken/build.htm ...)
21  *
22  * Please see the file LICENSE.txt in the source's root directory.
23  *
24  *  This file written by Ryan C. Gordon.
25  */
26
27 #if (defined PHYSFS_SUPPORTS_GRP)
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include "physfs.h"
33
34 #define __PHYSICSFS_INTERNAL__
35 #include "physfs_internal.h"
36
37 typedef struct
38 {
39     char name[13];
40     PHYSFS_uint32 startPos;
41     PHYSFS_uint32 size;
42 } GRPentry;
43
44 typedef struct
45 {
46     char *filename;
47     PHYSFS_sint64 last_mod_time;
48     PHYSFS_uint32 entryCount;
49     GRPentry *entries;
50 } GRPinfo;
51
52 typedef struct
53 {
54     void *handle;
55     GRPentry *entry;
56     PHYSFS_uint32 curPos;
57 } GRPfileinfo;
58
59
60 static void GRP_dirClose(dvoid *opaque)
61 {
62     GRPinfo *info = ((GRPinfo *) opaque);
63     allocator.Free(info->filename);
64     allocator.Free(info->entries);
65     allocator.Free(info);
66 } /* GRP_dirClose */
67
68
69 static PHYSFS_sint64 GRP_read(fvoid *opaque, void *buffer,
70                               PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
71 {
72     GRPfileinfo *finfo = (GRPfileinfo *) opaque;
73     GRPentry *entry = finfo->entry;
74     PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos;
75     PHYSFS_uint32 objsLeft = (bytesLeft / objSize);
76     PHYSFS_sint64 rc;
77
78     if (objsLeft < objCount)
79         objCount = objsLeft;
80
81     rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount);
82     if (rc > 0)
83         finfo->curPos += (PHYSFS_uint32) (rc * objSize);
84
85     return(rc);
86 } /* GRP_read */
87
88
89 static PHYSFS_sint64 GRP_write(fvoid *opaque, const void *buffer,
90                                PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
91 {
92     BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
93 } /* GRP_write */
94
95
96 static int GRP_eof(fvoid *opaque)
97 {
98     GRPfileinfo *finfo = (GRPfileinfo *) opaque;
99     GRPentry *entry = finfo->entry;
100     return(finfo->curPos >= entry->size);
101 } /* GRP_eof */
102
103
104 static PHYSFS_sint64 GRP_tell(fvoid *opaque)
105 {
106     return(((GRPfileinfo *) opaque)->curPos);
107 } /* GRP_tell */
108
109
110 static int GRP_seek(fvoid *opaque, PHYSFS_uint64 offset)
111 {
112     GRPfileinfo *finfo = (GRPfileinfo *) opaque;
113     GRPentry *entry = finfo->entry;
114     int rc;
115
116     BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
117     BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0);
118     rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset);
119     if (rc)
120         finfo->curPos = (PHYSFS_uint32) offset;
121
122     return(rc);
123 } /* GRP_seek */
124
125
126 static PHYSFS_sint64 GRP_fileLength(fvoid *opaque)
127 {
128     GRPfileinfo *finfo = (GRPfileinfo *) opaque;
129     return((PHYSFS_sint64) finfo->entry->size);
130 } /* GRP_fileLength */
131
132
133 static int GRP_fileClose(fvoid *opaque)
134 {
135     GRPfileinfo *finfo = (GRPfileinfo *) opaque;
136     BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
137     allocator.Free(finfo);
138     return(1);
139 } /* GRP_fileClose */
140
141
142 static int grp_open(const char *filename, int forWriting,
143                     void **fh, PHYSFS_uint32 *count)
144 {
145     PHYSFS_uint8 buf[12];
146
147     *fh = NULL;
148     BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
149
150     *fh = __PHYSFS_platformOpenRead(filename);
151     BAIL_IF_MACRO(*fh == NULL, NULL, 0);
152     
153     if (__PHYSFS_platformRead(*fh, buf, 12, 1) != 1)
154         goto openGrp_failed;
155
156     if (memcmp(buf, "KenSilverman", 12) != 0)
157     {
158         __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
159         goto openGrp_failed;
160     } /* if */
161
162     if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1)
163         goto openGrp_failed;
164
165     *count = PHYSFS_swapULE32(*count);
166
167     return(1);
168
169 openGrp_failed:
170     if (*fh != NULL)
171         __PHYSFS_platformClose(*fh);
172
173     *count = -1;
174     *fh = NULL;
175     return(0);
176 } /* grp_open */
177
178
179 static int GRP_isArchive(const char *filename, int forWriting)
180 {
181     void *fh;
182     PHYSFS_uint32 fileCount;
183     int retval = grp_open(filename, forWriting, &fh, &fileCount);
184
185     if (fh != NULL)
186         __PHYSFS_platformClose(fh);
187
188     return(retval);
189 } /* GRP_isArchive */
190
191
192 static int grp_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
193 {
194     GRPentry *a = (GRPentry *) _a;
195     return(strcmp(a[one].name, a[two].name));
196 } /* grp_entry_cmp */
197
198
199 static void grp_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
200 {
201     GRPentry tmp;
202     GRPentry *first = &(((GRPentry *) _a)[one]);
203     GRPentry *second = &(((GRPentry *) _a)[two]);
204     memcpy(&tmp, first, sizeof (GRPentry));
205     memcpy(first, second, sizeof (GRPentry));
206     memcpy(second, &tmp, sizeof (GRPentry));
207 } /* grp_entry_swap */
208
209
210 static int grp_load_entries(const char *name, int forWriting, GRPinfo *info)
211 {
212     void *fh = NULL;
213     PHYSFS_uint32 fileCount;
214     PHYSFS_uint32 location = 16;  /* sizeof sig. */
215     GRPentry *entry;
216     char *ptr;
217
218     BAIL_IF_MACRO(!grp_open(name, forWriting, &fh, &fileCount), NULL, 0);
219     info->entryCount = fileCount;
220     info->entries = (GRPentry *) allocator.Malloc(sizeof(GRPentry)*fileCount);
221     if (info->entries == NULL)
222     {
223         __PHYSFS_platformClose(fh);
224         BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
225     } /* if */
226
227     location += (16 * fileCount);
228
229     for (entry = info->entries; fileCount > 0; fileCount--, entry++)
230     {
231         if (__PHYSFS_platformRead(fh, &entry->name, 12, 1) != 1)
232         {
233             __PHYSFS_platformClose(fh);
234             return(0);
235         } /* if */
236
237         entry->name[12] = '\0';  /* name isn't null-terminated in file. */
238         if ((ptr = strchr(entry->name, ' ')) != NULL)
239             *ptr = '\0';  /* trim extra spaces. */
240
241         if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1)
242         {
243             __PHYSFS_platformClose(fh);
244             return(0);
245         } /* if */
246
247         entry->size = PHYSFS_swapULE32(entry->size);
248         entry->startPos = location;
249         location += entry->size;
250     } /* for */
251
252     __PHYSFS_platformClose(fh);
253
254     __PHYSFS_sort(info->entries, info->entryCount,
255                   grp_entry_cmp, grp_entry_swap);
256     return(1);
257 } /* grp_load_entries */
258
259
260 static void *GRP_openArchive(const char *name, int forWriting)
261 {
262     PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name);
263     GRPinfo *info = (GRPinfo *) allocator.Malloc(sizeof (GRPinfo));
264
265     BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, 0);
266
267     memset(info, '\0', sizeof (GRPinfo));
268     info->filename = (char *) allocator.Malloc(strlen(name) + 1);
269     GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, GRP_openArchive_failed);
270
271     if (!grp_load_entries(name, forWriting, info))
272         goto GRP_openArchive_failed;
273
274     strcpy(info->filename, name);
275     info->last_mod_time = modtime;
276
277     return(info);
278
279 GRP_openArchive_failed:
280     if (info != NULL)
281     {
282         if (info->filename != NULL)
283             allocator.Free(info->filename);
284         if (info->entries != NULL)
285             allocator.Free(info->entries);
286         allocator.Free(info);
287     } /* if */
288
289     return(NULL);
290 } /* GRP_openArchive */
291
292
293 static void GRP_enumerateFiles(dvoid *opaque, const char *dname,
294                                int omitSymLinks, PHYSFS_EnumFilesCallback cb,
295                                const char *origdir, void *callbackdata)
296 {
297     /* no directories in GRP files. */
298     if (*dname == '\0')
299     {
300         GRPinfo *info = (GRPinfo *) opaque;
301         GRPentry *entry = info->entries;
302         PHYSFS_uint32 max = info->entryCount;
303         PHYSFS_uint32 i;
304
305         for (i = 0; i < max; i++, entry++)
306             cb(callbackdata, origdir, entry->name);
307     } /* if */
308 } /* GRP_enumerateFiles */
309
310
311 static GRPentry *grp_find_entry(GRPinfo *info, const char *name)
312 {
313     char *ptr = strchr(name, '.');
314     GRPentry *a = info->entries;
315     PHYSFS_sint32 lo = 0;
316     PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
317     PHYSFS_sint32 middle;
318     int rc;
319
320     /*
321      * Rule out filenames to avoid unneeded processing...no dirs,
322      *   big filenames, or extensions > 3 chars.
323      */
324     BAIL_IF_MACRO((ptr) && (strlen(ptr) > 4), ERR_NO_SUCH_FILE, NULL);
325     BAIL_IF_MACRO(strlen(name) > 12, ERR_NO_SUCH_FILE, NULL);
326     BAIL_IF_MACRO(strchr(name, '/') != NULL, ERR_NO_SUCH_FILE, NULL);
327
328     while (lo <= hi)
329     {
330         middle = lo + ((hi - lo) / 2);
331         rc = strcmp(name, a[middle].name);
332         if (rc == 0)  /* found it! */
333             return(&a[middle]);
334         else if (rc > 0)
335             lo = middle + 1;
336         else
337             hi = middle - 1;
338     } /* while */
339
340     BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
341 } /* grp_find_entry */
342
343
344 static int GRP_exists(dvoid *opaque, const char *name)
345 {
346     return(grp_find_entry((GRPinfo *) opaque, name) != NULL);
347 } /* GRP_exists */
348
349
350 static int GRP_isDirectory(dvoid *opaque, const char *name, int *fileExists)
351 {
352     *fileExists = GRP_exists(opaque, name);
353     return(0);  /* never directories in a groupfile. */
354 } /* GRP_isDirectory */
355
356
357 static int GRP_isSymLink(dvoid *opaque, const char *name, int *fileExists)
358 {
359     *fileExists = GRP_exists(opaque, name);
360     return(0);  /* never symlinks in a groupfile. */
361 } /* GRP_isSymLink */
362
363
364 static PHYSFS_sint64 GRP_getLastModTime(dvoid *opaque,
365                                         const char *name,
366                                         int *fileExists)
367 {
368     GRPinfo *info = (GRPinfo *) opaque;
369     PHYSFS_sint64 retval = -1;
370
371     *fileExists = (grp_find_entry(info, name) != NULL);
372     if (*fileExists)  /* use time of GRP itself in the physical filesystem. */
373         retval = info->last_mod_time;
374
375     return(retval);
376 } /* GRP_getLastModTime */
377
378
379 static fvoid *GRP_openRead(dvoid *opaque, const char *fnm, int *fileExists)
380 {
381     GRPinfo *info = (GRPinfo *) opaque;
382     GRPfileinfo *finfo;
383     GRPentry *entry;
384
385     entry = grp_find_entry(info, fnm);
386     *fileExists = (entry != NULL);
387     BAIL_IF_MACRO(entry == NULL, NULL, NULL);
388
389     finfo = (GRPfileinfo *) allocator.Malloc(sizeof (GRPfileinfo));
390     BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
391
392     finfo->handle = __PHYSFS_platformOpenRead(info->filename);
393     if ( (finfo->handle == NULL) ||
394          (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
395     {
396         allocator.Free(finfo);
397         return(NULL);
398     } /* if */
399
400     finfo->curPos = 0;
401     finfo->entry = entry;
402     return(finfo);
403 } /* GRP_openRead */
404
405
406 static fvoid *GRP_openWrite(dvoid *opaque, const char *name)
407 {
408     BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
409 } /* GRP_openWrite */
410
411
412 static fvoid *GRP_openAppend(dvoid *opaque, const char *name)
413 {
414     BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
415 } /* GRP_openAppend */
416
417
418 static int GRP_remove(dvoid *opaque, const char *name)
419 {
420     BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
421 } /* GRP_remove */
422
423
424 static int GRP_mkdir(dvoid *opaque, const char *name)
425 {
426     BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
427 } /* GRP_mkdir */
428
429
430 const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_GRP =
431 {
432     "GRP",
433     GRP_ARCHIVE_DESCRIPTION,
434     "Ryan C. Gordon <icculus@icculus.org>",
435     "http://icculus.org/physfs/",
436 };
437
438
439 const PHYSFS_Archiver __PHYSFS_Archiver_GRP =
440 {
441     &__PHYSFS_ArchiveInfo_GRP,
442     GRP_isArchive,          /* isArchive() method      */
443     GRP_openArchive,        /* openArchive() method    */
444     GRP_enumerateFiles,     /* enumerateFiles() method */
445     GRP_exists,             /* exists() method         */
446     GRP_isDirectory,        /* isDirectory() method    */
447     GRP_isSymLink,          /* isSymLink() method      */
448     GRP_getLastModTime,     /* getLastModTime() method */
449     GRP_openRead,           /* openRead() method       */
450     GRP_openWrite,          /* openWrite() method      */
451     GRP_openAppend,         /* openAppend() method     */
452     GRP_remove,             /* remove() method         */
453     GRP_mkdir,              /* mkdir() method          */
454     GRP_dirClose,           /* dirClose() method       */
455     GRP_read,               /* read() method           */
456     GRP_write,              /* write() method          */
457     GRP_eof,                /* eof() method            */
458     GRP_tell,               /* tell() method           */
459     GRP_seek,               /* seek() method           */
460     GRP_fileLength,         /* fileLength() method     */
461     GRP_fileClose           /* fileClose() method      */
462 };
463
464 #endif  /* defined PHYSFS_SUPPORTS_GRP */
465
466 /* end of grp.c ... */
467