2 * HOG support routines for PhysicsFS.
4 * This driver handles Descent I/II HOG archives.
6 * The format is very simple:
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
16 * char sig[3] = {'D', 'H', 'F'}; // "DHF"=Descent HOG File
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.
24 * (That info is from http://www.descent2.com/ddn/specs/hog/)
26 * Please see the file LICENSE.txt in the source's root directory.
28 * This file written by Bradley Bell.
29 * Based on grp.c by Ryan C. Gordon.
32 #if (defined PHYSFS_SUPPORTS_HOG)
39 #define __PHYSICSFS_INTERNAL__
40 #include "physfs_internal.h"
43 * One HOGentry is kept for each file in an open HOG archive.
48 PHYSFS_uint32 startPos;
53 * One HOGinfo is kept for each open HOG archive.
58 PHYSFS_sint64 last_mod_time;
59 PHYSFS_uint32 entryCount;
64 * One HOGfileinfo is kept for each open file in a HOG archive.
74 static void HOG_dirClose(dvoid *opaque)
76 HOGinfo *info = ((HOGinfo *) opaque);
77 allocator.Free(info->filename);
78 allocator.Free(info->entries);
83 static PHYSFS_sint64 HOG_read(fvoid *opaque, void *buffer,
84 PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
86 HOGfileinfo *finfo = (HOGfileinfo *) opaque;
87 HOGentry *entry = finfo->entry;
88 PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos;
89 PHYSFS_uint32 objsLeft = (bytesLeft / objSize);
92 if (objsLeft < objCount)
95 rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount);
97 finfo->curPos += (PHYSFS_uint32) (rc * objSize);
103 static PHYSFS_sint64 HOG_write(fvoid *opaque, const void *buffer,
104 PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
106 BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
110 static int HOG_eof(fvoid *opaque)
112 HOGfileinfo *finfo = (HOGfileinfo *) opaque;
113 HOGentry *entry = finfo->entry;
114 return(finfo->curPos >= entry->size);
118 static PHYSFS_sint64 HOG_tell(fvoid *opaque)
120 return(((HOGfileinfo *) opaque)->curPos);
124 static int HOG_seek(fvoid *opaque, PHYSFS_uint64 offset)
126 HOGfileinfo *finfo = (HOGfileinfo *) opaque;
127 HOGentry *entry = finfo->entry;
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);
134 finfo->curPos = (PHYSFS_uint32) offset;
140 static PHYSFS_sint64 HOG_fileLength(fvoid *opaque)
142 HOGfileinfo *finfo = (HOGfileinfo *) opaque;
143 return((PHYSFS_sint64) finfo->entry->size);
144 } /* HOG_fileLength */
147 static int HOG_fileClose(fvoid *opaque)
149 HOGfileinfo *finfo = (HOGfileinfo *) opaque;
150 BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
151 allocator.Free(finfo);
153 } /* HOG_fileClose */
156 static int hog_open(const char *filename, int forWriting,
157 void **fh, PHYSFS_uint32 *count)
159 PHYSFS_uint8 buf[13];
166 BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
168 *fh = __PHYSFS_platformOpenRead(filename);
169 BAIL_IF_MACRO(*fh == NULL, NULL, 0);
171 if (__PHYSFS_platformRead(*fh, buf, 3, 1) != 1)
174 if (memcmp(buf, "DHF", 3) != 0)
176 __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
182 if (__PHYSFS_platformRead(*fh, buf, 13, 1) != 1)
183 break; /* eof here is ok */
185 if (__PHYSFS_platformRead(*fh, &size, 4, 1) != 1)
188 size = PHYSFS_swapULE32(size);
192 /* Skip over entry... */
193 pos = __PHYSFS_platformTell(*fh);
196 if (!__PHYSFS_platformSeek(*fh, pos + size))
200 /* Rewind to start of entries... */
201 if (!__PHYSFS_platformSeek(*fh, 3))
208 __PHYSFS_platformClose(*fh);
216 static int HOG_isArchive(const char *filename, int forWriting)
219 PHYSFS_uint32 fileCount;
220 int retval = hog_open(filename, forWriting, &fh, &fileCount);
223 __PHYSFS_platformClose(fh);
226 } /* HOG_isArchive */
229 static int hog_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
231 HOGentry *a = (HOGentry *) _a;
232 return(__PHYSFS_stricmpASCII(a[one].name, a[two].name));
233 } /* hog_entry_cmp */
236 static void hog_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
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 */
247 static int hog_load_entries(const char *name, int forWriting, HOGinfo *info)
250 PHYSFS_uint32 fileCount;
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)
258 __PHYSFS_platformClose(fh);
259 BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
262 for (entry = info->entries; fileCount > 0; fileCount--, entry++)
264 if (__PHYSFS_platformRead(fh, &entry->name, 13, 1) != 1)
266 __PHYSFS_platformClose(fh);
270 if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1)
272 __PHYSFS_platformClose(fh);
276 entry->size = PHYSFS_swapULE32(entry->size);
277 entry->startPos = (unsigned int) __PHYSFS_platformTell(fh);
278 if (entry->startPos == -1)
280 __PHYSFS_platformClose(fh);
284 /* Skip over entry */
285 if (!__PHYSFS_platformSeek(fh, entry->startPos + entry->size))
287 __PHYSFS_platformClose(fh);
292 __PHYSFS_platformClose(fh);
294 __PHYSFS_sort(info->entries, info->entryCount,
295 hog_entry_cmp, hog_entry_swap);
297 } /* hog_load_entries */
300 static void *HOG_openArchive(const char *name, int forWriting)
302 PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name);
303 HOGinfo *info = (HOGinfo *) allocator.Malloc(sizeof (HOGinfo));
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);
310 if (!hog_load_entries(name, forWriting, info))
311 goto HOG_openArchive_failed;
313 strcpy(info->filename, name);
314 info->last_mod_time = modtime;
318 HOG_openArchive_failed:
321 if (info->filename != NULL)
322 allocator.Free(info->filename);
323 if (info->entries != NULL)
324 allocator.Free(info->entries);
325 allocator.Free(info);
329 } /* HOG_openArchive */
332 static void HOG_enumerateFiles(dvoid *opaque, const char *dname,
333 int omitSymLinks, PHYSFS_EnumFilesCallback cb,
334 const char *origdir, void *callbackdata)
336 /* no directories in HOG files. */
339 HOGinfo *info = (HOGinfo *) opaque;
340 HOGentry *entry = info->entries;
341 PHYSFS_uint32 max = info->entryCount;
344 for (i = 0; i < max; i++, entry++)
345 cb(callbackdata, origdir, entry->name);
347 } /* HOG_enumerateFiles */
350 static HOGentry *hog_find_entry(HOGinfo *info, const char *name)
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;
360 * Rule out filenames to avoid unneeded processing...no dirs,
361 * big filenames, or extensions > 3 chars.
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);
369 middle = lo + ((hi - lo) / 2);
370 rc = __PHYSFS_stricmpASCII(name, a[middle].name);
371 if (rc == 0) /* found it! */
379 BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
380 } /* hog_find_entry */
383 static int HOG_exists(dvoid *opaque, const char *name)
385 return(hog_find_entry(((HOGinfo *) opaque), name) != NULL);
389 static int HOG_isDirectory(dvoid *opaque, const char *name, int *fileExists)
391 *fileExists = HOG_exists(opaque, name);
392 return(0); /* never directories in a groupfile. */
393 } /* HOG_isDirectory */
396 static int HOG_isSymLink(dvoid *opaque, const char *name, int *fileExists)
398 *fileExists = HOG_exists(opaque, name);
399 return(0); /* never symlinks in a groupfile. */
400 } /* HOG_isSymLink */
403 static PHYSFS_sint64 HOG_getLastModTime(dvoid *opaque,
407 HOGinfo *info = ((HOGinfo *) opaque);
408 PHYSFS_sint64 retval = -1;
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;
415 } /* HOG_getLastModTime */
418 static fvoid *HOG_openRead(dvoid *opaque, const char *fnm, int *fileExists)
420 HOGinfo *info = ((HOGinfo *) opaque);
424 entry = hog_find_entry(info, fnm);
425 *fileExists = (entry != NULL);
426 BAIL_IF_MACRO(entry == NULL, NULL, NULL);
428 finfo = (HOGfileinfo *) allocator.Malloc(sizeof (HOGfileinfo));
429 BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
431 finfo->handle = __PHYSFS_platformOpenRead(info->filename);
432 if ( (finfo->handle == NULL) ||
433 (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
435 allocator.Free(finfo);
440 finfo->entry = entry;
445 static fvoid *HOG_openWrite(dvoid *opaque, const char *name)
447 BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
448 } /* HOG_openWrite */
451 static fvoid *HOG_openAppend(dvoid *opaque, const char *name)
453 BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
454 } /* HOG_openAppend */
457 static int HOG_remove(dvoid *opaque, const char *name)
459 BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
463 static int HOG_mkdir(dvoid *opaque, const char *name)
465 BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
469 const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_HOG =
472 HOG_ARCHIVE_DESCRIPTION,
473 "Bradley Bell <btb@icculus.org>",
474 "http://icculus.org/physfs/",
478 const PHYSFS_Archiver __PHYSFS_Archiver_HOG =
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 */
503 #endif /* defined PHYSFS_SUPPORTS_HOG */
505 /* end of hog.c ... */