redesigned tempstring system, now uses a fixed size buffer based on the prvm_tempstri...
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Wed, 24 Jan 2007 04:15:45 +0000 (04:15 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Wed, 24 Jan 2007 04:15:45 +0000 (04:15 +0000)
audited PRVM_G_STRING use in the engine (eliminated many NULL checks in functions because it never returns NULL)
added DP_QC_UNLIMITEDTEMPSTRINGS extension to indicate this new tempstring system's wonderous features (not yet documented)

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@6746 d7cf8633-e32d-0410-b094-e92efae38249

14 files changed:
clvm_cmds.c
csprogs.c
mvm_cmds.c
progs.h
progsvm.h
prvm_cmds.c
prvm_cmds.h
prvm_edict.c
prvm_exec.c
prvm_execprogram.h
sv_phys.c
sv_user.c
svvm_cmds.c
todo

index dad081c..936d570 100644 (file)
@@ -65,6 +65,7 @@ char *vm_cl_extensions =
 "DP_QC_GETTAGINFO "
 "DP_QC_MINMAXBOUND "
 "DP_QC_MULTIPLETEMPSTRINGS "
+"DP_QC_UNLIMITEDTEMPSTRINGS "
 "DP_QC_RANDOMVEC "
 "DP_QC_SINCOSSQRTPOW "
 //"DP_QC_STRINGBUFFERS "       //[515]: not needed ?
@@ -297,10 +298,8 @@ void VM_CL_traceline (void)
 // #19 void(string s) precache_sound
 void VM_CL_precache_sound (void)
 {
-       const char *n;
        VM_SAFEPARMCOUNT(1, VM_CL_precache_sound);
-       n = PRVM_G_STRING(OFS_PARM0);
-       S_PrecacheSound(n, true, false);
+       S_PrecacheSound(PRVM_G_STRING(OFS_PARM0), true, false);
 }
 
 // #20 void(string s) precache_model
@@ -1001,17 +1000,17 @@ void VM_CL_getstati (void)
 void VM_CL_getstats (void)
 {
        int i;
-       char *t;
+       char t[17];
        VM_SAFEPARMCOUNT(1, VM_CL_getstats);
        i = (int)PRVM_G_FLOAT(OFS_PARM0);
        if(i < 0 || i > MAX_CL_STATS-4)
        {
+               PRVM_G_INT(OFS_RETURN) = OFS_NULL;
                VM_Warning("VM_CL_getstats: index>MAX_CL_STATS-4 or index<0\n");
                return;
        }
-       t = VM_GetTempString();
-       strlcpy(t, (char*)&cl.stats[i], 16);
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(t);
+       strlcpy(t, (char*)&cl.stats[i], sizeof(t));
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(t);
 }
 
 //#333 void(entity e, float mdlindex) setmodelindex (EXT_CSQC)
@@ -1050,7 +1049,7 @@ void VM_CL_modelnameforindex (void)
 
        VM_SAFEPARMCOUNT(1, VM_CL_modelnameforindex);
 
-       PRVM_G_INT(OFS_RETURN) = 0;
+       PRVM_G_INT(OFS_RETURN) = OFS_NULL;
        model = CSQC_GetModelByIndex((int)PRVM_G_FLOAT(OFS_PARM0));
        PRVM_G_INT(OFS_RETURN) = model ? PRVM_SetEngineString(model->name) : 0;
 }
@@ -1058,11 +1057,9 @@ void VM_CL_modelnameforindex (void)
 //#335 float(string effectname) particleeffectnum (EXT_CSQC)
 void VM_CL_particleeffectnum (void)
 {
-       const char      *n;
        int                     i;
        VM_SAFEPARMCOUNT(1, VM_CL_particleeffectnum);
-       n = PRVM_G_STRING(OFS_PARM0);
-       i = CL_ParticleEffectIndexForName(n);
+       i = CL_ParticleEffectIndexForName(PRVM_G_STRING(OFS_PARM0));
        if (i == 0)
                i = -1;
        PRVM_G_FLOAT(OFS_RETURN) = i;
@@ -1110,11 +1107,8 @@ void VM_CL_centerprint (void)
 //#342 string(float keynum) getkeybind (EXT_CSQC)
 void VM_CL_getkeybind (void)
 {
-       int i;
-
        VM_SAFEPARMCOUNT(1, VM_CL_getkeybind);
-       i = (int)PRVM_G_FLOAT(OFS_PARM0);
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(Key_GetBind(i));
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(Key_GetBind((int)PRVM_G_FLOAT(OFS_PARM0)));
 }
 
 //#343 void(float usecursor) setcursormode (EXT_CSQC)
@@ -1169,7 +1163,6 @@ void VM_CL_getplayerkey (void)
        int                     i;
        char            t[128];
        const char      *c;
-       char            *temp;
 
        VM_SAFEPARMCOUNT(2, VM_CL_getplayerkey);
 
@@ -1189,12 +1182,12 @@ void VM_CL_getplayerkey (void)
        else
                if(!strcasecmp(c, "frags"))
                        sprintf(t, "%i", cl.scores[i].frags);
-//     else
-//             if(!strcasecmp(c, "ping"))
-//                     sprintf(t, "%i", cl.scores[i].ping);
-//     else
-//             if(!strcasecmp(c, "entertime"))
-//                     sprintf(t, "%f", cl.scores[i].entertime);
+       else
+               if(!strcasecmp(c, "ping"))
+                       sprintf(t, "%i", cl.scores[i].qw_ping);
+       else
+               if(!strcasecmp(c, "entertime"))
+                       sprintf(t, "%f", cl.scores[i].qw_entertime);
        else
                if(!strcasecmp(c, "colors"))
                        sprintf(t, "%i", cl.scores[i].colors);
@@ -1209,9 +1202,7 @@ void VM_CL_getplayerkey (void)
                        sprintf(t, "%i", i+1);
        if(!t[0])
                return;
-       temp = VM_GetTempString();
-       strlcpy(temp, t, VM_STRINGTEMP_LENGTH);
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(temp);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(t);
 }
 
 //#349 float() isdemo (EXT_CSQC)
@@ -1305,15 +1296,7 @@ void VM_CL_ReadAngle (void)
 //#366 string() readstring (EXT_CSQC)
 void VM_CL_ReadString (void)
 {
-       char *t, *s;
-       t = VM_GetTempString();
-       s = MSG_ReadString();
-       PRVM_G_INT(OFS_RETURN) = 0;
-       if(s)
-       {
-               strlcpy(t, s, VM_STRINGTEMP_LENGTH);
-               PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(t);
-       }
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(MSG_ReadString());
 }
 
 //#367 float() readfloat (EXT_CSQC)
