+
+sizebuf_t vm_tempstringsbuf;
+
+const char *PRVM_GetString(int num)
+{
+ if (num >= 0)
+ {
+ 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
+ {
+ 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 "";
+ }
+ }
+}
+
+int PRVM_SetEngineString(const char *s)
+{
+ int i;
+ if (!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;
+ // new unknown engine string
+ if (developer.integer >= 100)
+ Con_Printf("new engine string %p\n", s);
+ for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
+ if (!prog->knownstrings[i])
+ break;
+ if (i >= prog->numknownstrings)
+ {
+ if (i >= prog->maxknownstrings)
+ {
+ const char **oldstrings = prog->knownstrings;
+ const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
+ prog->maxknownstrings += 128;
+ prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
+ prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
+ if (prog->numknownstrings)
+ {
+ memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
+ memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
+ }
+ }
+ prog->numknownstrings++;
+ }
+ prog->firstfreeknownstring = i + 1;
+ prog->knownstrings[i] = 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 automatically grown as needed
+
+int PRVM_SetTempString(const char *s)
+{
+ int size;
+ char *t;
+ if (!s)
+ return 0;
+ size = (int)strlen(s) + 1;
+ if (developer.integer >= 300)
+ Con_Printf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
+ if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
+ {
+ sizebuf_t old = vm_tempstringsbuf;
+ if (vm_tempstringsbuf.cursize + size >= 1<<28)
+ PRVM_ERROR("PRVM_SetTempString: ran out of tempstring memory! (refusing to grow tempstring buffer over 256MB, cursize %i, size %i)\n", vm_tempstringsbuf.cursize, size);
+ vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
+ while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
+ vm_tempstringsbuf.maxsize *= 2;
+ if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
+ {
+ if (developer.integer >= 100)
+ Con_Printf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
+ vm_tempstringsbuf.data = Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
+ if (old.cursize)
+ memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
+ if (old.data)
+ Mem_Free(old.data);
+ }
+ }
+ 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;
+ if (!bufferlength)
+ return 0;
+ for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
+ if (!prog->knownstrings[i])
+ break;
+ if (i >= prog->numknownstrings)
+ {
+ if (i >= prog->maxknownstrings)
+ {
+ const char **oldstrings = prog->knownstrings;
+ const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
+ prog->maxknownstrings += 128;
+ prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
+ prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
+ if (prog->numknownstrings)
+ {
+ memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
+ memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
+ }
+ }
+ prog->numknownstrings++;
+ }
+ prog->firstfreeknownstring = i + 1;
+ prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
+ prog->knownstrings_freeable[i] = true;
+ if (pointer)
+ *pointer = (char *)(prog->knownstrings[i]);
+ return -1 - i;
+}
+
+void PRVM_FreeString(int num)
+{
+ if (num == 0)
+ PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
+ else if (num >= 0 && num < prog->stringssize)
+ PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
+ else if (num < 0 && num >= -prog->numknownstrings)
+ {
+ num = -1 - num;
+ if (!prog->knownstrings[num])
+ PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
+ if (!prog->knownstrings[num])
+ PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
+ PRVM_Free((char *)prog->knownstrings[num]);
+ prog->knownstrings[num] = NULL;
+ prog->knownstrings_freeable[num] = false;
+ prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
+ }
+ else
+ PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
+}
+