<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">Index: common/bspfile.c
===================================================================
--- common/bspfile.c	(revision 814)
+++ common/bspfile.c	(working copy)
@@ -904,6 +904,17 @@
 	  BSPVersionString(bspdata-&gt;version), BSPVersionString(version));
 }
 
+static int 
+isHexen2(const dheader_t *header)
+{
+	/*
+	the world should always have some face.
+	however, if the sizes are wrong then we're actually reading headnode[6]. hexen2 only used 5 hulls, so this should be 0 in hexen2, and not in quake.
+	*/
+	const dmodelq1_t *modelsq1 = (const dmodelq1_t*)((const byte *)header + header-&gt;lumps[LUMP_MODELS].fileofs);
+	return !modelsq1-&gt;numfaces;
+}
+
 /*
  * =========================================================================
  * ...
@@ -990,12 +1001,45 @@
     length = header-&gt;lumps[lumpnum].filelen;
     ofs = header-&gt;lumps[lumpnum].fileofs;
 
+	if (buffer)
+		free(buffer);
+
+	if (lumpnum == LUMP_MODELS &amp;&amp; !isHexen2(header))
+	{	/*convert in-place. no need to care about endian here.*/
+		const dmodelq1_t *in = (const dmodelq1_t*)((const byte *)header + ofs);
+		dmodel_t *out;
+		int i, j;
+		if (length % sizeof(dmodelq1_t))
+			Error("%s: odd %s lump size", __func__, lumpspec-&gt;name);
+		length /= sizeof(dmodelq1_t);
+
+		buffer = *bufferptr = malloc(length * sizeof(dmodel_t));
+		if (!buffer)
+			Error("%s: allocation of %i bytes failed.", __func__, length);
+		out = (dmodel_t*)buffer;
+		for (i = 0; i &lt; length; i++)
+		{
+			for (j = 0; j &lt; 3; j++)
+			{
+				out[i].mins[j] = in[i].mins[j];
+				out[i].maxs[j] = in[i].maxs[j];
+				out[i].origin[j] = in[i].origin[j];
+			}
+			for (j = 0; j &lt; MAX_MAP_HULLS_Q1; j++)
+				out[i].headnode[j] = in[i].headnode[j];
+			for (     ; j &lt; MAX_MAP_HULLS_H2; j++)
+				out[i].headnode[j] = 0;
+			out[i].visleafs = in[i].visleafs;
+			out[i].firstface = in[i].firstface;
+			out[i].numfaces = in[i].numfaces;
+		}
+		return length;
+	}
+	else
+	{
     if (length % lumpspec-&gt;size)
 	Error("%s: odd %s lump size", __func__, lumpspec-&gt;name);
 
-    if (buffer)
-	free(buffer);
-
     buffer = *bufferptr = malloc(length + 1);
     if (!buffer)
 	Error("%s: allocation of %i bytes failed.", __func__, length);
@@ -1004,6 +1048,7 @@
     buffer[length] = 0; /* In case of corrupt entity lump */
 
     return length / lumpspec-&gt;size;
+	}
 }
 
 /*
@@ -1032,6 +1077,14 @@
 	header-&gt;lumps[i].filelen = LittleLong(header-&gt;lumps[i].filelen);
     }
 
+	if (isHexen2(header))
+	{
+		logprint("BSP appears to be from hexen2\n");
+		bspdata-&gt;hullcount = MAX_MAP_HULLS_H2;
+	}
+	else
+		bspdata-&gt;hullcount = MAX_MAP_HULLS_Q1;
+
     /* copy the data */
     if (header-&gt;version == BSPVERSION) {
 	bsp29_t *bsp = &amp;bspdata-&gt;data.bsp29;
@@ -1147,6 +1200,39 @@
 	SafeWrite(bspfile-&gt;file, pad, size % 4);
 }
 