@@ -1758,10 +1741,10 @@ void VM_CL_getsurfacetexture(void)
 {
        model_t *model;
        msurface_t *surface;
-       PRVM_G_INT(OFS_RETURN) = 0;
+       PRVM_G_INT(OFS_RETURN) = OFS_NULL;
        if (!(model = CSQC_GetModelFromEntity(PRVM_G_EDICT(OFS_PARM0))) || !(surface = cl_getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
                return;
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(surface->texture->name);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(surface->texture->name);
 }
 
 // #438 float(entity e, vector p) getsurfacenearpoint
@@ -2193,15 +2176,12 @@ static int Is_Text_Color (char c, char t)
 void VM_uncolorstring (void) //#170
 {
        const char      *in;
-       char            *out;
+       char            out[VM_STRINGTEMP_LENGTH];
        int                     k = 0, i = 0;
 
        VM_SAFEPARMCOUNT(1, VM_uncolorstring);
        in = PRVM_G_STRING(OFS_PARM0);
-       if(!in)
-               PRVM_ERROR ("VM_uncolorstring: %s: NULL\n", PRVM_NAME);
        VM_CheckEmptyString (in);
-       out = VM_GetTempString();
 
        while (in[k])
        {
@@ -2215,6 +2195,7 @@ void VM_uncolorstring (void) //#170
                ++k;
                ++i;
        }
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(out);
 }
 
 void VM_CL_selecttraceline (void)
@@ -2241,8 +2222,6 @@ void VM_charindex (void)
 {
        const char *s;
        s = PRVM_G_STRING(OFS_PARM0);
-       if(!s)
-               return;
        if((unsigned)PRVM_G_FLOAT(OFS_PARM1) > strlen(s))
                return;
        PRVM_G_FLOAT(OFS_RETURN) = (unsigned char)s[(int)PRVM_G_FLOAT(OFS_PARM1)];
@@ -2251,13 +2230,12 @@ void VM_charindex (void)
 //#223 string(float c, ...) chr2str (FTE_STRINGS)
 void VM_chr2str (void)
 {
-       char    *t;
+       char    t[128];
        int             i;
-       t = VM_GetTempString();
-       for(i=0;i<prog->argc;i++)
+       for(i = 0;i < prog->argc && i < (int)sizeof(t) - 1;i++)
                t[i] = (unsigned char)PRVM_G_FLOAT(OFS_PARM0+i*3);
        t[i] = 0;
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(t);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(t);
 }
 
 //#228 float(string s1, string s2, float len) strncmp (FTE_STRINGS)
index 7f3f428..19aef18 100644 (file)
--- a/csprogs.c
+++ b/csprogs.c
@@ -322,15 +322,19 @@ qboolean CL_VM_UpdateView (void)
        return true;
 }
 
+extern sizebuf_t vm_tempstringsbuf;
 qboolean CL_VM_ConsoleCommand (const char *cmd)
 {
+       int restorevm_tempstringsbuf_cursize;
        qboolean r;
        if(!csqc_loaded)
                return false;
        CSQC_BEGIN
                *prog->time = cl.time;
-               PRVM_G_INT(OFS_PARM0) = PRVM_SetEngineString(cmd);
+               restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
+               PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(cmd);
                PRVM_ExecuteProgram (prog->globals.client->CSQC_ConsoleCommand, CL_F_CONSOLECOMMAND);
+               vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
                r = CSQC_RETURNVAL;
        CSQC_END
        return r;
@@ -358,6 +362,7 @@ qboolean CL_VM_Parse_TempEntity (void)
 
 void CL_VM_Parse_StuffCmd (const char *msg)
 {
+       int restorevm_tempstringsbuf_cursize;
        if(msg[0] == 'c')
        if(msg[1] == 's')
        if(msg[2] == 'q')
@@ -382,17 +387,22 @@ void CL_VM_Parse_StuffCmd (const char *msg)
        }
        CSQC_BEGIN
                *prog->time = cl.time;
-               PRVM_G_INT(OFS_PARM0) = PRVM_SetEngineString(msg);
+               restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
+               PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(msg);
                PRVM_ExecuteProgram ((func_t)(CSQC_Parse_StuffCmd - prog->functions), CL_F_PARSE_STUFFCMD);
+               vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
        CSQC_END
 }
 
 static void CL_VM_Parse_Print (const char *msg)
 {
+       int restorevm_tempstringsbuf_cursize;
        CSQC_BEGIN
                *prog->time = cl.time;
-               PRVM_G_INT(OFS_PARM0) = PRVM_SetEngineString(msg);
+               restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
+               PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(msg);
                PRVM_ExecuteProgram ((func_t)(CSQC_Parse_Print - prog->functions), CL_F_PARSE_PRINT);
+               vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
        CSQC_END
 }
 
@@ -424,6 +434,7 @@ void CSQC_AddPrintText (const char *msg)
 
 void CL_VM_Parse_CenterPrint (const char *msg)
 {
+       int restorevm_tempstringsbuf_cursize;
        if(!csqc_loaded || !CSQC_Parse_CenterPrint)
        {
                SCR_CenterPrint((char*)msg);
@@ -431,8 +442,10 @@ void CL_VM_Parse_CenterPrint (const char *msg)
        }
        CSQC_BEGIN
                *prog->time = cl.time;
-               PRVM_G_INT(OFS_PARM0) = PRVM_SetEngineString(msg);
+               restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
+               PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(msg);
                PRVM_ExecuteProgram ((func_t)(CSQC_Parse_CenterPrint - prog->functions), CL_F_PARSE_CENTERPRINT);
+               vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
        CSQC_END
 }
 
index 4984873..251bbb8 100644 (file)
@@ -7,7 +7,8 @@ char *vm_m_extensions =
 "DP_CINEMATIC_DPV "
 "DP_MENU_EXTRESPONSEPACKET "
 "DP_QC_ASINACOSATANATAN2TAN "
-"DP_QC_STRINGCOLORFUNCTIONS";
+"DP_QC_STRINGCOLORFUNCTIONS "
+"DP_QC_UNLIMITEDTEMPSTRINGS";
 
 /*
 =========
@@ -181,9 +182,6 @@ void VM_M_callfunction(void)
 
        s = PRVM_G_STRING(OFS_PARM0 + (prog->argc - 1));
 
-       if(!s)
-               PRVM_ERROR("VM_M_callfunction: null string !");
-
        VM_CheckEmptyString(s);
 
        func = PRVM_ED_FindFunction(s);
@@ -225,9 +223,6 @@ void VM_M_isfunction(void)
 
        s = PRVM_G_STRING(OFS_PARM0);
 
-       if(!s)
-               PRVM_ERROR("VM_M_isfunction: null string !");
-
        VM_CheckEmptyString(s);
 
        func = PRVM_ED_FindFunction(s);
@@ -305,7 +300,7 @@ void M_FindKeysForCommand(const char *command, int *keys);
 void VM_M_findkeysforcommand(void)
 {
        const char *cmd;
-       char *ret;
+       char ret[VM_STRINGTEMP_LENGTH];
        int keys[NUMKEYS];
        int i;
 
@@ -315,14 +310,13 @@ void VM_M_findkeysforcommand(void)
 
        VM_CheckEmptyString(cmd);
 
-       (ret = VM_GetTempString())[0] = 0;
-
        M_FindKeysForCommand(cmd, keys);
 
+       ret[0] = 0;
        for(i = 0; i < NUMKEYS; i++)
-               strlcat(ret, va(" \'%i\'", keys[i]), VM_STRINGTEMP_LENGTH);
+               strlcat(ret, va(" \'%i\'", keys[i]), sizeof(ret));
 
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(ret);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(ret);
 }
 
 /*
@@ -412,8 +406,6 @@ void VM_M_setserverlistmaskstring( void )
 
        VM_SAFEPARMCOUNT( 4, VM_M_setserverlistmaskstring );
        str = PRVM_G_STRING( OFS_PARM2 );
-       if( !str )
-               PRVM_ERROR( "VM_M_setserverlistmaskstring: null string passed!" );
 
        masknr = (int)PRVM_G_FLOAT( OFS_PARM0 );
        if( masknr >= 0 && masknr <= SERVERLIST_ANDMASKCOUNT )
@@ -534,7 +526,7 @@ void VM_M_getserverliststring(void)
 
        VM_SAFEPARMCOUNT(2, VM_M_getserverliststring);
 
-       PRVM_G_INT(OFS_RETURN) = 0;
+       PRVM_G_INT(OFS_RETURN) = OFS_NULL;
 
        hostnr = (int)PRVM_G_FLOAT(OFS_PARM1);
 
@@ -586,7 +578,7 @@ void VM_M_getserverlistnumber(void)
 
        VM_SAFEPARMCOUNT(2, VM_M_getserverliststring);
 
-       PRVM_G_INT(OFS_RETURN) = 0;
+       PRVM_G_INT(OFS_RETURN) = OFS_NULL;
 
        hostnr = (int)PRVM_G_FLOAT(OFS_PARM1);
 
@@ -789,9 +781,7 @@ void VM_M_getextresponse (void)
        VM_SAFEPARMCOUNT(0,VM_argv);
 
        if (net_extresponse_count <= 0)
-       {
-               PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(NULL);
-       }
+               PRVM_G_INT(OFS_RETURN) = OFS_NULL;
        else
        {
                int first;
diff --git a/progs.h b/progs.h
index 1d6e6b9..6a96837 100644 (file)
--- a/progs.h
+++ b/progs.h
@@ -387,6 +387,7 @@ void ED_PrintNum (int ent);
 const char *PRVM_GetString(int num);
 int PR_SetQCString(const char *s);
 int PRVM_SetEngineString(const char *s);
+int PRVM_SetTempString(const char *s);
 char *PR_AllocString(int bufferlength);
 void PR_FreeString(char *s);
 
index 7e6250a..673d366 100644 (file)
--- a/progsvm.h
+++ b/progsvm.h
@@ -504,6 +504,7 @@ void PRVM_ED_PrintNum (int ent);
 
 const char *PRVM_GetString(int num);
 int PRVM_SetEngineString(const char *s);
+int PRVM_SetTempString(const char *s);
 int PRVM_AllocString(size_t bufferlength, char **pointer);
 void PRVM_FreeString(int num);
 
index 6ff3db2..f7c5e71 100644 (file)
@@ -25,20 +25,10 @@ void VM_Warning(const char *fmt, ...)
 //============================================================================
 // Common
 
-// temp string handling
-// LordHavoc: added this to semi-fix the problem of using many ftos calls in a print
-static char vm_string_temp[VM_STRINGTEMP_BUFFERS][VM_STRINGTEMP_LENGTH];
-static int vm_string_tempindex = 0;
-
-// TODO: (move vm_files and vm_fssearchlist to prvm_prog_t struct)
+// TODO DONE: move vm_files and vm_fssearchlist to prvm_prog_t struct
 // TODO: move vm_files and vm_fssearchlist back [9/13/2006 Black]
-char *VM_GetTempString(void)
-{
-       char *s;
-       s = vm_string_temp[vm_string_tempindex];
-       vm_string_tempindex = (vm_string_tempindex + 1) % VM_STRINGTEMP_BUFFERS;
-       return s;
-}
+// TODO: (move vm_files and vm_fssearchlist to prvm_prog_t struct again) [2007-01-23 LordHavoc]
+// TODO: will this war ever end? [2007-01-23 LordHavoc]
 
 void VM_CheckEmptyString (const char *s)
 {
@@ -508,25 +498,14 @@ const string      VM_cvar_string (string)
 */
 void VM_cvar_string(void)
 {
-       char *out;
        const char *name;
-       const char *cvar_string;
        VM_SAFEPARMCOUNT(1,VM_cvar_string);
 
        name = PRVM_G_STRING(OFS_PARM0);
 
-       if(!name)
-               PRVM_ERROR("VM_cvar_string: %s: null string", PRVM_NAME);
-
        VM_CheckEmptyString(name);
 
-       out = VM_GetTempString();
-
-       cvar_string = Cvar_VariableString(name);
-
-       strlcpy(out, cvar_string, VM_STRINGTEMP_LENGTH);
-
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(out);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(Cvar_VariableString(name));
 }
 
 
@@ -539,25 +518,14 @@ const string      VM_cvar_defstring (string)
 */
 void VM_cvar_defstring (void)
 {
-       char *out;
        const char *name;
-       const char *cvar_string;
        VM_SAFEPARMCOUNT(1,VM_cvar_string);
 
        name = PRVM_G_STRING(OFS_PARM0);
 
-       if(!name)
-               PRVM_ERROR("VM_cvar_defstring: %s: null string", PRVM_NAME);
-
        VM_CheckEmptyString(name);
 
-       out = VM_GetTempString();
-
-       cvar_string = Cvar_VariableDefString(name);
-
-       strlcpy(out, cvar_string, VM_STRINGTEMP_LENGTH);
-
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(out);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(Cvar_VariableDefString(name));
 }
 /*
 =================
@@ -605,18 +573,17 @@ string    ftos(float)
 void VM_ftos (void)
 {
        float v;
-       char *s;
+       char s[128];
 
        VM_SAFEPARMCOUNT(1, VM_ftos);
 
        v = PRVM_G_FLOAT(OFS_PARM0);
 
-       s = VM_GetTempString();
        if ((float)((int)v) == v)
                sprintf(s, "%i", (int)v);
        else
                sprintf(s, "%f", v);
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(s);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(s);
 }
 
 /*
@@ -647,13 +614,12 @@ string    vtos(vector)
 
 void VM_vtos (void)
 {
-       char *s;
+       char s[512];
 
        VM_SAFEPARMCOUNT(1,VM_vtos);
 
-       s = VM_GetTempString();
        sprintf (s, "'%5.1f %5.1f %5.1f'", PRVM_G_VECTOR(OFS_PARM0)[0], PRVM_G_VECTOR(OFS_PARM0)[1], PRVM_G_VECTOR(OFS_PARM0)[2]);
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(s);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(s);
 }
 
 /*
@@ -666,13 +632,12 @@ string    etos(entity)
 
 void VM_etos (void)
 {
-       char *s;
+       char s[128];
 
        VM_SAFEPARMCOUNT(1, VM_etos);
 
-       s = VM_GetTempString();
        sprintf (s, "entity %i", PRVM_G_EDICTNUM(OFS_PARM0));
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(s);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(s);
 }
 
 /*
@@ -795,8 +760,6 @@ void VM_find (void)
        // LordHavoc: apparently BloodMage does a find(world, weaponmodel, "") and
        // expects it to find all the monsters, so we must be careful to support
        // searching for ""
-       if (!s)
-               s = "";
 
        for (e++ ; e < prog->num_edicts ; e++)
        {
@@ -888,8 +851,6 @@ void VM_findchain (void)
        // LordHavoc: apparently BloodMage does a find(world, weaponmodel, "") and
        // expects it to find all the monsters, so we must be careful to support
        // searching for ""
-       if (!s)
-               s = "";
 
        ent = PRVM_NEXT_EDICT(prog->edicts);
        for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
@@ -1221,8 +1182,6 @@ changelevel(string map)
 */
 void VM_changelevel (void)
 {
-       const char      *s;
-
        VM_SAFEPARMCOUNT(1, VM_changelevel);
 
        if(!sv.active)
@@ -1236,8 +1195,7 @@ void VM_changelevel (void)
                return;
        svs.changelevel_issued = true;
 
-       s = PRVM_G_STRING(OFS_PARM0);
-       Cbuf_AddText (va("changelevel %s\n",s));
+       Cbuf_AddText (va("changelevel %s\n",PRVM_G_STRING(OFS_PARM0)));
 }
 
 /*
@@ -1691,7 +1649,7 @@ string    fgets(float fhandle)
 void VM_fgets(void)
 {
        int c, end;
-       static char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_STRINGTEMP_LENGTH];
        int filenum;
 
        VM_SAFEPARMCOUNT(1,VM_fgets);
@@ -1727,9 +1685,9 @@ void VM_fgets(void)
        if (developer.integer >= 100)
                Con_Printf("fgets: %s: %s\n", PRVM_NAME, string);
        if (c >= 0 || end)
-               PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(string);
+               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string);
        else
-               PRVM_G_INT(OFS_RETURN) = 0;
+               PRVM_G_INT(OFS_RETURN) = OFS_NULL;
 }
 
 /*
@@ -1776,15 +1734,9 @@ float    strlen(string s)
 //float(string s) strlen = #114; // returns how many characters are in a string
 void VM_strlen(void)
 {
-       const char *s;
-
        VM_SAFEPARMCOUNT(1,VM_strlen);
 
-       s = PRVM_G_STRING(OFS_PARM0);
-       if (s)
-               PRVM_G_FLOAT(OFS_RETURN) = strlen(s);
-       else
-               PRVM_G_FLOAT(OFS_RETURN) = 0;
+       PRVM_G_FLOAT(OFS_RETURN) = strlen(PRVM_G_STRING(OFS_PARM0));
 }
 
 // DRESK - Decolorized String
@@ -1798,7 +1750,7 @@ string    strdecolorize(string s)
 // string (string s) strdecolorize = #472; // returns the passed in string with color codes stripped
 void VM_strdecolorize(void)
 {
-       char *szNewString;
+       char szNewString[VM_STRINGTEMP_LENGTH];
        const char *szString;
        size_t nCnt;
        int nPos;
@@ -1812,7 +1764,6 @@ void VM_strdecolorize(void)
        // Prepare Strings
        VM_SAFEPARMCOUNT(1,VM_strdecolorize);
        szString = PRVM_G_STRING(OFS_PARM0);
-       szNewString = VM_GetTempString();
 
        while(!bFinished)
        { // Traverse through String
@@ -1849,7 +1800,7 @@ void VM_strdecolorize(void)
                        nPos = nPos + 1;
        }
 
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(szNewString);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(szNewString);
 }
 
 // DRESK - String Length (not counting color codes)
@@ -1875,48 +1826,44 @@ void VM_strlennocol(void)
        VM_SAFEPARMCOUNT(1,VM_strlennocol);
 
        szString = PRVM_G_STRING(OFS_PARM0);
-       if(szString)
-       { // Valid String
-               while(!bFinished)
-               { // Count Characters
-                       // SV_BroadcastPrintf("Position '%d'; Character '%c'; Length '%d'\n", nPos, szString[nPos], nCnt);
-
-                       if( szString[nPos] == '\n' || szString[nPos] == '\r' || szString[nPos] <= 0)
-                       { // String End Found
-                               // SV_BroadcastPrintf("Found End of String at '%d'\n", nPos);
-                               bFinished = 1;
+
+       while(!bFinished)
+       { // Count Characters
+               // SV_BroadcastPrintf("Position '%d'; Character '%c'; Length '%d'\n", nPos, szString[nPos], nCnt);
+
+               if( szString[nPos] == '\n' || szString[nPos] == '\r' || szString[nPos] <= 0)
+               { // String End Found
+                       // SV_BroadcastPrintf("Found End of String at '%d'\n", nPos);
+                       bFinished = 1;
+               }
+               else
+               if( szString[nPos] == STRING_COLOR_TAG)
+               { // Color Code Located
+                       if( szString[nPos + 1] == STRING_COLOR_TAG)
+                       { // Increment Length; Skip Color Code
+                               nCnt = nCnt + 1;
+                               nPos = nPos + 1;
                        }
                        else
-                       if( szString[nPos] == STRING_COLOR_TAG)
-                       { // Color Code Located
-                               if( szString[nPos + 1] == STRING_COLOR_TAG)
-                               { // Increment Length; Skip Color Code
-                                       nCnt = nCnt + 1;
-                                       nPos = nPos + 1;
-                               }
-                               else
-                               if( szString[nPos + 1] >= '0' && szString[nPos + 1] <= '9' )
-                               { // Color Code Found; Increment Position
-                                       // SV_BroadcastPrintf("Found Color Codes at '%d'\n", nPos);
-                                       nPos = nPos + 1;
-                               }
-                               else
-                               { // Unknown Color Code; Increment Length!
-                                       nPos = nPos + 1;
-                                       nCnt = nCnt + 1;
-                               }
+                       if( szString[nPos + 1] >= '0' && szString[nPos + 1] <= '9' )
+                       { // Color Code Found; Increment Position
+                               // SV_BroadcastPrintf("Found Color Codes at '%d'\n", nPos);
+                               nPos = nPos + 1;
                        }
                        else
-                               // Increment String Length
+                       { // Unknown Color Code; Increment Length!
+                               nPos = nPos + 1;
                                nCnt = nCnt + 1;
-
-                       // Increment Position
-                       nPos = nPos + 1;
+                       }
                }
-               PRVM_G_FLOAT(OFS_RETURN) = nCnt;
+               else
+                       // Increment String Length
+                       nCnt = nCnt + 1;
+
+               // Increment Position
+               nPos = nPos + 1;
        }
-       else
-               PRVM_G_FLOAT(OFS_RETURN) = 0;
+       PRVM_G_FLOAT(OFS_RETURN) = nCnt;
 }
 
 /*
@@ -1931,14 +1878,13 @@ string strcat(string,string,...[string])
 // and returns as a tempstring
 void VM_strcat(void)
 {
-       char *s;
+       char s[VM_STRINGTEMP_LENGTH];
 
        if(prog->argc < 1)
                PRVM_ERROR("VM_strcat wrong parameter count (min. 1 expected ) !");
 
-       s = VM_GetTempString();
-       VM_VarString(0, s, VM_STRINGTEMP_LENGTH);
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(s);
+       VM_VarString(0, s, sizeof(s));
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(s);
 }
 
 /*
@@ -1954,21 +1900,18 @@ void VM_substring(void)
 {
        int i, start, length;
        const char *s;
-       char *string;
+       char string[VM_STRINGTEMP_LENGTH];
 
        VM_SAFEPARMCOUNT(3,VM_substring);
 
-       string = VM_GetTempString();
        s = PRVM_G_STRING(OFS_PARM0);
        start = (int)PRVM_G_FLOAT(OFS_PARM1);
        length = (int)PRVM_G_FLOAT(OFS_PARM2);
-       if (!s)
-               s = "";
        for (i = 0;i < start && *s;i++, s++);
-       for (i = 0;i < VM_STRINGTEMP_LENGTH - 1 && *s && i < length;i++, s++)
+       for (i = 0;i < (int)sizeof(string) - 1 && *s && i < length;i++, s++)
                string[i] = *s;
        string[i] = 0;
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(string);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string);
 }
 
 /*
@@ -2066,29 +2009,35 @@ float tokenize(string s)
 //this function originally written by KrimZon, made shorter by LordHavoc
 //20040203: rewritten by LordHavoc (no longer uses allocations)
 int num_tokens = 0;
-char *tokens[256], tokenbuf[MAX_INPUTLINE];
+int tokens[256];
 void VM_tokenize (void)
 {
-       size_t pos;
        const char *p;
+#if 0
+       size_t pos = 0;
+       char tokenbuf[MAX_INPUTLINE];
+       size_t tokenlen;
+#endif
 
        VM_SAFEPARMCOUNT(1,VM_tokenize);
 
        p = PRVM_G_STRING(OFS_PARM0);
 
        num_tokens = 0;
-       pos = 0;
        while(COM_ParseToken(&p, false))
        {
-               size_t tokenlen;
                if (num_tokens >= (int)(sizeof(tokens)/sizeof(tokens[0])))
                        break;
+#if 0
                tokenlen = strlen(com_token) + 1;
                if (pos + tokenlen > sizeof(tokenbuf))
                        break;
-               tokens[num_tokens++] = tokenbuf + pos;
+               tokens[num_tokens++] = PRVM_SetEngineString(tokenbuf + pos);
                memcpy(tokenbuf + pos, com_token, tokenlen);
                pos += tokenlen;
+#else
+               tokens[num_tokens++] = PRVM_SetTempString(com_token);
+#endif
        }
 
        PRVM_G_FLOAT(OFS_RETURN) = num_tokens;
@@ -2105,9 +2054,9 @@ void VM_argv (void)
        token_num = (int)PRVM_G_FLOAT(OFS_PARM0);
 
        if (token_num >= 0 && token_num < num_tokens)
-               PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(tokens[token_num]);
+               PRVM_G_INT(OFS_RETURN) = tokens[token_num];
        else
-               PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(NULL);
+               PRVM_G_INT(OFS_RETURN) = OFS_NULL;
 }
 
 /*
@@ -2310,11 +2259,7 @@ void VM_loadfromfile(void)
        VM_SAFEPARMCOUNT(1,VM_loadfromfile);
 
        filename = PRVM_G_STRING(OFS_PARM0);
-       // .. is parent directory on many platforms
-       // / is parent directory on Amiga
-       // : is root of drive on Amiga (also used as a directory separator on Mac, but / works there too, so that's a bad idea)
-       // \ is a windows-ism (so it's naughty to use it, / works on all platforms)
-       if ((filename[0] == '.' && filename[1] == '.') || filename[0] == '/' || strrchr(filename, ':') || strrchr(filename, '\\'))
+       if (FS_CheckNastyPath(filename, false))
        {
                PRVM_G_FLOAT(OFS_RETURN) = -4;
                VM_Warning("VM_loadfromfile: %s dangerous or non-portable filename \"%s\" not allowed. (contains : or \\ or begins with .. or /)\n", PRVM_NAME, filename);
@@ -2476,7 +2421,6 @@ string    search_getfilename(float handle, float num)
 void VM_search_getfilename(void)
 {
        int handle, filenum;
-       char *tmp;
        VM_SAFEPARMCOUNT(2, VM_search_getfilename);
 
        handle = (int)PRVM_G_FLOAT(OFS_PARM0);
@@ -2498,10 +2442,7 @@ void VM_search_getfilename(void)
                return;
        }
 
-       tmp = VM_GetTempString();
-       strlcpy(tmp, prog->opensearches[handle]->filenames[filenum], VM_STRINGTEMP_LENGTH);
-
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(tmp);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog->opensearches[handle]->filenames[filenum]);
 }
 
 /*
@@ -2513,14 +2454,13 @@ string  chr(float ascii)
 */
 void VM_chr(void)
 {
-       char *tmp;
+       char tmp[2];
        VM_SAFEPARMCOUNT(1, VM_chr);
 
-       tmp = VM_GetTempString();
        tmp[0] = (unsigned char) PRVM_G_FLOAT(OFS_PARM0);
        tmp[1] = 0;
 
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(tmp);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(tmp);
 }
 
 //=============================================================================
@@ -2556,15 +2496,11 @@ void VM_precache_pic(void)
 
        s = PRVM_G_STRING(OFS_PARM0);
        PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
-
-       if(!s)
-               PRVM_ERROR ("VM_precache_pic: %s: NULL", PRVM_NAME);
-
        VM_CheckEmptyString (s);
 
        // AK Draw_CachePic is supposed to always return a valid pointer
        if( Draw_CachePic(s, false)->tex == r_texture_notexture )
-               PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(NULL);
+               PRVM_G_INT(OFS_RETURN) = OFS_NULL;
 }
 
 /*
@@ -2581,10 +2517,6 @@ void VM_freepic(void)
        VM_SAFEPARMCOUNT(1,VM_freepic);
 
        s = PRVM_G_STRING(OFS_PARM0);
-
-       if(!s)
-               PRVM_ERROR ("VM_freepic: NULL");
-
        VM_CheckEmptyString (s);
 
        Draw_FreePic(s);
@@ -2653,15 +2585,6 @@ void VM_drawstring(void)
        VM_SAFEPARMCOUNT(6,VM_drawstring);
 
        string = PRVM_G_STRING(OFS_PARM1);
-       if(!string)
-       {
-               PRVM_G_FLOAT(OFS_RETURN) = -1;
-               VM_Warning("VM_drawstring: %s passed null string !\n",PRVM_NAME);
-               return;
-       }
-
-       //VM_CheckEmptyString(string); Why should it be checked - perhaps the menu wants to support the precolored letters, too?
-
        pos = PRVM_G_VECTOR(OFS_PARM0);
        scale = PRVM_G_VECTOR(OFS_PARM2);
        rgb = PRVM_G_VECTOR(OFS_PARM3);
@@ -2703,14 +2626,6 @@ void VM_drawpic(void)
        VM_SAFEPARMCOUNT(6,VM_drawpic);
 
        picname = PRVM_G_STRING(OFS_PARM1);
-
-       if(!picname)
-       {
-               PRVM_G_FLOAT(OFS_RETURN) = -1;
-               VM_Warning("VM_drawpic: %s passed null picture name !\n", PRVM_NAME);
-               return;
-       }
-
        VM_CheckEmptyString (picname);
 
        // is pic cached ? no function yet for that
@@ -2823,10 +2738,6 @@ void VM_getimagesize(void)
        VM_SAFEPARMCOUNT(1,VM_getimagesize);
 
        p = PRVM_G_STRING(OFS_PARM0);
-
-       if(!p)
-               PRVM_ERROR("VM_getimagepos: %s passed null picture name !", PRVM_NAME);
-
        VM_CheckEmptyString (p);
 
        pic = Draw_CachePic (p, false);
@@ -2845,17 +2756,9 @@ string keynumtostring(float keynum)
 */
 void VM_keynumtostring (void)
 {
-       int keynum;
-       char *tmp;
        VM_SAFEPARMCOUNT(1, VM_keynumtostring);
 
-       keynum = (int)PRVM_G_FLOAT(OFS_PARM0);
-
-       tmp = VM_GetTempString();
-
-       strlcpy(tmp, Key_KeynumToString(keynum), VM_STRINGTEMP_LENGTH);
-
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(tmp);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(Key_KeynumToString((int)PRVM_G_FLOAT(OFS_PARM0)));
 }
 
 /*
@@ -2867,12 +2770,9 @@ float stringtokeynum(string key)
 */
 void VM_stringtokeynum (void)
 {
-       const char *str;
        VM_SAFEPARMCOUNT( 1, VM_keynumtostring );
 
-       str = PRVM_G_STRING( OFS_PARM0 );
-
-       PRVM_G_INT(OFS_RETURN) = Key_StringToKeynum( str );
+       PRVM_G_INT(OFS_RETURN) = Key_StringToKeynum(PRVM_G_STRING(OFS_PARM0));
 }
 
 // CL_Video interface functions
@@ -3351,17 +3251,16 @@ string altstr_prepare(string)
 */
 void VM_altstr_prepare( void )
 {
-       char *outstr, *out;
+       char *out;
        const char *instr, *in;
        int size;
+       char outstr[VM_STRINGTEMP_LENGTH];
 
        VM_SAFEPARMCOUNT( 1, VM_altstr_prepare );
 
        instr = PRVM_G_STRING( OFS_PARM0 );
-       //VM_CheckEmptyString( instr );
-       outstr = VM_GetTempString();
 
-       for( out = outstr, in = instr, size = VM_STRINGTEMP_LENGTH - 1 ; size && *in ; size--, in++, out++ )
+       for( out = outstr, in = instr, size = sizeof(outstr) - 1 ; size && *in ; size--, in++, out++ )
                if( *in == '\'' ) {
                        *out++ = '\\';
                        *out = '\'';
@@ -3370,7 +3269,7 @@ void VM_altstr_prepare( void )
                        *out = *in;
        *out = 0;
 
-       PRVM_G_INT( OFS_RETURN ) = PRVM_SetEngineString( outstr );
+       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
 }
 
 /*
@@ -3383,13 +3282,13 @@ string altstr_get(string, float)
 void VM_altstr_get( void )
 {
        const char *altstr, *pos;
-       char *outstr, *out;
+       char *out;
        int count, size;
+       char outstr[VM_STRINGTEMP_LENGTH];
 
        VM_SAFEPARMCOUNT( 2, VM_altstr_get );
 
        altstr = PRVM_G_STRING( OFS_PARM0 );
-       //VM_CheckEmptyString( altstr );
 
        count = (int)PRVM_G_FLOAT( OFS_PARM1 );
        count = count * 2 + 1;
@@ -3402,12 +3301,11 @@ void VM_altstr_get( void )
                        count--;
 
        if( !*pos ) {
-               PRVM_G_INT( OFS_RETURN ) = PRVM_SetEngineString( NULL );
+               PRVM_G_INT( OFS_RETURN ) = 0;
                return;
        }
 
-    outstr = VM_GetTempString();
-       for( out = outstr, size = VM_STRINGTEMP_LENGTH - 1 ; size && *pos ; size--, pos++, out++ )
+       for( out = outstr, size = sizeof(outstr) - 1 ; size && *pos ; size--, pos++, out++ )
                if( *pos == '\\' ) {
                        if( !*++pos )
                                break;
@@ -3419,7 +3317,7 @@ void VM_altstr_get( void )
                        *out = *pos;
 
        *out = 0;
-       PRVM_G_INT( OFS_RETURN ) = PRVM_SetEngineString( outstr );
+       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
 }
 
 /*
@@ -3434,19 +3332,18 @@ void VM_altstr_set( void )
     int num;
        const char *altstr, *str;
        const char *in;
-       char *outstr, *out;
+       char *out;
+       char outstr[VM_STRINGTEMP_LENGTH];
 
        VM_SAFEPARMCOUNT( 3, VM_altstr_set );
 
        altstr = PRVM_G_STRING( OFS_PARM0 );
-       //VM_CheckEmptyString( altstr );
 
        num = (int)PRVM_G_FLOAT( OFS_PARM1 );
 
        str = PRVM_G_STRING( OFS_PARM2 );
-       //VM_CheckEmptyString( str );
 
-       outstr = out = VM_GetTempString();
+       out = outstr;
        for( num = num * 2 + 1, in = altstr; *in && num; *out++ = *in++ )
                if( *in == '\\' ) {
                        if( !*++in ) {
@@ -3456,10 +3353,6 @@ void VM_altstr_set( void )
                        num--;
                }
 
-       if( !in ) {
-               PRVM_G_INT( OFS_RETURN ) = PRVM_SetEngineString( altstr );
-               return;
-       }
        // copy set in
        for( ; *str; *out++ = *str++ );
        // now jump over the old content
@@ -3467,13 +3360,8 @@ void VM_altstr_set( void )
                if( *in == '\'' || (*in == '\\' && !*++in) )
                        break;
 
-       if( !in ) {
-               PRVM_G_INT( OFS_RETURN ) = PRVM_SetEngineString( NULL );
-               return;
-       }
-
-       strlcpy(out, in, VM_STRINGTEMP_LENGTH);
-       PRVM_G_INT( OFS_RETURN ) = PRVM_SetEngineString( outstr );
+       strlcpy(out, in, outstr + sizeof(outstr) - out);
+       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
 }
 
 /*
@@ -3490,14 +3378,14 @@ void VM_altstr_ins(void)
        const char *set;
        const char *instr;
        const char *in;
-       char *outstr;
        char *out;
+       char outstr[VM_STRINGTEMP_LENGTH];
 
        in = instr = PRVM_G_STRING( OFS_PARM0 );
        num = (int)PRVM_G_FLOAT( OFS_PARM1 );
        set = setstr = PRVM_G_STRING( OFS_PARM2 );
 
-       out = outstr = VM_GetTempString();
+       out = outstr;
        for( num = num * 2 + 2 ; *in && num > 0 ; *out++ = *in++ )
                if( *in == '\\' ) {
                        if( !*++in ) {
@@ -3511,8 +3399,8 @@ void VM_altstr_ins(void)
        for( ; *set ; *out++ = *set++ );
        *out++ = '\'';
 
-       strlcpy(out, in, VM_STRINGTEMP_LENGTH);
-       PRVM_G_INT( OFS_RETURN ) = PRVM_SetEngineString( outstr );
+       strlcpy(out, in, outstr + sizeof(outstr) - out);
+       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
 }
 
 
@@ -3788,14 +3676,14 @@ string buf_implode(float bufhandle, string glue) = #465;
 void VM_buf_implode (void)
 {
        qcstrbuffer_t   *b;
-       char                    *k;
+       char                    k[VM_STRINGTEMP_LENGTH];
        const char              *sep;
        int                             i;
        size_t                  l;
        VM_SAFEPARMCOUNT(2, VM_buf_implode);
 
        b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0));
-       PRVM_G_INT(OFS_RETURN) = 0;
+       PRVM_G_INT(OFS_RETURN) = OFS_NULL;
        if(!b)
        {
                VM_Warning("VM_buf_implode: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
@@ -3804,30 +3692,23 @@ void VM_buf_implode (void)
        if(!b->num_strings)
                return;
        sep = PRVM_G_STRING(OFS_PARM1);
-       k = VM_GetTempString();
        k[0] = 0;
        for(l=i=0;i<b->num_strings;i++)
                if(b->strings[i])
                {
-                       l += strlen(b->strings[i]);
-                       if(l>=4095)
+                       l += (i > 0 ? strlen(sep) : 0) + strlen(b->strings[i]);
+                       if (l >= sizeof(k) - 1)
                                break;
-                       strlcat(k, b->strings[i], VM_STRINGTEMP_LENGTH);
-                       if(sep && (i != b->num_strings-1))
-                       {
-                               l += strlen(sep);
-                               if(l>=4095)
-                                       break;
-                               strlcat(k, sep, VM_STRINGTEMP_LENGTH);
-                       }
+                       strlcat(k, sep, sizeof(k));
+                       strlcat(k, b->strings[i], sizeof(k));
                }
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(k);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(k);
 }
 
 /*
 ========================
 VM_bufstr_get
-get a string from buffer, returns direct pointer, dont str_unzone it!
+get a string from buffer, returns tempstring, dont str_unzone it!
 string bufstr_get(float bufhandle, float string_index) = #465;
 ========================
 */
@@ -3837,6 +3718,7 @@ void VM_bufstr_get (void)
        int                             strindex;
        VM_SAFEPARMCOUNT(2, VM_bufstr_get);
 
+       PRVM_G_INT(OFS_RETURN) = OFS_NULL;
        b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0));
        if(!b)
        {
@@ -3849,11 +3731,10 @@ void VM_bufstr_get (void)
                VM_Warning("VM_bufstr_get: invalid string index %i used in %s\n", strindex, PRVM_NAME);
                return;
        }
