mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-30 07:31:13 +00:00
503 lines
13 KiB
C
503 lines
13 KiB
C
|
#include "quakedef.h"
|
||
|
#include "fs.h"
|
||
|
|
||
|
#ifdef PACKAGE_VPK
|
||
|
|
||
|
//
|
||
|
// in memory
|
||
|
//
|
||
|
|
||
|
typedef struct
|
||
|
{
|
||
|
fsbucket_t bucket;
|
||
|
|
||
|
char name[MAX_QPATH];
|
||
|
const struct dvpkfile_s *file;
|
||
|
} mvpkfile_t;
|
||
|
|
||
|
typedef struct vpk_s
|
||
|
{
|
||
|
searchpathfuncs_t pub;
|
||
|
char descname[MAX_OSPATH];
|
||
|
|
||
|
qbyte *treedata; //raw file list
|
||
|
size_t treesize;
|
||
|
|
||
|
struct vpk_s **fragments;
|
||
|
size_t numfragments;
|
||
|
|
||
|
void *mutex;
|
||
|
vfsfile_t *handle;
|
||
|
unsigned int filepos; //the pos the subfiles left it at (to optimize calls to vfs_seek)
|
||
|
int references; //seeing as all vfiles from a pak file use the parent's vfsfile, we need to keep the parent open until all subfiles are closed.
|
||
|
|
||
|
size_t numfiles;
|
||
|
mvpkfile_t files[1]; //processed file list
|
||
|
} vpk_t;
|
||
|
|
||
|
//
|
||
|
// on disk
|
||
|
//
|
||
|
typedef struct dvpkfile_s
|
||
|
{ //chars because these are misaligned.
|
||
|
unsigned char crc[4];
|
||
|
unsigned char preloadsize[2];
|
||
|
unsigned char archiveindex[2];
|
||
|
unsigned char archiveoffset[4];
|
||
|
unsigned char archivesize[4];
|
||
|
unsigned char sentinal[2];//=0xffff;
|
||
|
} dvpkfile_t;
|
||
|
|
||
|
typedef struct
|
||
|
{
|
||
|
//v1
|
||
|
unsigned int magic;
|
||
|
unsigned int version;
|
||
|
unsigned int tablesize;
|
||
|
|
||
|
//v2
|
||
|
unsigned int filedatasize;
|
||
|
unsigned int archivemd5size;
|
||
|
unsigned int globalmd5size;
|
||
|
unsigned int signaturesize;
|
||
|
} dvpkheader_t;
|
||
|
|
||
|
static void QDECL FSVPK_GetPathDetails(searchpathfuncs_t *handle, char *out, size_t outlen)
|
||
|
{
|
||
|
vpk_t *pak = (void*)handle;
|
||
|
|
||
|
*out = 0;
|
||
|
if (pak->references != 1)
|
||
|
Q_snprintfz(out, outlen, "(%i)", pak->references-1);
|
||
|
}
|
||
|
static void QDECL FSVPK_ClosePath(searchpathfuncs_t *handle)
|
||
|
{
|
||
|
qboolean stillopen;
|
||
|
size_t i;
|
||
|
vpk_t *pak = (void*)handle;
|
||
|
|
||
|
if (!Sys_LockMutex(pak->mutex))
|
||
|
return; //ohnoes
|
||
|
stillopen = --pak->references > 0;
|
||
|
Sys_UnlockMutex(pak->mutex);
|
||
|
if (stillopen)
|
||
|
return; //not free yet
|
||
|
|
||
|
|
||
|
VFS_CLOSE (pak->handle);
|
||
|
|
||
|
Sys_DestroyMutex(pak->mutex);
|
||
|
for (i = 0; i < pak->numfragments; i++)
|
||
|
{
|
||
|
if (pak->fragments[i])
|
||
|
pak->fragments[i]->Close(pak->fragments[i]);
|
||
|
pak->fragments[i] = NULL;
|
||
|
}
|
||
|
Z_Free(pak->fragments);
|
||
|
Z_Free(pak->treedata);
|
||
|
Z_Free(pak);
|
||
|
}
|
||
|
static void QDECL FSVPK_BuildHash(searchpathfuncs_t *handle, int depth, void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle))
|
||
|
{
|
||
|
vpk_t *pak = (void*)handle;
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < pak->numfiles; i++)
|
||
|
{
|
||
|
AddFileHash(depth, pak->files[i].name, &pak->files[i].bucket, &pak->files[i]);
|
||
|
}
|
||
|
}
|
||
|
static unsigned int QDECL FSVPK_FLocate(searchpathfuncs_t *handle, flocation_t *loc, const char *filename, void *hashedresult)
|
||
|
{
|
||
|
mvpkfile_t *pf = hashedresult;
|
||
|
int i;
|
||
|
vpk_t *pak = (void*)handle;
|
||
|
|
||
|
// look through all the pak file elements
|
||
|
|
||
|
if (pf)
|
||
|
{ //is this a pointer to a file in this pak?
|
||
|
if (pf < pak->files || pf > pak->files + pak->numfiles)
|
||
|
return FF_NOTFOUND; //was found in a different path
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for (i=0 ; i<pak->numfiles ; i++) //look for the file
|
||
|
{
|
||
|
if (!Q_strcasecmp (pak->files[i].name, filename))
|
||
|
{
|
||
|
pf = &pak->files[i];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pf)
|
||
|
{
|
||
|
if (loc)
|
||
|
{
|
||
|
loc->fhandle = pf;
|
||
|
*loc->rawname = 0;
|
||
|
loc->offset = (qofs_t)-1;
|
||
|
loc->len = ((pf->file->preloadsize[0]<<0)|(pf->file->preloadsize[1]<<8))
|
||
|
+((pf->file->archivesize[0]<<0)|(pf->file->archivesize[1]<<8)|(pf->file->archivesize[2]<<16)|(pf->file->archivesize[3]<<24));
|
||
|
}
|
||
|
return FF_FOUND;
|
||
|
}
|
||
|
return FF_NOTFOUND;
|
||
|
}
|
||
|
static int QDECL FSVPK_EnumerateFiles (searchpathfuncs_t *handle, const char *match, int (QDECL *func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *spath), void *parm)
|
||
|
{
|
||
|
vpk_t *pak = (void*)handle;
|
||
|
int num;
|
||
|
mvpkfile_t *file;
|
||
|
qofs_t size;
|
||
|
|
||
|
for (num = 0; num<(int)pak->numfiles; num++)
|
||
|
{
|
||
|
if (wildcmp(match, pak->files[num].name))
|
||
|
{
|
||
|
file = &pak->files[num];
|
||
|
//FIXME: time 0? maybe use the pak's mtime?
|
||
|
size = ((file->file->preloadsize[0]<<0)|(file->file->preloadsize[1]<<8))
|
||
|
+((file->file->archivesize[0]<<0)|(file->file->archivesize[1]<<8)|(file->file->archivesize[2]<<16)|(file->file->archivesize[3]<<24));
|
||
|
if (!func(file->name, size, 0, parm, handle))
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static int QDECL FSVPK_GeneratePureCRC(searchpathfuncs_t *handle, int seed, int crctype)
|
||
|
{
|
||
|
vpk_t *pak = (void*)handle;
|
||
|
|
||
|
return Com_BlockChecksum(pak->treedata, pak->treesize);
|
||
|
}
|
||
|
|
||
|
typedef struct {
|
||
|
vfsfile_t funcs;
|
||
|
vpk_t *parentpak;
|
||
|
const qbyte *preloaddata;
|
||
|
size_t preloadsize;
|
||
|
qofs_t startpos;
|
||
|
qofs_t length;
|
||
|
qofs_t currentpos;
|
||
|
} vfsvpk_t;
|
||
|
static int QDECL VFSVPK_ReadBytes (struct vfsfile_s *vfs, void *buffer, int bytestoread)
|
||
|
{
|
||
|
vfsvpk_t *vfsp = (void*)vfs;
|
||
|
int read;
|
||
|
|
||
|
if (bytestoread <= 0)
|
||
|
return 0;
|
||
|
|
||
|
if (vfsp->currentpos < vfsp->preloadsize)
|
||
|
{
|
||
|
read = bytestoread;
|
||
|
if (read > vfsp->preloadsize-vfsp->currentpos)
|
||
|
read = vfsp->preloadsize-vfsp->currentpos;
|
||
|
memcpy(buffer, vfsp->preloaddata, read);
|
||
|
vfsp->currentpos += read;
|
||
|
if (read == bytestoread)
|
||
|
return read; //we're done, no need to seek etc
|
||
|
bytestoread -= read;
|
||
|
}
|
||
|
|
||
|
if (vfsp->currentpos-vfsp->preloadsize + bytestoread > vfsp->length)
|
||
|
bytestoread = vfsp->length - (vfsp->currentpos-vfsp->preloadsize);
|
||
|
if (bytestoread <= 0)
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (Sys_LockMutex(vfsp->parentpak->mutex))
|
||
|
{
|
||
|
if (vfsp->parentpak->filepos != vfsp->startpos+vfsp->currentpos-vfsp->preloadsize)
|
||
|
VFS_SEEK(vfsp->parentpak->handle, vfsp->startpos+vfsp->currentpos-vfsp->preloadsize);
|
||
|
read = VFS_READ(vfsp->parentpak->handle, buffer, bytestoread);
|
||
|
if (read > 0)
|
||
|
vfsp->currentpos += read;
|
||
|
vfsp->parentpak->filepos = vfsp->startpos+vfsp->currentpos-vfsp->preloadsize;
|
||
|
Sys_UnlockMutex(vfsp->parentpak->mutex);
|
||
|
}
|
||
|
else
|
||
|
read = 0;
|
||
|
|
||
|
return read;
|
||
|
}
|
||
|
static int QDECL VFSVPK_WriteBytes (struct vfsfile_s *vfs, const void *buffer, int bytestoread)
|
||
|
{ //not supported.
|
||
|
Sys_Error("Cannot write to vpk files\n");
|
||
|
return 0;
|
||
|
}
|
||
|
static qboolean QDECL VFSVPK_Seek (struct vfsfile_s *vfs, qofs_t pos)
|
||
|
{
|
||
|
vfsvpk_t *vfsp = (void*)vfs;
|
||
|
if (pos > vfsp->length)
|
||
|
return false;
|
||
|
vfsp->currentpos = pos + vfsp->startpos;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
static qofs_t QDECL VFSVPK_Tell (struct vfsfile_s *vfs)
|
||
|
{
|
||
|
vfsvpk_t *vfsp = (void*)vfs;
|
||
|
return vfsp->currentpos - vfsp->startpos;
|
||
|
}
|
||
|
static qofs_t QDECL VFSVPK_GetLen (struct vfsfile_s *vfs)
|
||
|
{
|
||
|
vfsvpk_t *vfsp = (void*)vfs;
|
||
|
return vfsp->length;
|
||
|
}
|
||
|
static qboolean QDECL VFSVPK_Close(vfsfile_t *vfs)
|
||
|
{
|
||
|
vfsvpk_t *vfsp = (void*)vfs;
|
||
|
FSVPK_ClosePath(&vfsp->parentpak->pub); //tell the parent that we don't need it open any more (reference counts)
|
||
|
Z_Free(vfsp); //free ourselves.
|
||
|
return true;
|
||
|
}
|
||
|
static vfsfile_t *QDECL FSVPK_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode)
|
||
|
{
|
||
|
vpk_t *pack = (void*)handle;
|
||
|
vfsvpk_t *vfs;
|
||
|
mvpkfile_t *f = loc->fhandle;
|
||
|
unsigned int frag;
|
||
|
|
||
|
if (strcmp(mode, "rb"))
|
||
|
return NULL; //urm, unable to write/append
|
||
|
|
||
|
frag = (f->file->archiveindex[0]<<0)|(f->file->archiveindex[1]<<8);
|
||
|
if (frag >= pack->numfragments || !pack->fragments[frag])
|
||
|
return NULL;
|
||
|
pack = pack->fragments[frag];
|
||
|
|
||
|
vfs = Z_Malloc(sizeof(vfsvpk_t));
|
||
|
|
||
|
vfs->parentpak = pack;
|
||
|
if (!Sys_LockMutex(pack->mutex))
|
||
|
{
|
||
|
Z_Free(vfs);
|
||
|
return NULL;
|
||
|
}
|
||
|
vfs->parentpak->references++;
|
||
|
Sys_UnlockMutex(pack->mutex);
|
||
|
|
||
|
vfs->preloaddata = (const qbyte*)f->file + sizeof(*f->file);
|
||
|
vfs->preloadsize = (f->file->preloadsize[0]<<0) | (f->file->preloadsize[1]<<8);
|
||
|
|
||
|
vfs->startpos = (f->file->archiveoffset[0]<<0)|(f->file->archiveoffset[1]<<8)|(f->file->archiveoffset[2]<<16)|(f->file->archiveoffset[3]<<24);
|
||
|
vfs->length = loc->len;
|
||
|
vfs->currentpos = 0;
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
{
|
||
|
mpackfile_t *pf = loc->fhandle;
|
||
|
Q_strncpyz(vfs->funcs.dbgname, pf->name, sizeof(vfs->funcs.dbgname));
|
||
|
}
|
||
|
#endif
|
||
|
vfs->funcs.Close = VFSVPK_Close;
|
||
|
vfs->funcs.GetLen = VFSVPK_GetLen;
|
||
|
vfs->funcs.ReadBytes = VFSVPK_ReadBytes;
|
||
|
vfs->funcs.Seek = VFSVPK_Seek;
|
||
|
vfs->funcs.Tell = VFSVPK_Tell;
|
||
|
vfs->funcs.WriteBytes = VFSVPK_WriteBytes; //not supported
|
||
|
|
||
|
return (vfsfile_t *)vfs;
|
||
|
}
|
||
|
|
||
|
static void QDECL FSVPK_ReadFile(searchpathfuncs_t *handle, flocation_t *loc, char *buffer)
|
||
|
{
|
||
|
vfsfile_t *f;
|
||
|
f = FSVPK_OpenVFS(handle, loc, "rb");
|
||
|
if (!f) //err...
|
||
|
return;
|
||
|
VFS_READ(f, buffer, loc->len);
|
||
|
VFS_CLOSE(f);
|
||
|
}
|
||
|
|
||
|
static unsigned int FSVPK_WalkTree(vpk_t *vpk, const char *start, const char *end)
|
||
|
{ //the weird arrangement of these files is presumably an indicator of how source handles its file types.
|
||
|
const char *ext, *path, *name;
|
||
|
const dvpkfile_t *file;
|
||
|
size_t preloadsize;
|
||
|
unsigned int files = 0;
|
||
|
while(start < end)
|
||
|
{ //extensions
|
||
|
ext = start;
|
||
|
if (!*ext)
|
||
|
{
|
||
|
start++;
|
||
|
break;
|
||
|
}
|
||
|
if (ext[0] == ' ' && !ext[1])
|
||
|
ext = "";
|
||
|
start += strlen(start)+1;
|
||
|
while(start < end)
|
||
|
{ //paths
|
||
|
path = start;
|
||
|
if (!*path)
|
||
|
{
|
||
|
start++;
|
||
|
break;
|
||
|
}
|
||
|
if (path[0] == ' ' && !path[1])
|
||
|
path = "";
|
||
|
start += strlen(start)+1;
|
||
|
while(start < end)
|
||
|
{ //names
|
||
|
name = start;
|
||
|
if (!*name)
|
||
|
{
|
||
|
start++;
|
||
|
break;
|
||
|
}
|
||
|
if (name[0] == ' ' && !name[1])
|
||
|
name = "";
|
||
|
start += strlen(start)+1;
|
||
|
|
||
|
file = (const dvpkfile_t*)start;
|
||
|
preloadsize = (file->preloadsize[0]<<0) | (file->preloadsize[1]<<8);
|
||
|
start += sizeof(*file)+preloadsize;
|
||
|
if (start > end)
|
||
|
return 0; //truncated...
|
||
|
if (file->sentinal[0] != 0xff || file->sentinal[1] != 0xff)
|
||
|
return 0; //sentinal failure
|
||
|
// Con_Printf("Found file %s%s%s%s%s\n", path, *path?"/":"", name, *ext?".":"", ext);
|
||
|
if (!vpk)
|
||
|
files++;
|
||
|
else if (files < vpk->numfiles)
|
||
|
{
|
||
|
unsigned int frag = (file->archiveindex[0]<<0)|(file->archiveindex[1]<<8);
|
||
|
Q_snprintfz(vpk->files[files].name, sizeof(vpk->files[files].name), "%s%s%s%s%s", path, *path?"/":"", name, *ext?".":"", ext);
|
||
|
COM_CleanUpPath(vpk->files[files].name); //just in case...
|
||
|
vpk->files[files].file = file;
|
||
|
|
||
|
if (vpk->numfragments < frag+1)
|
||
|
vpk->numfragments = frag+1;
|
||
|
files++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return files;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=================
|
||
|
COM_LoadPackFile
|
||
|
|
||
|
Takes an explicit (not game tree related) path to a pak file.
|
||
|
|
||
|
Loads the header and directory, adding the files at the beginning
|
||
|
of the list so they override previous pack files.
|
||
|
=================
|
||
|
*/
|
||
|
searchpathfuncs_t *QDECL FSVPK_LoadArchive (vfsfile_t *file, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix)
|
||
|
{
|
||
|
dvpkheader_t header;
|
||
|
int i;
|
||
|
int numpackfiles;
|
||
|
vpk_t *vpk, *f;
|
||
|
vfsfile_t *packhandle;
|
||
|
int read;
|
||
|
qbyte *tree;
|
||
|
unsigned int frag;
|
||
|
|
||
|
packhandle = file;
|
||
|
if (packhandle == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
if (prefix && *prefix)
|
||
|
return NULL; //not supported at this time
|
||
|
|
||
|
read = VFS_READ(packhandle, &header, sizeof(header));
|
||
|
header.magic = LittleLong(header.magic);
|
||
|
header.version = LittleLong(header.version);
|
||
|
header.tablesize = LittleLong(header.tablesize);
|
||
|
|
||
|
header.filedatasize = LittleLong(header.filedatasize);
|
||
|
header.archivemd5size = LittleLong(header.archivemd5size);
|
||
|
header.globalmd5size = LittleLong(header.globalmd5size);
|
||
|
header.signaturesize = LittleLong(header.signaturesize);
|
||
|
|
||
|
if (read < 12 || header.magic != 0x55aa1234 || header.tablesize <= 0)
|
||
|
{ //this will include the non-dir files too.
|
||
|
// Con_Printf("%s is not a vpk\n", desc);
|
||
|
return NULL;
|
||
|
}
|
||
|
i = LittleLong(header.version);
|
||
|
if (i == 2)
|
||
|
;//VFS_SEEK(packhandle, 7*sizeof(int));
|
||
|
// else if (i == 1)
|
||
|
// VFS_SEEK(packhandle, 3*sizeof(int));
|
||
|
else
|
||
|
{
|
||
|
Con_Printf("vpk %s is version %x (unspported)\n", desc, i);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
tree = BZ_Malloc(header.tablesize);
|
||
|
read = VFS_READ(packhandle, tree, header.tablesize);
|
||
|
|
||
|
numpackfiles = FSVPK_WalkTree(NULL, tree, tree+read);
|
||
|
|
||
|
vpk = (vpk_t*)Z_Malloc (sizeof (*vpk) + sizeof(*vpk->files)*(numpackfiles-1));
|
||
|
vpk->treedata = tree;
|
||
|
vpk->treesize = read;
|
||
|
vpk->numfiles = numpackfiles;
|
||
|
vpk->numfragments = 0;
|
||
|
vpk->numfiles = FSVPK_WalkTree(vpk, tree, tree+read);
|
||
|
|
||
|
strcpy (vpk->descname, desc);
|
||
|
vpk->handle = packhandle;
|
||
|
vpk->filepos = 0;
|
||
|
VFS_SEEK(packhandle, vpk->filepos);
|
||
|
|
||
|
vpk->references++;
|
||
|
|
||
|
vpk->mutex = Sys_CreateMutex();
|
||
|
|
||
|
Con_TPrintf ("Added vpkfile %s (%i files)\n", desc, numpackfiles);
|
||
|
|
||
|
vpk->pub.fsver = FSVER;
|
||
|
vpk->pub.GetPathDetails = FSVPK_GetPathDetails;
|
||
|
vpk->pub.ClosePath = FSVPK_ClosePath;
|
||
|
vpk->pub.BuildHash = FSVPK_BuildHash;
|
||
|
vpk->pub.FindFile = FSVPK_FLocate;
|
||
|
vpk->pub.ReadFile = FSVPK_ReadFile;
|
||
|
vpk->pub.EnumerateFiles = FSVPK_EnumerateFiles;
|
||
|
vpk->pub.GeneratePureCRC = FSVPK_GeneratePureCRC;
|
||
|
vpk->pub.OpenVFS = FSVPK_OpenVFS;
|
||
|
|
||
|
vpk->fragments = Z_Malloc(vpk->numfragments*sizeof(*vpk->fragments));
|
||
|
for(frag = 0; frag < vpk->numfragments; frag++)
|
||
|
{
|
||
|
flocation_t loc;
|
||
|
char fragname[MAX_OSPATH], *ext;
|
||
|
Q_strncpyz(fragname, filename, sizeof(fragname));
|
||
|
ext = strrchr(fragname, '.');
|
||
|
if (!ext)
|
||
|
ext = fragname + strlen(fragname);
|
||
|
if (ext-fragname>4 && !strncmp(ext-4, "_dir", 4))
|
||
|
ext-=4;
|
||
|
Q_snprintfz(ext, sizeof(fragname)-(ext-fragname), "_%03u.vpk", frag);
|
||
|
if (parent->FindFile(parent, &loc, fragname, NULL) != FF_FOUND)
|
||
|
continue;
|
||
|
packhandle = parent->OpenVFS(parent, &loc, "rb");
|
||
|
if (!packhandle)
|
||
|
continue;
|
||
|
|
||
|
vpk->fragments[frag] = f = (vpk_t*)Z_Malloc(sizeof(*f));
|
||
|
// Q_strncpyz(f->descname, splitname, sizeof(f->descname));
|
||
|
f->handle = packhandle;
|
||
|
// f->rawsize = VFS_GETLEN(f->raw);
|
||
|
f->references = 1;
|
||
|
f->mutex = Sys_CreateMutex();
|
||
|
f->pub.ClosePath = FSVPK_ClosePath;
|
||
|
}
|
||
|
return &vpk->pub;
|
||
|
}
|
||
|
#endif
|