fteqw/engine/nacl/fs_ppapi.c

830 lines
21 KiB
C
Raw Normal View History

#include "quakedef.h"
#include "fs.h"
#include <ppapi/c/pp_errors.h>
#include <ppapi/c/ppb_core.h>
#include <ppapi/c/pp_file_info.h>
#include <ppapi/c/ppb_file_system.h>
#include <ppapi/c/ppb_file_ref.h>
#include <ppapi/c/ppb_url_request_info.h>
#include <ppapi/c/ppb_url_response_info.h>
#include <ppapi/c/pp_var.h>
#include <ppapi/c/ppb_var.h>
#include <ppapi/c/ppb_file_io.h>
#include <ppapi/c/ppb_url_loader.h>
extern PPB_FileIO *ppb_fileio;
extern PPB_FileRef *ppb_fileref;
extern PPB_FileSystem *ppb_filesystem;
extern PPB_Core *ppb_core;
extern PPB_URLLoader *urlloader;
extern PPB_URLRequestInfo *urlrequestinfo;
extern PPB_URLResponseInfo *urlresponseinfo;
extern PPB_Var *ppb_var_interface;
extern PP_Instance pp_instance;
#define GOOGLE_DONT_KNOW_HOW_TO_CREATE_USABLE_APIS
#ifdef GOOGLE_DONT_KNOW_HOW_TO_CREATE_USABLE_APIS
//the pepper api is flawed. deeply flawed.
//1: api calls (including various gl calls) may only be made on the main thread).
//2: the main thread may not use non-asyncronous calls.
//the recommendation to get around this is to run everything on a worker thread, but to make calls to the main thread to do the actual call, then signal the worker thread to wake up again in the main thread's callback.
//which is impractical when you have 80+ different sorts of performance-dependant gl calls.
//I can't easily put things requiring file access on another thread, if only because it would make alias/exec console commands non-synchronous
//to get around this, I instead make my own memory-only 'filesystem', populating it at startup with downloads. I *really* hope your browser/server are set to enable file caching.
//at some point I'll periodically write the data back to a persistant store/reload it at startup so saved games/configs can work
#define FSPPAPI_OpenTemp FS_OpenTemp
#define VFSPPAPI_Open VFSOS_Open
#define FSPPAPI_OpenPath VFSOS_OpenPath
typedef struct mfchunk_s
{
struct mfchunk_s *prev;
struct mfchunk_s *next;
unsigned long startpos;
unsigned int len;
char data[64];
} mfchunk_t;
typedef struct mfile_s
{
/*chunks can be trimmed only when there's no refs*/
char name[MAX_QPATH];
int refs;
int unlinked:1;
unsigned long length;
mfchunk_t *chunkhead;
mfchunk_t *chunktail;
struct mfile_s *prev;
struct mfile_s *next;
} mfile_t;
mfile_t *mfiles;
typedef struct
{
vfsfile_t funcs;
mfile_t *file;
unsigned long offset;
mfchunk_t *cchunk;
} vfsmfile_t;
typedef struct
{
searchpathfuncs_t pub;
int depth;
char rootpath[1];
} pppath_t;
qboolean FSPPAPI_Init(int *fileid)
{
return true; /*engine has all the content it needs*/
}
static int preparechunk(vfsmfile_t *f, int bytes, void **data)
{
int sz;
mfchunk_t *cnk;
if (!bytes)
{
*data = 0;
return 0;
}
if (!f->cchunk)
cnk = f->file->chunkhead;
else
{
cnk = f->cchunk;
//rewind through the chunks
while (cnk->startpos > f->offset)
cnk = cnk->prev;
}
//find the chunk that contains our start offset
while (!cnk || cnk->startpos+cnk->len <= f->offset)
{
if (!cnk)
{
sz = (bytes + sizeof(*cnk) - sizeof(cnk->data) + 4095) & ~4095;
if (sz < 65536)
sz = 65536;
cnk = malloc(sz);
memset(cnk, 0xcc, sz);
if (!cnk)
{
*data = 0;
return 0;
}
cnk->len = (sz + sizeof(cnk->data) - sizeof(*cnk));
cnk->next = NULL;
if (f->file->chunktail)
{
cnk->prev = f->file->chunktail;
cnk->prev->next = cnk;
cnk->startpos = cnk->prev->startpos + cnk->prev->len;
}
else
{
f->file->chunkhead = cnk;
cnk->prev = NULL;
cnk->startpos = 0;
}
// Sys_Printf("Allocated chunk %p: %u-%u\n", cnk, cnk->startpos, cnk->startpos + cnk->len);
f->file->chunktail = cnk;
}
else
cnk = cnk->next;
}
// Sys_Printf("Returning offset %p, %i\n", cnk, (f->offset - cnk->startpos));
// Sys_Printf("%u %u\n", f->offset, cnk->startpos);
*data = cnk->data + (f->offset - cnk->startpos);
f->cchunk = cnk;
sz = cnk->startpos + cnk->len - f->offset;
if (sz > bytes)
{
// Sys_Printf("Returning len %u\n", bytes);
return bytes;
}
// Sys_Printf("Returning len %u\n", sz);
return sz;
}
static int VFSMEM_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread)
{
int total = 0;
int chunklen;
void *chunkdat;
vfsmfile_t *f = (vfsmfile_t*)file;
if (bytestoread > f->file->length - f->offset)
bytestoread = f->file->length - f->offset;
while ((chunklen = preparechunk(f, bytestoread, &chunkdat)) > 0)
{
// Sys_Printf("Read %i at %u\n", chunklen, f->offset);
memcpy(buffer, chunkdat, chunklen);
buffer = (char*)buffer + chunklen;
bytestoread -= chunklen;
total += chunklen;
f->offset += chunklen;
}
// Sys_Printf("%s", (char*)buffer-total);
return total;
}
static int VFSMEM_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestoread)
{
int total = 0;
int chunklen;
void *chunkdat;
vfsmfile_t *f = (vfsmfile_t*)file;
while ((chunklen = preparechunk(f, bytestoread, &chunkdat)) > 0)
{
// Sys_Printf("Write %i at %u\n", chunklen, f->offset);
memcpy(chunkdat, buffer, chunklen);
buffer = (char*)buffer + chunklen;
bytestoread -= chunklen;
total += chunklen;
f->offset += chunklen;
}
if (f->file->length < f->offset)
f->file->length = f->offset;
// Sys_Printf("written: %i, file is now at %i\n", total, f->offset);
return total;
}
static qboolean VFSMEM_Seek (struct vfsfile_s *file, qofs_t pos)
{
vfsmfile_t *f = (vfsmfile_t*)file;
f->offset = pos;
return true;
}
static qofs_t VFSMEM_Tell (struct vfsfile_s *file)
{
vfsmfile_t *f = (vfsmfile_t*)file;
return f->offset;
}
static qofs_t VFSMEM_GetSize (struct vfsfile_s *file)
{
vfsmfile_t *f = (vfsmfile_t*)file;
return f->file->length;
}
static void FSPPAPI_DoUnlink(mfile_t *file)
{
mfchunk_t *cnk;
//must have no lingering references.
//free any file chunks.
while (file->chunkhead)
{
cnk = file->chunkhead->next;
free(file->chunkhead);
file->chunkhead = cnk;
}
//unlink the file so nothing else is harmed
if (file->prev)
file->prev->next = file->next;
else if (file == mfiles)
mfiles = file->next;
if (file->next)
file->next->prev = file->prev;
//and finally free the last bit of memory.
free(file);
}
static qboolean VFSMEM_Close(vfsfile_t *file)
{
vfsmfile_t *f = (vfsmfile_t*)file;
f->file->refs -= 1;
if (!f->file->refs)
if (f->file->unlinked)
FSPPAPI_DoUnlink(f->file);
free(f);
return true;
}
static void VFSMEM_Flush(struct vfsfile_s *file)
{
// vfsmfile_t *f = (vfsmfile_t*)file;
}
vfsfile_t *FSPPAPI_OpenTemp(void)
{
/*create a file which is already unlinked*/
mfile_t *f;
vfsmfile_t *r;
f = malloc(sizeof(*f));
if (!f)
return NULL;
strcpy(f->name, "some temp file");
f->refs = 0;
f->unlinked = true;
f->length = 0;
f->next = NULL;
f->prev = NULL;
f->chunkhead = NULL;
f->chunktail = NULL;
r = malloc(sizeof(*r));
if (!r)
return NULL;
r->file = f;
r->offset = 0;
r->cchunk = NULL;
f->refs++;
r->funcs.ReadBytes = VFSMEM_ReadBytes;
r->funcs.WriteBytes = VFSMEM_WriteBytes;
r->funcs.Seek = VFSMEM_Seek;
r->funcs.Tell = VFSMEM_Tell;
r->funcs.GetLen = VFSMEM_GetSize;
r->funcs.Close = VFSMEM_Close;
r->funcs.Flush = VFSMEM_Flush;
return &r->funcs;
}
qboolean Sys_remove (char *path)
{
mfile_t *f;
for (f = mfiles; f; f = f->next)
{
if (!strcmp(f->name, path))
{
if (!f->refs)
FSPPAPI_DoUnlink(f);
else
f->unlinked = true; //can't delete it yet, but we can orphan it so we can kill it later.
return true;
}
}
return false;
}
qboolean Sys_Rename (char *oldfname, char *newfname)
{
mfile_t *f;
for (f = mfiles; f; f = f->next)
{
if (!strcmp(f->name, oldfname))
{
Q_strncpyz(f->name, newfname, sizeof(f->name));
return true;
}
}
return false;
}
//no concept of directories.
void Sys_mkdir (char *path)
{
}
vfsfile_t *VFSPPAPI_Open(const char *osname, const char *mode)
{
mfile_t *f;
vfsmfile_t *r;
if (strlen(osname) >= sizeof(f->name)) //yay strcpy!
return NULL;
for (f = mfiles; f; f = f->next)
{
if (!strcmp(f->name, osname))
break;
}
if (!f && (*mode == 'w' || *mode == 'a'))
{
f = malloc(sizeof(*f));
if (f)
{
strcpy(f->name, osname);
f->refs = 0;
f->unlinked = false;
f->length = 0;
f->next = mfiles;
f->prev = NULL;
if (mfiles)
mfiles->prev = f;
mfiles = f;
f->chunkhead = NULL;
f->chunktail = NULL;
}
}
if (!f)
return NULL;
r = malloc(sizeof(*r));
if (!r)
return NULL;
r->file = f;
r->offset = 0;
r->cchunk = NULL;
f->refs++;
r->funcs.ReadBytes = VFSMEM_ReadBytes;
r->funcs.WriteBytes = VFSMEM_WriteBytes;
r->funcs.Seek = VFSMEM_Seek;
r->funcs.Tell = VFSMEM_Tell;
r->funcs.GetLen = VFSMEM_GetSize;
r->funcs.Close = VFSMEM_Close;
r->funcs.Flush = VFSMEM_Flush;
return &r->funcs;
}
static vfsfile_t *FSPPAPI_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode)
{
pppath_t *sp = (void*)handle;
char diskname[MAX_OSPATH];
//path is already cleaned, as anything that gets a valid loc needs cleaning up first.
snprintf(diskname, sizeof(diskname), "%s/%s", sp->rootpath, loc->rawname);
return VFSPPAPI_Open(diskname, mode);
}
static void FSPPAPI_ClosePath(searchpathfuncs_t *handle)
{
Z_Free(handle);
}
int Sys_EnumerateFiles (const char *rootpath, const char *match, int (*func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath)
{
int rootlen = strlen(rootpath);
char *sub;
mfile_t *f;
if (*match == '/')
match++;
for (f = mfiles; f; f = f->next)
{
sub = f->name;
if (strncmp(sub, rootpath, rootlen))
continue;
sub += rootlen;
if (*sub == '/')
sub++;
if (wildcmp(match, sub))
{
if (!func(sub, f->length, (time_t)0, parm, spath))
return false;
}
}
return true;
}
static int FSPPAPI_EnumerateFiles (searchpathfuncs_t *handle, const char *match, int (*func)(const char *, qofs_t, time_t, void *, searchpathfuncs_t *), void *parm)
{
pppath_t *sp = (void*)handle;
return Sys_EnumerateFiles(sp->rootpath, match, func, parm, handle);
}
static int FSPPAPI_RebuildFSHash(const char *filename, qofs_t filesize, time_t time, void *data, searchpathfuncs_t *handle)
{
pppath_t *sp = (void*)handle;
void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle) = data;
if (filename[strlen(filename)-1] == '/')
{ //this is actually a directory
char childpath[256];
Q_snprintfz(childpath, sizeof(childpath), "%s*", filename);
Sys_EnumerateFiles(sp->rootpath, childpath, FSPPAPI_RebuildFSHash, data, handle);
return true;
}
AddFileHash(0, filename, NULL, handle);
return true;
}
static void FSPPAPI_BuildHash(searchpathfuncs_t *handle, int depth, void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle))
{
pppath_t *sp = (void*)handle;
Sys_EnumerateFiles(sp->rootpath, "*", FSPPAPI_RebuildFSHash, AddFileHash, handle);
}
static qboolean FSPPAPI_FLocate(searchpathfuncs_t *handle, flocation_t *loc, const char *filename, void *hashedresult)
{
pppath_t *sp = (void*)handle;
int len;
char netpath[MAX_OSPATH];
if (hashedresult && (void *)hashedresult != handle)
return false;
/*
if (!static_registered)
{ // if not a registered version, don't ever go beyond base
if ( strchr (filename, '/') || strchr (filename,'\\'))
continue;
}
*/
// check a file in the directory tree
snprintf (netpath, sizeof(netpath)-1, "%s/%s",sp->rootpath, filename);
{
vfsfile_t *f = VFSPPAPI_Open(netpath, "rb");
if (!f)
return false;
len = VFS_GETLEN(f);
VFS_CLOSE(f);
}
if (loc)
{
loc->len = len;
loc->offset = 0;
loc->fhandle = 0;
Q_strncpyz(loc->rawname, filename, sizeof(loc->rawname));
}
return true;
}
static void FSPPAPI_ReadFile(searchpathfuncs_t *handle, flocation_t *loc, char *buffer)
{
vfsfile_t *f;
size_t result;
f = VFSPPAPI_Open(loc->rawname, "rb");
if (!f) //err...
return;
VFS_SEEK(f, loc->offset);
result = VFS_READ(f, buffer, loc->len);
if (result != loc->len)
Con_Printf("FSPPAPI_ReadFile() fread: Filename: %s, expected %i, result was %u\n",loc->rawname,(int)loc->len,(unsigned int)result);
VFS_CLOSE(f);
}
searchpathfuncs_t *QDECL FSPPAPI_OpenPath(vfsfile_t *mustbenull, const char *desc, const char *prefix)
{
pppath_t *np;
int dlen = strlen(desc);
if (mustbenull)
return NULL;
if (prefix && *prefix)
return NULL; //don't try to support this. too risky with absolute paths etc.
np = Z_Malloc(sizeof(*np) + dlen);
if (np)
{
np->depth = 0;
memcpy(np->rootpath, desc, dlen+1);
}
np->pub.fsver = FSVER;
np->pub.ClosePath = FSPPAPI_ClosePath;
np->pub.BuildHash = FSPPAPI_BuildHash;
np->pub.FindFile = FSPPAPI_FLocate;
np->pub.ReadFile = FSPPAPI_ReadFile;
np->pub.EnumerateFiles = FSPPAPI_EnumerateFiles;
np->pub.OpenVFS = FSPPAPI_OpenVFS;
//np->pub.PollChanges = FSPPAPI_PollChanges;
return &np->pub;
}
#else
//this code is old and won't work.
#define FSPPAPI_OpenTemp FS_OpenTemp
#define VFSPPAPI_Open VFSOS_Open
extern PPB_FileIO *ppb_fileio;
extern PPB_FileRef *ppb_fileref;
extern PPB_FileSystem *ppb_filesystem;
extern PPB_Core *ppb_core;
extern PP_Instance pp_instance;
static PP_Resource mainfilesystem;
struct PP_CompletionCallback nullccb;
void FSPPAPI_Init(void)
{
mainfilesystem = ppb_filesystem->Create(pp_instance, PP_FILESYSTEMTYPE_LOCALPERSISTENT);
ppb_filesystem->Open(mainfilesystem, 100*1024*1024, nullccb);
}
typedef struct {
vfsfile_t funcs;
PP_Resource handle;
int64_t offset;
} vfsppapifile_t;
static int VFSPPAPI_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread)
{
int res;
vfsppapifile_t *intfile = (vfsppapifile_t*)file;
res = ppb_fileio->Read(intfile->handle, intfile->offset, buffer, bytestoread, nullccb);
if (res > 0)
intfile->offset += res;
return res;
}
static int VFSPPAPI_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestoread)
{
int res;
vfsppapifile_t *intfile = (vfsppapifile_t*)file;
res = ppb_fileio->Write(intfile->handle, intfile->offset, buffer, bytestoread, nullccb);
if (res > 0)
intfile->offset += res;
return res;
}
static qboolean VFSPPAPI_Seek (struct vfsfile_s *file, qofs_t pos)
{
vfsppapifile_t *intfile = (vfsppapifile_t*)file;
intfile->offset = pos;
return true;
}
static qofs_t VFSPPAPI_Tell (struct vfsfile_s *file)
{
vfsppapifile_t *intfile = (vfsppapifile_t*)file;
return intfile->offset;
}
static void VFSPPAPI_Flush(struct vfsfile_s *file)
{
vfsppapifile_t *intfile = (vfsppapifile_t*)file;
ppb_fileio->Flush(intfile->handle, nullccb);
}
static qofs_t VFSPPAPI_GetSize (struct vfsfile_s *file)
{
vfsppapifile_t *intfile = (vfsppapifile_t*)file;
struct PP_FileInfo fileinfo;
fileinfo.size = 0;
ppb_fileio->Query(intfile->handle, &fileinfo, nullccb);
return fileinfo.size;
}
static void VFSPPAPI_Close(vfsfile_t *file)
{
vfsppapifile_t *intfile = (vfsppapifile_t*)file;
ppb_fileio->Close(intfile->handle);
ppb_core->ReleaseResource(intfile->handle);
Z_Free(file);
}
#ifdef _WIN32
static void VFSPPAPI_CloseTemp(vfsfile_t *file)
{
vfsppapifile_t *intfile = (vfsppapifile_t*)file;
char *fname = (char*)(intfile+1);
ppb_fileio->Close(intfile->handle);
ppb_core->ReleaseResource(intfile->handle);
/*FIXME: add the remove somewhere*/
// _unlink(fname);
Z_Free(file);
}
#endif
vfsfile_t *FSPPAPI_OpenTemp(void)
{
return NULL;
#if 0
FILE *f;
vfsppapifile_t *file;
f = tmpfile();
if (!f)
return NULL;
file = Z_Malloc(sizeof(vfsppapifile_t));
file->funcs.Close = VFSPPAPI_Close;
file->funcs.ReadBytes = VFSPPAPI_ReadBytes;
file->funcs.WriteBytes = VFSPPAPI_WriteBytes;
file->funcs.Seek = VFSPPAPI_Seek;
file->funcs.Tell = VFSPPAPI_Tell;
file->funcs.GetLen = VFSPPAPI_GetSize;
file->funcs.Flush = VFSPPAPI_Flush;
file->handle = f;
return (vfsfile_t*)file;
#endif
}
vfsfile_t *VFSPPAPI_Open(const char *osname, const char *mode)
{
int e;
PP_Resource f;
PP_Resource fsf;
vfsppapifile_t *file;
qboolean read = !!strchr(mode, 'r');
qboolean write = !!strchr(mode, 'w');
qboolean append = !!strchr(mode, 'a');
int newmode = 0;
if (read)
newmode |= PP_FILEOPENFLAG_READ;
if (write)
newmode |= PP_FILEOPENFLAG_WRITE|PP_FILEOPENFLAG_TRUNCATE|PP_FILEOPENFLAG_CREATE;
if (append)
newmode |= PP_FILEOPENFLAG_WRITE|PP_FILEOPENFLAG_CREATE;
/*should we support w+ or r+ */
fsf = ppb_fileref->Create(mainfilesystem, osname);
f = ppb_fileio->Create(pp_instance);
e = ppb_fileio->Open(f, fsf, newmode, nullccb);
ppb_core->ReleaseResource(fsf);
if (e != PP_OK)
{
Con_Printf("unable to open %s. error %i\n", osname, e);
return NULL;
}
file = Z_Malloc(sizeof(vfsppapifile_t));
file->funcs.ReadBytes = strchr(mode, 'r')?VFSPPAPI_ReadBytes:NULL;
file->funcs.WriteBytes = (strchr(mode, 'w')||strchr(mode, 'a'))?VFSPPAPI_WriteBytes:NULL;
file->funcs.Seek = VFSPPAPI_Seek;
file->funcs.Tell = VFSPPAPI_Tell;
file->funcs.GetLen = VFSPPAPI_GetSize;
file->funcs.Close = VFSPPAPI_Close;
file->funcs.Flush = VFSPPAPI_Flush;
file->handle = f;
if (append)
file->offset = VFSPPAPI_GetSize((vfsfile_t*)file);
else
file->offset = 0;
return (vfsfile_t*)file;
}
static vfsfile_t *FSPPAPI_OpenVFS(void *handle, flocation_t *loc, const char *mode)
{
char diskname[MAX_OSPATH];
//path is already cleaned, as anything that gets a valid loc needs cleaning up first.
snprintf(diskname, sizeof(diskname), "%s/%s", (char*)handle, loc->rawname);
return VFSPPAPI_Open(diskname, mode);
}
static void FSPPAPI_PrintPath(searchpathfuncs_t *handle)
{
Con_Printf("%s\n", (char*)handle);
}
static void FSPPAPI_ClosePath(searchpathfuncs_t *handle)
{
Z_Free(handle);
}
static int FSPPAPI_RebuildFSHash(const char *filename, qofs_t filesize, void *data)
{
if (filename[strlen(filename)-1] == '/')
{ //this is actually a directory
char childpath[256];
Q_snprintfz(childpath, sizeof(childpath), "%s*", filename);
Sys_EnumerateFiles((char*)data, childpath, FSPPAPI_RebuildFSHash, data);
return true;
}
if (!Hash_GetInsensative(&filesystemhash, filename))
{
bucket_t *bucket = (bucket_t*)BZ_Malloc(sizeof(bucket_t) + strlen(filename)+1);
strcpy((char *)(bucket+1), filename);
//#ifdef _WIN32
// Q_strlwr((char *)(bucket+1));
//#endif
Hash_AddInsensative(&filesystemhash, (char *)(bucket+1), data, bucket);
fs_hash_files++;
}
else
fs_hash_dups++;
return true;
}
static void FSPPAPI_BuildHash(searchpathfuncs_t *handle)
{
Sys_EnumerateFiles(handle, "*", FSPPAPI_RebuildFSHash, handle);
}
static qboolean FSPPAPI_FLocate(searchpathfuncs_t *handle, flocation_t *loc, const char *filename, void *hashedresult)
{
int len;
char netpath[MAX_OSPATH];
Con_Printf("Locate %s\n", filename);
if (hashedresult && (void *)hashedresult != handle)
return false;
/*
if (!static_registered)
{ // if not a registered version, don't ever go beyond base
if ( strchr (filename, '/') || strchr (filename,'\\'))
continue;
}
*/
// check a file in the directory tree
snprintf (netpath, sizeof(netpath)-1, "%s/%s",(char*)handle, filename);
{
vfsfile_t *f = VFSPPAPI_Open(netpath, "rb");
if (!f)
return false;
len = VFS_GETLEN(f);
VFS_CLOSE(f);
}
if (loc)
{
loc->len = len;
loc->offset = 0;
loc->index = 0;
Q_strncpyz(loc->rawname, filename, sizeof(loc->rawname));
}
return true;
}
static void FSPPAPI_ReadFile(searchpathfuncs_t *handle, flocation_t *loc, char *buffer)
{
vfsfile_t *f;
size_t result;
f = VFSPPAPI_Open(loc->rawname, "rb");
if (!f) //err...
return;
VFS_SEEK(f, loc->offset);
result = VFS_READ(f, buffer, loc->len);
if (result != loc->len)
Con_Printf("FSPPAPI_ReadFile() fread: Filename: %s, expected %i, result was %u\n",loc->rawname,loc->len,(unsigned int)result);
VFS_CLOSE(f);
}
static int FSPPAPI_EnumerateFiles (searchpathfuncs_t *handle, const char *match, int (*func)(const char *, int, void *), void *parm)
{
return Sys_EnumerateFiles(handle, match, func, parm);
}
searchpathfuncs_t *QDECL FSPPAPI_OpenPath(vfsfile_t *mustbenull, const char *desc)
{
stdiopath_t *np;
int dlen = strlen(desc);
if (mustbenull)
return NULL;
np = Z_Malloc(sizeof(*np) + dlen);
if (np)
{
np->depth = 0;
memcpy(np->rootpath, desc, dlen+1);
}
np->pub.fsver = FSVER;
np->pub.ClosePath = FSPPAPI_ClosePath;
np->pub.BuildHash = FSPPAPI_BuildHash;
np->pub.FindFile = FSPPAPI_FLocate;
np->pub.ReadFile = FSPPAPI_ReadFile;
np->pub.EnumerateFiles = FSPPAPI_EnumerateFiles;
np->pub.OpenVFS = FSPPAPI_OpenVFS;
//np->pub.PollChanges = FSPPAPI_PollChanges;
return &np->pub;
}
#endif