#include "quakedef.h" #include "winquake.h" #define bool int //we ain't c++ (grr microsoft stdbool.h gief!) #ifdef _WIN32 #ifndef _WINDOWS #define _WINDOWS //stupid GCC #endif #endif #include "npapi/npupp.h" #define Q_STRINGZ_TO_NPVARIANT(_val, _v) \ NP_BEGIN_MACRO \ NPString str = { _val, strlen(_val) }; \ (_v).type = NPVariantType_String; \ (_v).value.stringValue = str; \ NP_END_MACRO #undef STRINGZ_TO_NPVARIANT #define STRINGZ_TO_NPVARIANT Q_STRINGZ_TO_NPVARIANT #define NPQTV_VERSION 0.1 #define FIREFOX_BUGS_OVER_25MB //TODO: player name input (before allowing them to join) //TODO: fix active gl context (per thread, and we hijacked the browser's thread) NPNetscapeFuncs *browserfuncs; #ifdef _WIN32 #ifndef GetWindowLongPtr #define GetWindowLongPtr GetWindowLong #endif #ifndef SetWindowLongPtr #define SetWindowLongPtr SetWindowLong #define LONG_PTR LONG #endif extern HWND sys_parentwindow; extern unsigned int sys_parentwidth; extern unsigned int sys_parentheight; HINSTANCE global_hInstance; BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: global_hInstance = hinstDLL; break; default: break; } return TRUE; } #endif 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; } char binaryname[MAX_PATH]; struct qstream { vfsfile_t *pipe; struct pipetype *type; struct qstream *next; char url[1]; }; struct context { NPWindow window; qboolean contextrunning; int waitingfordatafiles; float availver; #ifdef _WIN32 WNDPROC oldproc; #endif char *datadownload; char *gamename; char *password; char *onstart; char *onend; char *ondemoend; NPP nppinstance; struct qstream *donestreams; int wait_size; int wait_offset; struct qstream *wait_stream; qtvfile_t qtvf; unsigned char *splashdata; int splashwidth; int splashheight; struct context *next; }; struct context *activecontext; struct context *contextlist; //////////////////////////////////////// struct pipetype { enum { WAIT_NO, WAIT_YES, WAIT_DONE } wait; qboolean needseeking; void (*completionfunc) (struct context *ctx, vfsfile_t *file, const char *streamsource); void (*beginfunc) (struct context *ctx, vfsfile_t *file, const char *streamsource); }; #include "fs.h" extern searchpathfuncs_t zipfilefuncs; 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; } void UnpackAndExtractPakFiles_Complete(struct context *ctx, vfsfile_t *file, const char *streamsource) { extern searchpathfuncs_t zipfilefuncs; void *zip; zip = zipfilefuncs.OpenNew(file, streamsource); if (zip) { zipfilefuncs.EnumerateFiles(zip, "*.pk3", ExtractDataFile, zip); zipfilefuncs.EnumerateFiles(zip, "*.pak", ExtractDataFile, zip); zipfilefuncs.ClosePath(zip); Cmd_ExecuteString("fs_restart", RESTRICT_LOCAL); } } struct pipetype UnpackAndExtractPakFiles = { WAIT_YES, true, UnpackAndExtractPakFiles_Complete }; void LoadSplashImage(struct context *ctx, vfsfile_t *f, const char *name) { int x, y; int width = 0; int height = 0; int len = VFS_GETLEN(f); char *buffer = malloc(len); unsigned char *image; VFS_READ(f, buffer, len); VFS_CLOSE(f); image = NULL; if (!image) image = ReadJPEGFile(buffer, len, &width, &height); if (!image) image = ReadPNGFile(buffer, len, &width, &height, name); 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->window.window) InvalidateRgn(ctx->window.window, NULL, FALSE); } } struct pipetype SplashscreenImageDescriptor = { WAIT_DONE, false, LoadSplashImage }; static void ReadQTVFileDescriptor(struct context *ctx, vfsfile_t *f, const char *name) { CL_ParseQTVFile(f, name, &ctx->qtvf); if (*ctx->qtvf.splashscreen) { browserfuncs->geturlnotify(ctx->nppinstance, ctx->qtvf.splashscreen, NULL, &SplashscreenImageDescriptor); } } struct pipetype QTVFileDescriptor = { WAIT_DONE, false, ReadQTVFileDescriptor }; 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 pipetype DemoFileDescriptor = { WAIT_NO, false, EndDemo, BeginDemo }; ///////////////////////////////////// #ifdef _WIN32 void DrawWndBack(struct context *ctx, HWND hWnd, HDC hdc, PAINTSTRUCT *p) { if (ctx->splashdata) { HBITMAP bmp; BITMAPINFOHEADER bmh; HDC memDC; bmh.biSize = sizeof(bmh); bmh.biWidth = ctx->splashwidth; bmh.biHeight = 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; memDC = CreateCompatibleDC(hdc); bmp = CreateDIBitmap(hdc, &bmh, CBM_INIT, (LPSTR)ctx->splashdata, (LPBITMAPINFO)&bmh, DIB_RGB_COLORS ); SelectObject(memDC, bmp); // StretchBlt(hdc, 0, 0, p->rcPaint.right-p->rcPaint.left, p->rcPaint.bottom-p->rcPaint.top, memDC, 0, 0, ctx->splashwidth, ctx->splashheight, SRCCOPY); StretchBlt(hdc, 0, 0, ctx->window.width, ctx->window.height, memDC, 0, 0, ctx->splashwidth, ctx->splashheight, SRCCOPY); SelectObject(memDC, NULL); DeleteDC(memDC); DeleteObject(bmp); } else PatBlt(hdc, p->rcPaint.left, p->rcPaint.top, p->rcPaint.right-p->rcPaint.left,p->rcPaint.bottom-p->rcPaint.top,PATCOPY); } char *cleanarg(char *arg) { //no hacking us, please. while (*arg == '-' || *arg == '+') arg++; while (*arg && *arg <= ' ') arg++; if (*arg) return arg; return "badarg"; } LRESULT CALLBACK MyPluginWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { struct qstream *str; struct context *ctx; ctx = (struct context *)GetWindowLongPtr(hWnd, GWL_USERDATA); if (!ctx) return DefWindowProc(hWnd, msg, wParam, lParam); switch(msg) { case WM_MOVE: if (ctx->contextrunning) { PostMessage(mainwindow, WM_MOVE, 0, 0); } break; case WM_TIMER: if (ctx->contextrunning && !ctx->waitingfordatafiles) { while (ctx->donestreams) { str = ctx->donestreams; ctx->donestreams = str->next; if (str->pipe) { if (str->type->completionfunc) str->type->completionfunc(ctx, str->pipe, str->url); else VFS_CLOSE(str->pipe); } free(str); } if (sys_parentwindow != ctx->window.window) { if (qrenderer == -1) { //urgh, its not started up yet sys_parentwindow = ctx->window.window; Host_FinishInit(); if (ctx->onstart) browserfuncs->geturl(ctx->nppinstance, va("javascript:%s;", ctx->onstart), "_self"); } else { sys_parentwindow = ctx->window.window; if (sys_parentwindow) { sys_parentwidth = ctx->window.width; sys_parentheight = ctx->window.height; Cmd_ExecuteString("vid_restart", RESTRICT_LOCAL); } } } else if (sys_parentwindow) { NPQTV_Sys_MainLoop(); if (!host_initialized) { //quit was issued ctx->contextrunning = false; activecontext = NULL; InvalidateRgn(hWnd, NULL, FALSE); if (ctx->onend) browserfuncs->geturl(ctx->nppinstance, va("javascript:%s;", ctx->onend), "_self"); } } } return TRUE; case WM_PAINT: if (activecontext == ctx && !ctx->contextrunning && ctx->window.window) { char *s; int argc; char *argv[16]; sys_parentwindow = NULL; GetModuleFileName(global_hInstance, binaryname, sizeof(binaryname)); argv[0] = binaryname; argc = 1; activecontext = ctx; switch(ctx->qtvf.connectiontype) { default: break; case QTVCT_STREAM: argv[argc++] = "+qtvplay"; argv[argc++] = cleanarg(ctx->qtvf.server); break; case QTVCT_CONNECT: argv[argc++] = "+connect"; argv[argc++] = cleanarg(ctx->qtvf.server); break; case QTVCT_JOIN: argv[argc++] = "+join"; argv[argc++] = cleanarg(ctx->qtvf.server); break; case QTVCT_OBSERVE: argv[argc++] = "+observe"; argv[argc++] = cleanarg(ctx->qtvf.server); break; case QTVCT_MAP: argv[argc++] = "+map"; argv[argc++] = cleanarg(ctx->qtvf.server); break; } if (ctx->password) { argv[argc++] = "+password"; argv[argc++] = cleanarg(ctx->password); } //figure out the game dirs (first token is the base game) s = ctx->gamename; s = COM_ParseOut(s, com_token, sizeof(com_token)); if (!*com_token || !strcmp(com_token, "q1") || !strcmp(com_token, "qw") || !strcmp(com_token, "quake")) argv[argc++] = "-quake"; else if (!strcmp(com_token, "q2") || !strcmp(com_token, "quake2")) argv[argc++] = "-q2"; else if (!strcmp(com_token, "q3") || !strcmp(com_token, "quake3")) argv[argc++] = "-q3"; else if (!strcmp(com_token, "hl") || !strcmp(com_token, "halflife")) argv[argc++] = "-halflife"; else if (!strcmp(com_token, "h2") || !strcmp(com_token, "hexen2")) argv[argc++] = "-hexen2"; else if (!strcmp(com_token, "nex") || !strcmp(com_token, "nexuiz")) argv[argc++] = "-nexuiz"; else { argv[argc++] = "-basegame"; argv[argc++] = strdup(cleanarg(com_token)); //FIXME: this will leak } //later options are additions to that while ((s = COM_ParseOut(s, com_token, sizeof(com_token)))) { if (argc == sizeof(argv)/sizeof(argv[0])) break; argv[argc++] = "-addbasegame"; argv[argc++] = strdup(cleanarg(com_token)); //FIXME: this will leak } sys_parentwidth = ctx->window.width; sys_parentheight = ctx->window.height; ctx->contextrunning = NPQTV_Sys_Startup(argc, argv); //now that the file system is started up, check to make sure its complete if (ctx->datadownload) { char *s = ctx->datadownload; char *c; vfsfile_t *f; 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); if (!browserfuncs->geturlnotify(ctx->nppinstance, c, NULL, &UnpackAndExtractPakFiles)) ctx->waitingfordatafiles++; } } if (ctx->contextrunning) { //windows timers have low precision, ~10ms //they're low priority anyway, so we might as well just create lots and spam them SetTimer(hWnd, 1, 1, NULL); SetTimer(hWnd, 2, 1, NULL); SetTimer(hWnd, 3, 1, NULL); SetTimer(hWnd, 4, 1, NULL); SetTimer(hWnd, 5, 1, NULL); } } if (ctx->waitingfordatafiles) { HDC hdc; PAINTSTRUCT paint; char *s; hdc = BeginPaint(hWnd, &paint); DrawWndBack(ctx, hWnd, hdc, &paint); SetBkMode(hdc, TRANSPARENT); TextOutA(hdc, 0, 0, "Downloading Data, please wait", 16); if (!ctx->wait_stream) s = "connecting"; else if (ctx->wait_size > 0) s = va("%i bytes (%i%%)", ctx->wait_offset, (int)((100.0f*ctx->wait_offset)/ctx->wait_size)); else s = va("%i bytes", ctx->wait_offset); TextOutA(hdc, 0, 32, s, strlen(s)); EndPaint(hWnd, &paint); return TRUE; } else { HDC hdc; PAINTSTRUCT paint; char *s; hdc = BeginPaint(hWnd, &paint); DrawWndBack(ctx, hWnd, hdc, &paint); SetBkMode(hdc, TRANSPARENT); if (!ctx->contextrunning) { if (!activecontext) { s = "Click to activate"; TextOutA(hdc, 0, 0, s, strlen(s)); } if (ctx->availver) { s = va("Your plugin is out of date"); TextOutA(hdc, 0, 16, s, strlen(s)); s = va("Version %3.1f is available", ctx->availver); TextOutA(hdc, 0, 32, s, strlen(s)); } } EndPaint(hWnd, &paint); return TRUE; } break; case WM_LBUTTONDOWN: if (!activecontext) { activecontext = ctx; InvalidateRgn(hWnd, NULL, FALSE); } else if (activecontext != ctx) Cbuf_AddText("quit\n", RESTRICT_LOCAL); break; default: break; } //I would call the previous wndproc... but that crashes firefox return DefWindowProc(hWnd, msg, wParam, lParam); } #endif NPError NP_LOADDS NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved) { int i; struct context *ctx; if (!instance || instance->pdata) { return NPERR_INVALID_INSTANCE_ERROR; } if (mode != NP_EMBED && mode != NP_FULL) { return NPERR_INVALID_PLUGIN_ERROR; } ctx = malloc(sizeof(struct context)); if (!ctx) { return NPERR_OUT_OF_MEMORY_ERROR; } memset(ctx, 0, sizeof(struct context)); //link the instance to the context and the context to the instance instance->pdata = ctx; ctx->nppinstance = instance; ctx->gamename = strdup("q1"); //parse out the properties for (i = 0; i < argc; i++) { if (!stricmp(argn[i], "dataDownload")) { ctx->datadownload = strdup(argv[i]); } else if (!stricmp(argn[i], "game")) { if (!strstr(argn[i], ".")) if (!strstr(argn[i], "/")) if (!strstr(argn[i], "\\")) if (!strstr(argn[i], ":")) { free(ctx->gamename); ctx->gamename = strdup(argv[i]); } } 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], "server") || !stricmp(argn[i], "stream")) { if (*ctx->qtvf.server) continue; Q_strncpyz(ctx->qtvf.server, argv[i], sizeof(ctx->qtvf.server)); } 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], "stream")) { if (ctx->qtvf.connectiontype) continue; ctx->qtvf.connectiontype = QTVCT_STREAM; 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], "splash")) { Q_strncpyz(ctx->qtvf.splashscreen, argv[i], sizeof(ctx->qtvf.splashscreen)); browserfuncs->geturlnotify(ctx->nppinstance, ctx->qtvf.splashscreen, NULL, &SplashscreenImageDescriptor); } 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; } else if (!stricmp(argn[i], "begin")) { if (atoi(argv[i]) && !activecontext) activecontext = ctx; } } if (!*ctx->qtvf.server) ctx->qtvf.connectiontype = QTVCT_NONE; else if (ctx->qtvf.connectiontype == QTVCT_NONE) ctx->qtvf.connectiontype = QTVCT_STREAM; //add it to the linked list ctx->next = contextlist; contextlist = ctx; return NPERR_NO_ERROR; } NPError NP_LOADDS NPP_Destroy(NPP instance, NPSavedData** save) { struct context *ctx = instance->pdata; struct context *prev; if (!ctx) return NPERR_INVALID_INSTANCE_ERROR; #ifdef _WIN32 if (ctx->window.window) { if (ctx->oldproc) SetWindowLongPtr(ctx->window.window, GWL_WNDPROC, (LONG_PTR)ctx->oldproc); SetWindowLongPtr(ctx->window.window, GWL_USERDATA, (LONG_PTR)NULL); } #endif //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); 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->contextrunning) { NPQTV_Sys_Shutdown(); } if (ctx == activecontext) { activecontext = NULL; sys_parentwindow = NULL; } free(ctx); instance->pdata = NULL; return NPERR_NO_ERROR; } NPError NP_LOADDS NPP_SetWindow(NPP instance, NPWindow* window) { extern cvar_t vid_width; struct context *ctx = instance->pdata; #ifdef _WIN32 HWND oldwindow; WNDPROC p; if (!ctx) return NPERR_INVALID_INSTANCE_ERROR; oldwindow = ctx->window.window; memcpy(&ctx->window, window, sizeof(ctx->window)); //if the window changed if (ctx->window.window != oldwindow) { //we switched window? if (oldwindow && ctx->oldproc) { SetWindowLongPtr(oldwindow, GWL_WNDPROC, (LONG_PTR)ctx->oldproc); ctx->oldproc = NULL; } p = (WNDPROC)GetWindowLongPtr(ctx->window.window, GWL_WNDPROC); if (p != MyPluginWndProc) ctx->oldproc = p; SetWindowLongPtr(ctx->window.window, GWL_WNDPROC, (LONG_PTR)MyPluginWndProc); SetWindowLongPtr(ctx->window.window, GWL_USERDATA, (LONG_PTR)ctx); if (ctx->contextrunning && mainwindow && oldwindow == sys_parentwindow) { sys_parentwindow = ctx->window.window; SetParent(mainwindow, ctx->window.window); oldwindow = sys_parentwindow; } } if (ctx->contextrunning) { extern cvar_t vid_conwidth; sys_parentwidth = ctx->window.width; sys_parentheight = ctx->window.height; Cvar_ForceCallback(&vid_width); Cvar_ForceCallback(&vid_conwidth); } InvalidateRgn(ctx->window.window, NULL, FALSE); #endif return NPERR_NO_ERROR; } NPError NP_LOADDS NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype) { // struct context *ctx = instance->pdata; struct qstream *qstr; stream->pdata = qstr = malloc(sizeof(*qstr) + strlen(stream->url)); memset(qstr, 0, sizeof(*qstr)); strcpy(qstr->url, stream->url); if (!stream->notifyData) { //choose source type based on mime type if (!strncmp(type, "text/x-quaketvident", 5)) stream->notifyData = &QTVFileDescriptor; else if (!strcmp(type, "application/x-multiviewdemo")) stream->notifyData = &DemoFileDescriptor; //well that failed, try choosing based on extension else if (!strcmp(COM_FileExtension(stream->url), "qtv")) stream->notifyData = &QTVFileDescriptor; else return NPERR_INVALID_PARAM; } qstr->type = stream->notifyData; if (qstr->type->needseeking) { *stype = NP_ASFILEONLY; //everything is a download #ifdef FIREFOX_BUGS_OVER_25MB *stype = NP_NORMAL; qstr->pipe = FS_OpenTemp(); #endif } else { *stype = NP_NORMAL; qstr->pipe = VFSPIPE_Open(); } return NPERR_NO_ERROR; } NPError NP_LOADDS NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason) { struct context *ctx = instance->pdata; struct qstream *qstr = stream->pdata; if (!qstr) //urm, got canceled before it finished downloading? return NPERR_NO_ERROR; if (ctx->wait_stream == qstr) ctx->wait_stream = NULL; if (qstr->type->wait == WAIT_YES) { ctx->waitingfordatafiles--; } if (qstr->type->wait == WAIT_DONE) qstr->type->completionfunc(ctx, qstr->pipe, qstr->url); else { qstr->next = ctx->donestreams; ctx->donestreams = qstr; } return NPERR_NO_ERROR; } int32 NP_LOADDS NPP_WriteReady(NPP instance, NPStream* stream) { struct qstream *qstr = stream->pdata; vfsfile_t *pipe = qstr?qstr->pipe:NULL; if (pipe && pipe->seekingisabadplan) return 1024*1024 - VFS_GETLEN(pipe); else return 8192; } int32 NP_LOADDS NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer) { int bytes = NPP_WriteReady(instance, stream); struct context *ctx = instance->pdata; struct qstream *qstr = stream->pdata; if (qstr && qstr->type && qstr->type->wait) { if (!ctx->wait_stream) ctx->wait_stream = qstr; if (ctx->wait_stream == qstr) { ctx->wait_offset = offset; ctx->wait_size = stream->end; InvalidateRgn(ctx->window.window, NULL, FALSE); } } if (!qstr || !qstr->pipe) return bytes; //we're not meant to read more bytes than we said we could read. if (len > bytes) len = bytes; return VFS_WRITE(qstr->pipe, buffer, len); } void NP_LOADDS NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname) { struct qstream *qstr = stream->pdata; if (!qstr) return; if (qstr->pipe) VFS_CLOSE(qstr->pipe); qstr->pipe = VFSOS_Open(fname, "rb"); } void NP_LOADDS NPP_Print(NPP instance, NPPrint* platformPrint) { //we don't support printing. //paper and ink doesn't give a good frame rate. return; } int16 NP_LOADDS NPP_HandleEvent(NPP instance, void* event) { // MessageBox(NULL, "NPP_HandleEvent", "npapi", 0); return NPERR_NO_ERROR; } void NP_LOADDS NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData) { } struct npscript_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); }; int npscript_property_isrunning_getb(struct context *ctx) { if (ctx->contextrunning) return true; else return false; } char *npscript_property_startserver_gets(struct context *ctx) { return ctx->qtvf.server; } void npscript_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 *npscript_property_curserver_gets(struct context *ctx) { if (!npscript_property_isrunning_getb(ctx)) return npscript_property_startserver_gets(ctx); return cls.servername; } void npscript_property_curserver_sets(struct context *ctx, const char *val) { if (!npscript_property_isrunning_getb(ctx)) { npscript_property_startserver_sets(ctx, val); return; } Q_strncpyz(cls.servername, val, sizeof(cls.servername)); CL_BeginServerConnect(); } extern cvar_t skin, team, topcolor, bottomcolor, vid_fullscreen; struct npscript_property npscript_properties[] = { {"isrunning", false, NULL, NULL, NULL, npscript_property_isrunning_getb}, {"startserver", false, NULL, npscript_property_startserver_gets, npscript_property_startserver_sets}, {"server", false, NULL, npscript_property_curserver_gets, npscript_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}, {NULL} }; struct npscript { NPObject obj; struct context *ctx; struct npscript_property *props; }; NPObject *npscript_allocate(NPP npp, NPClass *aClass) { struct npscript_property *prop; struct npscript *obj; obj = malloc(sizeof(*obj)); obj->obj._class = aClass; obj->obj.referenceCount = 1; obj->ctx = npp->pdata; obj->props = npscript_properties; for (prop = obj->props; prop->name||prop->cvar; prop++) { if(!prop->name) prop->name = prop->cvar->name; } return (NPObject*)obj; } void npscript_deallocate(NPObject *npobj) { free(npobj); } void npscript_invalidate(NPObject *npobj) { struct npscript *obj = (struct npscript *)npobj; obj->ctx = NULL; } bool npscript_hasMethod(NPObject *npobj, NPIdentifier name) { NPUTF8 *mname; mname = browserfuncs->utf8fromidentifier(name); return false; } bool npscript_invoke(NPObject *npobj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result) { return false; } bool npscript_invokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result) { return false; } bool npscript_hasProperty(NPObject *npobj, NPIdentifier name) { struct npscript *obj = (struct npscript *)npobj; struct npscript_property *prop; NPUTF8 *pname; pname = browserfuncs->utf8fromidentifier(name); for (prop = obj->props; prop->name; prop++) { if (!strcmp(prop->name, pname)) return true; } return false; } bool npscript_getProperty(NPObject *npobj, NPIdentifier name, NPVariant *result) { struct npscript *obj = (struct npscript *)npobj; struct context *ctx = obj->ctx; NPUTF8 *pname; char *res, *ns; int len; struct npscript_property *prop; pname = browserfuncs->utf8fromidentifier(name); for (prop = obj->props; prop->name; prop++) { if (!strcmp(prop->name, pname)) { if (prop->onlyifactive) { if (!ctx->contextrunning) return false; } if (prop->getstring) { //FIXME: Are we meant to malloc a new string buffer here? res = prop->getstring(ctx); len = strlen(res); ns = browserfuncs->memalloc(len); if (!ns) return false; memcpy(ns, res, len); STRINGZ_TO_NPVARIANT(ns, *result); return true; } else if (prop->getint) { INT32_TO_NPVARIANT(prop->getint(ctx), *result); return true; } else if (prop->cvar) { //FIXME: Are we meant to malloc a new string buffer here? res = prop->cvar->string; len = strlen(res); ns = browserfuncs->memalloc(len); if (!ns) return false; memcpy(ns, res, len); STRINGZ_TO_NPVARIANT(ns, *result); return true; } return false; } } return false; } bool npscript_setProperty(NPObject *npobj, NPIdentifier name, const NPVariant *value) { struct npscript *obj = (struct npscript *)npobj; struct context *ctx = obj->ctx; NPUTF8 *pname; NPString str; struct npscript_property *prop; pname = browserfuncs->utf8fromidentifier(name); for (prop = obj->props; prop->name; prop++) { if (!strcmp(prop->name, pname)) { if (prop->onlyifactive) { if (!ctx->contextrunning) return false; } if (NPVARIANT_IS_STRING(*value)) { char *t = NULL; str = NPVARIANT_TO_STRING(*value); if (str.utf8characters[str.utf8length] != 0) { t = malloc(str.utf8length+1); memcpy(t, str.utf8characters, str.utf8length); t[str.utf8length] = 0; str.utf8characters = t; } if (prop->setstring) { prop->setstring(ctx, str.utf8characters); if (t) free(t); return true; } if (prop->setint) { prop->setint(ctx, atoi(str.utf8characters)); if (t) free(t); return true; } if (t) free(t); } if (NPVARIANT_IS_INT32(*value)) { if (prop->setint) { prop->setint(ctx, NPVARIANT_TO_INT32(*value)); return true; } } if (NPVARIANT_IS_DOUBLE(*value)) { if (prop->setint) { prop->setint(ctx, NPVARIANT_TO_DOUBLE(*value)); return true; } } if (prop->cvar) { if (NPVARIANT_IS_STRING(*value)) { str = NPVARIANT_TO_STRING(*value); Cvar_Set(prop->cvar, str.utf8characters); return true; } if (NPVARIANT_IS_INT32(*value)) { Cvar_SetValue(prop->cvar, NPVARIANT_TO_INT32(*value)); return true; } if (NPVARIANT_IS_DOUBLE(*value)) { Cvar_SetValue(prop->cvar, NPVARIANT_TO_DOUBLE(*value)); return true; } if (NPVARIANT_IS_BOOLEAN(*value)) { Cvar_SetValue(prop->cvar, NPVARIANT_TO_BOOLEAN(*value)); return true; } } //sorry, no can do return false; } } //not known return false; } bool npscript_removeProperty(NPObject *npobj, NPIdentifier name) { return false; } bool npscript_enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count) { return false; } bool npscript_construct(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result) { return false; } NPClass npscript_class = { NP_CLASS_STRUCT_VERSION, npscript_allocate, npscript_deallocate, npscript_invalidate, npscript_hasMethod, npscript_invoke, npscript_invokeDefault, npscript_hasProperty, npscript_getProperty, npscript_setProperty, npscript_removeProperty, npscript_enumerate, npscript_construct }; NPError NP_LOADDS NPP_GetValue(NPP instance, NPPVariable variable, void *value) { switch(variable) { case NPPVpluginScriptableNPObject: *(void**)value = browserfuncs->createobject(instance, &npscript_class); return NPERR_NO_ERROR; default: return NPERR_INVALID_PARAM; } return NPERR_NO_ERROR; } NPError NP_LOADDS NPP_SetValue(NPP instance, NPNVariable variable, void *value) { switch(variable) { default: return NPERR_INVALID_PARAM; } return NPERR_NO_ERROR; } NPError OSCALL NP_Initialize(NPNetscapeFuncs* pFuncs) { browserfuncs = pFuncs; return NPERR_NO_ERROR; } NPError OSCALL NP_Shutdown(void) { if (contextlist) { //the browser isn't meant to call this when there's still instances left... return NPERR_GENERIC_ERROR; } return NPERR_NO_ERROR; } NPError OSCALL NP_GetValue(void *instance, NPPVariable variable, void *value) { if (value == NULL) return NPERR_INVALID_PARAM; switch(variable) { case NPPVpluginNameString: *(char**)value = "QTV Viewer"; break; case NPPVpluginDescriptionString: *(char**)value = "QTV Viewer"; break; default: return NPERR_INVALID_PARAM; } return NPERR_NO_ERROR; } NPError OSCALL NP_GetEntryPoints (NPPluginFuncs* pFuncs) { if (pFuncs->size < sizeof(NPPluginFuncs)) return NPERR_INVALID_FUNCTABLE_ERROR; pFuncs->size = sizeof(NPPluginFuncs); pFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR; pFuncs->newp = NPP_New; pFuncs->destroy = NPP_Destroy; pFuncs->setwindow = NPP_SetWindow; pFuncs->newstream = NPP_NewStream; pFuncs->destroystream = NPP_DestroyStream; pFuncs->asfile = NPP_StreamAsFile; pFuncs->writeready = NPP_WriteReady; pFuncs->write = NPP_Write; pFuncs->print = NPP_Print; pFuncs->event = NPP_HandleEvent; pFuncs->urlnotify = NPP_URLNotify; pFuncs->javaClass = NULL; pFuncs->getvalue = NPP_GetValue; pFuncs->setvalue = NPP_SetValue; return NPERR_NO_ERROR; } char *NP_GetMIMEDescription(void) { return "test/x-qtv:qtv:QTV Stream Description"; }