-       PRVM_G_INT(OFS_RETURN) = 0;
        if(b->num_strings <= strindex)
                return;
        if(b->strings[strindex])
-               PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(b->strings[strindex]);
+               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(b->strings[strindex]);
 }
 
 /*
@@ -3886,11 +3767,6 @@ void VM_bufstr_set (void)
                return;
        }
        news = PRVM_G_STRING(OFS_PARM2);
-       if(!news)
-       {
-               VM_Warning("VM_bufstr_set: null string used in %s\n", PRVM_NAME);
-               return;
-       }
        if(b->strings[strindex])
                Z_Free(b->strings[strindex]);
        alloclen = strlen(news) + 1;
@@ -3924,12 +3800,6 @@ void VM_bufstr_add (void)
                return;
        }
        string = PRVM_G_STRING(OFS_PARM1);
-       if(!string)
-       {
-               VM_Warning("VM_bufstr_add: null string used in %s\n", PRVM_NAME);
-               return;
-       }
-
        order = (int)PRVM_G_FLOAT(OFS_PARM2);
        if(order)
                strindex = b->num_strings;
index 562fe58..fc99f38 100644 (file)
@@ -205,7 +205,6 @@ float       getserverlistindexforkey(string key)
 
 // builtins and other general functions
 
-char *VM_GetTempString(void);
 void VM_CheckEmptyString (const char *s);
 void VM_VarString(int first, char *out, int outlength);
 
index 5f75aa7..97c769b 100644 (file)
@@ -37,6 +37,7 @@ cvar_t prvm_boundscheck = {0, "prvm_boundscheck", "1", "enables detection of out
 cvar_t prvm_traceqc = {0, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
 // LordHavoc: counts usage of each QuakeC statement
 cvar_t prvm_statementprofiling = {0, "prvm_statementprofiling", "0", "counts how many times each QuakeC statement has been executed, these counts are displayed in prvm_printfunction output (if enabled)"};
+cvar_t prvm_tempstringmemory = {0, "prvm_tempstringmemory", "8388608", "amount of temporary string memory allowed in a single QuakeC invocation (QuakeC function call made by the engine)"};
 
 //============================================================================
 // mempool handling
@@ -1773,6 +1774,7 @@ void PRVM_Init (void)
        Cvar_RegisterVariable (&prvm_boundscheck);
        Cvar_RegisterVariable (&prvm_traceqc);
        Cvar_RegisterVariable (&prvm_statementprofiling);
+       Cvar_RegisterVariable (&prvm_tempstringmemory);
 
        //VM_Cmd_Init();
 }
@@ -1870,21 +1872,63 @@ prvm_edict_t *PRVM_PROG_TO_EDICT(int n)
 */
 
 
