mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-30 15:41:49 +00:00
5118de8bdd
some minor changes. Mostly bug fixes and internal reorganisation. Added code to provide an activex control as part of the npfte.dll plugin. If the dll is registered the regsvr32 way, the plugin can be used with IE as well. fisheye/panoramic view enable is now controlled by rulesets instead of serverinfo. server will list all pak files it has loaded. client will probably do the wrong thing and still needs fixing properly. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@3909 fc73d0e0-1445-4013-8a0c-d673dee63da5
1143 lines
26 KiB
C
1143 lines
26 KiB
C
#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 windowleft;
|
|
int windowtop;
|
|
int windowwidth;
|
|
int windowheight;
|
|
|
|
int waitingfordatafiles;
|
|
|
|
char *datadownload;
|
|
char *gamename;
|
|
char *password;
|
|
char *onstart;
|
|
char *onend;
|
|
char *ondemoend;
|
|
|
|
void *hostinstance;
|
|
|
|
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;
|
|
}
|
|
|
|
#if _MSC_VER >= 1300
|
|
#define CATCHCRASH
|
|
#endif
|
|
|
|
#ifdef CATCHCRASH
|
|
#ifdef _DEBUG
|
|
#include "dbghelp.h"
|
|
DWORD CrashExceptionHandler (DWORD exceptionCode, LPEXCEPTION_POINTERS exceptionInfo);
|
|
#endif
|
|
#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);
|
|
|
|
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);
|
|
}
|
|
|
|
ctx->pub.downloading = true;
|
|
while(host_initialized && !ctx->shutdown && ctx->packagelist)
|
|
{
|
|
int total=0, done=0;
|
|
ctx->resetvideo = false;
|
|
Sys_LockMutex(ctx->mutex);
|
|
for (dl = ctx->packagelist; dl; dl = dl->next)
|
|
{
|
|
total += dl->totalsize;
|
|
done += dl->completed;
|
|
}
|
|
dl = ctx->packagelist;
|
|
if (total != ctx->pub.dlsize || done != ctx->pub.dldone)
|
|
{
|
|
ctx->pub.dlsize = total;
|
|
ctx->pub.dldone = done;
|
|
if (ctx->bfuncs.StatusChanged)
|
|
ctx->bfuncs.StatusChanged(ctx->hostinstance);
|
|
}
|
|
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);
|
|
}
|
|
ctx->pub.downloading = false;
|
|
|
|
if (host_initialized && !ctx->shutdown)
|
|
{
|
|
Sys_LockMutex(ctx->mutex);
|
|
ctx->resetvideo = false;
|
|
sys_parentwindow = ctx->windowhnd;
|
|
sys_parentleft = ctx->windowleft;
|
|
sys_parenttop = ctx->windowtop;
|
|
sys_parentwidth = ctx->windowwidth;
|
|
sys_parentheight = ctx->windowheight;
|
|
Host_FinishInit();
|
|
Sys_UnlockMutex(ctx->mutex);
|
|
}
|
|
if (ctx->bfuncs.StatusChanged)
|
|
ctx->bfuncs.StatusChanged(ctx->hostinstance);
|
|
|
|
while(host_initialized)
|
|
{
|
|
Sys_LockMutex(ctx->mutex);
|
|
if (ctx->shutdown)
|
|
{
|
|
ctx->shutdown = false;
|
|
Host_Shutdown(); /*will shut down the host*/
|
|
}
|
|
else if (ctx->resetvideo)
|
|
{
|
|
sys_parentwindow = ctx->windowhnd;
|
|
sys_parentleft = ctx->windowleft;
|
|
sys_parenttop = ctx->windowtop;
|
|
sys_parentwidth = ctx->windowwidth;
|
|
sys_parentheight = ctx->windowheight;
|
|
if (ctx->resetvideo == 2)
|
|
SetParent(mainwindow, sys_parentwindow);
|
|
ctx->resetvideo = false;
|
|
Cbuf_AddText("vid_recenter\n", RESTRICT_LOCAL);
|
|
}
|
|
else
|
|
NPQTV_Sys_MainLoop();
|
|
Sys_UnlockMutex(ctx->mutex);
|
|
}
|
|
}
|
|
#ifdef CATCHCRASH
|
|
#ifdef _DEBUG
|
|
__except (CrashExceptionHandler(GetExceptionCode(), GetExceptionInformation()))
|
|
{
|
|
|
|
}
|
|
#else
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Host_Shutdown();
|
|
MessageBox(sys_parentwindow, "Sorry, FTE plugin crashed.\nYou probably should restart your browser", "FTE crashed", 0);
|
|
}
|
|
#endif
|
|
#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);
|
|
if (ctx->bfuncs.StatusChanged)
|
|
ctx->bfuncs.StatusChanged(ctx->hostinstance);
|
|
|
|
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, THREADP_NORMAL, 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;
|
|
|
|
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->hostinstance = 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 left, int top, int width, int height)
|
|
{
|
|
qboolean result = false;
|
|
|
|
Plug_LockPlugin(ctx, true);
|
|
|
|
//if the window changed
|
|
if (ctx->windowhnd != whnd)
|
|
{
|
|
result = true;
|
|
ctx->windowhnd = whnd;
|
|
ctx->resetvideo = 2;
|
|
}
|
|
|
|
ctx->windowleft = left;
|
|
ctx->windowtop = top;
|
|
ctx->windowwidth = width;
|
|
ctx->windowheight = height;
|
|
|
|
if (ctx->pub.running && !ctx->resetvideo)
|
|
ctx->resetvideo = true;
|
|
Plug_LockPlugin(ctx, false);
|
|
|
|
while(ctx->pub.running && ctx->resetvideo)
|
|
Sleep(10);
|
|
|
|
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);
|
|
|
|
if (dl->status == DL_FINISHED)
|
|
zip = zipfilefuncs.OpenNew(dl->file, dl->url);
|
|
else
|
|
zip = NULL;
|
|
/*the zip code will have eaten the file handle*/
|
|
dl->file = NULL;
|
|
if (zip)
|
|
{
|
|
/*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)
|
|
{
|
|
if (ctx->bfuncs.StatusChanged)
|
|
ctx->bfuncs.StatusChanged(ctx->hostinstance);
|
|
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->bfuncs.StatusChanged)
|
|
ctx->bfuncs.StatusChanged(ctx->hostinstance);
|
|
}
|
|
}
|
|
|
|
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 strdup(ctx->qtvf.server);
|
|
}
|
|
void pscript_property_startserver_sets(struct context *ctx, const char *val)
|
|
{
|
|
if (strchr(val, '$') || strchr(val, ';') || strchr(val, '\n'))
|
|
return;
|
|
|
|
ctx->qtvf.connectiontype = QTVCT_JOIN;
|
|
Q_strncpyz(ctx->qtvf.server, val, sizeof(ctx->qtvf.server));
|
|
}
|
|
char *pscript_property_curserver_gets(struct context *ctx)
|
|
{
|
|
extern char lastdemoname[];
|
|
if (!pscript_property_running_getb(ctx))
|
|
return pscript_property_startserver_gets(ctx);
|
|
|
|
if (cls.demoplayback)
|
|
return strdup(va("demo:%s",lastdemoname));
|
|
else if (cls.state != ca_disconnected)
|
|
return strdup(cls.servername);
|
|
else
|
|
return strdup("");
|
|
}
|
|
void pscript_property_curserver_sets(struct context *ctx, const char *val)
|
|
{
|
|
if (strchr(val, '$') || strchr(val, ';') || strchr(val, '\n'))
|
|
return;
|
|
|
|
if (!pscript_property_running_getb(ctx))
|
|
{
|
|
pscript_property_startserver_sets(ctx, val);
|
|
return;
|
|
}
|
|
|
|
Q_strncpyz(cls.servername, val, sizeof(cls.servername));
|
|
CL_BeginServerConnect(0);
|
|
}
|
|
|
|
void pscript_property_stream_sets(struct context *ctx, const char *val)
|
|
{
|
|
if (strchr(val, '$') || strchr(val, ';') || strchr(val, '\n'))
|
|
return;
|
|
|
|
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_map_sets(struct context *ctx, const char *val)
|
|
{
|
|
if (strchr(val, '$') || strchr(val, ';') || strchr(val, '\n'))
|
|
return;
|
|
ctx->qtvf.connectiontype = QTVCT_MAP;
|
|
Q_strncpyz(ctx->qtvf.server, val, sizeof(ctx->qtvf.server));
|
|
|
|
if (pscript_property_running_getb(ctx))
|
|
Cmd_ExecuteString(va("map \"%s\"\n", val), RESTRICT_INSECURE);
|
|
}
|
|
|
|
float pscript_property_curver_getf(struct context *ctx)
|
|
{
|
|
return version_number();
|
|
}
|
|
|
|
void pscript_property_availver_setf(struct context *ctx, float val)
|
|
{
|
|
ctx->pub.availver = val;
|
|
if (ctx->pub.availver <= version_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 (strchr(val, '$') || strchr(val, ';') || strchr(val, '\n'))
|
|
return;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
char *pscript_property_build_gets(struct context *ctx)
|
|
{
|
|
return strdup(DISTRIBUTION " " __DATE__ " " __TIME__
|
|
#if defined(DEBUG) || defined(_DEBUG)
|
|
" (debug)"
|
|
#endif
|
|
);
|
|
}
|
|
|
|
extern cvar_t skin, team, topcolor, bottomcolor, vid_fullscreen, cl_download_mapsrc;
|
|
static struct pscript_property pscript_properties[] =
|
|
{
|
|
{"", false, NULL, pscript_property_curserver_gets, pscript_property_curserver_sets},
|
|
{"server", false, NULL, pscript_property_curserver_gets, pscript_property_curserver_sets},
|
|
{"running", false, NULL, NULL, NULL, pscript_property_running_getb, pscript_property_running_setb},
|
|
{"startserver", false, NULL, pscript_property_startserver_gets, pscript_property_startserver_sets},
|
|
{"join", false, NULL, NULL, 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},
|
|
{"mapsrc", true, &cl_download_mapsrc},
|
|
{"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},
|
|
{"plugver", false, NULL, NULL, NULL, NULL, NULL, pscript_property_curver_getf},
|
|
|
|
{"splash", false, NULL, NULL, pscript_property_splash_sets},
|
|
|
|
{"stream", false, NULL, NULL, pscript_property_stream_sets},
|
|
{"map", false, NULL, NULL, pscript_property_map_sets},
|
|
|
|
{"build", false, NULL, pscript_property_build_gets},
|
|
|
|
{NULL}
|
|
};
|
|
|
|
int 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 -1;
|
|
}
|
|
return prop - pscript_properties;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
qboolean Plug_SetString(struct context *ctx, int fieldidx, const char *value)
|
|
{
|
|
struct pscript_property *field = pscript_properties + fieldidx;
|
|
if (!ctx || fieldidx < 0 || fieldidx >= sizeof(pscript_properties)/sizeof(pscript_properties[0]) || !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_SetWString(struct context *ctx, int fieldidx, const wchar_t *value)
|
|
{
|
|
char tmp[1024];
|
|
wcstombs(tmp, value, sizeof(tmp));
|
|
return Plug_SetString(ctx, fieldidx, tmp);
|
|
}
|
|
qboolean Plug_SetInteger(struct context *ctx, int fieldidx, int value)
|
|
{
|
|
struct pscript_property *field = pscript_properties + fieldidx;
|
|
if (!ctx || fieldidx < 0 || fieldidx >= sizeof(pscript_properties)/sizeof(pscript_properties[0]))
|
|
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, int fieldidx, float value)
|
|
{
|
|
struct pscript_property *field = pscript_properties + fieldidx;
|
|
if (!ctx || fieldidx < 0 || fieldidx >= sizeof(pscript_properties)/sizeof(pscript_properties[0]))
|
|
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;
|
|
}
|
|
|
|
qboolean Plug_GetString(struct context *ctx, int fieldidx, const char **value)
|
|
{
|
|
struct pscript_property *field = pscript_properties + fieldidx;
|
|
if (!ctx || fieldidx < 0 || fieldidx >= sizeof(pscript_properties)/sizeof(pscript_properties[0]))
|
|
return false;
|
|
|
|
if (field->getstring)
|
|
{
|
|
*value = field->getstring(ctx);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
void Plug_GotString(const char *value)
|
|
{
|
|
free((char*)value);
|
|
}
|
|
qboolean Plug_GetInteger(struct context *ctx, int fieldidx, int *value)
|
|
{
|
|
struct pscript_property *field = pscript_properties + fieldidx;
|
|
if (!ctx || fieldidx < 0 || fieldidx >= sizeof(pscript_properties)/sizeof(pscript_properties[0]))
|
|
return false;
|
|
|
|
if (field->getint)
|
|
{
|
|
*value = field->getint(ctx);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
qboolean Plug_GetFloat(struct context *ctx, int fieldidx, float *value)
|
|
{
|
|
struct pscript_property *field = pscript_properties + fieldidx;
|
|
if (!ctx || fieldidx < 0 || fieldidx >= sizeof(pscript_properties)/sizeof(pscript_properties[0]))
|
|
return false;
|
|
|
|
if (field->getfloat)
|
|
{
|
|
*value = field->getfloat(ctx);
|
|
return true;
|
|
}
|
|
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,
|
|
|
|
Plug_SetWString
|
|
};
|
|
|
|
const struct plugfuncs *Plug_GetFuncs(int ver)
|
|
{
|
|
if (ver == 1)
|
|
return &exportedplugfuncs_1;
|
|
else
|
|
return NULL;
|
|
}
|