6ebabd8d88
'make npqtv-dbg' works for me. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@3152 fc73d0e0-1445-4013-8a0c-d673dee63da5
1026 lines
24 KiB
C
1026 lines
24 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 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_hijackwindow;
|
|
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[MAX_PATH];
|
|
char gamename[MAX_QPATH];
|
|
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);
|
|
|
|
//this code is to stop them from constantly downloading if that zip didn't contain one for some reason
|
|
if (!FS_FLocateFile("default.cfg", FSLFRT_IFFOUND, NULL))
|
|
{
|
|
FS_WriteFile("default.cfg", "", 0, FS_GAMEONLY);
|
|
}
|
|
}
|
|
}
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
|
|
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_hijackwindow != ctx->window.window)
|
|
{
|
|
if (!sys_hijackwindow)
|
|
{
|
|
switch(ctx->qtvf.connectiontype)
|
|
{
|
|
default:
|
|
break;
|
|
case QTVCT_STREAM:
|
|
Cmd_ExecuteString(va("qtvplay %s", ctx->qtvf.server), RESTRICT_LOCAL);
|
|
break;
|
|
case QTVCT_CONNECT:
|
|
Cmd_ExecuteString(va("connect %s", ctx->qtvf.server), RESTRICT_LOCAL);
|
|
break;
|
|
case QTVCT_JOIN:
|
|
Cmd_ExecuteString(va("join %s", ctx->qtvf.server), RESTRICT_LOCAL);
|
|
break;
|
|
case QTVCT_OBSERVE:
|
|
Cmd_ExecuteString(va("observe %s", ctx->qtvf.server), RESTRICT_LOCAL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
sys_hijackwindow = ctx->window.window;
|
|
if (sys_hijackwindow)
|
|
{
|
|
Cvar_SetValue(Cvar_FindVar("vid_width"), ctx->window.width);
|
|
Cvar_SetValue(Cvar_FindVar("vid_height"), ctx->window.height);
|
|
Cmd_ExecuteString("vid_restart", RESTRICT_LOCAL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
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)
|
|
{
|
|
int argc;
|
|
char *argv[16];
|
|
sys_hijackwindow = NULL;
|
|
|
|
GetModuleFileName(global_hInstance, binaryname, sizeof(binaryname));
|
|
argv[0] = binaryname;
|
|
argc = 1;
|
|
|
|
activecontext = ctx;
|
|
|
|
if (!*ctx->gamename || !strcmp(ctx->gamename, "q1") || !strcmp(ctx->gamename, "qw") || !strcmp(ctx->gamename, "quake") || !strcmp(ctx->gamename, "id1"))
|
|
argv[argc++] = "-quake";
|
|
else if (!strcmp(ctx->gamename, "q2") || !strcmp(ctx->gamename, "quake2"))
|
|
argv[argc++] = "-q2";
|
|
else if (!strcmp(ctx->gamename, "q3") || !strcmp(ctx->gamename, "quake3"))
|
|
argv[argc++] = "-q3";
|
|
else if (!strcmp(ctx->gamename, "hl") || !strcmp(ctx->gamename, "halflife"))
|
|
argv[argc++] = "-halflife";
|
|
else if (!strcmp(ctx->gamename, "h2") || !strcmp(ctx->gamename, "hexen2"))
|
|
argv[argc++] = "-hexen2";
|
|
else if (!strcmp(ctx->gamename, "nex") || !strcmp(ctx->gamename, "nexuiz"))
|
|
argv[argc++] = "-nexuiz";
|
|
else
|
|
{
|
|
argv[argc++] = "-basegame";
|
|
argv[argc++] = ctx->gamename;
|
|
}
|
|
ctx->contextrunning = NPQTV_Sys_Startup(argc, argv);
|
|
|
|
Cvar_SetValue(Cvar_FindVar("vid_width"), ctx->window.width);
|
|
Cvar_SetValue(Cvar_FindVar("vid_height"), ctx->window.height);
|
|
|
|
if (*ctx->datadownload)
|
|
{
|
|
if (!FS_FLocateFile("default.cfg", FSLFRT_IFFOUND, NULL) || !FS_FLocateFile("gfx.wad", FSLFRT_IFFOUND, NULL))
|
|
{
|
|
browserfuncs->geturlnotify(ctx->nppinstance, ctx->datadownload, NULL, &UnpackAndExtractPakFiles);
|
|
ctx->waitingfordatafiles++;
|
|
}
|
|
}
|
|
|
|
if (ctx->contextrunning)
|
|
{
|
|
if (ctx->onstart)
|
|
browserfuncs->geturl(ctx->nppinstance, va("javascript:%s;", ctx->onstart), "_self");
|
|
|
|
//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 if (!ctx->contextrunning)
|
|
{
|
|
HDC hdc;
|
|
PAINTSTRUCT paint;
|
|
char *s;
|
|
|
|
hdc = BeginPaint(hWnd, &paint);
|
|
DrawWndBack(ctx, hWnd, hdc, &paint);
|
|
SetBkMode(hdc, TRANSPARENT);
|
|
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
|
|
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;
|
|
|
|
Q_strncpyz(ctx->gamename, "q1", sizeof(ctx->gamename));
|
|
|
|
//parse out the properties
|
|
for (i = 0; i < argc; i++)
|
|
{
|
|
if (!stricmp(argn[i], "datadownload"))
|
|
{
|
|
Q_strncpyz(ctx->datadownload, argv[i], sizeof(ctx->datadownload));
|
|
}
|
|
else if (!stricmp(argn[i], "game"))
|
|
{
|
|
if (!strstr(argn[i], "."))
|
|
if (!strstr(argn[i], "/"))
|
|
if (!strstr(argn[i], "\\"))
|
|
if (!strstr(argn[i], ":"))
|
|
Q_strncpyz(ctx->gamename, argv[i], sizeof(ctx->gamename));
|
|
}
|
|
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;
|
|
}
|
|
|
|
}
|
|
|
|
//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
|
|
|
|
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_hijackwindow = NULL;
|
|
}
|
|
|
|
free(ctx);
|
|
instance->pdata = NULL;
|
|
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
NPError NP_LOADDS NPP_SetWindow(NPP instance, NPWindow* window)
|
|
{
|
|
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_hijackwindow)
|
|
{
|
|
sys_hijackwindow = ctx->window.window;
|
|
SetParent(mainwindow, ctx->window.window);
|
|
|
|
oldwindow = sys_hijackwindow;
|
|
}
|
|
}
|
|
|
|
if (ctx->contextrunning && mainwindow && oldwindow == sys_hijackwindow)
|
|
MoveWindow(mainwindow, 0, 0, ctx->window.width, ctx->window.height, FALSE);
|
|
|
|
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;
|
|
}
|
|
|
|
//CL_QTVPlay(pipe, false);
|
|
|
|
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)
|
|
{
|
|
}
|
|
|
|
NPError NP_LOADDS NPP_GetValue(NPP instance, NPPVariable variable, void *value)
|
|
{
|
|
switch(variable)
|
|
{
|
|
case NPPVpluginScriptableNPObject:
|
|
*(void**)value = NULL; //no scripting object, sorry.
|
|
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";
|
|
}
|