2 * MVL support routines for PhysicsFS.
4 * This driver handles Descent II Movielib archives.
6 * The file format of MVL is quite easy...
8 * //MVL File format - Written by Heiko Herrmann
9 * char sig[4] = {'D','M', 'V', 'L'}; // "DMVL"=Descent MoVie Library
11 * int num_files; // the number of files in this MVL
14 * char file_name[13]; // Filename, padded to 13 bytes with 0s
15 * int file_size; // filesize in bytes
16 * }DIR_STRUCT[num_files];
19 * char data[file_size]; // The file data
20 * }FILE_STRUCT[num_files];
22 * (That info is from http://www.descent2.com/ddn/specs/mvl/)
24 * Please see the file LICENSE.txt in the source's root directory.
26 * This file written by Bradley Bell.
27 * Based on grp.c by Ryan C. Gordon.
30 #if (defined PHYSFS_SUPPORTS_MVL)
37 #define __PHYSICSFS_INTERNAL__
38 #include "physfs_internal.h"
43 PHYSFS_uint32 startPos;
50 PHYSFS_sint64 last_mod_time;
51 PHYSFS_uint32 entryCount;
63 static void MVL_dirClose(dvoid *opaque)
65 MVLinfo *info = ((MVLinfo *) opaque);
66 allocator.Free(info->filename);
67 allocator.Free(info->entries);
72 static PHYSFS_sint64 MVL_read(fvoid *opaque, void *buffer,
73 PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
75 MVLfileinfo *finfo = (MVLfileinfo *) opaque;
76 MVLentry *entry = finfo->entry;
77 PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos;
78 PHYSFS_uint32 objsLeft = (bytesLeft / objSize);
81 if (objsLeft < objCount)
84 rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount);
86 finfo->curPos += (PHYSFS_uint32) (rc * objSize);
92 static PHYSFS_sint64 MVL_write(fvoid *opaque, const void *buffer,
93 PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
95 BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
99 static int MVL_eof(fvoid *opaque)
101 MVLfileinfo *finfo = (MVLfileinfo *) opaque;
102 MVLentry *entry = finfo->entry;
103 return(finfo->curPos >= entry->size);
107 static PHYSFS_sint64 MVL_tell(fvoid *opaque)
109 return(((MVLfileinfo *) opaque)->curPos);
113 static int MVL_seek(fvoid *opaque, PHYSFS_uint64 offset)
115 MVLfileinfo *finfo = (MVLfileinfo *) opaque;
116 MVLentry *entry = finfo->entry;
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);
123 finfo->curPos = (PHYSFS_uint32) offset;
129 static PHYSFS_sint64 MVL_fileLength(fvoid *opaque)
131 MVLfileinfo *finfo = (MVLfileinfo *) opaque;
132 return((PHYSFS_sint64) finfo->entry->size);
133 } /* MVL_fileLength */
136 static int MVL_fileClose(fvoid *opaque)
138 MVLfileinfo *finfo = (MVLfileinfo *) opaque;
139 BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
140 allocator.Free(finfo);
142 } /* MVL_fileClose */
145 static int mvl_open(const char *filename, int forWriting,
146 void **fh, PHYSFS_uint32 *count)
151 BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
153 *fh = __PHYSFS_platformOpenRead(filename);
154 BAIL_IF_MACRO(*fh == NULL, NULL, 0);
156 if (__PHYSFS_platformRead(*fh, buf, 4, 1) != 1)
159 if (memcmp(buf, "DMVL", 4) != 0)
161 __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
165 if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1)
168 *count = PHYSFS_swapULE32(*count);
174 __PHYSFS_platformClose(*fh);
182 static int MVL_isArchive(const char *filename, int forWriting)
185 PHYSFS_uint32 fileCount;
186 int retval = mvl_open(filename, forWriting, &fh, &fileCount);
189 __PHYSFS_platformClose(fh);
192 } /* MVL_isArchive */
195 static int mvl_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
199 const MVLentry *a = (const MVLentry *) _a;
200 return(strcmp(a[one].name, a[two].name));
204 } /* mvl_entry_cmp */
207 static void mvl_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
212 MVLentry *first = &(((MVLentry *) _a)[one]);
213 MVLentry *second = &(((MVLentry *) _a)[two]);
214 memcpy(&tmp, first, sizeof (MVLentry));
215 memcpy(first, second, sizeof (MVLentry));
216 memcpy(second, &tmp, sizeof (MVLentry));
218 } /* mvl_entry_swap */
221 static int mvl_load_entries(const char *name, int forWriting, MVLinfo *info)
224 PHYSFS_uint32 fileCount;
225 PHYSFS_uint32 location = 8; /* sizeof sig. */
228 BAIL_IF_MACRO(!mvl_open(name, forWriting, &fh, &fileCount), NULL, 0);
229 info->entryCount = fileCount;
230 info->entries = (MVLentry *) allocator.Malloc(sizeof(MVLentry)*fileCount);
231 if (info->entries == NULL)
233 __PHYSFS_platformClose(fh);
234 BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
237 location += (17 * fileCount);
239 for (entry = info->entries; fileCount > 0; fileCount--, entry++)
241 if (__PHYSFS_platformRead(fh, &entry->name, 13, 1) != 1)
243 __PHYSFS_platformClose(fh);
247 if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1)
249 __PHYSFS_platformClose(fh);
253 entry->size = PHYSFS_swapULE32(entry->size);
254 entry->startPos = location;
255 location += entry->size;
258 __PHYSFS_platformClose(fh);
260 __PHYSFS_sort(info->entries, info->entryCount,
261 mvl_entry_cmp, mvl_entry_swap);
263 } /* mvl_load_entries */
266 static void *MVL_openArchive(const char *name, int forWriting)
268 PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name);
269 MVLinfo *info = (MVLinfo *) allocator.Malloc(sizeof (MVLinfo));
271 BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, NULL);
272 memset(info, '\0', sizeof (MVLinfo));
274 info->filename = (char *) allocator.Malloc(strlen(name) + 1);
275 GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, MVL_openArchive_failed);
276 if (!mvl_load_entries(name, forWriting, info))
277 goto MVL_openArchive_failed;
279 strcpy(info->filename, name);
280 info->last_mod_time = modtime;
283 MVL_openArchive_failed:
286 if (info->filename != NULL)
287 allocator.Free(info->filename);
288 if (info->entries != NULL)
289 allocator.Free(info->entries);
290 allocator.Free(info);
294 } /* MVL_openArchive */
297 static void MVL_enumerateFiles(dvoid *opaque, const char *dname,
298 int omitSymLinks, PHYSFS_EnumFilesCallback cb,
299 const char *origdir, void *callbackdata)
301 /* no directories in MVL files. */
304 MVLinfo *info = ((MVLinfo *) opaque);
305 MVLentry *entry = info->entries;
306 PHYSFS_uint32 max = info->entryCount;
309 for (i = 0; i < max; i++, entry++)
310 cb(callbackdata, origdir, entry->name);
312 } /* MVL_enumerateFiles */
315 static MVLentry *mvl_find_entry(MVLinfo *info, const char *name)
317 char *ptr = strchr(name, '.');
318 MVLentry *a = info->entries;
319 PHYSFS_sint32 lo = 0;
320 PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
321 PHYSFS_sint32 middle;
325 * Rule out filenames to avoid unneeded processing...no dirs,
326 * big filenames, or extensions > 3 chars.
328 BAIL_IF_MACRO((ptr) && (strlen(ptr) > 4), ERR_NO_SUCH_FILE, NULL);
329 BAIL_IF_MACRO(strlen(name) > 12, ERR_NO_SUCH_FILE, NULL);
330 BAIL_IF_MACRO(strchr(name, '/') != NULL, ERR_NO_SUCH_FILE, NULL);
334 middle = lo + ((hi - lo) / 2);
335 rc = __PHYSFS_stricmpASCII(name, a[middle].name);
336 if (rc == 0) /* found it! */
344 BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
345 } /* mvl_find_entry */
348 static int MVL_exists(dvoid *opaque, const char *name)
350 return(mvl_find_entry(((MVLinfo *) opaque), name) != NULL);
354 static int MVL_isDirectory(dvoid *opaque, const char *name, int *fileExists)
356 *fileExists = MVL_exists(opaque, name);
357 return(0); /* never directories in a groupfile. */
358 } /* MVL_isDirectory */
361 static int MVL_isSymLink(dvoid *opaque, const char *name, int *fileExists)
363 *fileExists = MVL_exists(opaque, name);
364 return(0); /* never symlinks in a groupfile. */
365 } /* MVL_isSymLink */
368 static PHYSFS_sint64 MVL_getLastModTime(dvoid *opaque,
372 MVLinfo *info = ((MVLinfo *) opaque);
373 PHYSFS_sint64 retval = -1;
375 *fileExists = (mvl_find_entry(info, name) != NULL);
376 if (*fileExists) /* use time of MVL itself in the physical filesystem. */
377 retval = info->last_mod_time;
380 } /* MVL_getLastModTime */
383 static fvoid *MVL_openRead(dvoid *opaque, const char *fnm, int *fileExists)
385 MVLinfo *info = ((MVLinfo *) opaque);
389 entry = mvl_find_entry(info, fnm);
390 *fileExists = (entry != NULL);
391 BAIL_IF_MACRO(entry == NULL, NULL, NULL);
393 finfo = (MVLfileinfo *) allocator.Malloc(sizeof (MVLfileinfo));
394 BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
396 finfo->handle = __PHYSFS_platformOpenRead(info->filename);
397 if ( (finfo->handle == NULL) ||
398 (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
400 allocator.Free(finfo);
405 finfo->entry = entry;
410 static fvoid *MVL_openWrite(dvoid *opaque, const char *name)
412 BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
413 } /* MVL_openWrite */
416 static fvoid *MVL_openAppend(dvoid *opaque, const char *name)
418 BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
419 } /* MVL_openAppend */
422 static int MVL_remove(dvoid *opaque, const char *name)
424 BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
428 static int MVL_mkdir(dvoid *opaque, const char *name)
430 BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
434 const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_MVL =
437 MVL_ARCHIVE_DESCRIPTION,
438 "Bradley Bell <btb@icculus.org>",
439 "http://icculus.org/physfs/",
443 const PHYSFS_Archiver __PHYSFS_Archiver_MVL =
445 &__PHYSFS_ArchiveInfo_MVL,
446 MVL_isArchive, /* isArchive() method */
447 MVL_openArchive, /* openArchive() method */
448 MVL_enumerateFiles, /* enumerateFiles() method */
449 MVL_exists, /* exists() method */
450 MVL_isDirectory, /* isDirectory() method */
451 MVL_isSymLink, /* isSymLink() method */
452 MVL_getLastModTime, /* getLastModTime() method */
453 MVL_openRead, /* openRead() method */
454 MVL_openWrite, /* openWrite() method */
455 MVL_openAppend, /* openAppend() method */
456 MVL_remove, /* remove() method */
457 MVL_mkdir, /* mkdir() method */
458 MVL_dirClose, /* dirClose() method */
459 MVL_read, /* read() method */
460 MVL_write, /* write() method */
461 MVL_eof, /* eof() method */
462 MVL_tell, /* tell() method */
463 MVL_seek, /* seek() method */
464 MVL_fileLength, /* fileLength() method */
465 MVL_fileClose /* fileClose() method */
468 #endif /* defined PHYSFS_SUPPORTS_MVL */
470 /* end of mvl.c ... */