From 2368f5100c732fdcba615a976ae42c859ae418f9 Mon Sep 17 00:00:00 2001
From: TimePath <andrew.hardaker1995@gmail.com>
Date: Mon, 28 Dec 2015 18:43:13 +1100
Subject: [PATCH] spawn2 hack

---
 clvm_cmds.c        |  2 +-
 mvm_cmds.c         |  2 +-
 progsvm.h          | 17 ++++++++-
 prvm_cmds.c        |  9 +++++
 prvm_cmds.h        |  1 +
 prvm_edict.c       | 30 +++++++++++++++
 prvm_exec.c        |  6 +--
 prvm_execprogram.h | 91 +++++++++++++++++++++++++++++++---------------
 svvm_cmds.c        |  2 +-
 9 files changed, 122 insertions(+), 38 deletions(-)

diff --git a/clvm_cmds.c b/clvm_cmds.c
index 9d41dda6..8fe3ed15 100644
--- a/clvm_cmds.c
+++ b/clvm_cmds.c
@@ -4938,7 +4938,7 @@ NULL,							// #596
 NULL,							// #597
 NULL,							// #598
 NULL,							// #599
-NULL,							// #600
+VM_spawn2,						// #600
 NULL,							// #601
 NULL,							// #602
 NULL,							// #603
diff --git a/mvm_cmds.c b/mvm_cmds.c
index ace22bfa..80ede5f1 100644
--- a/mvm_cmds.c
+++ b/mvm_cmds.c
@@ -1558,7 +1558,7 @@ NULL,									// #596
 NULL,									// #597
 NULL,									// #598
 NULL,									// #599
-NULL,									// #600
+VM_spawn2,								// #600
 VM_M_setkeydest,					// #601 void setkeydest(float dest)
 VM_M_getkeydest,					// #602 float getkeydest(void)
 VM_M_setmousetarget,				// #603 void setmousetarget(float trg)
diff --git a/progsvm.h b/progsvm.h
index 8483fae9..7095b867 100644
--- a/progsvm.h
+++ b/progsvm.h
@@ -653,6 +653,12 @@ typedef struct prvm_prog_s
 	prvm_vec_t		*edictsfields;
 	void				*edictprivate;
 
+	struct {
+		prvm_edict_t *ed;
+		prvm_vec_t *fields;
+		void *priv;
+	} edicts2;
+
 	// size of the engine private struct
 	int					edictprivate_size; // [INIT]
 
@@ -809,6 +815,7 @@ void PRVM_MEM_IncreaseEdicts(prvm_prog_t *prog);
 
 qboolean PRVM_ED_CanAlloc(prvm_prog_t *prog, prvm_edict_t *e);
 prvm_edict_t *PRVM_ED_Alloc(prvm_prog_t *prog);
+prvm_edict_t *PRVM_ED_Alloc2(prvm_prog_t *prog);
 void PRVM_ED_Free(prvm_prog_t *prog, prvm_edict_t *ed);
 void PRVM_ED_ClearEdict(prvm_prog_t *prog, prvm_edict_t *e);
 
@@ -824,10 +831,16 @@ void PRVM_ED_LoadFromFile(prvm_prog_t *prog, const char *data);
 
 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline);
 #define	PRVM_EDICT(n) (((unsigned)(n) < (unsigned int)prog->max_edicts) ? (unsigned int)(n) : PRVM_EDICT_NUM_ERROR(prog, (unsigned int)(n), __FILE__, __LINE__))
-#define	PRVM_EDICT_NUM(n) (prog->edicts + PRVM_EDICT(n))
+#define	PRVM_EDICT_NUM(n) (n >= 0 ? (prog->edicts + PRVM_EDICT(n)) : (prog->edicts2.ed + (-(n) - 1)))
+
+#define SPAWN2_LIMIT (8192)
 
 //int NUM_FOR_EDICT_ERROR(prvm_edict_t *e);
-#define PRVM_NUM_FOR_EDICT(e) ((int)((prvm_edict_t *)(e) - prog->edicts))
+#define PRVM_NUM_FOR_EDICT(e) \
+	(((int)((prvm_edict_t *)(e) - prog->edicts2.ed) >= 0 \
+	&& (int)((prvm_edict_t *)(e) - prog->edicts2.ed) < SPAWN2_LIMIT) \
+	? (int)((prvm_edict_t *)(e) - prog->edicts2.ed) \
+	: (int)((prvm_edict_t *)(e) - prog->edicts))
 //int PRVM_NUM_FOR_EDICT(prvm_edict_t *e);
 
 #define	PRVM_NEXT_EDICT(e) ((e) + 1)