+sizebuf_t vm_tempstringsbuf;
+
 const char *PRVM_GetString(int num)
 {
-       if (num >= 0 && num < prog->stringssize)
-               return prog->strings + num;
-       else if (num < 0 && num >= -prog->numknownstrings)
+       if (num >= 0)
        {
-               num = -1 - num;
-               if (!prog->knownstrings[num])
-                       PRVM_ERROR("PRVM_GetString: attempt to get string that is already freed");
-               return prog->knownstrings[num];
+               if (num < prog->stringssize)
+                       return prog->strings + num;
+               else
+#if 1
+               if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
+               {
+                       num -= prog->stringssize;
+                       if (num < vm_tempstringsbuf.cursize)
+                               return (char *)vm_tempstringsbuf.data + num;
+                       else
+                       {
+                               VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)", num, vm_tempstringsbuf.cursize);
+                               return "";
+                       }
+               }
+               else
+#endif
+               {
+                       VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)", num, prog->stringssize);
+                       return "";
+               }
        }
        else
        {
-               PRVM_ERROR("PRVM_GetString: invalid string offset %i", num);
-               return "";
+               num = -1 - num;
+#if 0
+               if (num >= (1<<30))
+               {
+                       // special range reserved for tempstrings
+                       num -= (1<<30);
+                       if (num < vm_tempstringsbuf.cursize)
+                               return (char *)vm_tempstringsbuf.data + num;
+                       else
+                       {
+                               VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)", num, vm_tempstringsbuf.cursize);
+                               return "";
+                       }
+               }
+               else
+#endif
+               if (num < prog->numknownstrings)
+               {
+                       if (!prog->knownstrings[num])
+                               VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)", num);
+                       return prog->knownstrings[num];
+               }
+               else
+               {
+                       VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)", num, prog->numknownstrings);
+                       return "";
+               }
        }
 }
 
