315 lines
7.1 KiB
C
315 lines
7.1 KiB
C
|
//This file should be easily portable.
|
||
|
//The biggest strength of this plugin system is that ALL interactions are performed via
|
||
|
//named functions, this makes it *really* easy to port plugins from one engine to annother.
|
||
|
|
||
|
#include "quakedef.h"
|
||
|
|
||
|
|
||
|
|
||
|
typedef struct plugin_s {
|
||
|
char *name;
|
||
|
vm_t *vm;
|
||
|
int tick;
|
||
|
int executestring;
|
||
|
|
||
|
struct plugin_s *next;
|
||
|
} plugin_t;
|
||
|
|
||
|
plugin_t *currentplug;
|
||
|
|
||
|
//custom plugin builtins.
|
||
|
typedef int (*Plug_Builtin_t)(void *offset, unsigned int mask, const long *arg);
|
||
|
void Plug_RegisterBuiltin(char *name, Plug_Builtin_t bi, int flags);
|
||
|
#define PLUG_BIF_DLLONLY 1
|
||
|
#define PLUG_BIF_QVMONLY 2
|
||
|
|
||
|
void Plug_Init(void);
|
||
|
|
||
|
void Plug_Tick(void);
|
||
|
qboolean Plugin_ExecuteString(void);
|
||
|
void Plug_Shutdown(void);
|
||
|
|
||
|
|
||
|
static plugin_t *plugs;
|
||
|
|
||
|
|
||
|
typedef struct {
|
||
|
char *name;
|
||
|
Plug_Builtin_t func;
|
||
|
} Plug_Plugins_t;
|
||
|
Plug_Plugins_t *plugbuiltins;
|
||
|
int numplugbuiltins;
|
||
|
|
||
|
void Plug_RegisterBuiltin(char *name, Plug_Builtin_t bi, int flags)
|
||
|
{
|
||
|
//randomize the order a little.
|
||
|
int newnum;
|
||
|
|
||
|
newnum = rand()%128;
|
||
|
while(newnum < numplugbuiltins && plugbuiltins[newnum].func)
|
||
|
newnum+=128;
|
||
|
|
||
|
if (newnum > numplugbuiltins)
|
||
|
{
|
||
|
numplugbuiltins = newnum+128;
|
||
|
plugbuiltins = BZ_Realloc(plugbuiltins, sizeof(Plug_Plugins_t)*numplugbuiltins);
|
||
|
}
|
||
|
|
||
|
//got an empty number.
|
||
|
plugbuiltins[newnum].name = name;
|
||
|
plugbuiltins[newnum].func = bi;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
static void Plug_RegisterBuiltinIndex(char *name, Plug_Builtin_t bi, int flags, int index) //I d
|
||
|
{
|
||
|
//randomize the order a little.
|
||
|
int newnum;
|
||
|
|
||
|
newnum = rand()%128;
|
||
|
while(newnum+1 < numplugbuiltins && plugbuiltins[newnum+1].func)
|
||
|
newnum+=128;
|
||
|
|
||
|
newnum++;
|
||
|
|
||
|
if (newnum > numplugbuiltins)
|
||
|
{
|
||
|
numplugbuiltins = newnum+128;
|
||
|
plugbuiltins = BZ_Realloc(plugbuiltins, sizeof(Plug_Plugins_t)*numplugbuiltins);
|
||
|
}
|
||
|
|
||
|
//got an empty number.
|
||
|
plugbuiltins[newnum].name = name;
|
||
|
plugbuiltins[newnum].func = bi;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
int Plug_FindBuiltin(void *offset, unsigned int mask, const long *args)
|
||
|
{
|
||
|
int i;
|
||
|
for (i = 0; i < numplugbuiltins; i++)
|
||
|
if (plugbuiltins[i].name)
|
||
|
if (!strcmp(plugbuiltins[i].name, (char *)offset+args[0]))
|
||
|
return -i;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
long Plug_SystemCallsEx(void *offset, unsigned int mask, int fn, const long *arg)
|
||
|
{
|
||
|
fn = fn+1;
|
||
|
|
||
|
if (fn>=0 && fn < numplugbuiltins && plugbuiltins[fn].func!=NULL)
|
||
|
return plugbuiltins[fn].func(offset, mask, arg);
|
||
|
|
||
|
Sys_Error("QVM Plugin tried calling invalid builtin %i", fn);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
static long Plug_SystemCallsExWrapper(void *offset, unsigned int mask, int fn, const long *arg)
|
||
|
{ //this is so we can use edit and continue properly (vc doesn't like function pointers for edit+continue)
|
||
|
return Plug_SystemCallsEx(offset, mask, fn, arg);
|
||
|
}
|
||
|
#define Plug_SystemCallsEx Plug_SystemCallsExWrapper
|
||
|
#endif
|
||
|
|
||
|
//I'm not keen on this.
|
||
|
//but dlls call it without saying what sort of vm it comes from, so I've got to have them as specifics
|
||
|
static int EXPORT_FN Plug_SystemCalls(int arg, ...)
|
||
|
{
|
||
|
long args[9];
|
||
|
va_list argptr;
|
||
|
|
||
|
va_start(argptr, arg);
|
||
|
args[0]=va_arg(argptr, int);
|
||
|
args[1]=va_arg(argptr, int);
|
||
|
args[2]=va_arg(argptr, int);
|
||
|
args[3]=va_arg(argptr, int);
|
||
|
args[4]=va_arg(argptr, int);
|
||
|
args[5]=va_arg(argptr, int);
|
||
|
args[6]=va_arg(argptr, int);
|
||
|
args[7]=va_arg(argptr, int);
|
||
|
args[8]=va_arg(argptr, int);
|
||
|
va_end(argptr);
|
||
|
|
||
|
arg = -arg;
|
||
|
|
||
|
if (arg>=0 && arg < numplugbuiltins && plugbuiltins[arg].func)
|
||
|
return plugbuiltins[arg].func(NULL, ~0, args);
|
||
|
|
||
|
Sys_Error("DLL Plugin tried calling invalid biultin %i", arg);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
plugin_t *Plug_Load(char *file)
|
||
|
{
|
||
|
plugin_t *newplug;
|
||
|
int argarray;
|
||
|
|
||
|
for (newplug = plugs; newplug; newplug = newplug->next)
|
||
|
{
|
||
|
if (!stricmp(newplug->name, file))
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
newplug = Z_Malloc(sizeof(plugin_t)+strlen(file)+1);
|
||
|
newplug->name = (char*)(newplug+1);
|
||
|
strcpy(newplug->name, file);
|
||
|
|
||
|
newplug->vm = VM_Create(NULL, file, Plug_SystemCalls, Plug_SystemCallsEx);
|
||
|
currentplug = newplug;
|
||
|
if (newplug->vm)
|
||
|
{
|
||
|
newplug->next = plugs;
|
||
|
plugs = newplug;
|
||
|
|
||
|
argarray = (int)"Plug_GetEngineFunction";
|
||
|
VM_Call(newplug->vm, 0, Plug_FindBuiltin(NULL, ~0, &argarray));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Z_Free(newplug);
|
||
|
newplug = NULL;
|
||
|
}
|
||
|
currentplug = NULL;
|
||
|
|
||
|
return newplug;
|
||
|
}
|
||
|
|
||
|
int Plug_Emumerated (char *name, int size, void *param)
|
||
|
{
|
||
|
char vmname[MAX_QPATH];
|
||
|
strcpy(vmname, name);
|
||
|
vmname[strlen(vmname) - strlen(param)] = '\0';
|
||
|
Plug_Load(vmname);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
int Plug_Con_Print(void *offset, unsigned int mask, const long *arg)
|
||
|
{
|
||
|
Con_Print((char*)offset+arg[0]);
|
||
|
return 0;
|
||
|
}
|
||
|
int Plug_Sys_Error(void *offset, unsigned int mask, const long *arg)
|
||
|
{
|
||
|
Sys_Error("%s", (char*)offset+arg[0]);
|
||
|
return 0;
|
||
|
}
|
||
|
int Plug_ExportToEngine(void *offset, unsigned int mask, const long *arg)
|
||
|
{
|
||
|
char *name = (char*)offset+arg[0];
|
||
|
if (!strcmp(name, "Tick"))
|
||
|
currentplug->tick = arg[1];
|
||
|
else if (!strcmp(name, "ExecuteCommand"))
|
||
|
currentplug->executestring = arg[1];
|
||
|
else
|
||
|
return 0;
|
||
|
return 1;
|
||
|
}
|
||
|
//void(char *buffer, int buffersize)
|
||
|
int Plug_Cmd_Args(void *offset, unsigned int mask, const long *arg)
|
||
|
{
|
||
|
char *buffer = (char*)offset+arg[0];
|
||
|
char *args;
|
||
|
args = Cmd_Args();
|
||
|
if (strlen(args)+1>arg[1])
|
||
|
return 0;
|
||
|
strcpy(buffer, args);
|
||
|
return 1;
|
||
|
}
|
||
|
//void(int num, char *buffer, int buffersize)
|
||
|
int Plug_Cmd_Argv(void *offset, unsigned int mask, const long *arg)
|
||
|
{
|
||
|
char *buffer = (char*)offset+arg[1];
|
||
|
char *args;
|
||
|
args = Cmd_Argv(arg[0]);
|
||
|
if (strlen(args)+1>arg[2])
|
||
|
return 0;
|
||
|
strcpy(buffer, args);
|
||
|
return 1;
|
||
|
}
|
||
|
//int(void)
|
||
|
int Plug_Cmd_Argc(void *offset, unsigned int mask, const long *arg)
|
||
|
{
|
||
|
return Cmd_Argc();
|
||
|
}
|
||
|
void Plug_Init(void)
|
||
|
{
|
||
|
Plug_RegisterBuiltin("Plug_GetEngineFunction", Plug_FindBuiltin, 0);//plugin wishes to find a builtin number.
|
||
|
Plug_RegisterBuiltin("Plug_ExportToEngine", Plug_ExportToEngine, 0); //plugin has a call back that we might be interested in.
|
||
|
Plug_RegisterBuiltin("Con_Print", Plug_Con_Print, 0); //printf is not possible - qvm floats are never doubles, vararg floats in a cdecl call are always converted to doubles.
|
||
|
Plug_RegisterBuiltin("Sys_Error", Plug_Sys_Error, 0);
|
||
|
Plug_RegisterBuiltin("Com_Error", Plug_Sys_Error, 0); //make zquake programmers happy.
|
||
|
|
||
|
Plug_RegisterBuiltin("Cmd_Args", Plug_Cmd_Args, 0);
|
||
|
Plug_RegisterBuiltin("Cmd_Argc", Plug_Cmd_Argc, 0);
|
||
|
Plug_RegisterBuiltin("Cmd_Argv", Plug_Cmd_Argv, 0);
|
||
|
|
||
|
|
||
|
|
||
|
#ifdef _WIN32
|
||
|
COM_EnumerateFiles("plugins/*x86.dll", Plug_Emumerated, "x86.dll");
|
||
|
#elif defined(__linux__)
|
||
|
COM_EnumerateFiles("plugins/*x86.so", Plug_Emumerated, "x86.so");
|
||
|
#endif
|
||
|
COM_EnumerateFiles("plugins/*.qvm", Plug_Emumerated, ".qvm");
|
||
|
}
|
||
|
|
||
|
void Plug_Tick(void)
|
||
|
{
|
||
|
plugin_t *plug;
|
||
|
for (plug = plugs; plug; plug = plug->next)
|
||
|
{
|
||
|
if (plug->tick)
|
||
|
{
|
||
|
currentplug = plug;
|
||
|
VM_Call(plug->vm, plug->tick, (int)(realtime*1000));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
qboolean Plugin_ExecuteString(void)
|
||
|
{
|
||
|
plugin_t *plug;
|
||
|
if (Cmd_Argc()>0)
|
||
|
{
|
||
|
for (plug = plugs; plug; plug = plug->next)
|
||
|
{
|
||
|
if (plug->executestring)
|
||
|
{
|
||
|
currentplug = plug;
|
||
|
if (VM_Call(plug->vm, plug->executestring, 0))
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void Plug_Close(plugin_t *plug)
|
||
|
{
|
||
|
if (plugs == plug)
|
||
|
plugs = plug->next;
|
||
|
else
|
||
|
{
|
||
|
plugin_t *prev;
|
||
|
for (prev = plugs; prev; prev = prev->next)
|
||
|
{
|
||
|
if (prev->next == plug)
|
||
|
break;
|
||
|
}
|
||
|
if (!prev)
|
||
|
Sys_Error("Plug_Close: not linked\n");
|
||
|
prev->next = plug->next;
|
||
|
}
|
||
|
|
||
|
VM_Destroy(plug->vm);
|
||
|
}
|
||
|
|
||
|
void Plug_Shutdown(void)
|
||
|
{
|
||
|
|
||
|
}
|