+static void
+AddModelsLump(bspfile_t *bspfile, bspdata_t *bspdata, const void *data, int count)
+{
+	if (bspdata-&gt;hullcount == MAX_MAP_HULLS_Q1)
+	{	/*convert in-place. no need to care about endian here.*/
+		lump_t *lump = &amp;bspfile-&gt;header.lumps[LUMP_MODELS];
+		const dmodel_t *in = data;
+		dmodelq1_t *out = malloc(count * sizeof(dmodelq1_t));
+		int i, j;
+		for (i = 0; i &lt; count; i++)
+		{
+			for (j = 0; j &lt; 3; j++)
+			{
+				out[i].mins[j] = in[i].mins[j];
+				out[i].maxs[j] = in[i].maxs[j];
+				out[i].origin[j] = in[i].origin[j];
+			}
+			for (j = 0; j &lt; MAX_MAP_HULLS_Q1; j++)
+				out[i].headnode[j] = in[i].headnode[j];
+			out[i].visleafs = in[i].visleafs;
+			out[i].firstface = in[i].firstface;
+			out[i].numfaces = in[i].numfaces;
+		}
+		lump-&gt;fileofs = LittleLong(ftell(bspfile-&gt;file));
+		lump-&gt;filelen = LittleLong(sizeof(dmodelq1_t) * count);
+		SafeWrite(bspfile-&gt;file, out, lump-&gt;filelen);
+		free(out);
+		return;
+	}
+	else
+		AddLump(bspfile, LUMP_MODELS, data, count);
+}
+
 /*
  * =============
  * WriteBSPFile
@@ -1182,7 +1268,7 @@
 	AddLump(&amp;bspfile, LUMP_MARKSURFACES, bsp-&gt;dmarksurfaces, bsp-&gt;nummarksurfaces);
 	AddLump(&amp;bspfile, LUMP_SURFEDGES, bsp-&gt;dsurfedges, bsp-&gt;numsurfedges);
 	AddLump(&amp;bspfile, LUMP_EDGES, bsp-&gt;dedges, bsp-&gt;numedges);
-	AddLump(&amp;bspfile, LUMP_MODELS, bsp-&gt;dmodels, bsp-&gt;nummodels);
+	AddModelsLump(&amp;bspfile, bspdata, bsp-&gt;dmodels, bsp-&gt;nummodels);
 
 	AddLump(&amp;bspfile, LUMP_LIGHTING, bsp-&gt;dlightdata, bsp-&gt;lightdatasize);
 	AddLump(&amp;bspfile, LUMP_VISIBILITY, bsp-&gt;dvisdata, bsp-&gt;visdatasize);
@@ -1203,7 +1289,7 @@
 	AddLump(&amp;bspfile, LUMP_MARKSURFACES, bsp-&gt;dmarksurfaces, bsp-&gt;nummarksurfaces);
 	AddLump(&amp;bspfile, LUMP_SURFEDGES, bsp-&gt;dsurfedges, bsp-&gt;numsurfedges);
 	AddLump(&amp;bspfile, LUMP_EDGES, bsp-&gt;dedges, bsp-&gt;numedges);
-	AddLump(&amp;bspfile, LUMP_MODELS, bsp-&gt;dmodels, bsp-&gt;nummodels);
+	AddModelsLump(&amp;bspfile, bspdata, bsp-&gt;dmodels, bsp-&gt;nummodels);
 
 	AddLump(&amp;bspfile, LUMP_LIGHTING, bsp-&gt;dlightdata, bsp-&gt;lightdatasize);
 	AddLump(&amp;bspfile, LUMP_VISIBILITY, bsp-&gt;dvisdata, bsp-&gt;visdatasize);
@@ -1224,7 +1310,7 @@
 	AddLump(&amp;bspfile, LUMP_MARKSURFACES, bsp-&gt;dmarksurfaces, bsp-&gt;nummarksurfaces);
 	AddLump(&amp;bspfile, LUMP_SURFEDGES, bsp-&gt;dsurfedges, bsp-&gt;numsurfedges);
 	AddLump(&amp;bspfile, LUMP_EDGES, bsp-&gt;dedges, bsp-&gt;numedges);
-	AddLump(&amp;bspfile, LUMP_MODELS, bsp-&gt;dmodels, bsp-&gt;nummodels);
+	AddModelsLump(&amp;bspfile, bspdata, bsp-&gt;dmodels, bsp-&gt;nummodels);
 
 	AddLump(&amp;bspfile, LUMP_LIGHTING, bsp-&gt;dlightdata, bsp-&gt;lightdatasize);
 	AddLump(&amp;bspfile, LUMP_VISIBILITY, bsp-&gt;dvisdata, bsp-&gt;visdatasize);
Index: include/common/bspfile.h
===================================================================
--- include/common/bspfile.h	(revision 814)
+++ include/common/bspfile.h	(working copy)
@@ -27,7 +27,10 @@
 
 /* upper design bounds */
 
-#define MAX_MAP_HULLS              4
+#define MAX_MAP_HULLS_Q1              4
+#define MAX_MAP_HULLS_H2              8
+#define MAX_MAP_HULLS MAX_MAP_HULLS_H2
+
 #define MAX_MAP_MODELS           256
 #define MAX_MAP_BRUSHES         4096
 #define MAX_MAP_PLANES         16384