@@ -1895,6 +1939,15 @@ int PRVM_SetEngineString(const char *s)
                return 0;
        if (s >= prog->strings && s <= prog->strings + prog->stringssize)
                PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
+       // if it's in the tempstrings area, use a reserved range
+       // (otherwise we'd get millions of useless string offsets cluttering the database)
+       if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
+#if 1
+               return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
+#else
+               return -1 - ((1<<30) + (s - (char *)vm_tempstringsbuf.data));
+#endif
+       // see if it's a known string address
        for (i = 0;i < prog->numknownstrings;i++)
                if (prog->knownstrings[i] == s)
                        return -1 - i;
@@ -1926,6 +1979,31 @@ int PRVM_SetEngineString(const char *s)
        return -1 - i;
 }
 
+// temp string handling
+
+// all tempstrings go into this buffer consecutively, and it is reset
+// whenever PRVM_ExecuteProgram returns to the engine
+// (technically each PRVM_ExecuteProgram call saves the cursize value and
+//  restores it on return, so multiple recursive calls can share the same
+//  buffer)
+// the buffer size is controlled by the prvm_tempstringmemory cvar, causing it
+// to be reallocated between invocations if the cvar has changed
+
+int PRVM_SetTempString(const char *s)
+{
+       int size;
+       char *t;
+       if (!s)
+               return 0;
+       size = (int)strlen(s) + 1;
+       if (vm_tempstringsbuf.cursize + size >= vm_tempstringsbuf.maxsize)
+               PRVM_ERROR("PRVM_SetTempString: tempstrings buffer full!  (increase prvm_tempstringmemory cvar or reduce use of tempstrings in quakec code)\n");
+       t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
+       memcpy(t, s, size);
+       vm_tempstringsbuf.cursize += size;
+       return PRVM_SetEngineString(t);
+}
+
 int PRVM_AllocString(size_t bufferlength, char **pointer)
 {
        int i;
index eab6a2f..9ab430a 100644 (file)
@@ -363,7 +363,8 @@ void PRVM_PrintState(void)
        PRVM_StackTrace ();
 }
 
