diff --git a/engine/client/sys_plugfte.c b/engine/client/sys_plugfte.c new file mode 100644 index 000000000..d683bf357 --- /dev/null +++ b/engine/client/sys_plugfte.c @@ -0,0 +1,1101 @@ +#include "quakedef.h" +#include "winquake.h" +#include "sys_plugfte.h" +#include "../http/iweb.h" + +static void UnpackAndExtractPakFiles_Complete(struct dl_download *dl); +static void pscript_property_splash_sets(struct context *ctx, const char *val); + +typedef struct +{ + vfsfile_t funcs; + + char *data; + int maxlen; + int writepos; + int readpos; +} vfspipe_t; + +void VFSPIPE_Close(vfsfile_t *f) +{ + vfspipe_t *p = (vfspipe_t*)f; + free(p->data); + free(p); +} +unsigned long VFSPIPE_GetLen(vfsfile_t *f) +{ + vfspipe_t *p = (vfspipe_t*)f; + return p->writepos - p->readpos; +} +unsigned long VFSPIPE_Tell(vfsfile_t *f) +{ + return 0; +} +qboolean VFSPIPE_Seek(vfsfile_t *f, unsigned long offset) +{ + Con_Printf("Seeking is a bad plan, mmkay?"); + return false; +} +int VFSPIPE_ReadBytes(vfsfile_t *f, void *buffer, int len) +{ + vfspipe_t *p = (vfspipe_t*)f; + if (len > p->writepos - p->readpos) + len = p->writepos - p->readpos; + memcpy(buffer, p->data+p->readpos, len); + p->readpos += len; + + if (p->readpos > 8192) + { + //shift the memory down periodically + //fixme: use cyclic buffer? max size, etc? + memmove(p->data, p->data+p->readpos, p->writepos-p->readpos); + + p->writepos -= p->readpos; + p->readpos = 0; + } + return len; +} +int VFSPIPE_WriteBytes(vfsfile_t *f, const void *buffer, int len) +{ + vfspipe_t *p = (vfspipe_t*)f; + if (p->writepos + len > p->maxlen) + { + p->maxlen = p->writepos + len; + p->data = realloc(p->data, p->maxlen); + } + memcpy(p->data+p->writepos, buffer, len); + p->writepos += len; + return len; +} + +vfsfile_t *VFSPIPE_Open(void) +{ + vfspipe_t *newf; + newf = malloc(sizeof(*newf)); + newf->data = NULL; + newf->maxlen = 0; + newf->readpos = 0; + newf->writepos = 0; + newf->funcs.Close = VFSPIPE_Close; + newf->funcs.Flush = NULL; + newf->funcs.GetLen = VFSPIPE_GetLen; + newf->funcs.ReadBytes = VFSPIPE_ReadBytes; + newf->funcs.Seek = VFSPIPE_Seek; + newf->funcs.Tell = VFSPIPE_Tell; + newf->funcs.WriteBytes = VFSPIPE_WriteBytes; + newf->funcs.seekingisabadplan = true; + + return &newf->funcs; +} + + + + + + + + + + + + + +struct context +{ + struct contextpublic pub; + + void *windowhnd; + int windowwidth; + int windowheight; + + int waitingfordatafiles; + + char *datadownload; + char *gamename; + char *password; + char *onstart; + char *onend; + char *ondemoend; + + void *nppinstance; + + int read; + int written; + + qtvfile_t qtvf; + + unsigned char *splashdata; + int splashwidth; + int splashheight; + struct dl_download *splashdownload; + struct dl_download *packagelist; + + void *mutex; + void *thread; + char resetvideo; + qboolean shutdown; + + struct context *next; + + struct browserfuncs bfuncs; +}; + +#ifdef _WIN32 + +extern HWND sys_parentwindow; +extern unsigned int sys_parentwidth; +extern unsigned int sys_parentheight; +HINSTANCE global_hInstance; +static char binaryname[MAX_PATH]; + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + global_hInstance = hinstDLL; + GetModuleFileName(global_hInstance, binaryname, sizeof(binaryname)); + break; + default: + break; + } + return TRUE; +} +#endif + +struct context *activecontext; +struct context *contextlist; + +#define ADDRARG(x) do {if (argc < maxargs) argv[argc++] = strdup(x);} while(0) +#define ADDCARG(x) do {if (argc < maxargs) argv[argc++] = strdup(cleanarg(x));} while(0) +char *cleanarg(char *arg) +{ + //no hacking us, please. + while (*arg == '-' || *arg == '+') + arg++; + while (*arg && *(unsigned char*)arg <= ' ') + arg++; + if (*arg) + return arg; + return "badarg"; +} + +int Plug_GenCommandline(struct context *ctx, char **argv, int maxargs) +{ + char *s; + int argc; + char tok[256]; + + argv[0] = strdup(binaryname); + argc = 1; + + switch(ctx->qtvf.connectiontype) + { + default: + break; + case QTVCT_STREAM: + ADDRARG("+qtvplay"); + ADDCARG(ctx->qtvf.server); + break; + case QTVCT_CONNECT: + ADDRARG("+connect"); + ADDCARG(ctx->qtvf.server); + break; + case QTVCT_JOIN: + ADDRARG("+join"); + ADDCARG(ctx->qtvf.server); + break; + case QTVCT_OBSERVE: + ADDRARG("+observe"); + ADDCARG(ctx->qtvf.server); + break; + case QTVCT_MAP: + ADDRARG("+map"); + ADDCARG(ctx->qtvf.server); + break; + } + + if (ctx->password) + { + ADDRARG("+password"); + ADDCARG(ctx->password); + } + + //figure out the game dirs (first token is the base game) + s = ctx->gamename; + s = COM_ParseOut(s, tok, sizeof(tok)); + if (!*tok || !strcmp(tok, "q1") || !strcmp(tok, "qw") || !strcmp(tok, "quake")) + ADDRARG("-quake"); + else if (!strcmp(tok, "q2") || !strcmp(tok, "quake2")) + ADDRARG("-q2"); + else if (!strcmp(tok, "q3") || !strcmp(tok, "quake3")) + ADDRARG("-q3"); + else if (!strcmp(tok, "hl") || !strcmp(tok, "halflife")) + ADDRARG("-halflife"); + else if (!strcmp(tok, "h2") || !strcmp(tok, "hexen2")) + ADDRARG("-hexen2"); + else if (!strcmp(tok, "nex") || !strcmp(tok, "nexuiz")) + ADDRARG("-nexuiz"); + else + { + ADDRARG("-basegame"); + ADDCARG(tok); + } + //later options are additions to that + while ((s = COM_ParseOut(s, tok, sizeof(tok)))) + { + if (argc == sizeof(argv)/sizeof(argv[0])) + break; + ADDRARG("-addbasegame"); + ADDCARG(tok); + } + return argc; +} + +#ifdef _DEBUG +#if _MSC_VER >= 1300 +#define CATCHCRASH +#endif +#endif + +#ifdef CATCHCRASH +#include "dbghelp.h" +DWORD CrashExceptionHandler (DWORD exceptionCode, LPEXCEPTION_POINTERS exceptionInfo); +#endif + +int Plug_PluginThread(void *ctxptr) +{ + char *argv[16]; + int argc = 0; + struct context *ctx = ctxptr; + struct dl_download *dl; + +#ifdef CATCHCRASH + __try +#endif + { + argc = Plug_GenCommandline(ctx, argv, sizeof(argv)/sizeof(argv[0])); + NPQTV_Sys_Startup(argc, argv); + + if (ctx->datadownload) + { + char *s = ctx->datadownload; + char *c; + vfsfile_t *f; + Sys_LockMutex(ctx->mutex); + while ((s = COM_ParseOut(s, com_token, sizeof(com_token)))) + { + //FIXME: do we want to add some sort of file size indicator? + c = strchr(com_token, ':'); + if (!c) + continue; + *c++ = 0; + f = FS_OpenVFS(com_token, "rb", FS_ROOT); + if (f) + { + Con_Printf("Already have %s\n", com_token); + VFS_CLOSE(f); + continue; + } + + Con_Printf("Attempting to download %s\n", c); + VS_DebugLocation(__FILE__, __LINE__, "Queuing Download %s", c); + + dl = DL_Create(c); + dl->user_ctx = ctx; + dl->next = ctx->packagelist; + if (DL_CreateThread(dl, FS_OpenTemp(), UnpackAndExtractPakFiles_Complete)) + ctx->packagelist = dl; + } + Sys_UnlockMutex(ctx->mutex); + } + + while(host_initialized && !ctx->shutdown && ctx->packagelist) + { + Sys_LockMutex(ctx->mutex); + dl = ctx->packagelist; + if (!dl->file) + ctx->packagelist = dl->next; + else + dl = NULL; + Sys_UnlockMutex(ctx->mutex); + + /*file downloads are not canceled while the plugin is locked, to avoid a race condition*/ + if (dl) + DL_Close(dl); + Sleep(10); + } + + if (host_initialized && !ctx->shutdown) + { + Sys_LockMutex(ctx->mutex); + ctx->resetvideo = false; + sys_parentwindow = ctx->windowhnd; + sys_parentwidth = ctx->windowwidth; + sys_parentheight = ctx->windowheight; + VS_DebugLocation(__FILE__, __LINE__, "Host_FinishInit"); + Host_FinishInit(); + Sys_UnlockMutex(ctx->mutex); + } + + VS_DebugLocation(__FILE__, __LINE__, "main loop"); + while(host_initialized) + { + Sys_LockMutex(ctx->mutex); + if (ctx->shutdown) + { + ctx->shutdown = false; + VS_DebugLocation(__FILE__, __LINE__, "Sys_Shutdown"); + NPQTV_Sys_Shutdown(); + } + else if (ctx->resetvideo) + { + ctx->resetvideo = false; + sys_parentwindow = ctx->windowhnd; + sys_parentwidth = ctx->windowwidth; + sys_parentheight = ctx->windowheight; +// R_RestartRenderer_f(); + } + else + NPQTV_Sys_MainLoop(); + Sys_UnlockMutex(ctx->mutex); + } + } +#ifdef CATCHCRASH + __except (CrashExceptionHandler(GetExceptionCode(), GetExceptionInformation())) + { + + } +#endif + + Sys_LockMutex(ctx->mutex); + while (ctx->packagelist) + { + dl = ctx->packagelist; + ctx->packagelist = dl->next; + + /*don't close while locked*/ + Sys_UnlockMutex(ctx->mutex); + DL_Close(dl); + Sys_LockMutex(ctx->mutex); + } + ctx->pub.running = false; + Sys_UnlockMutex(ctx->mutex); + InvalidateRgn(ctx->windowhnd, NULL, FALSE); + + while(argc-- > 0) + free(argv[argc]); + + activecontext = NULL; + return 0; +} + +void Plug_LockPlugin(struct context *ctx, qboolean lockstate) +{ + if (!ctx || !ctx->mutex) + return; + + if (lockstate) + Sys_LockMutex(ctx->mutex); + else + Sys_UnlockMutex(ctx->mutex); +} +//#define Plug_LockPlugin(c,s) do{Plug_LockPlugin(c,s);VS_DebugLocation(__FILE__, __LINE__, s?"Lock":"Unlock"); }while(0) + +//begins the context, fails if one is already active +qboolean Plug_StartContext(struct context *ctx) +{ + if (activecontext) + return false; + ctx->pub.running = true; + activecontext = ctx; + ctx->mutex = Sys_CreateMutex(); + ctx->thread = Sys_CreateThread(Plug_PluginThread, ctx, 0); + + return true; +} + +//asks a context to stop, is not instant. +void Plug_StopContext(struct context *ctx) +{ + if (ctx == NULL) + ctx = activecontext; + if (!ctx) + return; + if (ctx->pub.running == true) + ctx->shutdown = true; +} + +//creates a plugin context +struct context *Plug_CreateContext(void *sysctx, const struct browserfuncs *funcs) +{ + struct context *ctx; + + if (!sysctx || !funcs) + return NULL; + +// MessageBox(0, "hello", "this one", 0); + + ctx = malloc(sizeof(struct context)); + if (!ctx) + return NULL; + memset(ctx, 0, sizeof(struct context)); + memcpy(&ctx->bfuncs, funcs, sizeof(ctx->bfuncs)); + + //link the instance to the context and the context to the instance + ctx->nppinstance = sysctx; + + ctx->gamename = strdup("q1"); + + //add it to the linked list + ctx->next = contextlist; + contextlist = ctx; + + ctx->qtvf.connectiontype = QTVCT_NONE; + + return ctx; +} + +//change the plugin's parent window, width, and height, returns true if the window handle actually changed, false otherwise +qboolean Plug_ChangeWindow(struct context *ctx, void *whnd, int width, int height) +{ + qboolean result = false; + + //if the window changed + if (ctx->windowhnd != whnd) + { + result = true; + +#ifdef _WIN32 + if (ctx->pub.running) + { + Plug_LockPlugin(ctx, true); + if (mainwindow && ctx->windowhnd == sys_parentwindow) + { + sys_parentwindow = ctx->windowhnd; + SetParent(mainwindow, ctx->windowhnd); + } + Plug_LockPlugin(ctx, false); + } +#endif + ctx->windowhnd = whnd; + } + if (ctx->windowwidth != width && ctx->windowheight != height) + { + ctx->windowwidth = width; + ctx->windowheight = height; + + Plug_LockPlugin(ctx, true); + if (ctx->pub.running) + ctx->resetvideo = true; + Plug_LockPlugin(ctx, false); + } + + return result; +} + +void Plug_DestroyContext(struct context *ctx) +{ + struct context *prev; + if (ctx == contextlist) + contextlist = ctx->next; + else + { + for (prev = contextlist; prev->next; prev = prev->next) + { + if (prev->next == ctx) + { + prev->next = ctx->next; + break; + } + } + } + + if (ctx->splashdownload) + { + DL_Close(ctx->splashdownload); + ctx->splashdownload = NULL; + } + + ctx->shutdown = true; + if (ctx->thread) + Sys_WaitOnThread(ctx->thread); + ctx->thread = NULL; + + //actually these ifs are not required, just the frees + if (ctx->gamename) + free(ctx->gamename); + if (ctx->password) + free(ctx->password); + if (ctx->datadownload) + free(ctx->datadownload); + if (ctx->splashdata) + free(ctx->splashdata); + + free(ctx); +} + + +//////////////////////////////////////// + +#include "fs.h" +extern searchpathfuncs_t zipfilefuncs; + +static int ExtractDataFile(const char *fname, int fsize, void *ptr) +{ + char buffer[8192]; + int read; + void *zip = ptr; + flocation_t loc; + int slashes; + const char *s; + vfsfile_t *compressedpak; + vfsfile_t *decompressedpak; + + if (zipfilefuncs.FindFile(zip, &loc, fname, NULL)) + { + compressedpak = zipfilefuncs.OpenVFS(zip, &loc, "rb"); + if (compressedpak) + { + //this extra logic is so we can handle things like nexuiz/data/blah.pk3 + //as well as just data/blah.pk3 + slashes = 0; + for (s = strchr(fname, '/'); s; s = strchr(s+1, '/')) + slashes++; + for (; slashes > 1; slashes--) + fname = strchr(fname, '/')+1; + + if (!slashes) + { + FS_CreatePath(fname, FS_GAMEONLY); + decompressedpak = FS_OpenVFS(fname, "wb", FS_GAMEONLY); + } + else + { + FS_CreatePath(fname, FS_ROOT); + decompressedpak = FS_OpenVFS(fname, "wb", FS_ROOT); + } + if (decompressedpak) + { + for(;;) + { + read = VFS_READ(compressedpak, buffer, sizeof(buffer)); + if (read <= 0) + break; + VFS_WRITE(decompressedpak, buffer, read); + } + VFS_CLOSE(decompressedpak); + } + VFS_CLOSE(compressedpak); + } + } + return true; +} + +static void UnpackAndExtractPakFiles_Complete(struct dl_download *dl) +{ + extern searchpathfuncs_t zipfilefuncs; + void *zip; + + Plug_LockPlugin(dl->user_ctx, true); + + zip = zipfilefuncs.OpenNew(dl->file, dl->url); + if (zip) + { + /*the zip code will eat the file handle*/ + dl->file = NULL; + + /*scan it to extract its contents*/ + zipfilefuncs.EnumerateFiles(zip, "*.pk3", ExtractDataFile, zip); + zipfilefuncs.EnumerateFiles(zip, "*.pak", ExtractDataFile, zip); + + /*close it, delete the temp file from disk, etc*/ + zipfilefuncs.ClosePath(zip); + + /*restart the filesystem so those new files can be found*/ + Cmd_ExecuteString("fs_restart", RESTRICT_LOCAL); + } + + Plug_LockPlugin(dl->user_ctx, false); +} + +void LoadSplashImage(struct dl_download *dl) +{ + struct context *ctx = dl->user_ctx; + vfsfile_t *f = dl->file; + int x, y; + int width = 0; + int height = 0; + int len; + char *buffer; + unsigned char *image; + + ctx->splashwidth = 0; + ctx->splashheight = 0; + image = ctx->splashdata; + ctx->splashdata = NULL; + free(image); + + if (!f) + return; + + len = VFS_GETLEN(f); + buffer = malloc(len); + VFS_READ(f, buffer, len); + VFS_CLOSE(f); + dl->file = NULL; + + image = NULL; + if (!image) + image = ReadJPEGFile(buffer, len, &width, &height); + if (!image) + image = ReadPNGFile(buffer, len, &width, &height, dl->url); + + free(buffer); + if (image) + { + if (ctx->splashdata) + free(ctx->splashdata); + ctx->splashdata = malloc(width*height*4); + for (y = 0; y < height; y++) + { + for (x = 0; x < width; x++) + { + ctx->splashdata[(y*width + x)*4+0] = image[((height-y-1)*width + x)*4+2]; + ctx->splashdata[(y*width + x)*4+1] = image[((height-y-1)*width + x)*4+1]; + ctx->splashdata[(y*width + x)*4+2] = image[((height-y-1)*width + x)*4+0]; + } + } + ctx->splashwidth = width; + ctx->splashheight = height; + BZ_Free(image); + + if (ctx->windowhnd) + InvalidateRgn(ctx->windowhnd, NULL, FALSE); + } +} + +static void ReadQTVFileDescriptor(struct context *ctx, vfsfile_t *f, const char *name) +{ + CL_ParseQTVFile(f, name, &ctx->qtvf); + + pscript_property_splash_sets(ctx, ctx->qtvf.splashscreen); +} + +void CL_QTVPlay (vfsfile_t *newf, qboolean iseztv); +static void BeginDemo(struct context *ctx, vfsfile_t *f, const char *name) +{ + if (!activecontext) + activecontext = ctx; + + CL_QTVPlay(f, false); +} +static void EndDemo(struct context *ctx, vfsfile_t *f, const char *name) +{ + Cmd_ExecuteString("disconnect", RESTRICT_LOCAL); +} + +///////////////////////////////////// + + + + + + +struct pscript_property +{ + char *name; + qboolean onlyifactive; + + cvar_t *cvar; + + char *(*getstring)(struct context *ctx); + void (*setstring)(struct context *ctx, const char *val); + + int (*getint)(struct context *ctx); + void (*setint)(struct context *ctx, int val); + + float (*getfloat)(struct context *ctx); + void (*setfloat)(struct context *ctx, float val); +}; + +int pscript_property_running_getb(struct context *ctx) +{ + if (ctx->pub.running) + return true; + else + return false; +} + +void pscript_property_running_setb(struct context *ctx, int i) +{ + i = !!i; + if (ctx->pub.running == i) + return; + if (i) + Plug_StartContext(ctx); + else + Plug_StopContext(ctx); +} + +char *pscript_property_startserver_gets(struct context *ctx) +{ + return ctx->qtvf.server; +} +void pscript_property_startserver_sets(struct context *ctx, const char *val) +{ + ctx->qtvf.connectiontype = QTVCT_CONNECT; + Q_strncpyz(ctx->qtvf.server, val, sizeof(ctx->qtvf.server)); +} +char *pscript_property_curserver_gets(struct context *ctx) +{ + if (!pscript_property_running_getb(ctx)) + return pscript_property_startserver_gets(ctx); + + return cls.servername; +} +void pscript_property_curserver_sets(struct context *ctx, const char *val) +{ + if (!pscript_property_running_getb(ctx)) + { + pscript_property_startserver_sets(ctx, val); + return; + } + + Q_strncpyz(cls.servername, val, sizeof(cls.servername)); + CL_BeginServerConnect(); +} + +void pscript_property_stream_sets(struct context *ctx, const char *val) +{ + ctx->qtvf.connectiontype = QTVCT_STREAM; + Q_strncpyz(ctx->qtvf.server, val, sizeof(ctx->qtvf.server)); + + if (pscript_property_running_getb(ctx)) + Cmd_ExecuteString(va("qtvplay \"%s\"\n", val), RESTRICT_INSECURE); +} + +void pscript_property_availver_setf(struct context *ctx, float val) +{ + ctx->pub.availver = val; + if (ctx->pub.availver <= build_number()) + ctx->pub.availver = 0; +} + +void pscript_property_datadownload_sets(struct context *ctx, const char *val) +{ + free(ctx->datadownload); + ctx->datadownload = strdup(val); +} + +void pscript_property_game_sets(struct context *ctx, const char *val) +{ + if (!strstr(val, ".")) + if (!strstr(val, "/")) + if (!strstr(val, "\\")) + if (!strstr(val, ":")) + { + free(ctx->gamename); + ctx->gamename = strdup(val); + } +} + +void pscript_property_splash_sets(struct context *ctx, const char *val) +{ + if (ctx->splashdownload) + DL_Close(ctx->splashdownload); + ctx->splashdownload = NULL; + + if (val != ctx->qtvf.splashscreen) + Q_strncpyz(ctx->qtvf.splashscreen, val, sizeof(ctx->qtvf.splashscreen)); + + ctx->splashdownload = DL_Create(ctx->qtvf.splashscreen); + ctx->splashdownload->user_ctx = ctx; + if (!DL_CreateThread(ctx->splashdownload, VFSPIPE_Open(), LoadSplashImage)) + { + DL_Close(ctx->splashdownload); + ctx->splashdownload = NULL; + } + +/* + if (ctx->browserfuncs.RequestDownload) + ctx->browserfuncs.RequestDownload(ctx->nppinstance, &SplashscreenImageDescriptor, ctx->qtvf.splashscreen); + else + HTTP_CL_Get(); +*/ +} + +extern cvar_t skin, team, topcolor, bottomcolor, vid_fullscreen; +static struct pscript_property pscript_properties[] = +{ + {"running", false, NULL, NULL, NULL, pscript_property_running_getb, pscript_property_running_setb}, + {"startserver", false, NULL, pscript_property_startserver_gets, pscript_property_startserver_sets}, + {"server", false, NULL, pscript_property_curserver_gets, pscript_property_curserver_sets}, + {"playername", true, &name}, + {NULL, true, &skin}, + {NULL, true, &team}, + {NULL, true, &topcolor}, + {NULL, true, &bottomcolor}, + {NULL, true, &password}, +// {NULL, true, &spectator}, + {"fullscreen", true, &vid_fullscreen}, + + {"datadownload",false, NULL, NULL, pscript_property_datadownload_sets}, + + {"game", false, NULL, NULL, pscript_property_game_sets}, + {"availver", false, NULL, NULL, NULL, NULL, NULL, NULL, pscript_property_availver_setf}, + + {"splash", false, NULL, NULL, pscript_property_splash_sets}, + + {"stream", false, NULL, NULL, pscript_property_stream_sets}, + +/* + else if (!stricmp(argn[i], "connType")) + { + if (ctx->qtvf.connectiontype) + continue; + if (!stricmp(argn[i], "join")) + ctx->qtvf.connectiontype = QTVCT_JOIN; + else if (!stricmp(argn[i], "qtv")) + ctx->qtvf.connectiontype = QTVCT_STREAM; + else if (!stricmp(argn[i], "connect")) + ctx->qtvf.connectiontype = QTVCT_CONNECT; + else if (!stricmp(argn[i], "map")) + ctx->qtvf.connectiontype = QTVCT_MAP; + else if (!stricmp(argn[i], "join")) + ctx->qtvf.connectiontype = QTVCT_JOIN; + else if (!stricmp(argn[i], "observe")) + ctx->qtvf.connectiontype = QTVCT_OBSERVE; + else + ctx->qtvf.connectiontype = QTVCT_NONE; + } + else if (!stricmp(argn[i], "map")) + { + if (ctx->qtvf.connectiontype) + continue; + ctx->qtvf.connectiontype = QTVCT_MAP; + Q_strncpyz(ctx->qtvf.server, argv[i], sizeof(ctx->qtvf.server)); + } + else if (!stricmp(argn[i], "join")) + { + if (ctx->qtvf.connectiontype) + continue; + ctx->qtvf.connectiontype = QTVCT_JOIN; + Q_strncpyz(ctx->qtvf.server, argv[i], sizeof(ctx->qtvf.server)); + } + else if (!stricmp(argn[i], "observe")) + { + if (ctx->qtvf.connectiontype) + continue; + ctx->qtvf.connectiontype = QTVCT_OBSERVE; + Q_strncpyz(ctx->qtvf.server, argv[i], sizeof(ctx->qtvf.server)); + } + else if (!stricmp(argn[i], "password")) + { + ctx->password = strdup(argv[i]); + } + else if (!stricmp(argn[i], "onStart")) + { + ctx->onstart = strdup(argv[i]); + } + else if (!stricmp(argn[i], "onEnd")) + { + ctx->onend = strdup(argv[i]); + } + else if (!stricmp(argn[i], "onDemoEnd")) + { + ctx->ondemoend = strdup(argv[i]); + } + else if (!stricmp(argn[i], "availVer")) + { + ctx->availver = atof(argv[i]); + if (ctx->availver <= NPQTV_VERSION) + ctx->availver = 0; + } +*/ + {NULL} +}; + +struct pscript_property *Plug_FindProp(struct context *ctx, const char *field) +{ + struct pscript_property *prop; + for (prop = pscript_properties; prop->name||prop->cvar; prop++) + { + if (!stricmp(prop->name?prop->name:prop->cvar->name, field)) + { + if (prop->onlyifactive) + { + if (!ctx->pub.running) + return NULL; + } + return prop; + } + } + return NULL; +} + +qboolean Plug_SetString(struct context *ctx, struct pscript_property *field, const char *value) +{ + if (!ctx || !field || !value) + return false; + if (field->setstring) + { + Plug_LockPlugin(ctx, true); + field->setstring(ctx, value); + Plug_LockPlugin(ctx, false); + } + else if (field->setint) + { + Plug_LockPlugin(ctx, true); + field->setint(ctx, atoi(value)); + Plug_LockPlugin(ctx, false); + } + else if (field->setfloat) + { + Plug_LockPlugin(ctx, true); + field->setfloat(ctx, atof(value)); + Plug_LockPlugin(ctx, false); + } + else if (field->cvar && ctx->pub.running) + { + Plug_LockPlugin(ctx, true); + Cvar_Set(field->cvar, value); + Plug_LockPlugin(ctx, false); + } + else + return false; + return true; +} +qboolean Plug_SetInteger(struct context *ctx, struct pscript_property *field, int value) +{ + if (!ctx || !field) + return false; + if (field->setint) + { + Plug_LockPlugin(ctx, true); + field->setint(ctx, value); + Plug_LockPlugin(ctx, false); + } + else if (field->setfloat) + { + Plug_LockPlugin(ctx, true); + field->setfloat(ctx, value); + Plug_LockPlugin(ctx, false); + } + else if (field->cvar && ctx->pub.running) + { + Plug_LockPlugin(ctx, true); + Cvar_SetValue(field->cvar, value); + Plug_LockPlugin(ctx, false); + } + else + return false; + return true; +} +qboolean Plug_SetFloat(struct context *ctx, struct pscript_property *field, float value) +{ + if (!ctx || !field) + return false; + if (field->setfloat) + { + Plug_LockPlugin(ctx, true); + field->setfloat(ctx, value); + Plug_LockPlugin(ctx, false); + } + else if (field->setint) + { + Plug_LockPlugin(ctx, true); + field->setint(ctx, value); + Plug_LockPlugin(ctx, false); + } + else if (field->cvar && ctx->pub.running) + { + Plug_LockPlugin(ctx, true); + Cvar_SetValue(field->cvar, value); + Plug_LockPlugin(ctx, false); + } + else + return false; + return true; +} + +#pragma message("Plug_Get* not implemented yet") +qboolean Plug_GetString(struct context *ctx, struct pscript_property *field, const char **value) +{ + return false; +} +void Plug_GotString(const char *value) +{ + free((char*)value); +} +qboolean Plug_GetInteger(struct context *ctx, struct pscript_property *field, int *value) +{ + return false; +} +qboolean Plug_GetFloat(struct context *ctx, struct pscript_property *field, float *value) +{ + return false; +} + +#ifdef _WIN32 +void *Plug_GetSplashBack(struct context *ctx, void *hdc, int *width, int *height) +{ + BITMAPINFOHEADER bmh; + + if (!ctx->splashdata) + return NULL; + + bmh.biSize = sizeof(bmh); + bmh.biWidth = *width = ctx->splashwidth; + bmh.biHeight = *height = ctx->splashheight; + bmh.biPlanes = 1; + bmh.biBitCount = 32; + bmh.biCompression = BI_RGB; + bmh.biSizeImage = 0; + bmh.biXPelsPerMeter = 0; + bmh.biYPelsPerMeter = 0; + bmh.biClrUsed = 0; + bmh.biClrImportant = 0; + + return CreateDIBitmap(hdc, + &bmh, + CBM_INIT, + (LPSTR)ctx->splashdata, + (LPBITMAPINFO)&bmh, + DIB_RGB_COLORS ); +} +void Plug_ReleaseSplashBack(struct context *ctx, void *bmp) +{ + DeleteObject(bmp); +} +#endif + +static const struct plugfuncs exportedplugfuncs_1 = +{ + Plug_CreateContext, + Plug_DestroyContext, + Plug_LockPlugin, + Plug_StartContext, + Plug_StopContext, + Plug_ChangeWindow, + + Plug_FindProp, + Plug_SetString, + Plug_GetString, + Plug_GotString, + Plug_SetInteger, + Plug_GetInteger, + Plug_SetFloat, + Plug_GetFloat, + + Plug_GetSplashBack, + Plug_ReleaseSplashBack +}; + +const struct plugfuncs *Plug_GetFuncs(int ver) +{ + if (ver == 1) + return &exportedplugfuncs_1; + else + return NULL; +} diff --git a/engine/client/sys_plugfte.h b/engine/client/sys_plugfte.h new file mode 100644 index 000000000..2eafa44cd --- /dev/null +++ b/engine/client/sys_plugfte.h @@ -0,0 +1,103 @@ +#ifndef __QUAKEDEF_H__ +typedef enum qboolean; +typedef void *vfsfile_t; +#endif + + +struct pipetype +{ + int dead; +}; + +struct browserfuncs +{ + qboolean (*RequestDownload)(void *ctx, struct pipetype *ftype, char *url); +}; + +/*the conext structure contains this at the start*/ +struct contextpublic +{ + qboolean running; /*set if the plugin context is actually active*/ + float availver; /*this is the version of the plugin that is available, if current is better, use 0*/ + +#if defined(_WIN32) && defined(__QUAKEDEF_H__) + /*the npapi stuff is lazy and uses this*/ + void *oldwnd; /*not used in the plugin itself*/ + void *oldproc; /*not used in the plugin itself*/ +#endif +}; + +#include +static void VS_DebugLocation(char *fname, int line, char *fmt, ...) +{ + char buffer[640]; + va_list va; + + sprintf(buffer, "%s(%i) : ", fname, line); + OutputDebugStringA(buffer); + + va_start(va, fmt); + vsprintf(buffer, fmt, va); + va_end(va); + OutputDebugStringA(buffer); + OutputDebugStringA("\n"); +} + + + + + + + + + +struct context; +struct pscript_property; + +struct context *Plug_CreateContext(void *sysctx, const struct browserfuncs *funcs); +void Plug_DestroyContext(struct context *ctx); +void Plug_LockPlugin(struct context *ctx, qboolean lockstate); +qboolean Plug_StartContext(struct context *ctx); +void Plug_StopContext(struct context *ctx); +qboolean Plug_ChangeWindow(struct context *ctx, void *whnd, int width, int height); + +struct pscript_property *Plug_FindProp(struct context *ctx, const char *field); +qboolean Plug_SetString(struct context *ctx, struct pscript_property *field, const char *value); +qboolean Plug_GetString(struct context *ctx, struct pscript_property *field, const char **value); +void Plug_GotString(const char *value); +qboolean Plug_SetInteger(struct context *ctx, struct pscript_property *field, int value); +qboolean Plug_GetInteger(struct context *ctx, struct pscript_property *field, int *value); +qboolean Plug_SetFloat(struct context *ctx, struct pscript_property *field, float value); +qboolean Plug_GetFloat(struct context *ctx, struct pscript_property *field, float *value); + +#ifdef _WIN32 +void *Plug_GetSplashBack(struct context *ctx, void *hdc, int *width, int *height);/*returns an HBITMAP*/ +void Plug_ReleaseSplashBack(struct context *ctx, void *bmp); +#endif + +struct plugfuncs +{ + struct context *(*CreateContext)(void *sysctx, const struct browserfuncs *funcs); + void (*DestroyContext)(struct context *ctx); + void (*LockPlugin)(struct context *ctx, qboolean lockstate); + qboolean (*StartContext)(struct context *ctx); + void (*StopContext)(struct context *ctx); + qboolean (*ChangeWindow)(struct context *ctx, void *whnd, int width, int height); + + struct pscript_property *(*FindProp)(struct context *ctx, const char *field); + qboolean (*SetString)(struct context *ctx, struct pscript_property *field, const char *value); + qboolean (*GetString)(struct context *ctx, struct pscript_property *field, const char **value); + void (*GotString)(const char *value); + qboolean (*SetInteger)(struct context *ctx, struct pscript_property *field, int value); + qboolean (*GetInteger)(struct context *ctx, struct pscript_property *field, int *value); + qboolean (*SetFloat)(struct context *ctx, struct pscript_property *field, float value); + qboolean (*GetFloat)(struct context *ctx, struct pscript_property *field, float *value); + +#ifdef _WIN32 + void *(*GetSplashBack)(struct context *ctx, void *hdc, int *width, int *height);/*returns an HBITMAP*/ + void (*ReleaseSplashBack)(struct context *ctx, void *bmp); +#endif +}; + +#define PLUG_APIVER 1 +const struct plugfuncs *Plug_GetFuncs(int ver);