1
0
Fork 0
forked from fte/fteqw
fteqw/engine/common/fs_vpk.c
Spoike 5b4756f3d9 Lazy GLSL loading, for faster load times.
Fixed some xim issues, for proper keyboard input under x11.
Cmake project can now work for cross compiling win32 targets.
Some other fun-but-pointless stuff.



git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5344 fc73d0e0-1445-4013-8a0c-d673dee63da5
2018-11-27 16:48:19 +00:00

502 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