-void PRVM_Crash()
+extern sizebuf_t vm_tempstringsbuf;
+void PRVM_Crash(void)
 {
        if (prog == NULL)
                return;
@@ -378,6 +379,9 @@ void PRVM_Crash()
        prog->depth = 0;
        prog->localstack_used = 0;
 
+       // delete all tempstrings (FIXME: is this safe in VM->engine->VM recursion?)
+       vm_tempstringsbuf.cursize = 0;
+
        // reset the prog pointer
        prog = NULL;
 }
@@ -484,8 +488,10 @@ PRVM_ExecuteProgram
 extern cvar_t prvm_boundscheck;
 extern cvar_t prvm_traceqc;
 extern cvar_t prvm_statementprofiling;
+extern cvar_t prvm_tempstringmemory;
 extern int             PRVM_ED_FindFieldOffset (const char *field);
 extern ddef_t* PRVM_ED_FindGlobal(const char *name);
+extern sizebuf_t vm_tempstringsbuf;
 void PRVM_ExecuteProgram (func_t fnum, const char *errormessage)
 {
        dstatement_t    *st, *startst;
@@ -493,6 +499,7 @@ void PRVM_ExecuteProgram (func_t fnum, const char *errormessage)
        prvm_edict_t    *ed;
        prvm_eval_t     *ptr;
        int             jumpcount, cachedpr_trace, exitdepth;
+       int             restorevm_tempstringsbuf_cursize;
 
        if (!fnum || fnum >= (unsigned int)prog->progs->numfunctions)
        {
@@ -503,6 +510,22 @@ void PRVM_ExecuteProgram (func_t fnum, const char *errormessage)
 
        f = &prog->functions[fnum];
 
+       // after executing this function, delete all tempstrings it created
+       restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
+       // if there is no stack, this is a good time to reallocate the
+       // vm_tempstringsbuf if the cvar has changed
+       if (restorevm_tempstringsbuf_cursize == 0)
+       {
+               int maxsize = bound(4096, prvm_tempstringmemory.integer, 1<<30);
+               if (vm_tempstringsbuf.maxsize != maxsize || !vm_tempstringsbuf.data)
+               {
+                       if (vm_tempstringsbuf.data)
+                               Mem_Free(vm_tempstringsbuf.data);
+                       vm_tempstringsbuf.maxsize = maxsize;
+                       vm_tempstringsbuf.data = Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
+               }
+       }
+
        prog->trace = prvm_traceqc.integer;
 
        // we know we're done when pr_depth drops to this
@@ -585,4 +608,10 @@ chooseexecprogram:
                        }
                }
        }
+
+cleanup:
+       if (developer.integer >= 200 && vm_tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize)
+               Con_Printf("PRVM_ExecuteProgram: %s used %i bytes of tempstrings\n", PRVM_GetString(prog->functions[fnum].s_name), vm_tempstringsbuf.cursize - restorevm_tempstringsbuf_cursize);
+       // delete tempstrings created by this function
+       vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
 }
index 46ab006..26fafb8 100644 (file)
                                        prog->xfunction->profile += (st - startst);
                                        prog->xstatement = st - prog->statements;
                                        PRVM_ERROR("%s attempted to write to an out of bounds edict (%i)", PRVM_NAME, OPB->_int);
-                                       return;
+                                       goto cleanup;
                                }
 #endif
                                ptr = (prvm_eval_t *)((unsigned char *)prog->edictsfields + OPB->_int);
                                        prog->xfunction->profile += (st - startst);
                                        prog->xstatement = st - prog->statements;
                                        PRVM_ERROR("%s attempted to write to an out of bounds edict (%i)", PRVM_NAME, OPB->_int);
