1
0
Fork 0
forked from fte/fteqw
fteqw/engine/client/cl_ui.c

1916 lines
49 KiB
C
Raw Normal View History

#include "q3common.h"
#ifdef VM_UI
#include "clq3defs.h"
#include "ui_public.h"
#include "cl_master.h"
#include "shader.h"
static int keycatcher;
#include "botlib.h"
void SV_InitBotLib(void);
extern botlib_export_t *botlib;
qboolean CG_GetLimboString(int index, char *outbuf);
#define TT_STRING 1 // string
#define TT_LITERAL 2 // literal
#define TT_NUMBER 3 // number
#define TT_NAME 4 // name
#define TT_PUNCTUATION 5 // punctuation
#define SCRIPT_MAXDEPTH 64
#define SCRIPT_DEFINELENGTH 256
typedef struct {
char *filestack[SCRIPT_MAXDEPTH];
char *originalfilestack[SCRIPT_MAXDEPTH];
char *lastreadptr;
int lastreaddepth;
char filename[MAX_QPATH][SCRIPT_MAXDEPTH];
int stackdepth;
char *defines;
int numdefines;
} script_t;
static script_t *scripts;
static int maxscripts;
static float ui_size[2]; //to track when it needs to be restarted (the api has no video mode changed event)
static menu_t uimenu;
void Q3_SetKeyCatcher(int newcatcher)
{
int delta = newcatcher^keycatcher;
keycatcher = newcatcher;
if (delta & 2)
{
uimenu.isopaque = false; //no surprises.
if (newcatcher&2)
inputfuncs->Menu_Push(&uimenu, false);
else
inputfuncs->Menu_Unlink(&uimenu, false);
}
}
int Q3_GetKeyCatcher(void)
{
return keycatcher;
}
#define Q3SCRIPTPUNCTUATION "(,{})(\':;=!><&|+-\""
void StripCSyntax (char *s)
{
while(*s)
{
if (*s == '\\')
{
memmove(s, s+1, strlen(s+1)+1);
switch (*s)
{
case 'r':
*s = '\r';
break;
case 'n':
*s = '\n';
break;
case '\\':
*s = '\\';
break;
default:
*s = '?';
break;
}
}
s++;
}
}
int Script_Read(int handle, struct pc_token_s *token)
{
char *s;
char readstring[8192];
int i;
script_t *sc = scripts+handle-1;
char thetoken[1024];
com_tokentype_t tokentype;
for(;;)
{
if (!sc->stackdepth)
{
memset(token, 0, sizeof(*token));
return 0;
}
s = sc->filestack[sc->stackdepth-1];
sc->lastreadptr = s;
sc->lastreaddepth = sc->stackdepth;
s = (char *)cmdfuncs->ParsePunctuation(s, Q3SCRIPTPUNCTUATION, thetoken, sizeof(thetoken), &tokentype);
Q_strncpyz(readstring, thetoken, sizeof(readstring));
if (tokentype == TTP_STRING)
{
while(s)
{
while (*s > '\0' && *s <= ' ')
s++;
if (*s == '/' && s[1] == '/')
{
while(*s && *s != '\n')
s++;
continue;
}
while (*s > '\0' && *s <= ' ')
s++;
if (*s == '\"')
{
s = (char*)cmdfuncs->ParsePunctuation(s, Q3SCRIPTPUNCTUATION, thetoken, sizeof(thetoken), &tokentype);
Q_strncatz(readstring, thetoken, sizeof(readstring));
}
else
break;
}
}
sc->filestack[sc->stackdepth-1] = s;
if (tokentype == TTP_LINEENDING)
continue; //apparently we shouldn't stop on linebreaks
if (!strcmp(readstring, "#include"))
{
sc->filestack[sc->stackdepth-1] = (char *)cmdfuncs->ParsePunctuation(sc->filestack[sc->stackdepth-1], Q3SCRIPTPUNCTUATION, thetoken, sizeof(thetoken), &tokentype);
if (sc->stackdepth == SCRIPT_MAXDEPTH) //just don't enter it
continue;
if (sc->originalfilestack[sc->stackdepth])
plugfuncs->Free(sc->originalfilestack[sc->stackdepth]);
sc->filestack[sc->stackdepth] = sc->originalfilestack[sc->stackdepth] = fsfuncs->LoadFile(thetoken, NULL);
Q_strncpyz(sc->filename[sc->stackdepth], thetoken, MAX_QPATH);
sc->stackdepth++;
continue;
}
if (!strcmp(readstring, "#define"))
{
sc->numdefines++;
sc->defines = plugfuncs->Realloc(sc->defines, sc->numdefines*SCRIPT_DEFINELENGTH*2);
sc->filestack[sc->stackdepth-1] = (char *)cmdfuncs->ParsePunctuation(sc->filestack[sc->stackdepth-1], Q3SCRIPTPUNCTUATION, thetoken, sizeof(thetoken), &tokentype);
Q_strncpyz(sc->defines+SCRIPT_DEFINELENGTH*2*(sc->numdefines-1), thetoken, SCRIPT_DEFINELENGTH);
sc->filestack[sc->stackdepth-1] = (char *)cmdfuncs->ParsePunctuation(sc->filestack[sc->stackdepth-1], Q3SCRIPTPUNCTUATION, thetoken, sizeof(thetoken), &tokentype);
Q_strncpyz(sc->defines+SCRIPT_DEFINELENGTH*2*(sc->numdefines-1)+SCRIPT_DEFINELENGTH, thetoken, SCRIPT_DEFINELENGTH);
continue;
}
if (!*readstring && tokentype != TTP_STRING)
{
if (sc->stackdepth==0)
{
memset(token, 0, sizeof(*token));
return 0;
}
sc->stackdepth--;
continue;
}
break;
}
if (tokentype == TTP_STRING)
{
i = sc->numdefines;
}
else
{
for (i = 0; i < sc->numdefines; i++)
{
if (!strcmp(readstring, sc->defines+SCRIPT_DEFINELENGTH*2*i))
{
Q_strncpyz(token->string, sc->defines+SCRIPT_DEFINELENGTH*2*i+SCRIPT_DEFINELENGTH, sizeof(token->string));
break;
}
}
}
if (i == sc->numdefines) //otherwise
Q_strncpyz(token->string, readstring, sizeof(token->string));
StripCSyntax(token->string);
if (token->string[0] == '0' && (token->string[1] == 'x'||token->string[1] == 'X'))
{
token->intvalue = strtoul(token->string, NULL, 16);
token->floatvalue = token->intvalue;
token->type = TT_NUMBER;
token->subtype = 0x100;//TT_HEX;
}
else
{
token->intvalue = atoi(token->string);
token->floatvalue = atof(token->string);
if (token->floatvalue || *token->string == '0' || *token->string == '.')
{
token->type = TT_NUMBER;
token->subtype = 0;
}
else if (tokentype == TTP_STRING)
{
token->type = TT_STRING;
token->subtype = strlen(token->string);
}
else
{
if (token->string[1] == '\0')
{
token->type = TT_PUNCTUATION;
token->subtype = token->string[0];
}
else
{
token->type = TT_NAME;
token->subtype = strlen(token->string);
}
}
}
// Con_Printf("Found %s (%i, %i)\n", token->string, token->type, token->subtype);
return tokentype != TTP_EOF;
}
int Script_LoadFile(char *filename)
{
int i;
script_t *sc;
for (i = 0; i < maxscripts; i++)
if (!scripts[i].stackdepth)
break;
if (i == maxscripts)
{
maxscripts++;
scripts = plugfuncs->Realloc(scripts, sizeof(script_t)*maxscripts);
}
sc = scripts+i;
memset(sc, 0, sizeof(*sc));
sc->filestack[0] = sc->originalfilestack[0] = fsfuncs->LoadFile(filename, NULL);
Q_strncpyz(sc->filename[sc->stackdepth], filename, MAX_QPATH);
sc->stackdepth = 1;
return i+1;
}
void Script_Free(int handle)
{
int i;
script_t *sc = scripts+handle-1;
if (sc->defines)
plugfuncs->Free(sc->defines);
for (i = 0; i < sc->stackdepth; i++)
plugfuncs->Free(sc->originalfilestack[i]);
sc->stackdepth = 0;
}
void Script_Get_File_And_Line(int handle, char *filename, int *line)
{
script_t *sc = scripts+handle-1;
char *src;
char *start;
if (!sc->lastreaddepth)
{
*line = 0;
Q_strncpyz(filename, sc->filename[0], MAX_QPATH);
return;
}
*line = 1;
src = sc->lastreadptr;
start = sc->originalfilestack[sc->lastreaddepth-1];
while(start < src)
{
if (*start == '\n')
(*line)++;
start++;
}
Q_strncpyz(filename, sc->filename[sc->lastreaddepth-1], MAX_QPATH);
}
static vm_t *uivm;
#define MAX_PINGREQUESTS 32
static struct
{
unsigned int startms;
netadr_t adr;
char adrstring[64];
const char *broker;
} ui_pings[MAX_PINGREQUESTS];
#define UITAGNUM 2452
struct q3refEntity_s {
refEntityType_t reType;
int renderfx;
int hModel; // opaque type outside refresh
// most recent data
vec3_t lightingOrigin; // so multi-part models can be lit identically (RF_LIGHTING_ORIGIN)
float shadowPlane; // projection shadows go here, stencils go slightly lower
vec3_t axis[3]; // rotation vectors
qboolean nonNormalizedAxes; // axis are not normalized, i.e. they have scale
float origin[3]; // also used as MODEL_BEAM's "from"
int frame; // also used as MODEL_BEAM's diameter
// previous data for frame interpolation
float oldorigin[3]; // also used as MODEL_BEAM's "to"
int oldframe;
float backlerp; // 0.0 = current, 1.0 = old
// texturing
int skinNum; // inline skin index
skinid_t customSkin; // NULL for default skin
int customShader; // use one image for the entire thing
// misc
qbyte shaderRGBA[4]; // colors used by rgbgen entity shaders
float shaderTexCoord[2]; // texture coordinates used by tcMod entity modifiers
float shaderTime; // subtracted from refdef time to control effect start times
// extra sprite information
float radius;
float rotation;
};
struct q3polyvert_s
{
vec3_t org;
vec2_t tcoord;
qbyte colours[4];
};
#define Q3RF_MINLIGHT 1
#define Q3RF_THIRD_PERSON 2 // don't draw through eyes, only mirrors (player bodies, chat sprites)
#define Q3RF_FIRST_PERSON 4 // only draw through eyes (view weapon, damage blood blob)
#define Q3RF_DEPTHHACK 8 // for view weapon Z crunching
#define Q3RF_NOSHADOW 64
#define Q3RF_LIGHTING_ORIGIN 128
#define MAX_VMQ3_CACHED_STRINGS 2048
static char *stringcache[MAX_VMQ3_CACHED_STRINGS];
void VMQ3_FlushStringHandles(void)
{
int i;
for (i = 0; i < MAX_VMQ3_CACHED_STRINGS; i++)
{
if (stringcache[i])
{
Z_Free(stringcache[i]);
stringcache[i] = NULL;
}
}
}
char *VMQ3_StringFromHandle(int handle)
{
if (!handle)
return "";
handle--;
if ((unsigned) handle >= MAX_VMQ3_CACHED_STRINGS)
return "";
return stringcache[handle];
}
int VMQ3_StringToHandle(char *str)
{
int i;
for (i = 0; i < MAX_VMQ3_CACHED_STRINGS; i++)
{
if (!stringcache[i])
break;
if (!strcmp(str, stringcache[i]))
return i+1;
}
if (i == MAX_VMQ3_CACHED_STRINGS)
{
Con_Printf("Q3VM out of string handle space\n");
return 0;
}
stringcache[i] = Z_Malloc(strlen(str)+1);
strcpy(stringcache[i], str);
return i+1;
}
#define VM_TOSTRCACHE(a) VMQ3_StringToHandle(VM_POINTER(a))
#define VM_FROMSTRCACHE(a) VMQ3_StringFromHandle(a)
void VQ3_AddEntity(const q3refEntity_t *q3)
{
entity_t ent;
memset(&ent, 0, sizeof(ent));
ent.model = scenefuncs->ModelFromId(q3->hModel);
ent.framestate.g[FS_REG].frame[0] = q3->frame;
ent.framestate.g[FS_REG].frame[1] = q3->oldframe;
memcpy(ent.axis, q3->axis, sizeof(q3->axis));
ent.framestate.g[FS_REG].lerpweight[1] = q3->backlerp;
ent.framestate.g[FS_REG].lerpweight[0] = 1 - ent.framestate.g[FS_REG].lerpweight[1];
if (q3->reType == RT_SPRITE)
{
ent.scale = q3->radius;
ent.rotation = q3->rotation;
}
else
ent.scale = 1;
ent.rtype = q3->reType;
rewrote ban code, merging bans+nonbans+cuffs+mute+cripple+deaf+lagged+vip. added timeouts. new penalties have no dedicated command. use the addip command for it. maplist command now generates links. implemented skin objects for q3. added a csqc builtin for it. also supports compositing skins. playing demos inside zips/pk3s/paks should now work. bumped default rate cvar. added cl_transfer to attempt to connect to a new server without disconnecting first. rewrote fog command. alpha and mindist arguments are now supported. fog change also happens over a short time period. added new args to the showpic console command. can now create clickable items for touchscreen/absmouse users. fixed menus to properly support right-aligned text. this finally fixes variable-width fonts. rewrote console tab completion suggestions display. now clickable links. strings obtained from qc are now marked as const. this has required quite a few added consts all over the place. probably crappy attempt at adding joypad support to the sdl port. no idea if it works. changed key bind event code. buttons now track which event they should trigger when released, instead of being the same one the whole time. this allows +forward etc clickable buttons on screen. Also simplified modifier keys - they no longer trigger random events when pressing the modifier key itself. Right modifiers can now be bound separately from left modifiers. Right will use left's binding if not otherwise bound. Bind assumes left if there's no prefix. multiplayer->setup->network menu no longer crashes. added rgb colours to the translation view (but not to the colour-changing keys). added modelviewer command to view models. added menu_mods menu to switch mods in a more friendly way. will be shown by default if multiple manifests exist in the binarydir. clamped classic tracer density. scrag particles no longer look quite so buggy. added ifdefs to facilitate a potential winrt port. the engine should now have no extra dependencies, but still needs system code+audio drivers to be written. if it can't set a renderer, it'll now try to use *every* renderer until it finds one that works. added experimental mapcluster server mode (that console command). New maps will be started up as required. rewrote skeletal blending code a bit. added cylinder geomtypes. fix cfg_save writing to the wrong path bug. VFS_CLOSE now returns a boolean. false means there was some sort of fatal error (either crc when reading was bad, or the write got corrupted or something). Typically ignorable, depends how robust you want to be. win32 tls code now supports running as a server. added connect tls://address support, as well as equivalent sv_addport support. exposed basic model loading api to plugins. d3d11 backend now optionally supports tessellation hlsl. no suitable hlsl provided by default. !!tess to enable. attempted to add gamma ramp support for d3d11. added support for shader blobs to speed up load times. r_shaderblobs 1 to enable. almost vital for d3d11. added vid_srgb cvar. shadowless lights are no longer disabled if shadows are not supported. attempt to add support for touchscreens in win7/8. Wrote gimmicky lua support, using lua instead of ssqc. define VM_LUA to enable. updated saved game code. can again load saved games from vanilla-like engines. changed scale clamping. 0.0001 should no longer appear as 1. changed default mintic from 0.03 to 0.013 to match vanilla qw. I don't know why it was at 0.03. probably a typo. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4623 fc73d0e0-1445-4013-8a0c-d673dee63da5
2014-03-30 08:55:06 +00:00
ent.customskin = q3->customSkin;
ent.skinnum = q3->skinNum;
ent.shaderRGBAf[0] = q3->shaderRGBA[0]/255.0f;
ent.shaderRGBAf[1] = q3->shaderRGBA[1]/255.0f;
ent.shaderRGBAf[2] = q3->shaderRGBA[2]/255.0f;
ent.shaderRGBAf[3] = q3->shaderRGBA[3]/255.0f;
/*don't set force-translucent etc, the shader is meant to already be correct*/
// if (ent.shaderRGBAf[3] <= 0)
// return;
ent.forcedshader = drawfuncs->ShaderFromId(q3->customShader);
ent.shaderTime = q3->shaderTime;
if (q3->renderfx & Q3RF_FIRST_PERSON)
ent.flags |= RF_FIRSTPERSON;
if (q3->renderfx & Q3RF_DEPTHHACK)
ent.flags |= RF_DEPTHHACK;
if (q3->renderfx & Q3RF_THIRD_PERSON)
ent.flags |= RF_EXTERNALMODEL;
if (q3->renderfx & Q3RF_NOSHADOW)
ent.flags |= RF_NOSHADOW;
rewrote ban code, merging bans+nonbans+cuffs+mute+cripple+deaf+lagged+vip. added timeouts. new penalties have no dedicated command. use the addip command for it. maplist command now generates links. implemented skin objects for q3. added a csqc builtin for it. also supports compositing skins. playing demos inside zips/pk3s/paks should now work. bumped default rate cvar. added cl_transfer to attempt to connect to a new server without disconnecting first. rewrote fog command. alpha and mindist arguments are now supported. fog change also happens over a short time period. added new args to the showpic console command. can now create clickable items for touchscreen/absmouse users. fixed menus to properly support right-aligned text. this finally fixes variable-width fonts. rewrote console tab completion suggestions display. now clickable links. strings obtained from qc are now marked as const. this has required quite a few added consts all over the place. probably crappy attempt at adding joypad support to the sdl port. no idea if it works. changed key bind event code. buttons now track which event they should trigger when released, instead of being the same one the whole time. this allows +forward etc clickable buttons on screen. Also simplified modifier keys - they no longer trigger random events when pressing the modifier key itself. Right modifiers can now be bound separately from left modifiers. Right will use left's binding if not otherwise bound. Bind assumes left if there's no prefix. multiplayer->setup->network menu no longer crashes. added rgb colours to the translation view (but not to the colour-changing keys). added modelviewer command to view models. added menu_mods menu to switch mods in a more friendly way. will be shown by default if multiple manifests exist in the binarydir. clamped classic tracer density. scrag particles no longer look quite so buggy. added ifdefs to facilitate a potential winrt port. the engine should now have no extra dependencies, but still needs system code+audio drivers to be written. if it can't set a renderer, it'll now try to use *every* renderer until it finds one that works. added experimental mapcluster server mode (that console command). New maps will be started up as required. rewrote skeletal blending code a bit. added cylinder geomtypes. fix cfg_save writing to the wrong path bug. VFS_CLOSE now returns a boolean. false means there was some sort of fatal error (either crc when reading was bad, or the write got corrupted or something). Typically ignorable, depends how robust you want to be. win32 tls code now supports running as a server. added connect tls://address support, as well as equivalent sv_addport support. exposed basic model loading api to plugins. d3d11 backend now optionally supports tessellation hlsl. no suitable hlsl provided by default. !!tess to enable. attempted to add gamma ramp support for d3d11. added support for shader blobs to speed up load times. r_shaderblobs 1 to enable. almost vital for d3d11. added vid_srgb cvar. shadowless lights are no longer disabled if shadows are not supported. attempt to add support for touchscreens in win7/8. Wrote gimmicky lua support, using lua instead of ssqc. define VM_LUA to enable. updated saved game code. can again load saved games from vanilla-like engines. changed scale clamping. 0.0001 should no longer appear as 1. changed default mintic from 0.03 to 0.013 to match vanilla qw. I don't know why it was at 0.03. probably a typo. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4623 fc73d0e0-1445-4013-8a0c-d673dee63da5
2014-03-30 08:55:06 +00:00
ent.topcolour = TOP_DEFAULT;
ent.bottomcolour = BOTTOM_DEFAULT;
ent.playerindex = -1;
if ((q3->renderfx & Q3RF_LIGHTING_ORIGIN) && ent.model)
{
VectorCopy(q3->lightingOrigin, ent.origin);
scenefuncs->CalcModelLighting(&ent, ent.model);
}
VectorCopy(q3->origin, ent.origin);
VectorCopy(q3->oldorigin, ent.oldorigin);
scenefuncs->AddEntity(&ent);
}
void VQ3_AddPolys(shader_t *s, int numverts, q3polyvert_t *verts, size_t polycount)
{
unsigned int v;
for (; polycount-->0; verts += numverts)
{
vecV_t *vertcoord;
vec2_t *texcoord;
vec4_t *colour;
index_t *indexes;
index_t indexbias = scenefuncs->AddPolydata(s, BEF_NODLIGHT|BEF_NOSHADOWS, numverts, (numverts-2)*3, &vertcoord, &texcoord, &colour, &indexes);
//and splurge the data out.
for (v = 0; v < numverts; v++)
{
VectorCopy(verts[v].org, vertcoord[v]);
Vector2Copy(verts[v].tcoord, texcoord[v]);
Vector4Scale(verts[v].colours, (1/255.0f), colour[v]);
}
for (v = 2; v < numverts; v++)
{
*indexes++ = indexbias + 0;
*indexes++ = indexbias + (v-1);
*indexes++ = indexbias + v;
}
}
}
int VM_LerpTag(void *out, model_t *model, int f1, int f2, float l2, char *tagname)
{
int tagnum;
float *ang;
float *org;
float tr[12];
qboolean found;
framestate_t fstate;
org = (float*)out;
ang = ((float*)out+3);
memset(&fstate, 0, sizeof(fstate));
fstate.g[FS_REG].frame[0] = f1;
fstate.g[FS_REG].frame[1] = f2;
fstate.g[FS_REG].lerpweight[0] = 1 - l2;
fstate.g[FS_REG].lerpweight[1] = l2;
tagnum = scenefuncs->TagNumForName(model, tagname, 0);
found = scenefuncs->GetTag(model, tagnum, &fstate, tr);
if (found && tagnum)
{
ang[0] = tr[0];
ang[1] = tr[1];
ang[2] = tr[2];
org[0] = tr[3];
ang[3] = tr[4];
ang[4] = tr[5];
ang[5] = tr[6];
org[1] = tr[7];
ang[6] = tr[8];
ang[7] = tr[9];
ang[8] = tr[10];
org[2] = tr[11];
return true;
}
else
{
org[0] = 0;
org[1] = 0;
org[2] = 0;
ang[0] = 1;
ang[1] = 0;
ang[2] = 0;
ang[3] = 0;
ang[4] = 1;
ang[5] = 0;
ang[6] = 0;
ang[7] = 0;
ang[8] = 1;
return false;
}
}
#define MAX_RENDER_STRINGS 8
#define MAX_RENDER_STRING_LENGTH 32
struct q3refdef_s {
int x, y, width, height;
float fov_x, fov_y;
vec3_t vieworg;
vec3_t viewaxis[3]; // transformation matrix
// time in milliseconds for shader effects and other time dependent rendering issues
int time;
int rdflags; // RDF_NOWORLDMODEL, etc
// 1 bits will prevent the associated area from rendering at all
qbyte areamask[MAX_MAP_AREA_BYTES];
// text messages for deform text shaders
char text[MAX_RENDER_STRINGS][MAX_RENDER_STRING_LENGTH];
};
void VQ3_RenderView(const q3refdef_t *ref)
{
plugrefdef_t scene;
scene.flags = 0;
scene.rect.x = ref->x;
scene.rect.y = ref->y;
scene.rect.w = ref->width;
scene.rect.h = ref->height;
scene.fov[0] = scene.fov_viewmodel[0] = ref->fov_x;
scene.fov[1] = scene.fov_viewmodel[1] = ref->fov_y;
VectorCopy(ref->viewaxis[0], scene.viewaxisorg[0]);
VectorCopy(ref->viewaxis[1], scene.viewaxisorg[1]);
VectorCopy(ref->viewaxis[2], scene.viewaxisorg[2]);
VectorCopy(ref->vieworg, scene.viewaxisorg[3]);
scene.time = ref->time/1000.0f;
if (ref->rdflags & 1/*Q3RDF_NOWORLDMODEL*/)
scene.flags |= RDF_NOWORLDMODEL;
scenefuncs->RenderScene(&scene, sizeof(ref->areamask), ref->areamask);
}
static void *Little4Block(void *out, qbyte *in, size_t count)
{
while (count-->0)
{
*(unsigned int*)out = (in[0]<<0)|(in[1]<<8)|(in[2]<<16)|(in[3]<<24);
out = (qbyte*)out + 4;
in+=4;
}
return in;
}
void UI_RegisterFont(char *fontName, int pointSize, fontInfo_t *font)
{
union
{
char *c;
int *i;
float *f;
} in;
int i;
char name[MAX_QPATH];
void *filedata;
size_t sz;
snprintf(name, sizeof(name), "fonts/fontImage_%i.dat",pointSize);
in.c = filedata = fsfuncs->LoadFile(name, &sz);
if (sz == sizeof(fontInfo_t))
{
for(i=0; i<GLYPHS_PER_FONT; i++)
{
in.c = Little4Block(&font->glyphs[i].height, in.c, (int*)font->glyphs[i].shaderName-&font->glyphs[i].height);
memcpy(font->glyphs[i].shaderName, in.i, 32);
in.c += 32;
}
in.c = Little4Block(&font->glyphScale, in.c, 1);
memcpy(font->name, in.i, sizeof(font->name));
// Com_Memcpy(font, faceData, sizeof(fontInfo_t));
Q_strncpyz(font->name, name, sizeof(font->name));
for (i = GLYPH_START; i < GLYPH_END; i++)
font->glyphs[i].glyph = drawfuncs->LoadImage(font->glyphs[i].shaderName);
}
plugfuncs->Free(filedata);
}
static struct
{
int shaderhandle;
int x, y, w, h;
qboolean loop;
} uicinematics[16];
int UI_Cin_Play(const char *name, int x, int y, int w, int h, unsigned int flags)
{
int idx;
qhandle_t mediashader;
//cin_t *cin;
for (idx = 0; ; idx++)
{
if (idx == countof(uicinematics))
return -1; //out of handles
if (uicinematics[idx].shaderhandle)
continue; //slot in use
break; //this slot is usable
}
/*mediashader = R_RegisterCustom(name, SUF_NONE, Shader_DefaultCinematic, va("video/%s", name));
if (!mediashader)
return -1; //wtf?
cin = R_ShaderGetCinematic(mediashader);
if (cin)
Media_SetState(cin, CINSTATE_PLAY);
else*/
return -1; //FAIL!
uicinematics[idx].x = x;
uicinematics[idx].y = y;
uicinematics[idx].w = w;
uicinematics[idx].h = h;
uicinematics[idx].loop = !!(flags&1);
uicinematics[idx].shaderhandle = mediashader;
return idx;
}
int UI_Cin_Stop(int idx)
{
if (idx >= 0 && idx < countof(uicinematics))
{
drawfuncs->UnloadImage(uicinematics[idx].shaderhandle);
uicinematics[idx].shaderhandle = 0;
}
return 0;
}
int UI_Cin_Run(int idx)
{
enum {
FMV_IDLE,
FMV_PLAY, // play
FMV_EOF, // all other conditions, i.e. stop/EOF/abort
FMV_ID_BLT,
FMV_ID_IDLE,
FMV_LOOPED,
FMV_ID_WAIT
};
int ret = FMV_IDLE;
if (idx >= 0 && idx < countof(uicinematics))
{
shader_t *shader = drawfuncs->ShaderFromId(uicinematics[idx].shaderhandle);
cin_t *cin = R_ShaderGetCinematic(shader);
if (cin)
{
switch(Media_GetState(cin))
{
case CINSTATE_INVALID: ret = FMV_IDLE; break;
case CINSTATE_PLAY: ret = FMV_PLAY; break;
case CINSTATE_LOOP: ret = FMV_PLAY; break;
case CINSTATE_PAUSE: ret = FMV_PLAY; break;
case CINSTATE_ENDED:
Media_SetState(cin, CINSTATE_FLUSHED);
ret = FMV_EOF;
break;
case CINSTATE_FLUSHED:
//FIXME: roq decoder has no reset method!
Media_Send_Reset(cin);
ret = FMV_LOOPED;
break;
}
}
}
return ret;
}
int UI_Cin_Draw(int idx)
{
if (idx >= 0 && idx < countof(uicinematics))
{
unsigned int size[2];
qhandle_t shader = uicinematics[idx].shaderhandle;
float x = uicinematics[idx].x;
float y = uicinematics[idx].y;
float w = uicinematics[idx].w;
float h = uicinematics[idx].h;
drawfuncs->GetVideoSize(NULL, size);
//gah! q3 compat sucks!
x *= size[0]/640.0;
w *= size[0]/640.0;
y *= size[1]/480.0;
h *= size[1]/480.0;
drawfuncs->Image(x, y, w, h, 0, 0, 1, 1, shader);
}
return 0;
}
int UI_Cin_SetExtents(int idx, int x, int y, int w, int h)
{
if (idx >= 0 && idx < countof(uicinematics))
{
uicinematics[idx].x = x;
uicinematics[idx].y = y;
uicinematics[idx].w = w;
uicinematics[idx].h = h;
}
return 0;
}
static const char *Cvar_FixQ3Name (const char *var_name)
{
struct {
const char *q3;
const char *fte;
} cvarremaps[] =
{
{"s_musicvolume", "bgmvolume"},
{"r_gamma", "gamma"},
{"s_sdlSpeed", "s_khz"},
{"r_fullscreen", "vid_fullscreen"},
{"r_picmip", "gl_picmip"},
{"r_textureMode", "gl_texturemode"},
{"r_lodBias", "d_lodbias"},
{"r_colorbits", "vid_bpp"},
{"r_dynamiclight", "r_dynamic"},
{"r_finish", "gl_finish"},
{"protocol", "com_protocolversion"},
// {"r_glDriver", NULL},
// {"r_depthbits", NULL},
// {"r_stencilbits", NULL},
// {"s_compression", NULL},
// {"r_texturebits", NULL},
// {"r_allowExtensions",NULL},
// {"s_useOpenAL", NULL},
// {"sv_running", NULL},
// {"sv_killserver", NULL},
// {"color1", NULL},
// {"in_joystick", NULL},
// {"joy_threshold", NULL},
// {"cl_freelook", NULL},
// {"color1", NULL},
// {"r_availableModes",NULL},
// {"r_mode", NULL},
};
size_t i;
for (i = 0; i < countof(cvarremaps); i++)
{
if (!strcmp(cvarremaps[i].q3, var_name))
return cvarremaps[i].fte;
}
return var_name;
}
static void UI_SimulateTextEntry(void *cb, const char *utf8)
{
const char *line = utf8;
unsigned int unicode;
int err;
while(*line)
{
unicode = utf8_decode(&err, line, &line);
if (uivm)
vmfuncs->Call(uivm, UI_KEY_EVENT, unicode|1024, true);
}
}
#define VALIDATEPOINTER(o,l) if ((quintptr_t)o + l >= mask || VM_POINTER(o) < offset) plugfuncs->EndGame("Call to ui trap %i passes invalid pointer\n", (int)fn); //out of bounds.
static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, const qintptr_t *arg)
{
static int overstrikemode;
int ret=0;
//Remember to range check pointers.
//The QVM must not be allowed to write to anything outside it's memory.
//This includes getting the exe to copy it for it.
//don't bother with reading, as this isn't a virus risk.
//could be a cheat risk, but hey.
//make sure that any called functions are also range checked.
//like reading from files copies names into alternate buffers, allowing stack screwups.
switch((uiImport_t)fn)
{
case UI_CVAR_CREATE: Con_Printf("Q3UI: Not implemented system trap: %s\n", "UI_CVAR_CREATE"); return 0;
case UI_CVAR_INFOSTRINGBUFFER: Con_Printf("Q3UI: Not implemented system trap: %s\n", "UI_CVAR_INFOSTRINGBUFFER"); return 0;
case UI_R_ADDPOLYTOSCENE: Con_Printf("Q3UI: Not implemented system trap: %s\n", "UI_R_ADDPOLYTOSCENE"); return 0;
case UI_R_REMAP_SHADER: Con_Printf("Q3UI: Not implemented system trap: %s\n", "UI_R_REMAP_SHADER"); return 0;
case UI_UPDATESCREEN: Con_Printf("Q3UI: Not implemented system trap: %s\n", "UI_UPDATESCREEN"); return 0;
case UI_CM_LOADMODEL: Con_Printf("Q3UI: Not implemented system trap: %s\n", "UI_CM_LOADMODEL"); return 0;
case UI_ERROR:
Con_Printf("%s", (char*)VM_POINTER(arg[0]));
UI_Stop();
plugfuncs->EndGame("UI error");
break;
case UI_PRINT:
Con_Printf("%s", (char*)VM_POINTER(arg[0]));
break;
case UI_MILLISECONDS:
VM_LONG(ret) = plugfuncs->GetMilliseconds();
break;
case UI_ARGC:
VM_LONG(ret) = cmdfuncs->Argc();
break;
case UI_ARGV:
// VALIDATEPOINTER(arg[1], arg[2]);
cmdfuncs->Argv(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2]));
break;
/* case UI_ARGS:
VALIDATEPOINTER(arg[0], arg[1]);
Q_strncpyz(VM_POINTER(arg[0]), Cmd_Args(), VM_LONG(arg[1]));
break;
*/
case UI_CVAR_SET:
cvarfuncs->SetString(Cvar_FixQ3Name(VM_POINTER(arg[0])), VM_POINTER(arg[1]));
break;
case UI_CVAR_VARIABLEVALUE:
VM_FLOAT(ret) = cvarfuncs->GetFloat(Cvar_FixQ3Name(VM_POINTER(arg[0])));
break;
case UI_CVAR_VARIABLESTRINGBUFFER:
if (arg[1] + arg[2] >= mask || VM_POINTER(arg[1]) < offset)
VM_LONG(ret) = -2; //out of bounds.
cvarfuncs->GetString(Cvar_FixQ3Name(VM_POINTER(arg[0])), VM_POINTER(arg[1]), VM_LONG(arg[2]));
break;
case UI_CVAR_SETVALUE:
cvarfuncs->SetFloat(Cvar_FixQ3Name(VM_POINTER(arg[0])), VM_FLOAT(arg[1]));
break;
case UI_CVAR_RESET: //cvar reset
cvarfuncs->SetString(Cvar_FixQ3Name(VM_POINTER(arg[0])), NULL);
break;
case UI_CMD_EXECUTETEXT:
{
char *cmdtext = VM_POINTER(arg[1]);
#ifdef CL_MASTER
if (!strncmp(cmdtext, "ping ", 5))
{
char token[1024];
int i;
for (i = 0; i < MAX_PINGREQUESTS; i++)
if (ui_pings[i].adr.type == NA_INVALID && ui_pings[i].adr.prot == NP_INVALID)
{
fixed eztv md4 incompatibility. reimplemented qtvreverse command. fixed some stuffcmds being handled by the wrong splitscreen seats (was noticable in TF). rework smartjump to try to be more predictable... rework relighting to try to be more robust (and more self-contained). allow the csqc to actually use VF_PROJECTIONOFFSET. jump now moves upwards instead of trying to lock on to a nearby player when spectating. assume 32 fullbright pixels when running with a palette.lmp yet no colormap.lmp (happens with some total conversions). tweaked scoreboard for fainter backgrounds. rearranged autoid, to be smaller etc. hacked around dodgy conchars.lmp - don't treat 128*128 qpics as qpics to work around workarounds for buggy wad tools (with a warning). fixed missing fullbrights on h2holey models. avoided warning about mod_h2holey_bugged on dedicated servers. added net_ice_exchangeprivateips, for people worried about exposing lan IPs when using ICE. sv_public 2: implemented client support for our webrtc broker in order to use our own ICE implementation without needing to faff around with irc accounts or plugins etc. TODO: ensure at least one ephemerial udp port when using ice or come up with some better sv_port handling fixed multiple tls bugs (one could cause server problems). change net_enable_tls to disabled by default anyway (reenable for the server to be able to respond to https/wss/tls schemes again). don't colourmap when there appears to be a highres diffusemap on q1 models. imgtool now understands exporting from qpics in wads, as well as just mips. implemented speed-o-meter in ezhud. added removeinstant builtin to avoid the half-second rule. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5614 fc73d0e0-1445-4013-8a0c-d673dee63da5
2020-02-11 18:06:10 +00:00
const char *p = NULL;
cmdfuncs->ParseToken(cmdtext + 5, token, sizeof(token), NULL);
ui_pings[i].startms = plugfuncs->GetMilliseconds();
Q_strncpyz(ui_pings[i].adrstring, token, sizeof(ui_pings[i].adrstring));
if (masterfuncs->StringToAdr(ui_pings[i].adrstring, 0, &ui_pings[i].adr, 1, &p))
{
#ifdef HAVE_PACKET
serverinfo_t *info = masterfuncs->InfoForServer(&ui_pings[i].adr, p);
if (info)
{
info->special |= SS_KEEPINFO;
info->sends++;
masterfuncs->QueryServer(info);
}
#endif
}
ui_pings[i].broker = p;
break;
}
}
else
#endif
cmdfuncs->AddText(cmdtext, false);
}
break;
case UI_FS_FOPENFILE: //fopen
if ((int)arg[1] + 4 >= mask || VM_POINTER(arg[1]) < offset)
break; //out of bounds.
VM_LONG(ret) = VM_fopen(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2]), 0);
break;
case UI_FS_READ: //fread
if ((int)arg[0] + VM_LONG(arg[1]) >= mask || VM_POINTER(arg[0]) < offset)
break; //out of bounds.
VM_LONG(ret) = VM_FRead(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), 0);
break;
case UI_FS_WRITE: //fwrite
break;
case UI_FS_SEEK:
return VM_FSeek(arg[0], arg[1], arg[2], 0);
case UI_FS_FCLOSEFILE: //fclose
VM_fclose(VM_LONG(arg[0]), 0);
break;
case UI_FS_GETFILELIST: //fs listing
if ((int)arg[2] + arg[3] >= mask || VM_POINTER(arg[2]) < offset)
break; //out of bounds.
return VM_GetFileList(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3]));
case UI_R_REGISTERMODEL: //precache model
{
char *name = VM_POINTER(arg[0]);
model_t *mod = scenefuncs->LoadModel(name, MLV_SILENTSYNC);
if (!mod || mod->loadstate != MLS_LOADED || mod->type == mod_dummy)
rewrote ban code, merging bans+nonbans+cuffs+mute+cripple+deaf+lagged+vip. added timeouts. new penalties have no dedicated command. use the addip command for it. maplist command now generates links. implemented skin objects for q3. added a csqc builtin for it. also supports compositing skins. playing demos inside zips/pk3s/paks should now work. bumped default rate cvar. added cl_transfer to attempt to connect to a new server without disconnecting first. rewrote fog command. alpha and mindist arguments are now supported. fog change also happens over a short time period. added new args to the showpic console command. can now create clickable items for touchscreen/absmouse users. fixed menus to properly support right-aligned text. this finally fixes variable-width fonts. rewrote console tab completion suggestions display. now clickable links. strings obtained from qc are now marked as const. this has required quite a few added consts all over the place. probably crappy attempt at adding joypad support to the sdl port. no idea if it works. changed key bind event code. buttons now track which event they should trigger when released, instead of being the same one the whole time. this allows +forward etc clickable buttons on screen. Also simplified modifier keys - they no longer trigger random events when pressing the modifier key itself. Right modifiers can now be bound separately from left modifiers. Right will use left's binding if not otherwise bound. Bind assumes left if there's no prefix. multiplayer->setup->network menu no longer crashes. added rgb colours to the translation view (but not to the colour-changing keys). added modelviewer command to view models. added menu_mods menu to switch mods in a more friendly way. will be shown by default if multiple manifests exist in the binarydir. clamped classic tracer density. scrag particles no longer look quite so buggy. added ifdefs to facilitate a potential winrt port. the engine should now have no extra dependencies, but still needs system code+audio drivers to be written. if it can't set a renderer, it'll now try to use *every* renderer until it finds one that works. added experimental mapcluster server mode (that console command). New maps will be started up as required. rewrote skeletal blending code a bit. added cylinder geomtypes. fix cfg_save writing to the wrong path bug. VFS_CLOSE now returns a boolean. false means there was some sort of fatal error (either crc when reading was bad, or the write got corrupted or something). Typically ignorable, depends how robust you want to be. win32 tls code now supports running as a server. added connect tls://address support, as well as equivalent sv_addport support. exposed basic model loading api to plugins. d3d11 backend now optionally supports tessellation hlsl. no suitable hlsl provided by default. !!tess to enable. attempted to add gamma ramp support for d3d11. added support for shader blobs to speed up load times. r_shaderblobs 1 to enable. almost vital for d3d11. added vid_srgb cvar. shadowless lights are no longer disabled if shadows are not supported. attempt to add support for touchscreens in win7/8. Wrote gimmicky lua support, using lua instead of ssqc. define VM_LUA to enable. updated saved game code. can again load saved games from vanilla-like engines. changed scale clamping. 0.0001 should no longer appear as 1. changed default mintic from 0.03 to 0.013 to match vanilla qw. I don't know why it was at 0.03. probably a typo. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4623 fc73d0e0-1445-4013-8a0c-d673dee63da5
2014-03-30 08:55:06 +00:00
VM_LONG(ret) = 0;
else
VM_LONG(ret) = scenefuncs->ModelToId(mod);
}
break;
case UI_R_MODELBOUNDS:
{
VALIDATEPOINTER(arg[1], sizeof(vec3_t));
VALIDATEPOINTER(arg[2], sizeof(vec3_t));
{
model_t *mod = scenefuncs->ModelFromId(arg[0]);
if (mod)
{
VectorCopy(mod->mins, ((float*)VM_POINTER(arg[1])));
VectorCopy(mod->maxs, ((float*)VM_POINTER(arg[2])));
}
else
{
VectorClear(((float*)VM_POINTER(arg[1])));
VectorClear(((float*)VM_POINTER(arg[2])));
}
}
}
break;
case UI_R_REGISTERSKIN:
VM_LONG(ret) = scenefuncs->RegisterSkinFile(VM_POINTER(arg[0]));
break;
case UI_R_REGISTERFONT: //register font
UI_RegisterFont(VM_POINTER(arg[0]), arg[1], VM_POINTER(arg[2]));
break;
case UI_R_REGISTERSHADERNOMIP:
if (!*(char*)VM_POINTER(arg[0]))
VM_LONG(ret) = 0;
else
VM_LONG(ret) = drawfuncs->LoadImage(VM_POINTER(arg[0]));
if (!drawfuncs->ImageSize(VM_LONG(ret), NULL, NULL))
VM_LONG(ret) = 0; //not found...
break;
case UI_R_CLEARSCENE: //clear scene
scenefuncs->ClearScene();
break;
case UI_R_ADDREFENTITYTOSCENE: //add ent to scene
VQ3_AddEntity(VM_POINTER(arg[0]));
break;
case UI_R_ADDLIGHTTOSCENE: //add light to scene.
break;
case UI_R_RENDERSCENE: //render scene
VQ3_RenderView(VM_POINTER(arg[0]));
break;
case UI_R_SETCOLOR: //setcolour float*
{
float *fl =VM_POINTER(arg[0]);
if (!fl)
drawfuncs->Colour4f(1, 1, 1, 1);
else
drawfuncs->Colour4f(fl[0], fl[1], fl[2], fl[3]);
}
break;
case UI_R_DRAWSTRETCHPIC:
drawfuncs->Image(VM_FLOAT(arg[0]), VM_FLOAT(arg[1]), VM_FLOAT(arg[2]), VM_FLOAT(arg[3]), VM_FLOAT(arg[4]), VM_FLOAT(arg[5]), VM_FLOAT(arg[6]), VM_FLOAT(arg[7]), VM_LONG(arg[8]));
break;
case UI_CM_LERPTAG: //Lerp tag...
VALIDATEPOINTER(arg[0], sizeof(float)*12);
VM_LONG(ret) = VM_LerpTag(VM_POINTER(arg[0]), scenefuncs->ModelFromId(arg[1]), VM_LONG(arg[2]), VM_LONG(arg[3]), VM_FLOAT(arg[4]), VM_POINTER(arg[5]));
break;
case UI_S_REGISTERSOUND:
{
sfx_t *sfx;
sfx = audiofuncs->PrecacheSound(va("%s", (char*)VM_POINTER(arg[0])));
if (sfx)
VM_LONG(ret) = VM_TOSTRCACHE(arg[0]); //return handle is the parameter they just gave
else
VM_LONG(ret) = -1;
}
break;
case UI_S_STARTLOCALSOUND:
if (VM_LONG(arg[0]) != -1 && arg[0])
audiofuncs->LocalSound(VM_FROMSTRCACHE(arg[0]), CHAN_AUTO, 1.0); //now we can fix up the sound name
break;
case UI_S_STARTBACKGROUNDTRACK:
audiofuncs->ChangeMusicTrack(VM_POINTER(arg[0]), VM_POINTER(arg[1]));
break;
case UI_S_STOPBACKGROUNDTRACK:
audiofuncs->ChangeMusicTrack(NULL, NULL);
break;
//q3 shares insert mode between its ui and console. whereas fte doesn't support it (FIXME: add to Key_EntryInsert).
case UI_KEY_SETOVERSTRIKEMODE:
overstrikemode = arg[0];
return 0;
case UI_KEY_GETOVERSTRIKEMODE:
return overstrikemode;
case UI_GETCLIPBOARDDATA:
if (VM_OOB(arg[0], VM_LONG(arg[1])))
break; //out of bounds.
//our clipboard doesn't allow us to simply query without blocking (would result in stalls on x11/wayland)
//so just return nothing - we can send the text as if it were regular text entry for a similar result.
Q_strncpyz(VM_POINTER(arg[0]), "", VM_LONG(arg[1]));
//but do we really want to let mods read the system clipboard? I suppose it SHOULD be okay if the UI was manually installed by the user.
//side note: q3's text entry logic is kinda flawed.
Sys_Clipboard_PasteText(CBT_CLIPBOARD, UI_SimulateTextEntry, NULL);
break;
case UI_KEY_KEYNUMTOSTRINGBUF:
if (VM_LONG(arg[0]) < 0 || VM_LONG(arg[0]) >= K_MAX || (int)arg[1] + VM_LONG(arg[2]) >= mask || VM_POINTER(arg[1]) < offset || VM_LONG(arg[2]) < 1)
break; //out of bounds.
Q_strncpyz(VM_POINTER(arg[1]), inputfuncs->GetKeyName(VM_LONG(arg[0]), 0), VM_LONG(arg[2]));
break;
case UI_KEY_GETBINDINGBUF:
if (VM_LONG(arg[0]) < 0 || VM_LONG(arg[0]) >= K_MAX || (int)arg[1] + VM_LONG(arg[2]) >= mask || VM_POINTER(arg[1]) < offset || VM_LONG(arg[2]) < 1)
break; //out of bounds.
{
const char *binding = inputfuncs->GetKeyBind(0, VM_LONG(arg[0]), 0);
if (binding)
Q_strncpyz(VM_POINTER(arg[1]), binding, VM_LONG(arg[2]));
else
*(char *)VM_POINTER(arg[1]) = '\0';
}
break;
case UI_KEY_SETBINDING:
inputfuncs->SetKeyBind(0, VM_LONG(arg[0]), 0, VM_POINTER(arg[1]));
break;
case UI_KEY_ISDOWN:
VM_LONG(ret) = inputfuncs->IsKeyDown(VM_LONG(arg[0]));
break;
case UI_KEY_CLEARSTATES:
inputfuncs->ClearKeyStates();
break;
case UI_KEY_GETCATCHER:
if (Key_Dest_Has(kdm_console))
VM_LONG(ret) = keycatcher | 1;
else
VM_LONG(ret) = keycatcher;
break;
case UI_KEY_SETCATCHER:
Q3_SetKeyCatcher(VM_LONG(arg[0]));
break;
case UI_GETGLCONFIG: //get glconfig
{
q3glconfig_t *cfg;
float vsize[2] = {0,0};
if ((int)arg[0] + sizeof(q3glconfig_t) >= mask || VM_POINTER(arg[0]) < offset)
break; //out of bounds.
drawfuncs->GetVideoSize(vsize, NULL);
cfg = VM_POINTER(arg[0]);
//do any needed work
memset(cfg, 0, sizeof(*cfg));
Q_strncpyz(cfg->renderer_string, "", sizeof(cfg->renderer_string));
Q_strncpyz(cfg->vendor_string, "", sizeof(cfg->vendor_string));
Q_strncpyz(cfg->version_string, "", sizeof(cfg->version_string));
Q_strncpyz(cfg->extensions_string, "", sizeof(cfg->extensions_string));
cfg->colorBits = 32;
cfg->depthBits = 24;
cfg->stencilBits = 8;//sh_config.stencilbits;
cfg->textureCompression = true;//!!sh_config.texfmt[PTI_BC1_RGBA];
cfg->textureEnvAddAvailable = true;//sh_config.env_add;
//these are the only three that really matter.
cfg->vidWidth = vsize[0];
cfg->vidHeight = vsize[1];
cfg->windowAspect = vsize[0]/vsize[1];;
#if 0
char *cfg;
if ((int)arg[0] + 11332/*sizeof(glconfig_t)*/ >= mask || VM_POINTER(arg[0]) < offset)
break; //out of bounds.
cfg = VM_POINTER(arg[0]);
//do any needed work
memset(cfg, 0, 11304);
*(int *)(cfg+11304) = vsize[0];
*(int *)(cfg+11308) = vid.height;
*(float *)(cfg+11312) = (float)vsize[0]/vid.height;
memset(cfg+11316, 0, 11332-11316);
#endif
}
break;
case UI_GETCLIENTSTATE: //get client state
//fixme: we need to fill in a structure.
// Con_Printf("ui_getclientstate\n");
VALIDATEPOINTER(arg[0], sizeof(uiClientState_t));
{
uiClientState_t *state = VM_POINTER(arg[0]);
state->connectPacketCount = 0;//clc.connectPacketCount;
switch(ccs.state)
{
case ca_disconnected:
if (CL_TryingToConnect())
state->connState = Q3CA_CONNECTING;
else
state->connState = Q3CA_DISCONNECTED;
break;
case ca_demostart:
state->connState = Q3CA_CONNECTING;
break;
case ca_connected:
state->connState = Q3CA_CONNECTED;
break;
case ca_onserver:
state->connState = Q3CA_PRIMED;
break;
case ca_active:
state->connState = Q3CA_ACTIVE;
break;
}
cvarfuncs->GetString("cl_servername", state->servername, sizeof(state->servername)); //error message from game server
Q_strncpyz(state->updateInfoString, "FTE!", sizeof(state->updateInfoString)); //warning/motd message from update server
cvarfuncs->GetString("com_errorMessage", state->messageString, sizeof(state->messageString)); //error message from game server
state->clientNum = ccs.playernum;
}
break;
case UI_GETCONFIGSTRING:
if (arg[1] + VM_LONG(arg[2]) >= mask || VM_POINTER(arg[1]) < offset || VM_LONG(arg[2]) < 1)
{
VM_LONG(ret) = 0;
break; //out of bounds.
}
#ifdef VM_CG
Q_strncpyz(VM_POINTER(arg[1]), CG_GetConfigString(VM_LONG(arg[0])), VM_LONG(arg[2]));
#endif
break;
#ifdef CL_MASTER
case UI_LAN_GETPINGQUEUECOUNT: //these four are master server polling.
{
int i;
for (i = 0; i < MAX_PINGREQUESTS; i++)
if (ui_pings[i].adr.type != NA_INVALID || ui_pings[i].adr.prot != NP_INVALID)
VM_LONG(ret)++;
}
break;
case UI_LAN_CLEARPING: //clear ping
//void (int pingnum)
if (VM_LONG(arg[0])>= 0 && VM_LONG(arg[0]) < MAX_PINGREQUESTS)
ui_pings[VM_LONG(arg[0])].adr.type = NA_INVALID, ui_pings[VM_LONG(arg[0])].adr.prot = NP_INVALID;
break;
case UI_LAN_GETPING:
//void (int pingnum, char *buffer, int buflen, int *ping)
if ((int)arg[1] + VM_LONG(arg[2]) >= mask || VM_POINTER(arg[1]) < offset)
break; //out of bounds.
if ((int)arg[3] + sizeof(int) >= mask || VM_POINTER(arg[3]) < offset)
break; //out of bounds.
masterfuncs->CheckPollSockets();
if (VM_LONG(arg[0])>= 0 && VM_LONG(arg[0]) < MAX_PINGREQUESTS)
{
int i = VM_LONG(arg[0]);
char *buf = VM_POINTER(arg[1]);
size_t bufsize = VM_LONG(arg[2]);
int *ping = VM_POINTER(arg[3]);
serverinfo_t *info;
if (ui_pings[i].adr.type != NA_INVALID || ui_pings[i].adr.prot != NP_INVALID)
info = masterfuncs->InfoForServer(&ui_pings[i].adr, ui_pings[i].broker);
fixed eztv md4 incompatibility. reimplemented qtvreverse command. fixed some stuffcmds being handled by the wrong splitscreen seats (was noticable in TF). rework smartjump to try to be more predictable... rework relighting to try to be more robust (and more self-contained). allow the csqc to actually use VF_PROJECTIONOFFSET. jump now moves upwards instead of trying to lock on to a nearby player when spectating. assume 32 fullbright pixels when running with a palette.lmp yet no colormap.lmp (happens with some total conversions). tweaked scoreboard for fainter backgrounds. rearranged autoid, to be smaller etc. hacked around dodgy conchars.lmp - don't treat 128*128 qpics as qpics to work around workarounds for buggy wad tools (with a warning). fixed missing fullbrights on h2holey models. avoided warning about mod_h2holey_bugged on dedicated servers. added net_ice_exchangeprivateips, for people worried about exposing lan IPs when using ICE. sv_public 2: implemented client support for our webrtc broker in order to use our own ICE implementation without needing to faff around with irc accounts or plugins etc. TODO: ensure at least one ephemerial udp port when using ice or come up with some better sv_port handling fixed multiple tls bugs (one could cause server problems). change net_enable_tls to disabled by default anyway (reenable for the server to be able to respond to https/wss/tls schemes again). don't colourmap when there appears to be a highres diffusemap on q1 models. imgtool now understands exporting from qpics in wads, as well as just mips. implemented speed-o-meter in ezhud. added removeinstant builtin to avoid the half-second rule. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5614 fc73d0e0-1445-4013-8a0c-d673dee63da5
2020-02-11 18:06:10 +00:00
else
info = NULL;
Q_strncpyz(buf, ui_pings[i].adrstring, bufsize);
fixed eztv md4 incompatibility. reimplemented qtvreverse command. fixed some stuffcmds being handled by the wrong splitscreen seats (was noticable in TF). rework smartjump to try to be more predictable... rework relighting to try to be more robust (and more self-contained). allow the csqc to actually use VF_PROJECTIONOFFSET. jump now moves upwards instead of trying to lock on to a nearby player when spectating. assume 32 fullbright pixels when running with a palette.lmp yet no colormap.lmp (happens with some total conversions). tweaked scoreboard for fainter backgrounds. rearranged autoid, to be smaller etc. hacked around dodgy conchars.lmp - don't treat 128*128 qpics as qpics to work around workarounds for buggy wad tools (with a warning). fixed missing fullbrights on h2holey models. avoided warning about mod_h2holey_bugged on dedicated servers. added net_ice_exchangeprivateips, for people worried about exposing lan IPs when using ICE. sv_public 2: implemented client support for our webrtc broker in order to use our own ICE implementation without needing to faff around with irc accounts or plugins etc. TODO: ensure at least one ephemerial udp port when using ice or come up with some better sv_port handling fixed multiple tls bugs (one could cause server problems). change net_enable_tls to disabled by default anyway (reenable for the server to be able to respond to https/wss/tls schemes again). don't colourmap when there appears to be a highres diffusemap on q1 models. imgtool now understands exporting from qpics in wads, as well as just mips. implemented speed-o-meter in ezhud. added removeinstant builtin to avoid the half-second rule. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5614 fc73d0e0-1445-4013-8a0c-d673dee63da5
2020-02-11 18:06:10 +00:00
if (info && /*(info->status & SRVSTATUS_ALIVE) &&*/ info->moreinfo)
{
VM_LONG(ret) = true;
fixed eztv md4 incompatibility. reimplemented qtvreverse command. fixed some stuffcmds being handled by the wrong splitscreen seats (was noticable in TF). rework smartjump to try to be more predictable... rework relighting to try to be more robust (and more self-contained). allow the csqc to actually use VF_PROJECTIONOFFSET. jump now moves upwards instead of trying to lock on to a nearby player when spectating. assume 32 fullbright pixels when running with a palette.lmp yet no colormap.lmp (happens with some total conversions). tweaked scoreboard for fainter backgrounds. rearranged autoid, to be smaller etc. hacked around dodgy conchars.lmp - don't treat 128*128 qpics as qpics to work around workarounds for buggy wad tools (with a warning). fixed missing fullbrights on h2holey models. avoided warning about mod_h2holey_bugged on dedicated servers. added net_ice_exchangeprivateips, for people worried about exposing lan IPs when using ICE. sv_public 2: implemented client support for our webrtc broker in order to use our own ICE implementation without needing to faff around with irc accounts or plugins etc. TODO: ensure at least one ephemerial udp port when using ice or come up with some better sv_port handling fixed multiple tls bugs (one could cause server problems). change net_enable_tls to disabled by default anyway (reenable for the server to be able to respond to https/wss/tls schemes again). don't colourmap when there appears to be a highres diffusemap on q1 models. imgtool now understands exporting from qpics in wads, as well as just mips. implemented speed-o-meter in ezhud. added removeinstant builtin to avoid the half-second rule. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5614 fc73d0e0-1445-4013-8a0c-d673dee63da5
2020-02-11 18:06:10 +00:00
*ping = (info->ping == PING_UNKNOWN)?1:info->ping;
break;
}
i = plugfuncs->GetMilliseconds()-ui_pings[i].startms;
if (i < 999)
i = 0; //don't time out yet.
*ping = i;
}
break;
case UI_LAN_GETPINGINFO:
//void (int pingnum, char *buffer, int buflen)
if ((int)arg[1] + VM_LONG(arg[2]) >= mask || VM_POINTER(arg[1]) < offset)
break; //out of bounds.
if ((int)arg[3] + sizeof(int) >= mask || VM_POINTER(arg[3]) < offset)
break; //out of bounds.
masterfuncs->CheckPollSockets();
if (VM_LONG(arg[0])>= 0 && VM_LONG(arg[0]) < MAX_PINGREQUESTS)
{
int i = VM_LONG(arg[0]);
char *buf = VM_POINTER(arg[1]);
size_t bufsize = VM_LONG(arg[2]);
char *adr;
serverinfo_t *info = masterfuncs->InfoForServer(&ui_pings[i].adr, ui_pings[i].broker);
fixed eztv md4 incompatibility. reimplemented qtvreverse command. fixed some stuffcmds being handled by the wrong splitscreen seats (was noticable in TF). rework smartjump to try to be more predictable... rework relighting to try to be more robust (and more self-contained). allow the csqc to actually use VF_PROJECTIONOFFSET. jump now moves upwards instead of trying to lock on to a nearby player when spectating. assume 32 fullbright pixels when running with a palette.lmp yet no colormap.lmp (happens with some total conversions). tweaked scoreboard for fainter backgrounds. rearranged autoid, to be smaller etc. hacked around dodgy conchars.lmp - don't treat 128*128 qpics as qpics to work around workarounds for buggy wad tools (with a warning). fixed missing fullbrights on h2holey models. avoided warning about mod_h2holey_bugged on dedicated servers. added net_ice_exchangeprivateips, for people worried about exposing lan IPs when using ICE. sv_public 2: implemented client support for our webrtc broker in order to use our own ICE implementation without needing to faff around with irc accounts or plugins etc. TODO: ensure at least one ephemerial udp port when using ice or come up with some better sv_port handling fixed multiple tls bugs (one could cause server problems). change net_enable_tls to disabled by default anyway (reenable for the server to be able to respond to https/wss/tls schemes again). don't colourmap when there appears to be a highres diffusemap on q1 models. imgtool now understands exporting from qpics in wads, as well as just mips. implemented speed-o-meter in ezhud. added removeinstant builtin to avoid the half-second rule. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5614 fc73d0e0-1445-4013-8a0c-d673dee63da5
2020-02-11 18:06:10 +00:00
if (info && /*(info->status & SRVSTATUS_ALIVE) &&*/ info->moreinfo)
{
adr = info->moreinfo->info;
if (!adr)
adr = "";
if (strlen(adr) < bufsize)
{
strcpy(buf, adr);
worldfuncs->SetInfoKey(buf, "mapname", info->map, bufsize);
worldfuncs->SetInfoKey(buf, "hostname", info->name, bufsize);
worldfuncs->SetInfoKey(buf, "g_humanplayers", va("%i", info->numhumans), bufsize);
worldfuncs->SetInfoKey(buf, "sv_maxclients", va("%i", info->maxplayers), bufsize);
worldfuncs->SetInfoKey(buf, "clients", va("%i", info->players), bufsize);
if (info->adr.type == NA_IPV6 && info->adr.prot == NP_DGRAM)
worldfuncs->SetInfoKey(buf, "nettype", "2", bufsize);
else if (info->adr.type == NA_IP && info->adr.prot == NP_DGRAM)
worldfuncs->SetInfoKey(buf, "nettype", "1", bufsize);
else
worldfuncs->SetInfoKey(buf, "nettype", "", bufsize);
VM_LONG(ret) = true;
}
}
else
strcpy(buf, "");
}
break;
case UI_LAN_UPDATEVISIBLEPINGS:
return masterfuncs->QueryServers(); //return true while we're still going.
case UI_LAN_RESETPINGS:
return 0;
case UI_LAN_ADDSERVER:
return 0;
case UI_LAN_REMOVESERVER:
return 0;
case UI_LAN_SERVERSTATUS:
//*address, *status, statuslen
return 0;
case UI_LAN_COMPARESERVERS:
{
hostcachekey_t q3tofte[] = {SLKEY_NAME, SLKEY_MAP, SLKEY_NUMHUMANS, SLKEY_GAMEDIR, SLKEY_PING, 0};
qboolean keyisstring[] = {true, true, false, true, false, false};
//int source = VM_LONG(arg[0]);
int key = bound(0, VM_LONG(arg[1]), countof(q3tofte));
int sortdir = VM_LONG(arg[2]);
serverinfo_t *s1 = masterfuncs->InfoForNum(VM_LONG(arg[3]));
serverinfo_t *s2 = masterfuncs->InfoForNum(VM_LONG(arg[4]));
if (keyisstring[key])
ret = strcasecmp(masterfuncs->ReadKeyString(s2, q3tofte[key]), masterfuncs->ReadKeyString(s1, q3tofte[key]));
else
{
ret = masterfuncs->ReadKeyFloat(s2, q3tofte[key]) - masterfuncs->ReadKeyFloat(s1, q3tofte[key]);
if (ret < 0)
ret = -1;
else if (ret > 0)
ret = 1;
}
if (sortdir)
ret = -ret;
}
return ret;
case UI_LAN_GETSERVERCOUNT: //LAN Get server count
//int (int source)
masterfuncs->CheckPollSockets();
VM_LONG(ret) = masterfuncs->TotalCount();
break;
case UI_LAN_GETSERVERADDRESSSTRING: //LAN get server address
//void (int source, int svnum, char *buffer, int buflen)
if ((int)arg[2] + VM_LONG(arg[3]) >= mask || VM_POINTER(arg[2]) < offset)
break; //out of bounds.
{
char *buf = VM_POINTER(arg[2]);
char *adr;
char adrbuf[MAX_ADR_SIZE];
serverinfo_t *info = masterfuncs->InfoForNum(VM_LONG(arg[1]));
strcpy(buf, "");
if (info)
{
adr = masterfuncs->ServerToString(adrbuf, sizeof(adrbuf), info);
if (strlen(adr) < VM_LONG(arg[3]))
{
strcpy(buf, adr);
VM_LONG(ret) = true;
}
}
}
break;
case UI_LAN_LOADCACHEDSERVERS:
break;
case UI_LAN_SAVECACHEDSERVERS:
break;
case UI_LAN_GETSERVERPING:
return 50;
case UI_LAN_GETSERVERINFO:
if (VM_OOB(arg[2], arg[3]) || !arg[3])
break; //out of bounds.
{
//int source = VM_LONG(arg[0]);
int servernum = VM_LONG(arg[1]);
char *out = VM_POINTER(arg[2]);
int maxsize = VM_LONG(arg[3]);
char adr[MAX_ADR_SIZE];
serverinfo_t *info = masterfuncs->InfoForNum(servernum);
*out = 0;
if (info)
{
worldfuncs->SetInfoKey(out, "hostname", info->name, maxsize);
worldfuncs->SetInfoKey(out, "mapname", info->map, maxsize);
worldfuncs->SetInfoKey(out, "clients", va("%i", info->players), maxsize);
worldfuncs->SetInfoKey(out, "sv_maxclients", va("%i", info->maxplayers), maxsize);
worldfuncs->SetInfoKey(out, "ping", va("%i", info->ping), maxsize);
// worldfuncs->SetInfoKey(out, "minping", info->map, maxsize);
// worldfuncs->SetInfoKey(out, "maxping", info->map, maxsize);
// worldfuncs->SetInfoKey(out, "game", info->map, maxsize);
// worldfuncs->SetInfoKey(out, "gametype", info->map, maxsize);
// worldfuncs->SetInfoKey(out, "nettype", info->map, maxsize);
worldfuncs->SetInfoKey(out, "addr", masterfuncs->AdrToString(adr, sizeof(adr), &info->adr), maxsize);
// worldfuncs->SetInfoKey(out, "punkbuster", info->map, maxsize);
// worldfuncs->SetInfoKey(out, "g_needpass", info->map, maxsize);
// worldfuncs->SetInfoKey(out, "g_humanplayers", info->map, maxsize);
}
}
break;
case UI_LAN_MARKSERVERVISIBLE:
/*not implemented*/
return 0;
case UI_LAN_SERVERISVISIBLE:
return 1;
#endif
case UI_CVAR_REGISTER:
if (VM_OOB(arg[0], sizeof(q3vmcvar_t)))
break; //out of bounds.
return VMQ3_Cvar_Register(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3]));
case UI_CVAR_UPDATE:
if (VM_OOB(arg[0], sizeof(q3vmcvar_t)))
break; //out of bounds.
return VMQ3_Cvar_Update(VM_POINTER(arg[0]));
case UI_MEMORY_REMAINING:
VM_LONG(ret) = 1024*1024*8;//Hunk_LowMemAvailable();
break;
case UI_GET_CDKEY: //get cd key
{
cvar_t *cvar = cvarfuncs->GetNVFDG("cl_cdkey", "", CVAR_ARCHIVE, "Quake3 auth", "Q3 Compat");
char *keydest = VM_POINTER(arg[0]);
if (!cvar || (int)arg[0] + VM_LONG(arg[1]) >= mask || VM_POINTER(arg[0]) < offset)
break; //out of bounds.
Q_strncpyz(keydest, cvar->string, VM_LONG(arg[1]));
}
break;
case UI_SET_CDKEY: //set cd key
{
char *keysrc = VM_POINTER(arg[0]);
if ((int)arg[0] + strlen(keysrc) >= mask || VM_POINTER(arg[0]) < offset)
break; //out of bounds.
cvarfuncs->SetString("cl_cdkey", keysrc);
}
break;
case UI_REAL_TIME:
VALIDATEPOINTER(arg[0], sizeof(q3time_t));
return Q3VM_GetRealtime(VM_POINTER(arg[0]));
case UI_VERIFY_CDKEY:
VM_LONG(ret) = true;
break;
case UI_SET_PBCLSTATUS:
break;
// standard Q3
case UI_MEMSET:
{
void *dest = VM_POINTER(arg[0]);
if ((int)arg[0] + arg[2] >= mask || dest < offset)
break; //out of bounds.
memset(dest, arg[1], arg[2]);
}
break;
case UI_MEMCPY:
{
void *dest = VM_POINTER(arg[0]);
void *src = VM_POINTER(arg[1]);
if ((int)arg[0] + arg[2] >= mask || VM_POINTER(arg[0]) < offset)
break; //out of bounds.
memcpy(dest, src, arg[2]);
}
break;
case UI_STRNCPY:
{
void *dest = VM_POINTER(arg[0]);
void *src = VM_POINTER(arg[1]);
if (arg[0] + arg[2] >= mask || VM_POINTER(arg[0]) < offset)
break; //out of bounds.
Q_strncpyS(dest, src, arg[2]);
}
break;
case UI_SIN:
VM_FLOAT(ret)=(float)sin(VM_FLOAT(arg[0]));
break;
case UI_COS:
VM_FLOAT(ret)=(float)cos(VM_FLOAT(arg[0]));
break;
case UI_ATAN2:
VM_FLOAT(ret)=(float)atan2(VM_FLOAT(arg[0]), VM_FLOAT(arg[1]));
break;
case UI_SQRT:
VM_FLOAT(ret)=(float)sqrt(VM_FLOAT(arg[0]));
break;
case UI_FLOOR:
VM_FLOAT(ret)=(float)floor(VM_FLOAT(arg[0]));
break;
case UI_CEIL:
VM_FLOAT(ret)=(float)ceil(VM_FLOAT(arg[0]));
break;
/*
case UI_GETPLAYERINFO:
if (arg[1] + sizeof(vmuiclientinfo_t) >= mask || VM_POINTER(arg[1]) < offset)
break; //out of bounds.
if (VM_LONG(arg[0]) < -1 || VM_LONG(arg[0] ) >= MAX_CLIENTS)
break;
{
int i = VM_LONG(arg[0]);
vmuiclientinfo_t *vci = VM_POINTER(arg[1]);
if (i == -1)
{
i = cl.playernum[0];
if (i < 0)
{
memset(vci, 0, sizeof(*vci));
return 0;
}
}
vci->bottomcolour = cl.players[i].rbottomcolor;
vci->frags = cl.players[i].frags;
Q_strncpyz(vci->name, cl.players[i].name, UIMAX_SCOREBOARDNAME);
vci->ping = cl.players[i].ping;
vci->pl = cl.players[i].pl;
vci->starttime = cl.players[i].entertime;
vci->topcolour = cl.players[i].rtopcolor;
vci->userid = cl.players[i].userid;
Q_strncpyz(vci->userinfo, cl.players[i].userinfo, sizeof(vci->userinfo));
}
break;
case UI_GETSTAT:
if (VM_LONG(arg[0]) < 0 || VM_LONG(arg[0]) >= MAX_CL_STATS)
VM_LONG(ret) = 0; //invalid stat num.
else
VM_LONG(ret) = cl.stats[0][VM_LONG(arg[0])];
break;
case UI_GETVIDINFO:
{
vidinfo_t *vi;
if (arg[0] + VM_LONG(arg[1]) >= mask || VM_POINTER(arg[1]) < offset)
{
VM_LONG(ret) = 0;
break; //out of bounds.
}
vi = VM_POINTER(arg[0]);
if (VM_LONG(arg[1]) < sizeof(vidinfo_t))
{
VM_LONG(ret) = 0;
break;
}
VM_LONG(ret) = sizeof(vidinfo_t);
vi->width = vsize[0];
vi->height = vsize[1];
vi->refreshrate = 60;
vi->fullscreen = 1;
Q_strncpyz(vi->renderername, q_renderername, sizeof(vi->renderername));
}
break;
*/
#if 1//def BOTLIB_STATIC
case UI_PC_ADD_GLOBAL_DEFINE:
return botlib->PC_AddGlobalDefine(VM_POINTER(arg[0]));
case UI_PC_LOAD_SOURCE:
return botlib->PC_LoadSourceHandle(VM_POINTER(arg[0]));
case UI_PC_FREE_SOURCE:
return botlib->PC_FreeSourceHandle(VM_LONG(arg[0]));
case UI_PC_READ_TOKEN:
return botlib->PC_ReadTokenHandle(VM_LONG(arg[0]), VM_POINTER(arg[1]));
case UI_PC_SOURCE_FILE_AND_LINE:
return botlib->PC_SourceFileAndLine(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]));
#else
case UI_PC_ADD_GLOBAL_DEFINE:
Con_Printf("UI_PC_ADD_GLOBAL_DEFINE not supported\n");
break;
case UI_PC_SOURCE_FILE_AND_LINE:
Script_Get_File_And_Line(arg[0], VM_POINTER(arg[1]), VM_POINTER(arg[2]));
break;
case UI_PC_LOAD_SOURCE:
return Script_LoadFile(VM_POINTER(arg[0]));
case UI_PC_FREE_SOURCE:
Script_Free(arg[0]);
break;
case UI_PC_READ_TOKEN:
//fixme: memory protect.
return Script_Read(arg[0], VM_POINTER(arg[1]));
#endif
case UI_CIN_PLAYCINEMATIC:
return UI_Cin_Play(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VM_LONG(arg[3]), VM_LONG(arg[4]), VM_LONG(arg[5]));
case UI_CIN_STOPCINEMATIC:
return UI_Cin_Stop(VM_LONG(arg[0]));
case UI_CIN_RUNCINEMATIC:
return UI_Cin_Run(VM_LONG(arg[0]));
case UI_CIN_DRAWCINEMATIC:
return UI_Cin_Draw(VM_LONG(arg[0]));
case UI_CIN_SETEXTENTS:
return UI_Cin_SetExtents(VM_LONG(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VM_LONG(arg[3]), VM_LONG(arg[4]));
#ifndef _DEBUG
default:
#endif
Con_Printf("Q3UI: Unknown system trap: %i\n", (int)fn);
return 0;
}
return ret;
}
static int UI_SystemCallsVM(void *offset, quintptr_t mask, int fn, const int *arg)
{ //this is so we can use edit and continue properly (vc doesn't like function pointers for edit+continue)
#if __WORDSIZE == 32
return UI_SystemCalls(offset, mask, fn, (qintptr_t*)arg);
#else
qintptr_t args[9];
args[0]=arg[0];
args[1]=arg[1];
args[2]=arg[2];
args[3]=arg[3];
args[4]=arg[4];
args[5]=arg[5];
args[6]=arg[6];
args[7]=arg[7];
args[8]=arg[8];
return UI_SystemCalls(offset, mask, fn, args);
#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 qintptr_t EXPORT_FN UI_SystemCallsNative(qintptr_t arg, ...)
{
qintptr_t args[9];
va_list argptr;
va_start(argptr, arg);
args[0]=va_arg(argptr, qintptr_t);
args[1]=va_arg(argptr, qintptr_t);
args[2]=va_arg(argptr, qintptr_t);
args[3]=va_arg(argptr, qintptr_t);
args[4]=va_arg(argptr, qintptr_t);
args[5]=va_arg(argptr, qintptr_t);
args[6]=va_arg(argptr, qintptr_t);
args[7]=va_arg(argptr, qintptr_t);
args[8]=va_arg(argptr, qintptr_t);
va_end(argptr);
return UI_SystemCalls(NULL, ~(quintptr_t)0, arg, args);
}
static void UI_Release(menu_t *m, qboolean forced)
{
keycatcher &= ~2;
}
static void UI_DrawMenu(menu_t *m)
{
if (uivm)
{
float vsize[2];
if (drawfuncs->GetVideoSize(vsize, NULL) && (ui_size[0] != vsize[0] || ui_size[1] != vsize[1]))
{ //should probably just rescale stuff instead.
qboolean hadfocus = keycatcher&2;
keycatcher &= ~2;
ui_size[0] = vsize[0];
ui_size[1] = vsize[1];
vmfuncs->Call(uivm, UI_INIT);
if (hadfocus)
{
if (ccs.state)
vmfuncs->Call(uivm, UI_SET_ACTIVE_MENU, 2);
else
vmfuncs->Call(uivm, UI_SET_ACTIVE_MENU, 1);
}
}
vmfuncs->Call(uivm, UI_REFRESH, plugfuncs->GetMilliseconds());
uimenu.isopaque = vmfuncs->Call(uivm, UI_IS_FULLSCREEN);
}
}
qboolean UI_KeyPress(struct menu_s *m, qboolean isdown, unsigned int devid, int key, int unicode)
{
// qboolean result;
if (!uivm)
return false;
// if (key_dest == key_menu)
// return false;
if (!(keycatcher&2))
{
if (key == K_ESCAPE && isdown)
{
UI_OpenMenu();
return true;
}
return false;
}
/* if you change this here, it'll have to be changed in cl_cg.c too */
switch (key)
{
/* all these get interpreted as enter in Q3's UI... */
case K_JOY1:
case K_JOY2:
case K_JOY3:
case K_JOY4:
case K_AUX1:
case K_AUX2:
case K_AUX3:
case K_AUX4:
case K_AUX5:
case K_AUX6:
case K_AUX7:
case K_AUX8:
case K_AUX9:
case K_AUX10:
case K_AUX11:
case K_AUX14:
case K_AUX15:
case K_AUX16:
return true;
break;
/* Q3 doesn't know about these keys, remap them */
case K_GP_START:
key = K_ESCAPE;
break;
case K_GP_DPAD_UP:
key = K_UPARROW;
break;
case K_GP_DPAD_DOWN:
key = K_DOWNARROW;
break;
case K_GP_DPAD_LEFT:
key = K_LEFTARROW;
break;
case K_GP_DPAD_RIGHT:
key = K_RIGHTARROW;
break;
case K_GP_A:
key = K_ENTER;
break;
case K_GP_B:
key = K_ESCAPE;
break;
case K_GP_X:
key = K_BACKSPACE;
break;
}
if (key && key < 1024)
/*result = */vmfuncs->Call(uivm, UI_KEY_EVENT, key, isdown);
if (unicode && unicode < 1024)
/*result = */vmfuncs->Call(uivm, UI_KEY_EVENT, unicode|1024, isdown);
return true;
}
static qboolean UI_MousePosition(struct menu_s *m, qboolean abs, unsigned int devid, float x, float y)
{ //q3ui is a peice of poo and only accepts relative mouse movements.
//which it then clamps arbitrarily
//which means we can't use hardware cursors
//which results in clumsyness when switching between q3ui and everything else.
if (uivm && !abs)
{
int px = x, py = y;
vmfuncs->Call(uivm, UI_MOUSE_EVENT, px, py);
return true;
}
return false;
}
void UI_Reset(void)
{
inputfuncs->Menu_Unlink(&uimenu, true);
if (!drawfuncs->GetVideoSize(NULL,NULL)) //no renderer loaded
UI_Stop();
else if (uivm)
vmfuncs->Call(uivm, UI_INIT);
}
void UI_Stop (void)
{
inputfuncs->Menu_Unlink(&uimenu, true);
if (uivm)
{
vmfuncs->Call(uivm, UI_SHUTDOWN);
vmfuncs->Destroy(uivm);
VM_fcloseall(0);
uivm = NULL;
//mimic Q3 and save the config if anything got changed.
//note that q3 checks every frame. we only check when the ui is closed.
cmdfuncs->AddText("cfg_save_ifmodified\n", true);
#if defined(CL_MASTER)
masterfuncs->WriteServers();
#endif
}
}
void UI_Start (void)
{
int i;
int apiversion;
if (!cl_shownet_ptr)
return; //make sure UI_Init was called. if it wasn't then something messed up and we can't do client stuff.
if (!drawfuncs->GetVideoSize(ui_size,NULL))
return;
UI_Stop();
for (i = 0; i < MAX_PINGREQUESTS; i++)
ui_pings[i].adr.type = NA_INVALID;
uimenu.drawmenu = UI_DrawMenu;
uimenu.mousemove = UI_MousePosition;
uimenu.keyevent = UI_KeyPress;
uimenu.release = UI_Release;
uimenu.lowpriority = true;
SV_InitBotLib();
uivm = vmfuncs->Create("ui", cvarfuncs->GetFloat("com_gamedirnativecode")?UI_SystemCallsNative:NULL, "vm/ui", UI_SystemCallsVM);
if (uivm)
{
apiversion = vmfuncs->Call(uivm, UI_GETAPIVERSION, 6);
if (apiversion != 4 && apiversion != 6) //make sure we can run the thing
{
Con_Printf("User-Interface VM uses incompatible API version (%i)\n", apiversion);
vmfuncs->Destroy(uivm);
VM_fcloseall(0);
uivm = NULL;
return;
}
vmfuncs->Call(uivm, UI_INIT);
UI_OpenMenu();
}
}
void UI_Restart_f(void)
{
char arg1[256];
cmdfuncs->Argv(1, arg1, sizeof(arg1));
UI_Stop();
if (strcmp(arg1, "off"))
{
UI_Start();
if (uivm)
{
if (ccs.state)
vmfuncs->Call(uivm, UI_SET_ACTIVE_MENU, 2);
else
vmfuncs->Call(uivm, UI_SET_ACTIVE_MENU, 1);
}
}
}
qboolean UI_OpenMenu(void)
{
if (!uivm)
UI_Start();
if (uivm)
{
if (ccs.state)
vmfuncs->Call(uivm, UI_SET_ACTIVE_MENU, 2);
else
vmfuncs->Call(uivm, UI_SET_ACTIVE_MENU, 1);
return true;
}
return false;
}
qboolean UI_ConsoleCommand(void)
{
if (uivm)
return vmfuncs->Call(uivm, UI_CONSOLE_COMMAND, plugfuncs->GetMilliseconds());
return false;
}
qboolean UI_IsRunning(void)
{
if (uivm)
return true;
return false;
}
vm_t *UI_GetUIVM(void)
{
return uivm;
}
void UI_Init (void)
{
cl_shownet_ptr = cvarfuncs->GetNVFDG("cl_shownet", "", 0, NULL, "Q3 Compat");
cl_c2sdupe_ptr = cvarfuncs->GetNVFDG("cl_c2sdupe", "", 0, NULL, "Q3 Compat");
cl_nodelta_ptr = cvarfuncs->GetNVFDG("cl_nodelta", "", 0, NULL, "Q3 Compat");
cmdfuncs->AddCommand("ui_restart", UI_Restart_f, "Reload the Q3-based User Interface module");
}
#endif