diff --git a/prvm_cmds.c b/prvm_cmds.c
index e1b248dd..715ef975 100644
--- a/prvm_cmds.c
+++ b/prvm_cmds.c
@@ -972,6 +972,15 @@ void VM_spawn(prvm_prog_t *prog)
 	VM_RETURN_EDICT(ed);
 }
 
+void VM_spawn2(prvm_prog_t *prog)
+{
+	prvm_edict_t	*ed;
+	VM_SAFEPARMCOUNT(0, VM_spawn2);
+	prog->xfunction->builtinsprofile += 20;
+	ed = PRVM_ED_Alloc2(prog);
+	prog->globals.ip[OFS_RETURN] = -(int)(ed - prog->edicts2.ed) - 1;
+}
+
 /*
 =========
 VM_remove
diff --git a/prvm_cmds.h b/prvm_cmds.h
index 01209758..0df2c8ea 100644
--- a/prvm_cmds.h
+++ b/prvm_cmds.h
@@ -256,6 +256,7 @@ void VM_itof(prvm_prog_t *prog);
 void VM_ftoe(prvm_prog_t *prog);
 void VM_strftime(prvm_prog_t *prog);
 void VM_spawn (prvm_prog_t *prog);
+void VM_spawn2 (prvm_prog_t *prog);
 void VM_remove (prvm_prog_t *prog);
 void VM_find (prvm_prog_t *prog);
 void VM_findfloat (prvm_prog_t *prog);
diff --git a/prvm_edict.c b/prvm_edict.c
index 77a1af71..88164ef4 100644
--- a/prvm_edict.c
+++ b/prvm_edict.c
@@ -84,6 +84,20 @@ static void PRVM_MEM_Alloc(prvm_prog_t *prog)
 		prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char  *)prog->edictprivate + i * prog->edictprivate_size);
 		prog->edicts[i].fields.fp = prog->edictsfields + i * prog->entityfields;
 	}
+
+	// pure entities
+	{
+		prog->edicts2.ed = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool, SPAWN2_LIMIT * sizeof(prvm_edict_t));
+		prog->edicts2.priv = Mem_Alloc(prog->progs_mempool, SPAWN2_LIMIT * prog->edictprivate_size);
+		prog->edicts2.fields = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, SPAWN2_LIMIT * prog->entityfields * sizeof(prvm_vec_t));
+		for(i = 0; i < SPAWN2_LIMIT; ++i)
+		{
+			prog->edicts2.ed[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edicts2.priv + i * prog->edictprivate_size);
+			prog->edicts2.ed[i].priv.required->free = true;
+			prog->edicts2.ed[i].priv.required->freetime = -1337;
+			prog->edicts2.ed[i].fields.fp = prog->edicts2.fields + i * prog->entityfields;
+		}
+	}
 }
 
 /*
@@ -287,6 +301,22 @@ prvm_edict_t *PRVM_ED_Alloc(prvm_prog_t *prog)
 	return e;
 }
 
+prvm_edict_t *PRVM_ED_Alloc2(prvm_prog_t *prog)
+{
+	int i;
+	for (i = 0; i < SPAWN2_LIMIT; ++i)
+	{
+		prvm_edict_t *e = &prog->edicts2.ed[i];
+		if (PRVM_ED_CanAlloc(prog, e))
+		{
+			PRVM_ED_ClearEdict(prog, e);
+			return e;
+		}
+	}
+	prog->error_cmd("%s: PRVM_ED_Alloc2: no free edicts (%d)", prog->name, i);
+	return NULL;
+}
+
 /*
 =================
 PRVM_ED_Free
diff --git a/prvm_exec.c b/prvm_exec.c
index ad11c113..4a5d8a31 100644
--- a/prvm_exec.c
+++ b/prvm_exec.c
@@ -753,7 +753,7 @@ void MVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessag
 	unsigned int cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields;
 	unsigned int cached_entityfieldsarea_3 = prog->entityfieldsarea - 3;
 	unsigned int cached_entityfieldsarea_entityfields_3 = prog->entityfieldsarea - prog->entityfields - 3;
-	unsigned int cached_max_edicts = prog->max_edicts;
+	int cached_max_edicts = prog->max_edicts;
 	// these do not change
 	mstatement_t *cached_statements = prog->statements;
 	qboolean cached_allowworldwrites = prog->allowworldwrites;
@@ -860,7 +860,7 @@ void CLVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessa
 	unsigned int cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields;
 	unsigned int cached_entityfieldsarea_3 = prog->entityfieldsarea - 3;
 	unsigned int cached_entityfieldsarea_entityfields_3 = prog->entityfieldsarea - prog->entityfields - 3;
-	unsigned int cached_max_edicts = prog->max_edicts;
+	int cached_max_edicts = prog->max_edicts;
 	// these do not change
 	mstatement_t *cached_statements = prog->statements;
 	qboolean cached_allowworldwrites = prog->allowworldwrites;
@@ -971,7 +971,7 @@ void PRVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessa
 	unsigned int cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields;
 	unsigned int cached_entityfieldsarea_3 = prog->entityfieldsarea - 3;
 	unsigned int cached_entityfieldsarea_entityfields_3 = prog->entityfieldsarea - prog->entityfields - 3;
-	unsigned int cached_max_edicts = prog->max_edicts;
+	int cached_max_edicts = prog->max_edicts;
 	// these do not change
 	mstatement_t *cached_statements = prog->statements;
 	qboolean cached_allowworldwrites = prog->allowworldwrites;
diff --git a/prvm_execprogram.h b/prvm_execprogram.h
index 1607bcfe..88f925fc 100644
--- a/prvm_execprogram.h
+++ b/prvm_execprogram.h
@@ -296,46 +296,68 @@
 			HANDLE_OPCODE(OP_STOREP_FLD):		// integers
 			HANDLE_OPCODE(OP_STOREP_S):
 			HANDLE_OPCODE(OP_STOREP_FNC):		// pointers
-				if ((prvm_uint_t)OPB->_int - cached_entityfields >= cached_entityfieldsarea_entityfields)
+			{
+				int i, fld;
+				if (OPB->_int >= 0) {
+					i = OPB->_int / cached_entityfields;
+					fld = OPB->_int % cached_entityfields;
+				} else {
+					int inp = -OPB->_int - 1;
+					i = -((inp / cached_entityfields) + 1);
+					fld = inp % cached_entityfields;
+					// VM_Warning(prog, "storep %i : %i . %i = %i\n", OPB->_int, i, fld);
+				}
+				if (i >= cached_max_edicts || i < -SPAWN2_LIMIT)
 				{
-					if ((prvm_uint_t)OPB->_int >= cached_entityfieldsarea)
-					{
-						PRE_ERROR();
-						prog->error_cmd("%s attempted to write to an out of bounds edict (%i)", prog->name, (int)OPB->_int);
-						goto cleanup;
-					}
-					if ((prvm_uint_t)OPB->_int < cached_entityfields && !cached_allowworldwrites)
-					{
-						PRE_ERROR();
-						VM_Warning(prog, "assignment to world.%s (field %i) in %s\n", PRVM_GetString(prog, PRVM_ED_FieldAtOfs(prog, OPB->_int)->s_name), (int)OPB->_int, prog->name);
-					}
+					PRE_ERROR();
+					prog->error_cmd("%s attempted to write to an out of bounds edict (%i)", prog->name, i);
+					goto cleanup;
 				}
-				ptr = (prvm_eval_t *)(cached_edictsfields + OPB->_int);
+				if (i == 0 && !cached_allowworldwrites)
+				{
+					PRE_ERROR();
+					VM_Warning(prog, "assignment to world.%s (field %i) in %s\n", PRVM_GetString(prog, PRVM_ED_FieldAtOfs(prog, OPB->_int)->s_name), (int)OPB->_int, prog->name);
+				}
+				ptr = i >= 0
+					? (prvm_eval_t *)(cached_edictsfields + OPB->_int)
+					: (prvm_eval_t *)(prog->edicts2.fields + (-i - 1) * cached_entityfields + fld);
 				ptr->_int = OPA->_int;
 				DISPATCH_OPCODE();
+			}
 			HANDLE_OPCODE(OP_STOREP_V):
-				if ((prvm_uint_t)OPB->_int - cached_entityfields > (prvm_uint_t)cached_entityfieldsarea_entityfields_3)
+			{
+				int i, fld;
+				if (OPB->_int >= 0) {
+					i = OPB->_int / cached_entityfields;
+					fld = OPB->_int % cached_entityfields;
+				} else {
+					int o = -OPB->_int - 1;
+					i = -((o / cached_entityfields) + 1);
+					fld = o % cached_entityfields;
+					// VM_Warning(prog, "storep %i : %i . %i = %i\n", OPB->_int, i, fld);
+				}
+				if (i >= cached_max_edicts || i < -SPAWN2_LIMIT)
 				{
-					if ((prvm_uint_t)OPB->_int > cached_entityfieldsarea_3)
-					{
-						PRE_ERROR();
-						prog->error_cmd("%s attempted to write to an out of bounds edict (%i)", prog->name, (int)OPB->_int);
-						goto cleanup;
-					}
-					if ((prvm_uint_t)OPB->_int < cached_entityfields && !cached_allowworldwrites)
-					{
-						PRE_ERROR();
-						VM_Warning(prog, "assignment to world.%s (field %i) in %s\n", PRVM_GetString(prog, PRVM_ED_FieldAtOfs(prog, OPB->_int)->s_name), (int)OPB->_int, prog->name);
-					}
+					PRE_ERROR();
+					prog->error_cmd("%s attempted to write to an out of bounds edict (%i)", prog->name, i);
+					goto cleanup;
+				}
+				if (i == 0 && !cached_allowworldwrites)
+				{
+					PRE_ERROR();
+					VM_Warning(prog, "assignment to world.%s (field %i) in %s\n", PRVM_GetString(prog, PRVM_ED_FieldAtOfs(prog, OPB->_int)->s_name), (int)OPB->_int, prog->name);
 				}
-				ptr = (prvm_eval_t *)(cached_edictsfields + OPB->_int);
+				ptr = i >= 0
+					? (prvm_eval_t *)(cached_edictsfields + OPB->_int)
+					: (prvm_eval_t *)(prog->edicts2.fields + (-i - 1) * cached_entityfields + fld);
 				ptr->ivector[0] = OPA->ivector[0];
 				ptr->ivector[1] = OPA->ivector[1];
 				ptr->ivector[2] = OPA->ivector[2];
 				DISPATCH_OPCODE();
+			}
 
 			HANDLE_OPCODE(OP_ADDRESS):
-				if ((prvm_uint_t)OPA->edict >= cached_max_edicts)
+				if (OPA->edict >= cached_max_edicts || OPA->edict < -SPAWN2_LIMIT)
 				{
 					PRE_ERROR();
 					prog->error_cmd("%s Progs attempted to address an out of bounds edict number", prog->name);
@@ -355,7 +377,16 @@
 					goto cleanup;
 				}
 #endif
-				OPC->_int = OPA->edict * cached_entityfields + OPB->_int;
+				if (OPA->edict >= 0)
+					OPC->_int = OPA->edict * cached_entityfields + OPB->_int;
+				else {
+					int e = -OPA->edict - 1;
+					int out = e * cached_entityfields + OPB->_int;
+					out = out + 1;
+					out = 0 - out;
+					//VM_Warning(prog, "addressing %i . %i = %i\n", OPA->edict, OPB->_int, out);
+					OPC->_int = out;
+				}
 				DISPATCH_OPCODE();
 
 			HANDLE_OPCODE(OP_LOAD_F):
@@ -363,7 +394,7 @@
 			HANDLE_OPCODE(OP_LOAD_ENT):
 			HANDLE_OPCODE(OP_LOAD_S):
 			HANDLE_OPCODE(OP_LOAD_FNC):
-				if ((prvm_uint_t)OPA->edict >= cached_max_edicts)
+				if (OPA->edict >= cached_max_edicts || OPA->edict < -SPAWN2_LIMIT)
 				{
 					PRE_ERROR();
 					prog->error_cmd("%s Progs attempted to read an out of bounds edict number", prog->name);
@@ -380,7 +411,7 @@
 				DISPATCH_OPCODE();
 
 			HANDLE_OPCODE(OP_LOAD_V):
-				if ((prvm_uint_t)OPA->edict >= cached_max_edicts)
+				if (OPA->edict >= cached_max_edicts || OPA->edict < -SPAWN2_LIMIT)
 				{
 					PRE_ERROR();
 					prog->error_cmd("%s Progs attempted to read an out of bounds edict number", prog->name);
diff --git a/svvm_cmds.c b/svvm_cmds.c
index ff55c4fd..ab7cc087 100644
--- a/svvm_cmds.c
+++ b/svvm_cmds.c
@@ -3783,7 +3783,7 @@ NULL,							// #596
 NULL,							// #597
 NULL,							// #598
 NULL,							// #599
-NULL,							// #600
+VM_spawn2,						// #600
 NULL,							// #601
 NULL,							// #602
 NULL,							// #603
-- 
2.39.5