@@ -88,11 +91,21 @@
     float mins[3];
     float maxs[3];
     float origin[3];
-    int32_t headnode[MAX_MAP_HULLS];
+    int32_t headnode[MAX_MAP_HULLS_Q1];
     int32_t visleafs;		/* not including the solid leaf 0 */
     int32_t firstface;
     int32_t numfaces;
-} dmodel_t;
+} dmodelq1_t;
+typedef struct {
+    float mins[3];
+    float maxs[3];
+    float origin[3];
+    int32_t headnode[MAX_MAP_HULLS_H2];
+    int32_t visleafs;		/* not including the solid leaf 0 */
+    int32_t firstface;
+    int32_t numfaces;
+} dmodelh2_t;
+typedef dmodelh2_t dmodel_t;
 
 typedef struct {
     int32_t nummiptex;
@@ -418,6 +431,7 @@
 
 typedef struct {
     int32_t version;
+	int hullcount;
     union {
 	bsp29_t bsp29;
 	bsp2rmq_t bsp2rmq;
Index: qbsp/brush.c
===================================================================
--- qbsp/brush.c	(revision 814)
+++ qbsp/brush.c	(working copy)
@@ -838,8 +838,53 @@
 	return NULL;
     }
 
+	if (options.hexen2)
+	{
     if (hullnum == 1) {
 	vec3_t size[2] = { {-16, -16, -32}, {16, 16, 24} };
+			ExpandBrush(&amp;hullbrush, size, facelist);
+			FreeBrushFaces(facelist);
+			facelist = CreateBrushFaces(&amp;hullbrush, rotate_offset, hullnum);
+		}
+		else	if (hullnum == 2) {
+			vec3_t size[2] = { {-24, -24, -20}, {24, 24, 20} };
+			ExpandBrush(&amp;hullbrush, size, facelist);
+			FreeBrushFaces(facelist);
+			facelist = CreateBrushFaces(&amp;hullbrush, rotate_offset, hullnum);
+		}
+		else	if (hullnum == 3) {
+			vec3_t size[2] = { {-16, -16, -12}, {16, 16, 16} };
+			ExpandBrush(&amp;hullbrush, size, facelist);
+			FreeBrushFaces(facelist);
+			facelist = CreateBrushFaces(&amp;hullbrush, rotate_offset, hullnum);
+		}
+		else	if (hullnum == 4) {
+#if 0
+			if (options.hexen2 == 1) { /*original game*/
+				vec3_t size[2] = { {-40, -40, -42}, {40, 40, 42} };
+				ExpandBrush(&amp;hullbrush, size, facelist);
+				FreeBrushFaces(facelist);
+				facelist = CreateBrushFaces(&amp;hullbrush, rotate_offset, hullnum);
+			} else
+#endif
+			{	/*mission pack*/
+				vec3_t size[2] = { {-8, -8, -8}, {8, 8, 8} };
+				ExpandBrush(&amp;hullbrush, size, facelist);
+				FreeBrushFaces(facelist);
+				facelist = CreateBrushFaces(&amp;hullbrush, rotate_offset, hullnum);
+			}
+		}
+		else	if (hullnum == 5) {
+			vec3_t size[2] = { {-48, -48, -50}, {48, 48, 50} };
+			ExpandBrush(&amp;hullbrush, size, facelist);
+			FreeBrushFaces(facelist);
+			facelist = CreateBrushFaces(&amp;hullbrush, rotate_offset, hullnum);
+		}
+	}
+	else
+	{
+		if (hullnum == 1) {
+			vec3_t size[2] = { {-16, -16, -32}, {16, 16, 24} };
 
 	ExpandBrush(&amp;hullbrush, size, facelist);
 	FreeBrushFaces(facelist);
@@ -851,6 +896,7 @@
 	FreeBrushFaces(facelist);
 	facelist = CreateBrushFaces(&amp;hullbrush, rotate_offset, hullnum);
     }
+	}
 
     // create the brush
     brush = AllocMem(BRUSH, 1, true);
Index: qbsp/bspfile.c
===================================================================
--- qbsp/bspfile.c	(revision 814)
+++ qbsp/bspfile.c	(working copy)
@@ -96,12 +96,35 @@
     for (i = 0, entity = map.entities; i &lt; map.numentities; i++, entity++) {
 	entities = &amp;entity-&gt;lumps[Type];
 	if (entities-&gt;data) {
+			if (Type == LUMP_MODELS &amp;&amp; !options.hexen2) {
+				const dmodel_t *in = entities-&gt;data;
+				dmodelq1_t out;
+				int j, k;
+				for (j = 0; j &lt; entities-&gt;count; j++)
+				{
+					for (k = 0; k &lt; 3; k++) {
+						out.mins[k] = in[j].mins[k];
+						out.maxs[k] = in[j].maxs[k];
+						out.origin[k] = in[j].origin[k];
+					}
+					for (k = 0; k &lt; MAX_MAP_HULLS_Q1; k++)
+						out.headnode[k] = in[j].headnode[k];
+					out.visleafs = in[j].visleafs;
+					out.firstface = in[j].firstface;
+					out.numfaces = in[j].numfaces;
+					ret = fwrite(&amp;out, sizeof(out), 1, f);
+					if (ret != 1)
+						Error("Failure writing to file");
+				}
+				cLen += entities-&gt;count * sizeof(out);
+			} else {
 	    ret = fwrite(entities-&gt;data, MemSize[Type], entities-&gt;count, f);
 	    if (ret != entities-&gt;count)
 		Error("Failure writing to file");
 	    cLen += entities-&gt;count * MemSize[Type];
 	}
     }
+	}
 
     // Add null terminating char for text
     if (Type == LUMP_ENTITIES) {
Index: qbsp/bspfile.h
===================================================================
--- qbsp/bspfile.h	(revision 814)
+++ qbsp/bspfile.h	(working copy)
@@ -56,12 +56,22 @@
 #define BSP_LUMPS         15
 
 typedef struct {
+#define MAX_MAP_HULLS_Q1 4
     float mins[3], maxs[3];
     float origin[3];
-    int32_t headnode[4];	/* 4 for backward compat, only 3 hulls exist */
+    int32_t headnode[MAX_MAP_HULLS_Q1];	/* 4 for backward compat, only 3 hulls exist */
     int32_t visleafs;		/* not including the solid leaf 0 */
     int32_t firstface, numfaces;
-} dmodel_t;
+} dmodelq1_t;
+typedef struct {
+#define MAX_MAP_HULLS_H2 8
+    float mins[3], maxs[3];
+    float origin[3];
+    int32_t headnode[MAX_MAP_HULLS_H2];	/* hexen2 only uses 6 */
+    int32_t visleafs;		/* not including the solid leaf 0 */
+    int32_t firstface, numfaces;
+} dmodelh2_t;
+typedef dmodelh2_t dmodel_t;
 
 typedef struct {
     int32_t version;
Index: qbsp/qbsp.c
===================================================================
--- qbsp/qbsp.c	(revision 814)
+++ qbsp/qbsp.c	(working copy)
@@ -289,6 +289,13 @@
 
     CreateSingleHull(1);
     CreateSingleHull(2);
+
+	if (options.hexen2)
+	{	/*note: h2mp doesn't use hull 2 automatically, however gamecode can explicitly set ent.hull=3 to access it*/
+		CreateSingleHull(3);
+		CreateSingleHull(4);
+		CreateSingleHull(5);
+	}
 }
 
 
@@ -502,6 +509,8 @@
 		options.fOldleak = true;
 	    else if (!strcasecmp(szTok, "nopercent"))
 		options.fNopercent = true;
+	    else if (!strcasecmp(szTok, "hexen2"))
+	     options.hexen2 = true;
 	    else if (!strcasecmp(szTok, "bsp2")) {
 		options.BSPVersion = BSP2VERSION;
 		MemSize = MemSize_BSP2;
Index: qbsp/qbsp.h
===================================================================
--- qbsp/qbsp.h	(revision 814)
+++ qbsp/qbsp.h	(working copy)
@@ -448,6 +448,7 @@
     bool fNopercent;
     bool forceGoodTree;
     bool fixRotateObjTexture;
+    int hexen2;/*2 if the worldspawn mission pack flag was set*/
     int BSPVersion;
     int dxSubdivide;
     int dxLeakDist;
Index: qbsp/writebsp.c
===================================================================
--- qbsp/writebsp.c	(revision 814)
+++ qbsp/writebsp.c	(working copy)
@@ -235,7 +235,8 @@
 	/* Worth special-casing for entity 0 (no modification needed) */
 	diff = clipcount - model-&gt;headnode[1];
 	if (diff != 0) {
-	    model-&gt;headnode[1] += diff;
+		for (i = 1; i &lt; hullnum; i++)
+			model-&gt;headnode[i] += diff;
 	    if (options.BSPVersion == BSPVERSION) {
 		bsp29_dclipnode_t *clipnode = clipnodes-&gt;data;
 		for (i = 0; i &lt; oldcount; i++, clipnode++) {
</pre></body></html>