-                                       return;
+                                       goto cleanup;
                                }
 #endif
                                ptr = (prvm_eval_t *)((unsigned char *)prog->edictsfields + OPB->_int);
                                        prog->xfunction->profile += (st - startst);
                                        prog->xstatement = st - prog->statements;
                                        PRVM_ERROR("%s attempted to address an invalid field (%i) in an edict", PRVM_NAME, OPB->_int);
-                                       return;
+                                       goto cleanup;
                                }
 #endif
                                if (OPA->edict == 0 && !prog->allowworldwrites)
                                        prog->xfunction->profile += (st - startst);
                                        prog->xstatement = st - prog->statements;
                                        PRVM_ERROR("forbidden assignment to null/world entity in %s", PRVM_NAME);
-                                       return;
+                                       goto cleanup;
                                }
                                ed = PRVM_PROG_TO_EDICT(OPA->edict);
                                OPC->_int = (unsigned char *)((int *)ed->fields.vp + OPB->_int) - (unsigned char *)prog->edictsfields;
                                        prog->xfunction->profile += (st - startst);
                                        prog->xstatement = st - prog->statements;
                                        PRVM_ERROR("%s attempted to read an invalid field in an edict (%i)", PRVM_NAME, OPB->_int);
-                                       return;
+                                       goto cleanup;
                                }
 #endif
                                ed = PRVM_PROG_TO_EDICT(OPA->edict);
                                        prog->xfunction->profile += (st - startst);
                                        prog->xstatement = st - prog->statements;
                                        PRVM_ERROR("%s attempted to read an invalid field in an edict (%i)", PRVM_NAME, OPB->_int);
-                                       return;
+                                       goto cleanup;
                                }
 #endif
                                ed = PRVM_PROG_TO_EDICT(OPA->edict);
                                st = prog->statements + PRVM_LeaveFunction();
                                startst = st;
                                if (prog->depth <= exitdepth)
-                                       return;         // all done
+                                       goto cleanup; // all done
                                if (prog->trace != cachedpr_trace)
                                        goto chooseexecprogram;
                                break;
                                        prog->xfunction->profile += (st - startst);
                                        prog->xstatement = st - prog->statements;
                                        PRVM_ERROR ("%s Progs attempted to write to an out of bounds edict", PRVM_NAME);
-                                       return;
+                                       goto cleanup;
                                }
 #endif
                                ptr = (prvm_eval_t *)((unsigned char *)prog->edictsfields + OPB->_int);
                                        prog->xfunction->profile += (st - startst);
                                        prog->xstatement = st - prog->statements;
                                        PRVM_ERROR ("%s Progs attempted to read an out of bounds edict number", PRVM_NAME);
-                                       return;
+                                       goto cleanup;
                                }
                                if (OPB->_int < 0 || OPB->_int >= progs->entityfields)
                                {
                                        prog->xfunction->profile += (st - startst);
                                        prog->xstatement = st - prog->statements;
                                        PRVM_ERROR ("%s Progs attempted to read an invalid field in an edict", PRVM_NAME);
-                                       return;
+                                       goto cleanup;
                                }
 #endif
                                ed = PRVM_PROG_TO_EDICT(OPA->edict);
                                        prog->xfunction->profile += (st - startst);
                                        prog->xstatement = st - prog->statements;
                                        PRVM_ERROR ("%s Progs attempted to write to an invalid indexed global", PRVM_NAME);
-                                       return;
+                                       goto cleanup;
                                }
 #endif
                                pr_globals[OPB->_int] = OPA->_float;
                                        prog->xfunction->profile += (st - startst);
                                        prog->xstatement = st - prog->statements;
                                        PRVM_ERROR ("%s Progs attempted to write to an invalid indexed global", PRVM_NAME);
-                                       return;
+                                       goto cleanup;
                                }
 #endif
                                pr_globals[OPB->_int  ] = OPA->vector[0];
                                        prog->xfunction->profile += (st - startst);
                                        prog->xstatement = st - prog->statements;
                                        PRVM_ERROR ("%s Progs attempted to address an out of bounds global", PRVM_NAME);
-                                       return;
+                                       goto cleanup;
                                }
 #endif
                                OPC->_float = pr_globals[i];
                                        prog->xfunction->profile += (st - startst);
                                        prog->xstatement = st - prog->statements;
                                        PRVM_ERROR ("%s Progs attempted to read an invalid indexed global", PRVM_NAME);
-                                       return;
+                                       goto cleanup;
                                }
 #endif
                                OPC->_float = pr_globals[OPA->_int];
                                        prog->xfunction->profile += (st - startst);
                                        prog->xstatement = st - prog->statements;
                                        PRVM_ERROR ("%s Progs attempted to read an invalid indexed global", PRVM_NAME);
-                                       return;
+                                       goto cleanup;
                                }
 #endif
                                OPC->vector[0] = pr_globals[OPA->_int  ];
                                        prog->xfunction->profile += (st - startst);
                                        prog->xstatement = st - prog->statements;
                                        PRVM_ERROR ("%s Progs boundcheck failed at line number %d, value is < 0 or >= %d", PRVM_NAME, st->b, st->c);
-                                       return;
+                                       goto cleanup;
                                }
                                break;
 
                                prog->xfunction->profile += (st - startst);
                                prog->xstatement = st - prog->statements;
                                PRVM_ERROR ("Bad opcode %i in %s", st->op, PRVM_NAME);
+                               goto cleanup;
                        }
                }
 
index e7e5d01..dbd6b33 100644 (file)
--- a/sv_phys.c
+++ b/sv_phys.c
@@ -20,8 +20,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 // sv_phys.c
 
 #include "quakedef.h"
-// used only for VM_GetTempString
-#include "prvm_cmds.h"
 
 /*
 
@@ -290,11 +288,7 @@ void SV_Impact (prvm_edict_t *e1, trace_t *trace)
                if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
                {
                        if (trace->hittexture)
-                       {
-                               char *s = VM_GetTempString();
-                               strlcpy(s, trace->hittexture->name, VM_STRINGTEMP_LENGTH);
-                               val->string = PRVM_SetEngineString(s);
-                       }
+                               val->string = PRVM_SetTempString(trace->hittexture->name);
                        else
                                val->string = 0;
                }
index 5e87f67..407428a 100644 (file)
--- a/sv_user.c
+++ b/sv_user.c
@@ -644,6 +644,7 @@ SV_ReadClientMessage
 ===================
 */
 extern void SV_SendServerinfo(client_t *client);
+extern sizebuf_t vm_tempstringsbuf;
 void SV_ReadClientMessage(void)
 {
        int cmd, num, start;
@@ -692,9 +693,12 @@ void SV_ReadClientMessage(void)
                                Cmd_ExecuteString (s, src_client);
                        else if (SV_ParseClientCommandQC)
                        {
-                               PRVM_G_INT(OFS_PARM0) = PRVM_SetEngineString(s);
+                               int restorevm_tempstringsbuf_cursize;
+                               restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
+                               PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s);
                                prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
                                PRVM_ExecuteProgram ((func_t)(SV_ParseClientCommandQC - prog->functions), "QC function SV_ParseClientCommand is missing");
+                               vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
                        }
                        else
                                Cmd_ExecuteString (s, src_client);
index f01aeff..9995849 100644 (file)
@@ -68,6 +68,7 @@ char *vm_sv_extensions =
 "DP_QC_SINCOSSQRTPOW "
 "DP_QC_STRINGBUFFERS "
 "DP_QC_STRINGCOLORFUNCTIONS "
+"DP_QC_UNLIMITEDTEMPSTRINGS "
 "DP_QC_TRACEBOX "
 "DP_QC_TRACETOSS "
 "DP_QC_TRACE_MOVETYPE_HITMODEL "
@@ -497,11 +498,7 @@ void PF_traceline (void)
        if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
        {
                if (trace.hittexture)
-               {
-                       char *s = VM_GetTempString();
-                       strlcpy(s, trace.hittexture->name, VM_STRINGTEMP_LENGTH);
-                       val->string = PRVM_SetEngineString(s);
-               }
+                       val->string = PRVM_SetTempString(trace.hittexture->name);
                else
                        val->string = 0;
        }
@@ -563,11 +560,7 @@ void PF_tracebox (void)
        if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
        {
                if (trace.hittexture)
-               {
-                       char *s = VM_GetTempString();
-                       strlcpy(s, trace.hittexture->name, VM_STRINGTEMP_LENGTH);
-                       val->string = PRVM_SetEngineString(s);
-               }
+                       val->string = PRVM_SetTempString(trace.hittexture->name);
                else
                        val->string = 0;
        }
@@ -614,11 +607,7 @@ void PF_tracetoss (void)
        if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
        {
                if (trace.hittexture)
-               {
-                       char *s = VM_GetTempString();
-                       strlcpy(s, trace.hittexture->name, VM_STRINGTEMP_LENGTH);
-                       val->string = PRVM_SetEngineString(s);
-               }
+                       val->string = PRVM_SetTempString(trace.hittexture->name);
                else
                        val->string = 0;
        }
@@ -1694,7 +1683,7 @@ void PF_effect (void)
        int i;
        const char *s;
        s = PRVM_G_STRING(OFS_PARM1);
