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)
233 const HOGentry *a = (const HOGentry *) _a;
234 return(__PHYSFS_stricmpASCII(a[one].name, a[two].name));
238 } /* hog_entry_cmp */
241 static void hog_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
246 HOGentry *first = &(((HOGentry *) _a)[one]);
247 HOGentry *second = &(((HOGentry *) _a)[two]);
248 memcpy(&tmp, first, sizeof (HOGentry));
249 memcpy(first, second, sizeof (HOGentry));
250 memcpy(second, &tmp, sizeof (HOGentry));
252 } /* hog_entry_swap */
255 static int hog_load_entries(const char *name, int forWriting, HOGinfo *info)
258 PHYSFS_uint32 fileCount;
261 BAIL_IF_MACRO(!hog_open(name, forWriting, &fh, &fileCount), NULL, 0);
262 info->entryCount = fileCount;
263 info->entries = (HOGentry *) allocator.Malloc(sizeof(HOGentry)*fileCount);
264 if (info->entries == NULL)
266 __PHYSFS_platformClose(fh);
267 BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
270 for (entry = info->entries; fileCount > 0; fileCount--, entry++)
272 if (__PHYSFS_platformRead(fh, &entry->name, 13, 1) != 1)
274 __PHYSFS_platformClose(fh);
278 if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1)
280 __PHYSFS_platformClose(fh);
284 entry->size = PHYSFS_swapULE32(entry->size);
285 entry->startPos = (unsigned int) __PHYSFS_platformTell(fh);
286 if (entry->startPos == -1)
288 __PHYSFS_platformClose(fh);
292 /* Skip over entry */
293 if (!__PHYSFS_platformSeek(fh, entry->startPos + entry->size))
295 __PHYSFS_platformClose(fh);
300 __PHYSFS_platformClose(fh);
302 __PHYSFS_sort(info->entries, info->entryCount,
303 hog_entry_cmp, hog_entry_swap);
305 } /* hog_load_entries */
308 static void *HOG_openArchive(const char *name, int forWriting)
310 PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name);
311 HOGinfo *info = (HOGinfo *) allocator.Malloc(sizeof (HOGinfo));
313 BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, 0);
314 memset(info, '\0', sizeof (HOGinfo));
315 info->filename = (char *) allocator.Malloc(strlen(name) + 1);
316 GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, HOG_openArchive_failed);
318 if (!hog_load_entries(name, forWriting, info))
319 goto HOG_openArchive_failed;
321 strcpy(info->filename, name);
322 info->last_mod_time = modtime;
326 HOG_openArchive_failed:
329 if (info->filename != NULL)
330 allocator.Free(info->filename);
331 if (info->entries != NULL)
332 allocator.Free(info->entries);
333 allocator.Free(info);
337 } /* HOG_openArchive */
340 static void HOG_enumerateFiles(dvoid *opaque, const char *dname,
341 int omitSymLinks, PHYSFS_EnumFilesCallback cb,
342 const char *origdir, void *callbackdata)
344 /* no directories in HOG files. */
347 HOGinfo *info = (HOGinfo *) opaque;
348 HOGentry *entry = info->entries;
349 PHYSFS_uint32 max = info->entryCount;
352 for (i = 0; i < max; i++, entry++)
353 cb(callbackdata, origdir, entry->name);
355 } /* HOG_enumerateFiles */
358 static HOGentry *hog_find_entry(HOGinfo *info, const char *name)
360 char *ptr = strchr(name, '.');
361 HOGentry *a = info->entries;
362 PHYSFS_sint32 lo = 0;
363 PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
364 PHYSFS_sint32 middle;
368 * Rule out filenames to avoid unneeded processing...no dirs,
369 * big filenames, or extensions > 3 chars.
371 BAIL_IF_MACRO((ptr) && (strlen(ptr) > 4), ERR_NO_SUCH_FILE, NULL);
372 BAIL_IF_MACRO(strlen(name) > 12, ERR_NO_SUCH_FILE, NULL);
373 BAIL_IF_MACRO(strchr(name, '/') != NULL, ERR_NO_SUCH_FILE, NULL);
377 middle = lo + ((hi - lo) / 2);
378 rc = __PHYSFS_stricmpASCII(name, a[middle].name);
379 if (rc == 0) /* found it! */
387 BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
388 } /* hog_find_entry */
391 static int HOG_exists(dvoid *opaque, const char *name)
393 return(hog_find_entry(((HOGinfo *) opaque), name) != NULL);
397 static int HOG_isDirectory(dvoid *opaque, const char *name, int *fileExists)
399 *fileExists = HOG_exists(opaque, name);
400 return(0); /* never directories in a groupfile. */
401 } /* HOG_isDirectory */
404 static int HOG_isSymLink(dvoid *opaque, const char *name, int *fileExists)
406 *fileExists = HOG_exists(opaque, name);
407 return(0); /* never symlinks in a groupfile. */
408 } /* HOG_isSymLink */
411 static PHYSFS_sint64 HOG_getLastModTime(dvoid *opaque,
415 HOGinfo *info = ((HOGinfo *) opaque);
416 PHYSFS_sint64 retval = -1;
418 *fileExists = (hog_find_entry(info, name) != NULL);
419 if (*fileExists) /* use time of HOG itself in the physical filesystem. */
420 retval = info->last_mod_time;
423 } /* HOG_getLastModTime */
426 static fvoid *HOG_openRead(dvoid *opaque, const char *fnm, int *fileExists)
428 HOGinfo *info = ((HOGinfo *) opaque);
432 entry = hog_find_entry(info, fnm);
433 *fileExists = (entry != NULL);
434 BAIL_IF_MACRO(entry == NULL, NULL, NULL);
436 finfo = (HOGfileinfo *) allocator.Malloc(sizeof (HOGfileinfo));
437 BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
439 finfo->handle = __PHYSFS_platformOpenRead(info->filename);
440 if ( (finfo->handle == NULL) ||
441 (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
443 allocator.Free(finfo);
448 finfo->entry = entry;
453 static fvoid *HOG_openWrite(dvoid *opaque, const char *name)
455 BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
456 } /* HOG_openWrite */
459 static fvoid *HOG_openAppend(dvoid *opaque, const char *name)
461 BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
462 } /* HOG_openAppend */
465 static int HOG_remove(dvoid *opaque, const char *name)
467 BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
471 static int HOG_mkdir(dvoid *opaque, const char *name)
473 BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
477 const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_HOG =
480 HOG_ARCHIVE_DESCRIPTION,
481 "Bradley Bell <btb@icculus.org>",
482 "http://icculus.org/physfs/",
486 const PHYSFS_Archiver __PHYSFS_Archiver_HOG =
488 &__PHYSFS_ArchiveInfo_HOG,
489 HOG_isArchive, /* isArchive() method */
490 HOG_openArchive, /* openArchive() method */
491 HOG_enumerateFiles, /* enumerateFiles() method */
492 HOG_exists, /* exists() method */
493 HOG_isDirectory, /* isDirectory() method */
494 HOG_isSymLink, /* isSymLink() method */
495 HOG_getLastModTime, /* getLastModTime() method */
496 HOG_openRead, /* openRead() method */
497 HOG_openWrite, /* openWrite() method */
498 HOG_openAppend, /* openAppend() method */
499 HOG_remove, /* remove() method */
500 HOG_mkdir, /* mkdir() method */
501 HOG_dirClose, /* dirClose() method */
502 HOG_read, /* read() method */
503 HOG_write, /* write() method */
504 HOG_eof, /* eof() method */
505 HOG_tell, /* tell() method */
506 HOG_seek, /* seek() method */
507 HOG_fileLength, /* fileLength() method */
508 HOG_fileClose /* fileClose() method */
511 #endif /* defined PHYSFS_SUPPORTS_HOG */
513 /* end of hog.c ... */