fteqw/engine/client/sys_npqtv.c

1495 lines
35 KiB
C

#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";
}