#include "quakedef.h" //#include "cg_public.h" #ifdef VM_CG #include "shader.h" #if 1 #include "glquake.h"//hack #else typedef float m3by3_t[3][3]; #endif #include "clq3defs.h" //cl_ui.c void CG_Command_f(void); #define CGAME_IMPORT_API_VERSION 4 #define CGTAGNUM 5423 #define VM_TOSTRCACHE(a) VMQ3_StringToHandle(VM_POINTER(a)) #define VM_FROMSTRCACHE(a) VMQ3_StringFromHandle(a) char *VMQ3_StringFromHandle(int handle); int VMQ3_StringToHandle(char *str); extern model_t mod_known[]; extern int mod_numknown; #define VM_FROMMHANDLE(a) ((a&&((unsigned int)a)<=mod_numknown)?mod_known+a-1:NULL) #define VM_TOMHANDLE(a) (a?a-mod_known+1:0) #define VM_FROMSHANDLE(a) (a?r_shaders+a-1:NULL) #define VM_TOSHANDLE(a) (a?a-r_shaders+1:0) extern model_t box_model; typedef enum { CG_PRINT, CG_ERROR, CG_MILLISECONDS, CG_CVAR_REGISTER, CG_CVAR_UPDATE, CG_CVAR_SET, CG_CVAR_VARIABLESTRINGBUFFER, CG_ARGC, CG_ARGV, CG_ARGS, CG_FS_FOPENFILE, //10 CG_FS_READ, CG_FS_WRITE, CG_FS_FCLOSEFILE, CG_SENDCONSOLECOMMAND, CG_ADDCOMMAND, CG_SENDCLIENTCOMMAND, CG_UPDATESCREEN, CG_CM_LOADMAP, CG_CM_NUMINLINEMODELS, CG_CM_INLINEMODEL, //20 CG_CM_LOADMODEL, CG_CM_TEMPBOXMODEL, CG_CM_POINTCONTENTS, CG_CM_TRANSFORMEDPOINTCONTENTS, CG_CM_BOXTRACE, CG_CM_TRANSFORMEDBOXTRACE, CG_CM_MARKFRAGMENTS, CG_S_STARTSOUND, CG_S_STARTLOCALSOUND, CG_S_CLEARLOOPINGSOUNDS, //30 CG_S_ADDLOOPINGSOUND, CG_S_UPDATEENTITYPOSITION, CG_S_RESPATIALIZE, CG_S_REGISTERSOUND, CG_S_STARTBACKGROUNDTRACK, CG_R_LOADWORLDMAP, CG_R_REGISTERMODEL, CG_R_REGISTERSKIN, CG_R_REGISTERSHADER, CG_R_CLEARSCENE, //40 CG_R_ADDREFENTITYTOSCENE, CG_R_ADDPOLYTOSCENE, CG_R_ADDLIGHTTOSCENE, CG_R_RENDERSCENE, CG_R_SETCOLOR, CG_R_DRAWSTRETCHPIC, CG_R_MODELBOUNDS, CG_R_LERPTAG, CG_GETGLCONFIG, CG_GETGAMESTATE, //50 CG_GETCURRENTSNAPSHOTNUMBER, CG_GETSNAPSHOT, CG_GETSERVERCOMMAND, CG_GETCURRENTCMDNUMBER, CG_GETUSERCMD, CG_SETUSERCMDVALUE, CG_R_REGISTERSHADERNOMIP, CG_MEMORY_REMAINING, CG_R_REGISTERFONT, CG_KEY_ISDOWN, //60 CG_KEY_GETCATCHER, CG_KEY_SETCATCHER, CG_KEY_GETKEY, CG_PC_ADD_GLOBAL_DEFINE, CG_PC_LOAD_SOURCE, CG_PC_FREE_SOURCE, CG_PC_READ_TOKEN, CG_PC_SOURCE_FILE_AND_LINE, CG_S_STOPBACKGROUNDTRACK, CG_REAL_TIME, //70 CG_SNAPVECTOR, CG_REMOVECOMMAND, CG_R_LIGHTFORPOINT, //73 CG_CIN_PLAYCINEMATIC, //64 CG_CIN_STOPCINEMATIC, //75 CG_CIN_RUNCINEMATIC, //76 CG_CIN_DRAWCINEMATIC, //77 CG_CIN_SETEXTENTS, //78 CG_R_REMAP_SHADER, //79 CG_S_ADDREALLOOPINGSOUND, //80 CG_S_STOPLOOPINGSOUND, //81 CG_CM_TEMPCAPSULEMODEL, //82 CG_CM_CAPSULETRACE, //83 CG_CM_TRANSFORMEDCAPSULETRACE, //84 CG_R_ADDADDITIVELIGHTTOSCENE, //85 CG_GET_ENTITY_TOKEN, //86 CG_R_ADDPOLYSTOSCENE, //87 CG_R_INPVS, //88 // 1.32 CG_FS_SEEK, //89 CG_MEMSET = 100, CG_MEMCPY, CG_STRNCPY, CG_SIN, CG_COS, CG_ATAN2, CG_SQRT, CG_FLOOR, CG_CEIL, CG_TESTPRINTINT, CG_TESTPRINTFLOAT, CG_ACOS, CG_FTE_FINDPARTICLEEFFECT = 200, CG_FTE_SPAWNPARTICLEEFFECT, CG_FTE_SPAWNPARTICLETRAIL, CG_FTE_FREEPARTICLESTATE } cgameImport_t; /* int CG_FTE_FINDPARTICLEEFFECT(char *particleeffectname) = #200; int CG_FTE_SPAWNPARTICLEEFFECT(vec3_t pos, vec3_t dir, float count, int effectnum, void *pstate) = #201; int CG_FTE_SPAWNPARTICLETRAIL(vec3_t start, vec3_t end, int effectnum, void *pstate) = #202; void CG_FTE_FREEPARTICLESTATE(void *pstate) = #203; */ /* ================================================================== functions exported to the main executable ================================================================== */ typedef enum { CG_INIT, // void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum ) // called when the level loads or when the renderer is restarted // all media should be registered at this time // cgame will display loading status by calling SCR_Update, which // will call CG_DrawInformation during the loading process // reliableCommandSequence will be 0 on fresh loads, but higher for // demos, tourney restarts, or vid_restarts CG_SHUTDOWN, // void (*CG_Shutdown)( void ); // oportunity to flush and close any open files CG_CONSOLE_COMMAND, // qboolean (*CG_ConsoleCommand)( void ); // a console command has been issued locally that is not recognized by the // main game system. // use Cmd_Argc() / Cmd_Argv() to read the command, return qfalse if the // command is not known to the game CG_DRAW_ACTIVE_FRAME, // void (*CG_DrawActiveFrame)( int serverTime, stereoFrame_t stereoView, qboolean demoPlayback ); // Generates and draws a game scene and status information at the given time. // If demoPlayback is set, local movement prediction will not be enabled CG_CROSSHAIR_PLAYER, // int (*CG_CrosshairPlayer)( void ); CG_LAST_ATTACKER, // int (*CG_LastAttacker)( void ); CG_KEY_EVENT, // void (*CG_KeyEvent)( int key, qboolean down ); CG_MOUSE_EVENT, // void (*CG_MouseEvent)( int dx, int dy ); CG_EVENT_HANDLING // void (*CG_EventHandling)(int type); } cgameExport_t; unsigned int Contents_To_Q3(unsigned int fte) { int ret = 0; if (fte & FTECONTENTS_SOLID) //should use q3 constants. ret |= 1; if (fte & FTECONTENTS_WATER) //should use q3 constants. ret |= 32; if (fte & FTECONTENTS_SLIME) //should use q3 constants. ret |= 16; if (fte & FTECONTENTS_LAVA) //should use q3 constants. ret |= 8; if (fte & FTECONTENTS_SKY) //should use q3 constants. ret |= 0x80000000; return ret; } unsigned int Contents_From_Q3(unsigned int Q3) { int ret = 0; if (Q3 & 1) //should use q3 constants. ret |= FTECONTENTS_SOLID; if (Q3 & 32) //should use q3 constants. ret |= FTECONTENTS_WATER; if (Q3 & 16) //should use q3 constants. ret |= FTECONTENTS_SLIME; if (Q3 & 8) //should use q3 constants. ret |= FTECONTENTS_LAVA; if (Q3 & 0x80000000) //should use q3 constants. ret |= FTECONTENTS_SKY; return ret; } #define MAX_GAMESTATE_CHARS 16000 #define MAX_CONFIGSTRINGS 1024 typedef struct { int stringOffsets[MAX_CONFIGSTRINGS]; char stringData[MAX_GAMESTATE_CHARS]; int dataCount; } gameState_t; gameState_t cggamestate; void CG_InsertIntoGameState(int num, char *str) { if (num < 5) { Con_DPrintf("%i: %s", num, str); } if (num == CFGSTR_SYSINFO) { //check some things. cl.servercount = atoi(Info_ValueForKey(str, "sv_serverid")); } if (cggamestate.dataCount + strlen(str)+1 > MAX_GAMESTATE_CHARS) { char oldstringData[MAX_GAMESTATE_CHARS]; int i; char *oldstr; //copy the old strings to a temporary buffer memcpy(oldstringData, cggamestate.stringData, MAX_GAMESTATE_CHARS); cggamestate.dataCount = 0; for (i = 0; i < MAX_CONFIGSTRINGS; i++) { oldstr = oldstringData+cggamestate.stringOffsets[i]; if (*oldstr) { if (cggamestate.dataCount + strlen(oldstr)+1 > MAX_GAMESTATE_CHARS) Host_EndGame("Too much configstring text\n"); cggamestate.dataCount+=1; strcpy(cggamestate.stringData+cggamestate.dataCount, oldstr); cggamestate.stringOffsets[i] = cggamestate.dataCount; cggamestate.dataCount += strlen(oldstr); } else cggamestate.stringOffsets[i] = 0; } } if (!*str) { cggamestate.stringOffsets[num] = 0; return; } cggamestate.dataCount+=1; strcpy(cggamestate.stringData+cggamestate.dataCount, str); cggamestate.stringOffsets[num] = cggamestate.dataCount; cggamestate.dataCount += strlen(str); } char *CG_GetConfigString(int num) { if ((unsigned)num >= MAX_CONFIGSTRINGS) return ""; return cggamestate.stringData + cggamestate.stringOffsets[num]; } int CG_GetGameState(gameState_t *gs) { memcpy(gs, &cggamestate, sizeof(gameState_t)); return sizeof(gameState_t); } typedef struct { int serverTime; int angles[3]; int buttons; qbyte weapon; // weapon signed char forwardmove, rightmove, upmove; } q3usercmd_t; #define CMD_MASK Q3UPDATE_MASK qboolean CGQ3_GetUserCmd(int cmdNumber, q3usercmd_t *ucmd) { usercmd_t *cmd; cmdNumber--; if (cmdNumber > ccs.currentUserCmdNumber) Host_EndGame("CL_GetUserCmd: cmdNumber > ccs.currentUserCmdNumber"); if (ccs.currentUserCmdNumber - cmdNumber > CMD_MASK) return false; // too old cmd = &cl.outframes[(cmdNumber) & CMD_MASK].cmd[0]; ucmd->angles[0] = cmd->angles[0]; ucmd->angles[1] = cmd->angles[1]; ucmd->angles[2] = cmd->angles[2]; ucmd->serverTime = cmd->servertime; ucmd->forwardmove = cmd->forwardmove; ucmd->rightmove = cmd->sidemove; ucmd->upmove = cmd->upmove; ucmd->buttons = cmd->buttons; ucmd->weapon = cmd->weapon; return true; } static vm_t *cgvm; extern int keycatcher; qboolean CG_GetServerCommand(int cmdnum) { //quote from cgame code: // get the gamestate from the client system, which will have the // new configstring already integrated char *str = ccs.serverCommands[cmdnum & TEXTCMD_MASK]; Con_DPrintf("Dispaching %s\n", str); Cmd_TokenizeString(str, false, false); if (!strcmp(Cmd_Argv(0), "cs")) CG_InsertIntoGameState(atoi(Cmd_Argv(1)), Cmd_Argv(2)); return true; } typedef struct { int firstPoint; int numPoints; } markFragment_t; int CG_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection, int maxPoints, float *pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ) { vec3_t center; vec3_t axis[3]; vec3_t p[4]; int i; float *clippedpoints; float radius; int numtris; if (numPoints != 4) return 0; /* q3 gamecode includes something like this originalPoints[0][i] = origin[i] - radius * axis[1][i] - radius * axis[2][i]; originalPoints[1][i] = origin[i] + radius * axis[1][i] - radius * axis[2][i]; originalPoints[2][i] = origin[i] + radius * axis[1][i] + radius * axis[2][i]; originalPoints[3][i] = origin[i] - radius * axis[1][i] + radius * axis[2][i]; We want that origional axis and the origin axis[0] is given in the 'projection' parameter. Yes, reversing this stuff means that we'll have no support for triangles. */ VectorClear(center); VectorMA(center, 0.25, points[0], center); VectorMA(center, 0.25, points[1], center); VectorMA(center, 0.25, points[2], center); VectorMA(center, 0.25, points[3], center); VectorSubtract(points[0], center, p[0]); VectorSubtract(points[1], center, p[1]); VectorSubtract(points[2], center, p[2]); VectorSubtract(points[3], center, p[3]); for (i = 0; i < 3; i++) { axis[1][i] = (p[2][i]-p[1][i]); axis[2][i] = (p[3][i]-p[2][i]); } radius = VectorNormalize(axis[1]); VectorNormalize(axis[2]); VectorNormalize2(projection, axis[0]); numtris = Q1BSP_ClipDecal(center, axis[0], axis[1], axis[2], radius, &clippedpoints); if (numtris > maxFragments) numtris = maxFragments; if (numtris > maxPoints/3) numtris = maxPoints/3; for (i = 0; i < numtris; i++) { fragmentBuffer[i].numPoints = 3; fragmentBuffer[i].firstPoint = i*3; pointBuffer[i*9+0] = clippedpoints[i*9+0]; pointBuffer[i*9+1] = clippedpoints[i*9+1]; pointBuffer[i*9+2] = clippedpoints[i*9+2]; pointBuffer[i*9+3] = clippedpoints[i*9+3]; pointBuffer[i*9+4] = clippedpoints[i*9+4]; pointBuffer[i*9+5] = clippedpoints[i*9+5]; pointBuffer[i*9+6] = clippedpoints[i*9+6]; pointBuffer[i*9+7] = clippedpoints[i*9+7]; pointBuffer[i*9+8] = clippedpoints[i*9+8]; } return numtris; } int VM_LerpTag(void *out, model_t *model, int f1, int f2, float l2, char *tagname); #define VALIDATEPOINTER(o,l) if ((int)o + l >= mask || VM_POINTER(o) < offset) Host_EndGame("Call to cgame trap %u passes invalid pointer\n", (unsigned int)fn); //out of bounds. static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, const qintptr_t *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. //OutputDebugString(va("cl_cg: %i\n", fn)); switch(fn) { case CG_PRINT: Con_Printf("%s", (char*)VM_POINTER(arg[0])); break; case CG_ERROR: Host_EndGame("cgame: %s", (char*)VM_POINTER(arg[0])); break; case CG_ARGC: VM_LONG(ret) = Cmd_Argc(); break; case CG_ARGV: VALIDATEPOINTER(arg[1], arg[2]); Q_strncpyz(VM_POINTER(arg[1]), Cmd_Argv(VM_LONG(arg[0])), VM_LONG(arg[2])); break; case CG_ARGS: VALIDATEPOINTER(arg[0], arg[1]); Q_strncpyz(VM_POINTER(arg[0]), Cmd_Args(), VM_LONG(arg[1])); break; case CG_CVAR_REGISTER: if (arg[0]) VALIDATEPOINTER(arg[0], sizeof(q3vmcvar_t)); return VMQ3_Cvar_Register(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3])); case CG_CVAR_UPDATE: VALIDATEPOINTER(arg[0], sizeof(q3vmcvar_t)); return VMQ3_Cvar_Update(VM_POINTER(arg[0])); case CG_CVAR_SET: { cvar_t *var; var = Cvar_FindVar(VM_POINTER(arg[0])); if (var) Cvar_Set(var, VM_POINTER(arg[1])?VM_POINTER(arg[1]):""); //set it else Cvar_Get(VM_POINTER(arg[0]), VM_POINTER(arg[1]), 0, "Q3CG created"); //create one } break; case CG_CVAR_VARIABLESTRINGBUFFER: { cvar_t *var; var = Cvar_FindVar(VM_POINTER(arg[0])); if (!VM_LONG(arg[2])) VM_LONG(ret) = 0; else if (!var) { VALIDATEPOINTER(arg[1], 1); *(char *)VM_POINTER(arg[1]) = '\0'; VM_LONG(ret) = -1; } else { VALIDATEPOINTER(arg[1], arg[2]); Q_strncpyz(VM_POINTER(arg[1]), var->string, VM_LONG(arg[2])); } } break; case CG_SENDCONSOLECOMMAND: Con_DPrintf("CG_SENDCONSOLECOMMAND: %s", (char*)VM_POINTER(arg[0])); Cbuf_AddText(VM_POINTER(arg[0]), RESTRICT_SERVER); break; case CG_ADDCOMMAND: Cmd_AddCommand(VM_POINTER(arg[0]), NULL); break; case CG_SENDCLIENTCOMMAND: Con_DPrintf("CG_SENDCLIENTCOMMAND: %s", (char*)VM_POINTER(arg[0])); CL_SendClientCommand(true, "%s", (char*)VM_POINTER(arg[0])); break; case CG_UPDATESCREEN: //force a buffer swap cos loading won't refresh it soon. if (key_dest != key_console) scr_con_current = 0; SCR_UpdateScreen(); break; case CG_FS_FOPENFILE: //fopen if (arg[1]) VALIDATEPOINTER(arg[1], 4); VM_LONG(ret) = VM_fopen(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2]), 1); break; case CG_FS_READ: //fread VALIDATEPOINTER(arg[1], 4); VM_LONG(ret) = VM_FRead(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), 1); break; case CG_FS_WRITE: //fwrite break; case CG_FS_FCLOSEFILE: //fclose VM_fclose(VM_LONG(arg[0]), 1); break; case CG_CM_POINTCONTENTS: //int trap_CM_PointContents( const vec3_t p, clipHandle_t model ); { unsigned int pc; unsigned int modhandle = VM_LONG(arg[1]); model_t *mod; if (modhandle >= MAX_MODELS) mod = &box_model; else mod = cl.model_precache[modhandle+1]; if (mod && !mod->needload) pc = mod->funcs.NativeContents(mod, 0, 0, NULL, VM_POINTER(arg[0]), vec3_origin, vec3_origin); else pc = 1;//FTECONTENTS_SOLID; VM_LONG(ret) = pc;//Contents_To_Q3(pc); } break; case CG_CM_TRANSFORMEDPOINTCONTENTS: //int trap_CM_TransformedPointContents( const vec3_t p, clipHandle_t model, const vec3_t origin, const vec3_t angles ) { { unsigned int pc; float *p = VM_POINTER(arg[0]); unsigned int modhandle = VM_LONG(arg[1]); float *origin = VM_POINTER(arg[2]); float *angles = VM_POINTER(arg[3]); model_t *mod; if (modhandle >= MAX_MODELS) mod = &box_model; else mod = cl.model_precache[modhandle+1]; if (mod) { vec3_t p_l; vec3_t axis[3]; // subtract origin offset VectorSubtract (p, origin, p_l); // rotate start and end into the models frame of reference if (angles[0] || angles[1] || angles[2]) { AngleVectors (angles, axis[0], axis[1], axis[2]); VectorNegate(axis[1], axis[1]); pc = mod->funcs.NativeContents(mod, 0, 0, axis, p_l, vec3_origin, vec3_origin); } else pc = mod->funcs.NativeContents(mod, 0, 0, NULL, p_l, vec3_origin, vec3_origin); } else pc = Q3CONTENTS_SOLID; VM_LONG(ret) = pc; } break; case CG_CM_TRANSFORMEDBOXTRACE: // void trap_CM_BoxTrace( trace_t *results, const vec3_t start, const vec3_t end, // const vec3_t mins, const vec3_t maxs, // clipHandle_t model, int brushmask ); { //FIXME: no protection of result trace. trace_t tr; q3trace_t *results = VM_POINTER(arg[0]); float *start = VM_POINTER(arg[1]); float *end = VM_POINTER(arg[2]); float *mins = VM_POINTER(arg[3]); float *maxs = VM_POINTER(arg[4]); unsigned int modhandle = VM_LONG(arg[5]); int brushmask = VM_LONG(arg[6]); float *origin = VM_POINTER(arg[7]); float *angles = VM_POINTER(arg[8]); model_t *mod; if (modhandle >= MAX_MODELS) mod = &box_model; else mod = cl.model_precache[modhandle+1]; if (!mins) mins = vec3_origin; if (!maxs) maxs = vec3_origin; if (!origin) origin = vec3_origin; if (!angles) angles = vec3_origin; if (mod) #ifndef CLIENTONLY TransformedNativeTrace(mod, 0, 0, start, end, mins, maxs, brushmask, &tr, origin, angles); #else { #ifdef warningmsg #pragma warningmsg("FIXME: G3 CGame requires TransformedNativeTrace!") #endif memset(&tr, 0, sizeof(tr)); tr.allsolid = tr.startsolid = true; tr.contents = 1; } #endif else { memset(&tr, 0, sizeof(tr)); tr.allsolid = tr.startsolid = true; tr.contents = 1; } results->allsolid = tr.allsolid; results->contents = tr.contents; results->fraction = tr.fraction; results->entityNum = 0; results->startsolid = tr.startsolid; results->surfaceFlags = tr.surface?tr.surface->flags:0; memcpy(results->endpos, tr.endpos, sizeof(vec3_t)); memcpy(&results->plane, &tr.plane, sizeof(cplane_t)); } break; case CG_CM_BOXTRACE: // void trap_CM_BoxTrace( trace_t *results, const vec3_t start, const vec3_t end, // const vec3_t mins, const vec3_t maxs, // clipHandle_t model, int brushmask ); { //FIXME: no protection of result trace. trace_t tr; q3trace_t *results = VM_POINTER(arg[0]); float *start = VM_POINTER(arg[1]); float *end = VM_POINTER(arg[2]); float *mins = VM_POINTER(arg[3]); float *maxs = VM_POINTER(arg[4]); unsigned int modhandle = VM_LONG(arg[5]); int brushmask = VM_LONG(arg[6]); model_t *mod; if (modhandle >= MAX_MODELS) mod = &box_model; else mod = cl.model_precache[modhandle+1]; if (!mins) mins = vec3_origin; if (!maxs) maxs = vec3_origin; mod->funcs.NativeTrace(mod, 0, 0, NULL, start, end, mins, maxs, brushmask, &tr); results->allsolid = tr.allsolid; results->contents = tr.contents; results->fraction = tr.fraction; results->entityNum = 0; results->startsolid = tr.startsolid; results->surfaceFlags = tr.surface?tr.surface->flags:0; memcpy(results->endpos, tr.endpos, sizeof(vec3_t)); memcpy(&results->plane, &tr.plane, sizeof(cplane_t)); } break; case CG_R_LOADWORLDMAP: //FTE can't distinguish. :/ break; //So long as noone has one collision model with a different rendering one, we'll be fine case CG_CM_LOADMAP: { int i; char *mapname = VM_POINTER(arg[0]); strcpy(cl.model_name[1], mapname); cl.worldmodel = cl.model_precache[1] = Mod_ForName(mapname, false); if (cl.worldmodel->needload) Host_EndGame("Couldn't load map"); for (i=1 ; inumsubmodels ; i++) { strcpy(cl.model_name[1+i], va("*%i", i)); cl.model_precache[i+1] = Mod_ForName (cl.model_name[i+1], false); } } break; case CG_CM_INLINEMODEL: if ((unsigned int)VM_LONG(arg[0]) > (cl.worldmodel?cl.worldmodel->numsubmodels:0)) Host_EndGame("cgame asked for invalid model number\n"); VM_LONG(ret) = VM_LONG(arg[0]); break; case CG_CM_NUMINLINEMODELS: VM_LONG(ret) = cl.worldmodel?cl.worldmodel->numsubmodels:0; break; case CG_CM_TEMPBOXMODEL: CM_TempBoxModel(VM_POINTER(arg[0]), VM_POINTER(arg[1])); VM_LONG(ret) = MAX_MODELS; break; case CG_R_MODELBOUNDS: VALIDATEPOINTER(arg[1], sizeof(vec3_t)); VALIDATEPOINTER(arg[2], sizeof(vec3_t)); { model_t *mod = VM_FROMMHANDLE(arg[0]); if (mod) { VectorCopy(mod->mins, ((float*)VM_POINTER(arg[1]))); VectorCopy(mod->maxs, ((float*)VM_POINTER(arg[2]))); } } break; case CG_R_REGISTERMODEL: //precache model { model_t *mod; mod = Mod_ForName(VM_POINTER(arg[0]), false); if (mod->needload || mod->type == mod_dummy) return 0; VM_LONG(ret) = VM_TOMHANDLE(mod); } break; case CG_R_REGISTERSKIN: VM_LONG(ret) = VM_TOSTRCACHE(arg[0]); break; case CG_R_REGISTERSHADER: if (!*(char*)VM_POINTER(arg[0])) VM_LONG(ret) = 0; else VM_LONG(ret) = VM_TOSHANDLE(R_RegisterPic(VM_POINTER(arg[0]))); break; case CG_R_REGISTERSHADERNOMIP: if (!*(char*)VM_POINTER(arg[0])) VM_LONG(ret) = 0; else VM_LONG(ret) = VM_TOSHANDLE(R_RegisterPic(VM_POINTER(arg[0]))); break; case CG_R_CLEARSCENE: //clear scene (not rtlights, only dynamic ones) CL_ClearEntityLists(); rtlights_first = RTL_FIRST; break; case CG_R_ADDPOLYTOSCENE: VQ3_AddPoly(VM_FROMSHANDLE(arg[0]), VM_LONG(arg[1]), VM_POINTER(arg[2])); break; case CG_R_ADDREFENTITYTOSCENE: //add ent to scene VQ3_AddEntity(VM_POINTER(arg[0])); break; case CG_R_ADDADDITIVELIGHTTOSCENE: case CG_R_ADDLIGHTTOSCENE: //add light to scene. { float *org = VM_POINTER(arg[0]); CL_NewDlight(-1, org, VM_FLOAT(arg[1]), 0, VM_FLOAT(arg[2]), VM_FLOAT(arg[3]), VM_FLOAT(arg[4])); } break; case CG_R_RENDERSCENE: //render scene R_PushDlights(); VQ3_RenderView(VM_POINTER(arg[0])); break; case CG_R_SETCOLOR: //setcolour float* { float *f = VM_POINTER(arg[0]); if (f) R2D_ImageColours(f[0], f[1], f[2], f[3]); else R2D_ImageColours(1, 1, 1, 1); } break; case CG_R_DRAWSTRETCHPIC: R2D_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_FROMSHANDLE(VM_LONG(arg[8]))); break; case CG_R_LERPTAG: //Lerp tag... VALIDATEPOINTER(arg[0], sizeof(float)*12); VM_LONG(ret) = VM_LerpTag(VM_POINTER(arg[0]), VM_FROMMHANDLE(arg[1]), VM_LONG(arg[2]), VM_LONG(arg[3]), VM_FLOAT(arg[4]), VM_POINTER(arg[5])); break; case CG_S_REGISTERSOUND: { sfx_t *sfx; sfx = S_PrecacheSound(VM_POINTER(arg[0])); if (sfx) VM_LONG(ret) = VM_TOSTRCACHE(arg[0]); else VM_LONG(ret) = -1; } break; case CG_S_STARTLOCALSOUND: if (VM_LONG(arg[0]) != -1 && arg[0]) S_LocalSound(VM_FROMSTRCACHE(arg[0])); break; case CG_S_STARTSOUND:// ( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx ) S_StartSound(VM_LONG(arg[1])+1, VM_LONG(arg[2]), S_PrecacheSound(VM_FROMSTRCACHE(arg[3])), VM_POINTER(arg[0]), 1, 1, 0, 0); break; case CG_S_ADDLOOPINGSOUND: break; case CG_S_STOPLOOPINGSOUND: break; case CG_S_STARTBACKGROUNDTRACK: case CG_S_STOPBACKGROUNDTRACK: case CG_S_CLEARLOOPINGSOUNDS: break; case CG_S_UPDATEENTITYPOSITION://void trap_S_UpdateEntityPosition( int entityNum, const vec3_t origin ); break; case CG_S_RESPATIALIZE://void trap_S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater ); { float *org = VM_POINTER(arg[1]); vec3_t *axis = VM_POINTER(arg[2]); S_UpdateListener(org, axis[0], axis[1], axis[2]); } break; case CG_S_ADDREALLOOPINGSOUND: break; case CG_KEY_ISDOWN: { extern qboolean keydown[256]; if (keydown[VM_LONG(arg[0])]) VM_LONG(ret) = 1; else VM_LONG(ret) = 0; } break; case CG_KEY_GETKEY: { int ret[2]; M_FindKeysForCommand (0, VM_POINTER(arg[0]), ret); return ret[0]; } break; case CG_KEY_GETCATCHER: VM_LONG(ret) = keycatcher; break; case CG_KEY_SETCATCHER: keycatcher = VM_LONG(arg[0]); break; case CG_GETGLCONFIG: VALIDATEPOINTER(arg[0], 11332); { //FIXME: Clean this shit up //do any needed work unsigned char *glconfig = VM_POINTER(arg[0]); memset(glconfig, 0, 11304); *(int *)(glconfig+11304) = vid.width; *(int *)(glconfig+11308) = vid.height; *(float *)(glconfig+11312) = (float)vid.width/vid.height; memset((glconfig+11316), 0, 11332-11316); } break; case CG_GETGAMESTATE: VALIDATEPOINTER(arg[0], sizeof(gameState_t)); VM_LONG(ret) = CG_GetGameState(VM_POINTER(arg[0])); break; case CG_CM_MARKFRAGMENTS: VM_LONG(ret) = CG_MarkFragments( VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3]), VM_POINTER(arg[4]), VM_LONG(arg[5]), VM_POINTER(arg[6]) ); break; case CG_GETCURRENTSNAPSHOTNUMBER: VALIDATEPOINTER(arg[0], sizeof(int)); VALIDATEPOINTER(arg[1], sizeof(int)); *(int *)VM_POINTER(arg[0]) = ccs.snap.serverMessageNum; *(int *)VM_POINTER(arg[1]) = ccs.snap.serverTime;// + Sys_DoubleTime()*1000-ccs.snap.localTime; break; case CG_GETSNAPSHOT: VALIDATEPOINTER(arg[1], sizeof(snapshot_t)); VM_LONG(ret) = CG_FillQ3Snapshot(VM_LONG(arg[0]), VM_POINTER(arg[1])); break; case CG_GETCURRENTCMDNUMBER: VM_LONG(ret) = ccs.currentUserCmdNumber; break; case CG_GETUSERCMD: VALIDATEPOINTER(arg[1], sizeof(q3usercmd_t)); VM_LONG(ret) = CGQ3_GetUserCmd(VM_LONG(arg[0]), VM_POINTER(arg[1])); break; case CG_SETUSERCMDVALUE: //weaponselect, zoomsensativity. ccs.selected_weapon = VM_LONG(arg[0]); in_sensitivityscale = VM_FLOAT(arg[1]); break; case CG_GETSERVERCOMMAND: VM_LONG(ret) = CG_GetServerCommand(VM_LONG(arg[0])); break; case CG_MEMORY_REMAINING: VM_LONG(ret) = Hunk_LowMemAvailable(); break; case CG_MILLISECONDS: VM_LONG(ret) = Sys_Milliseconds(); break; case CG_REAL_TIME: //really local time VM_LONG(ret) = Sys_Milliseconds(); break; case CG_SNAPVECTOR: // ( float *v ) VALIDATEPOINTER(arg[0], sizeof(vec3_t)); { float *fp = (float *)VM_POINTER(arg[0]); #define rint(x) (int)((x > 0)?(x + 0.5f):(x-0.5f)) fp[0] = rint(fp[0]); fp[1] = rint(fp[1]); fp[2] = rint(fp[2]); } break; case CG_PC_ADD_GLOBAL_DEFINE: Con_Printf("CG_PC_ADD_GLOBAL_DEFINE not supported\n"); break; case CG_PC_SOURCE_FILE_AND_LINE: Script_Get_File_And_Line(arg[0], VM_POINTER(arg[1]), VM_POINTER(arg[2])); break; case CG_PC_LOAD_SOURCE: return Script_LoadFile(VM_POINTER(arg[0])); case CG_PC_FREE_SOURCE: Script_Free(arg[0]); break; case CG_PC_READ_TOKEN: //fixme: memory protect. return Script_Read(arg[0], VM_POINTER(arg[1])); // standard Q3 case CG_MEMSET: VALIDATEPOINTER(arg[0], arg[2]); { void *dst = VM_POINTER(arg[0]); memset(dst, arg[1], arg[2]); } break; case CG_MEMCPY: VALIDATEPOINTER(arg[0], arg[2]); { void *dst = VM_POINTER(arg[0]); void *src = VM_POINTER(arg[1]); memcpy(dst, src, arg[2]); } break; case CG_STRNCPY: VALIDATEPOINTER(arg[0], arg[2]); { void *dst = VM_POINTER(arg[0]); void *src = VM_POINTER(arg[1]); strncpy(dst, src, arg[2]); } break; case CG_SIN: VM_FLOAT(ret)=(float)sin(VM_FLOAT(arg[0])); break; case CG_COS: VM_FLOAT(ret)=(float)cos(VM_FLOAT(arg[0])); break; case CG_ACOS: VM_FLOAT(ret)=(float)acos(VM_FLOAT(arg[0])); break; case CG_ATAN2: VM_FLOAT(ret)=(float)atan2(VM_FLOAT(arg[0]), VM_FLOAT(arg[1])); break; case CG_SQRT: VM_FLOAT(ret)=(float)sqrt(VM_FLOAT(arg[0])); break; case CG_FLOOR: VM_FLOAT(ret)=(float)floor(VM_FLOAT(arg[0])); break; case CG_CEIL: VM_FLOAT(ret)=(float)ceil(VM_FLOAT(arg[0])); break; case CG_R_REGISTERFONT: VALIDATEPOINTER(arg[2], sizeof(fontInfo_t)); UI_RegisterFont(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_POINTER(arg[2])); break; case CG_FTE_FINDPARTICLEEFFECT: return pe->FindParticleType(VM_POINTER(arg[0])); case CG_FTE_SPAWNPARTICLEEFFECT: return pe->RunParticleEffectState(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_FLOAT(arg[2]), VM_LONG(arg[3]), VM_POINTER(arg[4])); case CG_FTE_SPAWNPARTICLETRAIL: return pe->ParticleTrail(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2]), 0, VM_POINTER(arg[3])); case CG_FTE_FREEPARTICLESTATE: pe->DelinkTrailstate(VM_POINTER(arg[0])); break; default: Con_Printf("Q3CG: Bad system trap: %i\n", (int)fn); } return ret; } static int CG_SystemCallsVM(void *offset, quintptr_t mask, int fn, const int *arg) { if (sizeof(qintptr_t) == sizeof(int)) return CG_SystemCalls(offset, mask, fn, (qintptr_t*)arg); else { qintptr_t args[10]; 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]; args[9]=arg[9]; return CG_SystemCalls(offset, mask, fn, args); } } //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 CG_SystemCallsNative(qintptr_t arg, ...) { qintptr_t args[10]; 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); args[9]=va_arg(argptr, qintptr_t); va_end(argptr); return CG_SystemCalls(NULL, (unsigned)~0, arg, args); } int CG_Refresh(void) { int time; if (!cgvm) return false; time = ccs.serverTime; VM_Call(cgvm, CG_DRAW_ACTIVE_FRAME, time, 0, false); R2D_ImageColours(1, 1, 1, 1); return true; } void CG_Stop (void) { keycatcher &= ~2; if (cgvm) { VM_Call(cgvm, CG_SHUTDOWN); VM_Destroy(cgvm); VM_fcloseall(1); cgvm = NULL; } } void CG_Start (void) { SCR_SetLoadingStage(0); if (cls.protocol != CP_QUAKE3) { //q3 clients only. CG_Stop(); return; } Z_FreeTags(CGTAGNUM); SCR_BeginLoadingPlaque(); cgvm = VM_Create("vm/cgame", com_nogamedirnativecode.ival?NULL:CG_SystemCallsNative, CG_SystemCallsVM); if (cgvm) { //hu... cgame doesn't appear to have a query version call! SCR_EndLoadingPlaque(); VM_Call(cgvm, CG_INIT, ccs.serverMessageNum, ccs.lastServerCommandNum, cl.playernum[0]); } else { SCR_EndLoadingPlaque(); Host_EndGame("Failed to initialise cgame module\n"); } } qboolean CG_Command(void) { if (!cgvm) return false; Con_DPrintf("CG_Command: %s %s\n", Cmd_Argv(0), Cmd_Args()); return VM_Call(cgvm, CG_CONSOLE_COMMAND); } void CG_Command_f(void) { if (cgvm) { Con_DPrintf("CG_Command_f: %s %s\n", Cmd_Argv(0), Cmd_Args()); if (!VM_Call(cgvm, CG_CONSOLE_COMMAND)) { Cmd_ForwardToServer(); } } } qboolean CG_KeyPress(int key, int unicode, int down) { if (!cgvm || !(keycatcher&8)) return false; return VM_Call(cgvm, CG_KEY_EVENT, key, down); } void CG_Restart_f(void) { CG_Stop(); CG_Start(); } void CG_Init(void) { Cmd_AddCommand("cg_restart", CG_Restart_f); } #endif