diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index e41c36822..22ec5e876 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -591,7 +591,7 @@ qboolean CL_GetDemoMessage (void) if (msglength > net_message.maxsize) { - Con_Printf ("Demo message > MAX_MSGLEN"); + Con_Printf ("Demo message > MAX_MSGLEN\n"); CL_StopPlayback (); return 0; } diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index 00e59c2af..7bffb12c4 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -4606,7 +4606,7 @@ void CL_ParsePlayerinfo (void) if (flags & PF_COMMAND) { - MSG_ReadDeltaUsercmd (&nullcmd, &state->command); + MSG_ReadDeltaUsercmd (&nullcmd, &state->command, cl.protocol_qw); state->viewangles[0] = state->command.angles[0] * (360.0/65536); state->viewangles[1] = state->command.angles[1] * (360.0/65536); diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 741a12441..0534275ba 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1613,6 +1613,7 @@ void CL_ClearState (void) CL_ResetFog(0); CL_ResetFog(1); + cl.protocol_qw = PROTOCOL_VERSION_QW; //until we get an svc_serverdata cl.allocated_client_slots = QWMAX_CLIENTS; #ifndef CLIENTONLY //FIXME: we should just set it to 0 to make sure its set up properly elsewhere. @@ -5964,6 +5965,7 @@ void CL_ExecInitialConfigs(char *resetcommand) void Host_FinishLoading(void) { + extern qboolean r_forceheadless; extern int r_blockvidrestart; if (r_blockvidrestart == true) { @@ -6016,12 +6018,9 @@ void Host_FinishLoading(void) if (PM_IsApplying(true)) return; -#ifdef ANDROID - //android needs to wait a bit longer before it's allowed to init its video properly. - extern int sys_glesversion; - if (!sys_glesversion) + //android may find that it has no renderer at various points. + if (r_forceheadless) return; -#endif if (r_blockvidrestart == 2) { //2 is part of the initial startup diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index e3bf2501b..f4df928b6 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -3101,6 +3101,7 @@ static void CLQW_ParseServerData (void) Stats_NewMap(); #endif cl.servercount = svcnt; + cl.protocol_qw = protover; Cvar_ForceCallback(Cvar_FindVar("r_particlesdesc")); @@ -3216,17 +3217,33 @@ static void CLQW_ParseServerData (void) str = MSG_ReadString (); Q_strncpyz (cl.levelname, str, sizeof(cl.levelname)); - // get the movevars - movevars.gravity = MSG_ReadFloat(); - movevars.stopspeed = MSG_ReadFloat(); - maxspeed = MSG_ReadFloat(); - movevars.spectatormaxspeed = MSG_ReadFloat(); - movevars.accelerate = MSG_ReadFloat(); - movevars.airaccelerate = MSG_ReadFloat(); - movevars.wateraccelerate = MSG_ReadFloat(); - movevars.friction = MSG_ReadFloat(); - movevars.waterfriction = MSG_ReadFloat(); - entgrav = MSG_ReadFloat(); + if (cl.protocol_qw >= 25) + { + // get the movevars + movevars.gravity = MSG_ReadFloat(); + movevars.stopspeed = MSG_ReadFloat(); + maxspeed = MSG_ReadFloat(); + movevars.spectatormaxspeed = MSG_ReadFloat(); + movevars.accelerate = MSG_ReadFloat(); + movevars.airaccelerate = MSG_ReadFloat(); + movevars.wateraccelerate = MSG_ReadFloat(); + movevars.friction = MSG_ReadFloat(); + movevars.waterfriction = MSG_ReadFloat(); + entgrav = MSG_ReadFloat(); + } + else + { + movevars.gravity = 800; + movevars.stopspeed = 100; + maxspeed = 320; + movevars.spectatormaxspeed = 500; + movevars.accelerate = 10; + movevars.airaccelerate = 0.7f; + movevars.wateraccelerate = 10; + movevars.friction = 6.0f; + movevars.waterfriction = 1; + entgrav = 1; + } for (clnum = 0; clnum < cl.splitclients; clnum++) { @@ -3973,7 +3990,7 @@ static void CL_ParseSoundlist (qboolean lots) if (lots) numsounds = MSG_ReadShort(); else - numsounds = MSG_ReadByte(); + numsounds = (cl.protocol_qw>=26)?MSG_ReadByte():0; for (;;) { @@ -3991,7 +4008,7 @@ static void CL_ParseSoundlist (qboolean lots) strcpy (cl.sound_name[numsounds], str); } - n = MSG_ReadByte(); + n = (cl.protocol_qw>=26)?MSG_ReadByte():0; if (n) { @@ -4047,7 +4064,7 @@ static void CL_ParseModellist (qboolean lots) if (lots) nummodels = MSG_ReadShort(); else - nummodels = MSG_ReadByte(); + nummodels = (cl.protocol_qw>=26)?MSG_ReadByte():0; for (;;) { @@ -4089,7 +4106,7 @@ static void CL_ParseModellist (qboolean lots) if (nummodels) SCR_ImageName(cl.model_name[1]); - n = MSG_ReadByte(); + n = (cl.protocol_qw>=26)?MSG_ReadByte():0; if (n) { diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index 95a9143be..7d8541451 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -2810,6 +2810,8 @@ static void SCR_ScreenShot_VR_f(void) VectorCopy(cl.playerview->simangles, cl.playerview->viewangles); //FIXME: it should be possible to do this more inteligently, and get both strips with a single render. + //FIXME: we should render to a PBO instead, so that the gpu+cpu don't need to sync until the very end. + //FIXME: we should be using scissoring to avoid redrawing the entire screen (also tweak cull planes) ang = M_PI*2*(baseang[1]/360.0 + (lx+0.5*(rx-lx))/width); r_refdef.eyeoffset[0] = sin(ang) * r_stereo_separation.value * 0.5; diff --git a/engine/client/client.h b/engine/client/client.h index 5328aaa53..d1414702b 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -440,10 +440,10 @@ typedef struct /*QuakeWorld protocol flags*/ #ifdef PROTOCOLEXTENSIONS - unsigned long fteprotocolextensions; - unsigned long fteprotocolextensions2; + unsigned int fteprotocolextensions; + unsigned int fteprotocolextensions2; #endif - unsigned long z_ext; + unsigned int z_ext; /*NQ Protocol flags*/ enum @@ -453,7 +453,7 @@ typedef struct CPNQ_BJP1, //16bit models, strict 8bit sounds (otherwise based on nehahra) CPNQ_BJP2, //16bit models, strict 16bit sounds CPNQ_BJP3, //16bit models, flagged 16bit sounds, 8bit static sounds. - CPNQ_FITZ666, /*and rmqe999 protocol*/ + CPNQ_FITZ666, /*and rmqe999 protocol, which is a strict superset*/ CPNQ_DP5, CPNQ_DP6, CPNQ_DP7 @@ -739,6 +739,8 @@ typedef struct int fpd; int servercount; // server identification for prespawns + int protocol_qw; + float gamespeed; qboolean csqcdebug; qboolean allowsendpacket; diff --git a/engine/client/console.c b/engine/client/console.c index 6107878ae..e7442d279 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -980,6 +980,7 @@ extern redirect_t sv_redirected; extern char sv_redirected_buf[8000]; void SV_FlushRedirect (void); #endif +vfsfile_t *con_pipe; #define MAXPRINTMSG 4096 static void Con_PrintFromThread (void *ctx, void *data, size_t a, size_t b) @@ -988,6 +989,18 @@ static void Con_PrintFromThread (void *ctx, void *data, size_t a, size_t b) BZ_Free(data); } +vfsfile_t *Con_POpen(char *conname) +{ + if (!conname || !*conname) + { + if (con_pipe) + VFS_CLOSE(con_pipe); + con_pipe = VFSPIPE_Open(2, false); + return con_pipe; + } + return NULL; +} + // FIXME: make a buffer size safe vsprintf? void VARGS Con_Printf (const char *fmt, ...) { @@ -1021,6 +1034,9 @@ void VARGS Con_Printf (const char *fmt, ...) // log all messages to file Con_Log (msg); + if (con_pipe) + VFS_PUTS(con_pipe, msg); + if (!con_initialized) return; @@ -1029,7 +1045,7 @@ void VARGS Con_Printf (const char *fmt, ...) } void VARGS Con_SafePrintf (const char *fmt, ...) -{ +{ //obsolete version of the function va_list argptr; char msg[MAXPRINTMSG]; diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index d55a1225f..7431945c0 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -1741,6 +1741,12 @@ static void QCBUILTIN PF_cs_project (pubprogfuncs_t *prinst, struct globalvars_s out[0] = out[0]*r_refdef.vrect.width + r_refdef.vrect.x; out[1] = out[1]*r_refdef.vrect.height + r_refdef.vrect.y; + if (csqc_isdarkplaces) + { + out[0] *= (float)vid.pixelwidth / vid.width; + out[1] *= (float)vid.pixelheight / vid.height; + } + if (tempv[3] < 0) out[2] *= -1; } @@ -1753,12 +1759,18 @@ static void QCBUILTIN PF_cs_unproject (pubprogfuncs_t *prinst, struct globalvars { float *in = G_VECTOR(OFS_PARM0); float *out = G_VECTOR(OFS_RETURN); - float tx, ty; + float tx = in[0], ty = in[1]; float v[4], tempv[4]; - tx = ((in[0]-r_refdef.vrect.x)/r_refdef.vrect.width); - ty = ((in[1]-r_refdef.vrect.y)/r_refdef.vrect.height); + if (csqc_isdarkplaces) + { + tx *= (float)vid.width / vid.pixelwidth; + ty *= (float)vid.height / vid.pixelheight; + } + + tx = ((tx-r_refdef.vrect.x)/r_refdef.vrect.width); + ty = ((ty-r_refdef.vrect.y)/r_refdef.vrect.height); ty = 1-ty; v[0] = tx*2-1; v[1] = ty*2-1; diff --git a/engine/client/renderer.c b/engine/client/renderer.c index 135f2e9bd..5c6aff329 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -16,6 +16,7 @@ vec3_t r_origin, vpn, vright, vup; entity_t r_worldentity; entity_t *currententity; //nnggh int r_framecount; +qboolean r_forceheadless; struct texture_s *r_notexture_mip; int r_blockvidrestart; //'block' is a bit of a misnomer. 0=filesystem, configs, cinematics, video are all okay as they are. 1=starting up, waiting for filesystem, will restart after. 2=configs execed, but still need cinematics. 3=video will be restarted without any other init needed @@ -301,8 +302,11 @@ extern cvar_t r_novis; extern cvar_t r_speeds; extern cvar_t r_waterwarp; -cvar_t r_polygonoffset_submodel_factor = CVAR("r_polygonoffset_submodel_factor", "0"); -cvar_t r_polygonoffset_submodel_offset = CVAR("r_polygonoffset_submodel_offset", "0"); +#ifdef BEF_PUSHDEPTH +cvar_t r_polygonoffset_submodel_factor = CVARD("r_polygonoffset_submodel_factor", "0", "z-fighting avoidance. Pushes submodel depth values slightly towards the camera depending on the slope of the surface."); +cvar_t r_polygonoffset_submodel_offset = CVARD("r_polygonoffset_submodel_offset", "0", "z-fighting avoidance. Pushes submodel depth values slightly towards the camera by a consistent distance."); +cvar_t r_polygonoffset_submodel_maps = CVARD("r_polygonoffset_submodel_map", "e?m? r?m? hip?m?", "List of maps on which z-fighting reduction should be used. wildcards accepted."); +#endif cvar_t r_polygonoffset_shadowmap_offset = CVAR("r_polygonoffset_shadowmap_factor", "0.05"); cvar_t r_polygonoffset_shadowmap_factor = CVAR("r_polygonoffset_shadowmap_offset", "0"); @@ -329,7 +333,7 @@ cvar_t gl_ati_truform_type = CVAR ("gl_ati_truform_type", "1"); cvar_t r_tessellation_level = CVAR ("r_tessellation_level", "5"); cvar_t gl_blend2d = CVAR ("gl_blend2d", "1"); cvar_t gl_blendsprites = CVARD ("gl_blendsprites", "0", "Blend sprites instead of alpha testing them"); -cvar_t r_deluxmapping_cvar = CVARAFD ("r_deluxmapping", "0", "r_deluxemapping", //fixme: rename to r_glsl_deluxmapping once configs catch up +cvar_t r_deluxmapping_cvar = CVARAFD ("r_deluxemapping", "0", "r_glsl_deluxemapping", CVAR_ARCHIVE, "Enables bumpmapping based upon precomputed light directions.\n0=off\n1=use if available\n2=auto-generate (if possible)"); qboolean r_deluxmapping; cvar_t r_shaderblobs = CVARD ("r_shaderblobs", "0", "If enabled, can massively accelerate vid restarts / loading (especially with the d3d renderer). Can cause issues when upgrading engine versions, so this is disabled by default."); @@ -375,10 +379,11 @@ cvar_t gl_savecompressedtex = CVARD ("gl_savecompressedtex", "0", "Write ou //cvar_t gl_schematics = CVARD ("gl_schematics", "0", "Gimmick rendering mode that draws the length of various world edges."); cvar_t gl_skyboxdist = CVARD ("gl_skyboxdist", "0", "The distance of the skybox. If 0, the engine will determine it based upon the far clip plane distance."); //0 = guess. cvar_t gl_smoothcrosshair = CVAR ("gl_smoothcrosshair", "1"); -cvar_t gl_maxdist = CVARD ("gl_maxdist", "0", "The distance of the far clip plane. If set to 0, some fancy maths will be used to place it at an infinite distance."); +cvar_t gl_maxdist = CVARAD ("gl_maxdist", "0", "gl_farclip", "The distance of the far clip plane. If set to 0, some fancy maths will be used to place it at an infinite distance."); #ifdef SPECULAR -cvar_t gl_specular = CVARF ("gl_specular", "0.3", CVAR_ARCHIVE); +cvar_t gl_specular = CVARF ("gl_specular", "0.3", CVAR_ARCHIVE|CVAR_SHADERSYSTEM); +cvar_t gl_specular_power = CVARF ("gl_specular_power", "32", CVAR_ARCHIVE|CVAR_SHADERSYSTEM); cvar_t gl_specular_fallback = CVARF ("gl_specular_fallback", "0.05", CVAR_ARCHIVE|CVAR_RENDERERLATCH); cvar_t gl_specular_fallbackexp = CVARF ("gl_specular_fallbackexp", "1", CVAR_ARCHIVE|CVAR_RENDERERLATCH); #endif @@ -836,6 +841,7 @@ void Renderer_Init(void) Cvar_Register (&r_flashblend, GRAPHICALNICETIES); Cvar_Register (&r_flashblendscale, GRAPHICALNICETIES); Cvar_Register (&gl_specular, GRAPHICALNICETIES); + Cvar_Register (&gl_specular_power, GRAPHICALNICETIES); Cvar_Register (&gl_specular_fallback, GRAPHICALNICETIES); Cvar_Register (&gl_specular_fallbackexp, GRAPHICALNICETIES); @@ -957,9 +963,10 @@ void Renderer_Init(void) Cvar_Register (&r_showbboxes, GLRENDEREROPTIONS); Cvar_Register (&r_showfields, GLRENDEREROPTIONS); Cvar_Register (&r_showshaders, GLRENDEREROPTIONS); -#ifndef NOLEGACY +#ifdef BEF_PUSHDEPTH Cvar_Register (&r_polygonoffset_submodel_factor, GLRENDEREROPTIONS); Cvar_Register (&r_polygonoffset_submodel_offset, GLRENDEREROPTIONS); + Cvar_Register (&r_polygonoffset_submodel_maps, GLRENDEREROPTIONS); #endif Cvar_Register (&r_polygonoffset_shadowmap_factor, GLRENDEREROPTIONS); Cvar_Register (&r_polygonoffset_shadowmap_offset, GLRENDEREROPTIONS); @@ -1132,7 +1139,7 @@ extern rendererinfo_t nvvkrendererinfo; extern rendererinfo_t headlessrenderer; #endif -rendererinfo_t *rendererinfo[] = +rendererinfo_t *rendererinfo[16] = { #ifdef GLQUAKE #ifdef FTE_RPI @@ -1174,6 +1181,24 @@ rendererinfo_t *rendererinfo[] = #endif }; +void R_RegisterRenderer(rendererinfo_t *ri) +{ + size_t i; + for (i = 0; i < sizeof(rendererinfo)/sizeof(rendererinfo[0]); i++) + { //already registered + if (rendererinfo[i] == ri) + return; + } + for (i = 0; i < sizeof(rendererinfo)/sizeof(rendererinfo[0]); i++) + { //register it in the first empty slot + if (!rendererinfo[i]) + { + rendererinfo[i] = ri; + return; + } + } + Sys_Printf("unable to register renderer %s\n", ri->description); +} void R_SetRenderer(rendererinfo_t *ri) { @@ -1353,7 +1378,8 @@ qboolean R_ApplyRenderer_Load (rendererstate_t *newr) isDedicated = false; #endif if (newr) - Con_TPrintf("Setting mode %i*%i*%i*%i %s\n", newr->width, newr->height, newr->bpp, newr->rate, newr->renderer->description); + if (!r_forceheadless || newr->renderer->rtype != QR_HEADLESS) + Con_TPrintf("Setting mode %i*%i*%i*%i %s\n", newr->width, newr->height, newr->bpp, newr->rate, newr->renderer->description); vid.fullbright=0; @@ -1674,7 +1700,8 @@ TRACE(("dbg: R_ApplyRenderer: efrags\n")); if (newr && qrenderer != QR_NONE) { - Con_TPrintf("%s renderer initialized\n", newr->renderer->description); + if (!r_forceheadless || newr->renderer->rtype != QR_HEADLESS) + Con_TPrintf("%s renderer initialized\n", newr->renderer->description); } TRACE(("dbg: R_ApplyRenderer: done\n")); @@ -1728,11 +1755,22 @@ qboolean R_BuildRenderstate(rendererstate_t *newr, char *rendererstring) newr->renderer = NULL; rendererstring = COM_Parse(rendererstring); - if (!*com_token) + if (r_forceheadless) + { //special hack so that android doesn't weird out when not focused. + for (i = 0; i < sizeof(rendererinfo)/sizeof(rendererinfo[0]); i++) + { + if (rendererinfo[i] && rendererinfo[i]->name[0] && !stricmp(rendererinfo[i]->name[0], "headless")) + { + newr->renderer = rendererinfo[i]; + break; + } + } + } + else if (!*com_token) { for (i = 0; i < sizeof(rendererinfo)/sizeof(rendererinfo[0]); i++) { - if (rendererinfo[i]->name[0] && stricmp(rendererinfo[i]->name[0], "none")) + if (rendererinfo[i] && rendererinfo[i]->name[0] && stricmp(rendererinfo[i]->name[0], "none")) { newr->renderer = rendererinfo[i]; break; @@ -1744,7 +1782,7 @@ qboolean R_BuildRenderstate(rendererstate_t *newr, char *rendererstring) int count; for (i = 0, count = 0; i < sizeof(rendererinfo)/sizeof(rendererinfo[0]); i++) { - if (!rendererinfo[i]->description) + if (!rendererinfo[i] || !rendererinfo[i]->description) continue; //not valid in this build. :( if (rendererinfo[i]->rtype == QR_NONE || //dedicated servers are not useful rendererinfo[i]->rtype == QR_HEADLESS || //headless appears buggy @@ -1755,7 +1793,7 @@ qboolean R_BuildRenderstate(rendererstate_t *newr, char *rendererstring) count = rand()%count; for (i = 0; i < sizeof(rendererinfo)/sizeof(rendererinfo[0]); i++) { - if (!rendererinfo[i]->description) + if (!rendererinfo[i] || !rendererinfo[i]->description) continue; //not valid in this build. :( if (rendererinfo[i]->rtype == QR_NONE || rendererinfo[i]->rtype == QR_HEADLESS || @@ -1773,7 +1811,7 @@ qboolean R_BuildRenderstate(rendererstate_t *newr, char *rendererstring) { for (i = 0; i < sizeof(rendererinfo)/sizeof(rendererinfo[0]); i++) { - if (!rendererinfo[i]->description) + if (!rendererinfo[i] || !rendererinfo[i]->description) continue; //not valid in this build. :( for (j = 4-1; j >= 0; j--) { @@ -1954,7 +1992,7 @@ void R_SetRenderer_f (void) Con_Printf ("\nValid setrenderer parameters are:\n"); for (i = 0; i < sizeof(rendererinfo)/sizeof(rendererinfo[0]); i++) { - if (rendererinfo[i]->description) + if (rendererinfo[i] && rendererinfo[i]->description) Con_Printf("^[%s\\type\\/setrenderer %s^]^7: %s%s\n", rendererinfo[i]->name[0], rendererinfo[i]->name[0], rendererinfo[i]->description, (currentrendererstate.renderer == rendererinfo[i])?" ^2(current)":""); } return; diff --git a/engine/client/screen.h b/engine/client/screen.h index f7d0d3805..ba0fb401f 100644 --- a/engine/client/screen.h +++ b/engine/client/screen.h @@ -162,7 +162,7 @@ fte_inline conchar_t *Font_Decode(conchar_t *start, unsigned int *codeflags, uns if (*start & CON_LONGCHAR) if (!(*start & CON_RICHFORECOLOUR)) { - *codeflags = start[1]; + *codeflags = start[1] & CON_FLAGSMASK; *codepoint = ((start[0] & CON_CHARMASK)<<16) | (start[1] & CON_CHARMASK); return start+2; } diff --git a/engine/client/sys_droid.c b/engine/client/sys_droid.c index fc0b7ba54..0ec45def2 100644 --- a/engine/client/sys_droid.c +++ b/engine/client/sys_droid.c @@ -1,441 +1,419 @@ -#include -#include - -#include - -#include "quakedef.h" -#include -#include -#include -#include -#include - -#ifndef ANDROID -#error ANDROID wasnt defined -#endif - -#ifndef isDedicated -#ifdef SERVERONLY -qboolean isDedicated = true; -#else -qboolean isDedicated = false; -#endif -#endif -void *sys_window; /*public so the renderer can attach to the correct place*/ -static int sys_running = false; -int sys_glesversion; -extern int r_blockvidrestart; -float sys_dpi_x, sys_dpi_y; -int sys_soundflags; /*1 means active. 2 means reset (so claim that its not active for one frame to force a reset)*/ -static void *sys_memheap; -static unsigned int sys_lastframe; -static unsigned int vibrateduration; -static char errormessage[256]; -static char sys_basedir[MAX_OSPATH]; -static char sys_basepak[MAX_OSPATH]; -extern jmp_buf host_abort; - -cvar_t sys_vibrate = CVARFD("sys_vibrate", "1", CVAR_ARCHIVE, "Enables the system vibrator for damage events and such things. The value provided is a duration scaler."); -cvar_t sys_osk = CVAR("sys_osk", "0"); //to be toggled -cvar_t sys_keepscreenon = CVARFD("sys_keepscreenon", "1", CVAR_ARCHIVE, "If set, the screen will never darken. This might cost some extra battery power, but then so will running a 3d engine."); //to be toggled -cvar_t sys_orientation = CVARFD("sys_orientation", "landscape", CVAR_ARCHIVE, "Specifies what angle to render quake at.\nValid values are: sensor (autodetect), landscape, portrait, reverselandscape, reverseportrait"); -cvar_t sys_glesversion_cvar = CVARFD("sys_glesversion", "1", CVAR_ARCHIVE, "Specifies which version of gles to use. 1 or 2 are valid values."); -extern cvar_t vid_conautoscale; - - -#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, DISTRIBUTION"Droid", __VA_ARGS__)) -#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, DISTRIBUTION"Droid", __VA_ARGS__)) -#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, DISTRIBUTION"Droid", __VA_ARGS__)) - -void Sys_Vibrate(float count) -{ - if (count < 0) - count = 0; - vibrateduration += count*10*sys_vibrate.value; -} -void Sys_Vibrate_f(void) -{ - //input is in seconds, because this is quake. output is in ms. - vibrateduration += atof(Cmd_Argv(1)) * 1000; -} -JNIEXPORT jint JNICALL Java_com_fteqw_FTEDroidEngine_getvibrateduration(JNIEnv *env, jobject obj) -{ - unsigned int dur = vibrateduration; - vibrateduration = 0; - return dur; -} - -JNIEXPORT jstring JNICALL Java_com_fteqw_FTEDroidEngine_geterrormessage(JNIEnv *env, jobject obj) -{ - return (*env)->NewStringUTF(env, errormessage); -} -JNIEXPORT jstring JNICALL Java_com_fteqw_FTEDroidEngine_getpreferedorientation(JNIEnv *env, jobject obj) -{ - sys_orientation.modified = false; - sys_glesversion_cvar.modified = false; - return (*env)->NewStringUTF(env, sys_orientation.string); -} - -JNIEXPORT jint JNICALL Java_com_fteqw_FTEDroidEngine_getpreferedglesversion(JNIEnv *env, jobject obj) -{ - return sys_glesversion_cvar.ival; -} - -/*the java passes in all input directly via a 'UI' thread. we don't need to poll it at all*/ -void INS_Move(float *movements, int pnum) -{ -} -void INS_Commands(void) -{ -} +#include +#include + +#include + +#include "quakedef.h" +#include +#include +#include +#include +#include + +#ifndef ANDROID +#error ANDROID wasnt defined +#endif + +#ifndef isDedicated +#ifdef SERVERONLY +qboolean isDedicated = true; +#else +qboolean isDedicated = false; +#endif +#endif +static int sys_running = false; +extern int r_blockvidrestart; +float sys_dpi_x, sys_dpi_y; +int sys_soundflags; /*1 means active. 2 means reset (so claim that its not active for one frame to force a reset)*/ +static void *sys_memheap; +static unsigned int sys_lastframe; +static unsigned int vibrateduration; +static char errormessage[256]; +static char sys_basedir[MAX_OSPATH]; +static char sys_basepak[MAX_OSPATH]; +extern jmp_buf host_abort; +JNIEnv *sys_jenv; + +cvar_t sys_vibrate = CVARFD("sys_vibrate", "1", CVAR_ARCHIVE, "Enables the system vibrator for damage events and such things. The value provided is a duration scaler."); +cvar_t sys_osk = CVAR("sys_osk", "0"); //to be toggled +cvar_t sys_keepscreenon = CVARFD("sys_keepscreenon", "1", CVAR_ARCHIVE, "If set, the screen will never darken. This might cost some extra battery power, but then so will running a 3d engine."); //to be toggled +cvar_t sys_orientation = CVARFD("sys_orientation", "landscape", CVAR_ARCHIVE, "Specifies what angle to render quake at.\nValid values are: sensor (autodetect), landscape, portrait, reverselandscape, reverseportrait"); +extern cvar_t vid_conautoscale; +void VID_Register(void); + + +#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, DISTRIBUTION"Droid", __VA_ARGS__)) +#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, DISTRIBUTION"Droid", __VA_ARGS__)) +#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, DISTRIBUTION"Droid", __VA_ARGS__)) + +void Sys_Vibrate(float count) +{ + if (count < 0) + count = 0; + vibrateduration += count*10*sys_vibrate.value; +} +void Sys_Vibrate_f(void) +{ + //input is in seconds, because this is quake. output is in ms. + vibrateduration += atof(Cmd_Argv(1)) * 1000; +} +JNIEXPORT jint JNICALL Java_com_fteqw_FTEDroidEngine_getvibrateduration(JNIEnv *env, jobject obj) +{ + unsigned int dur = vibrateduration; + vibrateduration = 0; + return dur; +} + +JNIEXPORT jstring JNICALL Java_com_fteqw_FTEDroidEngine_geterrormessage(JNIEnv *env, jobject obj) +{ + return (*env)->NewStringUTF(env, errormessage); +} +JNIEXPORT jstring JNICALL Java_com_fteqw_FTEDroidEngine_getpreferedorientation(JNIEnv *env, jobject obj) +{ + sys_orientation.modified = false; + return (*env)->NewStringUTF(env, sys_orientation.string); +} + +/*the java passes in all input directly via a 'UI' thread. we don't need to poll it at all*/ +void INS_Move(float *movements, int pnum) +{ +} +void INS_Commands(void) +{ +} void INS_EnumerateDevices(void *ctx, void(*callback)(void *ctx, const char *type, const char *devicename, unsigned int *qdevid)) { -} -void INS_Init(void) -{ -} -void INS_ReInit(void) -{ -} -void INS_Shutdown(void) -{ -} -JNIEXPORT int JNICALL Java_com_fteqw_FTEDroidEngine_keypress(JNIEnv *env, jobject obj, - jint down, jint keycode, jint unicode) -{ - IN_KeyEvent(0, down, keycode, unicode); - return true; -} -JNIEXPORT void JNICALL Java_com_fteqw_FTEDroidEngine_motion(JNIEnv *env, jobject obj, - jint act, jint ptrid, jfloat x, jfloat y, jfloat size) -{ - if (act) - IN_KeyEvent(ptrid, act==1, K_MOUSE1, 0); - else - IN_MouseMove(ptrid, true, x, y, 0, size); -} - -JNIEXPORT jint JNICALL Java_com_fteqw_FTEDroidEngine_frame(JNIEnv *env, jobject obj, - jfloat ax, jfloat ay, jfloat az, - jfloat gx, jfloat gy, jfloat gz) -{ - int ret; - static vec3_t oacc; - static vec3_t ogyro; - - //if we had an error, don't even run a frame any more. - if (*errormessage || !sys_running) - { - Sys_Printf("Crashed or quit\n"); - return 8; - } - -// Sys_Printf("starting frame\n"); - - #ifdef SERVERONLY - SV_Frame(); - #else - unsigned int now = Sys_Milliseconds(); - double tdelta = (now - sys_lastframe) * 0.001; - if (oacc[0] != ax || oacc[1] != ay || oacc[2] != az) - { - //down: x= +9.8 - //left: y= -9.8 - //up: z= +9.8 - CSQC_Accelerometer(ax, ay, az); - oacc[0] = ax; - oacc[1] = ay; - oacc[2] = az; - } - if (ogyro[0] != gx || ogyro[1] != gy || ogyro[2] != gz) - { - CSQC_Gyroscope(gx * 180.0/M_PI, gy * 180.0/M_PI, gz * 180.0/M_PI); - ogyro[0] = gx; - ogyro[1] = gy; - ogyro[2] = gz; - } - Host_Frame(tdelta); - sys_lastframe = now; - #endif - - ret = 0; - if (Key_Dest_Has(kdm_console|kdm_message) || (!Key_Dest_Has(~kdm_game) && cls.state == ca_disconnected) || sys_osk.ival) - ret |= 1; - if (vibrateduration) - ret |= 2; - if (sys_keepscreenon.ival) - ret |= 4; - if (*errormessage || !sys_running) - ret |= 8; - if (sys_orientation.modified || sys_glesversion_cvar.modified) - ret |= 16; - if (sys_soundflags) - { - if (sys_soundflags & 2) - sys_soundflags &= ~2; - else - ret |= 32; - } -// Sys_Printf("frame ended\n"); - return ret; -} - -//tells us that our old gl context is about to be nuked. -JNIEXPORT void JNICALL Java_com_fteqw_FTEDroidEngine_killglcontext(JNIEnv *env, jobject obj) -{ - if (!sys_running) - return; - if (qrenderer == QR_NONE) - return; //not initialised yet... - Sys_Printf("Killing resources\n"); - R_ShutdownRenderer(true); - qrenderer = QR_NONE; - sys_glesversion = 0; - if (!r_blockvidrestart) - r_blockvidrestart = 3; //so video is restarted properly for the next frame -} - -//tells us that our old gl context got completely obliterated -JNIEXPORT void JNICALL Java_com_fteqw_FTEDroidEngine_newglcontext(JNIEnv *env, jobject obj) -{ - if (sys_running) - sys_running = 2; - - //fixme: wipe image handles, and vbos -} - -//called when the user tries to use us to open one of our file types -JNIEXPORT void JNICALL Java_com_fteqw_FTEDroidEngine_openfile(JNIEnv *env, jobject obj, - jstring openfile) -{ - const char *fname = (*env)->GetStringUTFChars(env, openfile, NULL); - if (sys_running) - Host_RunFile(fname, strlen(fname), NULL); - (*env)->ReleaseStringUTFChars(env, openfile, fname); -} - -//called for init or resizes -JNIEXPORT void JNICALL Java_com_fteqw_FTEDroidEngine_init(JNIEnv *env, jobject obj, - jint width, jint height, jfloat dpix, jfloat dpiy, jint glesversion, jstring japkpath, jstring jusrpath) -{ - const char *tmp; - - if (*errormessage) - return; - - vid.pixelwidth = width; - vid.pixelheight = height; - sys_glesversion = glesversion; - sys_dpi_x = dpix; - sys_dpi_y = dpiy; - if (sys_running) - { - if (!glesversion) - return; //gah! - Sys_Printf("vid size changed (%i %i gles%i)\n", width, height, glesversion); - if (!r_blockvidrestart) - { - //if our textures got destroyed, we need to reload them all - Cmd_ExecuteString("vid_reload\n", RESTRICT_LOCAL); - } - else - { - //otherwise we just need to set the size properly again. - Cvar_ForceCallback(&vid_conautoscale); - } - } - else - { - const char *args [] = - { - "ftedroid", - "-basepack", - sys_basepak, /*filled in later*/ - "", - "" - }; - quakeparms_t parms; - Sys_Printf("reinit\n"); - if (sys_memheap) - free(sys_memheap); - memset(&parms, 0, sizeof(parms)); - parms.basedir = sys_basedir; /*filled in later*/ - parms.argc = 3; - parms.argv = args; +} +void INS_Init(void) +{ +} +void INS_ReInit(void) +{ +} +void INS_Shutdown(void) +{ +} +JNIEXPORT int JNICALL Java_com_fteqw_FTEDroidEngine_keypress(JNIEnv *env, jobject obj, + jint down, jint keycode, jint unicode) +{ + IN_KeyEvent(0, down, keycode, unicode); + return true; +} +JNIEXPORT void JNICALL Java_com_fteqw_FTEDroidEngine_motion(JNIEnv *env, jobject obj, + jint act, jint ptrid, jfloat x, jfloat y, jfloat size) +{ + if (act) + IN_KeyEvent(ptrid, act==1, K_MOUSE1, 0); + else + IN_MouseMove(ptrid, true, x, y, 0, size); +} +JNIEXPORT void JNICALL Java_com_fteqw_FTEDroidEngine_accelerometer(JNIEnv *env, jobject obj, + jfloat x, jfloat y, jfloat z) +{ + IN_Accelerometer(0, x, y, z); +} +JNIEXPORT void JNICALL Java_com_fteqw_FTEDroidEngine_gryoscope(JNIEnv *env, jobject obj, + jfloat pitch, jfloat yaw, jfloat roll) +{ + IN_Gyroscope(0, pitch, yaw, roll); +} + +JNIEXPORT jint JNICALL Java_com_fteqw_FTEDroidEngine_frame(JNIEnv *env, jobject obj) +{ + int ret; + + sys_jenv = env; + + //if we had an error, don't even run a frame any more. + if (*errormessage) + { + Sys_Printf("Crashed: %s\n", errormessage); + return 8; + } + if (!sys_running) + { + Sys_Printf("quit\n"); + return 8; + } + +// Sys_Printf("starting frame\n"); + + #ifdef SERVERONLY + SV_Frame(); + #else + unsigned int now = Sys_Milliseconds(); + double tdelta = (now - sys_lastframe) * 0.001; + Host_Frame(tdelta); + sys_lastframe = now; + #endif + + ret = 0; + if (Key_Dest_Has(kdm_console|kdm_message) || (!Key_Dest_Has(~kdm_game) && cls.state == ca_disconnected) || sys_osk.ival) + ret |= 1; + if (vibrateduration) + ret |= 2; + if (sys_keepscreenon.ival) + ret |= 4; + if (*errormessage || !sys_running) + ret |= 8; + if (sys_orientation.modified) + ret |= 16; + if (sys_soundflags) + { + if (sys_soundflags & 2) + sys_soundflags &= ~2; + else + ret |= 32; + } +// Sys_Printf("frame ended\n"); + return ret; +} + +//called when the user tries to use us to open one of our file types +JNIEXPORT void JNICALL Java_com_fteqw_FTEDroidEngine_openfile(JNIEnv *env, jobject obj, + jstring openfile) +{ + const char *fname = (*env)->GetStringUTFChars(env, openfile, NULL); + if (sys_running) + Host_RunFile(fname, strlen(fname), NULL); + (*env)->ReleaseStringUTFChars(env, openfile, fname); +} + +qboolean r_forceheadless = true; + +//called for init or resizes +JNIEXPORT void JNICALL Java_com_fteqw_FTEDroidEngine_init(JNIEnv *env, jobject obj, + jfloat dpix, jfloat dpiy, jstring japkpath, jstring jusrpath) +{ + const char *tmp; + + if (*errormessage) + return; + + sys_dpi_x = dpix; + sys_dpi_y = dpiy; + if (!sys_running) + { + const char *args [] = + { + "ftedroid", + "-basepack", + sys_basepak, /*filled in later*/ + "", + "" + }; + quakeparms_t parms; + Sys_Printf("reinit\n"); + if (sys_memheap) + free(sys_memheap); + memset(&parms, 0, sizeof(parms)); + parms.basedir = sys_basedir; /*filled in later*/ + parms.argc = 3; + parms.argv = args; #ifdef CONFIG_MANIFEST_TEXT parms.manifest = CONFIG_MANIFEST_TEXT; -#endif - - tmp = (*env)->GetStringUTFChars(env, japkpath, NULL); - Q_strncpyz(sys_basepak, tmp, sizeof(sys_basepak)); - (*env)->ReleaseStringUTFChars(env, japkpath, tmp); - - tmp = (*env)->GetStringUTFChars(env, jusrpath, NULL); - Q_strncpyz(sys_basedir, tmp, sizeof(sys_basedir)); - (*env)->ReleaseStringUTFChars(env, jusrpath, tmp); - - - Sys_Printf("Starting up (apk=%s, usr=%s)\n", args[2], parms.basedir); - - COM_InitArgv(parms.argc, parms.argv); - TL_InitLanguages(sys_basedir); - #ifdef SERVERONLY - SV_Init(&parms); - #else - Host_Init(&parms); - #endif - sys_running = true; - sys_lastframe = Sys_Milliseconds(); - sys_orientation.modified = true; - - while(r_blockvidrestart == 1) - Java_com_fteqw_FTEDroidEngine_frame(env, obj, 0,0,0, 0,0,0); - - Sys_Printf("Engine started\n"); - } -} - -static int secbase; - -#ifdef _POSIX_TIMERS -double Sys_DoubleTime(void) -{ - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - - if (!secbase) - { - secbase = ts.tv_sec; - return ts.tv_nsec/1000000000.0; - } - return (ts.tv_sec - secbase) + ts.tv_nsec/1000000000.0; -} -unsigned int Sys_Milliseconds(void) -{ - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - - if (!secbase) - { - secbase = ts.tv_sec; - return ts.tv_nsec/1000000; - } - return (ts.tv_sec - secbase)*1000 + ts.tv_nsec/1000000; -} -#else -double Sys_DoubleTime(void) -{ - struct timeval tp; - struct timezone tzp; - - gettimeofday(&tp, &tzp); - - if (!secbase) - { - secbase = tp.tv_sec; - return tp.tv_usec/1000000.0; - } - - return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; -} -unsigned int Sys_Milliseconds(void) -{ - struct timeval tp; - struct timezone tzp; - - gettimeofday(&tp, &tzp); - - if (!secbase) - { - secbase = tp.tv_sec; - return tp.tv_usec/1000; - } - - return (tp.tv_sec - secbase)*1000 + tp.tv_usec/1000; -} -#endif - -void Sys_Shutdown(void) -{ - free(sys_memheap); -} -void Sys_Quit(void) -{ -#ifndef SERVERONLY - Host_Shutdown (); -#else - SV_Shutdown(); -#endif - - sys_running = false; - LOGI("%s", "quitting"); - - longjmp(host_abort, 1); - exit(0); -} -void Sys_Error (const char *error, ...) -{ - va_list argptr; - char string[1024]; - - va_start (argptr, error); - vsnprintf (string,sizeof(string)-1, error,argptr); - va_end (argptr); - if (!*string) - strcpy(string, "no error"); - - Q_strncpyz(errormessage, string, sizeof(errormessage)); - - LOGE("%s", string); - - longjmp(host_abort, 1); - exit(1); -} -void Sys_Printf (char *fmt, ...) -{ - va_list argptr; - char string[1024]; - - va_start (argptr, fmt); - vsnprintf (string,sizeof(string)-1, fmt,argptr); - va_end (argptr); - - LOGI("%s", string); -} -void Sys_Warn (char *fmt, ...) -{ - va_list argptr; - char string[1024]; - - va_start (argptr, fmt); - vsnprintf (string,sizeof(string)-1, fmt,argptr); - va_end (argptr); - - LOGW("%s", string); -} - -void Sys_CloseLibrary(dllhandle_t *lib) -{ - dlclose(lib); -} -dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs) -{ - dllhandle_t *h; - h = dlopen(name, RTLD_LAZY); - return h; -} -void *Sys_GetAddressForName(dllhandle_t *module, const char *exportname) -{ - return dlsym(module, exportname); -} -char *Sys_ConsoleInput (void) -{ - return NULL; -} -void Sys_mkdir (const char *path) //not all pre-unix systems have directories (including dos 1) -{ - mkdir(path, 0755); -} +#endif + + tmp = (*env)->GetStringUTFChars(env, japkpath, NULL); + Q_strncpyz(sys_basepak, tmp, sizeof(sys_basepak)); + (*env)->ReleaseStringUTFChars(env, japkpath, tmp); + + tmp = (*env)->GetStringUTFChars(env, jusrpath, NULL); + Q_strncpyz(sys_basedir, tmp, sizeof(sys_basedir)); + (*env)->ReleaseStringUTFChars(env, jusrpath, tmp); + + + Sys_Printf("Starting up (apk=%s, usr=%s)\n", args[2], parms.basedir); + + VID_Register(); + COM_InitArgv(parms.argc, parms.argv); + TL_InitLanguages(sys_basedir); + #ifdef SERVERONLY + SV_Init(&parms); + #else + Host_Init(&parms); + #endif + sys_running = true; + sys_lastframe = Sys_Milliseconds(); + sys_orientation.modified = true; + + while(r_blockvidrestart == 1) + Java_com_fteqw_FTEDroidEngine_frame(env, obj); + + Sys_Printf("Engine started\n"); + } +} + +static int secbase; + +#ifdef _POSIX_TIMERS +double Sys_DoubleTime(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + + if (!secbase) + { + secbase = ts.tv_sec; + return ts.tv_nsec/1000000000.0; + } + return (ts.tv_sec - secbase) + ts.tv_nsec/1000000000.0; +} +unsigned int Sys_Milliseconds(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + + if (!secbase) + { + secbase = ts.tv_sec; + return ts.tv_nsec/1000000; + } + return (ts.tv_sec - secbase)*1000 + ts.tv_nsec/1000000; +} +#else +double Sys_DoubleTime(void) +{ + struct timeval tp; + struct timezone tzp; + + gettimeofday(&tp, &tzp); + + if (!secbase) + { + secbase = tp.tv_sec; + return tp.tv_usec/1000000.0; + } + + return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; +} +unsigned int Sys_Milliseconds(void) +{ + struct timeval tp; + struct timezone tzp; + + gettimeofday(&tp, &tzp); + + if (!secbase) + { + secbase = tp.tv_sec; + return tp.tv_usec/1000; + } + + return (tp.tv_sec - secbase)*1000 + tp.tv_usec/1000; +} +#endif + +void Sys_Shutdown(void) +{ + free(sys_memheap); +} +void Sys_Quit(void) +{ +#ifndef SERVERONLY + Host_Shutdown (); +#else + SV_Shutdown(); +#endif + + sys_running = false; + LOGI("%s", "quitting"); + + longjmp(host_abort, 1); + exit(0); +} +void Sys_Error (const char *error, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr, error); + vsnprintf (string,sizeof(string)-1, error,argptr); + va_end (argptr); + if (!*string) + strcpy(string, "no error"); + + Q_strncpyz(errormessage, string, sizeof(errormessage)); + + LOGE("e: %s", string); + + longjmp(host_abort, 1); + exit(1); +} +void Sys_Printf (char *fmt, ...) +{ + va_list argptr; + static char linebuf[2048]; //android doesn't do \ns properly *sigh* + static char *endbuf = linebuf; //android doesn't do \ns properly *sigh* + char *e; + + va_start (argptr, fmt); + vsnprintf (endbuf,sizeof(linebuf)-(endbuf-linebuf)-1, fmt,argptr); + va_end (argptr); + endbuf += strlen(endbuf); + + while ((e = strchr(linebuf, '\n'))) + { + *e = 0; + LOGI("%s", linebuf); + memmove(linebuf, e+1, endbuf-(e+1)); + linebuf[endbuf-(e+1)] = 0; + endbuf -= (e+1)-linebuf; + } +} +void Sys_Warn (char *fmt, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr, fmt); + vsnprintf (string,sizeof(string)-1, fmt,argptr); + va_end (argptr); + + LOGW("w: %s", string); +} + +void Sys_CloseLibrary(dllhandle_t *lib) +{ + if (lib) + dlclose(lib); +} +void *Sys_GetAddressForName(dllhandle_t *module, const char *exportname) +{ + return dlsym(module, exportname); +} +dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs) +{ + size_t i; + dllhandle_t *h; + h = dlopen(va("%s.so", name), RTLD_LAZY|RTLD_LOCAL); + if (!h) + h = dlopen(name, RTLD_LAZY|RTLD_LOCAL); + + if (h && funcs) + { + for (i = 0; funcs[i].name; i++) + { + *funcs[i].funcptr = dlsym(h, funcs[i].name); + if (!*funcs[i].funcptr) + break; + } + if (funcs[i].name) + { + Sys_CloseLibrary(h); + h = NULL; + } + } + return h; +} +char *Sys_ConsoleInput (void) +{ + return NULL; +} +void Sys_mkdir (const char *path) //not all pre-unix systems have directories (including dos 1) +{ + mkdir(path, 0755); +} qboolean Sys_rmdir (const char *path) { if (rmdir (path) == 0) @@ -443,242 +421,243 @@ qboolean Sys_rmdir (const char *path) if (errno == ENOENT) return true; return false; -} -qboolean Sys_remove (const char *path) -{ - return !unlink(path); -} -qboolean Sys_Rename (const char *oldfname, const char *newfname) -{ - return !rename(oldfname, newfname); -} -void Sys_SendKeyEvents(void) -{ -} -void Sys_Init(void) -{ - Cmd_AddCommandD("sys_vibratetime", Sys_Vibrate_f, "Provides gamecode with a way to explicitly invoke the hardware vibrator in a simple way."); - Cvar_Register(&sys_vibrate, "android stuff"); - Cvar_Register(&sys_osk, "android stuff"); - Cvar_Register(&sys_keepscreenon, "android stuff"); - Cvar_Register(&sys_orientation, "android stuff"); - Cvar_Register(&sys_glesversion_cvar, "android stuff"); -} - -qboolean Sys_GetDesktopParameters(int *width, int *height, int *bpp, int *refreshrate) -{ - *width = 320; - *height = 240; - *bpp = 16; - *refreshrate = 60; - return false; -} -qboolean Sys_RandomBytes(qbyte *string, int len) -{ - qboolean res = false; - int fd = open("/dev/urandom", 0); - if (fd >= 0) - { - res = (read(fd, string, len) == len); - close(fd); - } - - return res; -} - -void Sys_ServerActivity(void) -{ - /*FIXME: flash window*/ -} -void Sys_Sleep (double seconds) -{ - struct timespec ts; - - ts.tv_sec = (time_t)seconds; - seconds -= ts.tv_sec; - ts.tv_nsec = seconds * 1000000000.0; - - nanosleep(&ts, NULL); -} -qboolean Sys_InitTerminal(void) -{ - /*switching to dedicated mode, show text window*/ - return false; -} -void Sys_CloseTerminal(void) -{ -} - -#define SYS_CLIPBOARD_SIZE 256 -static char clipboard_buffer[SYS_CLIPBOARD_SIZE] = {0}; -char *Sys_GetClipboard(void) -{ - return clipboard_buffer; -} -void Sys_CloseClipboard(char *bf) -{ -} -void Sys_SaveClipboard(char *text) -{ - Q_strncpyz(clipboard_buffer, text, SYS_CLIPBOARD_SIZE); -} - - -int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath) -{ - DIR *dir; - char apath[MAX_OSPATH]; - char file[MAX_OSPATH]; - char truepath[MAX_OSPATH]; - char *s; - struct dirent *ent; - struct stat st; - - //printf("path = %s\n", gpath); - //printf("match = %s\n", match); - - if (!gpath) - gpath = ""; - *apath = '\0'; - - Q_strncpyz(apath, match, sizeof(apath)); - for (s = apath+strlen(apath)-1; s >= apath; s--) - { - if (*s == '/') - { - s[1] = '\0'; - match += s - apath+1; - break; - } - } - if (s < apath) //didn't find a '/' - *apath = '\0'; - - Q_snprintfz(truepath, sizeof(truepath), "%s/%s", gpath, apath); - - - //printf("truepath = %s\n", truepath); - //printf("gamepath = %s\n", gpath); - //printf("apppath = %s\n", apath); - //printf("match = %s\n", match); - dir = opendir(truepath); - if (!dir) - { - Con_DPrintf("Failed to open dir %s\n", truepath); - return true; - } - do - { - ent = readdir(dir); - if (!ent) - break; - if (*ent->d_name != '.') - { - if (wildcmp(match, ent->d_name)) - { - Q_snprintfz(file, sizeof(file), "%s/%s", truepath, ent->d_name); - - if (stat(file, &st) == 0) - { - Q_snprintfz(file, sizeof(file), "%s%s%s", apath, ent->d_name, S_ISDIR(st.st_mode)?"/":""); - - if (!func(file, st.st_size, st.st_mtime, parm, spath)) - { - closedir(dir); - return false; - } - } - else - printf("Stat failed for \"%s\"\n", file); - } - } - } while(1); - closedir(dir); - - return true; -} - -#if 0 -#include -int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, qofs_t, void *), void *parm) -{ - qboolean go = true; - const char *f; - - struct AAssetDir *ad; - ad = AAssetManager_openDir(assetmgr, gpath); - - while(go && (f = AAssetDir_getNextFileName(ad))) - { - if (wildcmp(match, f)) - { -Sys_Printf("Found %s\n", f); - go = func(f, 0, parm); - } - } - - AAssetDir_close(ad); - return 0; -} - -typedef struct -{ - vfsfile_t funcs; - AAsset *handle; -} assetfile_t; -static int AF_ReadBytes(vfsfile_t *h, void *buf, int len) -{ - assetfile_t *f = (assetfile_t*)h; - return AAsset_read(f->handle, buf, len); -} -static qboolean AF_Seek(vfsfile_t *h, unsigned long offs) -{ - assetfile_t *f = (assetfile_t*)h; - AAsset_seek(f->handle, offs, SEEK_SET); - return true; -} -static unsigned long AF_Tell(vfsfile_t *h) -{ - assetfile_t *f = (assetfile_t*)h; - return AAsset_seek(f->handle, 0, SEEK_CUR); -} -static unsigned long AF_GetSize(vfsfile_t *h) -{ - assetfile_t *f = (assetfile_t*)h; - return AAsset_getLength(f->handle); -} - -static void AF_Close(vfsfile_t *h) -{ - assetfile_t *f = (assetfile_t*)h; - AAsset_close(f->handle); - Z_Free(f); -} -static void AF_Flush(vfsfile_t *h) -{ -} -vfsfile_t *Sys_OpenAsset(char *fname) -{ - assetfile_t *file; - AAsset *a; - a = AAssetManager_open(assetmgr, fname, AASSET_MODE_UNKNOWN); - if (!a) - { - Sys_Printf("Unable to open asset %s\n", fname); - return NULL; - } - Sys_Printf("opened asset %s\n", fname); - - file = Z_Malloc(sizeof(assetfile_t)); - file->funcs.ReadBytes = AF_ReadBytes; - file->funcs.WriteBytes = NULL; - file->funcs.Seek = AF_Seek; - file->funcs.Tell = AF_Tell; - file->funcs.GetLen = AF_GetSize; - file->funcs.Close = AF_Close; - file->funcs.Flush = AF_Flush; - file->handle = a; - - return (vfsfile_t*)file; -} -#endif +} +qboolean Sys_remove (const char *path) +{ + return !unlink(path); +} +qboolean Sys_Rename (const char *oldfname, const char *newfname) +{ + return !rename(oldfname, newfname); +} +void Sys_SendKeyEvents(void) +{ +} +void Sys_Init(void) +{ + Cmd_AddCommandD("sys_vibratetime", Sys_Vibrate_f, "Provides gamecode with a way to explicitly invoke the hardware vibrator in a simple way."); + Cvar_Register(&sys_vibrate, "android stuff"); + Cvar_Register(&sys_osk, "android stuff"); + Cvar_Register(&sys_keepscreenon, "android stuff"); + Cvar_Register(&sys_orientation, "android stuff"); +} + +qboolean Sys_GetDesktopParameters(int *width, int *height, int *bpp, int *refreshrate) +{ + *width = 320; + *height = 240; + *bpp = 16; + *refreshrate = 60; + return false; +} +qboolean Sys_RandomBytes(qbyte *string, int len) +{ + qboolean res = false; + int fd = open("/dev/urandom", 0); + if (fd >= 0) + { + res = (read(fd, string, len) == len); + close(fd); + } + + return res; +} + +void Sys_ServerActivity(void) +{ + /*FIXME: flash window*/ +} +#ifndef MULTITHREAD +void Sys_Sleep (double seconds) +{ + struct timespec ts; + + ts.tv_sec = (time_t)seconds; + seconds -= ts.tv_sec; + ts.tv_nsec = seconds * 1000000000.0; + + nanosleep(&ts, NULL); +} +#endif +qboolean Sys_InitTerminal(void) +{ + /*switching to dedicated mode, show text window*/ + return false; +} +void Sys_CloseTerminal(void) +{ +} + +#define SYS_CLIPBOARD_SIZE 256 +static char clipboard_buffer[SYS_CLIPBOARD_SIZE] = {0}; +char *Sys_GetClipboard(void) +{ + return clipboard_buffer; +} +void Sys_CloseClipboard(char *bf) +{ +} +void Sys_SaveClipboard(char *text) +{ + Q_strncpyz(clipboard_buffer, text, SYS_CLIPBOARD_SIZE); +} + + +int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath) +{ + DIR *dir; + char apath[MAX_OSPATH]; + char file[MAX_OSPATH]; + char truepath[MAX_OSPATH]; + char *s; + struct dirent *ent; + struct stat st; + + //printf("path = %s\n", gpath); + //printf("match = %s\n", match); + + if (!gpath) + gpath = ""; + *apath = '\0'; + + Q_strncpyz(apath, match, sizeof(apath)); + for (s = apath+strlen(apath)-1; s >= apath; s--) + { + if (*s == '/') + { + s[1] = '\0'; + match += s - apath+1; + break; + } + } + if (s < apath) //didn't find a '/' + *apath = '\0'; + + Q_snprintfz(truepath, sizeof(truepath), "%s/%s", gpath, apath); + + + //printf("truepath = %s\n", truepath); + //printf("gamepath = %s\n", gpath); + //printf("apppath = %s\n", apath); + //printf("match = %s\n", match); + dir = opendir(truepath); + if (!dir) + { + Con_DPrintf("Failed to open dir %s\n", truepath); + return true; + } + do + { + ent = readdir(dir); + if (!ent) + break; + if (*ent->d_name != '.') + { + if (wildcmp(match, ent->d_name)) + { + Q_snprintfz(file, sizeof(file), "%s/%s", truepath, ent->d_name); + + if (stat(file, &st) == 0) + { + Q_snprintfz(file, sizeof(file), "%s%s%s", apath, ent->d_name, S_ISDIR(st.st_mode)?"/":""); + + if (!func(file, st.st_size, st.st_mtime, parm, spath)) + { + closedir(dir); + return false; + } + } + else + printf("Stat failed for \"%s\"\n", file); + } + } + } while(1); + closedir(dir); + + return true; +} + +#if 0 +#include +int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, qofs_t, void *), void *parm) +{ + qboolean go = true; + const char *f; + + struct AAssetDir *ad; + ad = AAssetManager_openDir(assetmgr, gpath); + + while(go && (f = AAssetDir_getNextFileName(ad))) + { + if (wildcmp(match, f)) + { +Sys_Printf("Found %s\n", f); + go = func(f, 0, parm); + } + } + + AAssetDir_close(ad); + return 0; +} + +typedef struct +{ + vfsfile_t funcs; + AAsset *handle; +} assetfile_t; +static int AF_ReadBytes(vfsfile_t *h, void *buf, int len) +{ + assetfile_t *f = (assetfile_t*)h; + return AAsset_read(f->handle, buf, len); +} +static qboolean AF_Seek(vfsfile_t *h, unsigned long offs) +{ + assetfile_t *f = (assetfile_t*)h; + AAsset_seek(f->handle, offs, SEEK_SET); + return true; +} +static unsigned long AF_Tell(vfsfile_t *h) +{ + assetfile_t *f = (assetfile_t*)h; + return AAsset_seek(f->handle, 0, SEEK_CUR); +} +static unsigned long AF_GetSize(vfsfile_t *h) +{ + assetfile_t *f = (assetfile_t*)h; + return AAsset_getLength(f->handle); +} + +static void AF_Close(vfsfile_t *h) +{ + assetfile_t *f = (assetfile_t*)h; + AAsset_close(f->handle); + Z_Free(f); +} +static void AF_Flush(vfsfile_t *h) +{ +} +vfsfile_t *Sys_OpenAsset(char *fname) +{ + assetfile_t *file; + AAsset *a; + a = AAssetManager_open(assetmgr, fname, AASSET_MODE_UNKNOWN); + if (!a) + { + Sys_Printf("Unable to open asset %s\n", fname); + return NULL; + } + Sys_Printf("opened asset %s\n", fname); + + file = Z_Malloc(sizeof(assetfile_t)); + file->funcs.ReadBytes = AF_ReadBytes; + file->funcs.WriteBytes = NULL; + file->funcs.Seek = AF_Seek; + file->funcs.Tell = AF_Tell; + file->funcs.GetLen = AF_GetSize; + file->funcs.Close = AF_Close; + file->funcs.Flush = AF_Flush; + file->handle = a; + + return (vfsfile_t*)file; +} +#endif diff --git a/engine/common/common.c b/engine/common/common.c index 08e8c74ad..14cf0d369 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -1790,7 +1790,7 @@ float MSG_ReadAngle (void) } } -void MSG_ReadDeltaUsercmd (usercmd_t *from, usercmd_t *move) +void MSG_ReadDeltaUsercmd (usercmd_t *from, usercmd_t *move, int protover) { int bits; @@ -1798,31 +1798,61 @@ void MSG_ReadDeltaUsercmd (usercmd_t *from, usercmd_t *move) bits = MSG_ReadByte (); -// read current angles - if (bits & CM_ANGLE1) - move->angles[0] = MSG_ReadShort (); - if (bits & CM_ANGLE2) - move->angles[1] = MSG_ReadShort (); - if (bits & CM_ANGLE3) - move->angles[2] = MSG_ReadShort (); + if (protover <= 26) + { + if (bits & CM_ANGLE1) + move->angles[0] = MSG_ReadShort(); + if (1) + move->angles[1] = MSG_ReadShort(); + if (bits & CM_ANGLE3) + move->angles[2] = MSG_ReadShort(); -// read movement - if (bits & CM_FORWARD) - move->forwardmove = MSG_ReadShort (); - if (bits & CM_SIDE) - move->sidemove = MSG_ReadShort (); - if (bits & CM_UP) - move->upmove = MSG_ReadShort (); + if (bits & CM_FORWARD) + move->forwardmove = MSG_ReadByte()<<3; + if (bits & CM_SIDE) + move->sidemove = MSG_ReadByte()<<3; + if (bits & CM_UP) + move->upmove = MSG_ReadByte()<<3; -// read buttons - if (bits & CM_BUTTONS) - move->buttons = MSG_ReadByte (); + // read buttons + if (bits & CM_BUTTONS) + move->buttons = MSG_ReadByte(); - if (bits & CM_IMPULSE) - move->impulse = MSG_ReadByte (); + if (bits & CM_IMPULSE) + move->impulse = MSG_ReadByte(); // read time to run command - move->msec = MSG_ReadByte (); + if (bits & CM_ANGLE2) + move->msec = MSG_ReadByte(); + } + else + { +// read current angles + if (bits & CM_ANGLE1) + move->angles[0] = MSG_ReadShort(); + if (bits & CM_ANGLE2) + move->angles[1] = MSG_ReadShort(); + if (bits & CM_ANGLE3) + move->angles[2] = MSG_ReadShort(); + +// read movement + if (bits & CM_FORWARD) + move->forwardmove = MSG_ReadShort(); + if (bits & CM_SIDE) + move->sidemove = MSG_ReadShort(); + if (bits & CM_UP) + move->upmove = MSG_ReadShort(); + +// read buttons + if (bits & CM_BUTTONS) + move->buttons = MSG_ReadByte(); + + if (bits & CM_IMPULSE) + move->impulse = MSG_ReadByte(); + +// read time to run command + move->msec = MSG_ReadByte(); + } } void MSGQ2_ReadDeltaUsercmd (usercmd_t *from, usercmd_t *move) @@ -3159,6 +3189,9 @@ conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t conchar_t ext; conchar_t *oldout = out; +#ifndef NOLEGACY + extern cvar_t dpcompat_console; +#endif if (flags & PFS_EZQUAKEMARKUP) { @@ -3182,7 +3215,11 @@ conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t return out; #endif - if (*str == 1 || *str == 2) + if (*str == 1 || *str == 2 +#ifndef NOLEGACY + || (*str == 3 && dpcompat_console.ival) +#endif + ) { defaultflags ^= CON_2NDCHARSETTEXT; str++; @@ -5833,6 +5870,316 @@ unsigned int COM_RemapMapChecksum(model_t *model, unsigned int checksum) return checksum; } +/* + Info Buffers +*/ +/* +typedef struct +{ + struct infobuf_s + { + char *name; + char *value; + } *keys; + size_t numkeys; + qboolean nolegacy; //no \\ or \" limitations. +} infobuf_t; +char *InfoBuf_GetValue (infobuf_t *info, const char *key, char *outbuf, size_t outsize) +{ + size_t k; + for (k = 0; k < info->numkeys; k++) + { + if (!strcmp(info->keys[k].name, key)) + { + Q_strncpyz(outbuf, info->keys[k].value, outsize); + return outbuf; + } + } + *outbuf = 0; + return outbuf; +} +char *InfoBuf_GetValueTmp (infobuf_t *info, const char *key) +{ + static char value[4][1024]; // use multiple buffers so compares work without stomping on each other + static int valueindex; + COM_AssertMainThread("InfoBuf_GetValue"); + valueindex = (valueindex+1)&3; + return InfoBuf_GetValue(info, key, value[valueindex], sizeof(value[valueindex])); +} +qboolean InfoBuf_RemoveKey (infobuf_t *info, const char *key) +{ + size_t k; + for (k = 0; k < info->numkeys; k++) + { + if (!strcmp(info->keys[k].name, key)) + { + Z_Free(info->keys[k].name); + Z_Free(info->keys[k].value); + info->numkeys--; + memmove(info->keys+k+0, info->keys+k+1, sizeof(*info->keys) * (info->numkeys-k)); + return true; //only one entry per key, so we can give up here + } + } + return false; +} +void InfoBuf_SetKey (infobuf_t *info, const char *key, const char *val, qboolean force) +{ + qboolean removed; + size_t k; + + if (!val) + val = ""; + + if (!info->nolegacy) + { + //block invalid keys + //\\ makes parsing really really messy and isn't supported by most clients (although we could do it anyway) + //\" requires string escapes, again compat issues. + //0xff bugs out vanilla. + if (strchr(key, '\\') || strchr(key, '\"') || strchr(key, 0xff)) + return; + if (strchr(val, '\\') || strchr(val, '\"') || strchr(val, 0xff)) + return; + + if (strlen(key) >= 64) + return; //key length limits is a thing in vanilla qw. + if (strlen(val) >= 512) + return; //value length limits is a thing in vanilla qw. + //note that qw reads values up to 512, but only sets them up to 64 bytes... + //probably just so that people don't spot buffer overflows so easily. + } + + // *keys are meant to be secure (or rather unsettable by the user, preventing spoofing of stuff like *ip) + // but note that this is pointless as a hacked client can send whatever initial *keys it wants (they are blocked mid-connection at least) + // * userinfos are always sent even to clients that can't support large infokey blobs + if (*key == '*' && !force) + return; + + removed = InfoBuf_RemoveKey(info, key); + if (*val) + { + k = info->numkeys; + if (removed) + info->numkeys+=1; //the memory is still allocated, because we're too lazy to free it. + else + { + if (!ZF_ReallocElements((void**)&info->keys, &info->numkeys, info->numkeys+1, sizeof(*info->keys))) + return; //out of memory! + } + info->keys[k].name = Z_StrDup(key); + info->keys[k].value = Z_StrDup(val); + } +} +void InfoBuf_Clear(infobuf_t *info, qboolean all) +{//if all is false, leaves *keys + size_t k; + for (k = info->numkeys-1; k >= 0; k--) + { + if (all || *info->keys[k].name != '*') + { + Z_Free(info->keys[k].name); + Z_Free(info->keys[k].value); + info->numkeys--; + memmove(info->keys+k+0, info->keys+k+1, sizeof(*info->keys) * (info->numkeys-k)); + } + } + if (!info->numkeys) + { + Z_Free(info->keys); + info->keys = NULL; + } +} +void InfoBuf_FromString(infobuf_t *info, const char *infostring) +{ + if (*infostring++ != '\\') + return; //invalid... not an info string + while (*infostring) + { + const char *keystart = infostring; + const char *keyend; + const char *valstart; + const char *valend; + char *key; + char *val; + char *o; + while (*infostring) + { + if (*infostring == '\\') + { + if (infostring[1] == '\\') + infostring += 2; + break; + } + else infostring += 1; + } + keyend = infostring; + if (*infostring++ != '\\') + break; //missing value... + valstart = infostring; + while (*infostring) + { + if (*infostring == '\\') + { + if (infostring[1] == '\\') + infostring += 2; + break; + } + else infostring += 1; + } + valend = infostring; + // *infostring might be '\\' or '\0'. doesn't really matter + + if (!strncmp(keystart, " \\\\", 3)) + keystart += 3; + if (!strncmp(valstart, " \\\\", 3)) + valstart += 3; + + key = Z_Malloc(1+keyend-keystart); + for (o = key; keystart < keyend; ) + { + if (keystart[0] == '\\') + keystart+=1; + *o++ = *keystart++; + } + *o=0; + val = Z_Malloc(1+valend-valstart); + for (o = val; valstart < valend; ) + { + if (valstart[0] == '\\') + valstart+=1; + *o++ = *valstart++; + } + *o=0; + InfoBuf_SetKey(info, key, val, true); + Z_Free(key); + Z_Free(val); + } +} +static size_t InfoBuf_ToStringToken(const char *n, char *out, char *end) +{ + size_t r = 1; + if (out < end) + *out++ = '\\'; + if (*n == '\\' || (n[0] == ' ' && n[1] == '\\')) + { //" \\" prefix is stripped by the reader, and present to allow keys or values with a leading \\ in a well-defined-but-annoying way + // (vanilla qw doesn't allow double-backslash anywhere in infostrings) + r += 3; + if (out < end) + *out++ = ' '; + if (out < end) + *out++ = '\\'; + if (out < end) + *out++ = '\\'; + } + while (*n) + { + if (*n == '\\') + { + if (out < end) + *out++ = '\\'; + } + if (out < end) + *out++ = *n; + n++; + } + return r; +} +size_t InfoBuf_ToString(infobuf_t *info, char *infostring, size_t maxsize, const char **priority, const char **ignore, const char **exclusive) +{ + //if infostring is null, returns the needed buffer size + //\foo\\\bar is ambiguous. and interpreted as foo\ + bar + //\foo\ \\\\bar is interpreted as foo + \bar - leading " \\" is ignored if present. + + //FIXME: add a filter, for short/compated buffers. prioritisation or something + size_t k, r = 1, l; + char *o = infostring; + char *e = infostring?infostring + maxsize-1:infostring; + int pri, p; + for (pri = 0; pri < 2; pri++) + { + for (k = 0; k < info->numkeys; k++) + { + if (exclusive) + { + for (l = 0; exclusive[l]; l++) + if (!strcmp(exclusive[l], info->keys[k].name)) + break; + if (!exclusive[l]) + continue; //ignore when not in the list + } + if (ignore) + { + for (l = 0; ignore[l]; l++) + if (!strcmp(ignore[l], info->keys[k].name)) + break; + if (ignore[l]) + continue; //ignore when in the list + } + if (priority) + { + for (l = 0; priority[l]; l++) + if (!strcmp(priority[l], info->keys[k].name)) + break; + if (priority[l]) + p = 0; //high priority + else + p = 1; //low priority + } + else + { + if (*info->keys[k].name == '*') + p = 0; //keys that cannot be changed always have the highest priority (fixme: useless stuff like version doesn't need to be in here + else + p = 1; + } + if (pri != p) + continue; + + l = InfoBuf_ToStringToken(info->keys[k].name, o, e); + l += InfoBuf_ToStringToken(info->keys[k].value, o, e); + r += l; + if (o && o + l < e) + o += l; + } + } + *o = 0; + return r; +} + +void InfoBuf_WriteToFile(vfsfile_t *f, infobuf_t *info, const char *commandname, int cvarflags) +{ + char buffer[1024]; + const char *key; + const char *val; + cvar_t *var; + size_t k; + + for (k = 0; k < info->numkeys; k++) + { + key = info->keys[k].name; + val = info->keys[k].value; + if (*key == '*') //unsettable, so don't write it for later setting. + continue; + + if (cvarflags) + { + var = Cvar_FindVar(key); + if (var && var->flags & cvarflags) + continue; //this is saved via a cvar. + } + + VFS_WRITE(f, commandname, strlen(commandname)); + VFS_WRITE(f, " ", 1); + key = COM_QuotedString(key, buffer, sizeof(buffer), false); + VFS_WRITE(f, key, strlen(key)); + VFS_WRITE(f, " ", 1); + val = COM_QuotedString(val, buffer, sizeof(buffer), false); + VFS_WRITE(f, val, strlen(val)); + VFS_WRITE(f, "\n", 1); + } +} +*/ + /* ===================================================================== diff --git a/engine/common/common.h b/engine/common/common.h index 8a1c43051..c2e07a1a1 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -37,11 +37,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #define qintptr_t intptr_t #define quintptr_t uintptr_t + #define qint16_t int16_t + #define quint16_t uint16_t #define qint32_t int32_t #define quint32_t uint32_t #define qint64_t int64_t #define quint64_t uint64_t #else + #define qint16_t short + #define quint16_t unsigned short #define qint32_t int #define quint32_t unsigned qint32_t #if defined(_WIN64) @@ -289,7 +293,7 @@ float MSG_ReadCoord (void); void MSG_ReadPos (float *pos); float MSG_ReadAngle (void); float MSG_ReadAngle16 (void); -void MSG_ReadDeltaUsercmd (struct usercmd_s *from, struct usercmd_s *cmd); +void MSG_ReadDeltaUsercmd (struct usercmd_s *from, struct usercmd_s *cmd, int qwprotocolver); void MSGQ2_ReadDeltaUsercmd (struct usercmd_s *from, struct usercmd_s *move); void MSG_ReadData (void *data, int len); void MSG_ReadSkip (int len); @@ -753,8 +757,9 @@ void T_FreeInfoStrings(void); char *T_GetInfoString(int num); struct po_s; +struct po_s *PO_Create(void); +void PO_Merge(struct po_s *po, vfsfile_t *file); const char *PO_GetText(struct po_s *po, const char *msg); -struct po_s *PO_Load(vfsfile_t *file); void PO_Close(struct po_s *po); // diff --git a/engine/common/console.h b/engine/common/console.h index 0094fda30..a429dbb8c 100644 --- a/engine/common/console.h +++ b/engine/common/console.h @@ -239,7 +239,7 @@ void Con_ToggleConsole_Force(void); int Con_ExecuteLine(console_t *con, char *line); //takes normal console commands int Con_Navigate(console_t *con, char *line); //special webbrowser hacks - +vfsfile_t *Con_POpen(char *conname); void Con_CycleConsole (void); int Con_IsActive (console_t *con); void Con_Destroy (console_t *con); diff --git a/engine/common/log.c b/engine/common/log.c index d611184d3..a41a8e463 100644 --- a/engine/common/log.c +++ b/engine/common/log.c @@ -775,10 +775,11 @@ qboolean CertLog_ConnectOkay(const char *hostname, void *cert, size_t certsize) memcpy(ctx->cert, cert, certsize); strcpy(ctx->hostname, hostname); + //FIXME: display some sort of fingerprint if (!l) - M_Menu_Prompt(CertLog_Add_Prompted, ctx, "Server certificate is new", "Trust", NULL, "Disconnect"); + M_Menu_Prompt(CertLog_Add_Prompted, ctx, va("%s\nServer certificate is\nself-signed", hostname), "Trust", NULL, "Disconnect"); else - M_Menu_Prompt(CertLog_Add_Prompted, ctx, "^1Server certificate HAS CHANGED\nZomg\nFlee in Terror", "ReTrust", NULL, "Disconnect"); + M_Menu_Prompt(CertLog_Add_Prompted, ctx, va("%s\n^1Server certificate HAS CHANGED\nZomg\n^bFlee in Terror", hostname), "ReTrust", NULL, "Disconnect"); } return false; //can't connect yet... } diff --git a/engine/common/net_ssl_winsspi.c b/engine/common/net_ssl_winsspi.c index 73402c366..ce483258d 100644 --- a/engine/common/net_ssl_winsspi.c +++ b/engine/common/net_ssl_winsspi.c @@ -405,12 +405,14 @@ char *narrowen(char *out, size_t outlen, wchar_t *wide); static DWORD VerifyKnownCertificates(DWORD status, wchar_t *domain, qbyte *data, size_t datasize, qboolean datagram) { int i; +#ifndef SERVERONLY + char realdomain[256]; +#endif if (datagram) { if (status == CERT_E_UNTRUSTEDROOT || SUCCEEDED(status)) { #ifndef SERVERONLY - char realdomain[256]; if (CertLog_ConnectOkay(narrowen(realdomain, sizeof(realdomain), domain), data, datasize)) status = SEC_E_OK; else @@ -455,6 +457,15 @@ static DWORD VerifyKnownCertificates(DWORD status, wchar_t *domain, qbyte *data, } } } + +#ifndef SERVERONLY + //self-signed and expired certs are understandable in many situations. + //prompt and cache (although this connection attempt will fail). + if (status == CERT_E_UNTRUSTEDROOT || status == CERT_E_UNTRUSTEDTESTROOT || status == CERT_E_EXPIRED) + if (CertLog_ConnectOkay(narrowen(realdomain, sizeof(realdomain), domain), data, datasize)) + return SEC_E_OK; +#endif + return status; } diff --git a/engine/common/plugin.c b/engine/common/plugin.c index 62395c06f..7de0dd227 100644 --- a/engine/common/plugin.c +++ b/engine/common/plugin.c @@ -1155,6 +1155,17 @@ static void Plug_DownloadComplete(struct dl_download *dl) } #endif +static qintptr_t VARGS Plug_Con_POpen(void *offset, quintptr_t mask, const qintptr_t *arg) +{ + char *conname = VM_POINTER(arg[0]); + int handle; + if (!currentplug) + return -3; //streams depend upon current plugin context. which isn't valid in a thread. + handle = Plug_NewStreamHandle(STREAM_VFS); + pluginstreamarray[handle].vfs = Con_POpen(conname); + return handle; +} + qintptr_t VARGS Plug_FS_Open(void *offset, quintptr_t mask, const qintptr_t *arg) { //modes: @@ -1613,6 +1624,7 @@ void Plug_Initialise(qboolean fromgamedir) Plug_RegisterBuiltin("Plug_ExportNative", Plug_ExportNative, PLUG_BIF_DLLONLY); Plug_RegisterBuiltin("Plug_GetPluginName", Plug_GetPluginName, 0); Plug_RegisterBuiltin("Con_Print", Plug_Con_Print, 0); //printf is not possible - qvm floats are never doubles, vararg floats in a cdecl call are always converted to doubles. + Plug_RegisterBuiltin("Con_POpen", Plug_Con_POpen, PLUG_BIF_DLLONLY); Plug_RegisterBuiltin("Sys_Error", Plug_Sys_Error, 0); Plug_RegisterBuiltin("Sys_Milliseconds", Plug_Sys_Milliseconds, 0); Plug_RegisterBuiltin("Com_Error", Plug_Sys_Error, 0); //make zquake programmers happy. diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index dedfae9e7..0558c6c3e 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -18,7 +18,7 @@ cvar_t sv_gameplayfix_nolinknonsolid = CVARD("sv_gameplayfix_nolinknonsolid", "1 cvar_t sv_gameplayfix_blowupfallenzombies = CVARD("sv_gameplayfix_blowupfallenzombies", "0", "Allow findradius to find non-solid entities. This may break certain mods."); cvar_t dpcompat_findradiusarealinks = CVARD("dpcompat_findradiusarealinks", "0", "Use the world collision info to accelerate findradius instead of looping through every single entity. May actually be slower for large radiuses, or fail to find entities which have not been linked properly with setorigin."); #ifndef NOLEGACY -cvar_t dpcompat_strcatlimitation = CVARD("dpcompat_crippledstrcat", "", "When set, cripples strcat (and related function) string lengths to the value specified.\nSet to 16383 to replicate DP's limit, otherwise leave as 0 to avoid limits."); +cvar_t dpcompat_strcat_limit = CVARD("dpcompat_strcat_limit", "", "When set, cripples strcat (and related function) string lengths to the value specified.\nSet to 16383 to replicate DP's limit, otherwise leave as 0 to avoid limits."); #endif cvar_t pr_droptofloorunits = CVARD("pr_droptofloorunits", "256", "Distance that droptofloor is allowed to drop to be considered successul."); cvar_t pr_brokenfloatconvert = CVAR("pr_brokenfloatconvert", "0"); @@ -51,7 +51,7 @@ void PF_Common_RegisterCvars(void) Cvar_Register (&sv_gameplayfix_nolinknonsolid, cvargroup_progs); Cvar_Register (&dpcompat_findradiusarealinks, cvargroup_progs); #ifndef NOLEGACY - Cvar_Register (&dpcompat_strcatlimitation, cvargroup_progs); + Cvar_Register (&dpcompat_strcat_limit, cvargroup_progs); #endif Cvar_Register (&pr_droptofloorunits, cvargroup_progs); Cvar_Register (&pr_brokenfloatconvert, cvargroup_progs); @@ -3600,10 +3600,10 @@ void QCBUILTIN PF_strcat (pubprogfuncs_t *prinst, struct globalvars_s *pr_global len += l[i]; #ifndef NOLEGACY - if (dpcompat_strcatlimitation.ival && len > dpcompat_strcatlimitation.ival) + if (dpcompat_strcat_limit.ival && len > dpcompat_strcat_limit.ival) { - l[i] -= len-dpcompat_strcatlimitation.ival; - len -= len-dpcompat_strcatlimitation.ival; + l[i]-= len-dpcompat_strcat_limit.ival; + len -= len-dpcompat_strcat_limit.ival; } #endif } @@ -6251,7 +6251,7 @@ void PDECL PR_FoundDoTranslateGlobal(pubprogfuncs_t *progfuncs, char *name, eval //called after each progs is loaded void PR_ProgsAdded(pubprogfuncs_t *prinst, int newprogs, const char *modulename) { - vfsfile_t *f = NULL; + vfsfile_t *f = NULL, *f2 = NULL; char lang[64], *h; extern cvar_t language; if (!prinst || newprogs < 0) @@ -6265,8 +6265,11 @@ void PR_ProgsAdded(pubprogfuncs_t *prinst, int newprogs, const char *modulename) { if (!*lang) break; - f = FS_OpenVFS(va("%s.%s.po", modulename, lang), "rb", FS_GAME); - if (f) + if (!f) + f = FS_OpenVFS(va("%s.%s.po", modulename, lang), "rb", FS_GAME); + if (!f2) + f2 = FS_OpenVFS(va("common.%s.po", lang), "rb", FS_GAME); + if (f && f2) break; h = strchr(lang, '_'); if (h) @@ -6276,9 +6279,11 @@ void PR_ProgsAdded(pubprogfuncs_t *prinst, int newprogs, const char *modulename) } } - if (f) + if (f || f2) { - void *pofile = PO_Load(f); + void *pofile = PO_Create(); + PO_Merge(pofile, f); + PO_Merge(pofile, f2); prinst->FindPrefixGlobals (prinst, newprogs, "dotranslate_", PR_FoundDoTranslateGlobal, pofile); PO_Close(pofile); } diff --git a/engine/common/qvm.c b/engine/common/qvm.c index ae96363af..cc75593df 100644 --- a/engine/common/qvm.c +++ b/engine/common/qvm.c @@ -1136,7 +1136,7 @@ qintptr_t VARGS VM_Call(vm_t *vm, qintptr_t instruction, ...) arg[3]=va_arg(argptr, qintptr_t); arg[4]=va_arg(argptr, qintptr_t); arg[5]=va_arg(argptr, qintptr_t); - arg[6]=va_arg(argptr, qintptr_t); + arg[6]=va_arg(argptr, qintptr_t); arg[7]=va_arg(argptr, qintptr_t); va_end(argptr); diff --git a/engine/common/translate.c b/engine/common/translate.c index 8a15f9b32..4fad87fc6 100644 --- a/engine/common/translate.c +++ b/engine/common/translate.c @@ -23,7 +23,7 @@ static void QDECL TL_LanguageChanged(struct cvar_s *var, char *oldvalue) #endif } -cvar_t language = CVARFC("lang", sys_language, CVAR_USERINFO, TL_LanguageChanged); +cvar_t language = CVARAFC("lang", sys_language, "prvm_language", CVAR_USERINFO, TL_LanguageChanged); void TranslateInit(void) { @@ -77,7 +77,13 @@ static int TL_LoadLanguage(char *lang) return TL_LoadLanguage(lang); } languages[j].name = strdup(lang); - languages[j].po = f?PO_Load(f):NULL; + languages[j].po = NULL; + + if (f) + { + languages[j].po = PO_Create(); + PO_Merge(languages[j].po, f); + } return j; } @@ -328,19 +334,16 @@ static void PO_AddText(struct po_s *po, const char *orig, const char *trans) line->next = po->lines; po->lines = line; } -struct po_s *PO_Load(vfsfile_t *file) +void PO_Merge(struct po_s *po, vfsfile_t *file) { - struct po_s *po; - unsigned int buckets = 1024; char *instart, *in, *end; int inlen; char msgid[32768]; char msgstr[32768]; qboolean allowblanks = !!COM_CheckParm("-translatetoblank"); - - po = Z_Malloc(sizeof(*po) + Hash_BytesForBuckets(buckets)); - Hash_InitTable(&po->hash, buckets, po+1); + if (!file) + return; inlen = file?VFS_GETLEN(file):0; instart = in = BZ_Malloc(inlen+1); @@ -409,6 +412,14 @@ struct po_s *PO_Load(vfsfile_t *file) } BZ_Free(instart); +} +struct po_s *PO_Create(void) +{ + struct po_s *po; + unsigned int buckets = 1024; + + po = Z_Malloc(sizeof(*po) + Hash_BytesForBuckets(buckets)); + Hash_InitTable(&po->hash, buckets, po+1); return po; } void PO_Close(struct po_s *po) diff --git a/engine/dotnet2005/ftequake.vcproj b/engine/dotnet2005/ftequake.vcproj index 4e92e8b40..9b3d9aa84 100644 --- a/engine/dotnet2005/ftequake.vcproj +++ b/engine/dotnet2005/ftequake.vcproj @@ -1418,7 +1418,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - name, '#'); if (mask) Q_strncatz(args, mask, sizeof(args)); diff --git a/engine/gl/gl_vidcommon.c b/engine/gl/gl_vidcommon.c index b7d4cc0a2..a2ca2f54d 100644 --- a/engine/gl/gl_vidcommon.c +++ b/engine/gl/gl_vidcommon.c @@ -1251,11 +1251,15 @@ static const char *glsl_hdrs[] = "#ifndef SPECEXP\n" "#define SPECEXP 1.0\n" "#endif\n" - "#define FTE_SPECULAR_EXPONENT (32.0*float(SPECEXP))\n" + "#ifndef SPECULAR_BASE_POW\n" + "#define SPECULAR_BASE_POW 32.0\n" + "#define SPECULAR_BASE_MUL 1.0\n" + "#endif\n" + "#define FTE_SPECULAR_EXPONENT (SPECULAR_BASE_POW*float(SPECEXP))\n" "#ifndef SPECMUL\n" "#define SPECMUL 1.0\n" "#endif\n" - "#define FTE_SPECULAR_MULTIPLIER (cvar_gl_specular*float(SPECMUL))\n" + "#define FTE_SPECULAR_MULTIPLIER (SPECULAR_BASE_MUL*float(SPECMUL))\n" #ifndef NOLEGACY "uniform sampler2DShadow s_shadowmap;" "uniform samplerCube s_projectionmap;" @@ -1633,6 +1637,9 @@ static const char *glsl_hdrs[] = "#if !defined(OFFSETMAPPING_SCALE)\n" "#define OFFSETMAPPING_SCALE 1.0\n" "#endif\n" + "#if !defined(OFFSETMAPPING_BIAS)\n" + "#define OFFSETMAPPING_BIAS 0.0\n" + "#endif\n" "#if defined(RELIEFMAPPING) && !defined(GL_ES)\n" "float i, f;\n" "vec3 OffsetVector = vec3(normalize(eyevector.xyz).xy * cvar_r_glsl_offsetmapping_scale * OFFSETMAPPING_SCALE * vec2(-1.0, 1.0), -1.0);\n" diff --git a/engine/gl/r_bishaders.h b/engine/gl/r_bishaders.h index e1b5d6210..473eb209e 100644 --- a/engine/gl/r_bishaders.h +++ b/engine/gl/r_bishaders.h @@ -5695,7 +5695,6 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "!!permu SPECULAR\n" "!!permu REFLECTCUBEMASK\n" "!!cvarf r_glsl_offsetmapping_scale\n" -"!!cvarf gl_specular\n" "!!cvardf r_tessellation_level=5\n" "!!samps diffuse lightmap specular normalmap fullbright reflectmask reflectcube paletted lightmap1 lightmap2 lightmap3\n" @@ -5917,9 +5916,6 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#define s_colourmap s_t0\n" "uniform sampler2D s_colourmap;\n" -"#ifdef SPECULAR\n" -"uniform float cvar_gl_specular;\n" -"#endif\n" "#ifdef OFFSETMAPPING\n" "#include \"sys/offsetmapping.h\"\n" "#endif\n" @@ -5999,7 +5995,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#ifdef SPECULAR\n" "vec4 specs = texture2D(s_specular, tc);\n" "vec3 halfdir = normalize(normalize(eyevector) + delux); //this norm should be the deluxemap info instead\n" -"float spec = pow(max(dot(halfdir, norm), 0.0), 2.0*FTE_SPECULAR_EXPONENT * specs.a);\n" +"float spec = pow(max(dot(halfdir, norm), 0.0), FTE_SPECULAR_EXPONENT * specs.a);\n" "spec *= FTE_SPECULAR_MULTIPLIER;\n" //NOTE: rtlights tend to have a *4 scaler here to over-emphasise the effect because it looks cool. //As not all maps will have deluxemapping, and the double-cos from the light util makes everything far too dark anyway, @@ -6009,7 +6005,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#endif\n" "#ifdef REFLECTCUBEMASK\n" -"vec3 rtc = reflect(-eyevector, norm);\n" +"vec3 rtc = reflect(normalize(-eyevector), norm);\n" "rtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2];\n" "rtc = (m_model * vec4(rtc.xyz,0.0)).xyz;\n" "gl_FragColor.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;\n" @@ -6051,6 +6047,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#endif\n" "}\n" "#endif\n" + }, #endif #ifdef VKQUAKE @@ -8440,6 +8437,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#endif\n" "uniform sampler2DShadow s_shadowmap;\n" +//FIXME: shadowmaps need to be atlased! "uniform vec4 l_shadowmapproj; //light projection matrix info\n" "uniform vec2 l_shadowmapscale; //xy are the texture scale, z is 1, w is the scale.\n" "vec3 ShadowmapCoord(vec4 cubeproj)\n" @@ -8585,6 +8583,9 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "float nDotL = dot(norm, lightDir);\n" "float lightDiffuse = max(0.0, nDotL) * atten;\n" +/*calc specular term*/ +//fixme + //fixme: apply fog //fixme: output a specular term //fixme: cubemap filters diff --git a/engine/qclib/pr_comp.h b/engine/qclib/pr_comp.h index 5bab85714..b90247744 100644 --- a/engine/qclib/pr_comp.h +++ b/engine/qclib/pr_comp.h @@ -427,6 +427,7 @@ enum qcop_e { OP_BITXOR_V, OP_POW_F, + OP_CROSS_V, OP_EQ_FLD, OP_NE_FLD, @@ -461,10 +462,10 @@ typedef struct qtest_function_s int unused1; int locals; // assumed! (always 0 in real qtest progs) int profile; // assumed! (always 0 in real qtest progs) - + int s_name; int s_file; - + int numparms; int parm_start; // different order int parm_size[MAX_PARMS]; // ints instead of bytes... @@ -540,12 +541,12 @@ typedef struct int first_statement; // negative numbers are builtins int parm_start; int locals; // total ints of parms + locals - + int profile; // runtime - + string_t s_name; string_t s_file; // source file defined in - + int numparms; qbyte parm_size[MAX_PARMS]; } dfunction_t; @@ -555,14 +556,14 @@ typedef struct int first_statement; // negative numbers are builtins int parm_start; int locals; // total ints of parms + locals - + int profile; //number of qc instructions executed. prclocks_t profiletime; //total time inside (cpu cycles) prclocks_t profilechildtime; //time inside children (excluding builtins, cpu cycles) - + string_t s_name; string_t s_file; // source file defined in - + int numparms; qbyte parm_size[MAX_PARMS]; } mfunction_t; @@ -577,25 +578,25 @@ typedef struct { int version; int crc; // check of header file - + unsigned int ofs_statements; //comp 1 unsigned int numstatements; // statement 0 is an error unsigned int ofs_globaldefs; //comp 2 unsigned int numglobaldefs; - + unsigned int ofs_fielddefs; //comp 4 unsigned int numfielddefs; - + unsigned int ofs_functions; //comp 8 unsigned int numfunctions; // function 0 is an empty - + unsigned int ofs_strings; //comp 16 unsigned int numstrings; // first string is a null string unsigned int ofs_globals; //comp 32 unsigned int numglobals; - + unsigned int entityfields; //debug / version 7 extensions diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index 4a2649d08..98951c090 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -378,8 +378,8 @@ typedef struct QCC_def_s struct QCC_function_s *scope; // function the var was defined in, or NULL struct QCC_def_s *deftail; // arrays and structs create multiple globaldef objects providing different types at the different parts of the single object (struct), or alternative names (vectors). this allows us to correctly set the const type based upon how its initialised. struct QCC_def_s *generatedfor; - int initialized; // 1 when a declaration included "= immediate". 2 = extern. 3 = don't warn (unless actually used) - int constant; // 1 says we can use the value over and over again + int initialized; // 1 when a declaration included "= immediate". 2 = extern. + int constant; // 1 says we can use the value over and over again. 2 is used on fields, for some reason. struct QCC_def_s *symbolheader; //this is the original symbol within which the def is stored. union QCC_eval_s *symboldata; //null if uninitialised. use sym->symboldata[sym->ofs] to index. @@ -402,14 +402,17 @@ typedef struct QCC_def_s pbool saved:1; //def may be saved to saved games. pbool isstatic:1; //global, even if scoped. also specific to the file it was seen in. pbool subscoped_away:1; //this local is no longer linked into the locals hash table. don't do remove it twice. -// pbool followptr:1; +// pbool followptr:1; //float &foo; pbool strip:1; //info about this def should be stripped. it may still consume globals space however, and its storage can still be used, its just not visible. pbool allowinline:1; //calls to this function will attempt to inline the specified function. requires const, supposedly. pbool used:1; //if it remains 0, it may be stripped. this is forced for functions and fields. commonly 0 on fields. + pbool unused:1; //silently strip it if it wasn't referenced. pbool localscope:1; //is a local, as opposed to a static (which is only visible within its scope) pbool arraylengthprefix:1; //hexen2 style arrays have a length prefixed to them for auto bounds checks. this can only work reliably for simple non-struct arrays. pbool assumedtype:1; //#merged. the type is not reliable. pbool weak:1; //ignore any initialiser value (only permitted on functions) + pbool accumulate:1; //don't finalise the function's statements. + pbool nofold:1; int fromstatement; //statement that it is valid from. temp_t *temp; @@ -470,6 +473,7 @@ struct QCC_function_s string_t s_filed; // source file with definition const char *filen; int line; + int line_end; char *name; //internal name of function struct QCC_function_s *parentscope; //for nested functions struct QCC_type_s *type; //same as the def's type @@ -478,6 +482,9 @@ struct QCC_function_s QCC_sref_t returndef; //default return value pbool privatelocals; //false means locals may overlap with other functions, true is needed for compat if stuff is uninitialised. // unsigned int parm_ofs[MAX_PARMS]; // always contiguous, right? + + QCC_statement_t *statements; //if set, then this function isn't finialised yet. + size_t numstatements; }; @@ -578,6 +585,7 @@ extern pbool keyword_strip; //don't write the def to the output. extern pbool keyword_union; //you surly know what a union is! extern pbool keyword_wrap; extern pbool keyword_weak; +extern pbool keyword_accumulate; extern pbool keyword_unused; extern pbool keyword_used; @@ -670,6 +678,7 @@ extern pbool type_inlinefunction; QCC_type_t *QCC_TypeForName(char *name); QCC_type_t *QCC_PR_ParseFunctionType (int newtype, QCC_type_t *returntype); QCC_type_t *QCC_PR_ParseFunctionTypeReacc (int newtype, QCC_type_t *returntype); +QCC_type_t *QCC_PR_GenFunctionType (QCC_type_t *rettype, struct QCC_typeparam_s *args, int numargs); char *QCC_PR_ParseName (void); CompilerConstant_t *QCC_PR_DefineName(char *name); @@ -752,6 +761,7 @@ enum { WARN_NOTSTANDARDBEHAVIOUR, WARN_DUPLICATEPRECOMPILER, WARN_IDENTICALPRECOMPILER, + WARN_GMQCC_SPECIFIC, //extension created by gmqcc that conflicts or isn't properly implemented. WARN_FTE_SPECIFIC, //extension that only FTEQCC will have a clue about. WARN_EXTENSION_USED, //extension that frikqcc also understands WARN_IFSTRING_USED, @@ -938,8 +948,9 @@ void QCC_PR_NewLine (pbool incomment); #define GDF_INLINE 32 //attempt to inline calls to this function #define GDF_USED 64 //don't strip this, ever. #define GDF_BASICTYPE 128 //don't care about #merge types not being known correctly. -QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, struct QCC_function_s *scope, pbool allocate, int arraysize, unsigned int flags); -QCC_sref_t QCC_PR_GetSRef (QCC_type_t *type, char *name, struct QCC_function_s *scope, pbool allocate, int arraysize, unsigned int flags); +#define GDF_SCANLOCAL 256 //don't use the locals hash table +QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, const char *name, struct QCC_function_s *scope, pbool allocate, int arraysize, unsigned int flags); +QCC_sref_t QCC_PR_GetSRef (QCC_type_t *type, const char *name, struct QCC_function_s *scope, pbool allocate, int arraysize, unsigned int flags); void QCC_FreeTemp(QCC_sref_t t); void QCC_FreeDef(QCC_def_t *def); char *QCC_PR_CheckCompConstTooltip(char *word, char *outstart, char *outend); @@ -973,8 +984,9 @@ void QCC_PR_EmitArrayGetFunction(QCC_def_t *defscope, QCC_def_t *thearray, char void QCC_PR_EmitArraySetFunction(QCC_def_t *defscope, QCC_def_t *thearray, char *arrayname); void QCC_PR_EmitClassFromFunction(QCC_def_t *defscope, QCC_type_t *basetype); -QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_function_t *scope, int arraysize, QCC_def_t *rootsymbol, unsigned int ofs, int referable, unsigned int flags); +QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, const char *name, QCC_function_t *scope, int arraysize, QCC_def_t *rootsymbol, unsigned int ofs, int referable, unsigned int flags); void QCC_PR_ParseInitializerDef(QCC_def_t *def, unsigned int flags); +void QCC_PR_FinaliseFunctions(void); void PostCompile(void); diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index 95ceb4810..f5ef7fb11 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -90,6 +90,7 @@ pbool keyword_ignore; pbool keyword_union; //you surly know what a union is! pbool keyword_weak; pbool keyword_wrap; +pbool keyword_accumulate; #define keyword_not 1 //hexenc support needs this, and fteqcc can optimise without it, but it adds an extra token after the if, so it can cause no namespace conflicts @@ -180,7 +181,6 @@ void *(*pHash_GetNext)(hashtable_t *table, const char *name, void *old); void *(*pHash_Add)(hashtable_t *table, const char *name, void *data, bucket_t *); void (*pHash_RemoveData)(hashtable_t *table, const char *name, void *data); -QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, struct QCC_function_s *scope, int arraysize, QCC_def_t *rootsymbol, unsigned int ofs, int referable, unsigned int flags); QCC_type_t *QCC_PR_FindType (QCC_type_t *type); QCC_type_t *QCC_PR_PointerType (QCC_type_t *pointsto); QCC_type_t *QCC_PR_FieldType (QCC_type_t *pointsto); @@ -204,13 +204,17 @@ void QCC_PR_DiscardRef(QCC_ref_t *ref); QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *type, pbool dowrap); const char *QCC_VarAtOffset(QCC_sref_t ref); QCC_sref_t QCC_EvaluateCast(QCC_sref_t src, QCC_type_t *cast, pbool implicit); -void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t def, unsigned int flags); +pbool QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t def, unsigned int flags); +#define PIF_WRAP 1 //new initialisation is meant to wrap an existing one. +#define PIF_STRONGER 2 //previous initialisation was weak. +#define PIF_ACCUMULATE 4 //glue them together... +#define PIF_AUTOWRAP 8 //accumulate without wrap must autowrap if the function was not previously defined as accumulate... QCC_statement_t *QCC_Generate_OP_IFNOT(QCC_sref_t e, pbool preserve); QCC_statement_t *QCC_Generate_OP_IF(QCC_sref_t e, pbool preserve); QCC_statement_t *QCC_Generate_OP_GOTO(void); QCC_sref_t QCC_PR_GenerateLogicalNot(QCC_sref_t e, const char *errormessage); -QCC_function_t *QCC_PR_GenerateQCFunction (QCC_def_t *def, QCC_type_t *type, pbool dowrap); +QCC_function_t *QCC_PR_GenerateQCFunction (QCC_def_t *def, QCC_type_t *type, unsigned int pif_flags); //NOTE: prints may use from the func argument's symbol, which can be awkward if its a temp. QCC_sref_t QCC_PR_GenerateFunctionCallSref (QCC_sref_t newself, QCC_sref_t func, QCC_sref_t *arglist, int argcount); @@ -270,8 +274,8 @@ int num_cases; int *pr_breaks; int *pr_continues; int *pr_cases; -QCC_sref_t *pr_casesdef; -QCC_sref_t *pr_casesdef2; +QCC_ref_t *pr_casesref; +QCC_ref_t *pr_casesref2; typedef struct { int statementno; @@ -701,10 +705,11 @@ QCC_opcode_t pr_opcodes[] = {7, "~", "BITNOT_V", -1, ASSOC_LEFT, &type_vector, &type_void, &type_vector}, {7, "^", "BITXOR_V", 3, ASSOC_LEFT, &type_vector, &type_vector, &type_vector}, - {7, "^^", "POW_F", 3, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {7, "*^", "POW_F", 3, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {7, "><", "CROSS_V", 3, ASSOC_LEFT, &type_vector, &type_vector, &type_vector}, {6, "==", "EQ_FLD", 5, ASSOC_LEFT, &type_field, &type_field, &type_float}, - {6, "==", "NE_FLD", 5, ASSOC_LEFT, &type_field, &type_field, &type_float}, + {6, "!=", "NE_FLD", 5, ASSOC_LEFT, &type_field, &type_field, &type_float}, {0, NULL} }; @@ -992,6 +997,27 @@ QCC_opcode_t *opcodes_clearstorep[] = &pr_opcodes[OP_BITCLRSTOREP_F], NULL }; +QCC_opcode_t *opcodes_shlstore[] = +{ + &pr_opcodes[OP_LSHIFT_F], + &pr_opcodes[OP_LSHIFT_I], + &pr_opcodes[OP_LSHIFT_IF], + &pr_opcodes[OP_LSHIFT_FI], + NULL +}; +QCC_opcode_t *opcodes_shrstore[] = +{ + &pr_opcodes[OP_RSHIFT_F], + &pr_opcodes[OP_RSHIFT_I], + &pr_opcodes[OP_RSHIFT_IF], + &pr_opcodes[OP_RSHIFT_FI], + NULL +}; + +QCC_opcode_t *opcodes_none[] = +{ + NULL +}; //this system cuts out 10/120 //these evaluate as top first. @@ -1093,6 +1119,7 @@ QCC_opcode_t *opcodeprioritized[TOP_PRIORITY+1][128] = &pr_opcodes[OP_MOD_V], &pr_opcodes[OP_POW_F], + &pr_opcodes[OP_CROSS_V], NULL }, { //4 @@ -3430,12 +3457,29 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ { QCC_sref_t fnc = QCC_PR_GetSRef(NULL, "pow", NULL, false, 0, 0); if (!fnc.cast) - QCC_PR_ParseError(0, "pow function not defined: cannot emulate int^^int"); + QCC_PR_ParseError(0, "pow function not defined: cannot emulate float*^float"); var_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_float, var_b, type_float); var_c.cast = type_float; return var_c; } break; + case OP_CROSS_V: + { + QCC_sref_t t; + var_c = QCC_GetTemp(type_vector); + + t = QCC_PR_Statement(&pr_opcodes[OP_SUB_F], QCC_PR_Statement(&pr_opcodes[OP_MUL_F], QCC_MakeSRef(var_a.sym, var_a.ofs+1, type_float), QCC_MakeSRef(var_b.sym, var_b.ofs+2, type_float), NULL), QCC_PR_Statement(&pr_opcodes[OP_MUL_F], QCC_MakeSRef(var_a.sym, var_a.ofs+2, type_float), QCC_MakeSRef(var_b.sym, var_b.ofs+1, type_float), NULL), NULL); + QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], t, QCC_MakeSRef(var_c.sym, var_c.ofs+0, type_float), NULL, flags&STFL_DISCARDRESULT); + + t = QCC_PR_Statement(&pr_opcodes[OP_SUB_F], QCC_PR_Statement(&pr_opcodes[OP_MUL_F], QCC_MakeSRef(var_a.sym, var_a.ofs+2, type_float), QCC_MakeSRef(var_b.sym, var_b.ofs+0, type_float), NULL), QCC_PR_Statement(&pr_opcodes[OP_MUL_F], QCC_MakeSRef(var_a.sym, var_a.ofs+0, type_float), QCC_MakeSRef(var_b.sym, var_b.ofs+2, type_float), NULL), NULL); + QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], t, QCC_MakeSRef(var_c.sym, var_c.ofs+1, type_float), NULL, flags&STFL_DISCARDRESULT); + + t = QCC_PR_Statement(&pr_opcodes[OP_SUB_F], QCC_PR_Statement(&pr_opcodes[OP_MUL_F], QCC_MakeSRef(var_a.sym, var_a.ofs+0, type_float), QCC_MakeSRef(var_b.sym, var_b.ofs+1, type_float), NULL), QCC_PR_Statement(&pr_opcodes[OP_MUL_F], QCC_MakeSRef(var_a.sym, var_a.ofs+1, type_float), QCC_MakeSRef(var_b.sym, var_b.ofs+0, type_float), NULL), NULL); + QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], t, QCC_MakeSRef(var_c.sym, var_c.ofs+2, type_float), NULL, flags&STFL_DISCARDRESULT); + + return var_c; + } + break; case OP_CONV_ITOF: case OP_STORE_IF: @@ -4698,13 +4742,22 @@ static char *QCC_PR_InlineStatements(struct inlinectx_s *ctx) return t; */ QCC_sref_t a, b, c; - QCC_statement_t *st; + QCC_statement_t *st, *est; const QCC_eval_t *eval; // float af,bf; // int i; - st = &statements[ctx->func->code]; - while(1) + if (ctx->func->statements) + { + st = ctx->func->statements; + est = st + ctx->func->numstatements; + } + else + { + st = &statements[ctx->func->code]; + est = statements+numstatements; + } + while(st < est) { switch(st->op) { @@ -4893,6 +4946,17 @@ static char *QCC_PR_InlineStatements(struct inlinectx_s *ctx) } st++; } + + a = QCC_PR_InlineFindDef(ctx, nullsref, false); + ctx->result = a; + if (!a.cast) + { + if (ctx->func->type->aux_type->type == ev_void) + ctx->result.cast = type_void; + else + return "missing return type"; + } + return NULL; } #endif static QCC_sref_t QCC_PR_Inline(QCC_sref_t fdef, QCC_ref_t **arglist, unsigned int argcount) @@ -4904,14 +4968,15 @@ static QCC_sref_t QCC_PR_Inline(QCC_sref_t fdef, QCC_ref_t **arglist, unsigned i struct inlinectx_s ctx; char *error; int statements, i; + unsigned int a; const QCC_eval_t *eval = QCC_SRef_EvalConst(fdef); //make sure that its a function type and that there's no special weirdness if (!eval || fdef.cast != fdef.sym->type || eval->function < 0 || argcount > 8 || eval->function >= numfunctions || fdef.sym->arraysize != 0 || fdef.cast->type != ev_function || argcount != fdef.cast->num_parms || fdef.cast->vargs || fdef.cast->vargcount) return nullsref; ctx.numlocals = 0; - for (i = 0; i < argcount; i++) - ctx.arglist[i] = QCC_RefToDef(arglist[i], true); + for (a = 0; a < argcount; a++) + ctx.arglist[a] = QCC_RefToDef(arglist[a], true); ctx.fdef = fdef.sym; ctx.result = nullsref; ctx.func = &functions[eval->function]; @@ -4995,7 +5060,7 @@ QCC_sref_t QCC_PR_GenerateFunctionCallRef (QCC_sref_t newself, QCC_sref_t func, int callconvention; QCC_statement_t *st; - int parm; + unsigned int parm; struct { QCC_sref_t ref; @@ -5540,8 +5605,7 @@ QCC_sref_t QCC_PR_ParseFunctionCall (QCC_ref_t *funcref) //warning, the func cou if (!strcmp(funcname, "sizeof")) { QCC_type_t *t; - if (!func.sym->initialized) - func.sym->initialized = 3; + func.sym->unused = true; func.sym->referenced = true; QCC_FreeTemp(func); t = QCC_PR_ParseType(false, true); @@ -5591,8 +5655,7 @@ QCC_sref_t QCC_PR_ParseFunctionCall (QCC_ref_t *funcref) //warning, the func cou { //FIXME: half of these functions with known arguments should be handled later or something QCC_sref_t sz, ret; - if (!func.sym->initialized) - func.sym->initialized = 3; + func.sym->unused = true; func.sym->referenced = true; QCC_FreeTemp(func); @@ -5609,8 +5672,7 @@ QCC_sref_t QCC_PR_ParseFunctionCall (QCC_ref_t *funcref) //warning, the func cou } if (!strcmp(funcname, "_")) { - if (!func.sym->initialized) - func.sym->initialized = 3; + func.sym->unused = true; func.sym->referenced = true; QCC_FreeTemp(func); if (pr_token_type == tt_immediate && pr_immediate_type->type == ev_string) @@ -5641,16 +5703,14 @@ QCC_sref_t QCC_PR_ParseFunctionCall (QCC_ref_t *funcref) //warning, the func cou if (!va_list.cast || !va_list.sym || !va_list.sym->arraysize) QCC_PR_ParseError (ERR_TYPEMISMATCHPARM, "va_arg() intrinsic only works inside varadic functions"); - if (!func.sym->initialized) - func.sym->initialized = 3; + func.sym->unused = true; func.sym->referenced = true; QCC_FreeTemp(func); return QCC_LoadFromArray(va_list, idx, type, false); } if (!strcmp(funcname, "random")) { - if (!func.sym->initialized) - func.sym->initialized = 3; + func.sym->unused = true; func.sym->referenced = true; QCC_FreeTemp(func); if (!QCC_PR_CheckToken(")")) @@ -5723,8 +5783,7 @@ QCC_sref_t QCC_PR_ParseFunctionCall (QCC_ref_t *funcref) //warning, the func cou } if (!strcmp(funcname, "randomv")) { - if (!func.sym->initialized) - func.sym->initialized = 3; + func.sym->unused = true; func.sym->referenced=true; QCC_FreeTemp(func); if (!QCC_PR_CheckToken(")")) @@ -5954,8 +6013,7 @@ QCC_sref_t QCC_PR_ParseFunctionCall (QCC_ref_t *funcref) //warning, the func cou { //t = (a/%1) / (nextent(world)/%1) //a/%1 does a (int)entity to float conversion type thing - if (!func.sym->initialized) - func.sym->initialized = 3; + func.sym->unused = true; func.sym->referenced = true; QCC_FreeTemp(func); @@ -6196,9 +6254,9 @@ QCC_sref_t QCC_MakeSRef(QCC_def_t *def, unsigned int ofs, QCC_type_t *type) QCC_UnFreeTemp(sr); return sr; } -int constchecks; -int varchecks; -int typechecks; +//int constchecks; +//int varchecks; +//int typechecks; extern hashtable_t floatconstdefstable; QCC_sref_t QCC_MakeIntConst(int value) { @@ -6253,6 +6311,35 @@ QCC_sref_t QCC_MakeIntConst(int value) return QCC_MakeSRefForce(cn, 0, type_integer); } +QCC_sref_t QCC_MakeUniqueConst(QCC_type_t *type, void *data) +{ + QCC_def_t *cn; + +// allocate a new one + cn = (void *)qccHunkAlloc (sizeof(QCC_def_t) + sizeof(int) * type->size); + cn->next = NULL; + pr.def_tail->next = cn; + pr.def_tail = cn; + + cn->type = type; + cn->name = "IMMEDIATE"; + cn->constant = true; + cn->initialized = 1; + cn->scope = NULL; // always share immediates + cn->arraysize = 0; + + cn->ofs = 0; + cn->symbolheader = cn; + cn->symbolsize = cn->type->size; + cn->symboldata = (QCC_eval_t*)(cn+1); + + memcpy(cn->symboldata, data, sizeof(int) * type->size); + + return QCC_MakeSRefForce(cn, 0, type); +} + + +QCC_sref_t QCC_PR_GenerateVector(QCC_sref_t x, QCC_sref_t y, QCC_sref_t z); QCC_sref_t QCC_MakeVectorConst(float a, float b, float c) { QCC_def_t *cn; @@ -6260,15 +6347,14 @@ QCC_sref_t QCC_MakeVectorConst(float a, float b, float c) // check for a constant with the same value for (cn=pr.def_head.next ; cn ; cn=cn->next) { - varchecks++; if (!cn->initialized) continue; if (!cn->constant) continue; - constchecks++; if (cn->type != type_vector) continue; - typechecks++; + if (cn->arraysize) + continue; if (cn->symboldata[0].vector[0] == a && cn->symboldata[0].vector[1] == b && @@ -6278,6 +6364,22 @@ QCC_sref_t QCC_MakeVectorConst(float a, float b, float c) } } +/* if (pr_scope) + { //crappy-but-globals-efficient version + QCC_sref_t d = QCC_GetTemp(type_vector); + d.cast = type_float; + QCC_PR_StatementFlags(pr_opcodes + OP_STORE_F, QCC_MakeFloatConst(a), d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT); + d.ofs++; + QCC_PR_StatementFlags(pr_opcodes + OP_STORE_F, QCC_MakeFloatConst(b), d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT); + d.ofs++; + QCC_PR_StatementFlags(pr_opcodes + OP_STORE_F, QCC_MakeFloatConst(c), d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT); + d.ofs++; + d.ofs -= 3; + d.cast = type_vector; + return d; + } +*/ + // allocate a new one cn = (void *)qccHunkAlloc (sizeof(QCC_def_t)+sizeof(float)*3); cn->next = NULL; @@ -6374,6 +6476,7 @@ static QCC_sref_t QCC_MakeStringConstInternal(char *value, size_t length, pbool strcpy(cn->name, buf); cn->used = true; // cn->referenced = true; + cn->nofold = true; } else { @@ -6643,7 +6746,7 @@ void QCC_PR_EmitClassFromFunction(QCC_def_t *scope, QCC_type_t *basetype) pr_source_line = pr_token_line_last = scope->s_line; - pr_scope = QCC_PR_GenerateQCFunction(scope, scope->type, false); + pr_scope = QCC_PR_GenerateQCFunction(scope, scope->type, 0); //reset the locals chain pr.local_head.nextlocal = NULL; pr.local_tail = &pr.local_head; @@ -7023,7 +7126,7 @@ vectorarrayindex: else i = -1; if (i < 0 || i >= arraysize) - QCC_PR_ParseErrorPrintSRef(0, r->base, "(constant) array index out of bounds"); + QCC_PR_ParseErrorPrintSRef(0, r->base, "(constant) array index out of bounds (0 <= %i < %i)", i, arraysize); } else { @@ -7475,7 +7578,7 @@ QCC_ref_t *QCC_PR_ParseRefValue (QCC_ref_t *refbuf, QCC_type_t *assumeclass, pbo QCC_PR_ParseError (ERR_UNKNOWNVALUE, "Unknown field \"%s\" in class \"%s\"", name, assumeclass->name); else if (!assumeclass->parentclass && assumeclass != type_entity) { - QCC_PR_ParseWarning (ERR_UNKNOWNVALUE, "Class \"%s\" is not defined, cannot access memeber \"%s\"", assumeclass->name, name); + QCC_PR_ParseWarning (ERR_UNKNOWNVALUE, "Class \"%s\" is not defined, cannot access member \"%s\"", assumeclass->name, name); if (!autoprototype && !autoprototyped) QCC_PR_Note(ERR_UNKNOWNVALUE, s_filen, pr_source_line, "Consider using #pragma autoproto"); } @@ -9412,6 +9515,18 @@ QCC_ref_t *QCC_PR_RefExpression (QCC_ref_t *retbuf, int priority, int exprflags) ops_ptr = opcodes_mulstorep; opname = "*="; } + else if (QCC_PR_CheckToken ("<<=")) + { + ops = opcodes_shlstore; + ops_ptr = opcodes_none; + opname = "<<="; + } + else if (QCC_PR_CheckToken (">>=")) + { + ops = opcodes_shrstore; + ops_ptr = opcodes_none; + opname = ">>="; + } else if (QCC_PR_CheckToken ("/=")) { ops = opcodes_divstore; @@ -10057,6 +10172,8 @@ void QCC_PR_ParseStatement (void) QCC_ref_t r; if (!pr_scope->returndef.cast) pr_scope->returndef = QCC_PR_GetSRef(pr_scope->type->aux_type, "return", pr_scope, true, 0, GDF_NONE); + else + QCC_ForceUnFreeDef(pr_scope->returndef.sym); e = QCC_PR_Expression(TOP_PRIORITY, 0); QCC_PR_Expect (";"); @@ -10570,7 +10687,7 @@ void QCC_PR_ParseStatement (void) QCC_ForceUnFreeDef(e.sym); //in the following code, e should still be live for (i = cases; i < num_cases; i++) { - if (!pr_casesdef[i].cast) + if (!pr_casesref[i].cast) { if (defaultcase >= 0) QCC_PR_ParseError(ERR_MULTIPLEDEFAULTS, "Duplicated default case"); @@ -10578,16 +10695,19 @@ void QCC_PR_ParseStatement (void) } else { - if (pr_casesdef[i].cast->type != e.cast->type) - pr_casesdef[i] = QCC_SupplyConversion(pr_casesdef[i], e.cast->type, true); - if (pr_casesdef2[i].cast) + QCC_sref_t dmin, dmax; + dmin = QCC_RefToDef(&pr_casesref[i], true); + if (dmin.cast->type != e.cast->type) + dmin = QCC_SupplyConversion(dmin, e.cast->type, true); + if (pr_casesref2[i].cast) { - if (pr_casesdef2[i].cast->type != e.cast->type) - pr_casesdef2[i] = QCC_SupplyConversion(pr_casesdef2[i], e.cast->type, true); + dmax = QCC_RefToDef(&pr_casesref2[i], true); + if (dmax.cast->type != e.cast->type) + dmax = QCC_SupplyConversion(dmax, e.cast->type, true); if (hcstyle) { - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_CASERANGE], pr_casesdef[i], pr_casesdef2[i], &patch3)); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_CASERANGE], dmin, dmax, &patch3)); patch3->c.ofs = &statements[pr_cases[i]] - patch3; } else @@ -10596,16 +10716,16 @@ void QCC_PR_ParseStatement (void) if (e.cast->type == ev_float) { - e2 = QCC_PR_StatementFlags (&pr_opcodes[OP_GE_F], e, pr_casesdef[i], NULL, STFL_PRESERVEA); - e3 = QCC_PR_StatementFlags (&pr_opcodes[OP_LE_F], e, pr_casesdef2[i], NULL, STFL_PRESERVEA); + e2 = QCC_PR_StatementFlags (&pr_opcodes[OP_GE_F], e, dmin, NULL, STFL_PRESERVEA); + e3 = QCC_PR_StatementFlags (&pr_opcodes[OP_LE_F], e, dmax, NULL, STFL_PRESERVEA); e2 = QCC_PR_Statement (&pr_opcodes[OP_AND_F], e2, e3, NULL); patch3 = QCC_Generate_OP_IF(e2, false); patch3->b.ofs = &statements[pr_cases[i]] - patch3; } else if (e.cast->type == ev_integer) { - e2 = QCC_PR_StatementFlags (&pr_opcodes[OP_GE_I], e, pr_casesdef[i], NULL, STFL_PRESERVEA); - e3 = QCC_PR_StatementFlags (&pr_opcodes[OP_LE_I], e, pr_casesdef2[i], NULL, STFL_PRESERVEA); + e2 = QCC_PR_StatementFlags (&pr_opcodes[OP_GE_I], e, dmin, NULL, STFL_PRESERVEA); + e3 = QCC_PR_StatementFlags (&pr_opcodes[OP_LE_I], e, dmax, NULL, STFL_PRESERVEA); e2 = QCC_PR_Statement (&pr_opcodes[OP_AND_I], e2, e3, NULL); patch3 = QCC_Generate_OP_IF(e2, false); patch3->b.ofs = &statements[pr_cases[i]] - patch3; @@ -10618,39 +10738,39 @@ void QCC_PR_ParseStatement (void) { if (hcstyle) { - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_CASE], pr_casesdef[i], nullsref, &patch3)); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_CASE], dmin, nullsref, &patch3)); patch3->b.ofs = &statements[pr_cases[i]] - patch3; } else { - const QCC_eval_t *eval = QCC_SRef_EvalConst(pr_casesdef[i]); + const QCC_eval_t *eval = QCC_SRef_EvalConst(dmin); if (!eval || eval->_int) { switch(e.cast->type) { case ev_float: - e2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_F], e, pr_casesdef[i], NULL, STFL_PRESERVEA); + e2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_F], e, dmin, NULL, STFL_PRESERVEA); break; case ev_entity: //whu??? - e2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_E], e, pr_casesdef[i], NULL, STFL_PRESERVEA); + e2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_E], e, dmin, NULL, STFL_PRESERVEA); break; case ev_vector: - e2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_V], e, pr_casesdef[i], NULL, STFL_PRESERVEA); + e2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_V], e, dmin, NULL, STFL_PRESERVEA); break; case ev_string: - e2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_S], e, pr_casesdef[i], NULL, STFL_PRESERVEA); + e2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_S], e, dmin, NULL, STFL_PRESERVEA); break; case ev_function: - e2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_FNC], e, pr_casesdef[i], NULL, STFL_PRESERVEA); + e2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_FNC], e, dmin, NULL, STFL_PRESERVEA); break; case ev_field: - e2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_FNC], e, pr_casesdef[i], NULL, STFL_PRESERVEA); + e2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_FNC], e, dmin, NULL, STFL_PRESERVEA); break; case ev_pointer: - e2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_P], e, pr_casesdef[i], NULL, STFL_PRESERVEA); + e2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_P], e, dmin, NULL, STFL_PRESERVEA); break; case ev_integer: - e2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_I], e, pr_casesdef[i], NULL, STFL_PRESERVEA); + e2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_I], e, dmin, NULL, STFL_PRESERVEA); break; default: QCC_PR_ParseError(ERR_BADSWITCHTYPE, "Bad switch type"); @@ -10661,7 +10781,7 @@ void QCC_PR_ParseStatement (void) } else { - QCC_FreeTemp(pr_casesdef[i]); + QCC_FreeTemp(dmin); QCC_UnFreeTemp(e); patch3 = QCC_Generate_OP_IFNOT(e, false); } @@ -10803,31 +10923,31 @@ void QCC_PR_ParseStatement (void) { max_cases += 8; pr_cases = realloc(pr_cases, sizeof(*pr_cases)*max_cases); - pr_casesdef = realloc(pr_casesdef, sizeof(*pr_casesdef)*max_cases); - pr_casesdef2 = realloc(pr_casesdef2, sizeof(*pr_casesdef2)*max_cases); + pr_casesref = realloc(pr_casesref, sizeof(*pr_casesref)*max_cases); + pr_casesref2 = realloc(pr_casesref2, sizeof(*pr_casesref2)*max_cases); } pr_cases[num_cases] = numstatements; - pr_casesdef[num_cases] = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA); + QCC_PR_RefExpression(&pr_casesref[num_cases], TOP_PRIORITY, EXPR_DISALLOW_COMMA); if (QCC_PR_CheckToken("..")) { - const QCC_eval_t *evalmin, *evalmax; - pr_casesdef2[num_cases] = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA); - pr_casesdef2[num_cases] = QCC_SupplyConversion(pr_casesdef2[num_cases], pr_casesdef[num_cases].cast->type, true); + //const QCC_eval_t *evalmin, *evalmax; + QCC_PR_RefExpression (&pr_casesref2[num_cases], TOP_PRIORITY, EXPR_DISALLOW_COMMA); + //pr_casesref2[num_cases] = QCC_SupplyConversion(pr_casesdef2[num_cases], pr_casesdef[num_cases].cast->type, true); - evalmin = QCC_SRef_EvalConst(pr_casesdef[num_cases]); - evalmax = QCC_SRef_EvalConst(pr_casesdef2[num_cases]); + /*evalmin = QCC_Ref_EvalConst(pr_casesref[num_cases]); + evalmax = QCC_Ref_EvalConst(pr_casesref2[num_cases]); if (evalmin && evalmax) { - if ((pr_casesdef[num_cases].cast->type == ev_integer && evalmin->_int >= evalmax->_int) || - (pr_casesdef[num_cases].cast->type == ev_float && evalmin->_float >= evalmax->_float)) + if ((pr_casesref[num_cases].cast->type == ev_integer && evalmin->_int >= evalmax->_int) || + (pr_casesref[num_cases].cast->type == ev_float && evalmin->_float >= evalmax->_float)) QCC_PR_ParseError(ERR_CASENOTIMMEDIATE, "Caserange statement uses backwards range\n"); - } + }*/ } else - pr_casesdef2[num_cases] = nullsref; + pr_casesref2[num_cases].cast = NULL; if (numstatements != pr_cases[num_cases]) - QCC_PR_ParseError(ERR_CASENOTIMMEDIATE, "Case statements may not use formulas\n"); + QCC_PR_ParseError(ERR_CASENOTIMMEDIATE, "Case statements may not use formulas\n"); //fixme: insert them... num_cases++; QCC_PR_Expect(":"); return; @@ -10838,12 +10958,12 @@ void QCC_PR_ParseStatement (void) { max_cases += 8; pr_cases = realloc(pr_cases, sizeof(*pr_cases)*max_cases); - pr_casesdef = realloc(pr_casesdef, sizeof(*pr_casesdef)*max_cases); - pr_casesdef2 = realloc(pr_casesdef2, sizeof(*pr_casesdef2)*max_cases); + pr_casesref = realloc(pr_casesref, sizeof(*pr_casesref)*max_cases); + pr_casesref2 = realloc(pr_casesref2, sizeof(*pr_casesref2)*max_cases); } pr_cases[num_cases] = numstatements; - pr_casesdef[num_cases] = nullsref; - pr_casesdef2[num_cases] = nullsref; + pr_casesref[num_cases].cast = NULL; + pr_casesref2[num_cases].cast = NULL; num_cases++; QCC_PR_Expect(":"); return; @@ -11057,6 +11177,7 @@ void QCC_PR_ParseState (void) QCC_PR_Expect ("]"); + //FIXME: emulate this in order to provide other framerates QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STATE], s1, def, NULL)); } @@ -11756,13 +11877,18 @@ void QCC_Marshal_Locals(int firststatement, int laststatement) return; } - if (!opt_locals_overlapping) + if (1) + { + if (qccwarningaction[WARN_UNINITIALIZED]) + QCC_CheckUninitialised(firststatement, laststatement); //still need to call it for warnings, but if those warnings are off we can skip the cost + } + else if (!pr_scope->def->accumulate && !opt_locals_overlapping) { if (qccwarningaction[WARN_UNINITIALIZED]) QCC_CheckUninitialised(firststatement, laststatement); //still need to call it for warnings, but if those warnings are off we can skip the cost error = true; //always use the legacy behaviour } - else if (QCC_CheckUninitialised(firststatement, laststatement)) + else if (QCC_CheckUninitialised(firststatement, laststatement) && !pr_scope->def->accumulate) { error = true; // QCC_PR_Note(ERR_INTERNAL, strings+s_file, pr_source_line, "Not overlapping locals from %s due to uninitialised locals", pr_scope->name); @@ -11772,6 +11898,11 @@ void QCC_Marshal_Locals(int firststatement, int laststatement) //make sure we're allowed to marshall this function's locals for (local = pr.local_head.nextlocal; local; local = local->nextlocal) { + if (local->isstatic) + continue; //static variables are globals + if (local->constant && local->initialized) + continue; //as are initialised consts, because its pointless otherwise. + //FIXME: check for uninitialised locals. //these matter when the function goes recursive (and locals marshalling counts as recursive every time). if (local->symboldata[0]._int) @@ -11998,12 +12129,26 @@ QCC_function_t *QCC_PR_GenerateBuiltinFunction (QCC_def_t *def, int builtinnum, func->def = def; return func; } -QCC_function_t *QCC_PR_GenerateQCFunction (QCC_def_t *def, QCC_type_t *type, pbool dowrap) +QCC_function_t *QCC_PR_GenerateQCFunction (QCC_def_t *def, QCC_type_t *type, unsigned int pif_flags) { QCC_function_t *func; + if ((pif_flags & PIF_ACCUMULATE) && !(pif_flags & PIF_WRAP)) + { + if (def->symboldata[0].function) + { + func = &functions[def->symboldata[0].function]; //just resume the old one... + + if (func->def != def || func->type != type || func->parentscope != pr_scope) + QCC_PR_ParseError(ERR_INTERNAL, "invalid function accumulation"); + + //fixme: validate stuff + return func; + } + } + if (numfunctions >= MAX_FUNCTIONS) QCC_PR_ParseError(ERR_INTERNAL, "Too many functions - %i\nAdd \"MAX_FUNCTIONS\" \"%i\" to qcc.cfg", numfunctions, (numfunctions+4096)&~4095); - if (dowrap && def->symboldata[0].function) + if ((pif_flags&PIF_WRAP) && def->symboldata[0].function) { QCC_def_t *locals; QCC_function_t *prior = &functions[numfunctions++]; @@ -12018,7 +12163,7 @@ QCC_function_t *QCC_PR_GenerateQCFunction (QCC_def_t *def, QCC_type_t *type, pbo locals->scope = prior; } } - else if (dowrap) + else if (pif_flags&PIF_WRAP) { QCC_PR_ParseError(ERR_INTERNAL, "cannot wrap bodyless function %s", def->name); return NULL; @@ -12038,6 +12183,95 @@ QCC_function_t *QCC_PR_GenerateQCFunction (QCC_def_t *def, QCC_type_t *type, pbo return func; } +void QCC_PR_ResumeFunction(QCC_function_t *f) +{ + if (pr_scope != f) + { + pr_scope = f; + + //reset the locals chain + pr.local_head.nextlocal = f->firstlocal; + pr.local_tail = &pr.local_head; + while (pr.local_tail->nextlocal) + pr.local_tail = pr.local_tail->nextlocal; + + //qcvm sees the function start here. + f->code = numstatements; + + if (f->statements) + { + memcpy(statements+f->code, f->statements, sizeof(*statements) * f->numstatements); + numstatements += f->numstatements; + + f->statements = NULL; + f->numstatements = 0; + } + } +} +void QCC_PR_FinaliseFunction(QCC_function_t *f) +{ + QCC_statement_t *st; + pbool needsdone=false; + + QCC_PR_ResumeFunction(f); + + pr_token_line_last = f->line_end; + + if (f->returndef.cast) + { + PR_GenerateReturnOuts(); + QCC_ForceUnFreeDef(f->returndef.sym); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_RETURN], f->returndef, nullsref, NULL)); + } + + if (f->code == numstatements) + needsdone = true; + else if (statements[numstatements - 1].op != OP_RETURN && statements[numstatements - 1].op != OP_DONE) + needsdone = true; + + if (opt_return_only && !needsdone) + needsdone = QCC_FuncJumpsTo(f->code, numstatements, numstatements); + + // emit an end of statements opcode + if (!opt_return_only || needsdone) + { + /*if (pr_classtype) + { + QCC_def_t *e, *e2; + e = QCC_PR_GetDef(NULL, "__oself", pr_scope, false, 0); + e2 = QCC_PR_GetDef(NULL, "self", NULL, false, 0); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], e, QCC_PR_DummyDef(pr_classtype, "self", pr_scope, 0, e2->ofs, false), NULL)); + }*/ + + if (needsdone) + PR_GenerateReturnOuts(); + QCC_PR_Statement (&pr_opcodes[OP_DONE], nullsref, nullsref, &st); + } + else + optres_return_only++; + + QCC_CheckForDeadAndMissingReturns(f->code, numstatements, f->type->aux_type->type); + +// if (opt_comexprremoval) +// QCC_CommonSubExpressionRemoval(f->code, numstatements); + + + QCC_RemapLockedTemps(f->code, numstatements); + QCC_Marshal_Locals(f->code, numstatements); + QCC_WriteAsmFunction(f, f->code, f->firstlocal); + + if (opt_compound_jumps) + QCC_CompoundJumps(f->code, numstatements); +} +void QCC_PR_FinaliseFunctions(void) +{ + QCC_function_t *f; + for (f = functions; f < functions+numfunctions; f++) + { + if (f->statements) + QCC_PR_FinaliseFunction(f); + } +} /* ============ PR_ParseImmediateStatements @@ -12047,14 +12281,14 @@ Parse a function body If def is set, allows stuff to refer back to a def for the function. ============ */ -QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *type, pbool dowrap) +QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *type, unsigned int pif_flags) { unsigned int u, p; QCC_function_t *f; QCC_sref_t parm; - pbool needsdone=false; - QCC_def_t *prior = NULL; + QCC_def_t *prior = NULL, *d, *lastparm; + pbool mergeargs; conditional = 0; @@ -12064,131 +12298,139 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *typ // check for builtin function definition #1, #2, etc // // hexenC has void name() : 2; - if (QCC_PR_CheckToken ("#") || QCC_PR_CheckToken (":")) + if (!(pif_flags & PIF_ACCUMULATE)) { - int binum = 0; - if (pr_token_type == tt_immediate - && pr_immediate_type == type_float - && pr_immediate._float == (int)pr_immediate._float) - binum = (int)pr_immediate._float; - else if (pr_token_type == tt_immediate && pr_immediate_type == type_integer) - binum = pr_immediate._int; - else - QCC_PR_ParseError (ERR_BADBUILTINIMMEDIATE, "Bad builtin immediate"); - f = QCC_PR_GenerateBuiltinFunction(def, binum, def->name); - QCC_PR_Lex (); + if (QCC_PR_CheckToken ("#") || QCC_PR_CheckToken (":")) + { + int binum = 0; + if (pr_token_type == tt_immediate + && pr_immediate_type == type_float + && pr_immediate._float == (int)pr_immediate._float) + binum = (int)pr_immediate._float; + else if (pr_token_type == tt_immediate && pr_immediate_type == type_integer) + binum = pr_immediate._int; + else + QCC_PR_ParseError (ERR_BADBUILTINIMMEDIATE, "Bad builtin immediate"); + f = QCC_PR_GenerateBuiltinFunction(def, binum, def->name); + QCC_PR_Lex (); - return f; - } - if (QCC_PR_CheckKeyword(keyword_external, "external")) - { //reacc style builtin - if (pr_token_type != tt_immediate - || pr_immediate_type != type_float - || pr_immediate._float != (int)pr_immediate._float) - QCC_PR_ParseError (ERR_BADBUILTINIMMEDIATE, "Bad builtin immediate"); - f = QCC_PR_GenerateBuiltinFunction(def, (int)-pr_immediate._float, def->name); - QCC_PR_Lex (); - QCC_PR_Expect(";"); + return f; + } + if (QCC_PR_CheckKeyword(keyword_external, "external")) + { //reacc style builtin + if (pr_token_type != tt_immediate + || pr_immediate_type != type_float + || pr_immediate._float != (int)pr_immediate._float) + QCC_PR_ParseError (ERR_BADBUILTINIMMEDIATE, "Bad builtin immediate"); + f = QCC_PR_GenerateBuiltinFunction(def, (int)-pr_immediate._float, def->name); + QCC_PR_Lex (); + QCC_PR_Expect(";"); - return f; + return f; + } } // if (type->vargs) // QCC_PR_ParseError (ERR_FUNCTIONWITHVARGS, "QC function with variable arguments and function body"); - pr_scope = f = QCC_PR_GenerateQCFunction(def, type, dowrap); + f = QCC_PR_GenerateQCFunction(def, type, pif_flags); - //reset the locals chain - pr.local_head.nextlocal = NULL; - pr.local_tail = &pr.local_head; + QCC_PR_ResumeFunction(f); - //qcvm sees the function start here. - f->code = numstatements; + QCC_RemapLockedTemps(-1, -1); + mergeargs = !!f->firstlocal; // // define the basic parms // - for (u=0, p=0 ; unum_parms; u++) + if (mergeargs) { - unsigned int o; - if (!*pr_parm_names[u]) + QCC_def_t *arg; + for (u=0, p=0, arg=f->firstlocal ; unum_parms; u++, arg = arg->deftail->nextlocal) { - QC_snprintfz(pr_parm_names[u], sizeof(pr_parm_names[u]), "$arg_%u", u); - QCC_PR_ParseWarning(WARN_PARAMWITHNONAME, "Parameter %u of %s is not named", u+1, pr_scope->name); + QCC_PR_DummyDef(type->params[u].type, pr_parm_names[u], pr_scope, 0, arg, 0, true, false); } - parm = QCC_PR_GetSRef (type->params[u].type, pr_parm_names[u], pr_scope, 2, 0, false); - parm.sym->used = true; //make sure system parameters get seen by the engine, even if the names are stripped.. - parm.sym->referenced = true; - for (o = 0; o < (type->params[u].type->size+2)/3; o++) + if (type->vargcount) { - if (p < MAX_PARMS) - parm.ofs+=3;//no need to copy anything, as the engine will do it for us. + if (!pr_parm_argcount_name) + QCC_Error(ERR_INTERNAL, "I forgot what the va_count argument is meant to be called"); else - { //extra parms need to be explicitly copied. - if (!extra_parms[p - MAX_PARMS].sym) - { - char name[128]; - QC_snprintfz(name, sizeof(name), "$parm%u", p); - extra_parms[p - MAX_PARMS] = QCC_PR_GetSRef(type_vector, name, NULL, true, 0, GDF_STRIP); - } - else - QCC_ForceUnFreeDef(extra_parms[p - MAX_PARMS].sym); - QCC_UnFreeTemp(parm); - extra_parms[p - MAX_PARMS].cast = parm.cast; - if (type->params[u].type->size-o*3 >= 3) - { - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_V], extra_parms[p - MAX_PARMS], parm, NULL)); - parm.ofs+=3; - } - else if (type->params[u].type->size-o*3 == 2) - { - QCC_sref_t t = extra_parms[p - MAX_PARMS]; - QCC_UnFreeTemp(t); - QCC_UnFreeTemp(parm); - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_F], t, parm, NULL)); - t.ofs++; - parm.ofs++; - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_F], t, parm, NULL)); - parm.ofs++; - } - else - { - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_F], extra_parms[p - MAX_PARMS], parm, NULL)); - parm.ofs++; - } - } - p++; + QCC_PR_DummyDef(type_float, pr_parm_argcount_name, pr_scope, 0, arg, 0, true, false); } - QCC_FreeTemp(parm); } - - if (dowrap) - { //if we're wrapping, then we moved the old function entry to the end and reused it for our function. - //so we need to define some local that refers to the prior def. - QCC_sref_t priorim = QCC_MakeIntConst(numfunctions-1); - prior = QCC_PR_DummyDef(f->type, "prior", f, 0, priorim.sym, 0, true, GDF_CONST); //create a union into it - prior->initialized = true; - prior->filen = functions[numfunctions-1].filen; - prior->s_filed = functions[numfunctions-1].s_filed; - prior->s_line = functions[numfunctions-1].line; - - QCC_FreeTemp(priorim); - } - - if (type->vargcount) + else { - if (!pr_parm_argcount_name) - QCC_Error(ERR_INTERNAL, "I forgot what the va_count argument is meant to be called"); - else + for (u=0, p=0 ; unum_parms; u++) { - QCC_sref_t va_passcount = QCC_PR_GetSRef(type_float, "__va_count", NULL, true, 0, 0); - QCC_sref_t va_count = QCC_PR_GetSRef(type_float, pr_parm_argcount_name, pr_scope, true, 0, 0); - QCC_sref_t numparms = QCC_MakeFloatConst(type->num_parms); - QCC_PR_SimpleStatement(&pr_opcodes[OP_SUB_F], va_passcount, numparms, va_count, false); - QCC_FreeTemp(numparms); - QCC_FreeTemp(va_passcount); - QCC_FreeTemp(va_count); + unsigned int o; + if (!*pr_parm_names[u]) + { + QC_snprintfz(pr_parm_names[u], sizeof(pr_parm_names[u]), "$arg_%u", u); + QCC_PR_ParseWarning(WARN_PARAMWITHNONAME, "Parameter %u of %s is not named", u+1, pr_scope->name); + } + parm = QCC_PR_GetSRef (type->params[u].type, pr_parm_names[u], pr_scope, 2, 0, false); + parm.sym->used = true; //make sure system parameters get seen by the engine, even if the names are stripped.. + parm.sym->referenced = true; + + for (o = 0; o < (type->params[u].type->size+2)/3; o++) + { + if (p < MAX_PARMS) + parm.ofs+=3;//no need to copy anything, as the engine will do it for us. + else + { //extra parms need to be explicitly copied. + if (!extra_parms[p - MAX_PARMS].sym) + { + char name[128]; + QC_snprintfz(name, sizeof(name), "$parm%u", p); + extra_parms[p - MAX_PARMS] = QCC_PR_GetSRef(type_vector, name, NULL, true, 0, GDF_STRIP); + } + else + QCC_ForceUnFreeDef(extra_parms[p - MAX_PARMS].sym); + QCC_UnFreeTemp(parm); + extra_parms[p - MAX_PARMS].cast = parm.cast; + if (type->params[u].type->size-o*3 >= 3) + { + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_V], extra_parms[p - MAX_PARMS], parm, NULL)); + parm.ofs+=3; + } + else if (type->params[u].type->size-o*3 == 2) + { + QCC_sref_t t = extra_parms[p - MAX_PARMS]; + QCC_UnFreeTemp(t); + QCC_UnFreeTemp(parm); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_F], t, parm, NULL)); + t.ofs++; + parm.ofs++; + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_F], t, parm, NULL)); + parm.ofs++; + } + else + { + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_F], extra_parms[p - MAX_PARMS], parm, NULL)); + parm.ofs++; + } + } + p++; + } + QCC_FreeTemp(parm); + } + + if (type->vargcount) + { + if (!pr_parm_argcount_name) + QCC_Error(ERR_INTERNAL, "I forgot what the va_count argument is meant to be called"); + else + { + QCC_sref_t va_passcount = QCC_PR_GetSRef(type_float, "__va_count", NULL, true, 0, 0); + QCC_sref_t va_count = QCC_PR_GetSRef(type_float, pr_parm_argcount_name, pr_scope, true, 0, GDF_SCANLOCAL); + QCC_sref_t numparms = QCC_MakeFloatConst(type->num_parms); + va_passcount.sym->referenced = true; + QCC_PR_SimpleStatement(&pr_opcodes[OP_SUB_F], va_passcount, numparms, va_count, false); + QCC_FreeTemp(numparms); + QCC_FreeTemp(va_passcount); + QCC_FreeTemp(va_count); + } } } @@ -12197,40 +12439,60 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *typ int i; int maxvacount = 24; QCC_sref_t a; - pbool opcodeextensions = QCC_OPCodeValid(&pr_opcodes[OP_FETCH_GBL_F]) || QCC_OPCodeValid(&pr_opcodes[OP_LOADA_F]); //if we have opcode extensions, we can use those instead of via a function. this allows to use proper locals for the vargs. + //if we have opcode extensions, we can use those instead of via a function. this allows to use proper locals for the vargs. + //otherwise we have to use static/globals instead, so that the child function can access them without clobbering. FIXME: this is silly. we should be able to bias our locals to avoid stomping the array access' locals + pbool opcodeextensions = QCC_OPCodeValid(&pr_opcodes[OP_FETCH_GBL_F]) || QCC_OPCodeValid(&pr_opcodes[OP_LOADA_F]); QCC_sref_t va_list; - va_list = QCC_PR_GetSRef(type_vector, "__va_list", pr_scope, true, maxvacount, opcodeextensions?0:GDF_STATIC); - - for (i = 0; i < maxvacount; i++) + va_list = QCC_PR_GetSRef(type_vector, "__va_list", pr_scope, true, maxvacount, GDF_SCANLOCAL|(opcodeextensions?0:GDF_STATIC)); + if (QCC_OPCodeValid(&pr_opcodes[OP_FETCH_GBL_F]) && !QCC_OPCodeValid(&pr_opcodes[OP_LOADA_F])) + va_list.sym->arraylengthprefix = true; + if (!mergeargs) { - QCC_ref_t varef; - u = i + type->num_parms; - if (u >= MAX_PARMS) + for (i = 0; i < maxvacount; i++) { - if (!extra_parms[u - MAX_PARMS].sym) + QCC_ref_t varef; + u = i + type->num_parms; + if (u >= MAX_PARMS) { - char name[128]; - QC_snprintfz(name, sizeof(name), "$parm%u", u); - extra_parms[u - MAX_PARMS] = QCC_PR_GetSRef(type_vector, name, NULL, true, 0, GDF_STRIP); + if (!extra_parms[u - MAX_PARMS].sym) + { + char name[128]; + QC_snprintfz(name, sizeof(name), "$parm%u", u); + extra_parms[u - MAX_PARMS] = QCC_PR_GetSRef(type_vector, name, NULL, true, 0, GDF_STRIP); + } + else + QCC_ForceUnFreeDef(extra_parms[u - MAX_PARMS].sym); + a = extra_parms[u - MAX_PARMS]; } else - QCC_ForceUnFreeDef(extra_parms[u - MAX_PARMS].sym); - a = extra_parms[u - MAX_PARMS]; + { + a.sym = &def_parms[u]; + a.ofs = 0; + QCC_ForceUnFreeDef(a.sym); + } + a.cast = type_vector; + QCC_UnFreeTemp(va_list); + QCC_StoreSRefToRef(QCC_PR_BuildRef(&varef, REF_ARRAY, va_list, QCC_MakeIntConst(i*3), type_vector, false), a, false, false); } - else - { - a.sym = &def_parms[u]; - a.ofs = 0; - QCC_ForceUnFreeDef(a.sym); - } - a.cast = type_vector; - QCC_UnFreeTemp(va_list); - QCC_StoreSRefToRef(QCC_PR_BuildRef(&varef, REF_ARRAY, va_list, QCC_MakeIntConst(i*3), type_vector, false), a, false, false); } QCC_FreeTemp(va_list); } - QCC_RemapLockedTemps(-1, -1); + if (pif_flags & (PIF_WRAP|PIF_AUTOWRAP)) + { //if we're wrapping, then we moved the old function entry to the end and reused it for our function. + //so we need to define some local that refers to the prior def. + int funcref = numfunctions-1; + QCC_sref_t priorim = QCC_MakeUniqueConst(type_function, &funcref); + priorim.sym->referenced = true; + priorim.cast = f->type; + prior = QCC_PR_DummyDef(f->type, "prior", f, 0, priorim.sym, 0, true, GDF_CONST|GDF_STATIC|GDF_SCANLOCAL); //create a union into it + prior->initialized = true; + prior->filen = functions[numfunctions-1].filen; + prior->s_filed = functions[numfunctions-1].s_filed; + prior->s_line = functions[numfunctions-1].line; + + QCC_FreeTemp(priorim); + } /*if (pr_classtype) { @@ -12240,21 +12502,44 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *typ QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], QCC_PR_DummyDef(pr_classtype, "self", pr_scope, 0, e2->ofs, false), e, NULL)); }*/ + lastparm = pr.local_tail; + // // check for a state opcode // if (QCC_PR_CheckToken ("[")) QCC_PR_ParseState (); + //accumulate implies wrap when the first function wasn't defined to accumulate (should really be explicit, but gmqcc compat) + if (pif_flags & PIF_AUTOWRAP) + { + QCC_sref_t args[MAX_PARMS+MAX_EXTRA_PARMS]; + QCC_sref_t r; + unsigned int i; + for (i = 0; i < type->num_parms; i++) + args[i] = QCC_PR_GetSRef (type->params[i].type, pr_parm_names[i], pr_scope, 2, 0, false); + r = QCC_PR_GenerateFunctionCallSref(nullsref, QCC_MakeSRefForce(prior, 0, prior->type), args, type->num_parms); + prior->referenced = true; + + if (f->type->aux_type->type == ev_void) + QCC_FreeTemp(r); + else + { + if (!f->returndef.cast) + f->returndef = QCC_PR_GetSRef(f->type->aux_type, "return", pr_scope, true, 0, 0); + QCC_StoreToSRef(f->returndef, r, type, false, false); + } + } + if (QCC_PR_CheckKeyword (keyword_asm, "asm")) { QCC_PR_Expect ("{"); - while (!QCC_PR_CheckToken("}")) + while (STRCMP ("}", pr_token)) QCC_PR_ParseAsm (); } else { - if (QCC_PR_CheckKeyword (keyword_var, "var")) //reacc support + if (!(pif_flags & PIF_ACCUMULATE) && QCC_PR_CheckKeyword (keyword_var, "var")) //reacc support { //parse lots of locals char *name; do { @@ -12275,16 +12560,11 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *typ QCC_FreeTemps(); } } - if (f->returndef.cast) - { - PR_GenerateReturnOuts(); - QCC_ForceUnFreeDef(f->returndef.sym); - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_RETURN], f->returndef, nullsref, NULL)); - } + QCC_FreeTemps(); if (prior && !prior->referenced) - QCC_PR_ParseError(ERR_REDECLARATION, "Wrapper function does not refer to its prior function"); + QCC_PR_ParseError(ERR_REDECLARATION, "Wrapper function \"%s\" does not refer to its prior function", def->name); pr_token_line_last = pr_token_line; @@ -12293,11 +12573,6 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *typ // if (statements[numstatements - 1].op != OP_RETURN) // QCC_PR_ParseWarning(WARN_MISSINGRETURN, "%s: not all control paths return a value", pr_scope->name ); - if (f->code == numstatements) - needsdone = true; - else if (statements[numstatements - 1].op != OP_RETURN && statements[numstatements - 1].op != OP_DONE) - needsdone = true; - if (num_gotos) { int i, j; @@ -12325,40 +12600,22 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *typ num_gotos = 0; } - if (opt_return_only && !needsdone) - needsdone = QCC_FuncJumpsTo(f->code, numstatements, numstatements); + f->line_end = pr_token_line_last; + if (1)//(pif_flags & PIF_ACCUMULATE) || pr_scope->parentscope) + { //FIXME: should probably always take this path, but kinda pointless until we have relocs for defs + QCC_RemapLockedTemps(f->code, numstatements); + QCC_Marshal_Locals(f->code, numstatements); - // emit an end of statements opcode - if (!opt_return_only || needsdone) - { - /*if (pr_classtype) - { - QCC_def_t *e, *e2; - e = QCC_PR_GetDef(NULL, "__oself", pr_scope, false, 0); - e2 = QCC_PR_GetDef(NULL, "self", NULL, false, 0); - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], e, QCC_PR_DummyDef(pr_classtype, "self", pr_scope, 0, e2->ofs, false), NULL)); - }*/ - - if (needsdone) - PR_GenerateReturnOuts(); - QCC_PR_Statement (&pr_opcodes[OP_DONE], nullsref, nullsref, NULL); + f->numstatements = numstatements - f->code; + f->statements = qccHunkAlloc(sizeof(*statements)*f->numstatements); + memcpy(f->statements, statements+f->code, sizeof(*statements) * f->numstatements); + numstatements = f->code; } else - optres_return_only++; - - QCC_CheckForDeadAndMissingReturns(f->code, numstatements, type->aux_type->type); - -// if (opt_comexprremoval) -// QCC_CommonSubExpressionRemoval(f->code, numstatements); - - - QCC_RemapLockedTemps(f->code, numstatements); - QCC_WriteAsmFunction(pr_scope, f->code, f->firstlocal); - - QCC_Marshal_Locals(f->code, numstatements); - - if (opt_compound_jumps) - QCC_CompoundJumps(f->code, numstatements); + { + QCC_PR_FinaliseFunction(f); + } + pr_scope = NULL; if (num_labels) num_labels = 0; @@ -12380,6 +12637,29 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *typ QCC_PR_ParseError(ERR_ILLEGALCASES, "%s: function contains illegal cases", pr_scope->name); } + //clean up the locals. remove parms from the hashtable but don't clean subscoped_away so that we can repopulate on the next accumulation + for (d = f->firstlocal; d != lastparm->nextlocal; d = d->nextlocal) + { + if (!d->subscoped_away) + pHash_RemoveData(&localstable, d->name, d); + } + //any non-arg locals defined within the accumulation should not be visible next time around. + for (; d; d = d->nextlocal) + { + if (!d->subscoped_away) + { + pHash_RemoveData(&localstable, d->name, d); + d->subscoped_away = true; + } + } +#if 0//def _DEBUG + for (u = 0; u < localstable.numbuckets; u++) + { + if (localstable.bucket[u]) + localstable.bucket[u] = NULL; + } +#endif + QCC_PR_Lex(); return f; @@ -12485,7 +12765,7 @@ QCC_def_t *QCC_PR_EmitArrayGetVector(QCC_sref_t array) if (numfunctions >= MAX_FUNCTIONS) QCC_Error(ERR_INTERNAL, "Too many function defs"); - pr_scope = QCC_PR_GenerateQCFunction(func, ftype, false); + pr_scope = QCC_PR_GenerateQCFunction(func, ftype, 0); pr_source_line = pr_token_line_last = pr_scope->line = array.sym->s_line; //thankfully these functions are emitted after compilation. pr_scope->filen = array.sym->filen; pr_scope->s_filed = array.sym->s_filed; @@ -12542,7 +12822,7 @@ void QCC_PR_EmitArrayGetFunction(QCC_def_t *scope, QCC_def_t *arraydef, char *ar // vectortrick.cast = vectortrick.sym->type; // } - pr_scope = QCC_PR_GenerateQCFunction(scope, scope->type, false); + pr_scope = QCC_PR_GenerateQCFunction(scope, scope->type, 0); pr_source_line = pr_token_line_last = pr_scope->line = thearray.sym->s_line; //thankfully these functions are emitted after compilation. pr_scope->filen = thearray.sym->filen; pr_scope->s_filed = thearray.sym->s_filed; @@ -12732,7 +13012,7 @@ void QCC_PR_EmitArraySetFunction(QCC_def_t *scope, QCC_def_t *arraydef, char *ar s_filen = arraydef->filen; s_filed = arraydef->s_filed; - pr_scope = QCC_PR_GenerateQCFunction(scope, scope->type, false); + pr_scope = QCC_PR_GenerateQCFunction(scope, scope->type, 0); pr_source_line = pr_token_line_last = pr_scope->line = thearray.sym->s_line; //thankfully these functions are emitted after compilation. pr_scope->filen = thearray.sym->filen; pr_scope->s_filed = thearray.sym->s_filed; @@ -12802,7 +13082,7 @@ void QCC_PR_EmitArraySetFunction(QCC_def_t *scope, QCC_def_t *arraydef, char *ar //only the main def is of use to the compiler. //the subparts are emitted to the compiler and allow correct saving/loading //be careful with fields, this doesn't allocated space, so will it allocate fields. It only creates defs at specified offsets. -QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_function_t *scope, int arraysize, QCC_def_t *rootsymbol, unsigned int ofs, int referable, unsigned int flags) +QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, const char *name, QCC_function_t *scope, int arraysize, QCC_def_t *rootsymbol, unsigned int ofs, int referable, unsigned int flags) { char array[64]; char newname[256]; @@ -12862,7 +13142,7 @@ QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_function_t *scope, pr.def_tail->next = def; pr.def_tail = def; - if (scope && !(flags & (GDF_CONST|GDF_STATIC))) + if (scope)// && !(flags & (GDF_CONST|GDF_STATIC))) { //constants are never considered locals. static (local) variables are also not counted as locals as they're logically globals in all regards other than visibility. pr.local_tail->nextlocal = def; pr.local_tail = def; @@ -12898,7 +13178,7 @@ QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_function_t *scope, if (def->allowinline) { - int p; + unsigned int p; for (p = 0; p < type->num_parms; p++) { if (type->params[p].out) @@ -13030,7 +13310,7 @@ If allocate is 2, a new def will be allocated, and it'll error if there's a dupe ============ */ -QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, struct QCC_function_s *scope, pbool allocate, int arraysize, unsigned int flags) +QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, const char *name, struct QCC_function_s *scope, pbool allocate, int arraysize, unsigned int flags) { int ofs; QCC_def_t *def; @@ -13098,7 +13378,14 @@ QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, struct QCC_function_s *s } if (type && typecmp(def->type, type)) + { + if (scope && allocate && pr_subscopedlocals) + { //assume it was defined in a different subscope. hopefully we'll start favouring the new one until it leaves subscope. + def = pHash_GetNext(&localstable, name, def); + continue; + } QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHREDEC, def, "Type mismatch on redeclaration of %s. %s, should be %s",name, TypeName(type, typebuf1, sizeof(typebuf1)), TypeName(def->type, typebuf2, sizeof(typebuf2))); + } if (def->arraysize != arraysize && arraysize>=0) QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHARRAYSIZE, def, "Array sizes for redecleration of %s do not match",name); if (allocate && scope && !(flags & GDF_STATIC)) @@ -13144,7 +13431,7 @@ QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, struct QCC_function_s *s } //ignore it if its static in some other file. - if (def->isstatic && strcmp(def->filen, s_filen)) + if (def->isstatic && strcmp(def->filen, scope?scope->filen:s_filen)) { if (!foundstatic) foundstatic = def; //save it off purely as a warning. @@ -13197,7 +13484,7 @@ QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, struct QCC_function_s *s QCC_PR_ParseWarning (WARN_COMPATIBILITYHACK, "%s builtin was redefined as %s. ignoring alternative definition",name, TypeName(type, typebuf1, sizeof(typebuf1))); QCC_PR_ParsePrintDef(WARN_COMPATIBILITYHACK, def); } - else if (1)//def->initialized == 3) + else if (1)//def->unused) { //previous def was norefed def = pHash_GetNext(&localstable, name, def); continue; // in a different function @@ -13259,9 +13546,29 @@ QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, struct QCC_function_s *s QCC_PR_ParsePrintDef(WARN_DUPLICATEDEFINITION, foundstatic); } + if (flags & GDF_SCANLOCAL) + { + for (def = pr.local_head.nextlocal; def; def = def->nextlocal) + { + if (!strcmp(def->name, name)) + { + if (allocate && def->arraysize != arraysize) + continue; + if (allocate) + { //relink it + pHash_Add(&localstable, name, def, qccHunkAlloc(sizeof(bucket_t))); + def->subscoped_away = false; + } + QCC_ForceUnFreeDef(def); + return def; + } + } + } + if (!allocate) return NULL; + if (arraysize < 0) { QCC_PR_ParseError (ERR_ARRAYNEEDSSIZE, "First declaration of array %s with no size",name); @@ -13285,7 +13592,7 @@ QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, struct QCC_function_s *s QCC_ForceUnFreeDef(def); return def; } -QCC_sref_t QCC_PR_GetSRef (QCC_type_t *type, char *name, QCC_function_t *scope, pbool allocate, int arraysize, unsigned int flags) +QCC_sref_t QCC_PR_GetSRef (QCC_type_t *type, const char *name, QCC_function_t *scope, pbool allocate, int arraysize, unsigned int flags) { QCC_sref_t sr; QCC_def_t *def = QCC_PR_GetDef(type, name, scope, allocate, arraysize, flags); @@ -13403,13 +13710,13 @@ void QCC_PR_ExpandUnionToFields(QCC_type_t *type, unsigned int *fields) QCC_PR_DummyFieldDef(pass, pr_scope, 1, fields, GDF_SAVED|GDF_CONST); } -#define PIF_WRAP 1 //new initialisation is meant to wrap an existing one. -#define PIF_STRONGER 2 //previous initialisation was weak. //basedef can be null, meaning its initialising a temp -void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t def, unsigned int flags) +//returns true where its a const/static initialiser. false if non-const/final initialiser +pbool QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t def, unsigned int flags) { QCC_sref_t tmp; int i; + pbool ret = true; if (arraysize) { @@ -13419,7 +13726,7 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d { for (i = 0; i < arraysize; i++) { - QCC_PR_ParseInitializerType(0, basedef, def, flags); + ret &= QCC_PR_ParseInitializerType(0, basedef, def, flags); def.ofs += def.cast->size; if (!QCC_PR_CheckToken(",")) { @@ -13508,7 +13815,7 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d else f = NULL; } - else + else if (QCC_PR_PeekToken("{") || QCC_PR_PeekToken("[")) { if (basedef) { @@ -13535,10 +13842,13 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d { QCC_PR_ParseWarning(WARN_NOTSTANDARDBEHAVIOUR, "%s already declared as a builtin", defname); QCC_PR_ParsePrintSRef(WARN_NOTSTANDARDBEHAVIOUR, def); - basedef->initialized = 3; + basedef->unused = true; } else - QCC_PR_ParseErrorPrintSRef (ERR_REDECLARATION, def, "redeclaration of function body"); + { + QCC_PR_ParseWarning (ERR_REDECLARATION, "redeclaration of function body"); + QCC_PR_ParsePrintSRef(WARN_NOTSTANDARDBEHAVIOUR, def); + } } } @@ -13557,6 +13867,8 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d patch = QCC_Generate_OP_GOTO(); f = QCC_PR_ParseImmediateStatements (NULL, type, flags&PIF_WRAP); patch->a.ofs = &statements[numstatements] - patch; + if (patch->a.ofs == 1) + numstatements--; //never mind then. //make sure parent state is restored properly. pr.local_head.nextlocal = firstlocal; @@ -13579,7 +13891,13 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d } } } - if (!tmp.cast) + else + { + f = NULL; + tmp = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA); + tmp = QCC_EvaluateCast(tmp, type, true); + } + if (!tmp.cast && f) { pr_scope = parentfunc; tmp = QCC_MakeIntConst(f - functions); @@ -13607,7 +13925,7 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d } QCC_PR_Lex(); QCC_PR_Expect(")"); - return; + return ret; } tmp = QCC_MakeTranslateStringConst(pr_immediate_string); QCC_PR_Lex(); @@ -13629,7 +13947,7 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d def.cast = (type)->params[partnum].type; def.ofs = offset + (type)->params[partnum].ofs; - QCC_PR_ParseInitializerType((type)->params[partnum].arraysize, basedef, def, flags); + ret &= QCC_PR_ParseInitializerType((type)->params[partnum].arraysize, basedef, def, flags); if (isunion || !QCC_PR_CheckToken(",")) { if (isunion && (type->num_parms == 1 && !type->params->paramname)) @@ -13638,7 +13956,13 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d break; } } - return; + return ret; + } + else if (type->type == ev_vector && QCC_PR_PeekToken("{")) + { //vectors can be treated as an array of 3 floats. + def.cast = type_float; + ret &= QCC_PR_ParseInitializerType(3, basedef, def, flags); + return ret; } else if (type->type == ev_pointer && QCC_PR_CheckToken("{")) { @@ -13654,7 +13978,7 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d do { //expand the array - int newsize = tmp.sym->arraysize * tmp.cast->size; + unsigned int newsize = tmp.sym->arraysize * tmp.cast->size; if (tmp.sym->symbolsize < newsize) { void *newdata; @@ -13669,7 +13993,7 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d QCC_PR_DummyDef(tmp.cast, NULL, pr_scope, 0, tmp.sym, tmp.ofs, false, GDF_STRIP|(pr_scope?GDF_STATIC:0)); //and fill it in. - QCC_PR_ParseInitializerType(0, tmp.sym, tmp, flags); + ret &= QCC_PR_ParseInitializerType(0, tmp.sym, tmp, flags); tmp.ofs += type->aux_type->size; } while(QCC_PR_CheckToken(",")); QCC_PR_Expect("}"); @@ -13691,44 +14015,52 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t d { if (!tmp.sym->constant) { - if (basedef->scope && !basedef->isstatic) + if (basedef->scope && !basedef->isstatic && !basedef->initialized) + { +// QCC_PR_ParseWarning(WARN_GMQCC_SPECIFIC, "initializer for '%s' is not constant", basedef->name); +// QCC_PR_ParsePrintSRef(WARN_GMQCC_SPECIFIC, tmp); +// basedef->constant = false; goto finalnotconst; + } QCC_PR_ParseWarning(ERR_BADIMMEDIATETYPE, "initializer for '%s' is not constant", basedef->name); QCC_PR_ParsePrintSRef(ERR_BADIMMEDIATETYPE, tmp); } - tmp.sym->referenced = true; - if (!tmp.sym->initialized) - { - //FIXME: we NEED to support relocs somehow - QCC_PR_ParseWarning(WARN_UNINITIALIZED, "initializer is not initialised, %s will be treated as 0", QCC_GetSRefName(tmp)); - QCC_PR_ParsePrintSRef(WARN_UNINITIALIZED, tmp); - } - - if (basedef->initialized && basedef->initialized != 3 && !(flags & PIF_STRONGER)) - { - for (i = 0; (unsigned)i < type->size; i++) - if (def.sym->symboldata[def.ofs+i]._int != tmp.sym->symboldata[tmp.ofs+i]._int) - { - if (!def.sym->arraysize && def.cast->type == ev_function && !strcmp(def.sym->name, "parseentitydata") && (functions[def.sym->symboldata[def.ofs+i]._int].builtin == 608 || functions[def.sym->symboldata[def.ofs+i]._int].builtin == 613)) - { //dpextensions is WRONG, and claims it to be 608. its also too common, so lets try working around that. - if (functions[def.sym->symboldata[def.ofs+i]._int].builtin == 608) - functions[def.sym->symboldata[def.ofs+i]._int].builtin = 613; - QCC_PR_ParseWarning (WARN_COMPATIBILITYHACK, "incompatible redeclaration. Please validate builtin numbers. parseentitydata is #613"); - QCC_PR_ParsePrintSRef(WARN_COMPATIBILITYHACK, tmp); - } - else if (!def.sym->arraysize && def.cast->type == ev_function && functions[def.sym->symboldata[def.ofs+i]._int].code>-1 && functions[tmp.sym->symboldata[tmp.ofs+i]._int].code==-1) - { - QCC_PR_ParseWarning (WARN_COMPATIBILITYHACK, "incompatible redeclaration. Ignoring replacement of qc function with builtin."); - QCC_PR_ParsePrintSRef(WARN_COMPATIBILITYHACK, tmp); - } - else - QCC_PR_ParseErrorPrintSRef (ERR_REDECLARATION, def, "incompatible redeclaration"); - } - } else { - for (i = 0; (unsigned)i < type->size; i++) - def.sym->symboldata[def.ofs+i]._int = tmp.sym->symboldata[tmp.ofs+i]._int; + tmp.sym->referenced = true; + if (!tmp.sym->initialized) + { + //FIXME: we NEED to support relocs somehow + QCC_PR_ParseWarning(WARN_UNINITIALIZED, "initializer is not initialised, %s will be treated as 0", QCC_GetSRefName(tmp)); + QCC_PR_ParsePrintSRef(WARN_UNINITIALIZED, tmp); + } + + if (basedef->initialized && !basedef->unused && !(flags & PIF_STRONGER)) + { + for (i = 0; (unsigned)i < type->size; i++) + if (def.sym->symboldata[def.ofs+i]._int != tmp.sym->symboldata[tmp.ofs+i]._int) + { + if (!def.sym->arraysize && def.cast->type == ev_function && !strcmp(def.sym->name, "parseentitydata") && (functions[def.sym->symboldata[def.ofs+i]._int].builtin == 608 || functions[def.sym->symboldata[def.ofs+i]._int].builtin == 613)) + { //dpextensions is WRONG, and claims it to be 608. its also too common, so lets try working around that. + if (functions[def.sym->symboldata[def.ofs+i]._int].builtin == 608) + functions[def.sym->symboldata[def.ofs+i]._int].builtin = 613; + QCC_PR_ParseWarning (WARN_COMPATIBILITYHACK, "incompatible redeclaration. Please validate builtin numbers. parseentitydata is #613"); + QCC_PR_ParsePrintSRef(WARN_COMPATIBILITYHACK, tmp); + } + else if (!def.sym->arraysize && def.cast->type == ev_function && functions[def.sym->symboldata[def.ofs+i]._int].code>-1 && functions[tmp.sym->symboldata[tmp.ofs+i]._int].code==-1) + { + QCC_PR_ParseWarning (WARN_COMPATIBILITYHACK, "incompatible redeclaration. Ignoring replacement of qc function with builtin."); + QCC_PR_ParsePrintSRef(WARN_COMPATIBILITYHACK, tmp); + } + else + QCC_PR_ParseErrorPrintSRef (ERR_REDECLARATION, def, "incompatible redeclaration"); + } + } + else + { + for (i = 0; (unsigned)i < type->size; i++) + def.sym->symboldata[def.ofs+i]._int = tmp.sym->symboldata[tmp.ofs+i]._int; + } } } else @@ -13739,6 +14071,7 @@ finalnotconst: if (def.sym->initialized) QCC_PR_ParseErrorPrintSRef (ERR_REDECLARATION, def, "%s initialised twice", basedef->name); + ret = 0; for (i = 0; (unsigned)i < type->size; ) { if (type->size - i >= 3) @@ -13747,7 +14080,7 @@ finalnotconst: if (type->size - i == 3) { QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_V], rhs, def, NULL, STFL_PRESERVEB)); - return; + return ret; } else QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_V], rhs, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB)); @@ -13763,7 +14096,7 @@ finalnotconst: if (type->size - i == 1) { QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_FNC], rhs, def, NULL, STFL_PRESERVEB)); - return; + return ret; } else QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_FNC], rhs, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB)); @@ -13774,7 +14107,7 @@ finalnotconst: if (type->size - i == 1) { QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_S], rhs, def, NULL, STFL_PRESERVEB)); - return; + return ret; } else QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_S], rhs, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB)); @@ -13785,7 +14118,7 @@ finalnotconst: if (type->size - i == 1) { QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_ENT], rhs, def, NULL, STFL_PRESERVEB)); - return; + return ret; } else QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_ENT], rhs, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB)); @@ -13796,7 +14129,7 @@ finalnotconst: if (type->size - i == 1) { QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_FLD], rhs, def, NULL, STFL_PRESERVEB)); - return; + return ret; } else QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_FLD], rhs, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB)); @@ -13807,7 +14140,7 @@ finalnotconst: if (type->size - i == 1) { QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_I], rhs, def, NULL, STFL_PRESERVEB)); - return; + return ret; } else QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_I], rhs, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB)); @@ -13818,7 +14151,7 @@ finalnotconst: if (type->size - i == 1) { QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], rhs, def, NULL, STFL_PRESERVEB)); - return; + return ret; } else QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], rhs, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB)); @@ -13831,13 +14164,15 @@ finalnotconst: } QCC_FreeTemp(tmp); } + return ret; } void QCC_PR_ParseInitializerDef(QCC_def_t *def, unsigned int flags) { - QCC_PR_ParseInitializerType(def->arraysize, def, QCC_MakeSRef(def, 0, def->type), flags); - if (!def->initialized || def->initialized == 3) - def->initialized = 1; + if (QCC_PR_ParseInitializerType(def->arraysize, def, QCC_MakeSRef(def, 0, def->type), flags)) + if (!def->initialized) + def->initialized = 1; + QCC_FreeDef(def); } QCC_sref_t QCC_PR_ParseDefaultInitialiser(QCC_type_t *type) { @@ -13974,8 +14309,10 @@ void QCC_PR_ParseDefs (char *classname, pbool fatal) pbool dowrap = false; pbool doweak = false; pbool forceused = false; + pbool accumulate = false; int arraysize; unsigned int gd_flags; + const char *aliasof = NULL; pr_assumetermtype = NULL; @@ -14228,6 +14565,8 @@ void QCC_PR_ParseDefs (char *classname, pbool fatal) dowrap = true; else if (QCC_PR_CheckKeyword(keyword_weak, "weak")) doweak = true; + else if (QCC_PR_CheckKeyword(keyword_accumulate, "accumulate")) + accumulate = true; else if (flag_attributes && !pr_scope && QCC_PR_CheckToken("[")) { QCC_PR_Expect("["); @@ -14241,19 +14580,23 @@ void QCC_PR_ParseDefs (char *classname, pbool fatal) { QCC_PR_Expect("("); if (pr_token_type == tt_name) - /*aliasof =*/ QCC_PR_ParseName(); + aliasof = QCC_PR_ParseName(); else if (pr_token_type == tt_immediate) { - //aliasof = copy(pr_immediate_string); + aliasof = strcpy(qccHunkAlloc(strlen(pr_immediate_string)+1), pr_immediate_string); QCC_PR_Lex(); } QCC_PR_Expect(")"); } else if (QCC_PR_CheckName("accumulate")) - doweak = dowrap = true; //FIXME: should instead append to the previous function, I think, which requires not finishing it properly until later, or something. + { + if (accumulate) + QCC_PR_ParseWarning(WARN_GMQCC_SPECIFIC, "accumulating an accumulating accumulation"); + accumulate = true; + } else { - QCC_PR_ParseWarning(0, "Unknown attribute \"%s\"", pr_token); + QCC_PR_ParseWarning(WARN_GMQCC_SPECIFIC, "Unknown attribute \"%s\"", pr_token); while(pr_token_type != tt_eof) { if (QCC_PR_PeekToken("]")) @@ -14509,7 +14852,7 @@ void QCC_PR_ParseDefs (char *classname, pbool fatal) else if (forceused) //FIXME: make proper pragma(used) thingie gd_flags |= GDF_USED; - if (dynlength.cast) + if (dynlength.cast && !aliasof) { #if 1//IAMNOTLAZY def = QCC_PR_GetDef (QCC_PR_PointerType(type), name, pr_scope, allocatenew, 0, gd_flags); @@ -14524,15 +14867,28 @@ void QCC_PR_ParseDefs (char *classname, pbool fatal) #endif } else - def = QCC_PR_GetDef (type, name, pr_scope, allocatenew, arraysize, gd_flags); + { + if (aliasof) + { + def = QCC_PR_GetDef (NULL, aliasof, pr_scope, false, arraysize, gd_flags); + if (!def) + QCC_PR_ParseError(ERR_NOTANAME, "%s not yet defined, cannot create %s as an alias", aliasof, name); + def->referenced = true; + def = QCC_PR_DummyDef(type, name, pr_scope, arraysize, def, 0, true, gd_flags); + } + else + def = QCC_PR_GetDef (type, name, pr_scope, allocatenew, arraysize, gd_flags); + } if (!def) QCC_PR_ParseError(ERR_NOTANAME, "%s is not part of class %s", name, classname); + if (accumulate && (def->type->type != ev_function || def->arraysize != 0)) + QCC_PR_ParseError(ERR_NOTAFUNCTION, "accumulate applies only to functions, not %s", def->name); + if (noref) { - if (type->type == ev_function && !def->initialized) - def->initialized = 3; + def->unused = true; def->referenced = true; } @@ -14540,6 +14896,7 @@ void QCC_PR_ParseDefs (char *classname, pbool fatal) { def->shared = shared; def->initialized = true; + def->nofold = true; } if (externfnc) def->initialized = 2; @@ -14579,14 +14936,16 @@ void QCC_PR_ParseDefs (char *classname, pbool fatal) else if (isinitialised) //this is an initialisation (or a function) { QCC_type_t *parentclass; + if (aliasof) + QCC_PR_ParseError (ERR_SHAREDINITIALISED, "alias %s may not be initialised", name); if (def->shared) QCC_PR_ParseError (ERR_SHAREDINITIALISED, "shared values may not be assigned an initial value", name); //if weak, only use the first non-weak version of the function if (autoprototype || dostrip || (def->initialized && doweak) || (!def->initialized && doweak && dowrap)) { //ignore the code and stuff - if ((dostrip || (doweak && dowrap)) && !def->initialized) - def->initialized = 3; + if ((dostrip || (doweak && dowrap))) + def->unused = true; if (dostrip) def->referenced = true; if (QCC_PR_CheckToken("[")) @@ -14638,16 +14997,49 @@ void QCC_PR_ParseDefs (char *classname, pbool fatal) parentclass = pr_classtype; pr_classtype = defclass?defclass:pr_classtype; if (flag_assumevar) - def->constant = isconstant || (!isvar && !pr_scope && type->type == ev_function); + def->constant = isconstant || (!isvar && !pr_scope && (type->type == ev_function || type->type == ev_field)); else def->constant = (isconstant || (!isvar && !pr_scope)); - QCC_PR_ParseInitializerDef(def, (dowrap?PIF_WRAP:0)|(def->weak?PIF_STRONGER:0)); + if (accumulate || def->accumulate) + { + unsigned int pif_flags = PIF_ACCUMULATE; + if (!def->initialized) + def->accumulate |= true; //first time + else if (dowrap) + pif_flags |= PIF_WRAP; //explicitly wrapping a prior accumulation... + else if (!def->accumulate) + { + QCC_PR_ParseWarning(WARN_GMQCC_SPECIFIC, "%s redeclared to accumulate after initial declaration", def->name); + pif_flags |= PIF_WRAP|PIF_AUTOWRAP; //wrap it automatically so its not so obvious + } + def->accumulate |= true; + def->initialized = true; + def->symboldata[0].function = QCC_PR_ParseImmediateStatements (def, def->type, pif_flags) - functions; + } + else + QCC_PR_ParseInitializerDef(def, (dowrap?PIF_WRAP:0)|(def->weak?PIF_STRONGER:0)); if (doweak) def->weak = true; else def->weak = false; - QCC_FreeDef(def); pr_classtype = parentclass; + + if (!def->nofold && def->constant && def->initialized && def->symbolheader == def && def->ofs == 0 && !def->arraysize) + { + QCC_def_t *base = NULL; + QCC_eval_t *val = &def->symboldata[0]; + if (type->type == ev_float) + base = QCC_MakeFloatConst(val->_float).sym; + else if (type->type == ev_integer) + base = QCC_MakeIntConst(val->_int).sym; + else if (type->type == ev_vector) + base = QCC_MakeVectorConst(val->vector[0], val->vector[1], val->vector[2]).sym; + if (base && !base->symbolheader->nofold) + { + def->ofs = base->ofs; + def->symbolheader = base->symbolheader; + } + } } else { @@ -14668,6 +15060,7 @@ void QCC_PR_ParseDefs (char *classname, pbool fatal) if (def->constant != c) { QCC_PR_ParseWarning(WARN_REDECLARATIONMISMATCH, "Redeclaration of %s changes const.", def->name); + QCC_PR_ParsePrintDef(WARN_REDECLARATIONMISMATCH, def); } } @@ -14800,7 +15193,7 @@ pbool QCC_PR_CompileFile (char *string, char *filename) QCC_PR_ParseDefs (NULL, true); -#ifdef _DEBUG +#if 0//def _DEBUG if (!pr_error_count) { QCC_def_t *d; @@ -14894,14 +15287,14 @@ void QCC_Cleanup(void) free(pr_breaks); free(pr_continues); free(pr_cases); - free(pr_casesdef); - free(pr_casesdef2); + free(pr_casesref); + free(pr_casesref2); max_breaks = max_continues = max_cases = num_continues = num_breaks = num_cases = 0; pr_breaks = NULL; pr_continues = NULL; pr_cases = NULL; - pr_casesdef = NULL; - pr_casesdef2 = NULL; + pr_casesref = NULL; + pr_casesref2 = NULL; #ifdef _DEBUG OpAssignsTo_Debug(); diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index 49a151b66..8b6c5b073 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -71,13 +71,13 @@ static void Q_strlcpy(char *dest, const char *src, int sizeofdest) char *pr_punctuation[] = // longer symbols must be before a shorter partial match -{"&&", "||", "<=", ">=","==", "!=", "/=", "*=", "+=", "-=", "(+)", "(-)", "|=", "&~=", "&=", "++", "--", "->", "^=", "::", ";", ",", "!", "*", "/", "(", ")", "-", "+", "=", "[", "]", "{", "}", "...", "..", ".", "><", "<<", "<", ">>", ">" , "?", "#" , "@", "&" , "|", "%", "^^", "^", "~", ":", NULL}; +{"&&", "||", "<=", ">=","==", "!=", "/=", "*=", "+=", "-=", "(+)", "(-)", "|=", "&~=", "&=", "++", "--", "->", "^=", "::", ";", ",", "!", "*^", "*", "/", "(", ")", "-", "+", "=", "[", "]", "{", "}", "...", "..", ".", "><", "<<=", "<<", "<", ">>=", ">>", ">" , "?", "#" , "@", "&" , "|", "%", "^^", "^", "~", ":", NULL}; char *pr_punctuationremap[] = //a nice bit of evilness. //(+) -> |= //-> -> . //(-) -> &~= -{"&&", "||", "<=", ">=","==", "!=", "/=", "*=", "+=", "-=", "|=", "&~=", "|=", "&~=", "&=", "++", "--", ".", "^=", "::", ";", ",", "!", "*", "/", "(", ")", "-", "+", "=", "[", "]", "{", "}", "...", "..", ".", "><", "<<", "<", ">>", ">" , "?", "#" , "@", "&" , "|", "%", "^^", "^", "~", ":", NULL}; +{"&&", "||", "<=", ">=","==", "!=", "/=", "*=", "+=", "-=", "|=", "&~=", "|=", "&~=", "&=", "++", "--", ".", "^=", "::", ";", ",", "!", "*^", "*", "/", "(", ")", "-", "+", "=", "[", "]", "{", "}", "...", "..", ".", "><", "<<=", "<<", "<", ">>=", ">>", ">" , "?", "#" , "@", "&" , "|", "%", "^^", "^", "~", ":", NULL}; // simple types. function types are dynamically allocated QCC_type_t *type_void; //void @@ -473,7 +473,7 @@ static void QCC_PR_GetDefinesListEnumerate(void *vctx, void *data) QC_snprintfz(term, sizeof(term), "\n%s", def->name); if (def->numparams >= 0) { - unsigned int i; + int i; QC_strlcat(term, "(", sizeof(term)); for (i = 0; i < def->numparams; i++) { @@ -989,11 +989,10 @@ pbool QCC_PR_Precompiler(void) pr_file_p++; } msg[a] = 0; + pr_file_p++; QCC_FindBestInclude(msg, compilingfile, false); - pr_file_p++; - QCC_PR_SkipToEndOfLine(true); } else if (!strncmp(directive, "datafile", 8)) @@ -1866,6 +1865,7 @@ void QCC_PR_LexString (void) if (qccwarningaction[WARN_NOTUTF8]) { + size_t c; for (c = 0; c < pr_immediate_strlen; ) { len = utf8_check(&pr_token[c], &code); @@ -2243,8 +2243,9 @@ void QCC_PR_LexPunctuation (void) pr_token_type = tt_punct; if (pr_file_p[0] == '*' && pr_file_p[1] == '*' && flag_dblstarexp) - { - strcpy (pr_token, "**"); + { //for compat with gmqcc. fteqcc uses *^ internally (which does not conflict with multiplying by dereferenced pointers - sucks for MSCLR c++ syntax) + QCC_PR_ParseWarning(WARN_GMQCC_SPECIFIC, "** operator conflicts with pointers. Consider using *^ instead.", pr_token); + strcpy (pr_token, "*^"); pr_file_p += 2; return; } @@ -2896,7 +2897,7 @@ void QCC_PR_PreProcessor_Define(pbool append) { char *exploitcheck; s++; - QCC_PR_NewLine(false); + QCC_PR_NewLine(true); s++; if( s[-1] == '\r' && s[0] == '\n' ) { @@ -2921,7 +2922,7 @@ foo\nbar\nmoo so if present, the preceeding \\\n and following \\\n must become an actual \n instead of being stripped. */ - for (exploitcheck = s; *exploitcheck && qcc_iswhite(*exploitcheck); exploitcheck++) + for (exploitcheck = s; *exploitcheck && qcc_iswhitesameline(*exploitcheck); exploitcheck++) ; if (*exploitcheck == '#') { @@ -3454,7 +3455,12 @@ int QCC_PR_CheckCompConst(void) } } else - QCC_PR_ParseError(ERR_TOOFEWPARAMS, "Macro without argument list"); + { + //QCC_PR_ParseError(ERR_TOOFEWPARAMS, "Macro without argument list"); + pr_file_p = initial_file_p; + pr_source_line = initial_line; + return false; + } } else { @@ -3726,7 +3732,11 @@ static void QCC_PR_PrintMacro (qcc_includechunk_t *chunk) QCC_PR_PrintMacro(chunk->prev); if (chunk->cnst) { +#if 1 + printf ("%s:%i: %s is defined here\n", chunk->cnst->fromfile, chunk->cnst->fromline, chunk->cnst->name); +#else printf ("%s:%i: expanding %s\n", chunk->currentfilename, chunk->currentlinenumber, chunk->cnst->name); +#endif if (verbose) printf ("%s\n", chunk->datastart); } @@ -4877,8 +4887,13 @@ QCC_type_t *QCC_PR_GenFunctionType (QCC_type_t *rettype, struct QCC_typeparam_s ftype->vargcount = false; for (i = 0; i < numargs; i++, p++) { - p->paramname = qccHunkAlloc(strlen(args[i].paramname)+1); - strcpy(p->paramname, args[i].paramname); + if (args[i].paramname) + { + p->paramname = qccHunkAlloc(strlen(args[i].paramname)+1); + strcpy(p->paramname, args[i].paramname); + } + else + p->paramname = ""; p->type = args[i].type; p->out = args[i].out; p->optional = args[i].optional; @@ -5395,7 +5410,6 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) { pr_classtype = newt; QCC_PR_ParseInitializerDef(def, 0); - QCC_FreeDef(def); pr_classtype = NULL; /* f = QCC_PR_ParseImmediateStatements (def, newparm); diff --git a/engine/qclib/qccgui.c b/engine/qclib/qccgui.c index 5e135d0e1..d62406447 100644 --- a/engine/qclib/qccgui.c +++ b/engine/qclib/qccgui.c @@ -281,6 +281,10 @@ typedef struct editor_s { int savefmt; time_t filemodifiedtime; struct editor_s *next; + + //for avoiding silly redraws etc when titles don't actually change... + int oldsavefmt; + int oldline; } editor_t; editor_t *editors; @@ -2530,6 +2534,11 @@ static void UpdateEditorTitle(editor_t *editor) { char title[2048]; char *encoding = "unknown"; + if (editor->oldsavefmt == editor->savefmt && editor->oldline == editor->curline) + return; //nothing changed. + editor->oldsavefmt = editor->savefmt; + editor->oldline = editor->curline; + switch(editor->savefmt) { case UTF8_RAW: @@ -2633,7 +2642,7 @@ static LRESULT CALLBACK EditorWndProc(HWND hWnd,UINT message, editor->tooltip = CreateWindowEx(0, TOOLTIPS_CLASS, NULL, WS_POPUP|TTS_ALWAYSTIP|TTS_NOPREFIX, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hWnd, NULL, ghInstance, NULL); if (editor->tooltip) - { + { TOOLINFO toolInfo = { 0 }; toolInfo.cbSize = sizeof(toolInfo); toolInfo.hwnd = hWnd; @@ -2844,7 +2853,7 @@ static LRESULT CALLBACK EditorWndProc(HWND hWnd,UINT message, SendMessage(editor->editpane, SCI_CALLTIPCANCEL, 0, 0); break; } - UpdateEditorTitle(editor); + //UpdateEditorTitle(editor); } else { @@ -5513,6 +5522,7 @@ void OptionsDialog(void) tipwnd = CreateWindow(TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, optionsmenu, NULL, ghInstance, NULL); SetWindowPos(tipwnd, HWND_TOPMOST,0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + SendMessage(tipwnd, TTM_SETMAXTIPWIDTH, 0, 500); subsection = CreateWindow("BUTTON", "Optimisations", WS_CHILD|WS_VISIBLE|BS_GROUPBOX, 0, 0, 400, height-40*4+24, optionsmenu, NULL, ghInstance, NULL); @@ -6941,6 +6951,7 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin ghInstance= hInstance; GUI_SetDefaultOpts(); + GUI_ParseCommandLine(lpCmdLine); strcpy(enginebinary, ""); strcpy(enginebasedir, ""); diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index f94538c10..05b85179c 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -213,6 +213,7 @@ struct { {" F318", WARN_DUPLICATEMACRO}, {" F319", WARN_CONSTANTCOMPARISON}, {" F320", WARN_PARAMWITHNONAME}, + {" F321", WARN_GMQCC_SPECIFIC}, {" F208", WARN_NOTREFERENCEDCONST}, {" F209", WARN_EXTRAPRECACHE}, @@ -335,6 +336,7 @@ compiler_flag_t compiler_flag[] = { {&keyword_vector, defaultkeyword, "vector", "Keyword: vector", "Disables the 'vector' keyword."}, {&keyword_wrap, defaultkeyword, "wrap", "Keyword: wrap", "Disables the 'wrap' keyword."}, {&keyword_weak, defaultkeyword, "weak", "Keyword: weak", "Disables the 'weak' keyword."}, + {&keyword_accumulate, nondefaultkeyword,"accumulate", "Keyword: accumulate", "Disables the 'accumulate' keyword."}, //options {&keywords_coexist, FLAG_ASDEFAULT, "kce", "Keywords Coexist", "If you want keywords to NOT be disabled when they a variable by the same name is defined, check here."}, @@ -936,6 +938,8 @@ void QCC_DetermineNeededSymbols(QCC_def_t *endsyssym) { for (; sym; sym = sym->next) { + if (sym->unused && !sym->initialized) + sym->initialized = 1; //even if its not. sym->used = true; sym->referenced = true; //silence warnings about unreferenced things that can't be stripped if (sym == endsyssym) @@ -973,9 +977,22 @@ void QCC_DetermineNeededSymbols(QCC_def_t *endsyssym) } } +const QCC_eval_t *QCC_SRef_EvalConst(QCC_sref_t ref); //allocates final space for the def, making it a true def void QCC_FinaliseDef(QCC_def_t *def) { +//#define DEBUG_DUMP_GLOBALMAP +#if defined(DEBUG_DUMP) || defined(DEBUG_DUMP_GLOBALMAP) + int ssize = def->symbolsize; + const QCC_eval_t *v; + QCC_sref_t sr; +#endif + + if (def->symboldata == qcc_pr_globals + def->ofs) + return; //was already finalised. + + if (def->symbolheader != def) + QCC_FinaliseDef(def->symbolheader); if (def->symbolheader == def && def->deftail) { //for head symbols, we go through and touch all of their children @@ -1068,8 +1085,17 @@ void QCC_FinaliseDef(QCC_def_t *def) def->ofs += def->symbolheader->ofs; else { +/* globalspertype[def->type->type].size += def->symbolsize; + globalspertype[def->type->type].entries += 1; + globalspertype[def->type->type].vars += !def->constant; + globalspertype[def->type->type].consts += def->constant; +*/ + if (def->ofs) + QCC_Error(ERR_INTERNAL, "root symbol %s has an offset", def->name); + if (def->arraylengthprefix) { + //for hexen2 arrays, we need to emit a length first if (numpr_globals+1+def->symbolsize >= MAX_REGS) { if (!opt_overlaptemps || !opt_locals_overlapping) @@ -1077,20 +1103,21 @@ void QCC_FinaliseDef(QCC_def_t *def) else QCC_Error(ERR_TOOMANYGLOBALS, "numpr_globals exceeded MAX_REGS"); } - - //for hexen2 arrays, we need to emit a length first if (def->type->type == ev_vector) ((int *)qcc_pr_globals)[numpr_globals] = def->arraysize-1; else ((int *)qcc_pr_globals)[numpr_globals] = (def->arraysize*def->type->size)-1; //using float arrays for structs. numpr_globals+=1; } - else if (numpr_globals+def->symbolsize >= MAX_REGS) + else { - if (!opt_overlaptemps || !opt_locals_overlapping) - QCC_Error(ERR_TOOMANYGLOBALS, "numpr_globals exceeded MAX_REGS - you'll need to use more optimisations"); - else - QCC_Error(ERR_TOOMANYGLOBALS, "numpr_globals exceeded MAX_REGS"); + if (numpr_globals+def->symbolsize >= MAX_REGS) + { + if (!opt_overlaptemps || !opt_locals_overlapping) + QCC_Error(ERR_TOOMANYGLOBALS, "numpr_globals exceeded MAX_REGS - you'll need to use more optimisations"); + else + QCC_Error(ERR_TOOMANYGLOBALS, "numpr_globals exceeded MAX_REGS"); + } } def->ofs += numpr_globals; numpr_globals += def->symbolsize; @@ -1103,10 +1130,27 @@ void QCC_FinaliseDef(QCC_def_t *def) def->symboldata = qcc_pr_globals + def->ofs; def->symbolsize = numpr_globals - def->ofs; -#ifdef DEBUG_DUMP +#ifdef DEBUG_DUMP_GLOBALMAP if (!def->referenced) printf("Unreferenced "); - printf("Finalise %s(%p) @ %i+%i\n", def->name, def, def->ofs, ssize); + sr.sym = def; + sr.ofs = 0; + sr.cast = def->type; + v = QCC_SRef_EvalConst(sr); + if (v && def->type->type == ev_float) + printf("Finalise %s(%f) @ %i+%i\n", def->name, v->_float, def->ofs, ssize); + else if (v && def->type->type == ev_vector) + printf("Finalise %s(%f %f %f) @ %i+%i\n", def->name, v->vector[0], v->vector[1], v->vector[2], def->ofs, ssize); + else if (v && def->type->type == ev_integer) + printf("Finalise %s(%i) @ %i+%i\n", def->name, v->_int, def->ofs, ssize); + else if (v && def->type->type == ev_function) + printf("Finalise %s(@%i) @ %i+%i\n", def->name, v->_int, def->ofs, ssize); + else if (v && def->type->type == ev_field) + printf("Finalise %s(.%i) @ %i+%i\n", def->name, v->_int, def->ofs, ssize); + else if (v && def->type->type == ev_string) + printf("Finalise %s(%s) @ %i+%i\n", def->name, strings+v->_int, def->ofs, ssize); + else + printf("Finalise %s @ %i+%i\n", def->name, def->ofs, ssize); #endif } @@ -1115,9 +1159,29 @@ void QCC_FinaliseDef(QCC_def_t *def) void QCC_UnmarshalLocals(void) { QCC_def_t *d; - unsigned int onum, biggest, eog = numpr_globals; + unsigned int onum, biggest, eog; size_t i; - //first, finalize private locals. + + //finalise all the globals that we've seen so far + for (d = pr.def_head.next ; d ; d = d->next) + { + if (!d->localscope || d->isstatic) + QCC_FinaliseDef(d); + } + + //first, finalize all static locals that shouldn't form part of the local defs. + for (i=0 ; inextlocal) + if (d->isstatic || (d->constant && d->initialized)) + QCC_FinaliseDef(d); + } + } + + eog = numpr_globals; + //next, finalize non-static locals. for (i=0 ; inextlocal) - QCC_FinaliseDef(d); + if (!d->isstatic && !(d->constant && d->initialized)) + QCC_FinaliseDef(d); if (verbose >= VERBOSE_DEBUG) { if (onum == numpr_globals) @@ -1294,13 +1359,10 @@ pbool QCC_WriteData (int crc) def = QCC_PR_GetDef(NULL, "end_sys_fields", NULL, false, 0, false); if (def) def->referenced = true; + + QCC_PR_FinaliseFunctions(); QCC_DetermineNeededSymbols(def); - for (def = pr.def_head.next ; def ; def = def->next) - { - if (!def->localscope) - QCC_FinaliseDef(def); - } QCC_UnmarshalLocals(); QCC_FinaliseTemps(); @@ -2003,7 +2065,7 @@ strofs = (strofs+3)&~3; { char line[2048]; - QC_snprintfz(line, sizeof(line), "code: %s:%i: @%i %s t%i t%i %i (%s", QCC_FileForStatement(i), statements[i].linenum, i, pr_opcodes[statements[i].op].opname, a, b, c, QCC_VarAtOffset(statements[i].a)); + QC_snprintfz(line, sizeof(line), "code: %s:%i: @%i %s %i %i %i (%s", QCC_FileForStatement(i), statements[i].linenum, i, pr_opcodes[statements[i].op].opname, a, b, c, QCC_VarAtOffset(statements[i].a)); QC_snprintfz(line+strlen(line), sizeof(line)-strlen(line), " %s", QCC_VarAtOffset(statements[i].b)); QC_snprintfz(line+strlen(line), sizeof(line)-strlen(line), " %s)\n", QCC_VarAtOffset(statements[i].c)); printf("%s", line); @@ -2518,7 +2580,6 @@ static void QCC_MergeStatements16(dstatement16_t *in, unsigned int num) out->linenum = 0; numstatements++; } -QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_function_t *scope, int arraysize, QCC_def_t *rootsymbol, unsigned int ofs, int referable, unsigned int flags); static etype_t QCC_MergeFindFieldType(unsigned int ofs, const char *fldname, ddef16_t *fields, size_t numfields) { size_t i; @@ -3120,6 +3181,11 @@ int QCC_PR_FinishCompilation (void) continue; } } + if (d->unused) + { + d->initialized = 1; + continue; + } QCC_PR_Warning(ERR_NOFUNC, d->filen, d->s_line, "function %s has no body",d->name); QCC_PR_ParsePrintDef(ERR_NOFUNC, d); bodylessfuncs = true; @@ -4027,7 +4093,21 @@ void QCC_PR_CommandLinePrecompilerOptions (void) else *compiler_flag[p].enabled = false; } - if (!strcmp(myargv[i]+5, "fteqcc")) + if (!strcmp(myargv[i]+5, "qccx")) + { + flag_qccx = true; //fixme: extra stuff + qccwarningaction[WARN_DENORMAL] = WA_IGNORE; //this is just too spammy + qccwarningaction[WARN_LAXCAST] = WA_IGNORE; //more plausable, but still too spammy. easier to fix at least. + } + else if (!strcmp(myargv[i]+5, "preqcc")) + { + flag_hashonly = true; + } + else if (!strcmp(myargv[i]+5, "reacc")) + { + flag_acc = true; + } + else if (!strcmp(myargv[i]+5, "fteqcc")) ; //as above, its the default. else if (!strcmp(myargv[i]+5, "id")) { @@ -4537,10 +4617,11 @@ pbool QCC_main (int argc, char **argv) //as part of the quake engine MAX_GLOBALS = 1<<17; MAX_FIELDS = 1<<12; MAX_STATEMENTS = 0x80000; - MAX_FUNCTIONS = 16384; + MAX_FUNCTIONS = 32768; maxtypeinfos = 32768; MAX_CONSTANTS = 4096; + strcpy(destfile, ""); compressoutput = 0; p = externs->FileSize("qcc.cfg"); diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 4593467bc..29a16bd7a 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -686,7 +686,7 @@ void PR_Deinit(void) PRSV_ClearThreads(); #ifdef VM_Q1 - Q1QVM_Shutdown(); + Q1QVM_Shutdown(true); #endif if (svprogfuncs) { @@ -4197,7 +4197,12 @@ int PF_precache_sound_Internal (pubprogfuncs_t *prinst, const char *s) { if (!sv.strings.sound_precache[i]) { - sv.strings.sound_precache[i] = PR_AddString(prinst, s, 0, false); +#ifdef VM_Q1 + if (svs.gametype == GT_Q1QVM) + sv.strings.sound_precache[i] = s; + else +#endif + sv.strings.sound_precache[i] = PR_AddString(prinst, s, 0, false); /*touch the file, so any packs will be referenced*/ FS_FLocateFile(s, FSLF_IFFOUND, NULL); diff --git a/engine/server/pr_lua.c b/engine/server/pr_lua.c index 38a6bd784..ea00e1a5c 100644 --- a/engine/server/pr_lua.c +++ b/engine/server/pr_lua.c @@ -109,8 +109,8 @@ static struct qboolean triedlib; dllhandle_t lib; - lua_State * (QDECL *newstate) (lua_Alloc f, void *ud); - lua_CFunction (QDECL *atpanic) (lua_State *L, lua_CFunction panicf); + lua_State * (QDECL *newstate) (lua_Alloc f, void *ud); + lua_CFunction (QDECL *atpanic) (lua_State *L, lua_CFunction panicf); void (QDECL *close) (lua_State *L); int (QDECL *load) (lua_State *L, lua_Reader reader, void *dt, const char *chunkname, const char *mode); int (QDECL *pcallk) (lua_State *L, int nargs, int nresults, int errfunc, int ctx, lua_CFunction k); @@ -150,22 +150,22 @@ static struct int (QDECL *Lcallmeta) (lua_State *L, int obj, const char *e); int (QDECL *Lnewmetatable) (lua_State *L, const char *tname); -} lua; +} lua; #define pcall(L,n,r,f) pcallk(L, (n), (r), (f), 0, NULL) #define call(L,n,r) callk(L, (n), (r), 0, NULL) #define pop(L,n) settop(L, -(n)-1) #define pushstring(L,s) pushfstring(L,"%s",s) -#define LUA_TNONE (-1) -#define LUA_TNIL 0 -#define LUA_TBOOLEAN 1 -#define LUA_TLIGHTUSERDATA 2 -#define LUA_TNUMBER 3 -#define LUA_TSTRING 4 -#define LUA_TTABLE 5 -#define LUA_TFUNCTION 6 -#define LUA_TUSERDATA 7 -#define LUA_TTHREAD 8 +#define LUA_TNONE (-1) +#define LUA_TNIL 0 +#define LUA_TBOOLEAN 1 +#define LUA_TLIGHTUSERDATA 2 +#define LUA_TNUMBER 3 +#define LUA_TSTRING 4 +#define LUA_TTABLE 5 +#define LUA_TFUNCTION 6 +#define LUA_TUSERDATA 7 +#define LUA_TTHREAD 8 #define LUA_NUMTAGS 9 #define LUA_REGISTRYINDEX (-1000000 - 1000) @@ -506,13 +506,13 @@ static int my_lua_entity_get(lua_State *L) //__index lua.pushinteger(L, eval->_int); return 1; case ev_vector: - lua.createtable(L, 0, 0); - //FIXME: should provide a metatable with a __tostring - lua.pushnumber(L, eval->_vector[0]); - lua.setfield (L, -2, "x"); - lua.pushnumber(L, eval->_vector[1]); - lua.setfield (L, -2, "y"); - lua.pushnumber(L, eval->_vector[2]); + lua.createtable(L, 0, 0); + //FIXME: should provide a metatable with a __tostring + lua.pushnumber(L, eval->_vector[0]); + lua.setfield (L, -2, "x"); + lua.pushnumber(L, eval->_vector[1]); + lua.setfield (L, -2, "y"); + lua.pushnumber(L, eval->_vector[2]); lua.setfield (L, -2, "z"); return 1; case ev_function: @@ -1147,13 +1147,13 @@ static int bi_lua_vectoangles(lua_State *L) } VectorAngles(forward, up, ret); - lua.createtable(L, 0, 0); - //FIXME: should provide a metatable with a __tostring - lua.pushnumber(L, ret[0]); - lua.setfield (L, -2, "x"); - lua.pushnumber(L, ret[1]); - lua.setfield (L, -2, "y"); - lua.pushnumber(L, ret[2]); + lua.createtable(L, 0, 0); + //FIXME: should provide a metatable with a __tostring + lua.pushnumber(L, ret[0]); + lua.setfield (L, -2, "x"); + lua.pushnumber(L, ret[1]); + lua.setfield (L, -2, "y"); + lua.pushnumber(L, ret[2]); lua.setfield (L, -2, "z"); return 1; } @@ -1509,35 +1509,35 @@ static int bi_lua_loadlua(lua_State *L) #define registerfunc(n) lua.pushcclosure(L, bi_lua_##n, 0); lua.setglobal(L, #n); static void my_lua_registerbuiltins(lua_State *L) -{ - lua.atpanic (L, my_lua_panic); - - //standard lua library replacement - //this avoids the risk of including any way to access os.execute etc, or other file access. - lua.pushcclosure(L, my_lua_tostring, 0); - lua.setglobal(L, "tostring"); - lua.pushcclosure(L, my_lua_print, 0); - lua.setglobal(L, "print"); - - lua.pushcclosure(L, my_lua_conprint, 0); //for the luls. - lua.setglobal(L, "conprint"); - - registerfunc(loadlua); - - registerfunc(setmodel); - registerfunc(precache_model); - registerfunc(precache_sound); - registerfunc(lightstyle); - registerfunc(spawn); - registerfunc(remove); +{ + lua.atpanic (L, my_lua_panic); + + //standard lua library replacement + //this avoids the risk of including any way to access os.execute etc, or other file access. + lua.pushcclosure(L, my_lua_tostring, 0); + lua.setglobal(L, "tostring"); + lua.pushcclosure(L, my_lua_print, 0); + lua.setglobal(L, "print"); + + lua.pushcclosure(L, my_lua_conprint, 0); //for the luls. + lua.setglobal(L, "conprint"); + + registerfunc(loadlua); + + registerfunc(setmodel); + registerfunc(precache_model); + registerfunc(precache_sound); + registerfunc(lightstyle); + registerfunc(spawn); + registerfunc(remove); registerfunc(nextent); registerfunc(nextclient); //registerfunc(AIM); - registerfunc(makestatic); - registerfunc(setorigin); - registerfunc(setsize); - - registerfunc(dprint); + registerfunc(makestatic); + registerfunc(setorigin); + registerfunc(setsize); + + registerfunc(dprint); registerfunc(bprint); registerfunc(sprint); registerfunc(centerprint); @@ -1602,20 +1602,20 @@ static void my_lua_registerbuiltins(lua_State *L) //registerfunc(PRECACHE_VWEP_MODEL); //registerfunc(SETPAUSE); - - - - lua.createtable(L, 0, 0); - if (lua.Lnewmetatable(L, "globals")) - { - lua.pushcclosure(L, my_lua_global_set, 0); //for the luls. - lua.setfield (L, -2, "__newindex"); - - lua.pushcclosure(L, my_lua_global_get, 0); //for the luls. - lua.setfield (L, -2, "__index"); - } - lua.setmetatable(L, -2); - lua.setglobal(L, "glob"); + + + + lua.createtable(L, 0, 0); + if (lua.Lnewmetatable(L, "globals")) + { + lua.pushcclosure(L, my_lua_global_set, 0); //for the luls. + lua.setfield (L, -2, "__newindex"); + + lua.pushcclosure(L, my_lua_global_get, 0); //for the luls. + lua.setfield (L, -2, "__index"); + } + lua.setmetatable(L, -2); + lua.setglobal(L, "glob"); } @@ -1692,21 +1692,21 @@ static edict_t *Lua_DoRespawn(pubprogfuncs_t *pf, edict_t *e, int num) //create a new table for the entity, give it a suitable metatable, and store it into the registry (avoiding GC and allowing us to actually hold on to it). lua.pushlightuserdata(L, lua.edicttable[num]); - lua.createtable(L, 0, 0); - if (lua.Lnewmetatable(L, "entity")) - { - lua.pushcclosure(L, my_lua_entity_set, 0); //known writes should change the internal info so the engine can use the information. - lua.setfield (L, -2, "__newindex"); - - lua.pushcclosure(L, my_lua_entity_get, 0); //we need to de-translate the engine's fields too. - lua.setfield (L, -2, "__index"); - - lua.pushcclosure(L, my_lua_entity_tostring, 0); //cos its prettier than seeing 'table 0x5425729' all over the place - lua.setfield (L, -2, "__tostring"); - - lua.pushcclosure(L, my_lua_entity_eq, 0); //for comparisons, you know? - lua.setfield (L, -2, "__eq"); - } + lua.createtable(L, 0, 0); + if (lua.Lnewmetatable(L, "entity")) + { + lua.pushcclosure(L, my_lua_entity_set, 0); //known writes should change the internal info so the engine can use the information. + lua.setfield (L, -2, "__newindex"); + + lua.pushcclosure(L, my_lua_entity_get, 0); //we need to de-translate the engine's fields too. + lua.setfield (L, -2, "__index"); + + lua.pushcclosure(L, my_lua_entity_tostring, 0); //cos its prettier than seeing 'table 0x5425729' all over the place + lua.setfield (L, -2, "__tostring"); + + lua.pushcclosure(L, my_lua_entity_eq, 0); //for comparisons, you know? + lua.setfield (L, -2, "__eq"); + } lua.setmetatable(L, -2); lua.pushinteger(L, num); lua.setfield (L, -2, "entnum"); //so we know which entity it is. diff --git a/engine/server/pr_q1qvm.c b/engine/server/pr_q1qvm.c index ac1e7aa81..55c9a94c8 100755 --- a/engine/server/pr_q1qvm.c +++ b/engine/server/pr_q1qvm.c @@ -43,22 +43,20 @@ oh, wait, ktx no longer supports those properly. #include "pr_common.h" -#define GAME_API_VERSION 13 +/*version changes: +13: 2009/june gamecode no longer aware of edict_t data (just 'qc' fields). +14: 2017/march gamedata_t.maxentities added +15: 2017/june for-64bit string indirection changes. added GAME_CLEAR_EDICT. +*/ +#define GAME_API_VERSION 15 +#define GAME_API_VERSION_MIN 8 #define MAX_Q1QVM_EDICTS 768 //according to ktx at api version 12 (fte's protocols go to 2048) #define MAPNAME_LEN 64 void PR_SV_FillWorldGlobals(world_t *w); -#if GAME_API_VERSION >= 13 - #define WASTED_EDICT_T_SIZE (VM_NonNative(q1qvm)?sizeof(int):sizeof(void*)) - //in version 13, the actual edict_t struct is gone, and there's a pointer to it in its place (which we don't need, but it changes size based on vm/native). -#else - //this is probably broken on 64bit native code - #define WASTED_EDICT_T_SIZE 114 - //qclib has split edict_t and entvars_t. - //mvdsv and the api we're implementing has them in one lump - //so we need to bias our entvars_t and fake the offsets a little. -#endif +static int qvm_api_version; +static size_t wasted_edict_t_size; //=============================================================== @@ -209,6 +207,8 @@ typedef enum GAME_EDICT_BLOCKED, //(self,other) GAME_CLIENT_SAY, //(int isteam) GAME_PAUSED_TIC, //(int milliseconds) + + GAME_CLEAR_EDICT, //v15 (sets self.fields to safe values after they're cleared) } q1qvmgameExport_t; @@ -320,6 +320,9 @@ typedef struct unsigned int global; unsigned int fields; int APIversion; +#if GAME_API_VERSION >= 14 + unsigned int maxentities; +#endif } gameData32_t; typedef struct @@ -329,6 +332,9 @@ typedef struct quintptr_t global; quintptr_t fields; int APIversion; +#if GAME_API_VERSION >= 14 + unsigned int maxentities; +#endif } gameDataN_t; typedef enum { @@ -354,10 +360,18 @@ typedef enum { #define emufields \ - emufield(gravity, F_FLOAT) \ - emufield(maxspeed, F_FLOAT) \ - emufield(movement, F_VECTOR) \ - emufield(vw_index, F_FLOAT) + emufield(gravity, F_FLOAT) \ + emufield(maxspeed, F_FLOAT) \ + emufield(movement, F_VECTOR) \ + emufield(vw_index, F_FLOAT) \ + emufield(isBot, F_INT) +// emufield(items2, F_FLOAT) +// emufield(brokenankle, F_FLOAT) +// emufield(mod_admin, F_INT) +// emufield(hideentity, F_INT) +// emufield(trackent, F_INT) +// emufield(hideplayers, F_INT) +// emufield(visclients, F_INT) struct { @@ -371,7 +385,7 @@ static const char *q1qvmentstring; static vm_t *q1qvm; static pubprogfuncs_t q1qvmprogfuncs; - +static q1qvmglobalvars_t *gvars; static void *evars; //pointer to the gamecodes idea of an edict_t static quintptr_t vevars; //offset into the vm base of evars @@ -399,7 +413,7 @@ static edict_t *QDECL Q1QVMPF_EdictNum(pubprogfuncs_t *pf, unsigned int num) if (!e) { e = q1qvmprogfuncs.edicttable[num] = Z_TagMalloc(sizeof(edict_t)+sizeof(extentvars_t), VMFSID_Q1QVM); - e->v = (stdentvars_t*)((char*)evars + (num * sv.world.edict_size) + WASTED_EDICT_T_SIZE); + e->v = (stdentvars_t*)((char*)evars + (num * sv.world.edict_size) + wasted_edict_t_size); e->xv = (extentvars_t*)(e+1); e->entnum = num; } @@ -428,7 +442,14 @@ static void Q1QVMED_ClearEdict (edict_t *e, qboolean wipe) { int num = e->entnum; if (wipe) - memset (e->v, 0, sv.world.edict_size - WASTED_EDICT_T_SIZE); + memset (e->v, 0, sv.world.edict_size - wasted_edict_t_size); + if (qvm_api_version >= 15) + { + int oself = pr_global_struct->self; + pr_global_struct->self = Q1QVMPF_EdictToProgs(svprogfuncs, e); + VM_Call(q1qvm, GAME_CLEAR_EDICT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + pr_global_struct->self = oself; + } e->ereftype = ER_ENTITY; e->entnum = num; } @@ -520,7 +541,7 @@ static eval_t *QDECL Q1QVMPF_GetEdictFieldValue(pubprogfuncs_t *pf, edict_t *e, { if (cache && !cache->varname) { - return (eval_t*)((char*)e->v + cache->spare[0]-WASTED_EDICT_T_SIZE); + return (eval_t*)((char*)e->v + cache->spare[0]-wasted_edict_t_size); } if (!strcmp(fieldname, "message")) { @@ -560,13 +581,149 @@ static void *ASMCALL QDECL Q1QVMPF_PointerToNative(pubprogfuncs_t *prinst, quint static const char *ASMCALL QDECL Q1QVMPF_StringToNative(pubprogfuncs_t *prinst, string_t str) { - char *ret; + quintptr_t ref; if (str == ~0) return " "; //models are weird. yes, this is a hack. - if (!str || (quintptr_t)str >= VM_MemoryMask(q1qvm)) + if (qvm_api_version >= 15) + { + qboolean stringishacky = sizeof(quintptr_t) != sizeof(string_t) && qvm_api_version >= 15 && !VM_NonNative(q1qvm); //silly bullshit. Really, native gamecode should have its own implementation of this builtin or something, especially as its pretty much only ever used for classnames. + if (!str) + return ""; //invalid... + else if (stringishacky) + { + if (str >= 0 && str < sv.world.edict_size*sv.world.max_edicts - sizeof(quintptr_t)) + ref = *(quintptr_t*)((char*)evars + (int)str); //extra indirection added in api 15. + else + return ""; //error + } + else + { + if (str >= 0 && str < sv.world.edict_size*sv.world.max_edicts - sizeof(quintptr_t)) + ref = *(string_t*)((char*)evars + (int)str); //extra indirection added in api 15. + else + return ""; //error + } + } + else + ref = str; + if (!ref || (quintptr_t)ref >= VM_MemoryMask(q1qvm)) return ""; //null or invalid pointers. - ret = (char*)VM_MemoryBase(q1qvm) + str; - return ret; + return (char*)VM_MemoryBase(q1qvm) + ref; +} +static void QDECL Q1QVMPF_SetStringField(pubprogfuncs_t *progfuncs, struct edict_s *ed, string_t *fld, const char *str, pbool str_is_static) +{ + if (!str_is_static) + return; + if (qvm_api_version >= 15) + { + qboolean stringishacky = sizeof(quintptr_t) != sizeof(string_t) && qvm_api_version >= 15 && !VM_NonNative(q1qvm); //silly bullshit. Really, native gamecode should have its own implementation of this builtin or something, especially as its pretty much only ever used for classnames. + quintptr_t nval = (str - (char*)VM_MemoryBase(q1qvm)); + if (nval >= VM_MemoryMask(q1qvm)) + return; + + if (!*fld) + Con_DPrintf("Ignoring string set. mod pointer not set.\n"); + else if (stringishacky) + { + if (*fld >= 0 && *fld < sv.world.edict_size*sv.world.max_edicts - sizeof(quintptr_t)) + *(quintptr_t*)((char*)evars + *fld) = nval; + else + Con_DPrintf("Ignoring string set outside of progs VM\n"); + } + else + { + if (nval >= 0xffffffff) + return; //invalid string! blame 64bit. + if (*fld >= 0 && *fld < sv.world.edict_size*sv.world.max_edicts - sizeof(string_t)) + *(string_t*)((char*)evars + *fld) = (string_t)nval; + else + Con_DPrintf("Ignoring string set outside of progs VM\n"); + } + } + else + { + string_t newval = progfuncs->StringToProgs(progfuncs, str); + if (newval || !str) + *fld = newval; + else if (!str) + *fld = 0; + else + { + *fld = ~0; + //Con_DPrintf("Ignoring string set outside of progs VM\n"); + } + } +} + +static void Q1QVMPF_SetStringGlobal(pubprogfuncs_t *progfuncs, string_t *fld, const char *str, size_t copysize) +{ + if (qvm_api_version >= 15) + { + qboolean stringishacky = sizeof(quintptr_t) != sizeof(string_t) && qvm_api_version >= 15 && !VM_NonNative(q1qvm); //silly bullshit. Really, native gamecode should have its own implementation of this builtin or something, especially as its pretty much only ever used for classnames. + if (!*fld) + Con_Printf("Q1QVM: string reference not set. Fix the mod.\n"); + else if (stringishacky) + { //quintptr_t +// if (*fld >= 0 && *fld < sv.world.edict_size*sv.world.max_edicts - sizeof(intptr_t)) + { + if (!*(quintptr_t*)((char*)gvars + *fld)) + { + Con_DPrintf("String buffer not set. Hacking the data in instead.\n"); + *(quintptr_t*)((char*)gvars + *fld) = (str - (char*)VM_MemoryBase(q1qvm)); + } + else + { + char *ptr = (char*)*(quintptr_t*)((char*)gvars + *fld); + Q_strncpyz(ptr, str, copysize); + } + } + } + else + { //string_t +// if (*fld >= 0 && *fld < sv.world.edict_size*sv.world.max_edicts - sizeof(string_t)) + { + if (!*(quintptr_t*)((char*)gvars + *fld)) + { + quintptr_t nval = (str - (char*)VM_MemoryBase(q1qvm));; + if (nval > VM_MemoryMask(q1qvm)) + { + Con_Printf("Q1QVM: String buffer not set. Data out of QVM memory space. Fix the mod.\n"); + } + else + { + Con_DPrintf("String buffer not set. Hacking the data in instead.\n"); + *(quintptr_t*)((char*)gvars + *fld) = (str - (char*)VM_MemoryBase(q1qvm));; + } + } + else + { + char *ptr = (char*)*(quintptr_t*)((char*)gvars + *fld); + Q_strncpyz(ptr, str, copysize); + } + } + } + } + else + { + if (!*fld) + { + quintptr_t nval = (str - (char*)VM_MemoryBase(q1qvm)); + if (nval > VM_MemoryMask(q1qvm)) + { + Con_Printf("Q1QVM: String buffer not set. Data out of QVM memory space. Fix the mod.\n"); + } + else + { + Con_DPrintf("String buffer not set. Hacking the data in instead.\n"); + *fld = (str - (char*)VM_MemoryBase(q1qvm)); + } + } + else + { + char *ptr = (char*)VM_MemoryBase(q1qvm) + *fld; + Q_strncpyz(ptr, str, copysize); + } + } } static int WrapQCBuiltin(builtin_t func, void *offset, quintptr_t mask, const qintptr_t *arg, char *argtypes) @@ -607,7 +764,7 @@ static int WrapQCBuiltin(builtin_t func, void *offset, quintptr_t mask, const qi #define VALIDATEPOINTER(o,l) if ((qintptr_t)o + l >= mask || VM_POINTER(o) < offset) SV_Error("Call to game trap passes invalid pointer\n"); //out of bounds. static qintptr_t QVM_GetAPIVersion (void *offset, quintptr_t mask, const qintptr_t *arg) { - return GAME_API_VERSION; + return qvm_api_version; } static qintptr_t QVM_DPrint (void *offset, quintptr_t mask, const qintptr_t *arg) @@ -1245,26 +1402,30 @@ static qintptr_t QVM_strnicmp (void *offset, quintptr_t mask, const qintptr_t *a static qintptr_t QVM_Find (void *offset, quintptr_t mask, const qintptr_t *arg) { edict_t *e = VM_POINTER(arg[0]); - int ofs = VM_LONG(arg[1]) - WASTED_EDICT_T_SIZE; + int ofs = (VM_LONG(arg[1]) - wasted_edict_t_size); char *match = VM_POINTER(arg[2]); char *field; int first = e?((char*)e - (char*)evars)/sv.world.edict_size:0; int i; + qboolean stringishacky = sizeof(quintptr_t) != sizeof(string_t) && qvm_api_version >= 15 && !VM_NonNative(q1qvm); //silly bullshit. Really, native gamecode should have its own implementation of this builtin or something, especially as its pretty much only ever used for classnames. if (!match) match = ""; for (i = first+1; i < sv.world.num_edicts; i++) { e = q1qvmprogfuncs.edicttable[i]; - field = VM_POINTER(*((string_t*)e->v + ofs/4)); + if (stringishacky) + field = VM_POINTER(*(quintptr_t*)((char*)e->v + ofs)); + else + field = VM_POINTER(*(string_t*)((char*)e->v + ofs)); if (field == NULL) { if (*match == '\0') - return ((char*)e->v - (char*)offset)-WASTED_EDICT_T_SIZE; + return ((char*)e->v - (char*)offset)-wasted_edict_t_size; } else { if (!strcmp(field, match)) - return ((char*)e->v - (char*)offset)-WASTED_EDICT_T_SIZE; + return ((char*)e->v - (char*)offset)-wasted_edict_t_size; } } return 0; @@ -1346,26 +1507,93 @@ static qintptr_t QVM_RedirectCmd (void *offset, quintptr_t mask, const qintptr_t } static qintptr_t QVM_Add_Bot (void *offset, quintptr_t mask, const qintptr_t *arg) { + char *name = VM_POINTER(arg[0]); + int bottom = VM_LONG(arg[1]); + int top = VM_LONG(arg[2]); + char *skin = VM_POINTER(arg[3]); + +#if 1 + extern int nextuserid; + int i; + for (i = 0; i < sv.allocated_client_slots; i++) + { + client_t *cl = &svs.clients[i]; + if (!*cl->name && !cl->protocol && cl->state == cs_free) + { + cl->userid = ++nextuserid; + cl->protocol = SCP_BAD; //marker for bots + cl->state = cs_spawned; + cl->spawned = true; + sv.spawned_client_slots++; + cl->netchan.message.allowoverflow = true; + cl->netchan.message.maxsize = 0; + cl->datagram.allowoverflow = true; + cl->datagram.maxsize = 0; + + cl->edict = EDICT_NUM(sv.world.progs, i+1); + + Info_SetValueForKey(cl->userinfo, "name", name, sizeof(cl->userinfo)); + Info_SetValueForKey(cl->userinfo, "topcolor", va("%i", top), sizeof(cl->userinfo)); + Info_SetValueForKey(cl->userinfo, "bottomcolor", va("%i", bottom), sizeof(cl->userinfo)); + Info_SetValueForKey(cl->userinfo, "skin", skin, sizeof(cl->userinfo)); + Info_SetValueForStarKey(cl->userinfo, "*bot", "1", sizeof(cl->userinfo)); + SV_ExtractFromUserinfo(cl, true); + SV_SetUpClientEdict (cl, cl->edict); + + SV_FullClientUpdate(cl, NULL); + Q1QVM_ClientConnect(cl); + + return cl->edict->entnum; + } + } +#else //FIXME: not implemented, always returns failure. //the other bot functions only ever work on bots anyway, so don't need to be implemented until this one is //return WrapQCBuiltin(PF_spawnclient, offset, mask, arg, ""); Con_DPrintf("QVM_Add_Bot: not implemented\n"); +#endif return 0; } static qintptr_t QVM_Remove_Bot (void *offset, quintptr_t mask, const qintptr_t *arg) { - //fixme: should become general kick + //kicks NOW. which generally makes it unsafe for players (calling from StartFrame should be okay). + int entnum = VM_LONG(arg[0]); + if (entnum >= 1 && entnum <= svs.allocated_client_slots) + { + client_t *cl = &svs.clients[entnum-1]; + SV_DropClient(cl); + } - Con_DPrintf("QVM_Remove_Bot: not implemented\n"); - - //WrapQCBuiltin(PF_dropclient, offset, mask, arg, "n"); return 0; } static qintptr_t QVM_SetBotCMD (void *offset, quintptr_t mask, const qintptr_t *arg) { - //in mvdsv, this is run *after* the frame. - Con_DPrintf("QVM_SetBotCMD: not implemented\n"); + //this just queues the command for later, even in mvdsv. ignore the msecs value because its basically pointless + int edn = VM_LONG(arg[0]); + int msec = VM_LONG(arg[1]); + float angles_x = VM_FLOAT(arg[2]); + float angles_y = VM_FLOAT(arg[3]); + float angles_z = VM_FLOAT(arg[4]); + int forwardmove = VM_LONG(arg[5]); + int sidemove = VM_LONG(arg[6]); + int upmove = VM_LONG(arg[7]); + int buttons = VM_LONG(arg[8]); + int impulse = VM_LONG(arg[9]); + + if (edn >= 1 && edn <= svs.allocated_client_slots) + { + client_t *cl = &svs.clients[edn-1]; + cl->lastcmd.msec = msec; + cl->lastcmd.angles[0] = ANGLE2SHORT(angles_x); + cl->lastcmd.angles[1] = ANGLE2SHORT(angles_y); + cl->lastcmd.angles[2] = ANGLE2SHORT(angles_z); + cl->lastcmd.forwardmove = forwardmove; + cl->lastcmd.sidemove = sidemove; + cl->lastcmd.upmove = upmove; + cl->lastcmd.buttons = buttons; + cl->lastcmd.impulse = impulse; + } return 0; } static qintptr_t QVM_SetUserInfo (void *offset, quintptr_t mask, const qintptr_t *arg) @@ -1551,19 +1779,19 @@ static qintptr_t QVM_uri_query (void *offset, quintptr_t mask, const qintptr_t * if (!pr_enable_uriget.ival) { - Con_Printf("QVM_uri_query(\"%s\",%"PRIxPTR"): %s disabled\n", url, (qintptr_t)cb_context, pr_enable_uriget.name); + Con_Printf("QVM_uri_query(\"%s\"): %s disabled\n", url, pr_enable_uriget.name); return 0; } if (mimetype && *mimetype) { VALIDATEPOINTER(arg[4],datasize); - Con_DPrintf("QVM_uri_query(%s,%"PRIxPTR")\n", url, (qintptr_t)cb_context); + Con_DPrintf("QVM_uri_query(%s)\n", url); dl = HTTP_CL_Put(url, mimetype, data, datasize, QVM_uri_query_callback); } else { - Con_DPrintf("QVM_uri_query(%s,%"PRIxPTR")\n", url, (qintptr_t)cb_context); + Con_DPrintf("QVM_uri_query(%s)\n", url); dl = HTTP_CL_Get(url, NULL, QVM_uri_query_callback); } if (dl) @@ -1740,8 +1968,8 @@ struct //sql? //model querying? + //skeletal objects/tags? //heightmap / brush editing? - //custom stats (mod can always writebyte, I guess, sounds horrible though) //csqc ents {NULL, NULL} }; @@ -1823,7 +2051,7 @@ static qintptr_t EXPORT_FN syscallnative (qintptr_t arg, ...) return traps[arg](NULL, ~(quintptr_t)0, args); } -void Q1QVM_Shutdown(void) +void Q1QVM_Shutdown(qboolean notifygame) { int i; if (q1qvm) @@ -1834,7 +2062,8 @@ void Q1QVM_Shutdown(void) Q_strncpyz(svs.clients[i].namebuf, svs.clients[i].name, sizeof(svs.clients[i].namebuf)); svs.clients[i].name = svs.clients[i].namebuf; } - VM_Call(q1qvm, GAME_SHUTDOWN, 0, 0, 0); + if (notifygame) + VM_Call(q1qvm, GAME_SHUTDOWN, 0, 0, 0); VM_Destroy(q1qvm); q1qvm = NULL; VM_fcloseall(VMFSID_Q1QVM); @@ -1850,6 +2079,25 @@ void Q1QVM_Shutdown(void) } } +static void QDECL Q1QVM_Get_FrameState(world_t *w, wedict_t *ent, framestate_t *fstate) +{ + memset(fstate, 0, sizeof(*fstate)); + fstate->g[FS_REG].frame[0] = ent->v->frame; + fstate->g[FS_REG].frametime[0] = ent->xv->frame1time; + fstate->g[FS_REG].lerpweight[0] = 1; + fstate->g[FS_REG].endbone = 0x7fffffff; + + fstate->g[FST_BASE].frame[0] = ent->xv->baseframe; + fstate->g[FST_BASE].frametime[0] = ent->xv->/*base*/frame1time; + fstate->g[FST_BASE].lerpweight[0] = 1; + fstate->g[FST_BASE].endbone = ent->xv->basebone; + +#if defined(SKELETALOBJECTS) || defined(RAGDOLL) + if (ent->xv->skeletonindex) + skel_lookup(w, ent->xv->skeletonindex, fstate); +#endif +} + static void QDECL Q1QVM_Event_Touch(world_t *w, wedict_t *s, wedict_t *o) { int oself = pr_global_struct->self; @@ -1876,20 +2124,6 @@ static qboolean QDECL Q1QVM_Event_ContentsTransition(world_t *w, wedict_t *ent, return false; //always do legacy behaviour } -static void QDECL Q1QVMPF_SetStringField(pubprogfuncs_t *progfuncs, struct edict_s *ed, string_t *fld, const char *str, pbool str_is_static) -{ - string_t newval = progfuncs->StringToProgs(progfuncs, str); - if (newval || !str) - *fld = newval; - else if (!str) - *fld = 0; - else - { - *fld = ~0; -// Con_DPrintf("Ignoring string set outside of progs VM\n"); - } -} - qboolean PR_LoadQ1QVM(void) { static int writable_int; @@ -1902,12 +2136,11 @@ qboolean PR_LoadQ1QVM(void) gameDataPrivate_t gd; gameDataN_t *gdn; gameData32_t *gd32; - q1qvmglobalvars_t *global; qintptr_t ret; qintptr_t limit; extern cvar_t pr_maxedicts; - Q1QVM_Shutdown(); + Q1QVM_Shutdown(true); q1qvm = VM_Create("qwprogs", com_nogamedirnativecode.ival?NULL:syscallnative, syscallqvm); if (!q1qvm) @@ -1955,6 +2188,7 @@ qboolean PR_LoadQ1QVM(void) sv.world.Event_Sound = SVQ1_StartSound; sv.world.Event_ContentsTransition = Q1QVM_Event_ContentsTransition; sv.world.Get_CModel = SVPR_GetCModel; + sv.world.Get_FrameState = Q1QVM_Get_FrameState; sv.world.num_edicts = 0; //we're not ready for most of the builtins yet sv.world.max_edicts = 0; //so clear these out, just in case @@ -1964,10 +2198,12 @@ qboolean PR_LoadQ1QVM(void) q1qvmprogfuncs.stringtable = VM_MemoryBase(q1qvm); + qvm_api_version = GAME_API_VERSION; + ret = VM_Call(q1qvm, GAME_INIT, (qintptr_t)(sv.time*1000), rand(), 0, 0, 0); if (!ret) { - Q1QVM_Shutdown(); + Q1QVM_Shutdown(false); return false; } @@ -1982,7 +2218,10 @@ qboolean PR_LoadQ1QVM(void) gd.global = gd32->global; gd.fields = gd32->fields; - gd.maxedicts = pr_maxedicts.ival; //FIXME + if (qvm_api_version >= 14) + gd.maxedicts = gd32->maxentities; + else + gd.maxedicts = MAX_Q1QVM_EDICTS; } else { @@ -1994,7 +2233,30 @@ qboolean PR_LoadQ1QVM(void) gd.global = gdn->global; gd.fields = gdn->fields; - gd.maxedicts = pr_maxedicts.ival; //FIXME + if (qvm_api_version >= 14) + gd.maxedicts = gdn->maxentities; + else + gd.maxedicts = MAX_Q1QVM_EDICTS; + } + gd.maxedicts = bound(1, pr_maxedicts.ival, gd.maxedicts); + + qvm_api_version = gd.APIversion; + if (!(GAME_API_VERSION_MIN <= qvm_api_version && qvm_api_version <= GAME_API_VERSION)) + { + Con_Printf("QVM-API version %i not supported\n", qvm_api_version); + Q1QVM_Shutdown(false); + return false; + } + + //in version 13, the actual edict_t struct is gone, and there's a pointer to it in its place (which is unusable, and changes depending on modes). + if (qvm_api_version) + wasted_edict_t_size = (VM_NonNative(q1qvm)?sizeof(int):sizeof(void*)); + else + { + //fte/qclib has split edict_t and entvars_t. + //older versions of the qvm api has them in one lump + //so we need to bias the mod's entvars_t offsets a little. + wasted_edict_t_size = 114; } sv.world.num_edicts = 1; @@ -2014,21 +2276,21 @@ qboolean PR_LoadQ1QVM(void) sv.world.edict_size = gd.sizeofent; vevars = gd.ents; evars = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, vevars); - global = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, gd.global); + gvars = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, gd.global); - if (!evars || !global) + if (!evars || !gvars) { - Q1QVM_Shutdown(); + Q1QVM_Shutdown(false); return false; } //WARNING: global is not remapped yet... //This code is written evilly, but works well enough -#define globalint(required, name) pr_global_ptrs->name = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, (qintptr_t)&global->name) //the logic of this is somewhat crazy -#define globalfloat(required, name) pr_global_ptrs->name = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, (qintptr_t)&global->name) -#define globalstring(required, name) pr_global_ptrs->name = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, (qintptr_t)&global->name) -#define globalvec(required, name) pr_global_ptrs->name = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, (qintptr_t)&global->name) -#define globalfunc(required, name) pr_global_ptrs->name = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, (qintptr_t)&global->name) +#define globalint(required, name) pr_global_ptrs->name = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, (qintptr_t)&gvars->name) //the logic of this is somewhat crazy +#define globalfloat(required, name) pr_global_ptrs->name = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, (qintptr_t)&gvars->name) +#define globalstring(required, name) pr_global_ptrs->name = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, (qintptr_t)&gvars->name) +#define globalvec(required, name) pr_global_ptrs->name = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, (qintptr_t)&gvars->name) +#define globalfunc(required, name) pr_global_ptrs->name = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, (qintptr_t)&gvars->name) #define globalnull(required, name) pr_global_ptrs->name = NULL globalint (true, self); //we need the qw ones, but any in standard quake and not quakeworld, we don't really care about. globalint (true, other); @@ -2084,6 +2346,9 @@ qboolean PR_LoadQ1QVM(void) pr_global_ptrs->physics_mode = &physics_mode; pr_global_ptrs->trace_brush_id = &writable_int; pr_global_ptrs->trace_brush_faceid = &writable_int; + pr_global_ptrs->trace_surface_id = &writable_int; + pr_global_ptrs->trace_bone_id = &writable_int; + pr_global_ptrs->trace_triangle_id = &writable_int; pr_global_ptrs->global_gravitydir = &defaultgravity; // ensureglobal(input_timelength, input_timelength_default); @@ -2094,12 +2359,12 @@ qboolean PR_LoadQ1QVM(void) dimensionsend = dimensiondefault = 255; for (i = 0; i < 16; i++) - pr_global_ptrs->spawnparamglobals[i] = (float*)((char*)VM_MemoryBase(q1qvm)+(qintptr_t)(&global->parm1 + i)); + pr_global_ptrs->spawnparamglobals[i] = (float*)((char*)VM_MemoryBase(q1qvm)+(qintptr_t)(&gvars->parm1 + i)); for (; i < NUM_SPAWN_PARMS; i++) pr_global_ptrs->spawnparamglobals[i] = NULL; pr_global_ptrs->parm_string = NULL; -#define emufield(n,t) if (field[i].type == t && !strcmp(#n, fname)) {fofs.n = (field[i].ofs - WASTED_EDICT_T_SIZE)/sizeof(float); continue;} +#define emufield(n,t) if (field[i].type == t && !strcmp(#n, fname)) {fofs.n = (field[i].ofs - wasted_edict_t_size)/sizeof(float); continue;} if (VM_NonNative(q1qvm)) { field32_t *field = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, gd.fields); @@ -2127,10 +2392,26 @@ qboolean PR_LoadQ1QVM(void) sv.world.edicts = (wedict_t*)Q1QVMPF_EdictNum(svprogfuncs, 0); sv.world.usesolidcorpse = true; - if ((quintptr_t)global->mapname && (quintptr_t)global->mapname+MAPNAME_LEN < VM_MemoryMask(q1qvm)) - Q_strncpyz((char*)VM_MemoryBase(q1qvm) + global->mapname, svs.name, MAPNAME_LEN); + if (qvm_api_version >= 15) + { + int e; + for (e = 0; e <= 32; e++) + { + pr_global_struct->self = Q1QVMPF_EdictToProgs(svprogfuncs, Q1QVMPF_EdictNum(svprogfuncs, e)); + VM_Call(q1qvm, GAME_CLEAR_EDICT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + } + pr_global_struct->self = 0; + + + Q1QVMPF_SetStringGlobal(sv.world.progs, &gvars->mapname, svs.name, MAPNAME_LEN); + } else - global->mapname = Q1QVMPF_StringToProgs(sv.world.progs, svs.name); + { + if ((quintptr_t)gvars->mapname && (quintptr_t)gvars->mapname+MAPNAME_LEN < VM_MemoryMask(q1qvm)) + Q_strncpyz((char*)VM_MemoryBase(q1qvm) + gvars->mapname, svs.name, MAPNAME_LEN); + else + gvars->mapname = Q1QVMPF_StringToProgs(sv.world.progs, svs.name); + } PR_SV_FillWorldGlobals(&sv.world); return true; @@ -2141,12 +2422,25 @@ qboolean PR_LoadQ1QVM(void) void Q1QVM_ClientConnect(client_t *cl) { - if (cl->edict->v->netname) + if (qvm_api_version >= 15 && !VM_NonNative(q1qvm)) { - strcpy(cl->namebuf, cl->name); - cl->name = (char*)Q1QVMPF_StringToNative(svprogfuncs, cl->edict->v->netname); - //FIXME: check this pointer - strcpy(cl->name, cl->namebuf); + Q_strncpyz(cl->namebuf, cl->name, sizeof(cl->namebuf)); + Q1QVMPF_SetStringField(sv.world.progs, cl->edict, &cl->edict->v->netname, cl->namebuf, true); + } + else if (cl->edict->v->netname) + { + char *name; + char *base = VM_MemoryBase(q1qvm); + quintptr_t mask = VM_MemoryMask(q1qvm); + name = (char*)Q1QVMPF_StringToNative(svprogfuncs, cl->edict->v->netname); + if (cl->name > base && cl->name < base+mask) + { + Q_strncpyz(cl->namebuf, name, sizeof(cl->namebuf)); + strcpy(name, cl->namebuf); + cl->name = name; //so the gamecode can do changes if it wants. + } + else + Con_Printf("WARNING: Mod provided no netname buffer. Player names will not be set properly.\n"); } else if (!VM_NonNative(q1qvm)) { @@ -2160,9 +2454,11 @@ void Q1QVM_ClientConnect(client_t *cl) Con_Printf("WARNING: Mod provided no netname buffer. Player names will not be set properly.\n"); if (fofs.gravity) - ((float*)sv_player->v)[fofs.gravity] = sv_player->xv->gravity; + ((float*)cl->edict->v)[fofs.gravity] = cl->edict->xv->gravity; if (fofs.maxspeed) - ((float*)sv_player->v)[fofs.maxspeed] = sv_player->xv->maxspeed; + ((float*)cl->edict->v)[fofs.maxspeed] = cl->edict->xv->maxspeed; + if (fofs.isBot) + ((float*)cl->edict->v)[fofs.isBot] = !cl->protocol; // call the spawn function pr_global_struct->time = sv.world.physicstime; @@ -2251,9 +2547,11 @@ void Q1QVM_PostThink(void) sv_player->xv->vw_index = ((float*)sv_player->v)[fofs.vw_index]; } -void Q1QVM_StartFrame(void) +void Q1QVM_StartFrame(qboolean botsarespecialsnowflakes) { - VM_Call(q1qvm, GAME_START_FRAME, (qintptr_t)(sv.time*1000), 0, 0, 0); + if (botsarespecialsnowflakes && qvm_api_version < 15) + return; //this stupidity brought to you with api 15! + VM_Call(q1qvm, GAME_START_FRAME, (qintptr_t)(sv.time*1000), botsarespecialsnowflakes, 0, 0); } void Q1QVM_Blocked(void) diff --git a/engine/server/progs.h b/engine/server/progs.h index 1e3b6e17c..f67adcce2 100644 --- a/engine/server/progs.h +++ b/engine/server/progs.h @@ -132,7 +132,7 @@ qboolean PR_LoadLua(void); #ifdef VM_Q1 #define VMFSID_Q1QVM 57235 //the q1qvm zone tag that is freed when the module is purged. struct client_s; -void Q1QVM_Shutdown(void); +void Q1QVM_Shutdown(qboolean notifygame); qboolean PR_LoadQ1QVM(void); void Q1QVM_ClientConnect(struct client_s *cl); qboolean Q1QVM_GameConsoleCommand(void); @@ -141,7 +141,7 @@ qboolean Q1QVM_UserInfoChanged(edict_t *player); void Q1QVM_PlayerPreThink(void); void Q1QVM_RunPlayerThink(void); void Q1QVM_PostThink(void); -void Q1QVM_StartFrame(void); +void Q1QVM_StartFrame(qboolean botsarespecialsnowflakes); void Q1QVM_Blocked(void); void Q1QVM_SetNewParms(void); void Q1QVM_SetChangeParms(void); diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index f0f32e74b..146f7d3c1 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -622,7 +622,7 @@ void SV_UnspawnServer (void) //terminate the running server. SVHL_ShutdownGame(); #endif #ifdef VM_Q1 - Q1QVM_Shutdown(); + Q1QVM_Shutdown(true); #endif sv.world.worldmodel = NULL; sv.state = ss_dead; @@ -1147,7 +1147,7 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, #endif #ifdef VM_Q1 if (newgametype != GT_Q1QVM) - Q1QVM_Shutdown(); + Q1QVM_Shutdown(true); #endif SV_UpdateMaxPlayers(0); diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 66a658585..6523c4923 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -4647,7 +4647,7 @@ void SV_Impulse_f (void) SV_SetUpClientEdict(&svs.clients[i], svs.clients[i].edict); - svs.clients[i].edict->v->netname = PR_SetString(svprogfuncs, "Console"); + svprogfuncs->SetStringField(svprogfuncs, svs.clients[i].edict, &svs.clients[i].edict->v->netname, "Console", true); sv.skipbprintclient = &svs.clients[i]; pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, svs.clients[i].edict); @@ -5439,7 +5439,7 @@ void SV_ExtractFromUserinfo (client_t *cl, qboolean verbose) } } - if (!cl->drop && strncmp(val, cl->name, sizeof(cl->namebuf)-1)) + if (!cl->drop && strncmp(val, cl->name, sizeof(cl->namebuf)-1) && cl->state > cs_zombie) { if (*cl->name && cl->state >= cs_spawned && !cl->spectator && verbose) { diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index 73a1febc7..96fb2e46a 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -156,6 +156,19 @@ Handles cursor positioning, line wrapping, etc #define MAXPRINTMSG 4096 // FIXME: make a buffer size safe vsprintf? #ifdef SERVERONLY +vfsfile_t *con_pipe; +vfsfile_t *Con_POpen(char *conname) +{ + if (!conname || !*conname) + { + if (con_pipe) + VFS_CLOSE(con_pipe); + con_pipe = VFSPIPE_Open(2, false); + return con_pipe; + } + return NULL; +} + static void Con_PrintFromThread (void *ctx, void *data, size_t a, size_t b) { Con_Printf("%s", (char*)data); @@ -188,6 +201,9 @@ void VARGS Con_Printf (const char *fmt, ...) Sys_Printf ("%s", msg); // also echo to debugging console Con_Log(msg); // log to console + + if (con_pipe) + VFS_PUTS(con_pipe, msg); } void Con_TPrintf (translation_t stringnum, ...) { @@ -227,6 +243,9 @@ void Con_TPrintf (translation_t stringnum, ...) Sys_Printf ("%s", msg); // also echo to debugging console Con_Log(msg); // log to console + + if (con_pipe) + VFS_PUTS(con_pipe, msg); } /* ================ @@ -2706,9 +2725,10 @@ qboolean SV_SendClientDatagram (client_t *client) #endif } + msg.maxsize = clientlimit; // copy the accumulated multicast datagram // for this client out to the message - if (!client->datagram.overflowed && msg.cursize + client->datagram.cursize <= clientlimit) + if (!client->datagram.overflowed && !msg.overflowed && msg.cursize + client->datagram.cursize <= clientlimit) { SZ_Write (&msg, client->datagram.data, client->datagram.cursize); SZ_Clear (&client->datagram); diff --git a/engine/server/sv_sys_win.c b/engine/server/sv_sys_win.c index 3e4e7e642..8f3e6ba4e 100644 --- a/engine/server/sv_sys_win.c +++ b/engine/server/sv_sys_win.c @@ -1110,34 +1110,64 @@ void ApplyColour(unsigned int chr) //this could be much more efficient. static void Sys_PrintColouredChars(conchar_t *start, conchar_t *end) { - conchar_t m; wchar_t wc[256]; int l; DWORD dummy; + unsigned int cp, flags, m; + + m = CON_WHITEMASK; + l = 0; while(start < end) { - l = 0; - m = *start & CON_FLAGSMASK; - for (;;) + start = Font_Decode(start, &flags, &cp); + + if (l+2 >= countof(wc) || flags != m) { - if (start == end || m != (*start & CON_FLAGSMASK) || l >= countof(wc)) + ApplyColour(m); + if (WinNT) + WriteConsoleW(hconsoleout, wc, l, &dummy, NULL); + else { - ApplyColour(m); - if (WinNT) - WriteConsoleW(hconsoleout, wc, l, &dummy, NULL); - else - { - //win95 doesn't support wide chars *sigh*. blank consoles suck. - char ac[256]; - l = WideCharToMultiByte(CP_ACP, 0, wc, l, ac, sizeof(ac), NULL, NULL); - WriteConsole(hconsoleout, ac, l, &dummy, NULL); - } - break; + //win95 doesn't support wide chars *sigh*. blank consoles suck. + char ac[256]; + l = WideCharToMultiByte(CP_ACP, 0, wc, l, ac, sizeof(ac), NULL, NULL); + WriteConsole(hconsoleout, ac, l, &dummy, NULL); } - if (!(*start & CON_HIDDEN)) - wc[l++] = *start & CON_CHARMASK; - start++; + l = 0; + } + + if (!(flags & CON_HIDDEN)) + { + if (cp >= 0xe000 && cp < 0xe100) + { + cp -= 0xe000; + if (cp >= 0x80) + { + char c1[32] = "---..........> " "[]0123456789.---"; + cp -= 0x80; + if (cp <= countof(c1)) + cp = c1[cp]; + } + } + if (cp > 0xffff) + cp = '?'; //too lazy for utf-16 crap when its mostly smilies anyway. + wc[l++] = cp; + } + } + + //and flush it. + if (l) + { + ApplyColour(m); + if (WinNT) + WriteConsoleW(hconsoleout, wc, l, &dummy, NULL); + else + { + //win95 doesn't support wide chars *sigh*. blank consoles suck. + char ac[256]; + l = WideCharToMultiByte(CP_ACP, 0, wc, l, ac, sizeof(ac), NULL, NULL); + WriteConsole(hconsoleout, ac, l, &dummy, NULL); } } diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 1ec9cc8bd..87f0de51b 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -5393,7 +5393,7 @@ static void SVNQ_Spawn_f (void) ent->v->colormap = NUM_FOR_EDICT(svprogfuncs, ent); ent->v->team = 0; // FIXME - ent->v->netname = PR_SetString(svprogfuncs, host_client->name); + svprogfuncs->SetStringField(svprogfuncs, ent, &ent->v->netname, host_client->name, true); host_client->entgravity = ent->xv->gravity = 1.0; host_client->entgravity*=sv_gravity.value; @@ -7585,9 +7585,9 @@ void SV_ExecuteClientMessage (client_t *cl) if (split) split->lossage = cl->lossage; } - MSG_ReadDeltaUsercmd (&nullcmd, &oldest); - MSG_ReadDeltaUsercmd (&oldest, &oldcmd); - MSG_ReadDeltaUsercmd (&oldcmd, &newcmd); + MSG_ReadDeltaUsercmd (&nullcmd, &oldest, PROTOCOL_VERSION_QW); + MSG_ReadDeltaUsercmd (&oldest, &oldcmd, PROTOCOL_VERSION_QW); + MSG_ReadDeltaUsercmd (&oldcmd, &newcmd, PROTOCOL_VERSION_QW); if (!split) break; // either someone is trying to cheat, or they sent input commands for splitscreen clients they no longer own. diff --git a/engine/shaders/glsl/defaultwall.glsl b/engine/shaders/glsl/defaultwall.glsl index 27383ea16..c0412e2a6 100644 --- a/engine/shaders/glsl/defaultwall.glsl +++ b/engine/shaders/glsl/defaultwall.glsl @@ -8,7 +8,6 @@ !!permu SPECULAR !!permu REFLECTCUBEMASK !!cvarf r_glsl_offsetmapping_scale -!!cvarf gl_specular !!cvardf r_tessellation_level=5 !!samps diffuse lightmap specular normalmap fullbright reflectmask reflectcube paletted lightmap1 lightmap2 lightmap3 @@ -230,9 +229,6 @@ void main() #define s_colourmap s_t0 uniform sampler2D s_colourmap; -#ifdef SPECULAR -uniform float cvar_gl_specular; -#endif #ifdef OFFSETMAPPING #include "sys/offsetmapping.h" #endif @@ -298,7 +294,7 @@ void main () vec3 delux = (texture2D(s_deluxmap, lm0).rgb-0.5); #ifdef BUMPMODELSPACE delux = normalize(delux*invsurface); - #else +#else lightmaps *= 2.0 / max(0.25, delux.z); //counter the darkening from deluxmaps #endif lightmaps *= dot(norm, delux); @@ -312,7 +308,7 @@ void main () #ifdef SPECULAR vec4 specs = texture2D(s_specular, tc); vec3 halfdir = normalize(normalize(eyevector) + delux); //this norm should be the deluxemap info instead - float spec = pow(max(dot(halfdir, norm), 0.0), 2.0*FTE_SPECULAR_EXPONENT * specs.a); + float spec = pow(max(dot(halfdir, norm), 0.0), FTE_SPECULAR_EXPONENT * specs.a); spec *= FTE_SPECULAR_MULTIPLIER; //NOTE: rtlights tend to have a *4 scaler here to over-emphasise the effect because it looks cool. //As not all maps will have deluxemapping, and the double-cos from the light util makes everything far too dark anyway, @@ -322,7 +318,7 @@ void main () #endif #ifdef REFLECTCUBEMASK - vec3 rtc = reflect(-eyevector, norm); + vec3 rtc = reflect(normalize(-eyevector), norm); rtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2]; rtc = (m_model * vec4(rtc.xyz,0.0)).xyz; gl_FragColor.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb; @@ -364,3 +360,4 @@ void main () #endif } #endif + diff --git a/plugins/jabber/jabberclient.c b/plugins/jabber/jabberclient.c index 24472f467..4e3a79ee7 100644 --- a/plugins/jabber/jabberclient.c +++ b/plugins/jabber/jabberclient.c @@ -108,7 +108,7 @@ void Fallback_ConPrint(const char *conname, const char *message) } void XMPP_ConversationPrintf(const char *context, const char *title, char *format, ...); -void Con_SubPrintf(const char *subname, char *format, ...) +void Con_SubPrintf(const char *subname, const char *format, ...) { va_list argptr; static char string[1024]; @@ -122,12 +122,7 @@ void Con_SubPrintf(const char *subname, char *format, ...) //porting zone: - #define COLOURGREEN "^2" - #define COLORWHITE "^7" #define COLOURWHITE "^7" // word - #define COLOURRED "^1" - #define COLOURYELLOW "^3" - #define COLOURPURPLE "^5" #define COMMANDPREFIX "xmpp" #define COMMANDPREFIX2 "jab" #define COMMANDPREFIX3 "jabbercl" @@ -188,8 +183,7 @@ void Con_SubPrintf(const char *subname, char *format, ...) } -//GPL: ripped from quake -char *JCL_Info_ValueForKey (char *s, const char *key, char *valuebuf, int valuelen) +char *JCL_Info_ValueForKey (char *s, const char *key, char *valuebuf, int valuelen) //GPL: ripped from quake { char pkey[1024]; char *o; @@ -247,12 +241,345 @@ char *JCL_Info_ValueForKey (char *s, const char *key, char *valuebuf, int valuel } } + + + +/* +this is a fairly basic implementation. +don't expect it to do much. +You can probably get a better version from somewhere. +This has been tweaked to do xml markup in %s. Use %+s if you want to include xml-formatted text as-is. +*/ +int Q_vsnprintxf(char *buffer, size_t maxlen, const char *format, va_list vargs) +{ + int tokens=0; + char *string; + char tempbuffer[64]; + char sign; + unsigned int _uint; + int _int; + float _float; + int i; + int use0s; + int width, useprepad, plus; + int precision; + + if (!maxlen) + return 0; +maxlen--; + + while(*format) + { + switch(*format) + { + case '%': + plus = 0; + width= 0; + precision=-1; + useprepad=0; + use0s= 0; +retry: + switch(*(++format)) + { + case '-': + useprepad=true; + goto retry; + case '+': + plus = true; + goto retry; + case '.': + precision = 0; + while (format[1] >= '0' && format[1] <= '9') + precision = precision*10+*++format-'0'; + goto retry; + case '0': + if (!width) + { + use0s=true; + goto retry; + } + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + width=width*10+*format-'0'; + goto retry; + case '%': /*emit a %*/ + if (maxlen-- == 0) + {*buffer++='\0';return tokens;} + *buffer++ = *format; + break; + case 's': + string = va_arg(vargs, char *); + if (!string) + string = "(null)"; + if (!plus) + { + while (*string) + { + char *rep; + size_t replen; + char c = *string++; + switch(c) + { + default: + if (maxlen-- == 0) + {*buffer++='\0';return tokens;} + *buffer++ = c; + continue; + case '<': rep = "<"; break; + case '>': rep = ">"; break; + case '&': rep = "&"; break; + case '\'': rep = "'"; break; + case '\"': rep = """; break; + } + replen = strlen(rep); + if (maxlen < replen) + {*buffer++='\0';return tokens;} + maxlen -= replen; + memcpy(buffer, rep, replen); + buffer += replen; + } + } + else if (width) + { + while (*string && width--) + { + if (maxlen-- == 0) + {*buffer++='\0';return tokens;} + *buffer++ = *string++; + } + } + else + { + while (*string) + { + if (maxlen-- == 0) + {*buffer++='\0';return tokens;} + *buffer++ = *string++; + } + } + tokens++; + break; + /*case 'c': + _int = va_arg(vargs, int); + if (maxlen-- == 0) + {*buffer++='\0';return tokens;} + *buffer++ = _int; + tokens++; + break;*/ + case 'p': + if (1) + _uint = (size_t)va_arg(vargs, void*); + else + case 'x': + _uint = va_arg(vargs, unsigned int); + i = sizeof(tempbuffer)-2; + tempbuffer[i+1] = '\0'; + while(_uint) + { + tempbuffer[i] = (_uint&0xf) + '0'; + if (tempbuffer[i] > '9') + tempbuffer[i] = tempbuffer[i] - ':' + 'a'; + _uint/=16; + i--; + } + string = tempbuffer+i+1; + + if (!*string) + { + i=61; + string = tempbuffer+i+1; + string[0] = '0'; + string[1] = '\0'; + } + + width -= 62-i; + while (width>0) + { + string--; + if (use0s) + *string = '0'; + else + *string = ' '; + width--; + } + + while (*string) + { + if (maxlen-- == 0) + {*buffer++='\0';return tokens;} + *buffer++ = *string++; + } + tokens++; + break; + case 'd': + case 'u': + case 'i': + _int = va_arg(vargs, int); + if (useprepad) + { + + } + if (_int < 0) + { + sign = '-'; + _int *= -1; + } + else if (plus) + sign = '+'; + else + sign = 0; + i = sizeof(tempbuffer)-2; + tempbuffer[sizeof(tempbuffer)-1] = '\0'; + while(_int) + { + tempbuffer[i--] = _int%10 + '0'; + _int/=10; + } + if (sign) + tempbuffer[i--] = sign; + string = tempbuffer+i+1; + + if (!*string) + { + i=61; + string = tempbuffer+i+1; + string[0] = '0'; + string[1] = '\0'; + } + + width -= 62-i; +/* while (width>0) + { + string--; + *string = ' '; + width--; + } +*/ + while(width>0) + { + if (maxlen-- == 0) + {*buffer++='\0';return tokens;} + if (use0s) + *buffer++ = '0'; + else + *buffer++ = ' '; + width--; + } + + while (*string) + { + if (maxlen-- == 0) + {*buffer++='\0';return tokens;} + *buffer++ = *string++; + } + tokens++; + break; + case 'f': + _float = (float)va_arg(vargs, double); + +//integer part. + _int = (int)_float; + if (_int < 0) + { + if (maxlen-- == 0) + {*buffer++='\0';return tokens;} + *buffer++ = '-'; + _int *= -1; + } + i = sizeof(tempbuffer)-2; + tempbuffer[sizeof(tempbuffer)-1] = '\0'; + if (!_int) + { + tempbuffer[i--] = '0'; + } + else + { + while(_int) + { + tempbuffer[i--] = _int%10 + '0'; + _int/=10; + } + } + string = tempbuffer+i+1; + while (*string) + { + if (maxlen-- == 0) + {*buffer++='\0';return tokens;} + *buffer++ = *string++; + } + + _int = sizeof(tempbuffer)-2-i; + +//floating point part. + _float -= (int)_float; + i = 0; + tempbuffer[i++] = '.'; + if (precision < 0) + precision = 7; + while(_float - (int)_float) + { + if (i > precision) //remove the excess presision. + break; + + _float*=10; + tempbuffer[i++] = (int)_float%10 + '0'; + } + if (i == 1) //no actual fractional part + { + tokens++; + break; + } + + //concatinate to our string + tempbuffer[i] = '\0'; + string = tempbuffer; + while (*string) + { + if (maxlen-- == 0) + {*buffer++='\0';return tokens;} + *buffer++ = *string++; + } + + tokens++; + break; + default: + string = "ERROR IN FORMAT"; + while (*string) + { + if (maxlen-- == 0) + {*buffer++='\0';return tokens;} + *buffer++ = *string++; + } + break; + } + break; + default: + if (maxlen-- == 0) + {*buffer++='\0';return tokens;} + *buffer++ = *format; + break; + } + format++; + } + {*buffer++='\0';return tokens;} +} + + + + #if defined(_WIN32) && defined(HAVE_PACKET) #include static DNS_STATUS (WINAPI *pDnsQuery_UTF8) (PCSTR pszName, WORD wType, DWORD Options, PIP4_ARRAY aipServers, PDNS_RECORD *ppQueryResults, PVOID *pReserved); static VOID (WINAPI *pDnsRecordListFree)(PDNS_RECORD pRecordList, DNS_FREE_TYPE FreeType); static HMODULE dnsapi_lib; -qboolean NET_DNSLookup_SRV(char *host, char *out, int outlen) +qboolean NET_DNSLookup_SRV(const char *host, char *out, int outlen) { DNS_RECORD *result = NULL; if (!dnsapi_lib) @@ -408,7 +735,7 @@ void Base64_Byte(unsigned int byt) } } -void Base64_Add(char *s, int len) +void Base64_Add(const char *s, int len) { unsigned char *us = (unsigned char *)s; while(len-->0) @@ -467,7 +794,7 @@ static int Base64_DecodeByte(char byt) return -1; } //FIXME: we should be able to skip whitespace. -int Base64_Decode(char *out, int outlen, char *src, int srclen) +int Base64_Decode(char *out, int outlen, const char *src, int srclen) { int len = 0; int result; @@ -1267,7 +1594,7 @@ static int sasl_oauth2_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize) { char body[4096]; char header[4096]; - char *newrefresh; + const char *newrefresh; //send a refresh request @@ -1378,16 +1705,16 @@ https://oauth.live.com/authorize?client_id=000000004C07035A&scope=wl.messenger,w struct subtree_s; -void JCL_AddClientMessagef(jclient_t *jcl, char *fmt, ...); -qboolean JCL_FindBuddy(jclient_t *jcl, char *jid, buddy_t **buddy, bresource_t **bres, qboolean create); +//void JCL_AddClientMessagef(jclient_t *jcl, char *fmt, ...); +//qboolean JCL_FindBuddy(jclient_t *jcl, const char *jid, buddy_t **buddy, bresource_t **bres, qboolean create); void JCL_GeneratePresence(jclient_t *jcl, qboolean force); -struct iq_s *JCL_SendIQf(jclient_t *jcl, qboolean (*callback) (jclient_t *jcl, struct subtree_s *tree, struct iq_s *iq), char *iqtype, char *target, char *fmt, ...); -struct iq_s *JCL_SendIQNode(jclient_t *jcl, qboolean (*callback) (jclient_t *jcl, xmltree_t *tree, struct iq_s *iq), char *iqtype, char *target, xmltree_t *node, qboolean destroynode); +struct iq_s *JCL_SendIQf(jclient_t *jcl, qboolean (*callback) (jclient_t *jcl, struct subtree_s *tree, struct iq_s *iq), const char *iqtype, const char *target, const char *fmt, ...); +//struct iq_s *JCL_SendIQNode(jclient_t *jcl, qboolean (*callback) (jclient_t *jcl, xmltree_t *tree, struct iq_s *iq), const char *iqtype, const char *target, xmltree_t *node, qboolean destroynode); void JCL_CloseConnection(jclient_t *jcl, const char *reason, qboolean reconnect); -void JCL_JoinMUCChat(jclient_t *jcl, char *room, char *server, char *myhandle, char *password); +void JCL_JoinMUCChat(jclient_t *jcl, const char *room, const char *server, const char *myhandle, const char *password); static qboolean JCL_BuddyVCardReply(jclient_t *jcl, xmltree_t *tree, struct iq_s *iq); -void JCL_GenLink(jclient_t *jcl, char *out, int outlen, char *action, char *context, char *contextres, char *sid, char *txtfmt, ...) +void JCL_GenLink(jclient_t *jcl, char *out, int outlen, const char *action, const char *context, const char *contextres, const char *sid, const char *txtfmt, ...) { va_list argptr; qboolean textonly = false; @@ -1577,6 +1904,7 @@ qintptr_t JCL_ConsoleLink(qintptr_t *args) char what[256]; char which[256]; int i; + buddy_t *b = NULL; // pCmd_Argv(0, text, sizeof(text)); pCmd_Argv(1, link, sizeof(link)); @@ -1834,18 +2162,68 @@ qintptr_t JCL_ConsoleLink(qintptr_t *args) char chatlink[512]; char unfriend[512]; char realias[512]; - buddy_t *b = NULL; JCL_FindBuddy(jcl, *who?who:jcl->defaultdest, &b, NULL, false); if (b) { - JCL_GenLink(jcl, chatlink, sizeof(chatlink), NULL, b->accountdomain, NULL, NULL, "Chat with %s", b->name); - JCL_GenLink(jcl, unfriend, sizeof(unfriend), NULL, b->accountdomain, NULL, NULL, "Unfriend %s", b->name); - JCL_GenLink(jcl, realias, sizeof(realias), NULL, b->accountdomain, NULL, NULL, "Set alias for %s", b->name); + if (b->btype == BT_CHATROOM) + { + JCL_GenLink(jcl, chatlink, sizeof(chatlink), NULL, b->accountdomain, NULL, NULL, "Chat in %s", b->name); + JCL_GenLink(jcl, unfriend, sizeof(unfriend), "unfriend", b->accountdomain, NULL, NULL, "Forget %s", b->name); + JCL_GenLink(jcl, realias, sizeof(realias), "setbalias", b->accountdomain, NULL, NULL, "Set alias for %s", b->name); + } + else + { + JCL_GenLink(jcl, chatlink, sizeof(chatlink), NULL, b->accountdomain, NULL, NULL, "Chat with %s", b->name); + JCL_GenLink(jcl, unfriend, sizeof(unfriend), "unfriend", b->accountdomain, NULL, NULL, "Unfriend %s", b->name); + JCL_GenLink(jcl, realias, sizeof(realias), "setbalias", b->accountdomain, NULL, NULL, "Set alias for %s", b->name); + } Q_snprintf(footer, sizeof(footer), "\n%s\n%s\n%s", chatlink, unfriend, realias); pCon_SetConsoleString(BUDDYLISTTITLE, "footer", footer); } } + else if (!strcmp(what, "unfriend")) + { + JCL_FindBuddy(jcl, *who?who:jcl->defaultdest, &b, NULL, false); + if (b) + { + if (b->btype == BT_CHATROOM) + { + JCL_AddClientMessagef(jcl, "", b->accountdomain, b->ourselves?b->ourselves->resource:""); + JCL_ForgetBuddy(jcl, b, NULL); + } + else if (b->btype == BT_ROSTER) + { + //hide from em + JCL_AddClientMessagef(jcl, "", b->accountdomain); + + //stop looking for em + JCL_AddClientMessagef(jcl, "", b->accountdomain); + + //stop listing em + JCL_SendIQf(jcl, NULL, "set", NULL, "", b->accountdomain); +// b->btype = BT_UNKNOWN; + JCL_ForgetBuddy(jcl, b, NULL); + } + else + { + JCL_AddClientMessagef(jcl, "", b->accountdomain); + JCL_ForgetBuddy(jcl, b, NULL); + } + } + } + else if (!strcmp(what, "setbalias")) + { + JCL_FindBuddy(jcl, *who?who:jcl->defaultdest, &b, NULL, false); + if (b) + { + pCon_SetConsoleFloat(BUDDYLISTTITLE, "linebuffered", true); + pCon_SetConsoleString(BUDDYLISTTITLE, "footer", "Please enter what you want to see them as"); + jclient_action_cl = jcl; + jclient_action_buddy = b; + jclient_action = ACT_SETBALIAS; + } + } else if ((*who && !*what) || !strcmp(what, "msg")) { if (jcl) @@ -1857,7 +2235,8 @@ qintptr_t JCL_ConsoleLink(qintptr_t *args) if (b) { f = b->accountdomain; - b->defaultresource = br; + if (b->btype != BT_CHATROOM) + b->defaultresource = br; if (BUILTINISVALID(Con_SetConsoleFloat)) { @@ -2053,7 +2432,10 @@ qintptr_t JCL_ConExecuteCommand(qintptr_t *args) break; case ACT_SETBALIAS: Q_strncpyz(jclient_action_buddy->name, args, sizeof(jclient_action_buddy->name)); - JCL_SendIQf(jclient_action_cl, NULL, "set", NULL, "", jclient_action_buddy->accountdomain, jclient_action_buddy->name); + if (jclient_action_buddy->btype == BT_ROSTER) + JCL_SendIQf(jclient_action_cl, NULL, "set", NULL, "", jclient_action_buddy->accountdomain, jclient_action_buddy->name); + if (BUILTINISVALID(Con_SetConsoleString)) + pCon_SetConsoleString(jclient_action_buddy->accountdomain, "title", jclient_action_buddy->name); break; } jclient_action = ACT_NONE; @@ -2105,7 +2487,7 @@ void JCL_FlushOutgoing(jclient_t *jcl) { char t = jcl->outbuf[jcl->outbufpos+sent]; jcl->outbuf[jcl->outbufpos+sent] = 0; - XMPP_ConversationPrintf("xmppout", "xmppout", COLOURYELLOW "%s\n", jcl->outbuf + jcl->outbufpos); + XMPP_ConversationPrintf("xmppout", "xmppout", "^3%s\n", jcl->outbuf + jcl->outbufpos); jcl->outbuf[jcl->outbufpos+sent] = t; } @@ -2117,7 +2499,7 @@ void JCL_FlushOutgoing(jclient_t *jcl) // else // Con_Printf("Unable to send anything\n"); } -void JCL_AddClientMessage(jclient_t *jcl, char *msg, int datalen) +void JCL_AddClientMessage(jclient_t *jcl, const char *msg, int datalen) { //handle overflows if (jcl->outbufpos+jcl->outbuflen+datalen > jcl->outbufmax) @@ -2157,17 +2539,17 @@ void JCL_AddClientMessage(jclient_t *jcl, char *msg, int datalen) //try and flush it now JCL_FlushOutgoing(jcl); } -void JCL_AddClientMessageString(jclient_t *jcl, char *msg) +void JCL_AddClientMessageString(jclient_t *jcl, const char *msg) { JCL_AddClientMessage(jcl, msg, strlen(msg)); } -void JCL_AddClientMessagef(jclient_t *jcl, char *fmt, ...) +void JCL_AddClientMessagef(jclient_t *jcl, const char *fmt, ...) { va_list argptr; char body[4096]; va_start (argptr, fmt); - Q_vsnprintf (body, sizeof(body), fmt, argptr); + Q_vsnprintxf (body, sizeof(body), fmt, argptr); va_end (argptr); JCL_AddClientMessageString(jcl, body); @@ -2322,6 +2704,7 @@ jclient_t *JCL_ConnectXML(xmltree_t *acc) memset(jcl, 0, sizeof(jclient_t)); jcl->socket = -1; + jcl->rcon_pipe = -1; jcl->enabledcapabilities = CAP_DEFAULTENABLEDCAPS; @@ -2393,7 +2776,7 @@ jclient_t *JCL_ConnectXML(xmltree_t *acc) features = XML_ChildOfTree(acc, "features", 0); if (features && XML_GetParameter(features, "ver", JCL_BUILD)) { - char *val; + const char *val; int j; for (j = 0; capnames[j].names; j++) { @@ -2409,16 +2792,67 @@ jclient_t *JCL_ConnectXML(xmltree_t *acc) } + //parse chatrooms + features = XML_ChildOfTree(acc, "chats", 0); + if (features) + { + for (bn=0; ; bn++) + { + xmltree_t *b = XML_ChildOfTree(features, "room", bn); + if (b) + { + buddy_t *room; + const char *jid = XML_GetParameter(b, "name", ""); + const char *alias = XML_GetParameter(b, "alias", jid); + const char *topic = XML_GetParameter(b, "topic", NULL); + const char *nick = XML_GetParameter(b, "nick", NULL); + const char *password = XML_GetParameter(b, "password", NULL); + qboolean autojoin = atoi(XML_GetParameter(b, "autojoin", "1")); + + if (JCL_FindBuddy(jcl, jid, &room, NULL, true) && room) + { + room->btype = BT_CHATROOM; + Q_strlcpy(room->name, alias, sizeof(room->name)); + room->room_topic = topic?strdup(topic):NULL; + room->room_nick = nick?strdup(nick):NULL; + room->room_password = password?strdup(password):NULL; + room->room_autojoin = autojoin; + } + } + else + break; + } + } + else + { + /*buddy_t *room; + const char *jid = "quake@somedomain"; + const char *alias = "FTE Users"; + const char *topic = NULL; + const char *nick = NULL; + const char *password = NULL; + + if (JCL_FindBuddy(jcl, jid, &room, NULL, true) && room) + { + room->btype = BT_CHATROOM; + Q_strlcpy(room->name, alias, sizeof(room->name)); + room->room_topic = topic?strdup(topic):NULL; + room->room_nick = nick?strdup(nick):NULL; + room->room_password = password?strdup(password):NULL; + }*/ + } + + features = XML_ChildOfTree(acc, "buddyinfo", 0); for (bn=0; ; bn++) { xmltree_t *b = XML_ChildOfTree(features, "buddy", bn); if (b) { - char *buddyid = XML_GetParameter(b, "name", JCL_BUILD); - char *buddyimage = XML_GetChildBody(b, "image", NULL); - char *buddyimagemime = XML_GetChildBody(b, "imagemime", NULL); - char *buddyimagehash = XML_GetChildBody(b, "imagehash", NULL); + const char *buddyid = XML_GetParameter(b, "name", JCL_BUILD); + const char *buddyimage = XML_GetChildBody(b, "image", NULL); + const char *buddyimagemime = XML_GetChildBody(b, "imagemime", NULL); + const char *buddyimagehash = XML_GetChildBody(b, "imagehash", NULL); bi = malloc(sizeof(*bi) + strlen(buddyid)); strcpy(bi->accountdomain, buddyid); @@ -2529,6 +2963,13 @@ void JCL_ForgetBuddy(jclient_t *jcl, buddy_t *buddy, bresource_t *bres) { *link = b->next; JCL_ForgetBuddyResource(jcl, b, bres); + if (jclient_action_buddy == b) + { + pCon_SetConsoleFloat(BUDDYLISTTITLE, "linebuffered", false); + jclient_action = ACT_NONE; + jclient_action_buddy = NULL; + jclient_action_cl = NULL; + } free(b); if (buddy) break; @@ -2536,6 +2977,8 @@ void JCL_ForgetBuddy(jclient_t *jcl, buddy_t *buddy, bresource_t *bres) else link = &b->next; } + + jclient_updatebuddylist = true; } struct stringprep_range @@ -2795,14 +3238,14 @@ static qboolean JCL_NameResourcePrep(const char *in, char *nout, size_t noutsize } //FIXME: add flags to avoid creation -qboolean JCL_FindBuddy(jclient_t *jcl, char *jid, buddy_t **buddy, bresource_t **bres, qboolean create) +qboolean JCL_FindBuddy(jclient_t *jcl, const char *jid, buddy_t **buddy, bresource_t **bres, qboolean create) { char name[256]; char *res; buddy_t *b; bresource_t *r = NULL; - if (!jid) - { + if (!jid || !*jid || !JCL_NameResourcePrep(jid, name, sizeof(name), &res)) + { //no name / nameprep failed. if (buddy) *buddy = NULL; if (bres) @@ -2810,9 +3253,6 @@ qboolean JCL_FindBuddy(jclient_t *jcl, char *jid, buddy_t **buddy, bresource_t * return false; } - if (!JCL_NameResourcePrep(jid, name, sizeof(name), &res)) - return false; //nameprep failed. - for (b = jcl->buddies; b; b = b->next) { if (!strcmp(b->accountdomain, name)) @@ -2829,7 +3269,7 @@ qboolean JCL_FindBuddy(jclient_t *jcl, char *jid, buddy_t **buddy, bresource_t * Q_strlcpy(b->name, name, sizeof(b->name)); //default } *buddy = b; - if (res && bres) + if (res && bres && b) { for (r = b->resources; r; r = r->next) { @@ -2875,7 +3315,7 @@ void JCL_IQTimeouts(jclient_t *jcl) link = &(*link)->next; } } -struct iq_s *JCL_SendIQ(jclient_t *jcl, qboolean (*callback) (jclient_t *jcl, xmltree_t *tree, struct iq_s *iq), char *iqtype, char *target, char *body) +struct iq_s *JCL_SendIQ(jclient_t *jcl, qboolean (*callback) (jclient_t *jcl, xmltree_t *tree, struct iq_s *iq), const char *iqtype, const char *target, const char *body) { struct iq_s *iq; @@ -2908,18 +3348,18 @@ struct iq_s *JCL_SendIQ(jclient_t *jcl, qboolean (*callback) (jclient_t *jcl, xm JCL_AddClientMessageString(jcl, ""); return iq; } -struct iq_s *JCL_SendIQf(jclient_t *jcl, qboolean (*callback) (jclient_t *jcl, xmltree_t *tree, struct iq_s *iq), char *iqtype, char *target, char *fmt, ...) +struct iq_s *JCL_SendIQf(jclient_t *jcl, qboolean (*callback) (jclient_t *jcl, xmltree_t *tree, struct iq_s *iq), const char *iqtype, const char *target, const char *fmt, ...) { va_list argptr; char body[2048]; va_start (argptr, fmt); - Q_vsnprintf (body, sizeof(body), fmt, argptr); + Q_vsnprintxf (body, sizeof(body), fmt, argptr); va_end (argptr); return JCL_SendIQ(jcl, callback, iqtype, target, body); } -struct iq_s *JCL_SendIQNode(jclient_t *jcl, qboolean (*callback) (jclient_t *jcl, xmltree_t *tree, struct iq_s *iq), char *iqtype, char *target, xmltree_t *node, qboolean destroynode) +struct iq_s *JCL_SendIQNode(jclient_t *jcl, qboolean (*callback) (jclient_t *jcl, xmltree_t *tree, struct iq_s *iq), const char *iqtype, const char *target, xmltree_t *node, qboolean destroynode) { struct iq_s *n; char *s = XML_GenerateString(node, false); @@ -3005,12 +3445,12 @@ qboolean XMPP_CarbonsEnabledReply(jclient_t *jcl, xmltree_t *tree, struct iq_s * return true; } -static void JCL_RosterUpdate(jclient_t *jcl, xmltree_t *listp, char *from) +static void JCL_RosterUpdate(jclient_t *jcl, xmltree_t *listp, const char *from) { xmltree_t *i; buddy_t *buddy; int cnum = 0; - char *at = strrchr(from, '@'); + const char *at = strrchr(from, '@'); if (at) { if (strlen(jcl->username) != at-from || strncasecmp(from, jcl->username, at-from)) @@ -3022,11 +3462,12 @@ static void JCL_RosterUpdate(jclient_t *jcl, xmltree_t *listp, char *from) while ((i = XML_ChildOfTree(listp, "item", cnum++))) { - char *name = XML_GetParameter(i, "name", ""); - char *jid = XML_GetParameter(i, "jid", ""); - char *sub = XML_GetParameter(i, "subscription", "both"); - char *ask = XML_GetParameter(i, "ask", ""); + const char *name = XML_GetParameter(i, "name", ""); + const char *jid = XML_GetParameter(i, "jid", ""); + const char *sub = XML_GetParameter(i, "subscription", "both"); + const char *ask = XML_GetParameter(i, "ask", ""); JCL_FindBuddy(jcl, jid, &buddy, NULL, true); + buddy->btype = BT_ROSTER; if (*name) Q_strlcpy(buddy->name, name, sizeof(buddy->name)); @@ -3236,7 +3677,7 @@ static qboolean JCL_ServerFeatureReply(jclient_t *jcl, xmltree_t *tree, struct i { xmltree_t *query = XML_ChildOfTreeNS(tree, "http://jabber.org/protocol/disco#info", "query", 0); xmltree_t *feature; - char *featurename; + const char *featurename; int f; #ifdef USE_GOOGLE_MAIL_NOTIFY qboolean gmail = false; @@ -3274,11 +3715,80 @@ static qboolean JCL_ServerFeatureReply(jclient_t *jcl, xmltree_t *tree, struct i return true; } +static qboolean JCL_ServerPeerReply(jclient_t *jcl, xmltree_t *tree, struct iq_s *iq) +{ + xmltree_t *query = XML_ChildOfTreeNS(tree, "http://jabber.org/protocol/disco#info", "query", 0); + xmltree_t *feature; + xmltree_t *identity; + const char *from = iq->to; + const char *name; + const char *type; + const char *category; + int f; + + if (!query) + return false; + + for (f = 0; ; f++) + { + identity = XML_ChildOfTree(query, "identity", f); + if (!identity) + break; + name = XML_GetParameter(identity, "name", ""); + type = XML_GetParameter(identity, "type", ""); + category = XML_GetParameter(identity, "category", ""); + Con_Printf("Server %s supports identity type=%s category=%s name=%s\n", from, type, category, name); + } + + for (f = 0; ; f++) + { + feature = XML_ChildOfTree(query, "feature", f); + if (!feature) + break; + name = XML_GetParameter(feature, "var", ""); + Con_Printf("Server %s supports feature %s\n", from, name); + } + + return true; +} +static qboolean JCL_ServerItemsReply(jclient_t *jcl, xmltree_t *tree, struct iq_s *iq) +{ + xmltree_t *query = XML_ChildOfTreeNS(tree, "http://jabber.org/protocol/disco#items", "query", 0); + xmltree_t *item; + const char *itemname; + int f; + + if (!query) + return false; + + for (f = 0; ; f++) + { + item = XML_ChildOfTree(query, "item", f); + if (!item) + break; + itemname = XML_GetParameter(item, "jid", ""); + if (*itemname) + { + Con_DPrintf("Server reports peer %s\n", itemname); + JCL_SendIQf(jcl, JCL_ServerPeerReply, "get", itemname, ""); + } + } + return true; +} static qboolean JCL_SessionReply(jclient_t *jcl, xmltree_t *tree, struct iq_s *iq) { + buddy_t *b; JCL_SendIQf(jcl, JCL_RosterReply, "get", NULL, ""); JCL_SendIQf(jcl, JCL_MyVCardReply, "get", jcl->barejid, ""); JCL_SendIQf(jcl, JCL_ServerFeatureReply, "get", jcl->domain, ""); +// JCL_SendIQf(jcl, JCL_ServerItemsReply, "get", jcl->domain, ""); + + for (b = jcl->buddies; b; b = b->next) + { + if (b->btype == BT_CHATROOM) + if (b->room_autojoin) + JCL_JoinMUCChat(jcl, b->accountdomain, NULL, b->room_nick, b->room_password); + } return true; } @@ -3291,7 +3801,7 @@ static struct #if 1 {"http://jabber.org/protocol/caps"}, {"http://jabber.org/protocol/disco#info"}, -// {"http://jabber.org/protocol/disco#items"}, + {"http://jabber.org/protocol/disco#items"}, {"jabber:iq:version"}, #ifdef JINGLE @@ -3339,6 +3849,8 @@ static struct // {"http://jabber.org/protocol/tune+notify"}, {"http://jabber.org/protocol/nick"}, {"http://jabber.org/protocol/nick+notify"}, + + {"http://jabber.org/protocol/commands"}, #else //for testing, this is the list of features pidgin supports (which is the other client I'm testing against). @@ -3488,10 +4000,10 @@ char *buildcapsvcardpresence(jclient_t *jcl, char *caps, size_t sizeofcaps) void JCL_ParseIQ(jclient_t *jcl, xmltree_t *tree) { qboolean unparsable = true; - char *from; -// char *to; - char *id; - char *f; + const char *from; +// const char *to; + const char *id; + const char *f; xmltree_t *ot; //FIXME: block from people who we don't know. @@ -3510,24 +4022,25 @@ void JCL_ParseIQ(jclient_t *jcl, xmltree_t *tree) { //http://xmpp.org/extensions/xep-0030.html char msg[2048]; char hashednode[1024]; - char *node = XML_GetParameter(ot, "node", NULL); - unparsable = false; + const char *node = XML_GetParameter(ot, "node", NULL); buildcaps(jcl, msg, sizeof(msg)); Q_snprintf(hashednode, sizeof(hashednode),DISCONODE"#%s", buildcapshash(jcl)); if (!node || !strcmp(node, hashednode) || !strcmp(node, DISCONODE"#") ) { + unparsable = false; JCL_AddClientMessagef(jcl, "" "" - "%s" + "%+s" "" "", from, id, node?node:hashednode, msg); } #ifdef VOIP_LEGACY else if (!strcmp(node, DISCONODE"#voice-v1") && (jcl->enabledcapabilities & CAP_GOOGLE_VOICE)) { + unparsable = false; JCL_AddClientMessagef(jcl, "" "" @@ -3537,6 +4050,7 @@ void JCL_ParseIQ(jclient_t *jcl, xmltree_t *tree) } else if (!strcmp(node, DISCONODE"#camera-v1") && (jcl->enabledcapabilities & CAP_GOOGLE_VOICE)) { + unparsable = false; JCL_AddClientMessagef(jcl, "" "" @@ -3546,6 +4060,7 @@ void JCL_ParseIQ(jclient_t *jcl, xmltree_t *tree) } else if (!strcmp(node, DISCONODE"#video-v1") && (jcl->enabledcapabilities & CAP_GOOGLE_VOICE)) { + unparsable = false; JCL_AddClientMessagef(jcl, "" "" @@ -3564,6 +4079,28 @@ void JCL_ParseIQ(jclient_t *jcl, xmltree_t *tree) "", from, id); } } + else if (from && !strcmp(ot->xmlns, "http://jabber.org/protocol/disco#items")) + { //http://xmpp.org/extensions/xep-0030.html + const char *node = XML_GetParameter(ot, "node", NULL); + if (!strcmp(node, "http://jabber.org/protocol/commands")) + { + const char *to = XML_GetParameter(tree, "to", ""); + unparsable = false; + JCL_AddClientMessagef(jcl, + "" + "" + "" + "" + "", from, id, node, to); + } + else + { //send back an empty list if its something we don't recognise + JCL_AddClientMessagef(jcl, + "" + "" + "", from, id, node); + } + } else if (from && !strcmp(ot->xmlns, "jabber:iq:version")) { //client->client version request char msg[2048]; @@ -3679,7 +4216,123 @@ void JCL_ParseIQ(jclient_t *jcl, xmltree_t *tree) if (c && !strcmp(c->xmlns, "jabber:iq:roster")) { unparsable = false; - JCL_RosterUpdate(jcl, c, from); + if (strcmp(from, jcl->domain)) + Con_Printf("Roster push from unknown entity: \"%s\"\n", from); + else + JCL_RosterUpdate(jcl, c, from); + } + + c = XML_ChildOfTreeNS(tree, "http://jabber.org/protocol/commands", "command", 0); + if (c) + { + char *s; + const char *key, *val; + xmltree_t *rep, *cmd, *x, *fld; + const char *node = XML_GetParameter(c, "node", ""); + char *completed = NULL; + char *canceled = NULL; + char infostring[65536]; + int i; + unparsable = false; + + *infostring = 0; + x = XML_ChildOfTreeNS(c, "jabber:x:data", "x", 0); + for (i = 0; ; i++) + { + fld = XML_ChildOfTree(x, "field", i); + if (!fld) + break; + key = XML_GetParameter(fld, "var", ""); + val = XML_GetChildBody(fld, "value", ""); + if (*key && *val) + { + Q_strlcat(infostring, "\\", sizeof(infostring)); + Q_strlcat(infostring, key, sizeof(infostring)); + Q_strlcat(infostring, "\\", sizeof(infostring)); + Q_strlcat(infostring, val, sizeof(infostring)); + } + } + + if (!strcmp(XML_GetParameter(c, "action", "submit"), "cancel")) + canceled = ""; + else if (!strcmp(node, "rcon")) + { + char pwd[64]; + JCL_Info_ValueForKey(infostring, "password", pwd, sizeof(pwd)); + if (*pwd) + { + char rcon_password[64]; + pCvar_GetString("rcon_password", rcon_password, sizeof(rcon_password)); + if (*rcon_password && !strcmp(JCL_Info_ValueForKey(infostring, "password", pwd, sizeof(pwd)), rcon_password)) + { + Q_strlcpy(jcl->rcon_peer, from, sizeof(jcl->rcon_peer)); + if (jcl->rcon_pipe >= 0) + pFS_Close(jcl->rcon_pipe); + if (BUILTINISVALID(Con_POpen)) + jcl->rcon_pipe = pCon_POpen(NULL, 0); + + if (jcl->rcon_pipe < 0) + canceled = "Unable to read console"; + else + completed = "Rcon Enabled"; + } + else + completed = "Invalid Password"; + } + } + else + canceled = "Unrecognised request"; + + rep = XML_CreateNode(NULL, "iq", NULL, ""); + XML_AddParameter(rep, "type", "result"); + XML_AddParameter(rep, "id", id); + XML_AddParameter(rep, "to", from); + cmd = XML_CreateNode(rep, "command", "http://jabber.org/protocol/commands", ""); + XML_AddParameter(cmd, "sessionid", XML_GetParameter(c, "sessionid", id)); + XML_AddParameter(cmd, "node", node); + + if (canceled) + { + if (*canceled) + { + XML_AddParameter(cmd, "status", "completed"); + XML_AddParameter(XML_CreateNode(cmd, "note", NULL, canceled), "type", "error"); + } + else + XML_AddParameter(cmd, "status", "canceled"); + } + else if (completed) + { + XML_AddParameter(cmd, "status", "completed"); + if (*completed) + XML_AddParameter(XML_CreateNode(cmd, "note", NULL, completed), "type", "info"); + } + else + { + XML_AddParameter(cmd, "status", "executing"); + +// x = XML_CreateNode(cmd, "actions", NULL, ""); +// XML_AddParameter(x, "execute", "complete"); +// XML_CreateNode(x, "complete", NULL, NULL); + +// x = XML_CreateNode(cmd, "actions", NULL, ""); +// XML_AddParameter(x, "execute", "next"); +// XML_CreateNode(x, "next", NULL, NULL); + + x = XML_CreateNode(cmd, "x", "jabber:x:data", ""); + XML_AddParameter(x, "type", "result"); + XML_CreateNode(x, "title", NULL, "Enable RCon"); + XML_CreateNode(x, "instructions", NULL, "RCon password required...\nWARNING: This is NOT end-to-end encrypted."); + fld = XML_CreateNode(x, "field", NULL, NULL); + XML_AddParameter(fld, "var", "password"); + XML_AddParameter(fld, "type", "text-private"); + XML_AddParameter(fld, "label", "Rcon Password"); + XML_CreateNode(fld, "value", NULL, ""); + } + s = XML_GenerateString(rep, false); + XML_Destroy(rep); + JCL_AddClientMessagef(jcl, s, from, id); + free(s); } #ifdef USE_GOOGLE_MAIL_NOTIFY @@ -3728,9 +4381,9 @@ void JCL_ParseIQ(jclient_t *jcl, xmltree_t *tree) } else if (!strcmp(f, "result") || !strcmp(f, "error")) { - char *id = XML_GetParameter(tree, "id", ""); + const char *id = XML_GetParameter(tree, "id", ""); struct iq_s **link, *iq; - char *from = XML_GetParameter(tree, "from", jcl->domain); + const char *from = XML_GetParameter(tree, "from", jcl->domain); unparsable = false; for (link = &jcl->pendingiqs; *link; link = &(*link)->next) { @@ -3790,13 +4443,13 @@ void JCL_ParseMessage(jclient_t *jcl, xmltree_t *tree) { xmltree_t *ot; qboolean unparsable = true; - char *f = XML_GetParameter(tree, "from", jcl->domain); - char *type = XML_GetParameter(tree, "type", "normal"); - char *ctx = f; + const char *f = XML_GetParameter(tree, "from", jcl->domain); + const char *type = XML_GetParameter(tree, "type", "normal"); + const char *ctx = f; xmltree_t *received = XML_ChildOfTree(tree, "received", 0); xmltree_t *sent = XML_ChildOfTree(tree, "sent", 0); - char *showicon = NULL; //their icon + const char *showicon = NULL; //their icon if (received && sent) return; //no, just no. @@ -3826,21 +4479,27 @@ void JCL_ParseMessage(jclient_t *jcl, xmltree_t *tree) buddy_t *b = NULL; bresource_t *br = NULL; Q_strlcpy(jcl->defaultdest, f, sizeof(jcl->defaultdest)); - JCL_FindBuddy(jcl, f, &b, &br, true); + if (b && b->btype == BT_CHATROOM && strchr(jcl->defaultdest, '/')) + *strchr(jcl->defaultdest, '/') = 0; if (b) { ctx = b->accountdomain; if (!strcmp(type, "groupchat")) - f = br->resource; - else if (b->chatroom) + { //clients can act as a MUC too, which is awkward. + if (br) + f = br->resource; + } + else if (b->btype == BT_CHATROOM) { ctx = f; //one server with multiple rooms requires that we retain resource info. - f = br->resource; + if (br) + f = br->resource; } else { - showicon = b->accountdomain; + if (BUILTINISVALID(Con_SetConsoleString)) + showicon = b->accountdomain; f = b->name; if (br) b->defaultresource = br; @@ -3850,7 +4509,8 @@ void JCL_ParseMessage(jclient_t *jcl, xmltree_t *tree) if (!strcmp(type, "error")) { - char *reason = NULL; + const char *reason = NULL; + unparsable = false; if (sent) return; ot = XML_ChildOfTree(tree, "error", 0); @@ -3879,11 +4539,11 @@ void JCL_ParseMessage(jclient_t *jcl, xmltree_t *tree) if (XML_ChildOfTree(ot, "subscription-required", 0)) reason = "subscription-required"; if (XML_ChildOfTree(ot, "undefined-condition", 0)) reason = "undefined-condition"; if (XML_ChildOfTree(ot, "unexpected-request", 0)) reason = "unexpected-request"; + reason = XML_GetChildBody(ot, "text", reason); ot = XML_ChildOfTree(tree, "body", 0); if (ot) { - unparsable = false; if (reason) XMPP_ConversationPrintf(ctx, f, "^1Error: %s (%s): ", reason, f); else @@ -3891,13 +4551,18 @@ void JCL_ParseMessage(jclient_t *jcl, xmltree_t *tree) if (f) { if (!strncmp(ot->body, "/me ", 4)) - XMPP_ConversationPrintf(ctx, f, "* ^2%s^7%s\n", ((!strcmp(jcl->localalias, ">>"))?"me":jcl->localalias), ot->body+3); + XMPP_ConversationPrintf(ctx, f, "* ^2%s^7%s\r", ((!strcmp(jcl->localalias, ">>"))?"me":jcl->localalias), ot->body+3); else - XMPP_ConversationPrintf(ctx, f, "%s\n", ot->body); + XMPP_ConversationPrintf(ctx, f, "%s\r", ot->body); } } else - XMPP_ConversationPrintf(ctx, f, "error sending message to %s\r", f); + { + if (reason) + XMPP_ConversationPrintf(ctx, f, "^1Error: %s (%s)\r", reason, f); + else + XMPP_ConversationPrintf(ctx, f, "error sending message to %s\r", f); + } return; } @@ -3953,11 +4618,20 @@ void JCL_ParseMessage(jclient_t *jcl, xmltree_t *tree) ot = XML_ChildOfTree(tree, "subject", 0); if (ot && !strcmp(type, "groupchat")) { + buddy_t *b; + JCL_FindBuddy(jcl, ctx, &b, NULL, true); + if (b && b->btype == BT_CHATROOM) + { + free(b->room_topic); + b->room_topic = strdup(ot->body); + jclient_updatebuddylist = true; + } + unparsable = false; if (sent) - XMPP_ConversationPrintf(ctx, f, "^2You^7 have set the topic to: %s\n", ot->body); + XMPP_ConversationPrintf(ctx, f, COL_NAME_US"You"COL_TEXT_US" have set the topic to: %s\n", ot->body); else - XMPP_ConversationPrintf(ctx, f, "^2%s^7 has set the topic to: %s\n", f, ot->body); + XMPP_ConversationPrintf(ctx, f, COL_NAME_THEM"%s"COL_TEXT_THEM" has set the topic to: %s\n", f, ot->body); } ot = XML_ChildOfTreeNS(tree, "http://jabber.org/protocol/pubsub#event", "event", 0); @@ -4010,14 +4684,14 @@ void JCL_ParseMessage(jclient_t *jcl, xmltree_t *tree) xmltree_t *inv = XML_ChildOfTree(ot, "invite", 0); if (inv) { - char *who = XML_GetParameter(inv, "from", jcl->domain); - char *reason = XML_GetChildBody(inv, "reason", NULL); - char *password = XML_GetChildBody(ot, "password", 0); + const char *who = XML_GetParameter(inv, "from", jcl->domain); + const char *reason = XML_GetChildBody(inv, "reason", NULL); + const char *password = XML_GetChildBody(ot, "password", 0); char link[512]; buddy_t *b; if (!sent && JCL_FindBuddy(jcl, f, &b, NULL, true)) { - if (b->chatroom) + if (b->btype == BT_CHATROOM) return; //we already know about it. don't spam. JCL_GenLink(jcl, link, sizeof(link), "mucjoin", f, NULL, password, "%s", f); // ctx = who; @@ -4036,9 +4710,9 @@ void JCL_ParseMessage(jclient_t *jcl, xmltree_t *tree) if (ot) { char link[512]; - char *chatjit = XML_GetParameter(ot, "jid", ""); - char *reason = XML_GetParameter(ot, "reason", NULL); - char *password = XML_GetParameter(ot, "password", NULL); + const char *chatjit = XML_GetParameter(ot, "jid", ""); + const char *reason = XML_GetParameter(ot, "reason", NULL); + const char *password = XML_GetParameter(ot, "password", NULL); unparsable = false; JCL_GenLink(jcl, link, sizeof(link), "mucjoin", chatjit, NULL, password, "%s", chatjit); @@ -4181,7 +4855,7 @@ unsigned int JCL_ParseCaps(jclient_t *jcl, char *account, char *resource, xmltre qboolean sift = false; qboolean ibb = false; int i = 0; - char *var; + const char *var; // XML_ConPrintTree(query, 0); while((feature = XML_ChildOfTree(query, "feature", i++))) { @@ -4408,19 +5082,21 @@ void JCL_ParsePresence(jclient_t *jcl, xmltree_t *tree) buddy_t *buddy; bresource_t *bres; - char *from = XML_GetParameter(tree, "from", jcl->domain); + const char *from = XML_GetParameter(tree, "from", jcl->domain); xmltree_t *show = XML_ChildOfTree(tree, "show", 0); xmltree_t *status = XML_ChildOfTree(tree, "status", 0); xmltree_t *quake = XML_ChildOfTree(tree, "quake", 0); - xmltree_t *muc = XML_ChildOfTreeNS(tree, "http://jabber.org/protocol/muc#user", "x", 0); + xmltree_t *mucmain = XML_ChildOfTreeNS(tree, "http://jabber.org/protocol/muc", "x", 0); + xmltree_t *mucuser = XML_ChildOfTreeNS(tree, "http://jabber.org/protocol/muc#user", "x", 0); xmltree_t *caps = XML_ChildOfTreeNS(tree, "http://jabber.org/protocol/caps", "c", 0); - char *type = XML_GetParameter(tree, "type", ""); - char *serverip = NULL; - char *servermap = NULL; + const char *type = XML_GetParameter(tree, "type", ""); + const char *serverip = NULL; + const char *servermap = NULL; char startconvo[512]; char oldbstatus[128]; char oldfstatus[128]; + //should really be using pep for this. if (quake && !strcmp(quake->xmlns, "fteqw.com:game") && (jcl->enabledcapabilities & CAP_GAMEINVITE)) { serverip = XML_GetParameter(quake, "serverip", NULL); @@ -4429,7 +5105,19 @@ void JCL_ParsePresence(jclient_t *jcl, xmltree_t *tree) jclient_updatebuddylist = true; - if (type && !strcmp(type, "subscribe")) + if (type && !strcmp(type, "error")) + { + JCL_FindBuddy(jcl, from, &buddy, &bres, false); + if (buddy && bres) + { + xmltree_t *error = XML_ChildOfTree(tree, "error", 0); + if (error->child) + { + Q_strlcpy(bres->fstatus, error->child->name, sizeof(bres->fstatus)); + } + } + } + else if (type && !strcmp(type, "subscribe")) { //they want us to let them see us. char pauth[512], pdeny[512]; @@ -4495,7 +5183,7 @@ void JCL_ParsePresence(jclient_t *jcl, xmltree_t *tree) else { xmltree_t *vcu; - char *photohash; + const char *photohash; buddy_t *me; vcu = XML_ChildOfTreeNS(tree, "vcard-temp:x:update", "x", 0); photohash = XML_GetChildBody(vcu, "photo", buddy->vcardphotohash); @@ -4521,10 +5209,10 @@ void JCL_ParsePresence(jclient_t *jcl, xmltree_t *tree) if (caps) { - char *ext = XML_GetParameter(caps, "ext", ""); //deprecated - char *ver = XML_GetParameter(caps, "ver", ""); - char *node = XML_GetParameter(caps, "node", ""); - char *hash = XML_GetParameter(caps, "hash", ""); + const char *ext = XML_GetParameter(caps, "ext", ""); //deprecated + const char *ver = XML_GetParameter(caps, "ver", ""); + const char *node = XML_GetParameter(caps, "node", ""); + const char *hash = XML_GetParameter(caps, "hash", ""); if (!bres->client_hash || strcmp(ext, bres->client_ext) || strcmp(hash, bres->client_hash) || strcmp(node, bres->client_node) || strcmp(ver, bres->client_ver)) { @@ -4542,8 +5230,28 @@ void JCL_ParsePresence(jclient_t *jcl, xmltree_t *tree) JCL_CheckClientCaps(jcl, buddy, bres); } - if (muc) + if (mucuser) { + // + // + //100=public jids + //101=affiliation changed + //102=shows unavailable members + //103=no longer shows unavailable + //104=room config changed + //110=this is you + //170=room logging enabled + //171=room logging disabled + //172=now annon + //173=now semi-anon + //201=new room created + //210=roomnick changed + //301=you're banned + //303= + //307=you're kicked + //321=removed by affiliation change + //322=not a member + //332=shutdown JCL_GenLink(jcl, startconvo, sizeof(startconvo), NULL, from, NULL, NULL, "%s", bres->resource); if (type && !strcmp(type, "unavailable")) XMPP_ConversationPrintf(buddy->name, buddy->name, "%s has left the conversation\n", bres->resource); @@ -5121,23 +5829,23 @@ void JCL_GeneratePresence(jclient_t *jcl, qboolean force) if (!*jcl->curquakeserver) JCL_AddClientMessagef(jcl, "" - "%s" - "%s" + "%+s" + "%+s" "", jcl->fulljid, prior, caps); else if (*servermap) //if we're running a server, say so JCL_AddClientMessagef(jcl, "" - "%s" + "%+s" "" - "%s" + "%+s" "" , jcl->fulljid, prior, servermap, caps); else //if we're connected to a server, say so JCL_AddClientMessagef(jcl, "" - "%s" + "%+s" "" - "%s" + "%+s" "" , jcl->fulljid, prior, jcl->curquakeserver, caps); } @@ -5185,6 +5893,7 @@ static void JCL_RegenerateBuddyList(qboolean force) bresource_t *r; int i, j; char convolink[512]; + struct c2c_s *c2c; buddy_t *sortlist[256]; int buds; @@ -5218,9 +5927,9 @@ static void JCL_RegenerateBuddyList(qboolean force) if (!jcl) continue; if (*jcl->localalias && *jcl->localalias != '>') - Con_SubPrintf(console, "\n^3%s\n", jcl->localalias); + Con_SubPrintf(console, "\n"COL_NAME_US"%s\n", jcl->localalias); else - Con_SubPrintf(console, "\n^3%s@%s\n", jcl->username, jcl->domain); + Con_SubPrintf(console, "\n"COL_NAME_US"%s@%s\n", jcl->username, jcl->domain); if (jcl->status == JCL_INACTIVE) Con_SubPrintf(console, "Not connected.\n", jcl->accountnum); else if (jcl->status == JCL_DEAD) @@ -5249,6 +5958,10 @@ static void JCL_RegenerateBuddyList(qboolean force) Con_SubPrintf(console, " %s", convolink); } */ + + for (c2c = jcl->c2c; c2c; c2c = c2c->next) + c2c->displayed = false; + if (jcl->status != JCL_ACTIVE) Con_SubPrintf(console, "\n"); else @@ -5266,8 +5979,8 @@ static void JCL_RegenerateBuddyList(qboolean force) continue; if (!b->resources) //can't be online. continue; - if (b->chatroom) - continue; //these don't count + if (b->btype == BT_UNKNOWN) + continue; //don't list people we don't actually know for (j = buds; j > 0; j--) { @@ -5285,20 +5998,22 @@ static void JCL_RegenerateBuddyList(qboolean force) bresource_t *gameres = NULL; bresource_t *voiceres = NULL; bresource_t *chatres = NULL; - struct c2c_s *c2c; b = sortlist[buds]; - for (r = b->resources; r; r = r->next) - { - if (!strcmp(r->bstatus, "offline")) - continue; + if (b->btype == BT_ROSTER) + { //gather capabilities from all of their resources + for (r = b->resources; r; r = r->next) + { + if (!strcmp(r->bstatus, "offline")) + continue; - if ((r->caps & CAP_VOICE) && (!voiceres || r->priority > voiceres->priority)) - voiceres = r; - if ((r->caps & CAP_GAMEINVITE) && (!gameres || r->priority > gameres->priority)) - gameres = r; - if (!chatres || r->priority > chatres->priority) - chatres = r; + if ((r->caps & CAP_VOICE) && (!voiceres || r->priority > voiceres->priority)) + voiceres = r; + if ((r->caps & CAP_GAMEINVITE) && (!gameres || r->priority > gameres->priority)) + gameres = r; + if (!chatres || r->priority > chatres->priority) + chatres = r; + } } if (b->defaultresource) { @@ -5309,7 +6024,7 @@ static void JCL_RegenerateBuddyList(qboolean force) gameres = r; chatres = r; } - if (chatres) + if (chatres || b->btype == BT_CHATROOM) { youarealoner = false; @@ -5346,19 +6061,25 @@ static void JCL_RegenerateBuddyList(qboolean force) Con_SubPrintf(console, "^[\\img\\xmpp/%s.png\\fbimg\\"IMG_FB_THEM"\\w\\32\\h\\32^] ", b->accountdomain); Con_SubPrintf(console, "%s", convolink); - if (*chatres->fstatus) + if (chatres && *chatres->fstatus) Con_SubPrintf(console, "\v %s", chatres->fstatus); + else if (b->btype == BT_CHATROOM && b->room_topic) + Con_SubPrintf(console, "\v %s", b->room_topic); JCL_GenLink(jcl, convolink, sizeof(convolink), "buddyopts", b->accountdomain, NULL, NULL, "^h%s^h", "Options"); - Con_SubPrintf(console, "\v %s %s", chatres->bstatus, convolink); + if (chatres) + Con_SubPrintf(console, "\v %s %s", chatres->bstatus, convolink); + else if (b->btype == BT_CHATROOM) + Con_SubPrintf(console, "\v %s %s", "chatroom", convolink); + else + Con_SubPrintf(console, "\v %s %s", "unknown", convolink); for (c2c = jcl->c2c; c2c; c2c = c2c->next) { buddy_t *peer = NULL; qboolean voice = false, video = false, server = false; int c; - JCL_FindBuddy(jcl, c2c->with, &peer, NULL, true); - if (peer == b) + if (JCL_FindBuddy(jcl, c2c->with, &peer, NULL, false) && peer == b) { for (c = 0; c < c2c->contents; c++) { @@ -5371,6 +6092,7 @@ static void JCL_RegenerateBuddyList(qboolean force) case ICEP_QWCLIENT: /*client = true;*/ break; } } + c2c->displayed = true; if (server) { JCL_GenLink(jcl, convolink, sizeof(convolink), "jdeny", c2c->with, NULL, c2c->sid, "%s", "Kick"); @@ -5408,6 +6130,37 @@ static void JCL_RegenerateBuddyList(qboolean force) if (youarealoner) Con_SubPrintf(console, " You have no friends\n"); } + + //and show any lingering c2cs with anyone not on our list. + for (c2c = jcl->c2c; c2c; c2c = c2c->next) + { + if (!c2c->displayed) + { + buddy_t *peer = NULL; + qboolean voice = false, video = false, server = false; + int c; + + for (c = 0; c < c2c->contents; c++) + { + switch(c2c->content[c].mediatype) + { + case ICEP_INVALID: break; + case ICEP_VOICE: voice = true; break; + case ICEP_VIDEO: video = true; break; + case ICEP_QWSERVER: server = true; break; + case ICEP_QWCLIENT: /*client = true;*/ break; + } + } + c2c->displayed = true; + if (server) + JCL_GenLink(jcl, convolink, sizeof(convolink), "jdeny", c2c->with, NULL, c2c->sid, "%s", "Kick"); + else if (video || voice) + JCL_GenLink(jcl, convolink, sizeof(convolink), "jdeny", c2c->with, NULL, c2c->sid, "%s", "Hang Up"); + else /*if (client)*/ + JCL_GenLink(jcl, convolink, sizeof(convolink), "jdeny", c2c->with, NULL, c2c->sid, "%s", "Disconnect"); + Con_SubPrintf(console, "%s: %s\n", c2c->with, convolink); + } + } } for (i = 0; i < sizeof(jclients)/sizeof(jclients[0]); i++) @@ -5433,10 +6186,18 @@ static void JCL_PrintBuddyList(char *console, jclient_t *jcl, qboolean all) for (b = jcl->buddies; b; b = b->next) { //if we don't actually know them, don't list them. - if (!b->friended && !b->chatroom) + if (b->btype == BT_UNKNOWN) continue; - if (!b->resources) //offline + if (b->btype == BT_CHATROOM) + { + r = b->resources; + JCL_GenLink(jcl, convolink, sizeof(convolink), NULL, b->accountdomain, r->resource, NULL, "%s", b->name); + Con_SubPrintf(console, "%s: ", convolink); + JCL_PrintBuddyStatus(console, jcl, b, r); + Con_SubPrintf(console, "\n"); + } + else if (!b->resources) //offline { if (all) { @@ -5574,6 +6335,35 @@ qintptr_t JCL_Frame(qintptr_t *args) JCL_SendIQf(jcl, NULL, "get", NULL, ""); jcl->timeout = jclient_curtime + 60*1000; } + + if (jcl->status == JCL_ACTIVE) + { + if (jcl->rcon_pipe >= 0) + { + char rcondata[64000]; + int rconsize = pNet_Recv(jcl->rcon_pipe, rcondata, sizeof(rcondata)-1); + if (rconsize > 0) + { + char *ls, *le; + rcondata[rconsize] = 0; + ls = rcondata; + while (ls) + { + le = strchr(ls, '\n'); + if (le) + *le++ = 0; + JCL_AddClientMessagef(jcl, "%s", jcl->rcon_peer, ls); + ls = le; + } + } + else if (rconsize < 0) + { + JCL_AddClientMessagef(jcl, "%s", jcl->rcon_peer, "RCON ERROR"); + pFS_Close(jcl->rcon_pipe); + jcl->rcon_pipe = -1; + } + } + } } } @@ -5592,6 +6382,7 @@ void JCL_WriteConfig(void) int i, j; qhandle_t config; struct buddyinfo_s *bi; + buddy_t *bud; //don't write the config if we're meant to be reading it. avoid wiping it if we're killed fast. if (jclient_needreadconfig) @@ -5644,6 +6435,7 @@ void JCL_WriteConfig(void) XML_CreateNode(n, "certificatedomain", "", jcl->certificatedomain); XML_CreateNode(n, "inactive", "", jcl->status == JCL_INACTIVE?"1":"0"); + //write out what optional/risky features should be enabled (like file transfers, jingle, etc) features = XML_CreateNode(n, "features", "", ""); XML_AddParameter(features, "ver", JCL_BUILD); for (j = 0; capnames[j].names; j++) @@ -5651,6 +6443,23 @@ void JCL_WriteConfig(void) XML_CreateNode(features, capnames[j].names, "", (jcl->enabledcapabilities & capnames[j].cap)?"1":"0"); } + //write a list of chatrooms that can't be saved in rosters + features = XML_CreateNode(n, "chats", "", ""); + for (bud = jcl->buddies; bud; bud = bud->next) + { + if (bud->btype == BT_CHATROOM) + { + xmltree_t *b = XML_CreateNode(features, "room", "", ""); + XML_AddParameter(b, "name", bud->accountdomain); + XML_AddParameter(b, "alias", bud->name); + XML_AddParameter(b, "nick", bud->room_nick); + XML_AddParameter(b, "password", bud->room_password); + XML_AddParameteri(b, "autojoin", bud->room_autojoin); + } + } + + //write an avatar cache + //FIXME: some sort of timeout? only people on our roster? features = XML_CreateNode(n, "buddyinfo", "", ""); for (bi = jcl->buddyinfo; bi; bi = bi->next) { @@ -5769,15 +6578,15 @@ void JCL_SendMessage(jclient_t *jcl, char *to, char *msg) Con_Printf("Can't find buddy \"%s\"\n", to); return; } - if (b->chatroom) + if (b->btype == BT_CHATROOM) { if (br) - { + { //unicast... JCL_AddClientMessagef(jcl, "", b->accountdomain, br->resource); con = to; } else - { + { //send to all recipients JCL_AddClientMessagef(jcl, "", b->accountdomain); con = b->name; } @@ -5796,12 +6605,14 @@ void JCL_SendMessage(jclient_t *jcl, char *to, char *msg) } JCL_AddClientMessage(jcl, markup, XML_Markup(msg, markup, sizeof(markup)) - markup); JCL_AddClientMessageString(jcl, ""); - if (b->chatroom && !br) + if (b->btype == BT_CHATROOM && !br) return; if (!strncmp(msg, "/me ", 4)) - XMPP_ConversationPrintf(con, title, "* ^5%s^7"COLOURYELLOW"%s\n", ((!strcmp(jcl->localalias, ">>"))?"me":jcl->localalias), msg+3); + XMPP_ConversationPrintf(con, title, "* "COL_NAME_US"%s^7"COL_TEXT_US"%s\n", ((!strcmp(jcl->localalias, ">>"))?"me":jcl->localalias), msg+3); + else if (b->btype == BT_ROSTER && BUILTINISVALID(Con_SetConsoleString)) + XMPP_ConversationPrintf(con, title, "^[\\img\\xmpp/%s.png\\fbimg\\"IMG_FB_US"\\w\\32\\h\\32^]"COL_NAME_US"%s^7:\v"COL_TEXT_US"%s\n", jcl->barejid, jcl->localalias, msg); else - XMPP_ConversationPrintf(con, title, "^[\\img\\xmpp/%s.png\\fbimg\\"IMG_FB_US"\\w\\32\\h\\32^]^5%s^7:\v"COLOURYELLOW"%s\n", jcl->barejid, jcl->localalias, msg); + XMPP_ConversationPrintf(con, title, COL_NAME_US"%s^7: "COL_TEXT_US"%s\n", jcl->localalias, msg); } void JCL_AttentionMessage(jclient_t *jcl, char *to, char *msg) { @@ -5841,30 +6652,68 @@ void JCL_AttentionMessage(jclient_t *jcl, char *to, char *msg) if (msg) { if (!strncmp(msg, "/me ", 4)) - Con_SubPrintf(b->accountdomain, "*^5%s^7"COLOURYELLOW"%s\n", ((!strcmp(jcl->localalias, ">>"))?"me":jcl->localalias), msg+3); + Con_SubPrintf(b->accountdomain, "*"COL_NAME_US"%s^7"COL_TEXT_US"%s\n", ((!strcmp(jcl->localalias, ">>"))?"me":jcl->localalias), msg+3); else - Con_SubPrintf(b->accountdomain, "^5%s^7: "COLOURYELLOW"%s\n", jcl->localalias, msg); + Con_SubPrintf(b->accountdomain, COL_NAME_US"%s^7: "COL_TEXT_US"%s\n", jcl->localalias, msg); } } +static qboolean JCL_IsChatroom(jclient_t *jcl, char *room) +{ + buddy_t *b; + + if (!jcl) + return false; + + if (!JCL_FindBuddy(jcl, room, &b, NULL, true)) + return false; + + return b->btype == BT_CHATROOM; +} + //server may be null, in which case its expected to be folded into room. -void JCL_JoinMUCChat(jclient_t *jcl, char *room, char *server, char *myhandle, char *password) +void JCL_JoinMUCChat(jclient_t *jcl, const char *room, const char *server, const char *myhandle, const char *password) { char caps[512]; char roomserverhandle[512]; buddy_t *b; - bresource_t *r; - if (!myhandle) - myhandle = jcl->username; +// bresource_t *r; if (server) - Q_snprintf(roomserverhandle, sizeof(roomserverhandle), "%s@%s/%s", room, server, myhandle); + Q_snprintf(roomserverhandle, sizeof(roomserverhandle), "%s@%s", room, server); else - Q_snprintf(roomserverhandle, sizeof(roomserverhandle), "%s/%s", room, myhandle); - if (!JCL_FindBuddy(jcl, roomserverhandle, &b, &r, true)) + Q_snprintf(roomserverhandle, sizeof(roomserverhandle), "%s", room); + if (!JCL_FindBuddy(jcl, roomserverhandle, &b, NULL, true)) return; - b->chatroom = true; + if (!myhandle) + { + if (b->room_nick) + myhandle = b->room_nick; + else + myhandle = jcl->username; + } + b->btype = BT_CHATROOM; buildcapsvcardpresence(jcl, caps, sizeof(caps)); - JCL_AddClientMessagef(jcl, "%s%s", roomserverhandle, password, caps); + //FIXME: check for errors + JCL_AddClientMessagef(jcl, + "" + "" + "%s" + //"" + "" + "%+s" + "" + , roomserverhandle, myhandle, password?password:"", caps); + + if (b->room_nick != myhandle) + { + free(b->room_nick); + b->room_nick = myhandle?strdup(myhandle):NULL; + } + if (b->room_password != password) + { + free(b->room_password); + b->room_password = myhandle?strdup(password):NULL; + } } void XMPP_Menu_Password(jclient_t *acc) @@ -5922,7 +6771,7 @@ void XMPP_Menu_Connect(void) void JCL_Command(int accid, char *console) { char imsg[8192]; - char arg[6][1024]; + char arg[6][1024], *argstart[6]; char *msg; int i; char nname[256]; @@ -5938,14 +6787,151 @@ void JCL_Command(int accid, char *console) for (i = 0; i < 6; i++) { if (!msg) + { + argstart[i] = ""; *arg[i] = 0; + } else + { + while (*msg == ' ') + msg++; + argstart[i] = msg; msg = JCL_ParseOut(msg, arg[i], sizeof(arg[i])); + } } if (arg[0][0] == '/' && arg[0][1] != '/' && strcmp(arg[0]+1, "me")) { - if (!strcmp(arg[0]+1, "open") || !strcmp(arg[0]+1, "connect") || !strcmp(arg[0]+1, "autoopen") || !strcmp(arg[0]+1, "autoconnect") || !strcmp(arg[0]+1, "plainopen") || !strcmp(arg[0]+1, "plainconnect") || !strcmp(arg[0]+1, "tlsopen") || !strcmp(arg[0]+1, "tlsconnect")) + if (*console && !strcmp(arg[0]+1, "ban") && JCL_IsChatroom(jcl, console)) + { + char *roomnick = arg[1]; + char *comment = arg[2]; + char *jid; + if (strchr(roomnick, '@')) + jid = roomnick; + else + { + Con_TrySubPrint(console, "Unable to translate roomnicks. Please use their bare jid.\n"); + return; //FIXME: translate from roomnick to real barejid + } + + JCL_SendIQf(jcl, NULL, "set", console, + "" + "" + "%s" + "" + "" + , jid, comment + ); + } + else if (*console && !strcmp(arg[0]+1, "invite") && JCL_IsChatroom(jcl, console)) + { + char *jid = arg[1]; + char *reason = arg[2]; + JCL_AddClientMessagef(jcl, + "" + "" + "" + "%s" + "" + "" + "" + , console, jid, reason + ); + } + else if (*console && !strcmp(arg[0]+1, "join") && JCL_IsChatroom(jcl, console)) + { + char *room = arg[1]; + char *nick = ""; + char *pass = arg[2]; + JCL_AddClientMessagef(jcl, + "" + "" + "%s" + "" + "" + , room, nick, pass + ); + } + else if (*console && !strcmp(arg[0]+1, "kick") && JCL_IsChatroom(jcl, console)) + { + char *roomnick = arg[1]; + char *comment = arg[2]; + JCL_SendIQf(jcl, NULL, "set", console, + "" + "" + "%s" + "" + "" + ,roomnick, comment + ); + } + else if (*console && !strcmp(arg[0]+1, "msg") && JCL_IsChatroom(jcl, console)) + { + char *whom = arg[1]; + char *msgtext = argstart[2]; + JCL_AddClientMessagef(jcl, + "" + "%s" + "" + , console, whom, msgtext + ); + } + else if (*console && !strcmp(arg[0]+1, "nick") && JCL_IsChatroom(jcl, console)) + { //FIXME: needs escape + char *mynewnick = arg[1]; + JCL_AddClientMessagef(jcl, + "" + , console, mynewnick + ); + } + else if (*console && !strcmp(arg[0]+1, "part") && JCL_IsChatroom(jcl, console)) + { //FIXME: escapes + buddy_t *b; + char *myroomnick = ""; + char *comment = argstart[1]; + + if (JCL_FindBuddy(jcl, console, &b, NULL, false)) + if (b && b->ourselves) + myroomnick = b->ourselves->resource; + + JCL_AddClientMessagef(jcl, + "" + "%s" + "" + , console, myroomnick, comment + ); + + if (b && b->btype == BT_CHATROOM) + JCL_ForgetBuddy(jcl, b, NULL); + } + else if (*console && !strcmp(arg[0]+1, "topic") && JCL_IsChatroom(jcl, console)) + { //FIXME: escapes + char *newtopic = argstart[1]; + JCL_AddClientMessagef(jcl, + "" + "%s" + "" + , console, newtopic + ); + } + else if (*console && !strcmp(arg[0]+1, "userlist") && JCL_IsChatroom(jcl, console)) + { + buddy_t *b; + bresource_t *r; + if (JCL_FindBuddy(jcl, console, &b, NULL, false)) + { + Con_SubPrintf(console, "Users on channel %s\n", console); + for (r = b->resources; r; r = r->next) + { + Con_SubPrintf(console, " %s:", r->resource); + JCL_PrintBuddyStatus(console, jcl, b, r); + Con_SubPrintf(console, "\n"); + } + Con_SubPrintf(console, "\n"); + } + } + else if (!strcmp(arg[0]+1, "open") || !strcmp(arg[0]+1, "connect") || !strcmp(arg[0]+1, "autoopen") || !strcmp(arg[0]+1, "autoconnect") || !strcmp(arg[0]+1, "plainopen") || !strcmp(arg[0]+1, "plainconnect") || !strcmp(arg[0]+1, "tlsopen") || !strcmp(arg[0]+1, "tlsconnect")) { //tlsconnect is 'old'. int tls; if (!*arg[1]) @@ -6106,16 +7092,25 @@ void JCL_Command(int accid, char *console) } else if (!strcmp(arg[0]+1, "unfriend")) { - //FIXME: validate the name. deal with xml markup. + buddy_t *b; + if (JCL_FindBuddy(jcl, arg[1], &b, NULL, false)) + { + if (b->btype == BT_CHATROOM) + JCL_ForgetBuddy(jcl, b, NULL); + else if (b->btype == BT_ROSTER) + { + //hide from em + JCL_AddClientMessagef(jcl, "", arg[1]); - //hide from em - JCL_AddClientMessagef(jcl, "", arg[1]); + //stop looking for em + JCL_AddClientMessagef(jcl, "", arg[1]); - //stop looking for em - JCL_AddClientMessagef(jcl, "", arg[1]); - - //stop listing em - JCL_SendIQf(jcl, NULL, "set", NULL, "", arg[1]); + //stop listing em + JCL_SendIQf(jcl, NULL, "set", NULL, "", arg[1]); + } + else + JCL_AddClientMessagef(jcl, "", arg[1]); + } } #ifdef JINGLE else if (!strcmp(arg[0]+1, "join")) @@ -6165,7 +7160,10 @@ void JCL_Command(int accid, char *console) #endif else if (!strcmp(arg[0]+1, "joinchatroom") || !strcmp(arg[0]+1, "muc") || !strcmp(arg[0]+1, "joinmuc")) { - JCL_JoinMUCChat(jcl, arg[1], arg[2], arg[3], arg[4]); + if (!*arg[1]) + Con_SubPrintf(console, "xmpp %s room@server roomnick password\n"); + else + JCL_JoinMUCChat(jcl, arg[1], NULL, *arg[2]?arg[2]:NULL, arg[3]); } else if (!strcmp(arg[0]+1, "leavechatroom") || !strcmp(arg[0]+1, "leavemuc")) { @@ -6175,7 +7173,6 @@ void JCL_Command(int accid, char *console) Q_snprintf(roomserverhandle, sizeof(roomserverhandle), "%s@%s/%s", arg[1], arg[2], arg[3]); if (JCL_FindBuddy(jcl, roomserverhandle, &b, &r, false)) { - b->chatroom = true; JCL_AddClientMessagef(jcl, "", roomserverhandle); JCL_ForgetBuddy(jcl, b, NULL); } diff --git a/plugins/jabber/jingle.c b/plugins/jabber/jingle.c index 49104bac9..279c530a5 100644 --- a/plugins/jabber/jingle.c +++ b/plugins/jabber/jingle.c @@ -1,6 +1,6 @@ #include "xmpp.h" #ifdef JINGLE -static struct c2c_s *JCL_JingleAddContentToSession(jclient_t *jcl, struct c2c_s *c2c, char *with, bresource_t *bres, qboolean creator, char *sid, char *cname, int method, int mediatype) +static struct c2c_s *JCL_JingleAddContentToSession(jclient_t *jcl, struct c2c_s *c2c, const char *with, bresource_t *bres, qboolean creator, const char *sid, const char *cname, int method, int mediatype) { struct icestate_s *ice = NULL; char generatedname[64]; @@ -186,7 +186,7 @@ enum JE_UNKNOWNSESSION, JE_UNSUPPORTEDINFO }; -static void JCL_JingleError(jclient_t *jcl, xmltree_t *tree, char *from, char *id, int type) +static void JCL_JingleError(jclient_t *jcl, xmltree_t *tree, const char *from, const char *id, int type) { switch(type) { @@ -522,7 +522,7 @@ void JCL_JingleTimeouts(jclient_t *jcl, qboolean killall) } } -void JCL_Join(jclient_t *jcl, char *target, char *sid, qboolean allow, int protocol) +void JCL_Join(jclient_t *jcl, const char *target, const char *sid, qboolean allow, int protocol) { struct c2c_s *c2c = NULL, **link; char autotarget[256]; @@ -618,7 +618,7 @@ void JCL_Join(jclient_t *jcl, char *target, char *sid, qboolean allow, int proto } } -static void JCL_JingleParsePeerPorts(jclient_t *jcl, struct c2c_s *c2c, xmltree_t *inj, char *from, char *sid) +static void JCL_JingleParsePeerPorts(jclient_t *jcl, struct c2c_s *c2c, xmltree_t *inj, const char *from, const char *sid) { xmltree_t *incontent; xmltree_t *intransport; @@ -626,7 +626,7 @@ static void JCL_JingleParsePeerPorts(jclient_t *jcl, struct c2c_s *c2c, xmltree_ struct icecandinfo_s rem; struct icestate_s *ice; int i, contid; - char *cname; + const char *cname; if (!c2c->sid) return; @@ -665,7 +665,7 @@ static void JCL_JingleParsePeerPorts(jclient_t *jcl, struct c2c_s *c2c, xmltree_ for (i = 0; (incandidate = XML_ChildOfTree(intransport, "candidate", i)); i++) { - char *s; + const char *s; memset(&rem, 0, sizeof(rem)); Q_strlcpy(rem.addr, XML_GetParameter(incandidate, "ip", ""), sizeof(rem.addr)); Q_strlcpy(rem.candidateid, XML_GetParameter(incandidate, "id", ""), sizeof(rem.candidateid)); @@ -869,12 +869,12 @@ static qboolean JCL_JingleHandleInitiate_GoogleSession(jclient_t *jcl, xmltree_t return true; } #endif -static struct c2c_s *JCL_JingleHandleInitiate(jclient_t *jcl, xmltree_t *inj, char *from) +static struct c2c_s *JCL_JingleHandleInitiate(jclient_t *jcl, xmltree_t *inj, const char *from) { - char *sid = XML_GetParameter(inj, "sid", ""); + const char *sid = XML_GetParameter(inj, "sid", ""); qboolean okay; - char *initiator; + const char *initiator; struct c2c_s *c2c = NULL; int mt = ICEP_INVALID; @@ -894,12 +894,12 @@ static struct c2c_s *JCL_JingleHandleInitiate(jclient_t *jcl, xmltree_t *inj, ch for (i = 0; ; i++) { xmltree_t *incontent = XML_ChildOfTree(inj, "content", i); - char *cname = XML_GetParameter(incontent, "name", ""); + const char *cname = XML_GetParameter(incontent, "name", ""); xmltree_t *intransport = XML_ChildOfTree(incontent, "transport", 0); xmltree_t *indescription = XML_ChildOfTree(incontent, "description", 0); - char *transportxmlns = intransport?intransport->xmlns:""; - char *descriptionxmlns = indescription?indescription->xmlns:""; - char *descriptionmedia = XML_GetParameter(indescription, "media", ""); + const char *transportxmlns = intransport?intransport->xmlns:""; + const char *descriptionxmlns = indescription?indescription->xmlns:""; + const char *descriptionmedia = XML_GetParameter(indescription, "media", ""); if (!incontent) break; @@ -907,7 +907,7 @@ static struct c2c_s *JCL_JingleHandleInitiate(jclient_t *jcl, xmltree_t *inj, ch if (incontent && !strcmp(descriptionmedia, MEDIATYPE_QUAKE) && !strcmp(descriptionxmlns, QUAKEMEDIAXMLNS)) { - char *host = XML_GetParameter(indescription, "host", "you"); + const char *host = XML_GetParameter(indescription, "host", "you"); if (!strcmp(host, "you")) mt = ICEP_QWSERVER; else if (!strcmp(host, "me")) @@ -938,9 +938,9 @@ static struct c2c_s *JCL_JingleHandleInitiate(jclient_t *jcl, xmltree_t *inj, ch //chuck it at the engine and see what sticks. at least one must... while((payload = XML_ChildOfTree(indescription, "payload-type", i++))) { - char *name = XML_GetParameter(payload, "name", ""); - char *clock = XML_GetParameter(payload, "clockrate", ""); - char *id = XML_GetParameter(payload, "id", ""); + const char *name = XML_GetParameter(payload, "name", ""); + const char *clock = XML_GetParameter(payload, "clockrate", ""); + const char *id = XML_GetParameter(payload, "id", ""); char parm[64]; char val[64]; //note: the engine will ignore codecs it does not support, returning false. @@ -1028,7 +1028,7 @@ static qboolean JCL_JingleHandleSessionTerminate(jclient_t *jcl, xmltree_t *tree free(c2c); return true; } -static qboolean JCL_JingleHandleSessionAccept(jclient_t *jcl, xmltree_t *tree, char *from, struct c2c_s *c2c, buddy_t *b) +static qboolean JCL_JingleHandleSessionAccept(jclient_t *jcl, xmltree_t *tree, const char *from, struct c2c_s *c2c, buddy_t *b) { //peer accepted our session //make sure it actually was ours, and not theirs. sneaky sneaky. @@ -1053,7 +1053,7 @@ static qboolean JCL_JingleHandleSessionAccept(jclient_t *jcl, xmltree_t *tree, c } else { - char *responder = XML_GetParameter(tree, "responder", from); + const char *responder = XML_GetParameter(tree, "responder", from); int c; if (strcmp(responder, from)) { @@ -1134,10 +1134,10 @@ qboolean JCL_HandleGoogleSession(jclient_t *jcl, xmltree_t *tree, char *from, ch return true; } #endif -qboolean JCL_ParseJingle(jclient_t *jcl, xmltree_t *tree, char *from, char *id) +qboolean JCL_ParseJingle(jclient_t *jcl, xmltree_t *tree, const char *from, const char *id) { - char *action = XML_GetParameter(tree, "action", ""); - char *sid = XML_GetParameter(tree, "sid", ""); + const char *action = XML_GetParameter(tree, "action", ""); + const char *sid = XML_GetParameter(tree, "sid", ""); struct c2c_s *c2c = NULL, **link; buddy_t *b; diff --git a/plugins/jabber/sift.c b/plugins/jabber/sift.c index 993b59bc7..83f85b201 100644 --- a/plugins/jabber/sift.c +++ b/plugins/jabber/sift.c @@ -210,7 +210,7 @@ void XMPP_FT_AcceptFile(jclient_t *jcl, int fileid, qboolean accept) static qboolean XMPP_FT_IBBChunked(jclient_t *jcl, xmltree_t *x, struct iq_s *iq) { - char *from = XML_GetParameter(x, "from", jcl->domain); + const char *from = XML_GetParameter(x, "from", jcl->domain); struct ft_s *ft = iq->usrptr, **link, *v; for (link = &jcl->ft; (v=*link); link = &(*link)->next) { @@ -267,7 +267,7 @@ static qboolean XMPP_FT_IBBChunked(jclient_t *jcl, xmltree_t *x, struct iq_s *iq static qboolean XMPP_FT_IBBBegun(jclient_t *jcl, xmltree_t *x, struct iq_s *iq) { struct ft_s *ft = iq->usrptr, **link, *v; - char *from = XML_GetParameter(x, "from", jcl->domain); + const char *from = XML_GetParameter(x, "from", jcl->domain); for (link = &jcl->ft; (v=*link); link = &(*link)->next) { if (v == ft && !strcmp(ft->with, from)) @@ -296,7 +296,7 @@ static qboolean XMPP_FT_IBBBegun(jclient_t *jcl, xmltree_t *x, struct iq_s *iq) qboolean XMPP_FT_OfferAcked(jclient_t *jcl, xmltree_t *x, struct iq_s *iq) { struct ft_s *ft = iq->usrptr, **link, *v; - char *from = XML_GetParameter(x, "from", jcl->domain); + const char *from = XML_GetParameter(x, "from", jcl->domain); for (link = &jcl->ft; (v=*link); link = &(*link)->next) { if (v == ft && !strcmp(ft->with, from)) @@ -328,7 +328,7 @@ qboolean XMPP_FT_OfferAcked(jclient_t *jcl, xmltree_t *x, struct iq_s *iq) return false; } -void XMPP_FT_SendFile(jclient_t *jcl, char *console, char *to, char *fname) +void XMPP_FT_SendFile(jclient_t *jcl, const char *console, const char *to, const char *fname) { xmltree_t *xsi, *xfile, *c; struct ft_s *ft; @@ -371,7 +371,7 @@ void XMPP_FT_SendFile(jclient_t *jcl, char *console, char *to, char *fname) JCL_SendIQNode(jcl, XMPP_FT_OfferAcked, "set", to, xsi, true)->usrptr = ft; } -qboolean XMPP_FT_ParseIQSet(jclient_t *jcl, char *iqfrom, char *iqid, xmltree_t *tree) +qboolean XMPP_FT_ParseIQSet(jclient_t *jcl, const char *iqfrom, const char *iqid, xmltree_t *tree) { xmltree_t *ot; @@ -384,17 +384,17 @@ qboolean XMPP_FT_ParseIQSet(jclient_t *jcl, char *iqfrom, char *iqid, xmltree_t { xmltree_t *c; struct ft_s *ft; - char *sid = XML_GetParameter(ot, "sid", ""); + const char *sid = XML_GetParameter(ot, "sid", ""); for (ft = jcl->ft; ft; ft = ft->next) { if (!strcmp(ft->sid, sid) && !strcmp(ft->with, iqfrom)) { if (ft->allowed && !ft->begun && ft->transmitting == false) { - char *jid; - char *host; + const char *jid; + const char *host; int port; - char *mode = XML_GetParameter(ot, "mode", "tcp"); + const char *mode = XML_GetParameter(ot, "mode", "tcp"); int i; if (strcmp(mode, "tcp")) break; @@ -432,9 +432,9 @@ qboolean XMPP_FT_ParseIQSet(jclient_t *jcl, char *iqfrom, char *iqid, xmltree_t if (ot) { struct ft_s *ft; - char *sid = XML_GetParameter(ot, "sid", ""); + const char *sid = XML_GetParameter(ot, "sid", ""); int blocksize = atoi(XML_GetParameter(ot, "block-size", "4096")); //technically this is required. - char *stanza = XML_GetParameter(ot, "stanza", "iq"); + const char *stanza = XML_GetParameter(ot, "stanza", "iq"); for (ft = jcl->ft; ft; ft = ft->next) { if (!strcmp(ft->sid, sid) && !strcmp(ft->with, iqfrom)) @@ -480,7 +480,7 @@ qboolean XMPP_FT_ParseIQSet(jclient_t *jcl, char *iqfrom, char *iqid, xmltree_t if (ot) { struct ft_s **link, *ft; - char *sid = XML_GetParameter(ot, "sid", ""); + const char *sid = XML_GetParameter(ot, "sid", ""); for (link = &jcl->ft; *link; link = &(*link)->next) { ft = *link; @@ -522,7 +522,7 @@ qboolean XMPP_FT_ParseIQSet(jclient_t *jcl, char *iqfrom, char *iqid, xmltree_t if (ot) { char block[65536]; - char *sid = XML_GetParameter(ot, "sid", ""); + const char *sid = XML_GetParameter(ot, "sid", ""); // unsigned short seq = atoi(XML_GetParameter(ot, "seq", "0")); int blocksize; struct ft_s *ft; @@ -549,16 +549,16 @@ qboolean XMPP_FT_ParseIQSet(jclient_t *jcl, char *iqfrom, char *iqid, xmltree_t ot = XML_ChildOfTreeNS(tree, "http://jabber.org/protocol/si", "si", 0); if (ot) { - char *profile = XML_GetParameter(ot, "profile", ""); + const char *profile = XML_GetParameter(ot, "profile", ""); if (!strcmp(profile, "http://jabber.org/protocol/si/profile/file-transfer")) { // char *mimetype = XML_GetParameter(ot, "mime-type", "text/plain"); - char *sid = XML_GetParameter(ot, "id", ""); + const char *sid = XML_GetParameter(ot, "id", ""); xmltree_t *file = XML_ChildOfTreeNS(ot, "http://jabber.org/protocol/si/profile/file-transfer", "file", 0); - char *fname = XML_GetParameter(file, "name", "file.txt"); + const char *fname = XML_GetParameter(file, "name", "file.txt"); // char *date = XML_GetParameter(file, "date", ""); - char *md5hash = XML_GetParameter(file, "hash", ""); + const char *md5hash = XML_GetParameter(file, "hash", ""); int fsize = strtoul(XML_GetParameter(file, "size", "0"), NULL, 0); // char *desc = XML_GetChildBody(file, "desc", ""); char authlink[512]; diff --git a/plugins/jabber/xml.c b/plugins/jabber/xml.c index 5282dd60a..e991a3758 100644 --- a/plugins/jabber/xml.c +++ b/plugins/jabber/xml.c @@ -7,7 +7,7 @@ void (*Con_TrySubPrint)(const char *conname, const char *message); void XML_Destroy(xmltree_t *t); -char *XML_GetParameter(xmltree_t *t, char *paramname, char *def) +const char *XML_GetParameter(xmltree_t *t, const char *paramname, const char *def) { xmlparams_t *p; if (t) @@ -18,11 +18,11 @@ char *XML_GetParameter(xmltree_t *t, char *paramname, char *def) } return def; } -void XML_AddParameter(xmltree_t *t, char *paramname, char *value) +void XML_AddParameter(xmltree_t *t, const char *paramname, const char *value) { xmlparams_t *p = malloc(sizeof(xmlparams_t)); Q_strlcpy(p->name, paramname, sizeof(p->name)); - Q_strlcpy(p->val, value, sizeof(p->val)); + Q_strlcpy(p->val, value?value:"", sizeof(p->val)); if (t->params) //reverse insert { @@ -38,17 +38,24 @@ void XML_AddParameter(xmltree_t *t, char *paramname, char *value) t->params = p; } } -void XML_AddParameteri(xmltree_t *t, char *paramname, int value) +void XML_AddParameteri(xmltree_t *t, const char *paramname, int value) { char svalue[64]; Q_snprintf(svalue, sizeof(svalue), "%i", value); XML_AddParameter(t, paramname, svalue); } -xmltree_t *XML_CreateNode(xmltree_t *parent, char *name, char *xmlns, char *body) +xmltree_t *XML_CreateNode(xmltree_t *parent, const char *name, const char *xmlns, const char *body) { - int bodylen = strlen(body); + int bodylen; struct subtree_s *node = malloc(sizeof(*node)); + if (!body) + body = ""; + if (!xmlns) + xmlns = ""; + + bodylen = strlen(body); + //clear out links node->params = NULL; node->child = NULL; @@ -101,7 +108,7 @@ const struct }; //converts < to < etc. //returns the end of d. -char *XML_Markup(char *s, char *d, int dlen) +char *XML_Markup(const char *s, char *d, int dlen) { int i; dlen--; @@ -248,7 +255,7 @@ char *XML_GenerateString(xmltree_t *root, qboolean readable) buf_cat(&buf, "", 1); return buf.buf; } -xmltree_t *XML_Parse(char *buffer, int *startpos, int maxpos, qboolean headeronly, char *defaultnamespace) +xmltree_t *XML_Parse(const char *buffer, int *startpos, int maxpos, qboolean headeronly, const char *defaultnamespace) { xmlparams_t *p; xmltree_t *child; @@ -258,8 +265,8 @@ xmltree_t *XML_Parse(char *buffer, int *startpos, int maxpos, qboolean headeronl int bodymax = 0; int pos, i; char *tagend; - char *tagstart; - char *ns; + const char *tagstart; + const char *ns; char token[1024]; pos = *startpos; while (buffer[pos] >= '\0' && buffer[pos] <= ' ') @@ -337,11 +344,9 @@ skippedcomment: ns = strchr(token, ':'); if (ns) { - *ns = 0; - ns++; - memcpy(ret->xmlns, "xmlns:", 6); - Q_strlcpy(ret->xmlns+6, token, sizeof(ret->xmlns)-6); + Q_strlncpy(ret->xmlns+6, token, sizeof(ret->xmlns)-6, ns-token); + ns++; Q_strlcpy(ret->name, ns, sizeof(ret->name)); } else @@ -589,7 +594,7 @@ void XML_Destroy(xmltree_t *t) free(t); } -xmltree_t *XML_ChildOfTree(xmltree_t *t, char *name, int childnum) +xmltree_t *XML_ChildOfTree(xmltree_t *t, const char *name, int childnum) { if (t) { @@ -604,7 +609,7 @@ xmltree_t *XML_ChildOfTree(xmltree_t *t, char *name, int childnum) } return NULL; } -xmltree_t *XML_ChildOfTreeNS(xmltree_t *t, char *xmlns, char *name, int childnum) +xmltree_t *XML_ChildOfTreeNS(xmltree_t *t, const char *xmlns, const char *name, int childnum) { if (t) { @@ -619,7 +624,7 @@ xmltree_t *XML_ChildOfTreeNS(xmltree_t *t, char *xmlns, char *name, int childnum } return NULL; } -char *XML_GetChildBody(xmltree_t *t, char *paramname, char *def) +const char *XML_GetChildBody(xmltree_t *t, const char *paramname, const char *def) { xmltree_t *c = XML_ChildOfTree(t, paramname, 0); if (c) @@ -627,7 +632,7 @@ char *XML_GetChildBody(xmltree_t *t, char *paramname, char *def) return def; } -void XML_ConPrintTree(xmltree_t *t, char *subconsole, int indent) +void XML_ConPrintTree(xmltree_t *t, const char *subconsole, int indent) { int start, c, chunk; struct buf_ctx buf = {NULL, 0, 0}; @@ -653,7 +658,7 @@ void XML_ConPrintTree(xmltree_t *t, char *subconsole, int indent) } -static void XML_SkipWhite(char *msg, int *pos, int max) +static void XML_SkipWhite(const char *msg, int *pos, int max) { while (*pos < max && ( msg[*pos] == ' ' || @@ -663,7 +668,7 @@ static void XML_SkipWhite(char *msg, int *pos, int max) )) *pos+=1; } -static qboolean XML_ParseString(char *msg, int *pos, int max, char *out, int outlen) +static qboolean XML_ParseString(const char *msg, int *pos, int max, char *out, int outlen) { *out = 0; if (*pos < max && msg[*pos] == '\"') @@ -712,7 +717,7 @@ static qboolean XML_ParseString(char *msg, int *pos, int max, char *out, int out } return false; } -xmltree_t *XML_FromJSON(xmltree_t *t, char *name, char *json, int *jsonpos, int jsonlen) +xmltree_t *XML_FromJSON(xmltree_t *t, const char *name, const char *json, int *jsonpos, int jsonlen) { char child[4096]; XML_SkipWhite(json, jsonpos, jsonlen); diff --git a/plugins/jabber/xml.h b/plugins/jabber/xml.h index c034fb24e..f5e3e89a2 100644 --- a/plugins/jabber/xml.h +++ b/plugins/jabber/xml.h @@ -20,18 +20,18 @@ typedef struct subtree_s -char *XML_GetParameter(xmltree_t *t, char *paramname, char *def); -void XML_AddParameter(xmltree_t *t, char *paramname, char *value); -void XML_AddParameteri(xmltree_t *t, char *paramname, int value); -xmltree_t *XML_CreateNode(xmltree_t *parent, char *name, char *xmlns, char *body); -char *XML_Markup(char *s, char *d, int dlen); +const char *XML_GetParameter(xmltree_t *t, const char *paramname, const char *def); +void XML_AddParameter(xmltree_t *t, const char *paramname, const char *value); +void XML_AddParameteri(xmltree_t *t, const char *paramname, int value); +xmltree_t *XML_CreateNode(xmltree_t *parent, const char *name, const char *xmlns, const char *body); +char *XML_Markup(const char *s, char *d, int dlen); void XML_Unmark(char *s); char *XML_GenerateString(xmltree_t *root, qboolean readable); -xmltree_t *XML_Parse(char *buffer, int *startpos, int maxpos, qboolean headeronly, char *defaultnamespace); +xmltree_t *XML_Parse(const char *buffer, int *startpos, int maxpos, qboolean headeronly, const char *defaultnamespace); void XML_Destroy(xmltree_t *t); -xmltree_t *XML_ChildOfTree(xmltree_t *t, char *name, int childnum); -xmltree_t *XML_ChildOfTreeNS(xmltree_t *t, char *xmlns, char *name, int childnum); -char *XML_GetChildBody(xmltree_t *t, char *paramname, char *def); -void XML_ConPrintTree(xmltree_t *t, char *subconsole, int indent); +xmltree_t *XML_ChildOfTree(xmltree_t *t, const char *name, int childnum); +xmltree_t *XML_ChildOfTreeNS(xmltree_t *t, const char *xmlns, const char *name, int childnum); +const char *XML_GetChildBody(xmltree_t *t, const char *paramname, const char *def); +void XML_ConPrintTree(xmltree_t *t, const char *subconsole, int indent); -xmltree_t *XML_FromJSON(xmltree_t *t, char *name, char *json, int *jsonpos, int jsonlen); +xmltree_t *XML_FromJSON(xmltree_t *t, const char *name, const char *json, int *jsonpos, int jsonlen); diff --git a/plugins/jabber/xmpp.h b/plugins/jabber/xmpp.h index 95cff9236..6e89c5816 100644 --- a/plugins/jabber/xmpp.h +++ b/plugins/jabber/xmpp.h @@ -76,12 +76,22 @@ typedef struct bresource_s typedef struct buddy_s { bresource_t *resources; - bresource_t *defaultresource; //this is the one that last replied + bresource_t *defaultresource; //this is the one that last replied (must be null for chatrooms, so we only talk to the room in general) + bresource_t *ourselves; //this is set back to ourselves when in a chatroom int defaulttimestamp; qboolean askfriend; qboolean friended; - qboolean chatroom; //chatrooms are bizzare things that need special handling. qboolean vcardphotochanged; + enum { + BT_UNKNOWN, //this buddy isn't known... + BT_ROSTER, //this is a friend! or at least on our list of potential friends anyway. + BT_CHATROOM //we're treating this 'guy' as a MUC, each of their resources is a different person. which is weird. + } btype; + + qboolean room_autojoin; + char *room_nick; + char *room_password; + char *room_topic; char name[256]; char vcardphotohash[41]; @@ -110,6 +120,9 @@ typedef struct jclient_s qhandle_t socket; + qhandle_t rcon_pipe; //contains console prints + char rcon_peer[256]; //the name of the guy currently receiving console prints + //we buffer output for times when the outgoing socket is full. //mostly this only happens at the start of the connection when the socket isn't actually open yet. char *outbuf; @@ -226,6 +239,7 @@ typedef struct jclient_s qboolean accepted; //connection is going qboolean creator; //true if we're the creator. unsigned int peercaps; + qboolean displayed; //temp flag for displaying jingle sessions with people that are not on our buddy list for whatever reasons struct { @@ -313,32 +327,33 @@ extern icefuncs_t *piceapi; #endif -qboolean NET_DNSLookup_SRV(char *host, char *out, int outlen); +qboolean NET_DNSLookup_SRV(const char *host, char *out, int outlen); //xmpp functionality -struct iq_s *JCL_SendIQNode(jclient_t *jcl, qboolean (*callback) (jclient_t *jcl, xmltree_t *tree, struct iq_s *iq), char *iqtype, char *target, xmltree_t *node, qboolean destroynode); -void JCL_AddClientMessagef(jclient_t *jcl, char *fmt, ...); -void JCL_AddClientMessageString(jclient_t *jcl, char *msg); -qboolean JCL_FindBuddy(jclient_t *jcl, char *jid, buddy_t **buddy, bresource_t **bres, qboolean create); +struct iq_s *JCL_SendIQNode(jclient_t *jcl, qboolean (*callback) (jclient_t *jcl, xmltree_t *tree, struct iq_s *iq), const char *iqtype, const char *target, xmltree_t *node, qboolean destroynode); +void JCL_AddClientMessagef(jclient_t *jcl, const char *fmt, ...); +void JCL_AddClientMessageString(jclient_t *jcl, const char *msg); +qboolean JCL_FindBuddy(jclient_t *jcl, const char *jid, buddy_t **buddy, bresource_t **bres, qboolean create); +void JCL_ForgetBuddy(jclient_t *jcl, buddy_t *buddy, bresource_t *bres); //quake functionality -void JCL_GenLink(jclient_t *jcl, char *out, int outlen, char *action, char *context, char *contextres, char *sid, char *txtfmt, ...); -void Con_SubPrintf(const char *subname, char *format, ...); +void JCL_GenLink(jclient_t *jcl, char *out, int outlen, const char *action, const char *context, const char *contextres, const char *sid, const char *txtfmt, ...); +void Con_SubPrintf(const char *subname, const char *format, ...); void XMPP_ConversationPrintf(const char *context, const char *title, char *format, ...); //jingle functions -void JCL_Join(jclient_t *jcl, char *target, char *sid, qboolean allow, int protocol); +void JCL_Join(jclient_t *jcl, const char *target, const char *sid, qboolean allow, int protocol); void JCL_JingleTimeouts(jclient_t *jcl, qboolean killall); //jingle iq message handlers -qboolean JCL_HandleGoogleSession(jclient_t *jcl, xmltree_t *tree, char *from, char *id); -qboolean JCL_ParseJingle(jclient_t *jcl, xmltree_t *tree, char *from, char *id); +qboolean JCL_HandleGoogleSession(jclient_t *jcl, xmltree_t *tree, const char *from, const char *id); +qboolean JCL_ParseJingle(jclient_t *jcl, xmltree_t *tree, const char *from, const char *id); void XMPP_FT_AcceptFile(jclient_t *jcl, int fileid, qboolean accept); qboolean XMPP_FT_OfferAcked(jclient_t *jcl, xmltree_t *x, struct iq_s *iq); -qboolean XMPP_FT_ParseIQSet(jclient_t *jcl, char *iqfrom, char *iqid, xmltree_t *tree); -void XMPP_FT_SendFile(jclient_t *jcl, char *console, char *to, char *fname); +qboolean XMPP_FT_ParseIQSet(jclient_t *jcl, const char *iqfrom, const char *iqid, xmltree_t *tree); +void XMPP_FT_SendFile(jclient_t *jcl, const char *console, const char *to, const char *fname); void XMPP_FT_Frame(jclient_t *jcl); -void Base64_Add(char *s, int len); +void Base64_Add(const char *s, int len); char *Base64_Finish(void); -int Base64_Decode(char *out, int outlen, char *src, int srclen); +int Base64_Decode(char *out, int outlen, const char *src, int srclen); diff --git a/plugins/plugin.c b/plugins/plugin.c index bed27f0c2..da7dfa2be 100644 --- a/plugins/plugin.c +++ b/plugins/plugin.c @@ -59,6 +59,9 @@ BUILTINR(void *, Plug_GetNativePointer, (const char *funcname)); BUILTIN(void, Con_Print, (const char *text)); //on to main console. #undef ARGNAMES +#define ARGNAMES ,conname,flags +BUILTINR(qhandle_t, Con_POpen, (const char *conname, unsigned int flags)); +#undef ARGNAMES #define ARGNAMES ,conname,text BUILTIN(void, Con_SubPrint, (const char *conname, const char *text)); //on to named sub console (creating it too). #undef ARGNAMES @@ -528,6 +531,7 @@ void Plug_InitStandardBuiltins(void) CHECKBUILTIN(Con_SetConsoleFloat); CHECKBUILTIN(Con_GetConsoleString); CHECKBUILTIN(Con_SetConsoleString); + CHECKBUILTIN(Con_POpen); } #ifndef Q3_VM diff --git a/plugins/plugin.h b/plugins/plugin.h index e68b74b1d..c6f7c9391 100644 --- a/plugins/plugin.h +++ b/plugins/plugin.h @@ -140,6 +140,7 @@ extern "C" { #endif extern qintptr_t (QDECL *plugin_syscall)( qintptr_t arg, ... ); +void Q_strlncpy(char *d, const char *s, int sizeofd, int lenofs); void Q_strlcpy(char *d, const char *s, int n); void Q_strlcat(char *d, const char *s, int n); int Q_snprintf(char *buffer, size_t maxlen, const char *format, ...); @@ -198,6 +199,7 @@ EBUILTIN(void *, Plug_GetNativePointer, (const char *funcname)); #endif EBUILTIN(void, Con_Print, (const char *text)); //on to main console. +EBUILTIN(qhandle_t, Con_POpen, (const char *conname, unsigned int flags)); EBUILTIN(void, Con_SubPrint, (const char *subname, const char *text)); //on to sub console. EBUILTIN(void, Con_RenameSub, (const char *oldname, const char *newname)); //rename a console. EBUILTIN(int, Con_IsActive, (const char *conname)); diff --git a/plugins/qi/qi.c b/plugins/qi/qi.c index 203f0c1fe..59889f531 100644 --- a/plugins/qi/qi.c +++ b/plugins/qi/qi.c @@ -235,13 +235,13 @@ void QI_RefreshMapList(qboolean forcedisplay) for (file = thedatabase->child; file; file = file->sibling) { - char *id = XML_GetParameter(file, "id", "unnamed"); - char *rating = XML_GetParameter(file, "rating", ""); + const char *id = XML_GetParameter(file, "id", "unnamed"); + const char *rating = XML_GetParameter(file, "rating", ""); int ratingnum = atoi(rating); - char *author = XML_GetChildBody(file, "author", "unknown"); - char *desc = XML_GetChildBody(file, "description", ""); - char *type; - char *date; + const char *author = XML_GetChildBody(file, "author", "unknown"); + const char *desc = XML_GetChildBody(file, "description", ""); + const char *type; + const char *date; int year, month, day; int startmapnum, i; char ratingtext[65]; @@ -397,7 +397,7 @@ static xmltree_t *QI_FindArchive(const char *name) xmltree_t *file; for (file = thedatabase->child; file; file = file->sibling) { - char *id = XML_GetParameter(file, "id", "unnamed"); + const char *id = XML_GetParameter(file, "id", "unnamed"); if (strcmp(file->name, "file")) continue; //erk? @@ -408,7 +408,7 @@ static xmltree_t *QI_FindArchive(const char *name) } static void QI_AddPackages(xmltree_t *qifile) { - char *id; + const char *id; char extra[1024]; char clean[512]; unsigned int i; diff --git a/plugins/qvm_api.c b/plugins/qvm_api.c index ca2dc8798..400502c3a 100644 --- a/plugins/qvm_api.c +++ b/plugins/qvm_api.c @@ -567,6 +567,22 @@ int rand(void) } #endif +void Q_strlncpy(char *d, const char *s, int sizeofd, int lenofs) +{ + int i; + sizeofd--; + if (sizeofd < 0) + return; //this could be an error + + for (i=0; lenofs-- > 0; i++) + { + if (i == sizeofd) + break; + *d++ = *s++; + } + *d='\0'; +} + void Q_strlcpy(char *d, const char *s, int n) { int i;