mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-27 06:02:16 +00:00
503eff6421
I'm not sure if this will break anything. It shouldn't do, but it might. Not everything is ported over yet. Ideally there would be no more use of fopen anywhere else in the engine, and com_gamedir would be made static to fs.c There are a couple of other changes too. http/ftp stuff is currently disabled. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@1728 fc73d0e0-1445-4013-8a0c-d673dee63da5
1643 lines
39 KiB
C
1643 lines
39 KiB
C
#include "quakedef.h"
|
|
#ifdef VM_UI
|
|
#include "ui_public.h"
|
|
#include "cl_master.h"
|
|
|
|
int keycatcher;
|
|
|
|
#include "clq3defs.h"
|
|
|
|
void GLDraw_ShaderImage (int x, int y, int w, int h, float s1, float t1, float s2, float t2, struct shader_s *pic);
|
|
|
|
#define MAX_TOKENLENGTH 1024
|
|
typedef struct pc_token_s
|
|
{
|
|
int type;
|
|
int subtype;
|
|
int intvalue;
|
|
float floatvalue;
|
|
char string[MAX_TOKENLENGTH];
|
|
} pc_token_t;
|
|
#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 filename[MAX_QPATH][SCRIPT_MAXDEPTH];
|
|
int stackdepth;
|
|
|
|
char *defines;
|
|
int numdefines;
|
|
} script_t;
|
|
script_t *scripts;
|
|
int maxscripts;
|
|
#define Q3SCRIPTPUNCTUATION "(,{})(\':;=!><&|+-"
|
|
int Script_Read(int handle, pc_token_t *token)
|
|
{
|
|
int i;
|
|
script_t *sc = scripts+handle-1;
|
|
|
|
for(;;)
|
|
{
|
|
if (!sc->stackdepth)
|
|
return 0;
|
|
|
|
sc->filestack[sc->stackdepth-1] = (char *)COM_ParseToken(sc->filestack[sc->stackdepth-1], Q3SCRIPTPUNCTUATION);
|
|
|
|
if (!strcmp(com_token, "#include"))
|
|
{
|
|
sc->filestack[sc->stackdepth-1] = (char *)COM_ParseToken(sc->filestack[sc->stackdepth-1], Q3SCRIPTPUNCTUATION);
|
|
|
|
if (sc->stackdepth == SCRIPT_MAXDEPTH) //just don't enter it
|
|
continue;
|
|
|
|
if (sc->originalfilestack[sc->stackdepth])
|
|
BZ_Free(sc->originalfilestack[sc->stackdepth]);
|
|
sc->filestack[sc->stackdepth] = sc->originalfilestack[sc->stackdepth] = COM_LoadMallocFile(com_token);
|
|
sc->stackdepth++;
|
|
continue;
|
|
}
|
|
if (!strcmp(com_token, "#define"))
|
|
{
|
|
sc->numdefines++;
|
|
sc->defines = BZ_Realloc(sc->defines, sc->numdefines*SCRIPT_DEFINELENGTH*2);
|
|
sc->filestack[sc->stackdepth-1] = (char *)COM_ParseToken(sc->filestack[sc->stackdepth-1], Q3SCRIPTPUNCTUATION);
|
|
Q_strncpyz(sc->defines+SCRIPT_DEFINELENGTH*2*(sc->numdefines-1), com_token, SCRIPT_DEFINELENGTH);
|
|
sc->filestack[sc->stackdepth-1] = (char *)COM_ParseToken(sc->filestack[sc->stackdepth-1], Q3SCRIPTPUNCTUATION);
|
|
Q_strncpyz(sc->defines+SCRIPT_DEFINELENGTH*2*(sc->numdefines-1)+SCRIPT_DEFINELENGTH, com_token, SCRIPT_DEFINELENGTH);
|
|
|
|
continue;
|
|
}
|
|
if (!*com_token)
|
|
{
|
|
if (sc->stackdepth==0)
|
|
return 0;
|
|
|
|
sc->stackdepth--;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
for (i = 0; i < sc->numdefines; i++)
|
|
{
|
|
if (!strcmp(com_token, sc->defines+SCRIPT_DEFINELENGTH*2*i))
|
|
{
|
|
Q_strncpyz(token->string, sc->defines+SCRIPT_DEFINELENGTH*2*i+SCRIPT_DEFINELENGTH, sizeof(token->string));
|
|
break;
|
|
}
|
|
}
|
|
//fill in the token
|
|
if (i == sc->numdefines)
|
|
Q_strncpyz(token->string, com_token, sizeof(token->string));
|
|
token->intvalue = atoi(token->string);
|
|
token->floatvalue = atof(token->string);
|
|
if (token->floatvalue || *token->string == '0')
|
|
{
|
|
token->type = TT_NUMBER;
|
|
token->subtype = 0;
|
|
}
|
|
else if (com_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 !!*token->string;
|
|
}
|
|
|
|
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 = BZ_Realloc(scripts, sizeof(script_t)*maxscripts);
|
|
}
|
|
|
|
sc = scripts+i;
|
|
memset(sc, 0, sizeof(*sc));
|
|
sc->filestack[0] = sc->originalfilestack[0] = COM_LoadMallocFile(filename);
|
|
sc->stackdepth = 1;
|
|
|
|
return i+1;
|
|
}
|
|
|
|
void Script_Free(int handle)
|
|
{
|
|
int i;
|
|
script_t *sc = scripts+handle-1;
|
|
if (sc->defines)
|
|
BZ_Free(sc->defines);
|
|
|
|
for (i = 0; i < sc->stackdepth; i++)
|
|
BZ_Free(sc->originalfilestack[i]);
|
|
}
|
|
|
|
void Script_Get_File_And_Line(int handle, char *filename, int *line)
|
|
{
|
|
script_t *sc = scripts+handle-1;
|
|
char *src;
|
|
char *start;
|
|
|
|
*line = 0;
|
|
|
|
if (!sc->stackdepth)
|
|
return;
|
|
*line = 1;
|
|
|
|
src = sc->filestack[sc->stackdepth-1];
|
|
start = sc->originalfilestack[sc->stackdepth-1];
|
|
|
|
while(start < src)
|
|
{
|
|
if (*start == '\n')
|
|
(*line)++;
|
|
start++;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef RGLQUAKE
|
|
#include "glquake.h"//hack
|
|
#else
|
|
typedef float m3by3_t[3][3];
|
|
#endif
|
|
|
|
static vm_t *uivm;
|
|
|
|
static char *scr_centerstring;
|
|
|
|
static int ox, oy;
|
|
|
|
void GLDraw_Image(float x, float y, float w, float h, float s1, float t1, float s2, float t2, qpic_t *pic);
|
|
void SWDraw_Image (float xp, float yp, float wp, float hp, float s1, float t1, float s2, float t2, qpic_t *pic);
|
|
char *Get_Q2ConfigString(int i);
|
|
void SWDraw_ImageColours (float r, float g, float b, float a);
|
|
|
|
|
|
#define MAX_PINGREQUESTS 16
|
|
|
|
netadr_t ui_pings[MAX_PINGREQUESTS];
|
|
|
|
#define UITAGNUM 2452
|
|
|
|
|
|
#define MAX_VMUI_FILES 8
|
|
|
|
typedef struct {
|
|
char name[256];
|
|
char *data;
|
|
int bufferlen;
|
|
int len;
|
|
int ofs;
|
|
int accessmode;
|
|
int owner;
|
|
} vmui_fopen_files_t;
|
|
vmui_fopen_files_t vmui_fopen_files[MAX_VMUI_FILES];
|
|
|
|
int VMUI_fopen (char *name, int *handle, int fmode, int owner)
|
|
{
|
|
int i;
|
|
|
|
if (!handle)
|
|
return FS_FLocateFile(name, FSLFRT_LENGTH, NULL);
|
|
|
|
*handle = 0;
|
|
|
|
for (i = 0; i < MAX_VMUI_FILES; i++)
|
|
if (!vmui_fopen_files[i].data)
|
|
break;
|
|
|
|
if (i == MAX_VMUI_FILES) //too many already open
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (name[1] == ':' || //dos filename absolute path specified - reject.
|
|
*name == '\\' || *name == '/' || //absolute path was given - reject
|
|
strstr(name, "..")) //someone tried to be cleaver.
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
Q_strncpyz(vmui_fopen_files[i].name, name, sizeof(vmui_fopen_files[i].name));
|
|
|
|
vmui_fopen_files[i].accessmode = fmode;
|
|
vmui_fopen_files[i].owner = owner;
|
|
switch (fmode)
|
|
{
|
|
case 0: //read
|
|
vmui_fopen_files[i].data = COM_LoadMallocFile(name);
|
|
vmui_fopen_files[i].bufferlen = vmui_fopen_files[i].len = com_filesize;
|
|
vmui_fopen_files[i].ofs = 0;
|
|
if (vmui_fopen_files[i].data)
|
|
break;
|
|
else
|
|
return -1;
|
|
break;
|
|
/*
|
|
case 2: //append
|
|
case 3: //append
|
|
vmui_fopen_files[i].data = COM_LoadMallocFile(name);
|
|
vmui_fopen_files[i].ofs = vmui_fopen_files[i].bufferlen = vmui_fopen_files[i].len = com_filesize;
|
|
if (vmui_fopen_files[i].data)
|
|
break;
|
|
//fall through
|
|
case 1: //write
|
|
vmui_fopen_files[i].bufferlen = 8192;
|
|
vmui_fopen_files[i].data = BZ_Malloc(vmui_fopen_files[i].bufferlen);
|
|
vmui_fopen_files[i].len = 0;
|
|
vmui_fopen_files[i].ofs = 0;
|
|
break;
|
|
*/
|
|
default: //bad
|
|
return -1;
|
|
}
|
|
|
|
*handle = i+1;
|
|
return vmui_fopen_files[i].len;
|
|
}
|
|
|
|
void VMUI_fclose (int fnum, int owner)
|
|
{
|
|
fnum--;
|
|
|
|
if (fnum < 0 || fnum >= MAX_VMUI_FILES)
|
|
return; //out of range
|
|
|
|
if (vmui_fopen_files[fnum].owner != owner)
|
|
return; //cgs?
|
|
|
|
if (!vmui_fopen_files[fnum].data)
|
|
return; //not open
|
|
|
|
switch(vmui_fopen_files[fnum].accessmode)
|
|
{
|
|
case 0:
|
|
BZ_Free(vmui_fopen_files[fnum].data);
|
|
break;
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
COM_WriteFile(vmui_fopen_files[fnum].name, vmui_fopen_files[fnum].data, vmui_fopen_files[fnum].len);
|
|
BZ_Free(vmui_fopen_files[fnum].data);
|
|
break;
|
|
}
|
|
vmui_fopen_files[fnum].data = NULL;
|
|
}
|
|
|
|
void VMUI_FRead (char *dest, int quantity, int fnum, int owner)
|
|
{
|
|
fnum--;
|
|
if (fnum < 0 || fnum >= MAX_VMUI_FILES)
|
|
return; //out of range
|
|
|
|
if (vmui_fopen_files[fnum].owner != owner)
|
|
return; //cgs?
|
|
|
|
if (!vmui_fopen_files[fnum].data)
|
|
return; //not open
|
|
|
|
if (quantity > vmui_fopen_files[fnum].len - vmui_fopen_files[fnum].ofs)
|
|
quantity = vmui_fopen_files[fnum].len - vmui_fopen_files[fnum].ofs;
|
|
memcpy(dest, vmui_fopen_files[fnum].data + vmui_fopen_files[fnum].ofs, quantity);
|
|
vmui_fopen_files[fnum].ofs += quantity;
|
|
|
|
|
|
}
|
|
/*
|
|
void VMUI_fputs (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|
{
|
|
int fnum = G_FLOAT(OFS_PARM0);
|
|
char *msg = PF_VarString(prinst, 1, pr_globals);
|
|
int len = strlen(msg);
|
|
if (fnum < 0 || fnum >= MAX_QC_FILES)
|
|
return; //out of range
|
|
|
|
if (!pf_fopen_files[fnum].data)
|
|
return; //not open
|
|
|
|
if (pf_fopen_files[fnum].prinst != prinst)
|
|
return; //this just isn't ours.
|
|
|
|
if (pf_fopen_files[fnum].bufferlen < pf_fopen_files[fnum].ofs + len)
|
|
{
|
|
char *newbuf;
|
|
pf_fopen_files[fnum].bufferlen = pf_fopen_files[fnum].bufferlen*2 + len;
|
|
newbuf = BZF_Malloc(pf_fopen_files[fnum].bufferlen);
|
|
memcpy(newbuf, pf_fopen_files[fnum].data, pf_fopen_files[fnum].len);
|
|
BZ_Free(pf_fopen_files[fnum].data);
|
|
pf_fopen_files[fnum].data = newbuf;
|
|
}
|
|
|
|
memcpy(pf_fopen_files[fnum].data + pf_fopen_files[fnum].ofs, msg, len);
|
|
if (pf_fopen_files[fnum].len < pf_fopen_files[fnum].ofs + len)
|
|
pf_fopen_files[fnum].len = pf_fopen_files[fnum].ofs + len;
|
|
pf_fopen_files[fnum].ofs+=len;
|
|
}
|
|
*/
|
|
void VMUI_fcloseall (int owner)
|
|
{
|
|
int i;
|
|
for (i = 1; i <= MAX_VMUI_FILES; i++)
|
|
{
|
|
VMUI_fclose(i, owner);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
typedef struct {
|
|
char *initialbuffer;
|
|
char *buffer;
|
|
int found;
|
|
int bufferleft;
|
|
int skip;
|
|
} vmsearch_t;
|
|
int VMEnum(char *match, int size, void *args)
|
|
{
|
|
char *check;
|
|
int newlen;
|
|
match += ((vmsearch_t *)args)->skip;
|
|
newlen = strlen(match)+1;
|
|
if (newlen > ((vmsearch_t *)args)->bufferleft)
|
|
return false; //too many files for the buffer
|
|
|
|
check = ((vmsearch_t *)args)->initialbuffer;
|
|
while(check < ((vmsearch_t *)args)->buffer)
|
|
{
|
|
if (!stricmp(check, match))
|
|
return true; //we found this one already
|
|
check += strlen(check)+1;
|
|
}
|
|
|
|
memcpy(((vmsearch_t *)args)->buffer, match, newlen);
|
|
((vmsearch_t *)args)->buffer+=newlen;
|
|
((vmsearch_t *)args)->bufferleft-=newlen;
|
|
((vmsearch_t *)args)->found++;
|
|
return true;
|
|
}
|
|
|
|
int VMEnumMods(char *match, int size, void *args)
|
|
{
|
|
char *check;
|
|
char desc[1024];
|
|
int newlen;
|
|
int desclen;
|
|
|
|
newlen = strlen(match)+1;
|
|
|
|
if (*match && match[newlen-2] != '/')
|
|
return true;
|
|
match[newlen-2] = '\0';
|
|
|
|
Q_strncpyz(desc, match, sizeof(desc));
|
|
|
|
desclen = strlen(desc)+1;
|
|
|
|
if (newlen+desclen > ((vmsearch_t *)args)->bufferleft)
|
|
return false; //too many files for the buffer
|
|
|
|
check = ((vmsearch_t *)args)->initialbuffer;
|
|
while(check < ((vmsearch_t *)args)->buffer)
|
|
{
|
|
if (!stricmp(check, match))
|
|
return true; //we found this one already
|
|
check += strlen(check)+1;
|
|
check += strlen(check)+1;
|
|
}
|
|
|
|
memcpy(((vmsearch_t *)args)->buffer, match, newlen);
|
|
((vmsearch_t *)args)->buffer+=newlen;
|
|
((vmsearch_t *)args)->bufferleft-=newlen;
|
|
|
|
memcpy(((vmsearch_t *)args)->buffer, desc, desclen);
|
|
((vmsearch_t *)args)->buffer+=desclen;
|
|
((vmsearch_t *)args)->bufferleft-=desclen;
|
|
|
|
((vmsearch_t *)args)->found++;
|
|
return true;
|
|
}
|
|
|
|
int VMQ3_GetFileList(char *path, char *ext, char *output, int buffersize)
|
|
{
|
|
vmsearch_t vms;
|
|
vms.initialbuffer = vms.buffer = output;
|
|
vms.skip = strlen(path)+1;
|
|
vms.bufferleft = buffersize;
|
|
vms.found=0;
|
|
if (*(char *)path == '$')
|
|
{
|
|
vms.skip=0;
|
|
Sys_EnumerateFiles(com_quakedir, "*", VMEnumMods, &vms);
|
|
Sys_EnumerateFiles(com_homedir, "*", VMEnumMods, &vms);
|
|
}
|
|
else if (*(char *)ext == '.' || *(char *)ext == '/')
|
|
COM_EnumerateFiles(va("%s/*%s", path, ext), VMEnum, &vms);
|
|
else
|
|
COM_EnumerateFiles(va("%s/*.%s", path, ext), VMEnum, &vms);
|
|
return vms.found;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct q3refEntity_s {
|
|
refEntityType_t reType;
|
|
int renderfx;
|
|
|
|
struct model_s *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
|
|
int 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;
|
|
} q3refEntity_t;
|
|
|
|
#define Q2RF_VIEWERMODEL 2 // don't draw through eyes, only mirrors
|
|
#define Q2RF_WEAPONMODEL 4 // only draw through eyes
|
|
#define Q2RF_DEPTHHACK 16 // for view weapon Z crunching
|
|
|
|
|
|
#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
|
|
void VQ3_AddEntity(const q3refEntity_t *q3)
|
|
{
|
|
entity_t ent;
|
|
if (!cl_visedicts)
|
|
cl_visedicts = cl_visedicts_list[0];
|
|
memset(&ent, 0, sizeof(ent));
|
|
ent.model = q3->hModel;
|
|
ent.frame = q3->frame;
|
|
ent.oldframe = q3->oldframe;
|
|
memcpy(ent.axis, q3->axis, sizeof(q3->axis));
|
|
ent.lerpfrac = q3->backlerp;
|
|
ent.alpha = 1;
|
|
ent.scale = q3->radius;
|
|
ent.rtype = q3->reType;
|
|
ent.rotation = q3->rotation;
|
|
|
|
if (q3->customSkin)
|
|
ent.skinnum = Mod_SkinForName(ent.model, q3->customSkin);
|
|
#ifdef Q3SHADERS
|
|
ent.forcedshader = (void*)q3->customShader;
|
|
*(int*)ent.shaderRGBA = *(int*)q3->shaderRGBA;
|
|
ent.shaderTime = q3->shaderTime;
|
|
#endif
|
|
if (q3->renderfx & Q3RF_FIRST_PERSON)
|
|
ent.flags |= Q2RF_WEAPONMODEL;
|
|
if (q3->renderfx & Q3RF_DEPTHHACK)
|
|
ent.flags |= Q2RF_DEPTHHACK;
|
|
if (q3->renderfx & Q3RF_THIRD_PERSON)
|
|
ent.flags |= Q2RF_VIEWERMODEL;
|
|
VectorCopy(q3->origin, ent.origin);
|
|
VectorCopy(q3->oldorigin, ent.oldorigin);
|
|
V_AddAxisEntity(&ent);
|
|
}
|
|
|
|
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;
|
|
|
|
org = (float*)out;
|
|
ang = ((float*)out+3);
|
|
|
|
if (Mod_GetTag)
|
|
{
|
|
if (Mod_TagNumForName)
|
|
tagnum = Mod_TagNumForName(model, tagname);
|
|
else
|
|
tagnum = 0;
|
|
found = Mod_GetTag(model, tagnum, f1, f2, l2, 0, 0, tr);
|
|
}
|
|
else
|
|
found = false;
|
|
|
|
if (found)
|
|
{
|
|
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
|
|
|
|
typedef 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];
|
|
} q3refdef_t;
|
|
|
|
void VQ3_RenderView(const q3refdef_t *ref)
|
|
{
|
|
VectorCopy(ref->vieworg, r_refdef.vieworg);
|
|
r_refdef.viewangles[0] = -(atan2(ref->viewaxis[0][2], sqrt(ref->viewaxis[0][1]*ref->viewaxis[0][1]+ref->viewaxis[0][0]*ref->viewaxis[0][0])) * 180 / M_PI);
|
|
r_refdef.viewangles[1] = (atan2(ref->viewaxis[0][1], ref->viewaxis[0][0]) * 180 / M_PI);
|
|
r_refdef.viewangles[2] = 0;
|
|
if (ref->rdflags & 1)
|
|
r_refdef.flags |= Q2RDF_NOWORLDMODEL;
|
|
r_refdef.fov_x = ref->fov_x;
|
|
r_refdef.fov_y = ref->fov_y;
|
|
r_refdef.vrect.x = ref->x;
|
|
r_refdef.vrect.y = ref->y;
|
|
r_refdef.vrect.width = ref->width;
|
|
r_refdef.vrect.height = ref->height;
|
|
r_refdef.time = ref->time/1000.0f;
|
|
r_refdef.useperspective = true;
|
|
r_refdef.currentplayernum = -1;
|
|
|
|
memcpy(cl.q2frame.areabits, ref->areamask, sizeof(cl.q2frame.areabits));
|
|
#ifdef RGLQUAKE
|
|
if (qrenderer == QR_OPENGL)
|
|
{
|
|
gl_ztrickdisabled|=16;
|
|
qglDisable(GL_ALPHA_TEST);
|
|
qglDisable(GL_BLEND);
|
|
}
|
|
#endif
|
|
R_RenderView();
|
|
#ifdef RGLQUAKE
|
|
if (qrenderer == QR_OPENGL)
|
|
{
|
|
gl_ztrickdisabled&=~16;
|
|
GL_Set2D ();
|
|
qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
GL_TexEnv(GL_MODULATE);
|
|
}
|
|
#endif
|
|
|
|
#ifdef RGLQUAKE
|
|
if (qrenderer == QR_OPENGL)
|
|
{
|
|
qglDisable(GL_ALPHA_TEST);
|
|
qglEnable(GL_BLEND);
|
|
}
|
|
#endif
|
|
|
|
vid.recalc_refdef = 1;
|
|
r_refdef.time = 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
void UI_RegisterFont(char *fontName, int pointSize, fontInfo_t *font)
|
|
{
|
|
union
|
|
{
|
|
char *c;
|
|
int *i;
|
|
float *f;
|
|
} in;
|
|
int i;
|
|
char name[MAX_QPATH];
|
|
#define readInt() LittleLong(*in.i++)
|
|
#define readFloat() LittleFloat(*in.f++)
|
|
|
|
_snprintf(name, sizeof(name), "fonts/fontImage_%i.dat",pointSize);
|
|
|
|
in.c = COM_LoadTempFile(name);
|
|
if (com_filesize == sizeof(fontInfo_t))
|
|
{
|
|
for(i=0; i<GLYPHS_PER_FONT; i++)
|
|
{
|
|
font->glyphs[i].height = readInt();
|
|
font->glyphs[i].top = readInt();
|
|
font->glyphs[i].bottom = readInt();
|
|
font->glyphs[i].pitch = readInt();
|
|
font->glyphs[i].xSkip = readInt();
|
|
font->glyphs[i].imageWidth = readInt();
|
|
font->glyphs[i].imageHeight = readInt();
|
|
font->glyphs[i].s = readFloat();
|
|
font->glyphs[i].t = readFloat();
|
|
font->glyphs[i].s2 = readFloat();
|
|
font->glyphs[i].t2 = readFloat();
|
|
font->glyphs[i].glyph = readInt();
|
|
memcpy(font->glyphs[i].shaderName, in.i, 32);
|
|
in.c += 32;
|
|
}
|
|
font->glyphScale = readFloat();
|
|
memcpy(font->name, in.i, MAX_QPATH);
|
|
|
|
// 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 = (int)R_RegisterPic(font->glyphs[i].shaderName);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifndef _DEBUG
|
|
static
|
|
#endif
|
|
long UI_SystemCallsEx(void *offset, unsigned int mask, int fn, const long *arg)
|
|
{
|
|
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_ERROR:
|
|
Con_Printf("%s", VM_POINTER(arg[0]));
|
|
break;
|
|
case UI_PRINT:
|
|
Con_Printf("%s", VM_POINTER(arg[0]));
|
|
break;
|
|
|
|
case UI_MILLISECONDS:
|
|
VM_LONG(ret) = Sys_Milliseconds();
|
|
break;
|
|
|
|
case UI_ARGC:
|
|
VM_LONG(ret) = Cmd_Argc();
|
|
break;
|
|
case UI_ARGV:
|
|
// VALIDATEPOINTER(arg[1], arg[2]);
|
|
Q_strncpyz(VM_POINTER(arg[1]), Cmd_Argv(VM_LONG(arg[0])), 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:
|
|
{
|
|
cvar_t *var;
|
|
var = Cvar_FindVar(VM_POINTER(arg[0]));
|
|
if (var)
|
|
Cvar_Set(var, VM_POINTER(arg[1])); //set it
|
|
else
|
|
Cvar_Get(VM_POINTER(arg[0]), VM_POINTER(arg[1]), 0, "UI created"); //create one
|
|
}
|
|
break;
|
|
case UI_CVAR_VARIABLEVALUE:
|
|
{
|
|
cvar_t *var;
|
|
var = Cvar_FindVar(VM_POINTER(arg[0]));
|
|
if (var)
|
|
VM_FLOAT(ret) = var->value;
|
|
else
|
|
VM_FLOAT(ret) = 0;
|
|
}
|
|
break;
|
|
case UI_CVAR_VARIABLESTRINGBUFFER:
|
|
{
|
|
cvar_t *var;
|
|
var = Cvar_FindVar(VM_POINTER(arg[0]));
|
|
if (!VM_LONG(arg[2]))
|
|
VM_LONG(ret) = 0;
|
|
else if (!var)
|
|
{
|
|
*(char *)VM_POINTER(arg[1]) = '\0';
|
|
VM_LONG(ret) = -1;
|
|
}
|
|
else
|
|
{
|
|
if (arg[1] + arg[2] >= mask || VM_POINTER(arg[1]) < offset)
|
|
VM_LONG(ret) = -2; //out of bounds.
|
|
else
|
|
Q_strncpyz(VM_POINTER(arg[1]), var->string, VM_LONG(arg[2]));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case UI_CVAR_SETVALUE:
|
|
Cvar_SetValue(Cvar_FindVar(VM_POINTER(arg[0])), VM_FLOAT(arg[1]));
|
|
break;
|
|
|
|
case UI_CVAR_RESET: //cvar reset
|
|
{
|
|
cvar_t *var;
|
|
var = Cvar_FindVar((char *)VM_POINTER(arg[0]));
|
|
if (var)
|
|
Cvar_Set(var, var->defaultstr);
|
|
}
|
|
break;
|
|
|
|
case UI_CMD_EXECUTETEXT:
|
|
if (!strncmp(VM_POINTER(arg[1]), "ping ", 5))
|
|
{
|
|
int i;
|
|
for (i = 0; i < MAX_PINGREQUESTS; i++)
|
|
if (ui_pings[i].type == NA_INVALID)
|
|
{
|
|
serverinfo_t *info;
|
|
NET_StringToAdr((char *)VM_POINTER(arg[1]) + 5, &ui_pings[i]);
|
|
info = Master_InfoForServer(ui_pings[i]);
|
|
if (info)
|
|
{
|
|
info->special |= SS_KEEPINFO;
|
|
Master_QueryServer(info);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else if (!strncmp(VM_POINTER(arg[1]), "localservers", 12))
|
|
{
|
|
MasterInfo_Begin();
|
|
}
|
|
/* else if (!strncmp(VM_POINTER(arg[1]), "r_vidmode", 12))
|
|
{
|
|
MasterInfo_Begin();
|
|
}
|
|
*/ else
|
|
Cbuf_AddText(VM_POINTER(arg[1]), RESTRICT_SERVER);
|
|
break;
|
|
|
|
case UI_FS_FOPENFILE: //fopen
|
|
if ((int)arg[1] + 4 >= mask || VM_POINTER(arg[1]) < offset)
|
|
break; //out of bounds.
|
|
VM_LONG(ret) = VMUI_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.
|
|
|
|
VMUI_FRead(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), 0);
|
|
break;
|
|
case UI_FS_WRITE: //fwrite
|
|
break;
|
|
case UI_FS_FCLOSEFILE: //fclose
|
|
VMUI_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 VMQ3_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]);
|
|
VM_LONG(ret) = (int)Mod_ForName(name, false);
|
|
}
|
|
break;
|
|
|
|
case UI_R_REGISTERSKIN:
|
|
{
|
|
char *buf;
|
|
char *skinname = VM_POINTER(arg[0]);
|
|
buf = Z_TagMalloc(strlen(skinname)+1, UITAGNUM);
|
|
strcpy(buf, skinname);
|
|
VM_LONG(ret) = (int)buf; //precache skin - engine ignores these anyway... (for now)
|
|
}
|
|
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 (!Draw_SafeCachePic)
|
|
VM_LONG(ret) = 0;
|
|
else
|
|
// VM_LONG(ret) = (long)Draw_SafeCachePic(VM_POINTER(arg[0]));
|
|
VM_LONG(ret) = (long)R_RegisterPic(VM_POINTER(arg[0]));
|
|
break;
|
|
|
|
case UI_R_CLEARSCENE: //clear scene
|
|
cl_numvisedicts=0;
|
|
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*
|
|
if (Draw_ImageColours)
|
|
{
|
|
float *fl =VM_POINTER(arg[0]);
|
|
if (!fl)
|
|
Draw_ImageColours(1, 1, 1, 1);
|
|
else
|
|
Draw_ImageColours(fl[0], fl[1], fl[2], fl[3]);
|
|
}
|
|
break;
|
|
|
|
case UI_R_DRAWSTRETCHPIC:
|
|
// qglDisable(GL_ALPHA_TEST);
|
|
// qglEnable(GL_BLEND);
|
|
// GL_TexEnv(GL_MODULATE);
|
|
if (Draw_Image)
|
|
GLDraw_ShaderImage(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]), (void *)VM_LONG(arg[8]));
|
|
// Draw_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]), (mpic_t *)VM_LONG(arg[8]));
|
|
break;
|
|
|
|
case UI_CM_LERPTAG: //Lerp tag...
|
|
// tag, model, startFrame, endFrame, frac, tagName
|
|
if ((int)arg[0] + sizeof(float)*12 >= mask || VM_POINTER(arg[0]) < offset)
|
|
break; //out of bounds.
|
|
VM_LerpTag(VM_POINTER(arg[0]), (model_t*)VM_LONG(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 = S_PrecacheSound(va("../%s", VM_POINTER(arg[0])));
|
|
if (sfx)
|
|
VM_LONG(ret) = sfx->name - (char *)offset;
|
|
else
|
|
VM_LONG(ret) = -1;
|
|
}
|
|
break;
|
|
case UI_S_STARTLOCALSOUND:
|
|
if (VM_LONG(arg[0]) != -1 && arg[0])
|
|
S_LocalSound(VM_LONG(arg[0])+(char *)offset);
|
|
break;
|
|
|
|
case UI_KEY_GETOVERSTRIKEMODE:
|
|
return true;
|
|
|
|
case UI_KEY_KEYNUMTOSTRINGBUF:
|
|
if (VM_LONG(arg[0]) < 0 || VM_LONG(arg[0]) > 255 || (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]), Key_KeynumToString(VM_LONG(arg[0])), VM_LONG(arg[2]));
|
|
break;
|
|
|
|
case UI_KEY_GETBINDINGBUF:
|
|
if (VM_LONG(arg[0]) < 0 || VM_LONG(arg[0]) > 255 || (int)arg[1] + VM_LONG(arg[2]) >= mask || VM_POINTER(arg[1]) < offset || VM_LONG(arg[2]) < 1)
|
|
break; //out of bounds.
|
|
|
|
if (keybindings[VM_LONG(arg[0])][0])
|
|
Q_strncpyz(VM_POINTER(arg[1]), keybindings[VM_LONG(arg[0])][0], VM_LONG(arg[2]));
|
|
else
|
|
*(char *)VM_POINTER(arg[1]) = '\0';
|
|
break;
|
|
|
|
case UI_KEY_SETBINDING:
|
|
Key_SetBinding(VM_LONG(arg[0]), ~0, VM_POINTER(arg[1]), RESTRICT_LOCAL);
|
|
break;
|
|
|
|
case UI_KEY_ISDOWN:
|
|
{
|
|
extern qboolean keydown[256];
|
|
if (keydown[VM_LONG(arg[0])])
|
|
VM_LONG(ret) = 1;
|
|
else
|
|
VM_LONG(ret) = 0;
|
|
}
|
|
break;
|
|
|
|
case UI_KEY_CLEARSTATES:
|
|
Key_ClearStates();
|
|
break;
|
|
case UI_KEY_GETCATCHER:
|
|
if (key_dest == key_console)
|
|
VM_LONG(ret) = keycatcher | 1;
|
|
else
|
|
VM_LONG(ret) = keycatcher;
|
|
break;
|
|
case UI_KEY_SETCATCHER:
|
|
keycatcher = VM_LONG(arg[0]);
|
|
break;
|
|
|
|
case UI_GETGLCONFIG: //get glconfig
|
|
if ((int)arg[0] + 11332/*sizeof(glconfig_t)*/ >= mask || VM_POINTER(arg[0]) < offset)
|
|
break; //out of bounds.
|
|
|
|
//do any needed work
|
|
memset(VM_POINTER(arg[0]), 0, 11304);
|
|
*(int *)VM_POINTER(arg[0]+11304) = vid.width;
|
|
*(int *)VM_POINTER(arg[0]+11308) = vid.height;
|
|
*(float *)VM_POINTER(arg[0]+11312) = (float)vid.width/vid.height;
|
|
memset(VM_POINTER(arg[0]+11316), 0, 11332-11316);
|
|
break;
|
|
|
|
case UI_GETCLIENTSTATE: //get client state
|
|
//fixme: we need to fill in a structure.
|
|
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;
|
|
|
|
case UI_LAN_GETPINGQUEUECOUNT: //these four are master server polling.
|
|
{
|
|
int i;
|
|
for (i = 0; i < MAX_PINGREQUESTS; i++)
|
|
if (ui_pings[i].type != NA_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])].type = NA_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.
|
|
|
|
NET_CheckPollSockets();
|
|
if (VM_LONG(arg[0])>= 0 && VM_LONG(arg[0]) <= MAX_PINGREQUESTS)
|
|
{
|
|
char *buf = VM_POINTER(arg[1]);
|
|
char *adr;
|
|
serverinfo_t *info = Master_InfoForServer(ui_pings[VM_LONG(arg[0])]);
|
|
if (info)
|
|
{
|
|
adr = NET_AdrToString(info->adr);
|
|
if (strlen(adr) < VM_LONG(arg[2]))
|
|
{
|
|
strcpy(buf, adr);
|
|
VM_LONG(ret) = true;
|
|
*(int *)VM_POINTER(arg[3]) = info->ping;
|
|
}
|
|
}
|
|
else
|
|
strcpy(buf, "");
|
|
}
|
|
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.
|
|
|
|
NET_CheckPollSockets();
|
|
if (VM_LONG(arg[0])>= 0 && VM_LONG(arg[0]) <= MAX_PINGREQUESTS)
|
|
{
|
|
char *buf = VM_POINTER(arg[1]);
|
|
char *adr;
|
|
serverinfo_t *info = Master_InfoForServer(ui_pings[VM_LONG(arg[0])]);
|
|
if (info)
|
|
{
|
|
adr = info->moreinfo->info;
|
|
if (!adr)
|
|
adr = "";
|
|
if (strlen(adr) < VM_LONG(arg[2]))
|
|
{
|
|
strcpy(buf, adr);
|
|
if (!*Info_ValueForKey(buf, "mapname"))
|
|
{
|
|
Info_SetValueForKey(buf, "mapname", Info_ValueForKey(buf, "map"), VM_LONG(arg[2]));
|
|
Info_RemoveKey(buf, "map");
|
|
}
|
|
Info_SetValueForKey(buf, "sv_maxclients", va("%i", info->maxplayers), VM_LONG(arg[2]));
|
|
Info_SetValueForKey(buf, "clients", va("%i", info->players), VM_LONG(arg[2]));
|
|
VM_LONG(ret) = true;
|
|
}
|
|
}
|
|
else
|
|
strcpy(buf, "");
|
|
}
|
|
break;
|
|
|
|
case UI_CVAR_REGISTER:
|
|
if ((int)arg[0] + sizeof(vmcvar_t) >= mask || VM_POINTER(arg[0]) < offset)
|
|
break; //out of bounds.
|
|
{
|
|
vmcvar_t *vmc;
|
|
cvar_t *var;
|
|
vmc = VM_POINTER(arg[0]);
|
|
var = Cvar_Get(VM_POINTER(arg[1]), VM_POINTER(arg[2]), 0/*VM_LONG(arg[3])*/, "UI cvar");
|
|
if (!vmc)
|
|
break;
|
|
vmc->handle = (char *)var - (char *)offset;
|
|
|
|
vmc->integer = var->value;
|
|
vmc->value = var->value;
|
|
vmc->modificationCount = var->modified;
|
|
Q_strncpyz(vmc->string, var->string, sizeof(vmc->string));
|
|
}
|
|
case UI_CVAR_UPDATE:
|
|
if ((int)arg[0] + sizeof(vmcvar_t) >= mask || VM_POINTER(arg[0]) < offset)
|
|
break; //out of bounds.
|
|
{
|
|
cvar_t *var;
|
|
vmcvar_t *vmc;
|
|
vmc = VM_POINTER(arg[0]);
|
|
var = (cvar_t *)((int)vmc->handle + (char *)offset);
|
|
|
|
vmc->integer = var->value;
|
|
vmc->value = var->value;
|
|
vmc->modificationCount = var->modified;
|
|
Q_strncpyz(vmc->string, var->string, sizeof(vmc->string));
|
|
}
|
|
break;
|
|
|
|
case UI_MEMORY_REMAINING:
|
|
VM_LONG(ret) = Hunk_LowMemAvailable();
|
|
break;
|
|
|
|
case UI_GET_CDKEY: //get cd key
|
|
if ((int)arg[0] + VM_LONG(arg[1]) >= mask || VM_POINTER(arg[0]) < offset)
|
|
break; //out of bounds.
|
|
strncpy(VM_POINTER(arg[0]), Cvar_VariableString("cl_cdkey"), VM_LONG(arg[1]));
|
|
break;
|
|
case UI_SET_CDKEY: //set cd key
|
|
if ((int)arg[0] + strlen(VM_POINTER(arg[0])) >= mask || VM_POINTER(arg[0]) < offset)
|
|
break; //out of bounds.
|
|
{
|
|
cvar_t *cvar;
|
|
cvar = Cvar_Get("cl_cdkey", "", 0, "Quake3 auth");
|
|
Cvar_Set(cvar, VM_POINTER(arg[0]));
|
|
}
|
|
break;
|
|
|
|
case UI_REAL_TIME:
|
|
VM_FLOAT(ret) = realtime;
|
|
break;
|
|
|
|
case UI_LAN_GETSERVERCOUNT: //LAN Get server count
|
|
//int (int source)
|
|
VM_LONG(ret) = Master_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;
|
|
serverinfo_t *info = Master_InfoForNum(VM_LONG(arg[1]));
|
|
if (info)
|
|
{
|
|
adr = NET_AdrToString(info->adr);
|
|
if (strlen(adr) < VM_LONG(arg[3]))
|
|
{
|
|
strcpy(buf, adr);
|
|
VM_LONG(ret) = true;
|
|
}
|
|
}
|
|
else
|
|
strcpy(buf, "");
|
|
}
|
|
break;
|
|
case UI_LAN_LOADCACHEDSERVERS:
|
|
break;
|
|
case UI_LAN_SAVECACHEDSERVERS:
|
|
break;
|
|
case UI_LAN_GETSERVERPING:
|
|
return 50;
|
|
case UI_LAN_GETSERVERINFO:
|
|
break;
|
|
case UI_LAN_SERVERISVISIBLE:
|
|
return 1;
|
|
break;
|
|
|
|
case UI_VERIFY_CDKEY:
|
|
VM_LONG(ret) = true;
|
|
break;
|
|
|
|
case UI_SET_PBCLSTATUS:
|
|
break;
|
|
|
|
// standard Q3
|
|
case UI_MEMSET:
|
|
if ((int)arg[0] + arg[2] >= mask || VM_POINTER(arg[0]) < offset)
|
|
break; //out of bounds.
|
|
memset(VM_POINTER(arg[0]), arg[1], arg[2]);
|
|
break;
|
|
case UI_MEMCPY:
|
|
if ((int)arg[0] + arg[2] >= mask || VM_POINTER(arg[0]) < offset)
|
|
break; //out of bounds.
|
|
memcpy(VM_POINTER(arg[0]), VM_POINTER(arg[1]), arg[2]);
|
|
break;
|
|
case UI_STRNCPY:
|
|
if (arg[0] + arg[2] >= mask || VM_POINTER(arg[0]) < offset)
|
|
break; //out of bounds.
|
|
Q_strncpyS(VM_POINTER(arg[0]), VM_POINTER(arg[1]), 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_CACHE_PIC:
|
|
if (!Draw_SafeCachePic)
|
|
VM_LONG(ret) = 0;
|
|
else
|
|
VM_LONG(ret) = (long)Draw_SafeCachePic(VM_POINTER(arg[0]));
|
|
break;
|
|
case UI_PICFROMWAD:
|
|
if (!Draw_SafePicFromWad)
|
|
VM_LONG(ret) = 0;
|
|
else
|
|
VM_LONG(ret) = (long)Draw_SafePicFromWad(VM_POINTER(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].bottomcolor;
|
|
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].topcolor;
|
|
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 = vid.width;
|
|
vi->height = vid.height;
|
|
#ifdef SWQUAKE
|
|
vi->bpp = r_pixbytes;
|
|
#endif
|
|
vi->refreshrate = 60;
|
|
vi->fullscreen = 1;
|
|
Q_strncpyz(vi->renderername, q_renderername, sizeof(vi->renderername));
|
|
}
|
|
break;
|
|
case UI_GET_STRING:
|
|
{
|
|
char *str = NULL;
|
|
switch (arg[0])
|
|
{
|
|
case SID_Q2STATUSBAR:
|
|
str = cl.q2statusbar;
|
|
break;
|
|
case SID_Q2LAYOUT:
|
|
str = cl.q2layout;
|
|
break;
|
|
case SID_CENTERPRINTTEXT:
|
|
str = scr_centerstring;
|
|
break;
|
|
case SID_SERVERNAME:
|
|
str = cls.servername;
|
|
break;
|
|
|
|
default:
|
|
str = Get_Q2ConfigString(arg[0]);
|
|
break;
|
|
}
|
|
if (!str)
|
|
return -1;
|
|
|
|
if (arg[1] + arg[2] >= mask || VM_POINTER(arg[1]) < offset)
|
|
return -1; //out of bounds.
|
|
|
|
if (strlen(str)>= arg[2])
|
|
return -1;
|
|
|
|
strcpy(VM_POINTER(arg[1]), str); //already made sure buffer is big enough
|
|
|
|
return strlen(str);
|
|
}
|
|
|
|
|
|
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]));
|
|
|
|
default:
|
|
Con_Printf("Q3UI: Not implemented system trap: %d\n", fn);
|
|
return 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
static long UI_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 UI_SystemCallsEx(offset, mask, fn, arg);
|
|
}
|
|
#define UI_SystemCallsEx UI_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 UI_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);
|
|
|
|
return UI_SystemCallsEx(NULL, ~0, arg, args);
|
|
}
|
|
|
|
qboolean UI_DrawStatusBar(int scores)
|
|
{
|
|
if (!uivm)
|
|
return false;
|
|
|
|
return VM_Call(uivm, UI_DRAWSTATUSBAR, scores);
|
|
}
|
|
|
|
qboolean UI_DrawFinale(void)
|
|
{
|
|
if (!uivm)
|
|
return false;
|
|
|
|
return VM_Call(uivm, UI_FINALE);
|
|
}
|
|
|
|
qboolean UI_DrawIntermission(void)
|
|
{
|
|
if (!uivm)
|
|
return false;
|
|
|
|
return VM_Call(uivm, UI_INTERMISSION);
|
|
}
|
|
|
|
void UI_DrawMenu(void)
|
|
{
|
|
if (uivm)
|
|
{
|
|
VM_Call(uivm, UI_REFRESH, (int)(realtime * 1000));
|
|
if (keycatcher&2 && key_dest != key_console)
|
|
key_dest = key_game;
|
|
}
|
|
}
|
|
|
|
qboolean UI_CenterPrint(char *text, qboolean finale)
|
|
{
|
|
scr_centerstring = text;
|
|
|
|
if (!uivm)
|
|
return false;
|
|
|
|
return VM_Call(uivm, UI_STRINGCHANGED, SID_CENTERPRINTTEXT);
|
|
}
|
|
|
|
qboolean UI_Q2LayoutChanged(void)
|
|
{
|
|
if (!uivm)
|
|
return false;
|
|
|
|
return VM_Call(uivm, UI_STRINGCHANGED, SID_CENTERPRINTTEXT);
|
|
}
|
|
|
|
void UI_StringChanged(int num)
|
|
{
|
|
if (uivm)
|
|
VM_Call(uivm, UI_STRINGCHANGED, num);
|
|
}
|
|
|
|
void UI_Reset(void)
|
|
{
|
|
keycatcher &= ~2;
|
|
|
|
if (!Draw_SafeCachePic || qrenderer != QR_OPENGL) //no renderer loaded
|
|
UI_Stop();
|
|
else if (uivm)
|
|
VM_Call(uivm, UI_INIT);
|
|
}
|
|
|
|
int UI_MenuState(void)
|
|
{
|
|
if (key_dest == key_menu)
|
|
{
|
|
return false;
|
|
}
|
|
if (!uivm)
|
|
return false;
|
|
if (VM_Call(uivm, UI_IS_FULLSCREEN))
|
|
return 2;
|
|
else if (keycatcher&2)
|
|
return 3;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
qboolean UI_KeyPress(int key, qboolean down)
|
|
{
|
|
extern qboolean keydown[256];
|
|
extern int keyshift[256]; // key to map to if shift held down in console
|
|
// qboolean result;
|
|
if (!uivm)
|
|
return false;
|
|
// if (key_dest == key_menu)
|
|
// return false;
|
|
if (!(keycatcher&2))
|
|
{
|
|
if (key == K_ESCAPE && down)
|
|
{
|
|
if (cls.state)
|
|
return VM_Call(uivm, UI_SET_ACTIVE_MENU, 2)>0;
|
|
else
|
|
return VM_Call(uivm, UI_SET_ACTIVE_MENU, 1)>0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (keydown[K_SHIFT])
|
|
key = keyshift[key];
|
|
if (key < K_BACKSPACE && key >= ' ')
|
|
key |= 1024;
|
|
|
|
/*result = */VM_Call(uivm, UI_KEY_EVENT, key, down);
|
|
|
|
if (!keycatcher && !cls.state && key == K_ESCAPE && down)
|
|
{
|
|
M_Menu_Main_f();
|
|
return true;
|
|
}
|
|
|
|
return true;
|
|
|
|
// return result;
|
|
}
|
|
|
|
void UI_MousePosition(int xpos, int ypos)
|
|
{
|
|
if (uivm && (ox != xpos || oy != ypos))
|
|
{
|
|
if (xpos < 0)
|
|
xpos = 0;
|
|
if (ypos < 0)
|
|
ypos = 0;
|
|
if (xpos > vid.width)
|
|
xpos = vid.width;
|
|
if (ypos > vid.height)
|
|
ypos = vid.height;
|
|
ox=0;oy=0;
|
|
//force a cap
|
|
VM_Call(uivm, UI_MOUSE_EVENT, -32767, -32767);
|
|
VM_Call(uivm, UI_MOUSE_EVENT, (xpos-ox)*640/vid.width, (ypos-oy)*480/vid.height);
|
|
ox = xpos;
|
|
oy = ypos;
|
|
|
|
}
|
|
}
|
|
|
|
void UI_Stop (void)
|
|
{
|
|
keycatcher &= ~2;
|
|
if (uivm)
|
|
{
|
|
VM_Call(uivm, UI_SHUTDOWN);
|
|
VM_Destroy(uivm);
|
|
VMUI_fcloseall(0);
|
|
uivm = NULL;
|
|
}
|
|
}
|
|
|
|
void UI_Start (void)
|
|
{
|
|
int apiversion;
|
|
if (!Draw_SafeCachePic) //no renderer loaded
|
|
return;
|
|
|
|
if (qrenderer != QR_OPENGL)
|
|
return;
|
|
|
|
uivm = VM_Create(NULL, "vm/qwui", UI_SystemCalls, UI_SystemCallsEx);
|
|
if (!uivm) //broken currently, I believe.
|
|
uivm = VM_Create(NULL, "vm/ui", UI_SystemCalls, UI_SystemCallsEx);
|
|
if (uivm)
|
|
{
|
|
apiversion = VM_Call(uivm, UI_GETAPIVERSION, UI_API_VERSION);
|
|
if (apiversion == UI_API_VERSION)
|
|
keycatcher = 0;
|
|
else if (apiversion != 4 && apiversion != 6) //make sure we can run the thing
|
|
{
|
|
Con_Printf("User-Interface VM uses incompatable API version (%i)\n", apiversion);
|
|
VM_Destroy(uivm);
|
|
VMUI_fcloseall(0);
|
|
uivm = NULL;
|
|
return;
|
|
}
|
|
VM_Call(uivm, UI_INIT);
|
|
|
|
VM_Call(uivm, UI_MOUSE_EVENT, -32767, -32767);
|
|
ox = 0;
|
|
oy = 0;
|
|
|
|
VM_Call(uivm, UI_SET_ACTIVE_MENU, 1);
|
|
}
|
|
}
|
|
|
|
void UI_Restart_f(void)
|
|
{
|
|
UI_Stop();
|
|
UI_Start();
|
|
|
|
if (uivm)
|
|
{
|
|
if (cls.state)
|
|
VM_Call(uivm, UI_SET_ACTIVE_MENU, 2);
|
|
else
|
|
VM_Call(uivm, UI_SET_ACTIVE_MENU, 1);
|
|
}
|
|
}
|
|
|
|
qboolean UI_Command(void)
|
|
{
|
|
if (uivm)
|
|
return VM_Call(uivm, UI_CONSOLE_COMMAND);
|
|
return false;
|
|
}
|
|
|
|
void UI_Init (void)
|
|
{
|
|
Cmd_AddRemCommand("ui_restart", UI_Restart_f);
|
|
UI_Start();
|
|
}
|
|
#endif
|
|
|