-       if (!s || !s[0])
+       if (!s[0])
        {
                VM_Warning("effect: no model specified\n");
                return;
@@ -2240,10 +2229,10 @@ void PF_getsurfacetexture(void)
 {
        model_t *model;
        msurface_t *surface;
-       PRVM_G_INT(OFS_RETURN) = 0;
+       PRVM_G_INT(OFS_RETURN) = OFS_NULL;
        if (!(model = getmodel(PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
                return;
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(surface->texture->name);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(surface->texture->name);
 }
 //PF_getsurfacenearpoint, // #438 float(entity e, vector p) getsurfacenearpoint = #438;
 void PF_getsurfacenearpoint(void)
diff --git a/todo b/todo
index dd73a65..d70ebf1 100644 (file)
--- a/todo
+++ b/todo
@@ -59,7 +59,6 @@
 0 bug darkplaces readme: it would be a very good idea to add documentation of sv_gameplayfix_* cvars in the readme as a means to run broken mods (xaGe)
 0 bug darkplaces renderer: GL13 path has broken handling of unlit surfaces in Nexuiz toxic.bsp - the small red light surfaces are black in GL13 path (m0rfar)
 0 bug darkplaces renderer: monsters teleporting in really slow down rendering, perhaps the teleport light is casting huge shadows?  new information suggests it is the particles. (romi, lcatlnx)
-0 bug darkplaces renderer: there's some sort of bug with GL_CullFace, it is sometimes rendering the map using GL_CullFace(GL_NONE) depending on viewpoint
 0 bug darkplaces server: SV_PushMove is ignoring model type in its angles_x handling, where as the renderer checks only model type to determine angles_x handling (Urre)
 0 bug darkplaces server: SV_PushMove's call to SV_ClipMoveToEntity should do a trace, not just a point test, to support hollow pusher models (Urre)
 0 bug darkplaces wgl client: during video mode setup, sometimes another application's window becomes permanently top most, not darkplaces' fullscreen window, why? (tZork)
@@ -95,8 +94,8 @@
 0 change darkplaces client: particles shouldn't be using contents checks to decide whether to die, they should use movement traces
 0 change darkplaces client: restrict wateralpha and such cvars according to what is permitted in qw serverinfo?
 0 change darkplaces client: turn off coronas on dlights (Jago)
-0 change darkplaces extensions: edit FRIK_FILE documentation to mention that fgets uses its own separate buffer, so only one fgets can be done at a time without uzing strzone, but that darkplaces uses standard tempstrings for fgets (it doesn't - change it!) and mention DP_QC_MULTIPLETEMPSTRINGS (FrikaC)
-0 change darkplaces extensions: edit FRIK_FILE documentation to mention that strcat uses its own separate buffer, and that a = strcat(a, b);a = strcat(a, c); works correctly despite this, also mention that in DP strcat uses standard tempstrings, and mention DP_QC_MULTIPLETEMPSTRINGS (FrikaC)
+0 change darkplaces extensions: edit FRIK_FILE documentation to mention that fgets uses its own separate buffer, so only one fgets can be done at a time without uzing strzone, but that this is not true with DP_QC_UNLIMITEDTEMPSTRINGS (FrikaC)
+0 change darkplaces extensions: edit FRIK_FILE documentation to mention that strcat uses its own separate buffer, and that a = strcat(a, b);a = strcat(a, c); works correctly despite this, but that strcat is far more flexible with DP_QC_UNLIMITEDTEMPSTRINGS (FrikaC)
 0 change darkplaces general: make r_speeds 2 show timings for other subsystems such as client, sound, server, network (Carni)
 0 change darkplaces loader: load *lava and *teleport and *rift textures as a black diffuse texture with all the color put into a glow layer, and remove the MATERIALFLAG_FULLBRIGHT accordingly, this way replacement textures can make it not glow (Kedhrin)
 0 change darkplaces memory: optimize model loaders to use less individual allocations, especially the q1bsp loader, it is responsible for a lot of very small (1-8 byte) allocations in the memlist all report
 0 change hmap2: qbsp should do tjunc fixing on leaky maps
 0 change revelation: change the wabbit kill message to " was hunting wabbit but shot " " instead"
 0 change zmodel: include the example script in the build zips, not just in the files directory
-0 cleanup darkplaces cleanup: remove cgame* files and any references
-0 cleanup darkplaces cleanup: remove ui.* files and any references
 0 cleanup darkplaces renderer: split GLSL program compilation code into shaderobject and programobject functions to reduce code
 0 feature darkplaces client: add .loc file support and say macros
 0 feature darkplaces client: add .mvd demo support
 0 feature darkplaces dpv playback: when video ends, execute a console command, perhaps "endvideo", this could be an alias set by a mod before it began playing the video (SavageX, motorsep)
 0 feature darkplaces editlights: add r_editlights_edit commands for turn/turnx/turny/turnz to allow angle adjustments using binds (Kedhrin)
 0 feature darkplaces editlights: split rtlight drawshadows option into drawworldshadows and drawentityshadows options, this allows combinations like no world shadows (for speed) but still having entity shadows (Mitchell, romi, Kedhrin)
+0 feature darkplaces extensions: document DP_QC_UNLIMITEDTEMPSTRINGS extension explaining the new tempstring system and the prvm_tempstringmemory cvar, add a note to DP_QC_MULTIPLETEMPSTRINGS that it is superceded by DP_QC_UNLIMITEDTEMPSTRINGS when present
 0 feature darkplaces filesystem: gamedir command to switch between mods, should be able to take multiple parameters to load multiple mods ontop of eachother like the -game commandline option can (FrikaC)
 0 feature darkplaces loader: add hud_clearprecache and hud_precachepic commands to preload pics by name, these get reloaded by r_restart as well, mods can put a lot of these commands in their default.cfg to precache needed hud art (Tomaz)
 0 feature darkplaces loader: load .vis files produced by hmap2
 5 feature darkplaces client: add a "edictedit" command to open up a dialog to edit an edict (allow multiple dialogs to be open at once)
 5 feature darkplaces client: add qc debugger, which would have its own very simple fullscreen console, this would be called directly from the qc interpreter, not from client (painQuin)
 5 feature darkplaces console: r_textutf8 cvar (to parse UTF-8 codes), should affect all text rendering, using multiple conchar images for different groups of 256 characters (VorteX)
-5 feature darkplaces renderer: add ALIASSKIN_NOCULLFACE and ALIASSKIN_SORTTRIANGLES flags, and figure out how to activate them somehow (FrikaC)
 5 feature darkplaces renderer: add dpshader support
 5 feature darkplaces renderer: add some kind of sun flare support, possibly stored in a dpshader (CheapAlert)
 5 feature darkplaces renderer: do a minimap that works by simply using nearclip to sheer off anything above the eye, and draws anything below normally, or via a cvar as height coloring (Supajoe)
+5 feature darkplaces renderer: implement transparent triangle sorting within each entity (FrikaC)
 5 feature darkplaces renderer: lightshader files (probably loaded by the cubemap field already present in rtlights handling), these would indicate what attenuation textures to use for the light, what cubemap filter, which corona texture and size and so on, and all textures can be animated (romi, Urre)
 5 feature darkplaces server: split server into a separate thread when running listen mode so that a host running too slow won't spoil the game (Toddd)
 5 feature dpzoo.map: make some things that make the player bigger/smaller to demonstrate scaling and better viewheight handling and brush collisions (depends on brush collisions)
@@ -619,6 +617,7 @@ d bug darkplaces renderer: r_wateralpha 0.9 is invisible on r_glsl 0;gl_combine
 d bug darkplaces renderer: reverse corona traceline direction so that a player in solid can see coronas (Urre)
 d bug darkplaces renderer: shadow volumes from q3bsp brush models are broken, maybe inverted or something (Vermeulen)
 d bug darkplaces renderer: text coloring is only affecting the first line of messagemode text (LordHavoc)
+d bug darkplaces renderer: there's some sort of bug with GL_CullFace, it is sometimes rendering the map using GL_CullFace(GL_NONE) depending on viewpoint
 d bug darkplaces renderer: transparent entities are not being lit by rtlights, where as transparent water belonging to an opaque entity (world) is being lit by rtlights (SavageX)
 d bug darkplaces renderer: transparent surfaces are not being lit by rtlights (Vermeulen)
 d bug darkplaces renderer: vertex normals seem to be generated backwards
@@ -679,6 +678,8 @@ d change dpmod: use sv_maxairspeed cvar (engine) rather than sv_airmaxspeed (qc)
 d change dpmodel: include the example script in the build zips, not just in the files directory
 d change dpmodel: keep all bones instead of removing unused ones (Ghostface)
 d change hmap2: increase MAXTOKEN from 1024 to 16384 (FrikaC)
+d cleanup darkplaces cleanup: remove cgame* files and any references
+d cleanup darkplaces cleanup: remove ui.* files and any references
 d cleanup darkplaces console: look at Black's recent console args changes and clean it up as he requested, particularly removing a commented block (Black)
 d cleanup darkplaces csqc: cl.csqcentities/cl.num_csqcentities/cl.max_csqcentities are probably entirely unnecessary
 d cleanup darkplaces general: get rid of fs_filesize, use parameters/local variables instead (Randy)