mirror of
https://github.com/love2d/love-android.git
synced 2026-08-20 20:51:22 +02:00
preparing update of physfs
This commit is contained in:
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* APK support routines for PhysicsFS.
|
||||
* Based von zip.c.
|
||||
*
|
||||
* A apk (android) file is just a zip file. But for LÖVE
|
||||
* only the assets/ content is necessary and therefore
|
||||
* this hides everything outside the assets/ directory
|
||||
* (like a chroot into assets/).
|
||||
*
|
||||
* Please see the file LICENSE.txt in the source's root directory.
|
||||
*
|
||||
* This file is heavily based on zip.c by Ryan C. Gordon
|
||||
*/
|
||||
|
||||
#if (defined PHYSFS_SUPPORTS_APK)
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef _WIN32_WCE
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#endif
|
||||
#include "physfs.h"
|
||||
#include "zlib.h"
|
||||
|
||||
#define __PHYSICSFS_INTERNAL__
|
||||
#include "physfs_internal.h"
|
||||
|
||||
extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_ZIP;
|
||||
extern const PHYSFS_Archiver __PHYSFS_Archiver_ZIP;
|
||||
|
||||
static PHYSFS_sint64 APK_read(fvoid *opaque, void *buf,
|
||||
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
|
||||
{
|
||||
return __PHYSFS_Archiver_ZIP.read(opaque, buf, objSize, objCount);
|
||||
} /* APK_read */
|
||||
|
||||
|
||||
static PHYSFS_sint64 APK_write(fvoid *opaque, const void *buf,
|
||||
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
|
||||
{
|
||||
return __PHYSFS_Archiver_ZIP.write(opaque, buf, objSize, objCount);
|
||||
} /* APK_write */
|
||||
|
||||
|
||||
static int APK_eof(fvoid *opaque)
|
||||
{
|
||||
return __PHYSFS_Archiver_ZIP.eof(opaque);
|
||||
} /* APK_eof */
|
||||
|
||||
|
||||
static PHYSFS_sint64 APK_tell(fvoid *opaque)
|
||||
{
|
||||
return __PHYSFS_Archiver_ZIP.tell(opaque);
|
||||
} /* APK_tell */
|
||||
|
||||
|
||||
static int APK_seek(fvoid *opaque, PHYSFS_uint64 offset)
|
||||
{
|
||||
return __PHYSFS_Archiver_ZIP.seek(opaque, offset);
|
||||
} /* APK_seek */
|
||||
|
||||
|
||||
static PHYSFS_sint64 APK_fileLength(fvoid *opaque)
|
||||
{
|
||||
return __PHYSFS_Archiver_ZIP.fileLength(opaque);
|
||||
} /* APK_fileLength */
|
||||
|
||||
|
||||
static int APK_fileClose(fvoid *opaque)
|
||||
{
|
||||
return __PHYSFS_Archiver_ZIP.fileClose(opaque);
|
||||
} /* APK_fileClose */
|
||||
|
||||
|
||||
|
||||
static int APK_isArchive(const char *filename, int forWriting)
|
||||
{
|
||||
int filename_len = 0;
|
||||
|
||||
// does not end with .apk? then ignore this archive
|
||||
filename_len = strlen(filename);
|
||||
if ((filename_len <= 4) ||
|
||||
(filename[filename_len - 4] != '.') ||
|
||||
(filename[filename_len - 3] != 'a' && filename[filename_len - 3] != 'A') ||
|
||||
(filename[filename_len - 2] != 'p' && filename[filename_len - 2] != 'P') ||
|
||||
(filename[filename_len - 1] != 'k' && filename[filename_len - 1] != 'K'))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return __PHYSFS_Archiver_ZIP.isArchive(filename, forWriting);
|
||||
}
|
||||
} /* APK_isArchive */
|
||||
|
||||
|
||||
|
||||
static void *APK_openArchive(const char *name, int forWriting)
|
||||
{
|
||||
return __PHYSFS_Archiver_ZIP.openArchive(name, forWriting);
|
||||
} /* APK_openArchive */
|
||||
|
||||
|
||||
/**
|
||||
* Allocates a new string and appends assets/ name.
|
||||
* Result must be freed manually with allocator.Free
|
||||
* if not necessary anymore
|
||||
*/
|
||||
char *append_assets(const char *name)
|
||||
{
|
||||
char *patched_name;
|
||||
int name_len = strlen(name);
|
||||
|
||||
patched_name = allocator.Malloc(7 + name_len + 1);
|
||||
|
||||
memcpy(patched_name, "assets/", 7);
|
||||
memcpy(patched_name + 7, name, name_len);
|
||||
patched_name[7 + name_len + 1 - 1] = 0;
|
||||
|
||||
return patched_name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* just stores the parameter to an APK_enumerateFiles call to use them
|
||||
* in the delegated call to cb
|
||||
*/
|
||||
typedef struct APK_originalEnumerateFileCallParameter
|
||||
{
|
||||
dvoid *opaque;
|
||||
const char *dname;
|
||||
int omitSymLinks;
|
||||
PHYSFS_EnumFilesCallback cb;
|
||||
const char *origdir;
|
||||
void *callbackdata;
|
||||
} APK_originalEnumerateFileCallParameter;
|
||||
|
||||
static void apk_enumerateFilesCallback(void *data, const char *origdir,
|
||||
const char *fname)
|
||||
{
|
||||
APK_originalEnumerateFileCallParameter *callParameterPtr;
|
||||
|
||||
callParameterPtr = (APK_originalEnumerateFileCallParameter *)data;
|
||||
|
||||
(callParameterPtr->cb)(callParameterPtr->callbackdata, callParameterPtr->origdir, fname);
|
||||
}
|
||||
|
||||
static void APK_enumerateFiles(dvoid *opaque, const char *dname,
|
||||
int omitSymLinks, PHYSFS_EnumFilesCallback cb,
|
||||
const char *origdir, void *callbackdata)
|
||||
{
|
||||
char *patched_dname;
|
||||
APK_originalEnumerateFileCallParameter callParameter;
|
||||
|
||||
callParameter.opaque = opaque;
|
||||
callParameter.dname = dname;
|
||||
callParameter.omitSymLinks = omitSymLinks;
|
||||
callParameter.cb = cb;
|
||||
callParameter.origdir = origdir;
|
||||
callParameter.callbackdata = callbackdata;
|
||||
|
||||
patched_dname = append_assets(dname);
|
||||
|
||||
__PHYSFS_Archiver_ZIP.enumerateFiles(opaque, patched_dname, omitSymLinks, apk_enumerateFilesCallback, origdir, &callParameter);
|
||||
|
||||
allocator.Free(patched_dname);
|
||||
} /* APK_enumerateFiles */
|
||||
|
||||
|
||||
static int APK_exists(dvoid *opaque, const char *name)
|
||||
{
|
||||
int retval = 0;
|
||||
char *patched_name = append_assets(name);
|
||||
|
||||
retval = __PHYSFS_Archiver_ZIP.exists(opaque, patched_name);
|
||||
|
||||
allocator.Free(patched_name);
|
||||
|
||||
return retval;
|
||||
} /* APK_exists */
|
||||
|
||||
|
||||
static PHYSFS_sint64 APK_getLastModTime(dvoid *opaque,
|
||||
const char *name,
|
||||
int *fileExists)
|
||||
{
|
||||
int retval = 0;
|
||||
char *patched_name = append_assets(name);
|
||||
|
||||
retval = __PHYSFS_Archiver_ZIP.getLastModTime(opaque, patched_name, fileExists);
|
||||
|
||||
allocator.Free(patched_name);
|
||||
|
||||
return retval;
|
||||
} /* APK_getLastModTime */
|
||||
|
||||
|
||||
static int APK_isDirectory(dvoid *opaque, const char *name, int *fileExists)
|
||||
{
|
||||
int retval = 0;
|
||||
char *patched_name = append_assets(name);
|
||||
|
||||
retval = __PHYSFS_Archiver_ZIP.isDirectory(opaque, patched_name, fileExists);
|
||||
|
||||
allocator.Free(patched_name);
|
||||
|
||||
return retval;
|
||||
} /* APK_isDirectory */
|
||||
|
||||
|
||||
static int APK_isSymLink(dvoid *opaque, const char *name, int *fileExists)
|
||||
{
|
||||
int retval = 0;
|
||||
char *patched_name = append_assets(name);
|
||||
|
||||
retval = __PHYSFS_Archiver_ZIP.isSymLink(opaque, patched_name, fileExists);
|
||||
|
||||
allocator.Free(patched_name);
|
||||
|
||||
return retval;
|
||||
} /* APK_isSymLink */
|
||||
|
||||
|
||||
static fvoid *APK_openRead(dvoid *opaque, const char *fnm, int *fileExists)
|
||||
{
|
||||
fvoid * retval;
|
||||
char *patched_name = append_assets(fnm);
|
||||
|
||||
retval = __PHYSFS_Archiver_ZIP.openRead(opaque, patched_name, fileExists);
|
||||
|
||||
allocator.Free(patched_name);
|
||||
|
||||
return retval;
|
||||
} /* APK_openRead */
|
||||
|
||||
|
||||
static fvoid *APK_openWrite(dvoid *opaque, const char *filename)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
|
||||
} /* APK_openWrite */
|
||||
|
||||
|
||||
static fvoid *APK_openAppend(dvoid *opaque, const char *filename)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
|
||||
} /* APK_openAppend */
|
||||
|
||||
|
||||
static void APK_dirClose(dvoid *opaque)
|
||||
{
|
||||
__PHYSFS_Archiver_ZIP.dirClose(opaque);
|
||||
} /* APK_dirClose */
|
||||
|
||||
|
||||
static int APK_remove(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
|
||||
} /* APK_remove */
|
||||
|
||||
|
||||
static int APK_mkdir(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
|
||||
} /* APK_mkdir */
|
||||
|
||||
|
||||
const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_APK =
|
||||
{
|
||||
"APK",
|
||||
APK_ARCHIVE_DESCRIPTION,
|
||||
"based on __PHYSFS_Archiver_ZIP.ARCHIVE by Ryan C. Gordon <icculus@icculus.org>",
|
||||
"http://icculus.org/physfs/",
|
||||
};
|
||||
|
||||
|
||||
const PHYSFS_Archiver __PHYSFS_Archiver_APK =
|
||||
{
|
||||
&__PHYSFS_ArchiveInfo_APK,
|
||||
APK_isArchive, /* isArchive() method */
|
||||
APK_openArchive, /* openArchive() method */
|
||||
APK_enumerateFiles, /* enumerateFiles() method */
|
||||
APK_exists, /* exists() method */
|
||||
APK_isDirectory, /* isDirectory() method */
|
||||
APK_isSymLink, /* isSymLink() method */
|
||||
APK_getLastModTime, /* getLastModTime() method */
|
||||
APK_openRead, /* openRead() method */
|
||||
APK_openWrite, /* openWrite() method */
|
||||
APK_openAppend, /* openAppend() method */
|
||||
APK_remove, /* remove() method */
|
||||
APK_mkdir, /* mkdir() method */
|
||||
APK_dirClose, /* dirClose() method */
|
||||
APK_read, /* read() method */
|
||||
APK_write, /* write() method */
|
||||
APK_eof, /* eof() method */
|
||||
APK_tell, /* tell() method */
|
||||
APK_seek, /* seek() method */
|
||||
APK_fileLength, /* fileLength() method */
|
||||
APK_fileClose /* fileClose() method */
|
||||
};
|
||||
|
||||
#endif /* defined PHYSFS_SUPPORTS_APK */
|
||||
|
||||
/* end of apk.c ... */
|
||||
|
||||
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* Standard directory I/O support routines for PhysicsFS.
|
||||
*
|
||||
* Please see the file LICENSE.txt in the source's root directory.
|
||||
*
|
||||
* This file written by Ryan C. Gordon.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "physfs.h"
|
||||
|
||||
#define __PHYSICSFS_INTERNAL__
|
||||
#include "physfs_internal.h"
|
||||
|
||||
static PHYSFS_sint64 DIR_read(fvoid *opaque, void *buffer,
|
||||
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
|
||||
{
|
||||
PHYSFS_sint64 retval;
|
||||
retval = __PHYSFS_platformRead(opaque, buffer, objSize, objCount);
|
||||
return(retval);
|
||||
} /* DIR_read */
|
||||
|
||||
|
||||
static PHYSFS_sint64 DIR_write(fvoid *opaque, const void *buffer,
|
||||
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
|
||||
{
|
||||
PHYSFS_sint64 retval;
|
||||
retval = __PHYSFS_platformWrite(opaque, buffer, objSize, objCount);
|
||||
return(retval);
|
||||
} /* DIR_write */
|
||||
|
||||
|
||||
static int DIR_eof(fvoid *opaque)
|
||||
{
|
||||
return(__PHYSFS_platformEOF(opaque));
|
||||
} /* DIR_eof */
|
||||
|
||||
|
||||
static PHYSFS_sint64 DIR_tell(fvoid *opaque)
|
||||
{
|
||||
return(__PHYSFS_platformTell(opaque));
|
||||
} /* DIR_tell */
|
||||
|
||||
|
||||
static int DIR_seek(fvoid *opaque, PHYSFS_uint64 offset)
|
||||
{
|
||||
return(__PHYSFS_platformSeek(opaque, offset));
|
||||
} /* DIR_seek */
|
||||
|
||||
|
||||
static PHYSFS_sint64 DIR_fileLength(fvoid *opaque)
|
||||
{
|
||||
return(__PHYSFS_platformFileLength(opaque));
|
||||
} /* DIR_fileLength */
|
||||
|
||||
|
||||
static int DIR_fileClose(fvoid *opaque)
|
||||
{
|
||||
/*
|
||||
* we manually flush the buffer, since that's the place a close will
|
||||
* most likely fail, but that will leave the file handle in an undefined
|
||||
* state if it fails. Flush failures we can recover from.
|
||||
*/
|
||||
BAIL_IF_MACRO(!__PHYSFS_platformFlush(opaque), NULL, 0);
|
||||
BAIL_IF_MACRO(!__PHYSFS_platformClose(opaque), NULL, 0);
|
||||
return(1);
|
||||
} /* DIR_fileClose */
|
||||
|
||||
|
||||
static int DIR_isArchive(const char *filename, int forWriting)
|
||||
{
|
||||
/* directories ARE archives in this driver... */
|
||||
return(__PHYSFS_platformIsDirectory(filename));
|
||||
} /* DIR_isArchive */
|
||||
|
||||
|
||||
static void *DIR_openArchive(const char *name, int forWriting)
|
||||
{
|
||||
const char *dirsep = PHYSFS_getDirSeparator();
|
||||
char *retval = NULL;
|
||||
size_t namelen = strlen(name);
|
||||
size_t seplen = strlen(dirsep);
|
||||
|
||||
/* !!! FIXME: when is this not called right before openArchive? */
|
||||
BAIL_IF_MACRO(!DIR_isArchive(name, forWriting),
|
||||
ERR_UNSUPPORTED_ARCHIVE, 0);
|
||||
|
||||
retval = allocator.Malloc(namelen + seplen + 1);
|
||||
BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
|
||||
|
||||
/* make sure there's a dir separator at the end of the string */
|
||||
strcpy(retval, name);
|
||||
if (strcmp((name + namelen) - seplen, dirsep) != 0)
|
||||
strcat(retval, dirsep);
|
||||
|
||||
return(retval);
|
||||
} /* DIR_openArchive */
|
||||
|
||||
|
||||
static void DIR_enumerateFiles(dvoid *opaque, const char *dname,
|
||||
int omitSymLinks, PHYSFS_EnumFilesCallback cb,
|
||||
const char *origdir, void *callbackdata)
|
||||
{
|
||||
char *d = __PHYSFS_platformCvtToDependent((char *)opaque, dname, NULL);
|
||||
if (d != NULL)
|
||||
{
|
||||
__PHYSFS_platformEnumerateFiles(d, omitSymLinks, cb,
|
||||
origdir, callbackdata);
|
||||
allocator.Free(d);
|
||||
} /* if */
|
||||
} /* DIR_enumerateFiles */
|
||||
|
||||
|
||||
static int DIR_exists(dvoid *opaque, const char *name)
|
||||
{
|
||||
char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL);
|
||||
int retval;
|
||||
|
||||
BAIL_IF_MACRO(f == NULL, NULL, 0);
|
||||
retval = __PHYSFS_platformExists(f);
|
||||
allocator.Free(f);
|
||||
return(retval);
|
||||
} /* DIR_exists */
|
||||
|
||||
|
||||
static int DIR_isDirectory(dvoid *opaque, const char *name, int *fileExists)
|
||||
{
|
||||
char *d = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL);
|
||||
int retval = 0;
|
||||
|
||||
BAIL_IF_MACRO(d == NULL, NULL, 0);
|
||||
*fileExists = __PHYSFS_platformExists(d);
|
||||
if (*fileExists)
|
||||
retval = __PHYSFS_platformIsDirectory(d);
|
||||
allocator.Free(d);
|
||||
return(retval);
|
||||
} /* DIR_isDirectory */
|
||||
|
||||
|
||||
static int DIR_isSymLink(dvoid *opaque, const char *name, int *fileExists)
|
||||
{
|
||||
char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL);
|
||||
int retval = 0;
|
||||
|
||||
BAIL_IF_MACRO(f == NULL, NULL, 0);
|
||||
*fileExists = __PHYSFS_platformExists(f);
|
||||
if (*fileExists)
|
||||
retval = __PHYSFS_platformIsSymLink(f);
|
||||
allocator.Free(f);
|
||||
return(retval);
|
||||
} /* DIR_isSymLink */
|
||||
|
||||
|
||||
static PHYSFS_sint64 DIR_getLastModTime(dvoid *opaque,
|
||||
const char *name,
|
||||
int *fileExists)
|
||||
{
|
||||
char *d = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL);
|
||||
PHYSFS_sint64 retval = -1;
|
||||
|
||||
BAIL_IF_MACRO(d == NULL, NULL, 0);
|
||||
*fileExists = __PHYSFS_platformExists(d);
|
||||
if (*fileExists)
|
||||
retval = __PHYSFS_platformGetLastModTime(d);
|
||||
allocator.Free(d);
|
||||
return(retval);
|
||||
} /* DIR_getLastModTime */
|
||||
|
||||
|
||||
static fvoid *doOpen(dvoid *opaque, const char *name,
|
||||
void *(*openFunc)(const char *filename),
|
||||
int *fileExists)
|
||||
{
|
||||
char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL);
|
||||
void *rc = NULL;
|
||||
|
||||
BAIL_IF_MACRO(f == NULL, NULL, NULL);
|
||||
|
||||
if (fileExists != NULL)
|
||||
{
|
||||
*fileExists = __PHYSFS_platformExists(f);
|
||||
if (!(*fileExists))
|
||||
{
|
||||
allocator.Free(f);
|
||||
return(NULL);
|
||||
} /* if */
|
||||
} /* if */
|
||||
|
||||
rc = openFunc(f);
|
||||
allocator.Free(f);
|
||||
|
||||
return((fvoid *) rc);
|
||||
} /* doOpen */
|
||||
|
||||
|
||||
static fvoid *DIR_openRead(dvoid *opaque, const char *fnm, int *exist)
|
||||
{
|
||||
return(doOpen(opaque, fnm, __PHYSFS_platformOpenRead, exist));
|
||||
} /* DIR_openRead */
|
||||
|
||||
|
||||
static fvoid *DIR_openWrite(dvoid *opaque, const char *filename)
|
||||
{
|
||||
return(doOpen(opaque, filename, __PHYSFS_platformOpenWrite, NULL));
|
||||
} /* DIR_openWrite */
|
||||
|
||||
|
||||
static fvoid *DIR_openAppend(dvoid *opaque, const char *filename)
|
||||
{
|
||||
return(doOpen(opaque, filename, __PHYSFS_platformOpenAppend, NULL));
|
||||
} /* DIR_openAppend */
|
||||
|
||||
|
||||
static int DIR_remove(dvoid *opaque, const char *name)
|
||||
{
|
||||
char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL);
|
||||
int retval;
|
||||
|
||||
BAIL_IF_MACRO(f == NULL, NULL, 0);
|
||||
retval = __PHYSFS_platformDelete(f);
|
||||
allocator.Free(f);
|
||||
return(retval);
|
||||
} /* DIR_remove */
|
||||
|
||||
|
||||
static int DIR_mkdir(dvoid *opaque, const char *name)
|
||||
{
|
||||
char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL);
|
||||
int retval;
|
||||
|
||||
BAIL_IF_MACRO(f == NULL, NULL, 0);
|
||||
retval = __PHYSFS_platformMkDir(f);
|
||||
allocator.Free(f);
|
||||
return(retval);
|
||||
} /* DIR_mkdir */
|
||||
|
||||
|
||||
static void DIR_dirClose(dvoid *opaque)
|
||||
{
|
||||
allocator.Free(opaque);
|
||||
} /* DIR_dirClose */
|
||||
|
||||
|
||||
|
||||
const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_DIR =
|
||||
{
|
||||
"",
|
||||
DIR_ARCHIVE_DESCRIPTION,
|
||||
"Ryan C. Gordon <icculus@icculus.org>",
|
||||
"http://icculus.org/physfs/",
|
||||
};
|
||||
|
||||
|
||||
|
||||
const PHYSFS_Archiver __PHYSFS_Archiver_DIR =
|
||||
{
|
||||
&__PHYSFS_ArchiveInfo_DIR,
|
||||
DIR_isArchive, /* isArchive() method */
|
||||
DIR_openArchive, /* openArchive() method */
|
||||
DIR_enumerateFiles, /* enumerateFiles() method */
|
||||
DIR_exists, /* exists() method */
|
||||
DIR_isDirectory, /* isDirectory() method */
|
||||
DIR_isSymLink, /* isSymLink() method */
|
||||
DIR_getLastModTime, /* getLastModTime() method */
|
||||
DIR_openRead, /* openRead() method */
|
||||
DIR_openWrite, /* openWrite() method */
|
||||
DIR_openAppend, /* openAppend() method */
|
||||
DIR_remove, /* remove() method */
|
||||
DIR_mkdir, /* mkdir() method */
|
||||
DIR_dirClose, /* dirClose() method */
|
||||
DIR_read, /* read() method */
|
||||
DIR_write, /* write() method */
|
||||
DIR_eof, /* eof() method */
|
||||
DIR_tell, /* tell() method */
|
||||
DIR_seek, /* seek() method */
|
||||
DIR_fileLength, /* fileLength() method */
|
||||
DIR_fileClose /* fileClose() method */
|
||||
};
|
||||
|
||||
/* end of dir.c ... */
|
||||
|
||||
@@ -0,0 +1,475 @@
|
||||
/*
|
||||
* GRP support routines for PhysicsFS.
|
||||
*
|
||||
* This driver handles BUILD engine archives ("groupfiles"). This format
|
||||
* (but not this driver) was put together by Ken Silverman.
|
||||
*
|
||||
* The format is simple enough. In Ken's words:
|
||||
*
|
||||
* What's the .GRP file format?
|
||||
*
|
||||
* The ".grp" file format is just a collection of a lot of files stored
|
||||
* into 1 big one. I tried to make the format as simple as possible: The
|
||||
* first 12 bytes contains my name, "KenSilverman". The next 4 bytes is
|
||||
* the number of files that were compacted into the group file. Then for
|
||||
* each file, there is a 16 byte structure, where the first 12 bytes are
|
||||
* the filename, and the last 4 bytes are the file's size. The rest of
|
||||
* the group file is just the raw data packed one after the other in the
|
||||
* same order as the list of files.
|
||||
*
|
||||
* (That info is from http://www.advsys.net/ken/build.htm ...)
|
||||
*
|
||||
* Please see the file LICENSE.txt in the source's root directory.
|
||||
*
|
||||
* This file written by Ryan C. Gordon.
|
||||
*/
|
||||
|
||||
#if (defined PHYSFS_SUPPORTS_GRP)
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "physfs.h"
|
||||
|
||||
#define __PHYSICSFS_INTERNAL__
|
||||
#include "physfs_internal.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char name[13];
|
||||
PHYSFS_uint32 startPos;
|
||||
PHYSFS_uint32 size;
|
||||
} GRPentry;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *filename;
|
||||
PHYSFS_sint64 last_mod_time;
|
||||
PHYSFS_uint32 entryCount;
|
||||
GRPentry *entries;
|
||||
} GRPinfo;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void *handle;
|
||||
GRPentry *entry;
|
||||
PHYSFS_uint32 curPos;
|
||||
} GRPfileinfo;
|
||||
|
||||
|
||||
static void GRP_dirClose(dvoid *opaque)
|
||||
{
|
||||
GRPinfo *info = ((GRPinfo *) opaque);
|
||||
allocator.Free(info->filename);
|
||||
allocator.Free(info->entries);
|
||||
allocator.Free(info);
|
||||
} /* GRP_dirClose */
|
||||
|
||||
|
||||
static PHYSFS_sint64 GRP_read(fvoid *opaque, void *buffer,
|
||||
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
|
||||
{
|
||||
GRPfileinfo *finfo = (GRPfileinfo *) opaque;
|
||||
GRPentry *entry = finfo->entry;
|
||||
PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos;
|
||||
PHYSFS_uint32 objsLeft = (bytesLeft / objSize);
|
||||
PHYSFS_sint64 rc;
|
||||
|
||||
if (objsLeft < objCount)
|
||||
objCount = objsLeft;
|
||||
|
||||
rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount);
|
||||
if (rc > 0)
|
||||
finfo->curPos += (PHYSFS_uint32) (rc * objSize);
|
||||
|
||||
return(rc);
|
||||
} /* GRP_read */
|
||||
|
||||
|
||||
static PHYSFS_sint64 GRP_write(fvoid *opaque, const void *buffer,
|
||||
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
|
||||
} /* GRP_write */
|
||||
|
||||
|
||||
static int GRP_eof(fvoid *opaque)
|
||||
{
|
||||
GRPfileinfo *finfo = (GRPfileinfo *) opaque;
|
||||
GRPentry *entry = finfo->entry;
|
||||
return(finfo->curPos >= entry->size);
|
||||
} /* GRP_eof */
|
||||
|
||||
|
||||
static PHYSFS_sint64 GRP_tell(fvoid *opaque)
|
||||
{
|
||||
return(((GRPfileinfo *) opaque)->curPos);
|
||||
} /* GRP_tell */
|
||||
|
||||
|
||||
static int GRP_seek(fvoid *opaque, PHYSFS_uint64 offset)
|
||||
{
|
||||
GRPfileinfo *finfo = (GRPfileinfo *) opaque;
|
||||
GRPentry *entry = finfo->entry;
|
||||
int rc;
|
||||
|
||||
BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
|
||||
BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0);
|
||||
rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset);
|
||||
if (rc)
|
||||
finfo->curPos = (PHYSFS_uint32) offset;
|
||||
|
||||
return(rc);
|
||||
} /* GRP_seek */
|
||||
|
||||
|
||||
static PHYSFS_sint64 GRP_fileLength(fvoid *opaque)
|
||||
{
|
||||
GRPfileinfo *finfo = (GRPfileinfo *) opaque;
|
||||
return((PHYSFS_sint64) finfo->entry->size);
|
||||
} /* GRP_fileLength */
|
||||
|
||||
|
||||
static int GRP_fileClose(fvoid *opaque)
|
||||
{
|
||||
GRPfileinfo *finfo = (GRPfileinfo *) opaque;
|
||||
BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
|
||||
allocator.Free(finfo);
|
||||
return(1);
|
||||
} /* GRP_fileClose */
|
||||
|
||||
|
||||
static int grp_open(const char *filename, int forWriting,
|
||||
void **fh, PHYSFS_uint32 *count)
|
||||
{
|
||||
PHYSFS_uint8 buf[12];
|
||||
|
||||
*fh = NULL;
|
||||
BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
|
||||
|
||||
*fh = __PHYSFS_platformOpenRead(filename);
|
||||
BAIL_IF_MACRO(*fh == NULL, NULL, 0);
|
||||
|
||||
if (__PHYSFS_platformRead(*fh, buf, 12, 1) != 1)
|
||||
goto openGrp_failed;
|
||||
|
||||
if (memcmp(buf, "KenSilverman", 12) != 0)
|
||||
{
|
||||
__PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
|
||||
goto openGrp_failed;
|
||||
} /* if */
|
||||
|
||||
if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1)
|
||||
goto openGrp_failed;
|
||||
|
||||
*count = PHYSFS_swapULE32(*count);
|
||||
|
||||
return(1);
|
||||
|
||||
openGrp_failed:
|
||||
if (*fh != NULL)
|
||||
__PHYSFS_platformClose(*fh);
|
||||
|
||||
*count = -1;
|
||||
*fh = NULL;
|
||||
return(0);
|
||||
} /* grp_open */
|
||||
|
||||
|
||||
static int GRP_isArchive(const char *filename, int forWriting)
|
||||
{
|
||||
void *fh;
|
||||
PHYSFS_uint32 fileCount;
|
||||
int retval = grp_open(filename, forWriting, &fh, &fileCount);
|
||||
|
||||
if (fh != NULL)
|
||||
__PHYSFS_platformClose(fh);
|
||||
|
||||
return(retval);
|
||||
} /* GRP_isArchive */
|
||||
|
||||
|
||||
static int grp_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
|
||||
{
|
||||
if (one != two)
|
||||
{
|
||||
const GRPentry *a = (const GRPentry *) _a;
|
||||
return(strcmp(a[one].name, a[two].name));
|
||||
} /* if */
|
||||
|
||||
return 0;
|
||||
} /* grp_entry_cmp */
|
||||
|
||||
|
||||
static void grp_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
|
||||
{
|
||||
if (one != two)
|
||||
{
|
||||
GRPentry tmp;
|
||||
GRPentry *first = &(((GRPentry *) _a)[one]);
|
||||
GRPentry *second = &(((GRPentry *) _a)[two]);
|
||||
memcpy(&tmp, first, sizeof (GRPentry));
|
||||
memcpy(first, second, sizeof (GRPentry));
|
||||
memcpy(second, &tmp, sizeof (GRPentry));
|
||||
} /* if */
|
||||
} /* grp_entry_swap */
|
||||
|
||||
|
||||
static int grp_load_entries(const char *name, int forWriting, GRPinfo *info)
|
||||
{
|
||||
void *fh = NULL;
|
||||
PHYSFS_uint32 fileCount;
|
||||
PHYSFS_uint32 location = 16; /* sizeof sig. */
|
||||
GRPentry *entry;
|
||||
char *ptr;
|
||||
|
||||
BAIL_IF_MACRO(!grp_open(name, forWriting, &fh, &fileCount), NULL, 0);
|
||||
info->entryCount = fileCount;
|
||||
info->entries = (GRPentry *) allocator.Malloc(sizeof(GRPentry)*fileCount);
|
||||
if (info->entries == NULL)
|
||||
{
|
||||
__PHYSFS_platformClose(fh);
|
||||
BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
|
||||
} /* if */
|
||||
|
||||
location += (16 * fileCount);
|
||||
|
||||
for (entry = info->entries; fileCount > 0; fileCount--, entry++)
|
||||
{
|
||||
if (__PHYSFS_platformRead(fh, &entry->name, 12, 1) != 1)
|
||||
{
|
||||
__PHYSFS_platformClose(fh);
|
||||
return(0);
|
||||
} /* if */
|
||||
|
||||
entry->name[12] = '\0'; /* name isn't null-terminated in file. */
|
||||
if ((ptr = strchr(entry->name, ' ')) != NULL)
|
||||
*ptr = '\0'; /* trim extra spaces. */
|
||||
|
||||
if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1)
|
||||
{
|
||||
__PHYSFS_platformClose(fh);
|
||||
return(0);
|
||||
} /* if */
|
||||
|
||||
entry->size = PHYSFS_swapULE32(entry->size);
|
||||
entry->startPos = location;
|
||||
location += entry->size;
|
||||
} /* for */
|
||||
|
||||
__PHYSFS_platformClose(fh);
|
||||
|
||||
__PHYSFS_sort(info->entries, info->entryCount,
|
||||
grp_entry_cmp, grp_entry_swap);
|
||||
return(1);
|
||||
} /* grp_load_entries */
|
||||
|
||||
|
||||
static void *GRP_openArchive(const char *name, int forWriting)
|
||||
{
|
||||
PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name);
|
||||
GRPinfo *info = (GRPinfo *) allocator.Malloc(sizeof (GRPinfo));
|
||||
|
||||
BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, 0);
|
||||
|
||||
memset(info, '\0', sizeof (GRPinfo));
|
||||
info->filename = (char *) allocator.Malloc(strlen(name) + 1);
|
||||
GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, GRP_openArchive_failed);
|
||||
|
||||
if (!grp_load_entries(name, forWriting, info))
|
||||
goto GRP_openArchive_failed;
|
||||
|
||||
strcpy(info->filename, name);
|
||||
info->last_mod_time = modtime;
|
||||
|
||||
return(info);
|
||||
|
||||
GRP_openArchive_failed:
|
||||
if (info != NULL)
|
||||
{
|
||||
if (info->filename != NULL)
|
||||
allocator.Free(info->filename);
|
||||
if (info->entries != NULL)
|
||||
allocator.Free(info->entries);
|
||||
allocator.Free(info);
|
||||
} /* if */
|
||||
|
||||
return(NULL);
|
||||
} /* GRP_openArchive */
|
||||
|
||||
|
||||
static void GRP_enumerateFiles(dvoid *opaque, const char *dname,
|
||||
int omitSymLinks, PHYSFS_EnumFilesCallback cb,
|
||||
const char *origdir, void *callbackdata)
|
||||
{
|
||||
/* no directories in GRP files. */
|
||||
if (*dname == '\0')
|
||||
{
|
||||
GRPinfo *info = (GRPinfo *) opaque;
|
||||
GRPentry *entry = info->entries;
|
||||
PHYSFS_uint32 max = info->entryCount;
|
||||
PHYSFS_uint32 i;
|
||||
|
||||
for (i = 0; i < max; i++, entry++)
|
||||
cb(callbackdata, origdir, entry->name);
|
||||
} /* if */
|
||||
} /* GRP_enumerateFiles */
|
||||
|
||||
|
||||
static GRPentry *grp_find_entry(GRPinfo *info, const char *name)
|
||||
{
|
||||
char *ptr = strchr(name, '.');
|
||||
GRPentry *a = info->entries;
|
||||
PHYSFS_sint32 lo = 0;
|
||||
PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
|
||||
PHYSFS_sint32 middle;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* Rule out filenames to avoid unneeded processing...no dirs,
|
||||
* big filenames, or extensions > 3 chars.
|
||||
*/
|
||||
BAIL_IF_MACRO((ptr) && (strlen(ptr) > 4), ERR_NO_SUCH_FILE, NULL);
|
||||
BAIL_IF_MACRO(strlen(name) > 12, ERR_NO_SUCH_FILE, NULL);
|
||||
BAIL_IF_MACRO(strchr(name, '/') != NULL, ERR_NO_SUCH_FILE, NULL);
|
||||
|
||||
while (lo <= hi)
|
||||
{
|
||||
middle = lo + ((hi - lo) / 2);
|
||||
rc = strcmp(name, a[middle].name);
|
||||
if (rc == 0) /* found it! */
|
||||
return(&a[middle]);
|
||||
else if (rc > 0)
|
||||
lo = middle + 1;
|
||||
else
|
||||
hi = middle - 1;
|
||||
} /* while */
|
||||
|
||||
BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
|
||||
} /* grp_find_entry */
|
||||
|
||||
|
||||
static int GRP_exists(dvoid *opaque, const char *name)
|
||||
{
|
||||
return(grp_find_entry((GRPinfo *) opaque, name) != NULL);
|
||||
} /* GRP_exists */
|
||||
|
||||
|
||||
static int GRP_isDirectory(dvoid *opaque, const char *name, int *fileExists)
|
||||
{
|
||||
*fileExists = GRP_exists(opaque, name);
|
||||
return(0); /* never directories in a groupfile. */
|
||||
} /* GRP_isDirectory */
|
||||
|
||||
|
||||
static int GRP_isSymLink(dvoid *opaque, const char *name, int *fileExists)
|
||||
{
|
||||
*fileExists = GRP_exists(opaque, name);
|
||||
return(0); /* never symlinks in a groupfile. */
|
||||
} /* GRP_isSymLink */
|
||||
|
||||
|
||||
static PHYSFS_sint64 GRP_getLastModTime(dvoid *opaque,
|
||||
const char *name,
|
||||
int *fileExists)
|
||||
{
|
||||
GRPinfo *info = (GRPinfo *) opaque;
|
||||
PHYSFS_sint64 retval = -1;
|
||||
|
||||
*fileExists = (grp_find_entry(info, name) != NULL);
|
||||
if (*fileExists) /* use time of GRP itself in the physical filesystem. */
|
||||
retval = info->last_mod_time;
|
||||
|
||||
return(retval);
|
||||
} /* GRP_getLastModTime */
|
||||
|
||||
|
||||
static fvoid *GRP_openRead(dvoid *opaque, const char *fnm, int *fileExists)
|
||||
{
|
||||
GRPinfo *info = (GRPinfo *) opaque;
|
||||
GRPfileinfo *finfo;
|
||||
GRPentry *entry;
|
||||
|
||||
entry = grp_find_entry(info, fnm);
|
||||
*fileExists = (entry != NULL);
|
||||
BAIL_IF_MACRO(entry == NULL, NULL, NULL);
|
||||
|
||||
finfo = (GRPfileinfo *) allocator.Malloc(sizeof (GRPfileinfo));
|
||||
BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
|
||||
|
||||
finfo->handle = __PHYSFS_platformOpenRead(info->filename);
|
||||
if ( (finfo->handle == NULL) ||
|
||||
(!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
|
||||
{
|
||||
allocator.Free(finfo);
|
||||
return(NULL);
|
||||
} /* if */
|
||||
|
||||
finfo->curPos = 0;
|
||||
finfo->entry = entry;
|
||||
return(finfo);
|
||||
} /* GRP_openRead */
|
||||
|
||||
|
||||
static fvoid *GRP_openWrite(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
|
||||
} /* GRP_openWrite */
|
||||
|
||||
|
||||
static fvoid *GRP_openAppend(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
|
||||
} /* GRP_openAppend */
|
||||
|
||||
|
||||
static int GRP_remove(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
|
||||
} /* GRP_remove */
|
||||
|
||||
|
||||
static int GRP_mkdir(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
|
||||
} /* GRP_mkdir */
|
||||
|
||||
|
||||
const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_GRP =
|
||||
{
|
||||
"GRP",
|
||||
GRP_ARCHIVE_DESCRIPTION,
|
||||
"Ryan C. Gordon <icculus@icculus.org>",
|
||||
"http://icculus.org/physfs/",
|
||||
};
|
||||
|
||||
|
||||
const PHYSFS_Archiver __PHYSFS_Archiver_GRP =
|
||||
{
|
||||
&__PHYSFS_ArchiveInfo_GRP,
|
||||
GRP_isArchive, /* isArchive() method */
|
||||
GRP_openArchive, /* openArchive() method */
|
||||
GRP_enumerateFiles, /* enumerateFiles() method */
|
||||
GRP_exists, /* exists() method */
|
||||
GRP_isDirectory, /* isDirectory() method */
|
||||
GRP_isSymLink, /* isSymLink() method */
|
||||
GRP_getLastModTime, /* getLastModTime() method */
|
||||
GRP_openRead, /* openRead() method */
|
||||
GRP_openWrite, /* openWrite() method */
|
||||
GRP_openAppend, /* openAppend() method */
|
||||
GRP_remove, /* remove() method */
|
||||
GRP_mkdir, /* mkdir() method */
|
||||
GRP_dirClose, /* dirClose() method */
|
||||
GRP_read, /* read() method */
|
||||
GRP_write, /* write() method */
|
||||
GRP_eof, /* eof() method */
|
||||
GRP_tell, /* tell() method */
|
||||
GRP_seek, /* seek() method */
|
||||
GRP_fileLength, /* fileLength() method */
|
||||
GRP_fileClose /* fileClose() method */
|
||||
};
|
||||
|
||||
#endif /* defined PHYSFS_SUPPORTS_GRP */
|
||||
|
||||
/* end of grp.c ... */
|
||||
|
||||
@@ -0,0 +1,514 @@
|
||||
/*
|
||||
* HOG support routines for PhysicsFS.
|
||||
*
|
||||
* This driver handles Descent I/II HOG archives.
|
||||
*
|
||||
* The format is very simple:
|
||||
*
|
||||
* The file always starts with the 3-byte signature "DHF" (Descent
|
||||
* HOG file). After that the files of a HOG are just attached after
|
||||
* another, divided by a 17 bytes header, which specifies the name
|
||||
* and length (in bytes) of the forthcoming file! So you just read
|
||||
* the header with its information of how big the following file is,
|
||||
* and then skip exact that number of bytes to get to the next file
|
||||
* in that HOG.
|
||||
*
|
||||
* char sig[3] = {'D', 'H', 'F'}; // "DHF"=Descent HOG File
|
||||
*
|
||||
* struct {
|
||||
* char file_name[13]; // Filename, padded to 13 bytes with 0s
|
||||
* int file_size; // filesize in bytes
|
||||
* char data[file_size]; // The file data
|
||||
* } FILE_STRUCT; // Repeated until the end of the file.
|
||||
*
|
||||
* (That info is from http://www.descent2.com/ddn/specs/hog/)
|
||||
*
|
||||
* Please see the file LICENSE.txt in the source's root directory.
|
||||
*
|
||||
* This file written by Bradley Bell.
|
||||
* Based on grp.c by Ryan C. Gordon.
|
||||
*/
|
||||
|
||||
#if (defined PHYSFS_SUPPORTS_HOG)
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "physfs.h"
|
||||
|
||||
#define __PHYSICSFS_INTERNAL__
|
||||
#include "physfs_internal.h"
|
||||
|
||||
/*
|
||||
* One HOGentry is kept for each file in an open HOG archive.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
char name[13];
|
||||
PHYSFS_uint32 startPos;
|
||||
PHYSFS_uint32 size;
|
||||
} HOGentry;
|
||||
|
||||
/*
|
||||
* One HOGinfo is kept for each open HOG archive.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
char *filename;
|
||||
PHYSFS_sint64 last_mod_time;
|
||||
PHYSFS_uint32 entryCount;
|
||||
HOGentry *entries;
|
||||
} HOGinfo;
|
||||
|
||||
/*
|
||||
* One HOGfileinfo is kept for each open file in a HOG archive.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
void *handle;
|
||||
HOGentry *entry;
|
||||
PHYSFS_uint32 curPos;
|
||||
} HOGfileinfo;
|
||||
|
||||
|
||||
static void HOG_dirClose(dvoid *opaque)
|
||||
{
|
||||
HOGinfo *info = ((HOGinfo *) opaque);
|
||||
allocator.Free(info->filename);
|
||||
allocator.Free(info->entries);
|
||||
allocator.Free(info);
|
||||
} /* HOG_dirClose */
|
||||
|
||||
|
||||
static PHYSFS_sint64 HOG_read(fvoid *opaque, void *buffer,
|
||||
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
|
||||
{
|
||||
HOGfileinfo *finfo = (HOGfileinfo *) opaque;
|
||||
HOGentry *entry = finfo->entry;
|
||||
PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos;
|
||||
PHYSFS_uint32 objsLeft = (bytesLeft / objSize);
|
||||
PHYSFS_sint64 rc;
|
||||
|
||||
if (objsLeft < objCount)
|
||||
objCount = objsLeft;
|
||||
|
||||
rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount);
|
||||
if (rc > 0)
|
||||
finfo->curPos += (PHYSFS_uint32) (rc * objSize);
|
||||
|
||||
return(rc);
|
||||
} /* HOG_read */
|
||||
|
||||
|
||||
static PHYSFS_sint64 HOG_write(fvoid *opaque, const void *buffer,
|
||||
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
|
||||
} /* HOG_write */
|
||||
|
||||
|
||||
static int HOG_eof(fvoid *opaque)
|
||||
{
|
||||
HOGfileinfo *finfo = (HOGfileinfo *) opaque;
|
||||
HOGentry *entry = finfo->entry;
|
||||
return(finfo->curPos >= entry->size);
|
||||
} /* HOG_eof */
|
||||
|
||||
|
||||
static PHYSFS_sint64 HOG_tell(fvoid *opaque)
|
||||
{
|
||||
return(((HOGfileinfo *) opaque)->curPos);
|
||||
} /* HOG_tell */
|
||||
|
||||
|
||||
static int HOG_seek(fvoid *opaque, PHYSFS_uint64 offset)
|
||||
{
|
||||
HOGfileinfo *finfo = (HOGfileinfo *) opaque;
|
||||
HOGentry *entry = finfo->entry;
|
||||
int rc;
|
||||
|
||||
BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
|
||||
BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0);
|
||||
rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset);
|
||||
if (rc)
|
||||
finfo->curPos = (PHYSFS_uint32) offset;
|
||||
|
||||
return(rc);
|
||||
} /* HOG_seek */
|
||||
|
||||
|
||||
static PHYSFS_sint64 HOG_fileLength(fvoid *opaque)
|
||||
{
|
||||
HOGfileinfo *finfo = (HOGfileinfo *) opaque;
|
||||
return((PHYSFS_sint64) finfo->entry->size);
|
||||
} /* HOG_fileLength */
|
||||
|
||||
|
||||
static int HOG_fileClose(fvoid *opaque)
|
||||
{
|
||||
HOGfileinfo *finfo = (HOGfileinfo *) opaque;
|
||||
BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
|
||||
allocator.Free(finfo);
|
||||
return(1);
|
||||
} /* HOG_fileClose */
|
||||
|
||||
|
||||
static int hog_open(const char *filename, int forWriting,
|
||||
void **fh, PHYSFS_uint32 *count)
|
||||
{
|
||||
PHYSFS_uint8 buf[13];
|
||||
PHYSFS_uint32 size;
|
||||
PHYSFS_sint64 pos;
|
||||
|
||||
*count = 0;
|
||||
|
||||
*fh = NULL;
|
||||
BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
|
||||
|
||||
*fh = __PHYSFS_platformOpenRead(filename);
|
||||
BAIL_IF_MACRO(*fh == NULL, NULL, 0);
|
||||
|
||||
if (__PHYSFS_platformRead(*fh, buf, 3, 1) != 1)
|
||||
goto openHog_failed;
|
||||
|
||||
if (memcmp(buf, "DHF", 3) != 0)
|
||||
{
|
||||
__PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
|
||||
goto openHog_failed;
|
||||
} /* if */
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (__PHYSFS_platformRead(*fh, buf, 13, 1) != 1)
|
||||
break; /* eof here is ok */
|
||||
|
||||
if (__PHYSFS_platformRead(*fh, &size, 4, 1) != 1)
|
||||
goto openHog_failed;
|
||||
|
||||
size = PHYSFS_swapULE32(size);
|
||||
|
||||
(*count)++;
|
||||
|
||||
/* Skip over entry... */
|
||||
pos = __PHYSFS_platformTell(*fh);
|
||||
if (pos == -1)
|
||||
goto openHog_failed;
|
||||
if (!__PHYSFS_platformSeek(*fh, pos + size))
|
||||
goto openHog_failed;
|
||||
} /* while */
|
||||
|
||||
/* Rewind to start of entries... */
|
||||
if (!__PHYSFS_platformSeek(*fh, 3))
|
||||
goto openHog_failed;
|
||||
|
||||
return(1);
|
||||
|
||||
openHog_failed:
|
||||
if (*fh != NULL)
|
||||
__PHYSFS_platformClose(*fh);
|
||||
|
||||
*count = -1;
|
||||
*fh = NULL;
|
||||
return(0);
|
||||
} /* hog_open */
|
||||
|
||||
|
||||
static int HOG_isArchive(const char *filename, int forWriting)
|
||||
{
|
||||
void *fh;
|
||||
PHYSFS_uint32 fileCount;
|
||||
int retval = hog_open(filename, forWriting, &fh, &fileCount);
|
||||
|
||||
if (fh != NULL)
|
||||
__PHYSFS_platformClose(fh);
|
||||
|
||||
return(retval);
|
||||
} /* HOG_isArchive */
|
||||
|
||||
|
||||
static int hog_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
|
||||
{
|
||||
if (one != two)
|
||||
{
|
||||
const HOGentry *a = (const HOGentry *) _a;
|
||||
return(__PHYSFS_stricmpASCII(a[one].name, a[two].name));
|
||||
} /* if */
|
||||
|
||||
return 0;
|
||||
} /* hog_entry_cmp */
|
||||
|
||||
|
||||
static void hog_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
|
||||
{
|
||||
if (one != two)
|
||||
{
|
||||
HOGentry tmp;
|
||||
HOGentry *first = &(((HOGentry *) _a)[one]);
|
||||
HOGentry *second = &(((HOGentry *) _a)[two]);
|
||||
memcpy(&tmp, first, sizeof (HOGentry));
|
||||
memcpy(first, second, sizeof (HOGentry));
|
||||
memcpy(second, &tmp, sizeof (HOGentry));
|
||||
} /* if */
|
||||
} /* hog_entry_swap */
|
||||
|
||||
|
||||
static int hog_load_entries(const char *name, int forWriting, HOGinfo *info)
|
||||
{
|
||||
void *fh = NULL;
|
||||
PHYSFS_uint32 fileCount;
|
||||
HOGentry *entry;
|
||||
|
||||
BAIL_IF_MACRO(!hog_open(name, forWriting, &fh, &fileCount), NULL, 0);
|
||||
info->entryCount = fileCount;
|
||||
info->entries = (HOGentry *) allocator.Malloc(sizeof(HOGentry)*fileCount);
|
||||
if (info->entries == NULL)
|
||||
{
|
||||
__PHYSFS_platformClose(fh);
|
||||
BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
|
||||
} /* if */
|
||||
|
||||
for (entry = info->entries; fileCount > 0; fileCount--, entry++)
|
||||
{
|
||||
if (__PHYSFS_platformRead(fh, &entry->name, 13, 1) != 1)
|
||||
{
|
||||
__PHYSFS_platformClose(fh);
|
||||
return(0);
|
||||
} /* if */
|
||||
|
||||
if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1)
|
||||
{
|
||||
__PHYSFS_platformClose(fh);
|
||||
return(0);
|
||||
} /* if */
|
||||
|
||||
entry->size = PHYSFS_swapULE32(entry->size);
|
||||
entry->startPos = (unsigned int) __PHYSFS_platformTell(fh);
|
||||
if (entry->startPos == -1)
|
||||
{
|
||||
__PHYSFS_platformClose(fh);
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* Skip over entry */
|
||||
if (!__PHYSFS_platformSeek(fh, entry->startPos + entry->size))
|
||||
{
|
||||
__PHYSFS_platformClose(fh);
|
||||
return(0);
|
||||
}
|
||||
} /* for */
|
||||
|
||||
__PHYSFS_platformClose(fh);
|
||||
|
||||
__PHYSFS_sort(info->entries, info->entryCount,
|
||||
hog_entry_cmp, hog_entry_swap);
|
||||
return(1);
|
||||
} /* hog_load_entries */
|
||||
|
||||
|
||||
static void *HOG_openArchive(const char *name, int forWriting)
|
||||
{
|
||||
PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name);
|
||||
HOGinfo *info = (HOGinfo *) allocator.Malloc(sizeof (HOGinfo));
|
||||
|
||||
BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, 0);
|
||||
memset(info, '\0', sizeof (HOGinfo));
|
||||
info->filename = (char *) allocator.Malloc(strlen(name) + 1);
|
||||
GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, HOG_openArchive_failed);
|
||||
|
||||
if (!hog_load_entries(name, forWriting, info))
|
||||
goto HOG_openArchive_failed;
|
||||
|
||||
strcpy(info->filename, name);
|
||||
info->last_mod_time = modtime;
|
||||
|
||||
return(info);
|
||||
|
||||
HOG_openArchive_failed:
|
||||
if (info != NULL)
|
||||
{
|
||||
if (info->filename != NULL)
|
||||
allocator.Free(info->filename);
|
||||
if (info->entries != NULL)
|
||||
allocator.Free(info->entries);
|
||||
allocator.Free(info);
|
||||
} /* if */
|
||||
|
||||
return(NULL);
|
||||
} /* HOG_openArchive */
|
||||
|
||||
|
||||
static void HOG_enumerateFiles(dvoid *opaque, const char *dname,
|
||||
int omitSymLinks, PHYSFS_EnumFilesCallback cb,
|
||||
const char *origdir, void *callbackdata)
|
||||
{
|
||||
/* no directories in HOG files. */
|
||||
if (*dname == '\0')
|
||||
{
|
||||
HOGinfo *info = (HOGinfo *) opaque;
|
||||
HOGentry *entry = info->entries;
|
||||
PHYSFS_uint32 max = info->entryCount;
|
||||
PHYSFS_uint32 i;
|
||||
|
||||
for (i = 0; i < max; i++, entry++)
|
||||
cb(callbackdata, origdir, entry->name);
|
||||
} /* if */
|
||||
} /* HOG_enumerateFiles */
|
||||
|
||||
|
||||
static HOGentry *hog_find_entry(HOGinfo *info, const char *name)
|
||||
{
|
||||
char *ptr = strchr(name, '.');
|
||||
HOGentry *a = info->entries;
|
||||
PHYSFS_sint32 lo = 0;
|
||||
PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
|
||||
PHYSFS_sint32 middle;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* Rule out filenames to avoid unneeded processing...no dirs,
|
||||
* big filenames, or extensions > 3 chars.
|
||||
*/
|
||||
BAIL_IF_MACRO((ptr) && (strlen(ptr) > 4), ERR_NO_SUCH_FILE, NULL);
|
||||
BAIL_IF_MACRO(strlen(name) > 12, ERR_NO_SUCH_FILE, NULL);
|
||||
BAIL_IF_MACRO(strchr(name, '/') != NULL, ERR_NO_SUCH_FILE, NULL);
|
||||
|
||||
while (lo <= hi)
|
||||
{
|
||||
middle = lo + ((hi - lo) / 2);
|
||||
rc = __PHYSFS_stricmpASCII(name, a[middle].name);
|
||||
if (rc == 0) /* found it! */
|
||||
return(&a[middle]);
|
||||
else if (rc > 0)
|
||||
lo = middle + 1;
|
||||
else
|
||||
hi = middle - 1;
|
||||
} /* while */
|
||||
|
||||
BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
|
||||
} /* hog_find_entry */
|
||||
|
||||
|
||||
static int HOG_exists(dvoid *opaque, const char *name)
|
||||
{
|
||||
return(hog_find_entry(((HOGinfo *) opaque), name) != NULL);
|
||||
} /* HOG_exists */
|
||||
|
||||
|
||||
static int HOG_isDirectory(dvoid *opaque, const char *name, int *fileExists)
|
||||
{
|
||||
*fileExists = HOG_exists(opaque, name);
|
||||
return(0); /* never directories in a groupfile. */
|
||||
} /* HOG_isDirectory */
|
||||
|
||||
|
||||
static int HOG_isSymLink(dvoid *opaque, const char *name, int *fileExists)
|
||||
{
|
||||
*fileExists = HOG_exists(opaque, name);
|
||||
return(0); /* never symlinks in a groupfile. */
|
||||
} /* HOG_isSymLink */
|
||||
|
||||
|
||||
static PHYSFS_sint64 HOG_getLastModTime(dvoid *opaque,
|
||||
const char *name,
|
||||
int *fileExists)
|
||||
{
|
||||
HOGinfo *info = ((HOGinfo *) opaque);
|
||||
PHYSFS_sint64 retval = -1;
|
||||
|
||||
*fileExists = (hog_find_entry(info, name) != NULL);
|
||||
if (*fileExists) /* use time of HOG itself in the physical filesystem. */
|
||||
retval = info->last_mod_time;
|
||||
|
||||
return(retval);
|
||||
} /* HOG_getLastModTime */
|
||||
|
||||
|
||||
static fvoid *HOG_openRead(dvoid *opaque, const char *fnm, int *fileExists)
|
||||
{
|
||||
HOGinfo *info = ((HOGinfo *) opaque);
|
||||
HOGfileinfo *finfo;
|
||||
HOGentry *entry;
|
||||
|
||||
entry = hog_find_entry(info, fnm);
|
||||
*fileExists = (entry != NULL);
|
||||
BAIL_IF_MACRO(entry == NULL, NULL, NULL);
|
||||
|
||||
finfo = (HOGfileinfo *) allocator.Malloc(sizeof (HOGfileinfo));
|
||||
BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
|
||||
|
||||
finfo->handle = __PHYSFS_platformOpenRead(info->filename);
|
||||
if ( (finfo->handle == NULL) ||
|
||||
(!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
|
||||
{
|
||||
allocator.Free(finfo);
|
||||
return(NULL);
|
||||
} /* if */
|
||||
|
||||
finfo->curPos = 0;
|
||||
finfo->entry = entry;
|
||||
return(finfo);
|
||||
} /* HOG_openRead */
|
||||
|
||||
|
||||
static fvoid *HOG_openWrite(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
|
||||
} /* HOG_openWrite */
|
||||
|
||||
|
||||
static fvoid *HOG_openAppend(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
|
||||
} /* HOG_openAppend */
|
||||
|
||||
|
||||
static int HOG_remove(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
|
||||
} /* HOG_remove */
|
||||
|
||||
|
||||
static int HOG_mkdir(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
|
||||
} /* HOG_mkdir */
|
||||
|
||||
|
||||
const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_HOG =
|
||||
{
|
||||
"HOG",
|
||||
HOG_ARCHIVE_DESCRIPTION,
|
||||
"Bradley Bell <btb@icculus.org>",
|
||||
"http://icculus.org/physfs/",
|
||||
};
|
||||
|
||||
|
||||
const PHYSFS_Archiver __PHYSFS_Archiver_HOG =
|
||||
{
|
||||
&__PHYSFS_ArchiveInfo_HOG,
|
||||
HOG_isArchive, /* isArchive() method */
|
||||
HOG_openArchive, /* openArchive() method */
|
||||
HOG_enumerateFiles, /* enumerateFiles() method */
|
||||
HOG_exists, /* exists() method */
|
||||
HOG_isDirectory, /* isDirectory() method */
|
||||
HOG_isSymLink, /* isSymLink() method */
|
||||
HOG_getLastModTime, /* getLastModTime() method */
|
||||
HOG_openRead, /* openRead() method */
|
||||
HOG_openWrite, /* openWrite() method */
|
||||
HOG_openAppend, /* openAppend() method */
|
||||
HOG_remove, /* remove() method */
|
||||
HOG_mkdir, /* mkdir() method */
|
||||
HOG_dirClose, /* dirClose() method */
|
||||
HOG_read, /* read() method */
|
||||
HOG_write, /* write() method */
|
||||
HOG_eof, /* eof() method */
|
||||
HOG_tell, /* tell() method */
|
||||
HOG_seek, /* seek() method */
|
||||
HOG_fileLength, /* fileLength() method */
|
||||
HOG_fileClose /* fileClose() method */
|
||||
};
|
||||
|
||||
#endif /* defined PHYSFS_SUPPORTS_HOG */
|
||||
|
||||
/* end of hog.c ... */
|
||||
|
||||
@@ -0,0 +1,736 @@
|
||||
/*
|
||||
* LZMA support routines for PhysicsFS.
|
||||
*
|
||||
* Please see the file lzma.txt in the lzma/ directory.
|
||||
*
|
||||
* This file was written by Dennis Schridde, with some peeking at "7zMain.c"
|
||||
* by Igor Pavlov.
|
||||
*/
|
||||
|
||||
#if (defined PHYSFS_SUPPORTS_7Z)
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "physfs.h"
|
||||
|
||||
#define __PHYSICSFS_INTERNAL__
|
||||
#include "physfs_internal.h"
|
||||
|
||||
#include "lzma/C/7zCrc.h"
|
||||
#include "lzma/C/Archive/7z/7zIn.h"
|
||||
#include "lzma/C/Archive/7z/7zExtract.h"
|
||||
|
||||
|
||||
/* 7z internal from 7zIn.c */
|
||||
extern int TestSignatureCandidate(Byte *testBytes);
|
||||
|
||||
|
||||
#ifdef _LZMA_IN_CB
|
||||
# define BUFFER_SIZE (1 << 12)
|
||||
#endif /* _LZMA_IN_CB */
|
||||
|
||||
|
||||
/*
|
||||
* Carries filestream metadata through 7z
|
||||
*/
|
||||
typedef struct _FileInputStream
|
||||
{
|
||||
ISzAlloc allocImp; /* Allocation implementation, used by 7z */
|
||||
ISzAlloc allocTempImp; /* Temporary allocation implementation, used by 7z */
|
||||
ISzInStream inStream; /* Input stream with read callbacks, used by 7z */
|
||||
void *file; /* Filehandle, used by read implementation */
|
||||
#ifdef _LZMA_IN_CB
|
||||
Byte buffer[BUFFER_SIZE]; /* Buffer, used by read implementation */
|
||||
#endif /* _LZMA_IN_CB */
|
||||
} FileInputStream;
|
||||
|
||||
/*
|
||||
* In the 7z format archives are splited into blocks, those are called folders
|
||||
* Set by LZMA_read()
|
||||
*/
|
||||
typedef struct _LZMAfolder
|
||||
{
|
||||
PHYSFS_uint32 index; /* Index of folder in archive */
|
||||
PHYSFS_uint32 references; /* Number of files using this block */
|
||||
PHYSFS_uint8 *cache; /* Cached folder */
|
||||
size_t size; /* Size of folder */
|
||||
} LZMAfolder;
|
||||
|
||||
/*
|
||||
* Set by LZMA_openArchive(), except folder which gets it's values
|
||||
* in LZMA_read()
|
||||
*/
|
||||
typedef struct _LZMAarchive
|
||||
{
|
||||
struct _LZMAfile *files; /* Array of files, size == archive->db.Database.NumFiles */
|
||||
LZMAfolder *folders; /* Array of folders, size == archive->db.Database.NumFolders */
|
||||
CArchiveDatabaseEx db; /* For 7z: Database */
|
||||
FileInputStream stream; /* For 7z: Input file incl. read and seek callbacks */
|
||||
} LZMAarchive;
|
||||
|
||||
/* Set by LZMA_openArchive(), except offset which is set by LZMA_read() */
|
||||
typedef struct _LZMAfile
|
||||
{
|
||||
PHYSFS_uint32 index; /* Index of file in archive */
|
||||
LZMAarchive *archive; /* Link to corresponding archive */
|
||||
LZMAfolder *folder; /* Link to corresponding folder */
|
||||
CFileItem *item; /* For 7z: File info, eg. name, size */
|
||||
size_t offset; /* Offset in folder */
|
||||
size_t position; /* Current "virtual" position in file */
|
||||
} LZMAfile;
|
||||
|
||||
|
||||
/* Memory management implementations to be passed to 7z */
|
||||
|
||||
static void *SzAllocPhysicsFS(size_t size)
|
||||
{
|
||||
return ((size == 0) ? NULL : allocator.Malloc(size));
|
||||
} /* SzAllocPhysicsFS */
|
||||
|
||||
|
||||
static void SzFreePhysicsFS(void *address)
|
||||
{
|
||||
if (address != NULL)
|
||||
allocator.Free(address);
|
||||
} /* SzFreePhysicsFS */
|
||||
|
||||
|
||||
/* Filesystem implementations to be passed to 7z */
|
||||
|
||||
#ifdef _LZMA_IN_CB
|
||||
|
||||
/*
|
||||
* Read implementation, to be passed to 7z
|
||||
* WARNING: If the ISzInStream in 'object' is not contained in a valid FileInputStream this _will_ break horribly!
|
||||
*/
|
||||
SZ_RESULT SzFileReadImp(void *object, void **buffer, size_t maxReqSize,
|
||||
size_t *processedSize)
|
||||
{
|
||||
FileInputStream *s = (FileInputStream *)(object - offsetof(FileInputStream, inStream)); /* HACK! */
|
||||
PHYSFS_sint64 processedSizeLoc = 0;
|
||||
|
||||
if (maxReqSize > BUFFER_SIZE)
|
||||
maxReqSize = BUFFER_SIZE;
|
||||
processedSizeLoc = __PHYSFS_platformRead(s->file, s->buffer, 1, maxReqSize);
|
||||
*buffer = s->buffer;
|
||||
if (processedSize != NULL)
|
||||
*processedSize = (size_t) processedSizeLoc;
|
||||
|
||||
return SZ_OK;
|
||||
} /* SzFileReadImp */
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Read implementation, to be passed to 7z
|
||||
* WARNING: If the ISzInStream in 'object' is not contained in a valid FileInputStream this _will_ break horribly!
|
||||
*/
|
||||
SZ_RESULT SzFileReadImp(void *object, void *buffer, size_t size,
|
||||
size_t *processedSize)
|
||||
{
|
||||
FileInputStream *s = (FileInputStream *)((unsigned long)object - offsetof(FileInputStream, inStream)); /* HACK! */
|
||||
size_t processedSizeLoc = __PHYSFS_platformRead(s->file, buffer, 1, size);
|
||||
if (processedSize != 0)
|
||||
*processedSize = processedSizeLoc;
|
||||
return SZ_OK;
|
||||
} /* SzFileReadImp */
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Seek implementation, to be passed to 7z
|
||||
* WARNING: If the ISzInStream in 'object' is not contained in a valid FileInputStream this _will_ break horribly!
|
||||
*/
|
||||
SZ_RESULT SzFileSeekImp(void *object, CFileSize pos)
|
||||
{
|
||||
FileInputStream *s = (FileInputStream *)((unsigned long)object - offsetof(FileInputStream, inStream)); /* HACK! */
|
||||
if (__PHYSFS_platformSeek(s->file, (PHYSFS_uint64) pos))
|
||||
return SZ_OK;
|
||||
return SZE_FAIL;
|
||||
} /* SzFileSeekImp */
|
||||
|
||||
|
||||
/*
|
||||
* Translate Microsoft FILETIME (used by 7zip) into UNIX timestamp
|
||||
*/
|
||||
static PHYSFS_sint64 lzma_filetime_to_unix_timestamp(CArchiveFileTime *ft)
|
||||
{
|
||||
/* MS counts in nanoseconds ... */
|
||||
const PHYSFS_uint64 FILETIME_NANOTICKS_PER_SECOND = __PHYSFS_UI64(10000000);
|
||||
/* MS likes to count seconds since 01.01.1601 ... */
|
||||
const PHYSFS_uint64 FILETIME_UNIX_DIFF = __PHYSFS_UI64(11644473600);
|
||||
|
||||
PHYSFS_uint64 filetime = ft->Low | ((PHYSFS_uint64)ft->High << 32);
|
||||
return filetime/FILETIME_NANOTICKS_PER_SECOND - FILETIME_UNIX_DIFF;
|
||||
} /* lzma_filetime_to_unix_timestamp */
|
||||
|
||||
|
||||
/*
|
||||
* Compare a file with a given name, C89 stdlib variant
|
||||
* Used for sorting
|
||||
*/
|
||||
static int lzma_file_cmp_stdlib(const void *key, const void *object)
|
||||
{
|
||||
const char *name = (const char *) key;
|
||||
LZMAfile *file = (LZMAfile *) object;
|
||||
return(strcmp(name, file->item->Name));
|
||||
} /* lzma_file_cmp_posix */
|
||||
|
||||
|
||||
/*
|
||||
* Compare two files with each other based on the name
|
||||
* Used for sorting
|
||||
*/
|
||||
static int lzma_file_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
|
||||
{
|
||||
LZMAfile *files = (LZMAfile *) _a;
|
||||
return(strcmp(files[one].item->Name, files[two].item->Name));
|
||||
} /* lzma_file_cmp */
|
||||
|
||||
|
||||
/*
|
||||
* Swap two entries in the file array
|
||||
*/
|
||||
static void lzma_file_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
|
||||
{
|
||||
LZMAfile tmp;
|
||||
LZMAfile *first = &(((LZMAfile *) _a)[one]);
|
||||
LZMAfile *second = &(((LZMAfile *) _a)[two]);
|
||||
memcpy(&tmp, first, sizeof (LZMAfile));
|
||||
memcpy(first, second, sizeof (LZMAfile));
|
||||
memcpy(second, &tmp, sizeof (LZMAfile));
|
||||
} /* lzma_file_swap */
|
||||
|
||||
|
||||
/*
|
||||
* Find entry 'name' in 'archive'
|
||||
*/
|
||||
static LZMAfile * lzma_find_file(LZMAarchive *archive, const char *name)
|
||||
{
|
||||
LZMAfile *file = bsearch(name, archive->files, archive->db.Database.NumFiles, sizeof(*archive->files), lzma_file_cmp_stdlib); /* FIXME: Should become __PHYSFS_search!!! */
|
||||
|
||||
BAIL_IF_MACRO(file == NULL, ERR_NO_SUCH_FILE, NULL);
|
||||
|
||||
return(file);
|
||||
} /* lzma_find_file */
|
||||
|
||||
|
||||
/*
|
||||
* Load metadata for the file at given index
|
||||
*/
|
||||
static int lzma_file_init(LZMAarchive *archive, PHYSFS_uint32 fileIndex)
|
||||
{
|
||||
LZMAfile *file = &archive->files[fileIndex];
|
||||
PHYSFS_uint32 folderIndex = archive->db.FileIndexToFolderIndexMap[fileIndex];
|
||||
|
||||
file->index = fileIndex; /* Store index into 7z array, since we sort our own. */
|
||||
file->archive = archive;
|
||||
file->folder = (folderIndex != (PHYSFS_uint32)-1 ? &archive->folders[folderIndex] : NULL); /* Directories don't have a folder (they contain no own data...) */
|
||||
file->item = &archive->db.Database.Files[fileIndex]; /* Holds crucial data and is often referenced -> Store link */
|
||||
file->position = 0;
|
||||
file->offset = 0; /* Offset will be set by LZMA_read() */
|
||||
|
||||
return(1);
|
||||
} /* lzma_load_file */
|
||||
|
||||
|
||||
/*
|
||||
* Load metadata for all files
|
||||
*/
|
||||
static int lzma_files_init(LZMAarchive *archive)
|
||||
{
|
||||
PHYSFS_uint32 fileIndex = 0, numFiles = archive->db.Database.NumFiles;
|
||||
|
||||
for (fileIndex = 0; fileIndex < numFiles; fileIndex++ )
|
||||
{
|
||||
if (!lzma_file_init(archive, fileIndex))
|
||||
{
|
||||
return(0); /* FALSE on failure */
|
||||
}
|
||||
} /* for */
|
||||
|
||||
__PHYSFS_sort(archive->files, numFiles, lzma_file_cmp, lzma_file_swap);
|
||||
|
||||
return(1);
|
||||
} /* lzma_load_files */
|
||||
|
||||
|
||||
/*
|
||||
* Initialise specified archive
|
||||
*/
|
||||
static void lzma_archive_init(LZMAarchive *archive)
|
||||
{
|
||||
memset(archive, 0, sizeof(*archive));
|
||||
|
||||
/* Prepare callbacks for 7z */
|
||||
archive->stream.inStream.Read = SzFileReadImp;
|
||||
archive->stream.inStream.Seek = SzFileSeekImp;
|
||||
|
||||
archive->stream.allocImp.Alloc = SzAllocPhysicsFS;
|
||||
archive->stream.allocImp.Free = SzFreePhysicsFS;
|
||||
|
||||
archive->stream.allocTempImp.Alloc = SzAllocPhysicsFS;
|
||||
archive->stream.allocTempImp.Free = SzFreePhysicsFS;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Deinitialise archive
|
||||
*/
|
||||
static void lzma_archive_exit(LZMAarchive *archive)
|
||||
{
|
||||
/* Free arrays */
|
||||
allocator.Free(archive->folders);
|
||||
allocator.Free(archive->files);
|
||||
allocator.Free(archive);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrap all 7z calls in this, so the physfs error state is set appropriately.
|
||||
*/
|
||||
static int lzma_err(SZ_RESULT rc)
|
||||
{
|
||||
switch (rc)
|
||||
{
|
||||
case SZ_OK: /* Same as LZMA_RESULT_OK */
|
||||
break;
|
||||
case SZE_DATA_ERROR: /* Same as LZMA_RESULT_DATA_ERROR */
|
||||
__PHYSFS_setError(ERR_DATA_ERROR);
|
||||
break;
|
||||
case SZE_OUTOFMEMORY:
|
||||
__PHYSFS_setError(ERR_OUT_OF_MEMORY);
|
||||
break;
|
||||
case SZE_CRC_ERROR:
|
||||
__PHYSFS_setError(ERR_CORRUPTED);
|
||||
break;
|
||||
case SZE_NOTIMPL:
|
||||
__PHYSFS_setError(ERR_NOT_IMPLEMENTED);
|
||||
break;
|
||||
case SZE_FAIL:
|
||||
__PHYSFS_setError(ERR_UNKNOWN_ERROR); /* !!! FIXME: right? */
|
||||
break;
|
||||
case SZE_ARCHIVE_ERROR:
|
||||
__PHYSFS_setError(ERR_CORRUPTED); /* !!! FIXME: right? */
|
||||
break;
|
||||
default:
|
||||
__PHYSFS_setError(ERR_UNKNOWN_ERROR);
|
||||
} /* switch */
|
||||
|
||||
return(rc);
|
||||
} /* lzma_err */
|
||||
|
||||
|
||||
static PHYSFS_sint64 LZMA_read(fvoid *opaque, void *outBuffer,
|
||||
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
|
||||
{
|
||||
LZMAfile *file = (LZMAfile *) opaque;
|
||||
|
||||
size_t wantedSize = objSize*objCount;
|
||||
size_t remainingSize = file->item->Size - file->position;
|
||||
size_t fileSize = 0;
|
||||
|
||||
BAIL_IF_MACRO(wantedSize == 0, NULL, 0); /* quick rejection. */
|
||||
BAIL_IF_MACRO(remainingSize == 0, ERR_PAST_EOF, 0);
|
||||
|
||||
if (remainingSize < wantedSize)
|
||||
{
|
||||
wantedSize = remainingSize - (remainingSize % objSize);
|
||||
objCount = (PHYSFS_uint32) (remainingSize / objSize);
|
||||
BAIL_IF_MACRO(objCount == 0, ERR_PAST_EOF, 0); /* quick rejection. */
|
||||
__PHYSFS_setError(ERR_PAST_EOF); /* this is always true here. */
|
||||
} /* if */
|
||||
|
||||
/* Only decompress the folder if it is not allready cached */
|
||||
if (file->folder->cache == NULL)
|
||||
{
|
||||
int rc = lzma_err(SzExtract(
|
||||
&file->archive->stream.inStream, /* compressed data */
|
||||
&file->archive->db, /* 7z's database, containing everything */
|
||||
file->index, /* Index into database arrays */
|
||||
/* Index of cached folder, will be changed by SzExtract */
|
||||
&file->folder->index,
|
||||
/* Cache for decompressed folder, allocated/freed by SzExtract */
|
||||
&file->folder->cache,
|
||||
/* Size of cache, will be changed by SzExtract */
|
||||
&file->folder->size,
|
||||
/* Offset of this file inside the cache, set by SzExtract */
|
||||
&file->offset,
|
||||
&fileSize, /* Size of this file */
|
||||
&file->archive->stream.allocImp,
|
||||
&file->archive->stream.allocTempImp));
|
||||
|
||||
if (rc != SZ_OK)
|
||||
return -1;
|
||||
} /* if */
|
||||
|
||||
/* Copy wanted bytes over from cache to outBuffer */
|
||||
memcpy(outBuffer,
|
||||
(file->folder->cache +
|
||||
file->offset + file->position),
|
||||
wantedSize);
|
||||
file->position += wantedSize; /* Increase virtual position */
|
||||
|
||||
return objCount;
|
||||
} /* LZMA_read */
|
||||
|
||||
|
||||
static PHYSFS_sint64 LZMA_write(fvoid *opaque, const void *buf,
|
||||
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
|
||||
} /* LZMA_write */
|
||||
|
||||
|
||||
static int LZMA_eof(fvoid *opaque)
|
||||
{
|
||||
LZMAfile *file = (LZMAfile *) opaque;
|
||||
return (file->position >= file->item->Size);
|
||||
} /* LZMA_eof */
|
||||
|
||||
|
||||
static PHYSFS_sint64 LZMA_tell(fvoid *opaque)
|
||||
{
|
||||
LZMAfile *file = (LZMAfile *) opaque;
|
||||
return (file->position);
|
||||
} /* LZMA_tell */
|
||||
|
||||
|
||||
static int LZMA_seek(fvoid *opaque, PHYSFS_uint64 offset)
|
||||
{
|
||||
LZMAfile *file = (LZMAfile *) opaque;
|
||||
|
||||
BAIL_IF_MACRO(offset < 0, ERR_SEEK_OUT_OF_RANGE, 0);
|
||||
BAIL_IF_MACRO(offset > file->item->Size, ERR_PAST_EOF, 0);
|
||||
|
||||
file->position = offset; /* We only use a virtual position... */
|
||||
|
||||
return 1;
|
||||
} /* LZMA_seek */
|
||||
|
||||
|
||||
static PHYSFS_sint64 LZMA_fileLength(fvoid *opaque)
|
||||
{
|
||||
LZMAfile *file = (LZMAfile *) opaque;
|
||||
return (file->item->Size);
|
||||
} /* LZMA_fileLength */
|
||||
|
||||
|
||||
static int LZMA_fileClose(fvoid *opaque)
|
||||
{
|
||||
LZMAfile *file = (LZMAfile *) opaque;
|
||||
|
||||
BAIL_IF_MACRO(file->folder == NULL, ERR_NOT_A_FILE, 0);
|
||||
|
||||
/* Only decrease refcount if someone actually requested this file... Prevents from overflows and close-on-open... */
|
||||
if (file->folder->references > 0)
|
||||
file->folder->references--;
|
||||
if (file->folder->references == 0)
|
||||
{
|
||||
/* Free the cache which might have been allocated by LZMA_read() */
|
||||
allocator.Free(file->folder->cache);
|
||||
file->folder->cache = NULL;
|
||||
}
|
||||
|
||||
return(1);
|
||||
} /* LZMA_fileClose */
|
||||
|
||||
|
||||
static int LZMA_isArchive(const char *filename, int forWriting)
|
||||
{
|
||||
PHYSFS_uint8 sig[k7zSignatureSize];
|
||||
void *in;
|
||||
|
||||
BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
|
||||
|
||||
in = __PHYSFS_platformOpenRead(filename);
|
||||
BAIL_IF_MACRO(in == NULL, NULL, 0);
|
||||
|
||||
/* Read signature bytes */
|
||||
if (__PHYSFS_platformRead(in, sig, k7zSignatureSize, 1) != 1)
|
||||
{
|
||||
__PHYSFS_platformClose(in); /* Don't forget to close the file before returning... */
|
||||
BAIL_MACRO(NULL, 0);
|
||||
}
|
||||
|
||||
__PHYSFS_platformClose(in);
|
||||
|
||||
/* Test whether sig is the 7z signature */
|
||||
return(TestSignatureCandidate(sig));
|
||||
} /* LZMA_isArchive */
|
||||
|
||||
|
||||
static void *LZMA_openArchive(const char *name, int forWriting)
|
||||
{
|
||||
size_t len = 0;
|
||||
LZMAarchive *archive = NULL;
|
||||
|
||||
BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, NULL);
|
||||
BAIL_IF_MACRO(!LZMA_isArchive(name,forWriting), ERR_UNSUPPORTED_ARCHIVE, 0);
|
||||
|
||||
archive = (LZMAarchive *) allocator.Malloc(sizeof (LZMAarchive));
|
||||
BAIL_IF_MACRO(archive == NULL, ERR_OUT_OF_MEMORY, NULL);
|
||||
|
||||
lzma_archive_init(archive);
|
||||
|
||||
if ( (archive->stream.file = __PHYSFS_platformOpenRead(name)) == NULL )
|
||||
{
|
||||
__PHYSFS_platformClose(archive->stream.file);
|
||||
lzma_archive_exit(archive);
|
||||
return(NULL); /* Error is set by platformOpenRead! */
|
||||
}
|
||||
|
||||
CrcGenerateTable();
|
||||
SzArDbExInit(&archive->db);
|
||||
if (lzma_err(SzArchiveOpen(&archive->stream.inStream,
|
||||
&archive->db,
|
||||
&archive->stream.allocImp,
|
||||
&archive->stream.allocTempImp)) != SZ_OK)
|
||||
{
|
||||
SzArDbExFree(&archive->db, SzFreePhysicsFS);
|
||||
__PHYSFS_platformClose(archive->stream.file);
|
||||
lzma_archive_exit(archive);
|
||||
return NULL; /* Error is set by lzma_err! */
|
||||
} /* if */
|
||||
|
||||
len = archive->db.Database.NumFiles * sizeof (LZMAfile);
|
||||
archive->files = (LZMAfile *) allocator.Malloc(len);
|
||||
if (archive->files == NULL)
|
||||
{
|
||||
SzArDbExFree(&archive->db, SzFreePhysicsFS);
|
||||
__PHYSFS_platformClose(archive->stream.file);
|
||||
lzma_archive_exit(archive);
|
||||
BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Init with 0 so we know when a folder is already cached
|
||||
* Values will be set by LZMA_openRead()
|
||||
*/
|
||||
memset(archive->files, 0, len);
|
||||
|
||||
len = archive->db.Database.NumFolders * sizeof (LZMAfolder);
|
||||
archive->folders = (LZMAfolder *) allocator.Malloc(len);
|
||||
if (archive->folders == NULL)
|
||||
{
|
||||
SzArDbExFree(&archive->db, SzFreePhysicsFS);
|
||||
__PHYSFS_platformClose(archive->stream.file);
|
||||
lzma_archive_exit(archive);
|
||||
BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Init with 0 so we know when a folder is already cached
|
||||
* Values will be set by LZMA_read()
|
||||
*/
|
||||
memset(archive->folders, 0, len);
|
||||
|
||||
if(!lzma_files_init(archive))
|
||||
{
|
||||
SzArDbExFree(&archive->db, SzFreePhysicsFS);
|
||||
__PHYSFS_platformClose(archive->stream.file);
|
||||
lzma_archive_exit(archive);
|
||||
BAIL_MACRO(ERR_UNKNOWN_ERROR, NULL);
|
||||
}
|
||||
|
||||
return(archive);
|
||||
} /* LZMA_openArchive */
|
||||
|
||||
|
||||
/*
|
||||
* Moved to seperate function so we can use alloca then immediately throw
|
||||
* away the allocated stack space...
|
||||
*/
|
||||
static void doEnumCallback(PHYSFS_EnumFilesCallback cb, void *callbackdata,
|
||||
const char *odir, const char *str, size_t flen)
|
||||
{
|
||||
char *newstr = __PHYSFS_smallAlloc(flen + 1);
|
||||
if (newstr == NULL)
|
||||
return;
|
||||
|
||||
memcpy(newstr, str, flen);
|
||||
newstr[flen] = '\0';
|
||||
cb(callbackdata, odir, newstr);
|
||||
__PHYSFS_smallFree(newstr);
|
||||
} /* doEnumCallback */
|
||||
|
||||
|
||||
static void LZMA_enumerateFiles(dvoid *opaque, const char *dname,
|
||||
int omitSymLinks, PHYSFS_EnumFilesCallback cb,
|
||||
const char *origdir, void *callbackdata)
|
||||
{
|
||||
size_t dlen = strlen(dname),
|
||||
dlen_inc = dlen + ((dlen > 0) ? 1 : 0);
|
||||
LZMAarchive *archive = (LZMAarchive *) opaque;
|
||||
LZMAfile *file = NULL,
|
||||
*lastFile = &archive->files[archive->db.Database.NumFiles];
|
||||
if (dlen)
|
||||
{
|
||||
file = lzma_find_file(archive, dname);
|
||||
if (file != NULL) /* if 'file' is NULL it should stay so, otherwise errors will not be handled */
|
||||
file += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
file = archive->files;
|
||||
}
|
||||
|
||||
BAIL_IF_MACRO(file == NULL, ERR_NO_SUCH_FILE, );
|
||||
|
||||
while (file < lastFile)
|
||||
{
|
||||
const char * fname = file->item->Name;
|
||||
const char * dirNameEnd = fname + dlen_inc;
|
||||
|
||||
if (strncmp(dname, fname, dlen) != 0) /* Stop after mismatch, archive->files is sorted */
|
||||
break;
|
||||
|
||||
if (strchr(dirNameEnd, '/')) /* Skip subdirs */
|
||||
{
|
||||
file++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Do the actual callback... */
|
||||
doEnumCallback(cb, callbackdata, origdir, dirNameEnd, strlen(dirNameEnd));
|
||||
|
||||
file++;
|
||||
}
|
||||
} /* LZMA_enumerateFiles */
|
||||
|
||||
|
||||
static int LZMA_exists(dvoid *opaque, const char *name)
|
||||
{
|
||||
LZMAarchive *archive = (LZMAarchive *) opaque;
|
||||
return(lzma_find_file(archive, name) != NULL);
|
||||
} /* LZMA_exists */
|
||||
|
||||
|
||||
static PHYSFS_sint64 LZMA_getLastModTime(dvoid *opaque,
|
||||
const char *name,
|
||||
int *fileExists)
|
||||
{
|
||||
LZMAarchive *archive = (LZMAarchive *) opaque;
|
||||
LZMAfile *file = lzma_find_file(archive, name);
|
||||
|
||||
*fileExists = (file != NULL);
|
||||
|
||||
BAIL_IF_MACRO(file == NULL, NULL, -1);
|
||||
BAIL_IF_MACRO(!file->item->IsLastWriteTimeDefined, NULL, -1); /* write-time may not be defined for every file */
|
||||
|
||||
return(lzma_filetime_to_unix_timestamp(&file->item->LastWriteTime));
|
||||
} /* LZMA_getLastModTime */
|
||||
|
||||
|
||||
static int LZMA_isDirectory(dvoid *opaque, const char *name, int *fileExists)
|
||||
{
|
||||
LZMAarchive *archive = (LZMAarchive *) opaque;
|
||||
LZMAfile *file = lzma_find_file(archive, name);
|
||||
|
||||
*fileExists = (file != NULL);
|
||||
|
||||
return(file == NULL ? 0 : file->item->IsDirectory);
|
||||
} /* LZMA_isDirectory */
|
||||
|
||||
|
||||
static int LZMA_isSymLink(dvoid *opaque, const char *name, int *fileExists)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
|
||||
} /* LZMA_isSymLink */
|
||||
|
||||
|
||||
static fvoid *LZMA_openRead(dvoid *opaque, const char *name, int *fileExists)
|
||||
{
|
||||
LZMAarchive *archive = (LZMAarchive *) opaque;
|
||||
LZMAfile *file = lzma_find_file(archive, name);
|
||||
|
||||
*fileExists = (file != NULL);
|
||||
BAIL_IF_MACRO(file == NULL, ERR_NO_SUCH_FILE, NULL);
|
||||
BAIL_IF_MACRO(file->folder == NULL, ERR_NOT_A_FILE, NULL);
|
||||
|
||||
file->position = 0;
|
||||
file->folder->references++; /* Increase refcount for automatic cleanup... */
|
||||
|
||||
return(file);
|
||||
} /* LZMA_openRead */
|
||||
|
||||
|
||||
static fvoid *LZMA_openWrite(dvoid *opaque, const char *filename)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
|
||||
} /* LZMA_openWrite */
|
||||
|
||||
|
||||
static fvoid *LZMA_openAppend(dvoid *opaque, const char *filename)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
|
||||
} /* LZMA_openAppend */
|
||||
|
||||
|
||||
static void LZMA_dirClose(dvoid *opaque)
|
||||
{
|
||||
LZMAarchive *archive = (LZMAarchive *) opaque;
|
||||
PHYSFS_uint32 fileIndex = 0, numFiles = archive->db.Database.NumFiles;
|
||||
|
||||
for (fileIndex = 0; fileIndex < numFiles; fileIndex++)
|
||||
{
|
||||
LZMA_fileClose(&archive->files[fileIndex]);
|
||||
} /* for */
|
||||
|
||||
SzArDbExFree(&archive->db, SzFreePhysicsFS);
|
||||
__PHYSFS_platformClose(archive->stream.file);
|
||||
lzma_archive_exit(archive);
|
||||
} /* LZMA_dirClose */
|
||||
|
||||
|
||||
static int LZMA_remove(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
|
||||
} /* LZMA_remove */
|
||||
|
||||
|
||||
static int LZMA_mkdir(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
|
||||
} /* LZMA_mkdir */
|
||||
|
||||
|
||||
const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_LZMA =
|
||||
{
|
||||
"7Z",
|
||||
LZMA_ARCHIVE_DESCRIPTION,
|
||||
"Dennis Schridde <devurandom@gmx.net>",
|
||||
"http://icculus.org/physfs/",
|
||||
};
|
||||
|
||||
|
||||
const PHYSFS_Archiver __PHYSFS_Archiver_LZMA =
|
||||
{
|
||||
&__PHYSFS_ArchiveInfo_LZMA,
|
||||
LZMA_isArchive, /* isArchive() method */
|
||||
LZMA_openArchive, /* openArchive() method */
|
||||
LZMA_enumerateFiles, /* enumerateFiles() method */
|
||||
LZMA_exists, /* exists() method */
|
||||
LZMA_isDirectory, /* isDirectory() method */
|
||||
LZMA_isSymLink, /* isSymLink() method */
|
||||
LZMA_getLastModTime, /* getLastModTime() method */
|
||||
LZMA_openRead, /* openRead() method */
|
||||
LZMA_openWrite, /* openWrite() method */
|
||||
LZMA_openAppend, /* openAppend() method */
|
||||
LZMA_remove, /* remove() method */
|
||||
LZMA_mkdir, /* mkdir() method */
|
||||
LZMA_dirClose, /* dirClose() method */
|
||||
LZMA_read, /* read() method */
|
||||
LZMA_write, /* write() method */
|
||||
LZMA_eof, /* eof() method */
|
||||
LZMA_tell, /* tell() method */
|
||||
LZMA_seek, /* seek() method */
|
||||
LZMA_fileLength, /* fileLength() method */
|
||||
LZMA_fileClose /* fileClose() method */
|
||||
};
|
||||
|
||||
#endif /* defined PHYSFS_SUPPORTS_7Z */
|
||||
|
||||
/* end of lzma.c ... */
|
||||
|
||||
@@ -0,0 +1,471 @@
|
||||
/*
|
||||
* MVL support routines for PhysicsFS.
|
||||
*
|
||||
* This driver handles Descent II Movielib archives.
|
||||
*
|
||||
* The file format of MVL is quite easy...
|
||||
*
|
||||
* //MVL File format - Written by Heiko Herrmann
|
||||
* char sig[4] = {'D','M', 'V', 'L'}; // "DMVL"=Descent MoVie Library
|
||||
*
|
||||
* int num_files; // the number of files in this MVL
|
||||
*
|
||||
* struct {
|
||||
* char file_name[13]; // Filename, padded to 13 bytes with 0s
|
||||
* int file_size; // filesize in bytes
|
||||
* }DIR_STRUCT[num_files];
|
||||
*
|
||||
* struct {
|
||||
* char data[file_size]; // The file data
|
||||
* }FILE_STRUCT[num_files];
|
||||
*
|
||||
* (That info is from http://www.descent2.com/ddn/specs/mvl/)
|
||||
*
|
||||
* Please see the file LICENSE.txt in the source's root directory.
|
||||
*
|
||||
* This file written by Bradley Bell.
|
||||
* Based on grp.c by Ryan C. Gordon.
|
||||
*/
|
||||
|
||||
#if (defined PHYSFS_SUPPORTS_MVL)
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "physfs.h"
|
||||
|
||||
#define __PHYSICSFS_INTERNAL__
|
||||
#include "physfs_internal.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char name[13];
|
||||
PHYSFS_uint32 startPos;
|
||||
PHYSFS_uint32 size;
|
||||
} MVLentry;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *filename;
|
||||
PHYSFS_sint64 last_mod_time;
|
||||
PHYSFS_uint32 entryCount;
|
||||
MVLentry *entries;
|
||||
} MVLinfo;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void *handle;
|
||||
MVLentry *entry;
|
||||
PHYSFS_uint32 curPos;
|
||||
} MVLfileinfo;
|
||||
|
||||
|
||||
static void MVL_dirClose(dvoid *opaque)
|
||||
{
|
||||
MVLinfo *info = ((MVLinfo *) opaque);
|
||||
allocator.Free(info->filename);
|
||||
allocator.Free(info->entries);
|
||||
allocator.Free(info);
|
||||
} /* MVL_dirClose */
|
||||
|
||||
|
||||
static PHYSFS_sint64 MVL_read(fvoid *opaque, void *buffer,
|
||||
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
|
||||
{
|
||||
MVLfileinfo *finfo = (MVLfileinfo *) opaque;
|
||||
MVLentry *entry = finfo->entry;
|
||||
PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos;
|
||||
PHYSFS_uint32 objsLeft = (bytesLeft / objSize);
|
||||
PHYSFS_sint64 rc;
|
||||
|
||||
if (objsLeft < objCount)
|
||||
objCount = objsLeft;
|
||||
|
||||
rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount);
|
||||
if (rc > 0)
|
||||
finfo->curPos += (PHYSFS_uint32) (rc * objSize);
|
||||
|
||||
return(rc);
|
||||
} /* MVL_read */
|
||||
|
||||
|
||||
static PHYSFS_sint64 MVL_write(fvoid *opaque, const void *buffer,
|
||||
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
|
||||
} /* MVL_write */
|
||||
|
||||
|
||||
static int MVL_eof(fvoid *opaque)
|
||||
{
|
||||
MVLfileinfo *finfo = (MVLfileinfo *) opaque;
|
||||
MVLentry *entry = finfo->entry;
|
||||
return(finfo->curPos >= entry->size);
|
||||
} /* MVL_eof */
|
||||
|
||||
|
||||
static PHYSFS_sint64 MVL_tell(fvoid *opaque)
|
||||
{
|
||||
return(((MVLfileinfo *) opaque)->curPos);
|
||||
} /* MVL_tell */
|
||||
|
||||
|
||||
static int MVL_seek(fvoid *opaque, PHYSFS_uint64 offset)
|
||||
{
|
||||
MVLfileinfo *finfo = (MVLfileinfo *) opaque;
|
||||
MVLentry *entry = finfo->entry;
|
||||
int rc;
|
||||
|
||||
BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
|
||||
BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0);
|
||||
rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset);
|
||||
if (rc)
|
||||
finfo->curPos = (PHYSFS_uint32) offset;
|
||||
|
||||
return(rc);
|
||||
} /* MVL_seek */
|
||||
|
||||
|
||||
static PHYSFS_sint64 MVL_fileLength(fvoid *opaque)
|
||||
{
|
||||
MVLfileinfo *finfo = (MVLfileinfo *) opaque;
|
||||
return((PHYSFS_sint64) finfo->entry->size);
|
||||
} /* MVL_fileLength */
|
||||
|
||||
|
||||
static int MVL_fileClose(fvoid *opaque)
|
||||
{
|
||||
MVLfileinfo *finfo = (MVLfileinfo *) opaque;
|
||||
BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
|
||||
allocator.Free(finfo);
|
||||
return(1);
|
||||
} /* MVL_fileClose */
|
||||
|
||||
|
||||
static int mvl_open(const char *filename, int forWriting,
|
||||
void **fh, PHYSFS_uint32 *count)
|
||||
{
|
||||
PHYSFS_uint8 buf[4];
|
||||
|
||||
*fh = NULL;
|
||||
BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
|
||||
|
||||
*fh = __PHYSFS_platformOpenRead(filename);
|
||||
BAIL_IF_MACRO(*fh == NULL, NULL, 0);
|
||||
|
||||
if (__PHYSFS_platformRead(*fh, buf, 4, 1) != 1)
|
||||
goto openMvl_failed;
|
||||
|
||||
if (memcmp(buf, "DMVL", 4) != 0)
|
||||
{
|
||||
__PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
|
||||
goto openMvl_failed;
|
||||
} /* if */
|
||||
|
||||
if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1)
|
||||
goto openMvl_failed;
|
||||
|
||||
*count = PHYSFS_swapULE32(*count);
|
||||
|
||||
return(1);
|
||||
|
||||
openMvl_failed:
|
||||
if (*fh != NULL)
|
||||
__PHYSFS_platformClose(*fh);
|
||||
|
||||
*count = -1;
|
||||
*fh = NULL;
|
||||
return(0);
|
||||
} /* mvl_open */
|
||||
|
||||
|
||||
static int MVL_isArchive(const char *filename, int forWriting)
|
||||
{
|
||||
void *fh;
|
||||
PHYSFS_uint32 fileCount;
|
||||
int retval = mvl_open(filename, forWriting, &fh, &fileCount);
|
||||
|
||||
if (fh != NULL)
|
||||
__PHYSFS_platformClose(fh);
|
||||
|
||||
return(retval);
|
||||
} /* MVL_isArchive */
|
||||
|
||||
|
||||
static int mvl_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
|
||||
{
|
||||
if (one != two)
|
||||
{
|
||||
const MVLentry *a = (const MVLentry *) _a;
|
||||
return(strcmp(a[one].name, a[two].name));
|
||||
} /* if */
|
||||
|
||||
return 0;
|
||||
} /* mvl_entry_cmp */
|
||||
|
||||
|
||||
static void mvl_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
|
||||
{
|
||||
if (one != two)
|
||||
{
|
||||
MVLentry tmp;
|
||||
MVLentry *first = &(((MVLentry *) _a)[one]);
|
||||
MVLentry *second = &(((MVLentry *) _a)[two]);
|
||||
memcpy(&tmp, first, sizeof (MVLentry));
|
||||
memcpy(first, second, sizeof (MVLentry));
|
||||
memcpy(second, &tmp, sizeof (MVLentry));
|
||||
} /* if */
|
||||
} /* mvl_entry_swap */
|
||||
|
||||
|
||||
static int mvl_load_entries(const char *name, int forWriting, MVLinfo *info)
|
||||
{
|
||||
void *fh = NULL;
|
||||
PHYSFS_uint32 fileCount;
|
||||
PHYSFS_uint32 location = 8; /* sizeof sig. */
|
||||
MVLentry *entry;
|
||||
|
||||
BAIL_IF_MACRO(!mvl_open(name, forWriting, &fh, &fileCount), NULL, 0);
|
||||
info->entryCount = fileCount;
|
||||
info->entries = (MVLentry *) allocator.Malloc(sizeof(MVLentry)*fileCount);
|
||||
if (info->entries == NULL)
|
||||
{
|
||||
__PHYSFS_platformClose(fh);
|
||||
BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
|
||||
} /* if */
|
||||
|
||||
location += (17 * fileCount);
|
||||
|
||||
for (entry = info->entries; fileCount > 0; fileCount--, entry++)
|
||||
{
|
||||
if (__PHYSFS_platformRead(fh, &entry->name, 13, 1) != 1)
|
||||
{
|
||||
__PHYSFS_platformClose(fh);
|
||||
return(0);
|
||||
} /* if */
|
||||
|
||||
if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1)
|
||||
{
|
||||
__PHYSFS_platformClose(fh);
|
||||
return(0);
|
||||
} /* if */
|
||||
|
||||
entry->size = PHYSFS_swapULE32(entry->size);
|
||||
entry->startPos = location;
|
||||
location += entry->size;
|
||||
} /* for */
|
||||
|
||||
__PHYSFS_platformClose(fh);
|
||||
|
||||
__PHYSFS_sort(info->entries, info->entryCount,
|
||||
mvl_entry_cmp, mvl_entry_swap);
|
||||
return(1);
|
||||
} /* mvl_load_entries */
|
||||
|
||||
|
||||
static void *MVL_openArchive(const char *name, int forWriting)
|
||||
{
|
||||
PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name);
|
||||
MVLinfo *info = (MVLinfo *) allocator.Malloc(sizeof (MVLinfo));
|
||||
|
||||
BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, NULL);
|
||||
memset(info, '\0', sizeof (MVLinfo));
|
||||
|
||||
info->filename = (char *) allocator.Malloc(strlen(name) + 1);
|
||||
GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, MVL_openArchive_failed);
|
||||
if (!mvl_load_entries(name, forWriting, info))
|
||||
goto MVL_openArchive_failed;
|
||||
|
||||
strcpy(info->filename, name);
|
||||
info->last_mod_time = modtime;
|
||||
return(info);
|
||||
|
||||
MVL_openArchive_failed:
|
||||
if (info != NULL)
|
||||
{
|
||||
if (info->filename != NULL)
|
||||
allocator.Free(info->filename);
|
||||
if (info->entries != NULL)
|
||||
allocator.Free(info->entries);
|
||||
allocator.Free(info);
|
||||
} /* if */
|
||||
|
||||
return(NULL);
|
||||
} /* MVL_openArchive */
|
||||
|
||||
|
||||
static void MVL_enumerateFiles(dvoid *opaque, const char *dname,
|
||||
int omitSymLinks, PHYSFS_EnumFilesCallback cb,
|
||||
const char *origdir, void *callbackdata)
|
||||
{
|
||||
/* no directories in MVL files. */
|
||||
if (*dname == '\0')
|
||||
{
|
||||
MVLinfo *info = ((MVLinfo *) opaque);
|
||||
MVLentry *entry = info->entries;
|
||||
PHYSFS_uint32 max = info->entryCount;
|
||||
PHYSFS_uint32 i;
|
||||
|
||||
for (i = 0; i < max; i++, entry++)
|
||||
cb(callbackdata, origdir, entry->name);
|
||||
} /* if */
|
||||
} /* MVL_enumerateFiles */
|
||||
|
||||
|
||||
static MVLentry *mvl_find_entry(MVLinfo *info, const char *name)
|
||||
{
|
||||
char *ptr = strchr(name, '.');
|
||||
MVLentry *a = info->entries;
|
||||
PHYSFS_sint32 lo = 0;
|
||||
PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
|
||||
PHYSFS_sint32 middle;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* Rule out filenames to avoid unneeded processing...no dirs,
|
||||
* big filenames, or extensions > 3 chars.
|
||||
*/
|
||||
BAIL_IF_MACRO((ptr) && (strlen(ptr) > 4), ERR_NO_SUCH_FILE, NULL);
|
||||
BAIL_IF_MACRO(strlen(name) > 12, ERR_NO_SUCH_FILE, NULL);
|
||||
BAIL_IF_MACRO(strchr(name, '/') != NULL, ERR_NO_SUCH_FILE, NULL);
|
||||
|
||||
while (lo <= hi)
|
||||
{
|
||||
middle = lo + ((hi - lo) / 2);
|
||||
rc = __PHYSFS_stricmpASCII(name, a[middle].name);
|
||||
if (rc == 0) /* found it! */
|
||||
return(&a[middle]);
|
||||
else if (rc > 0)
|
||||
lo = middle + 1;
|
||||
else
|
||||
hi = middle - 1;
|
||||
} /* while */
|
||||
|
||||
BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
|
||||
} /* mvl_find_entry */
|
||||
|
||||
|
||||
static int MVL_exists(dvoid *opaque, const char *name)
|
||||
{
|
||||
return(mvl_find_entry(((MVLinfo *) opaque), name) != NULL);
|
||||
} /* MVL_exists */
|
||||
|
||||
|
||||
static int MVL_isDirectory(dvoid *opaque, const char *name, int *fileExists)
|
||||
{
|
||||
*fileExists = MVL_exists(opaque, name);
|
||||
return(0); /* never directories in a groupfile. */
|
||||
} /* MVL_isDirectory */
|
||||
|
||||
|
||||
static int MVL_isSymLink(dvoid *opaque, const char *name, int *fileExists)
|
||||
{
|
||||
*fileExists = MVL_exists(opaque, name);
|
||||
return(0); /* never symlinks in a groupfile. */
|
||||
} /* MVL_isSymLink */
|
||||
|
||||
|
||||
static PHYSFS_sint64 MVL_getLastModTime(dvoid *opaque,
|
||||
const char *name,
|
||||
int *fileExists)
|
||||
{
|
||||
MVLinfo *info = ((MVLinfo *) opaque);
|
||||
PHYSFS_sint64 retval = -1;
|
||||
|
||||
*fileExists = (mvl_find_entry(info, name) != NULL);
|
||||
if (*fileExists) /* use time of MVL itself in the physical filesystem. */
|
||||
retval = info->last_mod_time;
|
||||
|
||||
return(retval);
|
||||
} /* MVL_getLastModTime */
|
||||
|
||||
|
||||
static fvoid *MVL_openRead(dvoid *opaque, const char *fnm, int *fileExists)
|
||||
{
|
||||
MVLinfo *info = ((MVLinfo *) opaque);
|
||||
MVLfileinfo *finfo;
|
||||
MVLentry *entry;
|
||||
|
||||
entry = mvl_find_entry(info, fnm);
|
||||
*fileExists = (entry != NULL);
|
||||
BAIL_IF_MACRO(entry == NULL, NULL, NULL);
|
||||
|
||||
finfo = (MVLfileinfo *) allocator.Malloc(sizeof (MVLfileinfo));
|
||||
BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
|
||||
|
||||
finfo->handle = __PHYSFS_platformOpenRead(info->filename);
|
||||
if ( (finfo->handle == NULL) ||
|
||||
(!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
|
||||
{
|
||||
allocator.Free(finfo);
|
||||
return(NULL);
|
||||
} /* if */
|
||||
|
||||
finfo->curPos = 0;
|
||||
finfo->entry = entry;
|
||||
return(finfo);
|
||||
} /* MVL_openRead */
|
||||
|
||||
|
||||
static fvoid *MVL_openWrite(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
|
||||
} /* MVL_openWrite */
|
||||
|
||||
|
||||
static fvoid *MVL_openAppend(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
|
||||
} /* MVL_openAppend */
|
||||
|
||||
|
||||
static int MVL_remove(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
|
||||
} /* MVL_remove */
|
||||
|
||||
|
||||
static int MVL_mkdir(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
|
||||
} /* MVL_mkdir */
|
||||
|
||||
|
||||
const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_MVL =
|
||||
{
|
||||
"MVL",
|
||||
MVL_ARCHIVE_DESCRIPTION,
|
||||
"Bradley Bell <btb@icculus.org>",
|
||||
"http://icculus.org/physfs/",
|
||||
};
|
||||
|
||||
|
||||
const PHYSFS_Archiver __PHYSFS_Archiver_MVL =
|
||||
{
|
||||
&__PHYSFS_ArchiveInfo_MVL,
|
||||
MVL_isArchive, /* isArchive() method */
|
||||
MVL_openArchive, /* openArchive() method */
|
||||
MVL_enumerateFiles, /* enumerateFiles() method */
|
||||
MVL_exists, /* exists() method */
|
||||
MVL_isDirectory, /* isDirectory() method */
|
||||
MVL_isSymLink, /* isSymLink() method */
|
||||
MVL_getLastModTime, /* getLastModTime() method */
|
||||
MVL_openRead, /* openRead() method */
|
||||
MVL_openWrite, /* openWrite() method */
|
||||
MVL_openAppend, /* openAppend() method */
|
||||
MVL_remove, /* remove() method */
|
||||
MVL_mkdir, /* mkdir() method */
|
||||
MVL_dirClose, /* dirClose() method */
|
||||
MVL_read, /* read() method */
|
||||
MVL_write, /* write() method */
|
||||
MVL_eof, /* eof() method */
|
||||
MVL_tell, /* tell() method */
|
||||
MVL_seek, /* seek() method */
|
||||
MVL_fileLength, /* fileLength() method */
|
||||
MVL_fileClose /* fileClose() method */
|
||||
};
|
||||
|
||||
#endif /* defined PHYSFS_SUPPORTS_MVL */
|
||||
|
||||
/* end of mvl.c ... */
|
||||
|
||||
@@ -0,0 +1,633 @@
|
||||
/*
|
||||
* QPAK support routines for PhysicsFS.
|
||||
*
|
||||
* This archiver handles the archive format utilized by Quake 1 and 2.
|
||||
* Quake3-based games use the PkZip/Info-Zip format (which our zip.c
|
||||
* archiver handles).
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* This format info (in more detail) comes from:
|
||||
* http://debian.fmi.uni-sofia.bg/~sergei/cgsr/docs/pak.txt
|
||||
*
|
||||
* Quake PAK Format
|
||||
*
|
||||
* Header
|
||||
* (4 bytes) signature = 'PACK'
|
||||
* (4 bytes) directory offset
|
||||
* (4 bytes) directory length
|
||||
*
|
||||
* Directory
|
||||
* (56 bytes) file name
|
||||
* (4 bytes) file position
|
||||
* (4 bytes) file length
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Please see the file LICENSE.txt in the source's root directory.
|
||||
*
|
||||
* This file written by Ryan C. Gordon.
|
||||
*/
|
||||
|
||||
#if (defined PHYSFS_SUPPORTS_QPAK)
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "physfs.h"
|
||||
|
||||
#define __PHYSICSFS_INTERNAL__
|
||||
#include "physfs_internal.h"
|
||||
|
||||
#if 1 /* Make this case insensitive? */
|
||||
#define QPAK_strcmp(x, y) __PHYSFS_stricmpASCII(x, y)
|
||||
#define QPAK_strncmp(x, y, z) __PHYSFS_strnicmpASCII(x, y, z)
|
||||
#else
|
||||
#define QPAK_strcmp(x, y) strcmp(x, y)
|
||||
#define QPAK_strncmp(x, y, z) strncmp(x, y, z)
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char name[56];
|
||||
PHYSFS_uint32 startPos;
|
||||
PHYSFS_uint32 size;
|
||||
} QPAKentry;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *filename;
|
||||
PHYSFS_sint64 last_mod_time;
|
||||
PHYSFS_uint32 entryCount;
|
||||
QPAKentry *entries;
|
||||
} QPAKinfo;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void *handle;
|
||||
QPAKentry *entry;
|
||||
PHYSFS_uint32 curPos;
|
||||
} QPAKfileinfo;
|
||||
|
||||
/* Magic numbers... */
|
||||
#define QPAK_SIG 0x4b434150 /* "PACK" in ASCII. */
|
||||
|
||||
|
||||
static void QPAK_dirClose(dvoid *opaque)
|
||||
{
|
||||
QPAKinfo *info = ((QPAKinfo *) opaque);
|
||||
allocator.Free(info->filename);
|
||||
allocator.Free(info->entries);
|
||||
allocator.Free(info);
|
||||
} /* QPAK_dirClose */
|
||||
|
||||
|
||||
static PHYSFS_sint64 QPAK_read(fvoid *opaque, void *buffer,
|
||||
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
|
||||
{
|
||||
QPAKfileinfo *finfo = (QPAKfileinfo *) opaque;
|
||||
QPAKentry *entry = finfo->entry;
|
||||
PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos;
|
||||
PHYSFS_uint32 objsLeft = (bytesLeft / objSize);
|
||||
PHYSFS_sint64 rc;
|
||||
|
||||
if (objsLeft < objCount)
|
||||
objCount = objsLeft;
|
||||
|
||||
rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount);
|
||||
if (rc > 0)
|
||||
finfo->curPos += (PHYSFS_uint32) (rc * objSize);
|
||||
|
||||
return(rc);
|
||||
} /* QPAK_read */
|
||||
|
||||
|
||||
static PHYSFS_sint64 QPAK_write(fvoid *opaque, const void *buffer,
|
||||
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
|
||||
} /* QPAK_write */
|
||||
|
||||
|
||||
static int QPAK_eof(fvoid *opaque)
|
||||
{
|
||||
QPAKfileinfo *finfo = (QPAKfileinfo *) opaque;
|
||||
QPAKentry *entry = finfo->entry;
|
||||
return(finfo->curPos >= entry->size);
|
||||
} /* QPAK_eof */
|
||||
|
||||
|
||||
static PHYSFS_sint64 QPAK_tell(fvoid *opaque)
|
||||
{
|
||||
return(((QPAKfileinfo *) opaque)->curPos);
|
||||
} /* QPAK_tell */
|
||||
|
||||
|
||||
static int QPAK_seek(fvoid *opaque, PHYSFS_uint64 offset)
|
||||
{
|
||||
QPAKfileinfo *finfo = (QPAKfileinfo *) opaque;
|
||||
QPAKentry *entry = finfo->entry;
|
||||
int rc;
|
||||
|
||||
BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
|
||||
BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0);
|
||||
rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset);
|
||||
if (rc)
|
||||
finfo->curPos = (PHYSFS_uint32) offset;
|
||||
|
||||
return(rc);
|
||||
} /* QPAK_seek */
|
||||
|
||||
|
||||
static PHYSFS_sint64 QPAK_fileLength(fvoid *opaque)
|
||||
{
|
||||
QPAKfileinfo *finfo = (QPAKfileinfo *) opaque;
|
||||
return((PHYSFS_sint64) finfo->entry->size);
|
||||
} /* QPAK_fileLength */
|
||||
|
||||
|
||||
static int QPAK_fileClose(fvoid *opaque)
|
||||
{
|
||||
QPAKfileinfo *finfo = (QPAKfileinfo *) opaque;
|
||||
BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
|
||||
allocator.Free(finfo);
|
||||
return(1);
|
||||
} /* QPAK_fileClose */
|
||||
|
||||
|
||||
static int qpak_open(const char *filename, int forWriting,
|
||||
void **fh, PHYSFS_uint32 *count)
|
||||
{
|
||||
PHYSFS_uint32 buf;
|
||||
|
||||
*fh = NULL;
|
||||
BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
|
||||
|
||||
*fh = __PHYSFS_platformOpenRead(filename);
|
||||
BAIL_IF_MACRO(*fh == NULL, NULL, 0);
|
||||
|
||||
if (__PHYSFS_platformRead(*fh, &buf, sizeof (PHYSFS_uint32), 1) != 1)
|
||||
goto openQpak_failed;
|
||||
|
||||
buf = PHYSFS_swapULE32(buf);
|
||||
GOTO_IF_MACRO(buf != QPAK_SIG, ERR_UNSUPPORTED_ARCHIVE, openQpak_failed);
|
||||
|
||||
if (__PHYSFS_platformRead(*fh, &buf, sizeof (PHYSFS_uint32), 1) != 1)
|
||||
goto openQpak_failed;
|
||||
|
||||
buf = PHYSFS_swapULE32(buf); /* directory table offset. */
|
||||
|
||||
if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1)
|
||||
goto openQpak_failed;
|
||||
|
||||
*count = PHYSFS_swapULE32(*count);
|
||||
|
||||
/* corrupted archive? */
|
||||
GOTO_IF_MACRO((*count % 64) != 0, ERR_CORRUPTED, openQpak_failed);
|
||||
|
||||
if (!__PHYSFS_platformSeek(*fh, buf))
|
||||
goto openQpak_failed;
|
||||
|
||||
*count /= 64;
|
||||
return(1);
|
||||
|
||||
openQpak_failed:
|
||||
if (*fh != NULL)
|
||||
__PHYSFS_platformClose(*fh);
|
||||
|
||||
*count = -1;
|
||||
*fh = NULL;
|
||||
return(0);
|
||||
} /* qpak_open */
|
||||
|
||||
|
||||
static int QPAK_isArchive(const char *filename, int forWriting)
|
||||
{
|
||||
void *fh;
|
||||
PHYSFS_uint32 fileCount;
|
||||
int retval = qpak_open(filename, forWriting, &fh, &fileCount);
|
||||
|
||||
if (fh != NULL)
|
||||
__PHYSFS_platformClose(fh);
|
||||
|
||||
return(retval);
|
||||
} /* QPAK_isArchive */
|
||||
|
||||
|
||||
static int qpak_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
|
||||
{
|
||||
if (one != two)
|
||||
{
|
||||
const QPAKentry *a = (const QPAKentry *) _a;
|
||||
return(QPAK_strcmp(a[one].name, a[two].name));
|
||||
} /* if */
|
||||
|
||||
return 0;
|
||||
} /* qpak_entry_cmp */
|
||||
|
||||
|
||||
static void qpak_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
|
||||
{
|
||||
if (one != two)
|
||||
{
|
||||
QPAKentry tmp;
|
||||
QPAKentry *first = &(((QPAKentry *) _a)[one]);
|
||||
QPAKentry *second = &(((QPAKentry *) _a)[two]);
|
||||
memcpy(&tmp, first, sizeof (QPAKentry));
|
||||
memcpy(first, second, sizeof (QPAKentry));
|
||||
memcpy(second, &tmp, sizeof (QPAKentry));
|
||||
} /* if */
|
||||
} /* qpak_entry_swap */
|
||||
|
||||
|
||||
static int qpak_load_entries(const char *name, int forWriting, QPAKinfo *info)
|
||||
{
|
||||
void *fh = NULL;
|
||||
PHYSFS_uint32 fileCount;
|
||||
QPAKentry *entry;
|
||||
|
||||
BAIL_IF_MACRO(!qpak_open(name, forWriting, &fh, &fileCount), NULL, 0);
|
||||
info->entryCount = fileCount;
|
||||
info->entries = (QPAKentry*) allocator.Malloc(sizeof(QPAKentry)*fileCount);
|
||||
if (info->entries == NULL)
|
||||
{
|
||||
__PHYSFS_platformClose(fh);
|
||||
BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
|
||||
} /* if */
|
||||
|
||||
for (entry = info->entries; fileCount > 0; fileCount--, entry++)
|
||||
{
|
||||
PHYSFS_uint32 loc;
|
||||
|
||||
if (__PHYSFS_platformRead(fh,&entry->name,sizeof(entry->name),1) != 1)
|
||||
{
|
||||
__PHYSFS_platformClose(fh);
|
||||
return(0);
|
||||
} /* if */
|
||||
|
||||
if (__PHYSFS_platformRead(fh,&loc,sizeof(loc),1) != 1)
|
||||
{
|
||||
__PHYSFS_platformClose(fh);
|
||||
return(0);
|
||||
} /* if */
|
||||
|
||||
if (__PHYSFS_platformRead(fh,&entry->size,sizeof(entry->size),1) != 1)
|
||||
{
|
||||
__PHYSFS_platformClose(fh);
|
||||
return(0);
|
||||
} /* if */
|
||||
|
||||
entry->size = PHYSFS_swapULE32(entry->size);
|
||||
entry->startPos = PHYSFS_swapULE32(loc);
|
||||
} /* for */
|
||||
|
||||
__PHYSFS_platformClose(fh);
|
||||
|
||||
__PHYSFS_sort(info->entries, info->entryCount,
|
||||
qpak_entry_cmp, qpak_entry_swap);
|
||||
return(1);
|
||||
} /* qpak_load_entries */
|
||||
|
||||
|
||||
static void *QPAK_openArchive(const char *name, int forWriting)
|
||||
{
|
||||
QPAKinfo *info = (QPAKinfo *) allocator.Malloc(sizeof (QPAKinfo));
|
||||
PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name);
|
||||
|
||||
BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, NULL);
|
||||
memset(info, '\0', sizeof (QPAKinfo));
|
||||
|
||||
info->filename = (char *) allocator.Malloc(strlen(name) + 1);
|
||||
if (info->filename == NULL)
|
||||
{
|
||||
__PHYSFS_setError(ERR_OUT_OF_MEMORY);
|
||||
goto QPAK_openArchive_failed;
|
||||
} /* if */
|
||||
|
||||
if (!qpak_load_entries(name, forWriting, info))
|
||||
goto QPAK_openArchive_failed;
|
||||
|
||||
strcpy(info->filename, name);
|
||||
info->last_mod_time = modtime;
|
||||
return(info);
|
||||
|
||||
QPAK_openArchive_failed:
|
||||
if (info != NULL)
|
||||
{
|
||||
if (info->filename != NULL)
|
||||
allocator.Free(info->filename);
|
||||
if (info->entries != NULL)
|
||||
allocator.Free(info->entries);
|
||||
allocator.Free(info);
|
||||
} /* if */
|
||||
|
||||
return(NULL);
|
||||
} /* QPAK_openArchive */
|
||||
|
||||
|
||||
static PHYSFS_sint32 qpak_find_start_of_dir(QPAKinfo *info, const char *path,
|
||||
int stop_on_first_find)
|
||||
{
|
||||
PHYSFS_sint32 lo = 0;
|
||||
PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
|
||||
PHYSFS_sint32 middle;
|
||||
PHYSFS_uint32 dlen = strlen(path);
|
||||
PHYSFS_sint32 retval = -1;
|
||||
const char *name;
|
||||
int rc;
|
||||
|
||||
if (*path == '\0') /* root dir? */
|
||||
return(0);
|
||||
|
||||
if ((dlen > 0) && (path[dlen - 1] == '/')) /* ignore trailing slash. */
|
||||
dlen--;
|
||||
|
||||
while (lo <= hi)
|
||||
{
|
||||
middle = lo + ((hi - lo) / 2);
|
||||
name = info->entries[middle].name;
|
||||
rc = QPAK_strncmp(path, name, dlen);
|
||||
if (rc == 0)
|
||||
{
|
||||
char ch = name[dlen];
|
||||
if (ch < '/') /* make sure this isn't just a substr match. */
|
||||
rc = -1;
|
||||
else if (ch > '/')
|
||||
rc = 1;
|
||||
else
|
||||
{
|
||||
if (stop_on_first_find) /* Just checking dir's existance? */
|
||||
return(middle);
|
||||
|
||||
if (name[dlen + 1] == '\0') /* Skip initial dir entry. */
|
||||
return(middle + 1);
|
||||
|
||||
/* there might be more entries earlier in the list. */
|
||||
retval = middle;
|
||||
hi = middle - 1;
|
||||
} /* else */
|
||||
} /* if */
|
||||
|
||||
if (rc > 0)
|
||||
lo = middle + 1;
|
||||
else
|
||||
hi = middle - 1;
|
||||
} /* while */
|
||||
|
||||
return(retval);
|
||||
} /* qpak_find_start_of_dir */
|
||||
|
||||
|
||||
/*
|
||||
* Moved to seperate function so we can use alloca then immediately throw
|
||||
* away the allocated stack space...
|
||||
*/
|
||||
static void doEnumCallback(PHYSFS_EnumFilesCallback cb, void *callbackdata,
|
||||
const char *odir, const char *str, PHYSFS_sint32 ln)
|
||||
{
|
||||
char *newstr = __PHYSFS_smallAlloc(ln + 1);
|
||||
if (newstr == NULL)
|
||||
return;
|
||||
|
||||
memcpy(newstr, str, ln);
|
||||
newstr[ln] = '\0';
|
||||
cb(callbackdata, odir, newstr);
|
||||
__PHYSFS_smallFree(newstr);
|
||||
} /* doEnumCallback */
|
||||
|
||||
|
||||
static void QPAK_enumerateFiles(dvoid *opaque, const char *dname,
|
||||
int omitSymLinks, PHYSFS_EnumFilesCallback cb,
|
||||
const char *origdir, void *callbackdata)
|
||||
{
|
||||
QPAKinfo *info = ((QPAKinfo *) opaque);
|
||||
PHYSFS_sint32 dlen, dlen_inc, max, i;
|
||||
|
||||
i = qpak_find_start_of_dir(info, dname, 0);
|
||||
if (i == -1) /* no such directory. */
|
||||
return;
|
||||
|
||||
dlen = strlen(dname);
|
||||
if ((dlen > 0) && (dname[dlen - 1] == '/')) /* ignore trailing slash. */
|
||||
dlen--;
|
||||
|
||||
dlen_inc = ((dlen > 0) ? 1 : 0) + dlen;
|
||||
max = (PHYSFS_sint32) info->entryCount;
|
||||
while (i < max)
|
||||
{
|
||||
char *add;
|
||||
char *ptr;
|
||||
PHYSFS_sint32 ln;
|
||||
char *e = info->entries[i].name;
|
||||
if ((dlen) && ((QPAK_strncmp(e, dname, dlen)) || (e[dlen] != '/')))
|
||||
break; /* past end of this dir; we're done. */
|
||||
|
||||
add = e + dlen_inc;
|
||||
ptr = strchr(add, '/');
|
||||
ln = (PHYSFS_sint32) ((ptr) ? ptr-add : strlen(add));
|
||||
doEnumCallback(cb, callbackdata, origdir, add, ln);
|
||||
ln += dlen_inc; /* point past entry to children... */
|
||||
|
||||
/* increment counter and skip children of subdirs... */
|
||||
while ((++i < max) && (ptr != NULL))
|
||||
{
|
||||
char *e_new = info->entries[i].name;
|
||||
if ((QPAK_strncmp(e, e_new, ln) != 0) || (e_new[ln] != '/'))
|
||||
break;
|
||||
} /* while */
|
||||
} /* while */
|
||||
} /* QPAK_enumerateFiles */
|
||||
|
||||
|
||||
/*
|
||||
* This will find the QPAKentry associated with a path in platform-independent
|
||||
* notation. Directories don't have QPAKentries associated with them, but
|
||||
* (*isDir) will be set to non-zero if a dir was hit.
|
||||
*/
|
||||
static QPAKentry *qpak_find_entry(QPAKinfo *info, const char *path, int *isDir)
|
||||
{
|
||||
QPAKentry *a = info->entries;
|
||||
PHYSFS_sint32 pathlen = strlen(path);
|
||||
PHYSFS_sint32 lo = 0;
|
||||
PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
|
||||
PHYSFS_sint32 middle;
|
||||
const char *thispath = NULL;
|
||||
int rc;
|
||||
|
||||
while (lo <= hi)
|
||||
{
|
||||
middle = lo + ((hi - lo) / 2);
|
||||
thispath = a[middle].name;
|
||||
rc = QPAK_strncmp(path, thispath, pathlen);
|
||||
|
||||
if (rc > 0)
|
||||
lo = middle + 1;
|
||||
|
||||
else if (rc < 0)
|
||||
hi = middle - 1;
|
||||
|
||||
else /* substring match...might be dir or entry or nothing. */
|
||||
{
|
||||
if (isDir != NULL)
|
||||
{
|
||||
*isDir = (thispath[pathlen] == '/');
|
||||
if (*isDir)
|
||||
return(NULL);
|
||||
} /* if */
|
||||
|
||||
if (thispath[pathlen] == '\0') /* found entry? */
|
||||
return(&a[middle]);
|
||||
/* adjust search params, try again. */
|
||||
else if (thispath[pathlen] > '/')
|
||||
hi = middle - 1;
|
||||
else
|
||||
lo = middle + 1;
|
||||
} /* if */
|
||||
} /* while */
|
||||
|
||||
if (isDir != NULL)
|
||||
*isDir = 0;
|
||||
|
||||
BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
|
||||
} /* qpak_find_entry */
|
||||
|
||||
|
||||
static int QPAK_exists(dvoid *opaque, const char *name)
|
||||
{
|
||||
int isDir;
|
||||
QPAKinfo *info = (QPAKinfo *) opaque;
|
||||
QPAKentry *entry = qpak_find_entry(info, name, &isDir);
|
||||
return((entry != NULL) || (isDir));
|
||||
} /* QPAK_exists */
|
||||
|
||||
|
||||
static int QPAK_isDirectory(dvoid *opaque, const char *name, int *fileExists)
|
||||
{
|
||||
QPAKinfo *info = (QPAKinfo *) opaque;
|
||||
int isDir;
|
||||
QPAKentry *entry = qpak_find_entry(info, name, &isDir);
|
||||
|
||||
*fileExists = ((isDir) || (entry != NULL));
|
||||
if (isDir)
|
||||
return(1); /* definitely a dir. */
|
||||
|
||||
BAIL_MACRO(ERR_NO_SUCH_FILE, 0);
|
||||
} /* QPAK_isDirectory */
|
||||
|
||||
|
||||
static int QPAK_isSymLink(dvoid *opaque, const char *name, int *fileExists)
|
||||
{
|
||||
*fileExists = QPAK_exists(opaque, name);
|
||||
return(0); /* never symlinks in a quake pak. */
|
||||
} /* QPAK_isSymLink */
|
||||
|
||||
|
||||
static PHYSFS_sint64 QPAK_getLastModTime(dvoid *opaque,
|
||||
const char *name,
|
||||
int *fileExists)
|
||||
{
|
||||
int isDir;
|
||||
QPAKinfo *info = ((QPAKinfo *) opaque);
|
||||
PHYSFS_sint64 retval = -1;
|
||||
QPAKentry *entry = qpak_find_entry(info, name, &isDir);
|
||||
|
||||
*fileExists = ((isDir) || (entry != NULL));
|
||||
if (*fileExists) /* use time of QPAK itself in the physical filesystem. */
|
||||
retval = info->last_mod_time;
|
||||
|
||||
return(retval);
|
||||
} /* QPAK_getLastModTime */
|
||||
|
||||
|
||||
static fvoid *QPAK_openRead(dvoid *opaque, const char *fnm, int *fileExists)
|
||||
{
|
||||
QPAKinfo *info = ((QPAKinfo *) opaque);
|
||||
QPAKfileinfo *finfo;
|
||||
QPAKentry *entry;
|
||||
int isDir;
|
||||
|
||||
entry = qpak_find_entry(info, fnm, &isDir);
|
||||
*fileExists = ((entry != NULL) || (isDir));
|
||||
BAIL_IF_MACRO(isDir, ERR_NOT_A_FILE, NULL);
|
||||
BAIL_IF_MACRO(entry == NULL, ERR_NO_SUCH_FILE, NULL);
|
||||
|
||||
finfo = (QPAKfileinfo *) allocator.Malloc(sizeof (QPAKfileinfo));
|
||||
BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
|
||||
|
||||
finfo->handle = __PHYSFS_platformOpenRead(info->filename);
|
||||
if ( (finfo->handle == NULL) ||
|
||||
(!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
|
||||
{
|
||||
allocator.Free(finfo);
|
||||
return(NULL);
|
||||
} /* if */
|
||||
|
||||
finfo->curPos = 0;
|
||||
finfo->entry = entry;
|
||||
return(finfo);
|
||||
} /* QPAK_openRead */
|
||||
|
||||
|
||||
static fvoid *QPAK_openWrite(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
|
||||
} /* QPAK_openWrite */
|
||||
|
||||
|
||||
static fvoid *QPAK_openAppend(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
|
||||
} /* QPAK_openAppend */
|
||||
|
||||
|
||||
static int QPAK_remove(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
|
||||
} /* QPAK_remove */
|
||||
|
||||
|
||||
static int QPAK_mkdir(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
|
||||
} /* QPAK_mkdir */
|
||||
|
||||
|
||||
const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_QPAK =
|
||||
{
|
||||
"PAK",
|
||||
QPAK_ARCHIVE_DESCRIPTION,
|
||||
"Ryan C. Gordon <icculus@icculus.org>",
|
||||
"http://icculus.org/physfs/",
|
||||
};
|
||||
|
||||
|
||||
const PHYSFS_Archiver __PHYSFS_Archiver_QPAK =
|
||||
{
|
||||
&__PHYSFS_ArchiveInfo_QPAK,
|
||||
QPAK_isArchive, /* isArchive() method */
|
||||
QPAK_openArchive, /* openArchive() method */
|
||||
QPAK_enumerateFiles, /* enumerateFiles() method */
|
||||
QPAK_exists, /* exists() method */
|
||||
QPAK_isDirectory, /* isDirectory() method */
|
||||
QPAK_isSymLink, /* isSymLink() method */
|
||||
QPAK_getLastModTime, /* getLastModTime() method */
|
||||
QPAK_openRead, /* openRead() method */
|
||||
QPAK_openWrite, /* openWrite() method */
|
||||
QPAK_openAppend, /* openAppend() method */
|
||||
QPAK_remove, /* remove() method */
|
||||
QPAK_mkdir, /* mkdir() method */
|
||||
QPAK_dirClose, /* dirClose() method */
|
||||
QPAK_read, /* read() method */
|
||||
QPAK_write, /* write() method */
|
||||
QPAK_eof, /* eof() method */
|
||||
QPAK_tell, /* tell() method */
|
||||
QPAK_seek, /* seek() method */
|
||||
QPAK_fileLength, /* fileLength() method */
|
||||
QPAK_fileClose /* fileClose() method */
|
||||
};
|
||||
|
||||
#endif /* defined PHYSFS_SUPPORTS_QPAK */
|
||||
|
||||
/* end of qpak.c ... */
|
||||
|
||||
@@ -0,0 +1,534 @@
|
||||
/*
|
||||
* WAD support routines for PhysicsFS.
|
||||
*
|
||||
* This driver handles DOOM engine archives ("wads").
|
||||
* This format (but not this driver) was designed by id Software for use
|
||||
* with the DOOM engine.
|
||||
* The specs of the format are from the unofficial doom specs v1.666
|
||||
* found here: http://www.gamers.org/dhs/helpdocs/dmsp1666.html
|
||||
* The format of the archive: (from the specs)
|
||||
*
|
||||
* A WAD file has three parts:
|
||||
* (1) a twelve-byte header
|
||||
* (2) one or more "lumps"
|
||||
* (3) a directory or "info table" that contains the names, offsets, and
|
||||
* sizes of all the lumps in the WAD
|
||||
*
|
||||
* The header consists of three four-byte parts:
|
||||
* (a) an ASCII string which must be either "IWAD" or "PWAD"
|
||||
* (b) a 4-byte (long) integer which is the number of lumps in the wad
|
||||
* (c) a long integer which is the file offset to the start of
|
||||
* the directory
|
||||
*
|
||||
* The directory has one 16-byte entry for every lump. Each entry consists
|
||||
* of three parts:
|
||||
*
|
||||
* (a) a long integer, the file offset to the start of the lump
|
||||
* (b) a long integer, the size of the lump in bytes
|
||||
* (c) an 8-byte ASCII string, the name of the lump, padded with zeros.
|
||||
* For example, the "DEMO1" entry in hexadecimal would be
|
||||
* (44 45 4D 4F 31 00 00 00)
|
||||
*
|
||||
* Note that there is no way to tell if an opened WAD archive is a
|
||||
* IWAD or PWAD with this archiver.
|
||||
* I couldn't think of a way to provide that information, without being too
|
||||
* hacky.
|
||||
* I don't think it's really that important though.
|
||||
*
|
||||
*
|
||||
* Please see the file LICENSE.txt in the source's root directory.
|
||||
*
|
||||
* This file written by Travis Wells, based on the GRP archiver by
|
||||
* Ryan C. Gordon.
|
||||
*/
|
||||
|
||||
#if (defined PHYSFS_SUPPORTS_WAD)
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "physfs.h"
|
||||
|
||||
#define __PHYSICSFS_INTERNAL__
|
||||
#include "physfs_internal.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char name[18];
|
||||
PHYSFS_uint32 startPos;
|
||||
PHYSFS_uint32 size;
|
||||
} WADentry;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *filename;
|
||||
PHYSFS_sint64 last_mod_time;
|
||||
PHYSFS_uint32 entryCount;
|
||||
PHYSFS_uint32 entryOffset;
|
||||
WADentry *entries;
|
||||
} WADinfo;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void *handle;
|
||||
WADentry *entry;
|
||||
PHYSFS_uint32 curPos;
|
||||
} WADfileinfo;
|
||||
|
||||
|
||||
static void WAD_dirClose(dvoid *opaque)
|
||||
{
|
||||
WADinfo *info = ((WADinfo *) opaque);
|
||||
allocator.Free(info->filename);
|
||||
allocator.Free(info->entries);
|
||||
allocator.Free(info);
|
||||
} /* WAD_dirClose */
|
||||
|
||||
|
||||
static PHYSFS_sint64 WAD_read(fvoid *opaque, void *buffer,
|
||||
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
|
||||
{
|
||||
WADfileinfo *finfo = (WADfileinfo *) opaque;
|
||||
WADentry *entry = finfo->entry;
|
||||
PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos;
|
||||
PHYSFS_uint32 objsLeft = (bytesLeft / objSize);
|
||||
PHYSFS_sint64 rc;
|
||||
|
||||
if (objsLeft < objCount)
|
||||
objCount = objsLeft;
|
||||
|
||||
rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount);
|
||||
if (rc > 0)
|
||||
finfo->curPos += (PHYSFS_uint32) (rc * objSize);
|
||||
|
||||
return(rc);
|
||||
} /* WAD_read */
|
||||
|
||||
|
||||
static PHYSFS_sint64 WAD_write(fvoid *opaque, const void *buffer,
|
||||
PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
|
||||
} /* WAD_write */
|
||||
|
||||
|
||||
static int WAD_eof(fvoid *opaque)
|
||||
{
|
||||
WADfileinfo *finfo = (WADfileinfo *) opaque;
|
||||
WADentry *entry = finfo->entry;
|
||||
return(finfo->curPos >= entry->size);
|
||||
} /* WAD_eof */
|
||||
|
||||
|
||||
static PHYSFS_sint64 WAD_tell(fvoid *opaque)
|
||||
{
|
||||
return(((WADfileinfo *) opaque)->curPos);
|
||||
} /* WAD_tell */
|
||||
|
||||
|
||||
static int WAD_seek(fvoid *opaque, PHYSFS_uint64 offset)
|
||||
{
|
||||
WADfileinfo *finfo = (WADfileinfo *) opaque;
|
||||
WADentry *entry = finfo->entry;
|
||||
int rc;
|
||||
|
||||
BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
|
||||
BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0);
|
||||
rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset);
|
||||
if (rc)
|
||||
finfo->curPos = (PHYSFS_uint32) offset;
|
||||
|
||||
return(rc);
|
||||
} /* WAD_seek */
|
||||
|
||||
|
||||
static PHYSFS_sint64 WAD_fileLength(fvoid *opaque)
|
||||
{
|
||||
WADfileinfo *finfo = (WADfileinfo *) opaque;
|
||||
return((PHYSFS_sint64) finfo->entry->size);
|
||||
} /* WAD_fileLength */
|
||||
|
||||
|
||||
static int WAD_fileClose(fvoid *opaque)
|
||||
{
|
||||
WADfileinfo *finfo = (WADfileinfo *) opaque;
|
||||
BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
|
||||
allocator.Free(finfo);
|
||||
return(1);
|
||||
} /* WAD_fileClose */
|
||||
|
||||
|
||||
static int wad_open(const char *filename, int forWriting,
|
||||
void **fh, PHYSFS_uint32 *count,PHYSFS_uint32 *offset)
|
||||
{
|
||||
PHYSFS_uint8 buf[4];
|
||||
|
||||
*fh = NULL;
|
||||
BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
|
||||
|
||||
*fh = __PHYSFS_platformOpenRead(filename);
|
||||
BAIL_IF_MACRO(*fh == NULL, NULL, 0);
|
||||
|
||||
if (__PHYSFS_platformRead(*fh, buf, 4, 1) != 1)
|
||||
goto openWad_failed;
|
||||
|
||||
if (memcmp(buf, "IWAD", 4) != 0 && memcmp(buf, "PWAD", 4) != 0)
|
||||
{
|
||||
__PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
|
||||
goto openWad_failed;
|
||||
} /* if */
|
||||
|
||||
if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1)
|
||||
goto openWad_failed;
|
||||
|
||||
*count = PHYSFS_swapULE32(*count);
|
||||
|
||||
if (__PHYSFS_platformRead(*fh, offset, sizeof (PHYSFS_uint32), 1) != 1)
|
||||
goto openWad_failed;
|
||||
|
||||
*offset = PHYSFS_swapULE32(*offset);
|
||||
|
||||
return(1);
|
||||
|
||||
openWad_failed:
|
||||
if (*fh != NULL)
|
||||
__PHYSFS_platformClose(*fh);
|
||||
|
||||
*count = -1;
|
||||
*fh = NULL;
|
||||
return(0);
|
||||
} /* wad_open */
|
||||
|
||||
|
||||
static int WAD_isArchive(const char *filename, int forWriting)
|
||||
{
|
||||
void *fh;
|
||||
PHYSFS_uint32 fileCount,offset;
|
||||
int retval = wad_open(filename, forWriting, &fh, &fileCount,&offset);
|
||||
|
||||
if (fh != NULL)
|
||||
__PHYSFS_platformClose(fh);
|
||||
|
||||
return(retval);
|
||||
} /* WAD_isArchive */
|
||||
|
||||
|
||||
static int wad_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
|
||||
{
|
||||
if (one != two)
|
||||
{
|
||||
const WADentry *a = (const WADentry *) _a;
|
||||
return(strcmp(a[one].name, a[two].name));
|
||||
} /* if */
|
||||
|
||||
return 0;
|
||||
} /* wad_entry_cmp */
|
||||
|
||||
|
||||
static void wad_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
|
||||
{
|
||||
if (one != two)
|
||||
{
|
||||
WADentry tmp;
|
||||
WADentry *first = &(((WADentry *) _a)[one]);
|
||||
WADentry *second = &(((WADentry *) _a)[two]);
|
||||
memcpy(&tmp, first, sizeof (WADentry));
|
||||
memcpy(first, second, sizeof (WADentry));
|
||||
memcpy(second, &tmp, sizeof (WADentry));
|
||||
} /* if */
|
||||
} /* wad_entry_swap */
|
||||
|
||||
|
||||
static int wad_load_entries(const char *name, int forWriting, WADinfo *info)
|
||||
{
|
||||
void *fh = NULL;
|
||||
PHYSFS_uint32 fileCount;
|
||||
PHYSFS_uint32 directoryOffset;
|
||||
WADentry *entry;
|
||||
char lastDirectory[9];
|
||||
|
||||
lastDirectory[8] = 0; /* Make sure lastDirectory stays null-terminated. */
|
||||
|
||||
BAIL_IF_MACRO(!wad_open(name, forWriting, &fh, &fileCount,&directoryOffset), NULL, 0);
|
||||
info->entryCount = fileCount;
|
||||
info->entries = (WADentry *) allocator.Malloc(sizeof(WADentry)*fileCount);
|
||||
if (info->entries == NULL)
|
||||
{
|
||||
__PHYSFS_platformClose(fh);
|
||||
BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
|
||||
} /* if */
|
||||
|
||||
__PHYSFS_platformSeek(fh,directoryOffset);
|
||||
|
||||
for (entry = info->entries; fileCount > 0; fileCount--, entry++)
|
||||
{
|
||||
if (__PHYSFS_platformRead(fh, &entry->startPos, 4, 1) != 1)
|
||||
{
|
||||
__PHYSFS_platformClose(fh);
|
||||
return(0);
|
||||
} /* if */
|
||||
|
||||
if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1)
|
||||
{
|
||||
__PHYSFS_platformClose(fh);
|
||||
return(0);
|
||||
} /* if */
|
||||
|
||||
if (__PHYSFS_platformRead(fh, &entry->name, 8, 1) != 1)
|
||||
{
|
||||
__PHYSFS_platformClose(fh);
|
||||
return(0);
|
||||
} /* if */
|
||||
|
||||
entry->name[8] = '\0'; /* name might not be null-terminated in file. */
|
||||
entry->size = PHYSFS_swapULE32(entry->size);
|
||||
entry->startPos = PHYSFS_swapULE32(entry->startPos);
|
||||
} /* for */
|
||||
|
||||
__PHYSFS_platformClose(fh);
|
||||
|
||||
__PHYSFS_sort(info->entries, info->entryCount,
|
||||
wad_entry_cmp, wad_entry_swap);
|
||||
return(1);
|
||||
} /* wad_load_entries */
|
||||
|
||||
|
||||
static void *WAD_openArchive(const char *name, int forWriting)
|
||||
{
|
||||
PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name);
|
||||
WADinfo *info = (WADinfo *) allocator.Malloc(sizeof (WADinfo));
|
||||
|
||||
BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, NULL);
|
||||
memset(info, '\0', sizeof (WADinfo));
|
||||
|
||||
info->filename = (char *) allocator.Malloc(strlen(name) + 1);
|
||||
GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, WAD_openArchive_failed);
|
||||
|
||||
if (!wad_load_entries(name, forWriting, info))
|
||||
goto WAD_openArchive_failed;
|
||||
|
||||
strcpy(info->filename, name);
|
||||
info->last_mod_time = modtime;
|
||||
return(info);
|
||||
|
||||
WAD_openArchive_failed:
|
||||
if (info != NULL)
|
||||
{
|
||||
if (info->filename != NULL)
|
||||
allocator.Free(info->filename);
|
||||
if (info->entries != NULL)
|
||||
allocator.Free(info->entries);
|
||||
allocator.Free(info);
|
||||
} /* if */
|
||||
|
||||
return(NULL);
|
||||
} /* WAD_openArchive */
|
||||
|
||||
|
||||
static void WAD_enumerateFiles(dvoid *opaque, const char *dname,
|
||||
int omitSymLinks, PHYSFS_EnumFilesCallback cb,
|
||||
const char *origdir, void *callbackdata)
|
||||
{
|
||||
WADinfo *info = ((WADinfo *) opaque);
|
||||
WADentry *entry = info->entries;
|
||||
PHYSFS_uint32 max = info->entryCount;
|
||||
PHYSFS_uint32 i;
|
||||
const char *name;
|
||||
char *sep;
|
||||
|
||||
if (*dname == '\0') /* root directory enumeration? */
|
||||
{
|
||||
for (i = 0; i < max; i++, entry++)
|
||||
{
|
||||
name = entry->name;
|
||||
if (strchr(name, '/') == NULL)
|
||||
cb(callbackdata, origdir, name);
|
||||
} /* for */
|
||||
} /* if */
|
||||
else
|
||||
{
|
||||
for (i = 0; i < max; i++, entry++)
|
||||
{
|
||||
name = entry->name;
|
||||
sep = strchr(name, '/');
|
||||
if (sep != NULL)
|
||||
{
|
||||
if (strncmp(dname, name, (sep - name)) == 0)
|
||||
cb(callbackdata, origdir, sep + 1);
|
||||
} /* if */
|
||||
} /* for */
|
||||
} /* else */
|
||||
} /* WAD_enumerateFiles */
|
||||
|
||||
|
||||
static WADentry *wad_find_entry(WADinfo *info, const char *name)
|
||||
{
|
||||
WADentry *a = info->entries;
|
||||
PHYSFS_sint32 lo = 0;
|
||||
PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
|
||||
PHYSFS_sint32 middle;
|
||||
int rc;
|
||||
|
||||
while (lo <= hi)
|
||||
{
|
||||
middle = lo + ((hi - lo) / 2);
|
||||
rc = strcmp(name, a[middle].name);
|
||||
if (rc == 0) /* found it! */
|
||||
return(&a[middle]);
|
||||
else if (rc > 0)
|
||||
lo = middle + 1;
|
||||
else
|
||||
hi = middle - 1;
|
||||
} /* while */
|
||||
|
||||
BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
|
||||
} /* wad_find_entry */
|
||||
|
||||
|
||||
static int WAD_exists(dvoid *opaque, const char *name)
|
||||
{
|
||||
return(wad_find_entry(((WADinfo *) opaque), name) != NULL);
|
||||
} /* WAD_exists */
|
||||
|
||||
|
||||
static int WAD_isDirectory(dvoid *opaque, const char *name, int *fileExists)
|
||||
{
|
||||
WADentry *entry = wad_find_entry(((WADinfo *) opaque), name);
|
||||
if (entry != NULL)
|
||||
{
|
||||
char *n;
|
||||
|
||||
*fileExists = 1;
|
||||
|
||||
/* Can't be a directory if it's a subdirectory. */
|
||||
if (strchr(entry->name, '/') != NULL)
|
||||
return(0);
|
||||
|
||||
/* Check if it matches "MAP??" or "E?M?" ... */
|
||||
n = entry->name;
|
||||
if ((n[0] == 'E' && n[2] == 'M') ||
|
||||
(n[0] == 'M' && n[1] == 'A' && n[2] == 'P' && n[6] == 0))
|
||||
{
|
||||
return(1);
|
||||
} /* if */
|
||||
return(0);
|
||||
} /* if */
|
||||
else
|
||||
{
|
||||
*fileExists = 0;
|
||||
return(0);
|
||||
} /* else */
|
||||
} /* WAD_isDirectory */
|
||||
|
||||
|
||||
static int WAD_isSymLink(dvoid *opaque, const char *name, int *fileExists)
|
||||
{
|
||||
*fileExists = WAD_exists(opaque, name);
|
||||
return(0); /* never symlinks in a wad. */
|
||||
} /* WAD_isSymLink */
|
||||
|
||||
|
||||
static PHYSFS_sint64 WAD_getLastModTime(dvoid *opaque,
|
||||
const char *name,
|
||||
int *fileExists)
|
||||
{
|
||||
WADinfo *info = ((WADinfo *) opaque);
|
||||
PHYSFS_sint64 retval = -1;
|
||||
|
||||
*fileExists = (wad_find_entry(info, name) != NULL);
|
||||
if (*fileExists) /* use time of WAD itself in the physical filesystem. */
|
||||
retval = info->last_mod_time;
|
||||
|
||||
return(retval);
|
||||
} /* WAD_getLastModTime */
|
||||
|
||||
|
||||
static fvoid *WAD_openRead(dvoid *opaque, const char *fnm, int *fileExists)
|
||||
{
|
||||
WADinfo *info = ((WADinfo *) opaque);
|
||||
WADfileinfo *finfo;
|
||||
WADentry *entry;
|
||||
|
||||
entry = wad_find_entry(info, fnm);
|
||||
*fileExists = (entry != NULL);
|
||||
BAIL_IF_MACRO(entry == NULL, NULL, NULL);
|
||||
|
||||
finfo = (WADfileinfo *) allocator.Malloc(sizeof (WADfileinfo));
|
||||
BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
|
||||
|
||||
finfo->handle = __PHYSFS_platformOpenRead(info->filename);
|
||||
if ( (finfo->handle == NULL) ||
|
||||
(!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
|
||||
{
|
||||
allocator.Free(finfo);
|
||||
return(NULL);
|
||||
} /* if */
|
||||
|
||||
finfo->curPos = 0;
|
||||
finfo->entry = entry;
|
||||
return(finfo);
|
||||
} /* WAD_openRead */
|
||||
|
||||
|
||||
static fvoid *WAD_openWrite(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
|
||||
} /* WAD_openWrite */
|
||||
|
||||
|
||||
static fvoid *WAD_openAppend(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
|
||||
} /* WAD_openAppend */
|
||||
|
||||
|
||||
static int WAD_remove(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
|
||||
} /* WAD_remove */
|
||||
|
||||
|
||||
static int WAD_mkdir(dvoid *opaque, const char *name)
|
||||
{
|
||||
BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
|
||||
} /* WAD_mkdir */
|
||||
|
||||
|
||||
const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_WAD =
|
||||
{
|
||||
"WAD",
|
||||
WAD_ARCHIVE_DESCRIPTION,
|
||||
"Travis Wells <traviswells@mchsi.com>",
|
||||
"http://www.3dmm2.com/doom/",
|
||||
};
|
||||
|
||||
|
||||
const PHYSFS_Archiver __PHYSFS_Archiver_WAD =
|
||||
{
|
||||
&__PHYSFS_ArchiveInfo_WAD,
|
||||
WAD_isArchive, /* isArchive() method */
|
||||
WAD_openArchive, /* openArchive() method */
|
||||
WAD_enumerateFiles, /* enumerateFiles() method */
|
||||
WAD_exists, /* exists() method */
|
||||
WAD_isDirectory, /* isDirectory() method */
|
||||
WAD_isSymLink, /* isSymLink() method */
|
||||
WAD_getLastModTime, /* getLastModTime() method */
|
||||
WAD_openRead, /* openRead() method */
|
||||
WAD_openWrite, /* openWrite() method */
|
||||
WAD_openAppend, /* openAppend() method */
|
||||
WAD_remove, /* remove() method */
|
||||
WAD_mkdir, /* mkdir() method */
|
||||
WAD_dirClose, /* dirClose() method */
|
||||
WAD_read, /* read() method */
|
||||
WAD_write, /* write() method */
|
||||
WAD_eof, /* eof() method */
|
||||
WAD_tell, /* tell() method */
|
||||
WAD_seek, /* seek() method */
|
||||
WAD_fileLength, /* fileLength() method */
|
||||
WAD_fileClose /* fileClose() method */
|
||||
};
|
||||
|
||||
#endif /* defined PHYSFS_SUPPORTS_WAD */
|
||||
|
||||
/* end of wad.c ... */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user