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)
197 MVLentry *a = (MVLentry *) _a;
198 return(strcmp(a[one].name, a[two].name));
199 } /* mvl_entry_cmp */
202 static void mvl_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
205 MVLentry *first = &(((MVLentry *) _a)[one]);
206 MVLentry *second = &(((MVLentry *) _a)[two]);
207 memcpy(&tmp, first, sizeof (MVLentry));
208 memcpy(first, second, sizeof (MVLentry));
209 memcpy(second, &tmp, sizeof (MVLentry));
210 } /* mvl_entry_swap */
213 static int mvl_load_entries(const char *name, int forWriting, MVLinfo *info)
216 PHYSFS_uint32 fileCount;
217 PHYSFS_uint32 location = 8; /* sizeof sig. */
220 BAIL_IF_MACRO(!mvl_open(name, forWriting, &fh, &fileCount), NULL, 0);
221 info->entryCount = fileCount;
222 info->entries = (MVLentry *) allocator.Malloc(sizeof(MVLentry)*fileCount);
223 if (info->entries == NULL)
225 __PHYSFS_platformClose(fh);
226 BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
229 location += (17 * fileCount);
231 for (entry = info->entries; fileCount > 0; fileCount--, entry++)
233 if (__PHYSFS_platformRead(fh, &entry->name, 13, 1) != 1)
235 __PHYSFS_platformClose(fh);
239 if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1)
241 __PHYSFS_platformClose(fh);
245 entry->size = PHYSFS_swapULE32(entry->size);
246 entry->startPos = location;
247 location += entry->size;
250 __PHYSFS_platformClose(fh);
252 __PHYSFS_sort(info->entries, info->entryCount,
253 mvl_entry_cmp, mvl_entry_swap);
255 } /* mvl_load_entries */
258 static void *MVL_openArchive(const char *name, int forWriting)
260 PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name);
261 MVLinfo *info = (MVLinfo *) allocator.Malloc(sizeof (MVLinfo));
263 BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, NULL);
264 memset(info, '\0', sizeof (MVLinfo));
266 info->filename = (char *) allocator.Malloc(strlen(name) + 1);
267 GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, MVL_openArchive_failed);
268 if (!mvl_load_entries(name, forWriting, info))
269 goto MVL_openArchive_failed;
271 strcpy(info->filename, name);
272 info->last_mod_time = modtime;
275 MVL_openArchive_failed:
278 if (info->filename != NULL)
279 allocator.Free(info->filename);
280 if (info->entries != NULL)
281 allocator.Free(info->entries);
282 allocator.Free(info);
286 } /* MVL_openArchive */
289 static void MVL_enumerateFiles(dvoid *opaque, const char *dname,
290 int omitSymLinks, PHYSFS_EnumFilesCallback cb,
291 const char *origdir, void *callbackdata)
293 /* no directories in MVL files. */
296 MVLinfo *info = ((MVLinfo *) opaque);
297 MVLentry *entry = info->entries;
298 PHYSFS_uint32 max = info->entryCount;
301 for (i = 0; i < max; i++, entry++)
302 cb(callbackdata, origdir, entry->name);
304 } /* MVL_enumerateFiles */
307 static MVLentry *mvl_find_entry(MVLinfo *info, const char *name)
309 char *ptr = strchr(name, '.');
310 MVLentry *a = info->entries;
311 PHYSFS_sint32 lo = 0;
312 PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
313 PHYSFS_sint32 middle;
317 * Rule out filenames to avoid unneeded processing...no dirs,
318 * big filenames, or extensions > 3 chars.
320 BAIL_IF_MACRO((ptr) && (strlen(ptr) > 4), ERR_NO_SUCH_FILE, NULL);
321 BAIL_IF_MACRO(strlen(name) > 12, ERR_NO_SUCH_FILE, NULL);
322 BAIL_IF_MACRO(strchr(name, '/') != NULL, ERR_NO_SUCH_FILE, NULL);
326 middle = lo + ((hi - lo) / 2);
327 rc = __PHYSFS_stricmpASCII(name, a[middle].name);
328 if (rc == 0) /* found it! */
336 BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
337 } /* mvl_find_entry */
340 static int MVL_exists(dvoid *opaque, const char *name)
342 return(mvl_find_entry(((MVLinfo *) opaque), name) != NULL);
346 static int MVL_isDirectory(dvoid *opaque, const char *name, int *fileExists)
348 *fileExists = MVL_exists(opaque, name);
349 return(0); /* never directories in a groupfile. */
350 } /* MVL_isDirectory */
353 static int MVL_isSymLink(dvoid *opaque, const char *name, int *fileExists)
355 *fileExists = MVL_exists(opaque, name);
356 return(0); /* never symlinks in a groupfile. */
357 } /* MVL_isSymLink */
360 static PHYSFS_sint64 MVL_getLastModTime(dvoid *opaque,
364 MVLinfo *info = ((MVLinfo *) opaque);
365 PHYSFS_sint64 retval = -1;
367 *fileExists = (mvl_find_entry(info, name) != NULL);
368 if (*fileExists) /* use time of MVL itself in the physical filesystem. */
369 retval = info->last_mod_time;
372 } /* MVL_getLastModTime */
375 static fvoid *MVL_openRead(dvoid *opaque, const char *fnm, int *fileExists)
377 MVLinfo *info = ((MVLinfo *) opaque);
381 entry = mvl_find_entry(info, fnm);
382 *fileExists = (entry != NULL);
383 BAIL_IF_MACRO(entry == NULL, NULL, NULL);
385 finfo = (MVLfileinfo *) allocator.Malloc(sizeof (MVLfileinfo));
386 BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
388 finfo->handle = __PHYSFS_platformOpenRead(info->filename);
389 if ( (finfo->handle == NULL) ||
390 (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
392 allocator.Free(finfo);
397 finfo->entry = entry;
402 static fvoid *MVL_openWrite(dvoid *opaque, const char *name)
404 BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
405 } /* MVL_openWrite */
408 static fvoid *MVL_openAppend(dvoid *opaque, const char *name)
410 BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
411 } /* MVL_openAppend */
414 static int MVL_remove(dvoid *opaque, const char *name)
416 BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
420 static int MVL_mkdir(dvoid *opaque, const char *name)
422 BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
426 const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_MVL =
429 MVL_ARCHIVE_DESCRIPTION,
430 "Bradley Bell <btb@icculus.org>",
431 "http://icculus.org/physfs/",
435 const PHYSFS_Archiver __PHYSFS_Archiver_MVL =
437 &__PHYSFS_ArchiveInfo_MVL,
438 MVL_isArchive, /* isArchive() method */
439 MVL_openArchive, /* openArchive() method */
440 MVL_enumerateFiles, /* enumerateFiles() method */
441 MVL_exists, /* exists() method */
442 MVL_isDirectory, /* isDirectory() method */
443 MVL_isSymLink, /* isSymLink() method */
444 MVL_getLastModTime, /* getLastModTime() method */
445 MVL_openRead, /* openRead() method */
446 MVL_openWrite, /* openWrite() method */
447 MVL_openAppend, /* openAppend() method */
448 MVL_remove, /* remove() method */
449 MVL_mkdir, /* mkdir() method */
450 MVL_dirClose, /* dirClose() method */
451 MVL_read, /* read() method */
452 MVL_write, /* write() method */
453 MVL_eof, /* eof() method */
454 MVL_tell, /* tell() method */
455 MVL_seek, /* seek() method */
456 MVL_fileLength, /* fileLength() method */
457 MVL_fileClose /* fileClose() method */
460 #endif /* defined PHYSFS_SUPPORTS_MVL */
462 /* end of mvl.c ... */