mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-10 22:51:57 +00:00
858 lines
21 KiB
C
858 lines
21 KiB
C
|
#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
|
||
|
|
||
|
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
|
||
|
{
|
||
|
PP_Resource dl;
|
||
|
char buffer[65536];
|
||
|
char *fname;
|
||
|
vfsfile_t *out;
|
||
|
} dlfile_t;
|
||
|
|
||
|
static int activedls;
|
||
|
static int availdlslots = 2;
|
||
|
void readfinished(void* user_data, int32_t result)
|
||
|
{
|
||
|
dlfile_t *f = user_data;
|
||
|
/*if there was a prior request that didn't finish yet...*/
|
||
|
struct PP_CompletionCallback ccb = {readfinished, f, PP_COMPLETIONCALLBACK_FLAG_OPTIONAL};
|
||
|
|
||
|
// Sys_Printf("lastresult: %i\n", result);
|
||
|
|
||
|
if (result == PP_OK)
|
||
|
{
|
||
|
Sys_Printf("%s completed\n", f->fname);
|
||
|
if (f->out)
|
||
|
VFS_CLOSE(f->out);
|
||
|
//ppb_core->ReleaseResource(f->dl);
|
||
|
activedls--;
|
||
|
availdlslots++;
|
||
|
free(f);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for (; result > 0; result = urlloader->ReadResponseBody(f->dl, f->buffer, sizeof(f->buffer), ccb))
|
||
|
{
|
||
|
if (!f->out)
|
||
|
{
|
||
|
Sys_Printf("Downloading %s\n", f->fname);
|
||
|
f->out = VFSOS_Open(f->fname, "wb");
|
||
|
}
|
||
|
|
||
|
// Sys_Printf("write: %i\n", result);
|
||
|
VFS_WRITE(f->out, f->buffer, result);
|
||
|
}
|
||
|
|
||
|
// Sys_Printf("result: %i\n", result);
|
||
|
if (result != PP_OK_COMPLETIONPENDING)
|
||
|
{
|
||
|
Sys_Printf("file %s failed or something\n", f->fname);
|
||
|
if (f->out)
|
||
|
VFS_CLOSE(f->out);
|
||
|
ppb_core->ReleaseResource(f->dl);
|
||
|
activedls--;
|
||
|
availdlslots++;
|
||
|
free(f);
|
||
|
}
|
||
|
}
|
||
|
void dlstarted(void* user_data, int32_t result)
|
||
|
{
|
||
|
dlfile_t *f = user_data;
|
||
|
|
||
|
struct PP_CompletionCallback ccb = {readfinished, f, PP_COMPLETIONCALLBACK_FLAG_OPTIONAL};
|
||
|
readfinished(user_data, urlloader->ReadResponseBody(f->dl, f->buffer, sizeof(f->buffer), ccb));
|
||
|
}
|
||
|
qboolean FSPPAPI_Init(int *fileid)
|
||
|
{
|
||
|
dlfile_t *dlf;
|
||
|
PP_Resource dlri;
|
||
|
static char *dlnames[] =
|
||
|
{
|
||
|
"id1/pak0.pak",
|
||
|
// "id1/gfx/conback.tga",
|
||
|
// "id1/gfx/conchars.tga",
|
||
|
"id1/pak1.pak",
|
||
|
// "id1/bigass1.dem",
|
||
|
// "id1/overkill.qwd",
|
||
|
"id1/pak2.pak",
|
||
|
"id1/pak3.pak",
|
||
|
"id1/pak4.pak",
|
||
|
NULL
|
||
|
};
|
||
|
if (availdlslots)
|
||
|
{
|
||
|
if (!dlnames[*fileid])
|
||
|
{
|
||
|
if (!activedls)
|
||
|
return true; /*engine has all the content it needs*/
|
||
|
return false; /*still downloading something, don't let it continue just yet*/
|
||
|
}
|
||
|
|
||
|
dlf = malloc(sizeof(*dlf));
|
||
|
if (!dlf)
|
||
|
return false;
|
||
|
dlf->out = NULL;
|
||
|
|
||
|
activedls++;
|
||
|
availdlslots--;
|
||
|
dlf->fname = dlnames[*fileid];
|
||
|
*fileid+=1;
|
||
|
dlf->dl = urlloader->Create(pp_instance);
|
||
|
dlri = urlrequestinfo->Create(pp_instance);
|
||
|
urlrequestinfo->SetProperty(dlri, PP_URLREQUESTPROPERTY_URL, ppb_var_interface->VarFromUtf8(dlf->fname, strlen(dlf->fname)));
|
||
|
|
||
|
struct PP_CompletionCallback ccb = {dlstarted, dlf, PP_COMPLETIONCALLBACK_FLAG_NONE};
|
||
|
urlloader->Open(dlf->dl, dlri, ccb);
|
||
|
ppb_core->ReleaseResource(dlri);
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
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, unsigned long pos)
|
||
|
{
|
||
|
vfsmfile_t *f = (vfsmfile_t*)file;
|
||
|
f->offset = pos;
|
||
|
return true;
|
||
|
}
|
||
|
static unsigned long VFSMEM_Tell (struct vfsfile_s *file)
|
||
|
{
|
||
|
vfsmfile_t *f = (vfsmfile_t*)file;
|
||
|
return f->offset;
|
||
|
}
|
||
|
static unsigned long VFSMEM_GetSize (struct vfsfile_s *file)
|
||
|
{
|
||
|
vfsmfile_t *f = (vfsmfile_t*)file;
|
||
|
return f->file->length;
|
||
|
}
|
||
|
static void VFSMEM_Close(vfsfile_t *file)
|
||
|
{
|
||
|
vfsmfile_t *f = (vfsmfile_t*)file;
|
||
|
f->file->refs -= 1;
|
||
|
if (!f->file->refs)
|
||
|
{
|
||
|
if (f->file->unlinked)
|
||
|
{
|
||
|
mfchunk_t *cnk;
|
||
|
while (f->file->chunkhead)
|
||
|
{
|
||
|
cnk = f->file->chunkhead->next;
|
||
|
free(f->file->chunkhead);
|
||
|
f->file->chunkhead = cnk;
|
||
|
}
|
||
|
free(f->file);
|
||
|
}
|
||
|
}
|
||
|
free(f);
|
||
|
}
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
vfsfile_t *VFSPPAPI_Open(const char *osname, const char *mode)
|
||
|
{
|
||
|
mfile_t *f;
|
||
|
vfsmfile_t *r;
|
||
|
|
||
|
if (strlen(osname) >= sizeof(f->name))
|
||
|
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(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(void *handle)
|
||
|
{
|
||
|
Con_Printf("%s\n", (char*)handle);
|
||
|
}
|
||
|
static void FSPPAPI_ClosePath(void *handle)
|
||
|
{
|
||
|
Z_Free(handle);
|
||
|
}
|
||
|
static int FSPPAPI_RebuildFSHash(const char *filename, int 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(void *handle)
|
||
|
{
|
||
|
Sys_EnumerateFiles(handle, "*", FSPPAPI_RebuildFSHash, handle);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
void debugfs(void)
|
||
|
{
|
||
|
static qboolean firstrun = true;
|
||
|
int len;
|
||
|
FILE *f;
|
||
|
vfsfile_t *v;
|
||
|
char *buf;
|
||
|
if (!firstrun)
|
||
|
return;
|
||
|
firstrun = false;
|
||
|
f = fopen("C:/Games/Quake/fte_trunk/pub/id1/pak0.pak", "rb");
|
||
|
fseek(f, 0, SEEK_END);
|
||
|
len = ftell(f);
|
||
|
fseek(f, 0, SEEK_SET);
|
||
|
buf = malloc(len);
|
||
|
fread(buf, 1, len, f);
|
||
|
fclose(f);
|
||
|
v = VFSPPAPI_Open("C:\\Games\\Quake/id1/pak0.pak", "wb");
|
||
|
VFS_WRITE(v, buf, len);
|
||
|
free(buf);
|
||
|
VFS_CLOSE(v);
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
static qboolean FSPPAPI_FLocate(void *handle, flocation_t *loc, const char *filename, void *hashedresult)
|
||
|
{
|
||
|
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",(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(void *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 (void *handle, const char *match, int (*func)(const char *, int, void *), void *parm)
|
||
|
{
|
||
|
return Sys_EnumerateFiles(handle, match, func, parm);
|
||
|
}
|
||
|
|
||
|
searchpathfuncs_t osfilefuncs = {
|
||
|
FSPPAPI_PrintPath,
|
||
|
FSPPAPI_ClosePath,
|
||
|
FSPPAPI_BuildHash,
|
||
|
FSPPAPI_FLocate,
|
||
|
FSPPAPI_ReadFile,
|
||
|
FSPPAPI_EnumerateFiles,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
FSPPAPI_OpenVFS
|
||
|
};
|
||
|
|
||
|
#else
|
||
|
#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, unsigned long pos)
|
||
|
{
|
||
|
vfsppapifile_t *intfile = (vfsppapifile_t*)file;
|
||
|
intfile->offset = pos;
|
||
|
return true;
|
||
|
}
|
||
|
static unsigned long 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 unsigned long 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(void *handle)
|
||
|
{
|
||
|
Con_Printf("%s\n", (char*)handle);
|
||
|
}
|
||
|
static void FSPPAPI_ClosePath(void *handle)
|
||
|
{
|
||
|
Z_Free(handle);
|
||
|
}
|
||
|
static int FSPPAPI_RebuildFSHash(const char *filename, int 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(void *handle)
|
||
|
{
|
||
|
Sys_EnumerateFiles(handle, "*", FSPPAPI_RebuildFSHash, handle);
|
||
|
}
|
||
|
static qboolean FSPPAPI_FLocate(void *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(void *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 (void *handle, const char *match, int (*func)(const char *, int, void *), void *parm)
|
||
|
{
|
||
|
return Sys_EnumerateFiles(handle, match, func, parm);
|
||
|
}
|
||
|
|
||
|
searchpathfuncs_t osfilefuncs = {
|
||
|
FSPPAPI_PrintPath,
|
||
|
FSPPAPI_ClosePath,
|
||
|
FSPPAPI_BuildHash,
|
||
|
FSPPAPI_FLocate,
|
||
|
FSPPAPI_ReadFile,
|
||
|
FSPPAPI_EnumerateFiles,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
FSPPAPI_OpenVFS
|
||
|
};
|
||
|
#endif
|