summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTravis Bradshaw <travis.bradshaw@idsoftware.com>2012-01-31 15:48:05 -0600
committerTravis Bradshaw <travis.bradshaw@idsoftware.com>2012-01-31 15:48:05 -0600
commit6df9737f9cbaf410853514463ef59e215fc698ba (patch)
tree67b87aaff83d64dc0a19c357c28aefe588551df9
downloadquaketools-6df9737f9cbaf410853514463ef59e215fc698ba.tar.gz
quaketools-6df9737f9cbaf410853514463ef59e215fc698ba.tar.bz2
quaketools-6df9737f9cbaf410853514463ef59e215fc698ba.zip
The source release of the qutils.
-rw-r--r--qutils/BIN/BSPINFO.EXEbin0 -> 54784 bytes
-rw-r--r--qutils/BIN/LIGHT.EXEbin0 -> 84992 bytes
-rw-r--r--qutils/BIN/MODELGEN.EXEbin0 -> 89600 bytes
-rw-r--r--qutils/BIN/QBSP.EXEbin0 -> 128512 bytes
-rw-r--r--qutils/BIN/QCC.EXEbin0 -> 72192 bytes
-rw-r--r--qutils/BIN/QFILES.EXEbin0 -> 54272 bytes
-rw-r--r--qutils/BIN/QLUMPY.EXEbin0 -> 55808 bytes
-rw-r--r--qutils/BIN/SPRGEN.EXEbin0 -> 48640 bytes
-rw-r--r--qutils/BIN/TEXMAKE.EXEbin0 -> 55296 bytes
-rw-r--r--qutils/BIN/VIS.EXEbin0 -> 81408 bytes
-rw-r--r--qutils/BSPINFO/BSPINFO.C25
-rw-r--r--qutils/BSPINFO/BSPINFO.MAK234
-rw-r--r--qutils/BSPINFO/BSPINFO.MDPbin0 -> 35840 bytes
-rw-r--r--qutils/BSPINFO/BSPINFO.NCBbin0 -> 74752 bytes
-rw-r--r--qutils/COMMON/BSPFILE.C375
-rw-r--r--qutils/COMMON/BSPFILE.H251
-rw-r--r--qutils/COMMON/CMDLIB.C867
-rw-r--r--qutils/COMMON/CMDLIB.H97
-rw-r--r--qutils/COMMON/LBMLIB.C508
-rw-r--r--qutils/COMMON/LBMLIB.H42
-rw-r--r--qutils/COMMON/MATHLIB.C109
-rw-r--r--qutils/COMMON/MATHLIB.H48
-rw-r--r--qutils/COMMON/POLYLIB.C400
-rw-r--r--qutils/COMMON/POLYLIB.H22
-rw-r--r--qutils/COMMON/SCRIPLIB.C185
-rw-r--r--qutils/COMMON/SCRIPLIB.H21
-rw-r--r--qutils/COMMON/THREADS.C239
-rw-r--r--qutils/COMMON/THREADS.H8
-rw-r--r--qutils/COMMON/TRILIB.C170
-rw-r--r--qutils/COMMON/TRILIB.H11
-rw-r--r--qutils/COMMON/WADLIB.C328
-rw-r--r--qutils/COMMON/WADLIB.H53
-rw-r--r--qutils/INSTALL.BAT43
-rw-r--r--qutils/LIGHT/ENTITIES.C278
-rw-r--r--qutils/LIGHT/ENTITIES.H33
-rw-r--r--qutils/LIGHT/LIGHT.C146
-rw-r--r--qutils/LIGHT/LIGHT.H38
-rw-r--r--qutils/LIGHT/LIGHT.MAK407
-rw-r--r--qutils/LIGHT/LIGHT.MDPbin0 -> 37376 bytes
-rw-r--r--qutils/LIGHT/LIGHT.NCBbin0 -> 99328 bytes
-rw-r--r--qutils/LIGHT/LTFACE.C588
-rw-r--r--qutils/LIGHT/THREADS.C66
-rw-r--r--qutils/LIGHT/THREADS.H17
-rw-r--r--qutils/LIGHT/TRACE.C197
-rw-r--r--qutils/MODELGEN/ANORMS.H162
-rw-r--r--qutils/MODELGEN/MODELGEN.C1212
-rw-r--r--qutils/MODELGEN/MODELGEN.H113
-rw-r--r--qutils/MODELGEN/MODELGEN.MAK294
-rw-r--r--qutils/MODELGEN/MODELGEN.MDPbin0 -> 36352 bytes
-rw-r--r--qutils/MODELGEN/MODELGEN.NCBbin0 -> 91136 bytes
-rw-r--r--qutils/QBSP/BRUSH.C870
-rw-r--r--qutils/QBSP/BSP5.H297
-rw-r--r--qutils/QBSP/CSG4.C464
-rw-r--r--qutils/QBSP/MAKEFILE65
-rw-r--r--qutils/QBSP/MAP.C579
-rw-r--r--qutils/QBSP/MAP.H50
-rw-r--r--qutils/QBSP/MERGE.C277
-rw-r--r--qutils/QBSP/NODRAW.C54
-rw-r--r--qutils/QBSP/OUTSIDE.C252
-rw-r--r--qutils/QBSP/PORTALS.C576
-rw-r--r--qutils/QBSP/QBSP.C1030
-rw-r--r--qutils/QBSP/QBSP.MAK527
-rw-r--r--qutils/QBSP/QBSP.MDPbin0 -> 38400 bytes
-rw-r--r--qutils/QBSP/QBSP.NCBbin0 -> 123904 bytes
-rw-r--r--qutils/QBSP/REGION.C511
-rw-r--r--qutils/QBSP/SOLIDBSP.C754
-rw-r--r--qutils/QBSP/SURFACES.C512
-rw-r--r--qutils/QBSP/TJUNC.C506
-rw-r--r--qutils/QBSP/WRITEBSP.C523
-rw-r--r--qutils/QCC/MAKEFILE42
-rw-r--r--qutils/QCC/PR_COMP.C936
-rw-r--r--qutils/QCC/PR_COMP.H161
-rw-r--r--qutils/QCC/PR_LEX.C677
-rw-r--r--qutils/QCC/QCCbin0 -> 146232 bytes
-rw-r--r--qutils/QCC/QCC.C808
-rw-r--r--qutils/QCC/QCC.H436
-rw-r--r--qutils/QCC/QCC.MAK249
-rw-r--r--qutils/QCC/QCC.MDPbin0 -> 36352 bytes
-rw-r--r--qutils/QCC/QCC.NCBbin0 -> 99328 bytes
-rw-r--r--qutils/QCC/QCC.PDBbin0 -> 164864 bytes
-rw-r--r--qutils/QCC/VC40.PDBbin0 -> 45056 bytes
-rw-r--r--qutils/QFILES/QFILES.C251
-rw-r--r--qutils/QFILES/QFILES.MAK221
-rw-r--r--qutils/QFILES/QFILES.MDPbin0 -> 36352 bytes
-rw-r--r--qutils/QFILES/QFILES.NCBbin0 -> 58368 bytes
-rw-r--r--qutils/QLUMPY/QLUMPY.C301
-rw-r--r--qutils/QLUMPY/QLUMPY.H16
-rw-r--r--qutils/QLUMPY/QLUMPY.MAK324
-rw-r--r--qutils/QLUMPY/QLUMPY.MDPbin0 -> 36352 bytes
-rw-r--r--qutils/QLUMPY/QLUMPY.NCBbin0 -> 107520 bytes
-rw-r--r--qutils/QLUMPY/QUAKEGRB.C523
-rw-r--r--qutils/README.TXT148
-rw-r--r--qutils/SPRGEN/SPRGEN.C526
-rw-r--r--qutils/SPRGEN/SPRGEN.MAK300
-rw-r--r--qutils/SPRGEN/SPRGEN.MDPbin0 -> 36352 bytes
-rw-r--r--qutils/SPRGEN/SPRGEN.NCBbin0 -> 82944 bytes
-rw-r--r--qutils/SPRGEN/SPRITEGN.H88
-rw-r--r--qutils/SPRGEN/S_BUBBLE.SPRbin0 -> 588 bytes
-rw-r--r--qutils/SPRGEN/S_EXPLOD.SPRbin0 -> 18972 bytes
-rw-r--r--qutils/SPRGEN/S_LIGHT.SPRbin0 -> 1080 bytes
-rw-r--r--qutils/TEXMAKE/TEXMAKE.C222
-rw-r--r--qutils/TEXMAKE/TEXMAKE.MAK269
-rw-r--r--qutils/TEXMAKE/TEXMAKE.MDPbin0 -> 35840 bytes
-rw-r--r--qutils/TEXMAKE/TEXMAKE.NCBbin0 -> 58368 bytes
-rw-r--r--qutils/VIS/FLOW.C475
-rw-r--r--qutils/VIS/SOUNDPVS.C146
-rw-r--r--qutils/VIS/VIS.C963
-rw-r--r--qutils/VIS/VIS.H125
-rw-r--r--qutils/VIS/VIS.MAK288
-rw-r--r--qutils/VIS/VIS.MDPbin0 -> 35840 bytes
-rw-r--r--qutils/VIS/VIS.NCBbin0 -> 74752 bytes
111 files changed, 23929 insertions, 0 deletions
diff --git a/qutils/BIN/BSPINFO.EXE b/qutils/BIN/BSPINFO.EXE
new file mode 100644
index 0000000..bc95fc9
--- /dev/null
+++ b/qutils/BIN/BSPINFO.EXE
Binary files differ
diff --git a/qutils/BIN/LIGHT.EXE b/qutils/BIN/LIGHT.EXE
new file mode 100644
index 0000000..d2ed095
--- /dev/null
+++ b/qutils/BIN/LIGHT.EXE
Binary files differ
diff --git a/qutils/BIN/MODELGEN.EXE b/qutils/BIN/MODELGEN.EXE
new file mode 100644
index 0000000..825b9c6
--- /dev/null
+++ b/qutils/BIN/MODELGEN.EXE
Binary files differ
diff --git a/qutils/BIN/QBSP.EXE b/qutils/BIN/QBSP.EXE
new file mode 100644
index 0000000..8eaf56a
--- /dev/null
+++ b/qutils/BIN/QBSP.EXE
Binary files differ
diff --git a/qutils/BIN/QCC.EXE b/qutils/BIN/QCC.EXE
new file mode 100644
index 0000000..9f208f9
--- /dev/null
+++ b/qutils/BIN/QCC.EXE
Binary files differ
diff --git a/qutils/BIN/QFILES.EXE b/qutils/BIN/QFILES.EXE
new file mode 100644
index 0000000..66ee526
--- /dev/null
+++ b/qutils/BIN/QFILES.EXE
Binary files differ
diff --git a/qutils/BIN/QLUMPY.EXE b/qutils/BIN/QLUMPY.EXE
new file mode 100644
index 0000000..297809c
--- /dev/null
+++ b/qutils/BIN/QLUMPY.EXE
Binary files differ
diff --git a/qutils/BIN/SPRGEN.EXE b/qutils/BIN/SPRGEN.EXE
new file mode 100644
index 0000000..46242ef
--- /dev/null
+++ b/qutils/BIN/SPRGEN.EXE
Binary files differ
diff --git a/qutils/BIN/TEXMAKE.EXE b/qutils/BIN/TEXMAKE.EXE
new file mode 100644
index 0000000..98094ad
--- /dev/null
+++ b/qutils/BIN/TEXMAKE.EXE
Binary files differ
diff --git a/qutils/BIN/VIS.EXE b/qutils/BIN/VIS.EXE
new file mode 100644
index 0000000..491fdc7
--- /dev/null
+++ b/qutils/BIN/VIS.EXE
Binary files differ
diff --git a/qutils/BSPINFO/BSPINFO.C b/qutils/BSPINFO/BSPINFO.C
new file mode 100644
index 0000000..08e46df
--- /dev/null
+++ b/qutils/BSPINFO/BSPINFO.C
@@ -0,0 +1,25 @@
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "bspfile.h"
+
+void main (int argc, char **argv)
+{
+ int i;
+ char source[1024];
+
+ if (argc == 1)
+ Error ("usage: bspinfo bspfile [bspfiles]");
+
+ for (i=1 ; i<argc ; i++)
+ {
+ printf ("---------------------\n");
+ strcpy (source, argv[i]);
+ DefaultExtension (source, ".bsp");
+ printf ("%s\n", source);
+
+ LoadBSPFile (source);
+ PrintBSPFileSizes ();
+ printf ("---------------------\n");
+ }
+}
diff --git a/qutils/BSPINFO/BSPINFO.MAK b/qutils/BSPINFO/BSPINFO.MAK
new file mode 100644
index 0000000..e893e24
--- /dev/null
+++ b/qutils/BSPINFO/BSPINFO.MAK
@@ -0,0 +1,234 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.10
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+!IF "$(CFG)" == ""
+CFG=bspinfo - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to bspinfo - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "bspinfo - Win32 Release" && "$(CFG)" !=\
+ "bspinfo - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "bspinfo.mak" CFG="bspinfo - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "bspinfo - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "bspinfo - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "bspinfo - Win32 Debug"
+RSC=rc.exe
+CPP=cl.exe
+
+!IF "$(CFG)" == "bspinfo - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\bspinfo.exe"
+
+CLEAN :
+ -@erase "$(INTDIR)\bspfile.obj"
+ -@erase "$(INTDIR)\bspinfo.obj"
+ -@erase "$(INTDIR)\cmdlib.obj"
+ -@erase "$(OUTDIR)\bspinfo.exe"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /ML /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/bspinfo.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\Release/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/bspinfo.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:console /incremental:no\
+ /pdb:"$(OUTDIR)/bspinfo.pdb" /machine:I386 /out:"$(OUTDIR)/bspinfo.exe"
+LINK32_OBJS= \
+ "$(INTDIR)\bspfile.obj" \
+ "$(INTDIR)\bspinfo.obj" \
+ "$(INTDIR)\cmdlib.obj"
+
+"$(OUTDIR)\bspinfo.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "bspinfo - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\bspinfo.exe"
+
+CLEAN :
+ -@erase "$(INTDIR)\bspfile.obj"
+ -@erase "$(INTDIR)\bspinfo.obj"
+ -@erase "$(INTDIR)\cmdlib.obj"
+ -@erase "$(INTDIR)\vc40.idb"
+ -@erase "$(INTDIR)\vc40.pdb"
+ -@erase "$(OUTDIR)\bspinfo.exe"
+ -@erase "$(OUTDIR)\bspinfo.ilk"
+ -@erase "$(OUTDIR)\bspinfo.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MLd /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/bspinfo.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\Debug/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/bspinfo.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:console /incremental:yes\
+ /pdb:"$(OUTDIR)/bspinfo.pdb" /debug /machine:I386 /out:"$(OUTDIR)/bspinfo.exe"
+LINK32_OBJS= \
+ "$(INTDIR)\bspfile.obj" \
+ "$(INTDIR)\bspinfo.obj" \
+ "$(INTDIR)\cmdlib.obj"
+
+"$(OUTDIR)\bspinfo.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+################################################################################
+# Begin Target
+
+# Name "bspinfo - Win32 Release"
+# Name "bspinfo - Win32 Debug"
+
+!IF "$(CFG)" == "bspinfo - Win32 Release"
+
+!ELSEIF "$(CFG)" == "bspinfo - Win32 Debug"
+
+!ENDIF
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\bspinfo.c
+DEP_CPP_BSPIN=\
+ "..\common\bspfile.h"\
+ "..\common\cmdlib.h"\
+ "..\common\mathlib.h"\
+
+
+"$(INTDIR)\bspinfo.obj" : $(SOURCE) $(DEP_CPP_BSPIN) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\cmdlib.c
+DEP_CPP_CMDLI=\
+ "..\..\..\..\..\quake\utils2\common\cmdlib.h"\
+ {$(INCLUDE)}"\sys\stat.h"\
+ {$(INCLUDE)}"\sys\types.h"\
+
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\bspfile.c
+DEP_CPP_BSPFI=\
+ "..\..\..\..\..\quake\utils2\common\bspfile.h"\
+ "..\..\..\..\..\quake\utils2\common\cmdlib.h"\
+ "..\..\..\..\..\quake\utils2\common\mathlib.h"\
+
+
+"$(INTDIR)\bspfile.obj" : $(SOURCE) $(DEP_CPP_BSPFI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+# End Target
+# End Project
+################################################################################
diff --git a/qutils/BSPINFO/BSPINFO.MDP b/qutils/BSPINFO/BSPINFO.MDP
new file mode 100644
index 0000000..609635c
--- /dev/null
+++ b/qutils/BSPINFO/BSPINFO.MDP
Binary files differ
diff --git a/qutils/BSPINFO/BSPINFO.NCB b/qutils/BSPINFO/BSPINFO.NCB
new file mode 100644
index 0000000..112a95e
--- /dev/null
+++ b/qutils/BSPINFO/BSPINFO.NCB
Binary files differ
diff --git a/qutils/COMMON/BSPFILE.C b/qutils/COMMON/BSPFILE.C
new file mode 100644
index 0000000..bddd03a
--- /dev/null
+++ b/qutils/COMMON/BSPFILE.C
@@ -0,0 +1,375 @@
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "bspfile.h"
+
+//=============================================================================
+
+int nummodels;
+dmodel_t dmodels[MAX_MAP_MODELS];
+
+int visdatasize;
+byte dvisdata[MAX_MAP_VISIBILITY];
+
+int lightdatasize;
+byte dlightdata[MAX_MAP_LIGHTING];
+
+int texdatasize;
+byte dtexdata[MAX_MAP_MIPTEX]; // (dmiptexlump_t)
+
+int entdatasize;
+char dentdata[MAX_MAP_ENTSTRING];
+
+int numleafs;
+dleaf_t dleafs[MAX_MAP_LEAFS];
+
+int numplanes;
+dplane_t dplanes[MAX_MAP_PLANES];
+
+int numvertexes;
+dvertex_t dvertexes[MAX_MAP_VERTS];
+
+int numnodes;
+dnode_t dnodes[MAX_MAP_NODES];
+
+int numtexinfo;
+texinfo_t texinfo[MAX_MAP_TEXINFO];
+
+int numfaces;
+dface_t dfaces[MAX_MAP_FACES];
+
+int numclipnodes;
+dclipnode_t dclipnodes[MAX_MAP_CLIPNODES];
+
+int numedges;
+dedge_t dedges[MAX_MAP_EDGES];
+
+int nummarksurfaces;
+unsigned short dmarksurfaces[MAX_MAP_MARKSURFACES];
+
+int numsurfedges;
+int dsurfedges[MAX_MAP_SURFEDGES];
+
+//=============================================================================
+
+/*
+=============
+SwapBSPFile
+
+Byte swaps all data in a bsp file.
+=============
+*/
+void SwapBSPFile (qboolean todisk)
+{
+ int i, j, c;
+ dmodel_t *d;
+ dmiptexlump_t *mtl;
+
+
+// models
+ for (i=0 ; i<nummodels ; i++)
+ {
+ d = &dmodels[i];
+
+ for (j=0 ; j<MAX_MAP_HULLS ; j++)
+ d->headnode[j] = LittleLong (d->headnode[j]);
+
+ d->visleafs = LittleLong (d->visleafs);
+ d->firstface = LittleLong (d->firstface);
+ d->numfaces = LittleLong (d->numfaces);
+
+ for (j=0 ; j<3 ; j++)
+ {
+ d->mins[j] = LittleFloat(d->mins[j]);
+ d->maxs[j] = LittleFloat(d->maxs[j]);
+ d->origin[j] = LittleFloat(d->origin[j]);
+ }
+ }
+
+//
+// vertexes
+//
+ for (i=0 ; i<numvertexes ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ dvertexes[i].point[j] = LittleFloat (dvertexes[i].point[j]);
+ }
+
+//
+// planes
+//
+ for (i=0 ; i<numplanes ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ dplanes[i].normal[j] = LittleFloat (dplanes[i].normal[j]);
+ dplanes[i].dist = LittleFloat (dplanes[i].dist);
+ dplanes[i].type = LittleLong (dplanes[i].type);
+ }
+
+//
+// texinfos
+//
+ for (i=0 ; i<numtexinfo ; i++)
+ {
+ for (j=0 ; j<8 ; j++)
+ texinfo[i].vecs[0][j] = LittleFloat (texinfo[i].vecs[0][j]);
+ texinfo[i].miptex = LittleLong (texinfo[i].miptex);
+ texinfo[i].flags = LittleLong (texinfo[i].flags);
+ }
+
+//
+// faces
+//
+ for (i=0 ; i<numfaces ; i++)
+ {
+ dfaces[i].texinfo = LittleShort (dfaces[i].texinfo);
+ dfaces[i].planenum = LittleShort (dfaces[i].planenum);
+ dfaces[i].side = LittleShort (dfaces[i].side);
+ dfaces[i].lightofs = LittleLong (dfaces[i].lightofs);
+ dfaces[i].firstedge = LittleLong (dfaces[i].firstedge);
+ dfaces[i].numedges = LittleShort (dfaces[i].numedges);
+ }
+
+//
+// nodes
+//
+ for (i=0 ; i<numnodes ; i++)
+ {
+ dnodes[i].planenum = LittleLong (dnodes[i].planenum);
+ for (j=0 ; j<3 ; j++)
+ {
+ dnodes[i].mins[j] = LittleShort (dnodes[i].mins[j]);
+ dnodes[i].maxs[j] = LittleShort (dnodes[i].maxs[j]);
+ }
+ dnodes[i].children[0] = LittleShort (dnodes[i].children[0]);
+ dnodes[i].children[1] = LittleShort (dnodes[i].children[1]);
+ dnodes[i].firstface = LittleShort (dnodes[i].firstface);
+ dnodes[i].numfaces = LittleShort (dnodes[i].numfaces);
+ }
+
+//
+// leafs
+//
+ for (i=0 ; i<numleafs ; i++)
+ {
+ dleafs[i].contents = LittleLong (dleafs[i].contents);
+ for (j=0 ; j<3 ; j++)
+ {
+ dleafs[i].mins[j] = LittleShort (dleafs[i].mins[j]);
+ dleafs[i].maxs[j] = LittleShort (dleafs[i].maxs[j]);
+ }
+
+ dleafs[i].firstmarksurface = LittleShort (dleafs[i].firstmarksurface);
+ dleafs[i].nummarksurfaces = LittleShort (dleafs[i].nummarksurfaces);
+ dleafs[i].visofs = LittleLong (dleafs[i].visofs);
+ }
+
+//
+// clipnodes
+//
+ for (i=0 ; i<numclipnodes ; i++)
+ {
+ dclipnodes[i].planenum = LittleLong (dclipnodes[i].planenum);
+ dclipnodes[i].children[0] = LittleShort (dclipnodes[i].children[0]);
+ dclipnodes[i].children[1] = LittleShort (dclipnodes[i].children[1]);
+ }
+
+//
+// miptex
+//
+ if (texdatasize)
+ {
+ mtl = (dmiptexlump_t *)dtexdata;
+ if (todisk)
+ c = mtl->nummiptex;
+ else
+ c = LittleLong(mtl->nummiptex);
+ mtl->nummiptex = LittleLong (mtl->nummiptex);
+ for (i=0 ; i<c ; i++)
+ mtl->dataofs[i] = LittleLong(mtl->dataofs[i]);
+ }
+
+//
+// marksurfaces
+//
+ for (i=0 ; i<nummarksurfaces ; i++)
+ dmarksurfaces[i] = LittleShort (dmarksurfaces[i]);
+
+//
+// surfedges
+//
+ for (i=0 ; i<numsurfedges ; i++)
+ dsurfedges[i] = LittleLong (dsurfedges[i]);
+
+//
+// edges
+//
+ for (i=0 ; i<numedges ; i++)
+ {
+ dedges[i].v[0] = LittleShort (dedges[i].v[0]);
+ dedges[i].v[1] = LittleShort (dedges[i].v[1]);
+ }
+}
+
+
+dheader_t *header;
+
+int CopyLump (int lump, void *dest, int size)
+{
+ int length, ofs;
+
+ length = header->lumps[lump].filelen;
+ ofs = header->lumps[lump].fileofs;
+
+ if (length % size)
+ Error ("LoadBSPFile: odd lump size");
+
+ memcpy (dest, (byte *)header + ofs, length);
+
+ return length / size;
+}
+
+/*
+=============
+LoadBSPFile
+=============
+*/
+void LoadBSPFile (char *filename)
+{
+ int i;
+
+//
+// load the file header
+//
+ LoadFile (filename, (void **)&header);
+
+// swap the header
+ for (i=0 ; i< sizeof(dheader_t)/4 ; i++)
+ ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
+
+ if (header->version != BSPVERSION)
+ Error ("%s is version %i, not %i", filename, i, BSPVERSION);
+
+ nummodels = CopyLump (LUMP_MODELS, dmodels, sizeof(dmodel_t));
+ numvertexes = CopyLump (LUMP_VERTEXES, dvertexes, sizeof(dvertex_t));
+ numplanes = CopyLump (LUMP_PLANES, dplanes, sizeof(dplane_t));
+ numleafs = CopyLump (LUMP_LEAFS, dleafs, sizeof(dleaf_t));
+ numnodes = CopyLump (LUMP_NODES, dnodes, sizeof(dnode_t));
+ numtexinfo = CopyLump (LUMP_TEXINFO, texinfo, sizeof(texinfo_t));
+ numclipnodes = CopyLump (LUMP_CLIPNODES, dclipnodes, sizeof(dclipnode_t));
+ numfaces = CopyLump (LUMP_FACES, dfaces, sizeof(dface_t));
+ nummarksurfaces = CopyLump (LUMP_MARKSURFACES, dmarksurfaces, sizeof(dmarksurfaces[0]));
+ numsurfedges = CopyLump (LUMP_SURFEDGES, dsurfedges, sizeof(dsurfedges[0]));
+ numedges = CopyLump (LUMP_EDGES, dedges, sizeof(dedge_t));
+
+ texdatasize = CopyLump (LUMP_TEXTURES, dtexdata, 1);
+ visdatasize = CopyLump (LUMP_VISIBILITY, dvisdata, 1);
+ lightdatasize = CopyLump (LUMP_LIGHTING, dlightdata, 1);
+ entdatasize = CopyLump (LUMP_ENTITIES, dentdata, 1);
+
+ free (header); // everything has been copied out
+
+//
+// swap everything
+//
+ SwapBSPFile (false);
+}
+
+//============================================================================
+
+FILE *wadfile;
+dheader_t outheader;
+
+void AddLump (int lumpnum, void *data, int len)
+{
+ lump_t *lump;
+
+ lump = &header->lumps[lumpnum];
+
+ lump->fileofs = LittleLong( ftell(wadfile) );
+ lump->filelen = LittleLong(len);
+ SafeWrite (wadfile, data, (len+3)&~3);
+}
+
+/*
+=============
+WriteBSPFile
+
+Swaps the bsp file in place, so it should not be referenced again
+=============
+*/
+void WriteBSPFile (char *filename)
+{
+ header = &outheader;
+ memset (header, 0, sizeof(dheader_t));
+
+ SwapBSPFile (true);
+
+ header->version = LittleLong (BSPVERSION);
+
+ wadfile = SafeOpenWrite (filename);
+ SafeWrite (wadfile, header, sizeof(dheader_t)); // overwritten later
+
+ AddLump (LUMP_PLANES, dplanes, numplanes*sizeof(dplane_t));
+ AddLump (LUMP_LEAFS, dleafs, numleafs*sizeof(dleaf_t));
+ AddLump (LUMP_VERTEXES, dvertexes, numvertexes*sizeof(dvertex_t));
+ AddLump (LUMP_NODES, dnodes, numnodes*sizeof(dnode_t));
+ AddLump (LUMP_TEXINFO, texinfo, numtexinfo*sizeof(texinfo_t));
+ AddLump (LUMP_FACES, dfaces, numfaces*sizeof(dface_t));
+ AddLump (LUMP_CLIPNODES, dclipnodes, numclipnodes*sizeof(dclipnode_t));
+ AddLump (LUMP_MARKSURFACES, dmarksurfaces, nummarksurfaces*sizeof(dmarksurfaces[0]));
+ AddLump (LUMP_SURFEDGES, dsurfedges, numsurfedges*sizeof(dsurfedges[0]));
+ AddLump (LUMP_EDGES, dedges, numedges*sizeof(dedge_t));
+ AddLump (LUMP_MODELS, dmodels, nummodels*sizeof(dmodel_t));
+
+ AddLump (LUMP_LIGHTING, dlightdata, lightdatasize);
+ AddLump (LUMP_VISIBILITY, dvisdata, visdatasize);
+ AddLump (LUMP_ENTITIES, dentdata, entdatasize);
+ AddLump (LUMP_TEXTURES, dtexdata, texdatasize);
+
+ fseek (wadfile, 0, SEEK_SET);
+ SafeWrite (wadfile, header, sizeof(dheader_t));
+ fclose (wadfile);
+}
+
+//============================================================================
+
+/*
+=============
+PrintBSPFileSizes
+
+Dumps info about current file
+=============
+*/
+void PrintBSPFileSizes (void)
+{
+ printf ("%5i planes %6i\n"
+ ,numplanes, (int)(numplanes*sizeof(dplane_t)));
+ printf ("%5i vertexes %6i\n"
+ ,numvertexes, (int)(numvertexes*sizeof(dvertex_t)));
+ printf ("%5i nodes %6i\n"
+ ,numnodes, (int)(numnodes*sizeof(dnode_t)));
+ printf ("%5i texinfo %6i\n"
+ ,numtexinfo, (int)(numtexinfo*sizeof(texinfo_t)));
+ printf ("%5i faces %6i\n"
+ ,numfaces, (int)(numfaces*sizeof(dface_t)));
+ printf ("%5i clipnodes %6i\n"
+ ,numclipnodes, (int)(numclipnodes*sizeof(dclipnode_t)));
+ printf ("%5i leafs %6i\n"
+ ,numleafs, (int)(numleafs*sizeof(dleaf_t)));
+ printf ("%5i marksurfaces %6i\n"
+ ,nummarksurfaces, (int)(nummarksurfaces*sizeof(dmarksurfaces[0])));
+ printf ("%5i surfedges %6i\n"
+ ,numsurfedges, (int)(numsurfedges*sizeof(dmarksurfaces[0])));
+ printf ("%5i edges %6i\n"
+ ,numedges, (int)(numedges*sizeof(dedge_t)));
+ if (!texdatasize)
+ printf (" 0 textures 0\n");
+ else
+ printf ("%5i textures %6i\n",((dmiptexlump_t*)dtexdata)->nummiptex, texdatasize);
+ printf (" lightdata %6i\n", lightdatasize);
+ printf (" visdata %6i\n", visdatasize);
+ printf (" entdata %6i\n", entdatasize);
+}
+
+
diff --git a/qutils/COMMON/BSPFILE.H b/qutils/COMMON/BSPFILE.H
new file mode 100644
index 0000000..28d4c53
--- /dev/null
+++ b/qutils/COMMON/BSPFILE.H
@@ -0,0 +1,251 @@
+
+
+// upper design bounds
+
+#define MAX_MAP_HULLS 4
+
+#define MAX_MAP_MODELS 256
+#define MAX_MAP_BRUSHES 4096
+#define MAX_MAP_ENTITIES 1024
+#define MAX_MAP_ENTSTRING 65536
+
+#define MAX_MAP_PLANES 8192
+#define MAX_MAP_NODES 32767 // because negative shorts are contents
+#define MAX_MAP_CLIPNODES 32767 //
+#define MAX_MAP_LEAFS 32767 //
+#define MAX_MAP_VERTS 65535
+#define MAX_MAP_FACES 65535
+#define MAX_MAP_MARKSURFACES 65535
+#define MAX_MAP_TEXINFO 4096
+#define MAX_MAP_EDGES 256000
+#define MAX_MAP_SURFEDGES 512000
+#define MAX_MAP_MIPTEX 0x200000
+#define MAX_MAP_LIGHTING 0x100000
+#define MAX_MAP_VISIBILITY 0x100000
+
+// key / value pair sizes
+
+#define MAX_KEY 32
+#define MAX_VALUE 1024
+
+//=============================================================================
+
+
+#define BSPVERSION 29
+
+typedef struct
+{
+ int fileofs, filelen;
+} lump_t;
+
+#define LUMP_ENTITIES 0
+#define LUMP_PLANES 1
+#define LUMP_TEXTURES 2
+#define LUMP_VERTEXES 3
+#define LUMP_VISIBILITY 4
+#define LUMP_NODES 5
+#define LUMP_TEXINFO 6
+#define LUMP_FACES 7
+#define LUMP_LIGHTING 8
+#define LUMP_CLIPNODES 9
+#define LUMP_LEAFS 10
+#define LUMP_MARKSURFACES 11
+#define LUMP_EDGES 12
+#define LUMP_SURFEDGES 13
+#define LUMP_MODELS 14
+
+#define HEADER_LUMPS 15
+
+typedef struct
+{
+ float mins[3], maxs[3];
+ float origin[3];
+ int headnode[MAX_MAP_HULLS];
+ int visleafs; // not including the solid leaf 0
+ int firstface, numfaces;
+} dmodel_t;
+
+typedef struct
+{
+ int version;
+ lump_t lumps[HEADER_LUMPS];
+} dheader_t;
+
+typedef struct
+{
+ int nummiptex;
+ int dataofs[4]; // [nummiptex]
+} dmiptexlump_t;
+
+#define MIPLEVELS 4
+typedef struct miptex_s
+{
+ char name[16];
+ unsigned width, height;
+ unsigned offsets[MIPLEVELS]; // four mip maps stored
+} miptex_t;
+
+
+typedef struct
+{
+ float point[3];
+} dvertex_t;
+
+
+// 0-2 are axial planes
+#define PLANE_X 0
+#define PLANE_Y 1
+#define PLANE_Z 2
+
+// 3-5 are non-axial planes snapped to the nearest
+#define PLANE_ANYX 3
+#define PLANE_ANYY 4
+#define PLANE_ANYZ 5
+
+typedef struct
+{
+ float normal[3];
+ float dist;
+ int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate
+} dplane_t;
+
+
+
+#define CONTENTS_EMPTY -1
+#define CONTENTS_SOLID -2
+#define CONTENTS_WATER -3
+#define CONTENTS_SLIME -4
+#define CONTENTS_LAVA -5
+#define CONTENTS_SKY -6
+
+// !!! if this is changed, it must be changed in asm_i386.h too !!!
+typedef struct
+{
+ int planenum;
+ short children[2]; // negative numbers are -(leafs+1), not nodes
+ short mins[3]; // for sphere culling
+ short maxs[3];
+ unsigned short firstface;
+ unsigned short numfaces; // counting both sides
+} dnode_t;
+
+typedef struct
+{
+ int planenum;
+ short children[2]; // negative numbers are contents
+} dclipnode_t;
+
+
+typedef struct texinfo_s
+{
+ float vecs[2][4]; // [s/t][xyz offset]
+ int miptex;
+ int flags;
+} texinfo_t;
+#define TEX_SPECIAL 1 // sky or slime, no lightmap or 256 subdivision
+
+// note that edge 0 is never used, because negative edge nums are used for
+// counterclockwise use of the edge in a face
+typedef struct
+{
+ unsigned short v[2]; // vertex numbers
+} dedge_t;
+
+#define MAXLIGHTMAPS 4
+typedef struct
+{
+ short planenum;
+ short side;
+
+ int firstedge; // we must support > 64k edges
+ short numedges;
+ short texinfo;
+
+// lighting info
+ byte styles[MAXLIGHTMAPS];
+ int lightofs; // start of [numstyles*surfsize] samples
+} dface_t;
+
+
+
+#define AMBIENT_WATER 0
+#define AMBIENT_SKY 1
+#define AMBIENT_SLIME 2
+#define AMBIENT_LAVA 3
+
+#define NUM_AMBIENTS 4 // automatic ambient sounds
+
+// leaf 0 is the generic CONTENTS_SOLID leaf, used for all solid areas
+// all other leafs need visibility info
+typedef struct
+{
+ int contents;
+ int visofs; // -1 = no visibility info
+
+ short mins[3]; // for frustum culling
+ short maxs[3];
+
+ unsigned short firstmarksurface;
+ unsigned short nummarksurfaces;
+
+ byte ambient_level[NUM_AMBIENTS];
+} dleaf_t;
+
+//============================================================================
+
+#ifndef QUAKE_GAME
+
+// the utilities get to be lazy and just use large static arrays
+
+extern int nummodels;
+extern dmodel_t dmodels[MAX_MAP_MODELS];
+
+extern int visdatasize;
+extern byte dvisdata[MAX_MAP_VISIBILITY];
+
+extern int lightdatasize;
+extern byte dlightdata[MAX_MAP_LIGHTING];
+
+extern int texdatasize;
+extern byte dtexdata[MAX_MAP_MIPTEX]; // (dmiptexlump_t)
+
+extern int entdatasize;
+extern char dentdata[MAX_MAP_ENTSTRING];
+
+extern int numleafs;
+extern dleaf_t dleafs[MAX_MAP_LEAFS];
+
+extern int numplanes;
+extern dplane_t dplanes[MAX_MAP_PLANES];
+
+extern int numvertexes;
+extern dvertex_t dvertexes[MAX_MAP_VERTS];
+
+extern int numnodes;
+extern dnode_t dnodes[MAX_MAP_NODES];
+
+extern int numtexinfo;
+extern texinfo_t texinfo[MAX_MAP_TEXINFO];
+
+extern int numfaces;
+extern dface_t dfaces[MAX_MAP_FACES];
+
+extern int numclipnodes;
+extern dclipnode_t dclipnodes[MAX_MAP_CLIPNODES];
+
+extern int numedges;
+extern dedge_t dedges[MAX_MAP_EDGES];
+
+extern int nummarksurfaces;
+extern unsigned short dmarksurfaces[MAX_MAP_MARKSURFACES];
+
+extern int numsurfedges;
+extern int dsurfedges[MAX_MAP_SURFEDGES];
+
+
+
+void LoadBSPFile (char *filename);
+void WriteBSPFile (char *filename);
+void PrintBSPFileSizes (void);
+
+#endif
diff --git a/qutils/COMMON/CMDLIB.C b/qutils/COMMON/CMDLIB.C
new file mode 100644
index 0000000..af7ee52
--- /dev/null
+++ b/qutils/COMMON/CMDLIB.C
@@ -0,0 +1,867 @@
+// cmdlib.c
+
+#include "cmdlib.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef WIN32
+#include <direct.h>
+#endif
+
+#ifdef NeXT
+#include <libc.h>
+#endif
+
+#define PATHSEPERATOR '/'
+
+// set these before calling CheckParm
+int myargc;
+char **myargv;
+
+char com_token[1024];
+qboolean com_eof;
+
+qboolean archive;
+char archivedir[1024];
+
+
+/*
+=================
+Error
+
+For abnormal program terminations
+=================
+*/
+void Error (char *error, ...)
+{
+ va_list argptr;
+
+ printf ("************ ERROR ************\n");
+
+ va_start (argptr,error);
+ vprintf (error,argptr);
+ va_end (argptr);
+ printf ("\n");
+ exit (1);
+}
+
+
+/*
+
+qdir will hold the path up to the quake directory, including the slash
+
+ f:\quake\
+ /raid/quake/
+
+gamedir will hold qdir + the game directory (id1, id2, etc)
+
+ */
+
+char qdir[1024];
+char gamedir[1024];
+
+void SetQdirFromPath (char *path)
+{
+ char temp[1024];
+ char *c;
+
+ if (!(path[0] == '/' || path[0] == '\\' || path[1] == ':'))
+ { // path is partial
+ Q_getwd (temp);
+ strcat (temp, path);
+ path = temp;
+ }
+
+ // search for "quake" in path
+
+ for (c=path ; *c ; c++)
+ if (!Q_strncasecmp (c, "quake", 5))
+ {
+ strncpy (qdir, path, c+6-path);
+ printf ("qdir: %s\n", qdir);
+ c += 6;
+ while (*c)
+ {
+ if (*c == '/' || *c == '\\')
+ {
+ strncpy (gamedir, path, c+1-path);
+ printf ("gamedir: %s\n", gamedir);
+ return;
+ }
+ c++;
+ }
+ Error ("No gamedir in %s", path);
+ return;
+ }
+ Error ("SeetQdirFromPath: no 'quake' in %s", path);
+}
+
+char *ExpandPath (char *path)
+{
+ static char full[1024];
+ if (!qdir)
+ Error ("ExpandPath called without qdir set");
+ if (path[0] == '/' || path[0] == '\\' || path[1] == ':')
+ return path;
+ sprintf (full, "%s%s", qdir, path);
+ return full;
+}
+
+char *ExpandPathAndArchive (char *path)
+{
+ char *expanded;
+ char archivename[1024];
+
+ expanded = ExpandPath (path);
+
+ if (archive)
+ {
+ sprintf (archivename, "%s/%s", archivedir, path);
+ CopyFile (expanded, archivename);
+ }
+ return expanded;
+}
+
+
+char *copystring(char *s)
+{
+ char *b;
+ b = malloc(strlen(s)+1);
+ strcpy (b, s);
+ return b;
+}
+
+
+
+/*
+================
+I_FloatTime
+================
+*/
+double I_FloatTime (void)
+{
+ time_t t;
+
+ time (&t);
+
+ return t;
+#if 0
+// more precise, less portable
+ struct timeval tp;
+ struct timezone tzp;
+ static int secbase;
+
+ gettimeofday(&tp, &tzp);
+
+ if (!secbase)
+ {
+ secbase = tp.tv_sec;
+ return tp.tv_usec/1000000.0;
+ }
+
+ return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0;
+#endif
+}
+
+void Q_getwd (char *out)
+{
+#ifdef WIN32
+ _getcwd (out, 256);
+ strcat (out, "\\");
+#else
+ getwd (out);
+#endif
+}
+
+
+void Q_mkdir (char *path)
+{
+#ifdef WIN32
+ if (_mkdir (path) != -1)
+ return;
+#else
+ if (mkdir (path, 0777) != -1)
+ return;
+#endif
+ if (errno != EEXIST)
+ Error ("mkdir %s: %s",path, strerror(errno));
+}
+
+/*
+============
+FileTime
+
+returns -1 if not present
+============
+*/
+int FileTime (char *path)
+{
+ struct stat buf;
+
+ if (stat (path,&buf) == -1)
+ return -1;
+
+ return buf.st_mtime;
+}
+
+
+
+/*
+==============
+COM_Parse
+
+Parse a token out of a string
+==============
+*/
+char *COM_Parse (char *data)
+{
+ int c;
+ int len;
+
+ len = 0;
+ com_token[0] = 0;
+
+ if (!data)
+ return NULL;
+
+// skip whitespace
+skipwhite:
+ while ( (c = *data) <= ' ')
+ {
+ if (c == 0)
+ {
+ com_eof = true;
+ return NULL; // end of file;
+ }
+ data++;
+ }
+
+// skip // comments
+ if (c=='/' && data[1] == '/')
+ {
+ while (*data && *data != '\n')
+ data++;
+ goto skipwhite;
+ }
+
+
+// handle quoted strings specially
+ if (c == '\"')
+ {
+ data++;
+ do
+ {
+ c = *data++;
+ if (c=='\"')
+ {
+ com_token[len] = 0;
+ return data;
+ }
+ com_token[len] = c;
+ len++;
+ } while (1);
+ }
+
+// parse single characters
+ if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
+ {
+ com_token[len] = c;
+ len++;
+ com_token[len] = 0;
+ return data+1;
+ }
+
+// parse a regular word
+ do
+ {
+ com_token[len] = c;
+ data++;
+ len++;
+ c = *data;
+ if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
+ break;
+ } while (c>32);
+
+ com_token[len] = 0;
+ return data;
+}
+
+
+int Q_strncasecmp (char *s1, char *s2, int n)
+{
+ int c1, c2;
+
+ while (1)
+ {
+ c1 = *s1++;
+ c2 = *s2++;
+
+ if (!n--)
+ return 0; // strings are equal until end point
+
+ if (c1 != c2)
+ {
+ if (c1 >= 'a' && c1 <= 'z')
+ c1 -= ('a' - 'A');
+ if (c2 >= 'a' && c2 <= 'z')
+ c2 -= ('a' - 'A');
+ if (c1 != c2)
+ return -1; // strings not equal
+ }
+ if (!c1)
+ return 0; // strings are equal
+ }
+
+ return -1;
+}
+
+int Q_strcasecmp (char *s1, char *s2)
+{
+ return Q_strncasecmp (s1, s2, 99999);
+}
+
+
+char *strupr (char *start)
+{
+ char *in;
+ in = start;
+ while (*in)
+ {
+ *in = toupper(*in);
+ in++;
+ }
+ return start;
+}
+
+char *strlower (char *start)
+{
+ char *in;
+ in = start;
+ while (*in)
+ {
+ *in = tolower(*in);
+ in++;
+ }
+ return start;
+}
+
+
+/*
+=============================================================================
+
+ MISC FUNCTIONS
+
+=============================================================================
+*/
+
+
+/*
+=================
+CheckParm
+
+Checks for the given parameter in the program's command line arguments
+Returns the argument number (1 to argc-1) or 0 if not present
+=================
+*/
+int CheckParm (char *check)
+{
+ int i;
+
+ for (i = 1;i<myargc;i++)
+ {
+ if ( !Q_strcasecmp(check, myargv[i]) )
+ return i;
+ }
+
+ return 0;
+}
+
+
+
+/*
+================
+filelength
+================
+*/
+int filelength (FILE *f)
+{
+ int pos;
+ int end;
+
+ pos = ftell (f);
+ fseek (f, 0, SEEK_END);
+ end = ftell (f);
+ fseek (f, pos, SEEK_SET);
+
+ return end;
+}
+
+
+FILE *SafeOpenWrite (char *filename)
+{
+ FILE *f;
+
+ f = fopen(filename, "wb");
+
+ if (!f)
+ Error ("Error opening %s: %s",filename,strerror(errno));
+
+ return f;
+}
+
+FILE *SafeOpenRead (char *filename)
+{
+ FILE *f;
+
+ f = fopen(filename, "rb");
+
+ if (!f)
+ Error ("Error opening %s: %s",filename,strerror(errno));
+
+ return f;
+}
+
+
+void SafeRead (FILE *f, void *buffer, int count)
+{
+ if ( fread (buffer, 1, count, f) != (size_t)count)
+ Error ("File read failure");
+}
+
+
+void SafeWrite (FILE *f, void *buffer, int count)
+{
+ if (fwrite (buffer, 1, count, f) != (size_t)count)
+ Error ("File read failure");
+}
+
+
+
+/*
+==============
+LoadFile
+==============
+*/
+int LoadFile (char *filename, void **bufferptr)
+{
+ FILE *f;
+ int length;
+ void *buffer;
+
+ f = SafeOpenRead (filename);
+ length = filelength (f);
+ buffer = malloc (length+1);
+ ((char *)buffer)[length] = 0;
+ SafeRead (f, buffer, length);
+ fclose (f);
+
+ *bufferptr = buffer;
+ return length;
+}
+
+
+/*
+==============
+SaveFile
+==============
+*/
+void SaveFile (char *filename, void *buffer, int count)
+{
+ FILE *f;
+
+ f = SafeOpenWrite (filename);
+ SafeWrite (f, buffer, count);
+ fclose (f);
+}
+
+
+
+void DefaultExtension (char *path, char *extension)
+{
+ char *src;
+//
+// if path doesn't have a .EXT, append extension
+// (extension should include the .)
+//
+ src = path + strlen(path) - 1;
+
+ while (*src != PATHSEPERATOR && src != path)
+ {
+ if (*src == '.')
+ return; // it has an extension
+ src--;
+ }
+
+ strcat (path, extension);
+}
+
+
+void DefaultPath (char *path, char *basepath)
+{
+ char temp[128];
+
+ if (path[0] == PATHSEPERATOR)
+ return; // absolute path location
+ strcpy (temp,path);
+ strcpy (path,basepath);
+ strcat (path,temp);
+}
+
+
+void StripFilename (char *path)
+{
+ int length;
+
+ length = strlen(path)-1;
+ while (length > 0 && path[length] != PATHSEPERATOR)
+ length--;
+ path[length] = 0;
+}
+
+void StripExtension (char *path)
+{
+ int length;
+
+ length = strlen(path)-1;
+ while (length > 0 && path[length] != '.')
+ {
+ length--;
+ if (path[length] == '/')
+ return; // no extension
+ }
+ if (length)
+ path[length] = 0;
+}
+
+
+/*
+====================
+Extract file parts
+====================
+*/
+void ExtractFilePath (char *path, char *dest)
+{
+ char *src;
+
+ src = path + strlen(path) - 1;
+
+//
+// back up until a \ or the start
+//
+ while (src != path && *(src-1) != PATHSEPERATOR)
+ src--;
+
+ memcpy (dest, path, src-path);
+ dest[src-path] = 0;
+}
+
+void ExtractFileBase (char *path, char *dest)
+{
+ char *src;
+
+ src = path + strlen(path) - 1;
+
+//
+// back up until a \ or the start
+//
+ while (src != path && *(src-1) != PATHSEPERATOR)
+ src--;
+
+ while (*src && *src != '.')
+ {
+ *dest++ = *src++;
+ }
+ *dest = 0;
+}
+
+void ExtractFileExtension (char *path, char *dest)
+{
+ char *src;
+
+ src = path + strlen(path) - 1;
+
+//
+// back up until a . or the start
+//
+ while (src != path && *(src-1) != '.')
+ src--;
+ if (src == path)
+ {
+ *dest = 0; // no extension
+ return;
+ }
+
+ strcpy (dest,src);
+}
+
+
+/*
+==============
+ParseNum / ParseHex
+==============
+*/
+int ParseHex (char *hex)
+{
+ char *str;
+ int num;
+
+ num = 0;
+ str = hex;
+
+ while (*str)
+ {
+ num <<= 4;
+ if (*str >= '0' && *str <= '9')
+ num += *str-'0';
+ else if (*str >= 'a' && *str <= 'f')
+ num += 10 + *str-'a';
+ else if (*str >= 'A' && *str <= 'F')
+ num += 10 + *str-'A';
+ else
+ Error ("Bad hex number: %s",hex);
+ str++;
+ }
+
+ return num;
+}
+
+
+int ParseNum (char *str)
+{
+ if (str[0] == '$')
+ return ParseHex (str+1);
+ if (str[0] == '0' && str[1] == 'x')
+ return ParseHex (str+2);
+ return atol (str);
+}
+
+
+
+/*
+============================================================================
+
+ BYTE ORDER FUNCTIONS
+
+============================================================================
+*/
+
+#ifdef _SGI_SOURCE
+#define __BIG_ENDIAN__
+#endif
+
+#ifdef __BIG_ENDIAN__
+
+short LittleShort (short l)
+{
+ byte b1,b2;
+
+ b1 = l&255;
+ b2 = (l>>8)&255;
+
+ return (b1<<8) + b2;
+}
+
+short BigShort (short l)
+{
+ return l;
+}
+
+
+int LittleLong (int l)
+{
+ byte b1,b2,b3,b4;
+
+ b1 = l&255;
+ b2 = (l>>8)&255;
+ b3 = (l>>16)&255;
+ b4 = (l>>24)&255;
+
+ return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
+}
+
+int BigLong (int l)
+{
+ return l;
+}
+
+
+float LittleFloat (float l)
+{
+ union {byte b[4]; float f;} in, out;
+
+ in.f = l;
+ out.b[0] = in.b[3];
+ out.b[1] = in.b[2];
+ out.b[2] = in.b[1];
+ out.b[3] = in.b[0];
+
+ return out.f;
+}
+
+float BigFloat (float l)
+{
+ return l;
+}
+
+
+#else
+
+
+short BigShort (short l)
+{
+ byte b1,b2;
+
+ b1 = l&255;
+ b2 = (l>>8)&255;
+
+ return (b1<<8) + b2;
+}
+
+short LittleShort (short l)
+{
+ return l;
+}
+
+
+int BigLong (int l)
+{
+ byte b1,b2,b3,b4;
+
+ b1 = l&255;
+ b2 = (l>>8)&255;
+ b3 = (l>>16)&255;
+ b4 = (l>>24)&255;
+
+ return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
+}
+
+int LittleLong (int l)
+{
+ return l;
+}
+
+float BigFloat (float l)
+{
+ union {byte b[4]; float f;} in, out;
+
+ in.f = l;
+ out.b[0] = in.b[3];
+ out.b[1] = in.b[2];
+ out.b[2] = in.b[1];
+ out.b[3] = in.b[0];
+
+ return out.f;
+}
+
+float LittleFloat (float l)
+{
+ return l;
+}
+
+
+#endif
+
+
+//=======================================================
+
+
+// FIXME: byte swap?
+
+// this is a 16 bit, non-reflected CRC using the polynomial 0x1021
+// and the initial and final xor values shown below... in other words, the
+// CCITT standard CRC used by XMODEM
+
+#define CRC_INIT_VALUE 0xffff
+#define CRC_XOR_VALUE 0x0000
+
+static unsigned short crctable[256] =
+{
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
+ 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
+ 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
+ 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
+ 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
+ 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
+ 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
+ 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
+ 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
+ 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
+ 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
+ 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
+ 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
+ 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
+ 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
+ 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
+ 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
+ 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
+ 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
+ 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
+ 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
+ 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+ 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
+ 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
+ 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
+ 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
+ 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
+ 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
+ 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
+ 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
+ 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
+ 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
+};
+
+void CRC_Init(unsigned short *crcvalue)
+{
+ *crcvalue = CRC_INIT_VALUE;
+}
+
+void CRC_ProcessByte(unsigned short *crcvalue, byte data)
+{
+ *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data];
+}
+
+unsigned short CRC_Value(unsigned short crcvalue)
+{
+ return crcvalue ^ CRC_XOR_VALUE;
+}
+//=============================================================================
+
+/*
+============
+CreatePath
+============
+*/
+void CreatePath (char *path)
+{
+ char *ofs, c;
+
+ for (ofs = path+1 ; *ofs ; ofs++)
+ {
+ c = *ofs;
+ if (c == '/' || c == '\\')
+ { // create the directory
+ *ofs = 0;
+ Q_mkdir (path);
+ *ofs = c;
+ }
+ }
+}
+
+
+/*
+============
+CopyFile
+
+ Used to archive source files
+============
+*/
+void CopyFile (char *from, char *to)
+{
+ void *buffer;
+ int length;
+
+ length = LoadFile (from, &buffer);
+ CreatePath (to);
+ SaveFile (to, buffer, length);
+ free (buffer);
+}
diff --git a/qutils/COMMON/CMDLIB.H b/qutils/COMMON/CMDLIB.H
new file mode 100644
index 0000000..b556288
--- /dev/null
+++ b/qutils/COMMON/CMDLIB.H
@@ -0,0 +1,97 @@
+// cmdlib.h
+
+#ifndef __CMDLIB__
+#define __CMDLIB__
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <time.h>
+#include <stdarg.h>
+
+#ifndef __BYTEBOOL__
+#define __BYTEBOOL__
+typedef enum {false, true} qboolean;
+typedef unsigned char byte;
+#endif
+
+// the dec offsetof macro doesn't work very well...
+#define myoffsetof(type,identifier) ((size_t)&((type *)0)->identifier)
+
+
+// set these before calling CheckParm
+extern int myargc;
+extern char **myargv;
+
+char *strupr (char *in);
+char *strlower (char *in);
+int Q_strncasecmp (char *s1, char *s2, int n);
+int Q_strcasecmp (char *s1, char *s2);
+void Q_getwd (char *out);
+
+int filelength (FILE *f);
+int FileTime (char *path);
+
+void Q_mkdir (char *path);
+
+extern char qdir[1024];
+extern char gamedir[1024];
+void SetQdirFromPath (char *path);
+char *ExpandPath (char *path);
+char *ExpandPathAndArchive (char *path);
+
+
+double I_FloatTime (void);
+
+void Error (char *error, ...);
+int CheckParm (char *check);
+
+FILE *SafeOpenWrite (char *filename);
+FILE *SafeOpenRead (char *filename);
+void SafeRead (FILE *f, void *buffer, int count);
+void SafeWrite (FILE *f, void *buffer, int count);
+
+int LoadFile (char *filename, void **bufferptr);
+void SaveFile (char *filename, void *buffer, int count);
+
+void DefaultExtension (char *path, char *extension);
+void DefaultPath (char *path, char *basepath);
+void StripFilename (char *path);
+void StripExtension (char *path);
+
+void ExtractFilePath (char *path, char *dest);
+void ExtractFileBase (char *path, char *dest);
+void ExtractFileExtension (char *path, char *dest);
+
+int ParseNum (char *str);
+
+short BigShort (short l);
+short LittleShort (short l);
+int BigLong (int l);
+int LittleLong (int l);
+float BigFloat (float l);
+float LittleFloat (float l);
+
+
+char *COM_Parse (char *data);
+
+extern char com_token[1024];
+extern qboolean com_eof;
+
+char *copystring(char *s);
+
+
+void CRC_Init(unsigned short *crcvalue);
+void CRC_ProcessByte(unsigned short *crcvalue, byte data);
+unsigned short CRC_Value(unsigned short crcvalue);
+
+void CreatePath (char *path);
+void CopyFile (char *from, char *to);
+
+extern qboolean archive;
+extern char archivedir[1024];
+
+
+#endif
diff --git a/qutils/COMMON/LBMLIB.C b/qutils/COMMON/LBMLIB.C
new file mode 100644
index 0000000..c64f44e
--- /dev/null
+++ b/qutils/COMMON/LBMLIB.C
@@ -0,0 +1,508 @@
+// lbmlib.c
+
+#include "cmdlib.h"
+#include "lbmlib.h"
+
+
+
+/*
+============================================================================
+
+ LBM STUFF
+
+============================================================================
+*/
+
+
+#define FORMID ('F'+('O'<<8)+((int)'R'<<16)+((int)'M'<<24))
+#define ILBMID ('I'+('L'<<8)+((int)'B'<<16)+((int)'M'<<24))
+#define PBMID ('P'+('B'<<8)+((int)'M'<<16)+((int)' '<<24))
+#define BMHDID ('B'+('M'<<8)+((int)'H'<<16)+((int)'D'<<24))
+#define BODYID ('B'+('O'<<8)+((int)'D'<<16)+((int)'Y'<<24))
+#define CMAPID ('C'+('M'<<8)+((int)'A'<<16)+((int)'P'<<24))
+
+
+bmhd_t bmhd;
+
+int Align (int l)
+{
+ if (l&1)
+ return l+1;
+ return l;
+}
+
+
+
+/*
+================
+=
+= LBMRLEdecompress
+=
+= Source must be evenly aligned!
+=
+================
+*/
+
+byte *LBMRLEDecompress (byte *source,byte *unpacked, int bpwidth)
+{
+ int count;
+ byte b,rept;
+
+ count = 0;
+
+ do
+ {
+ rept = *source++;
+
+ if (rept > 0x80)
+ {
+ rept = (rept^0xff)+2;
+ b = *source++;
+ memset(unpacked,b,rept);
+ unpacked += rept;
+ }
+ else if (rept < 0x80)
+ {
+ rept++;
+ memcpy(unpacked,source,rept);
+ unpacked += rept;
+ source += rept;
+ }
+ else
+ rept = 0; // rept of 0x80 is NOP
+
+ count += rept;
+
+ } while (count<bpwidth);
+
+ if (count>bpwidth)
+ Error ("Decompression exceeded width!\n");
+
+
+ return source;
+}
+
+
+#define BPLANESIZE 128
+byte bitplanes[9][BPLANESIZE]; // max size 1024 by 9 bit planes
+
+
+/*
+=================
+=
+= MungeBitPlanes8
+=
+= This destroys the bit plane data!
+=
+=================
+*/
+
+void MungeBitPlanes8 (int width, byte *dest)
+{
+ *dest=width; // shut up the compiler warning
+ Error ("MungeBitPlanes8 not rewritten!");
+#if 0
+asm les di,[dest]
+asm mov si,-1
+asm mov cx,[width]
+mungebyte:
+asm inc si
+asm mov dx,8
+mungebit:
+asm shl [BYTE PTR bitplanes + BPLANESIZE*7 +si],1
+asm rcl al,1
+asm shl [BYTE PTR bitplanes + BPLANESIZE*6 +si],1
+asm rcl al,1
+asm shl [BYTE PTR bitplanes + BPLANESIZE*5 +si],1
+asm rcl al,1
+asm shl [BYTE PTR bitplanes + BPLANESIZE*4 +si],1
+asm rcl al,1
+asm shl [BYTE PTR bitplanes + BPLANESIZE*3 +si],1
+asm rcl al,1
+asm shl [BYTE PTR bitplanes + BPLANESIZE*2 +si],1
+asm rcl al,1
+asm shl [BYTE PTR bitplanes + BPLANESIZE*1 +si],1
+asm rcl al,1
+asm shl [BYTE PTR bitplanes + BPLANESIZE*0 +si],1
+asm rcl al,1
+asm stosb
+asm dec cx
+asm jz done
+asm dec dx
+asm jnz mungebit
+asm jmp mungebyte
+
+done:
+#endif
+}
+
+
+void MungeBitPlanes4 (int width, byte *dest)
+{
+ *dest=width; // shut up the compiler warning
+ Error ("MungeBitPlanes4 not rewritten!");
+#if 0
+
+asm les di,[dest]
+asm mov si,-1
+asm mov cx,[width]
+mungebyte:
+asm inc si
+asm mov dx,8
+mungebit:
+asm xor al,al
+asm shl [BYTE PTR bitplanes + BPLANESIZE*3 +si],1
+asm rcl al,1
+asm shl [BYTE PTR bitplanes + BPLANESIZE*2 +si],1
+asm rcl al,1
+asm shl [BYTE PTR bitplanes + BPLANESIZE*1 +si],1
+asm rcl al,1
+asm shl [BYTE PTR bitplanes + BPLANESIZE*0 +si],1
+asm rcl al,1
+asm stosb
+asm dec cx
+asm jz done
+asm dec dx
+asm jnz mungebit
+asm jmp mungebyte
+
+done:
+#endif
+}
+
+
+void MungeBitPlanes2 (int width, byte *dest)
+{
+ *dest=width; // shut up the compiler warning
+ Error ("MungeBitPlanes2 not rewritten!");
+#if 0
+asm les di,[dest]
+asm mov si,-1
+asm mov cx,[width]
+mungebyte:
+asm inc si
+asm mov dx,8
+mungebit:
+asm xor al,al
+asm shl [BYTE PTR bitplanes + BPLANESIZE*1 +si],1
+asm rcl al,1
+asm shl [BYTE PTR bitplanes + BPLANESIZE*0 +si],1
+asm rcl al,1
+asm stosb
+asm dec cx
+asm jz done
+asm dec dx
+asm jnz mungebit
+asm jmp mungebyte
+
+done:
+#endif
+}
+
+
+void MungeBitPlanes1 (int width, byte *dest)
+{
+ *dest=width; // shut up the compiler warning
+ Error ("MungeBitPlanes1 not rewritten!");
+#if 0
+asm les di,[dest]
+asm mov si,-1
+asm mov cx,[width]
+mungebyte:
+asm inc si
+asm mov dx,8
+mungebit:
+asm xor al,al
+asm shl [BYTE PTR bitplanes + BPLANESIZE*0 +si],1
+asm rcl al,1
+asm stosb
+asm dec cx
+asm jz done
+asm dec dx
+asm jnz mungebit
+asm jmp mungebyte
+
+done:
+#endif
+}
+
+
+/*
+=================
+=
+= LoadLBM
+=
+=================
+*/
+
+void LoadLBM (char *filename, byte **picture, byte **palette)
+{
+ byte *LBMbuffer, *picbuffer, *cmapbuffer;
+ int y,p,planes;
+ byte *LBM_P, *LBMEND_P;
+ byte *pic_p;
+ byte *body_p;
+ unsigned rowsize;
+
+ int formtype,formlength;
+ int chunktype,chunklength;
+ void (*mungecall) (int, byte *);
+
+// qiet compiler warnings
+ picbuffer = NULL;
+ cmapbuffer = NULL;
+ mungecall = NULL;
+
+//
+// load the LBM
+//
+ LoadFile (filename, (void **)&LBMbuffer);
+
+//
+// parse the LBM header
+//
+ LBM_P = LBMbuffer;
+ if ( *(int *)LBMbuffer != LittleLong(FORMID) )
+ Error ("No FORM ID at start of file!\n");
+
+ LBM_P += 4;
+ formlength = BigLong( *(int *)LBM_P );
+ LBM_P += 4;
+ LBMEND_P = LBM_P + Align(formlength);
+
+ formtype = LittleLong(*(int *)LBM_P);
+
+ if (formtype != ILBMID && formtype != PBMID)
+ Error ("Unrecognized form type: %c%c%c%c\n", formtype&0xff
+ ,(formtype>>8)&0xff,(formtype>>16)&0xff,(formtype>>24)&0xff);
+
+ LBM_P += 4;
+
+//
+// parse chunks
+//
+
+ while (LBM_P < LBMEND_P)
+ {
+ chunktype = LBM_P[0] + (LBM_P[1]<<8) + (LBM_P[2]<<16) + (LBM_P[3]<<24);
+ LBM_P += 4;
+ chunklength = LBM_P[3] + (LBM_P[2]<<8) + (LBM_P[1]<<16) + (LBM_P[0]<<24);
+ LBM_P += 4;
+
+ switch ( chunktype )
+ {
+ case BMHDID:
+ memcpy (&bmhd,LBM_P,sizeof(bmhd));
+ bmhd.w = BigShort(bmhd.w);
+ bmhd.h = BigShort(bmhd.h);
+ bmhd.x = BigShort(bmhd.x);
+ bmhd.y = BigShort(bmhd.y);
+ bmhd.pageWidth = BigShort(bmhd.pageWidth);
+ bmhd.pageHeight = BigShort(bmhd.pageHeight);
+ break;
+
+ case CMAPID:
+ cmapbuffer = malloc (768);
+ memset (cmapbuffer, 0, 768);
+ memcpy (cmapbuffer, LBM_P, chunklength);
+ break;
+
+ case BODYID:
+ body_p = LBM_P;
+
+ pic_p = picbuffer = malloc (bmhd.w*bmhd.h);
+ if (formtype == PBMID)
+ {
+ //
+ // unpack PBM
+ //
+ for (y=0 ; y<bmhd.h ; y++, pic_p += bmhd.w)
+ {
+ if (bmhd.compression == cm_rle1)
+ body_p = LBMRLEDecompress ((byte *)body_p
+ , pic_p , bmhd.w);
+ else if (bmhd.compression == cm_none)
+ {
+ memcpy (pic_p,body_p,bmhd.w);
+ body_p += Align(bmhd.w);
+ }
+ }
+
+ }
+ else
+ {
+ //
+ // unpack ILBM
+ //
+ planes = bmhd.nPlanes;
+ if (bmhd.masking == ms_mask)
+ planes++;
+ rowsize = (bmhd.w+15)/16 * 2;
+ switch (bmhd.nPlanes)
+ {
+ case 1:
+ mungecall = MungeBitPlanes1;
+ break;
+ case 2:
+ mungecall = MungeBitPlanes2;
+ break;
+ case 4:
+ mungecall = MungeBitPlanes4;
+ break;
+ case 8:
+ mungecall = MungeBitPlanes8;
+ break;
+ default:
+ Error ("Can't munge %i bit planes!\n",bmhd.nPlanes);
+ }
+
+ for (y=0 ; y<bmhd.h ; y++, pic_p += bmhd.w)
+ {
+ for (p=0 ; p<planes ; p++)
+ if (bmhd.compression == cm_rle1)
+ body_p = LBMRLEDecompress ((byte *)body_p
+ , bitplanes[p] , rowsize);
+ else if (bmhd.compression == cm_none)
+ {
+ memcpy (bitplanes[p],body_p,rowsize);
+ body_p += rowsize;
+ }
+
+ mungecall (bmhd.w , pic_p);
+ }
+ }
+ break;
+ }
+
+ LBM_P += Align(chunklength);
+ }
+
+ free (LBMbuffer);
+
+ *picture = picbuffer;
+ *palette = cmapbuffer;
+}
+
+
+/*
+============================================================================
+
+ WRITE LBM
+
+============================================================================
+*/
+
+/*
+==============
+=
+= WriteLBMfile
+=
+==============
+*/
+
+void WriteLBMfile (char *filename, byte *data, int width, int height, byte *palette)
+{
+ byte *lbm, *lbmptr;
+ int *formlength, *bmhdlength, *cmaplength, *bodylength;
+ int length;
+ bmhd_t basebmhd;
+
+ lbm = lbmptr = malloc (width*height+1000);
+
+//
+// start FORM
+//
+ *lbmptr++ = 'F';
+ *lbmptr++ = 'O';
+ *lbmptr++ = 'R';
+ *lbmptr++ = 'M';
+
+ formlength = (int*)lbmptr;
+ lbmptr+=4; // leave space for length
+
+ *lbmptr++ = 'P';
+ *lbmptr++ = 'B';
+ *lbmptr++ = 'M';
+ *lbmptr++ = ' ';
+
+//
+// write BMHD
+//
+ *lbmptr++ = 'B';
+ *lbmptr++ = 'M';
+ *lbmptr++ = 'H';
+ *lbmptr++ = 'D';
+
+ bmhdlength = (int *)lbmptr;
+ lbmptr+=4; // leave space for length
+
+ memset (&basebmhd,0,sizeof(basebmhd));
+ basebmhd.w = BigShort((short)width);
+ basebmhd.h = BigShort((short)height);
+ basebmhd.nPlanes = BigShort(8);
+ basebmhd.xAspect = BigShort(5);
+ basebmhd.yAspect = BigShort(6);
+ basebmhd.pageWidth = BigShort((short)width);
+ basebmhd.pageHeight = BigShort((short)height);
+
+ memcpy (lbmptr,&basebmhd,sizeof(basebmhd));
+ lbmptr += sizeof(basebmhd);
+
+ length = lbmptr-(byte *)bmhdlength-4;
+ *bmhdlength = BigLong(length);
+ if (length&1)
+ *lbmptr++ = 0; // pad chunk to even offset
+
+//
+// write CMAP
+//
+ *lbmptr++ = 'C';
+ *lbmptr++ = 'M';
+ *lbmptr++ = 'A';
+ *lbmptr++ = 'P';
+
+ cmaplength = (int *)lbmptr;
+ lbmptr+=4; // leave space for length
+
+ memcpy (lbmptr,palette,768);
+ lbmptr += 768;
+
+ length = lbmptr-(byte *)cmaplength-4;
+ *cmaplength = BigLong(length);
+ if (length&1)
+ *lbmptr++ = 0; // pad chunk to even offset
+
+//
+// write BODY
+//
+ *lbmptr++ = 'B';
+ *lbmptr++ = 'O';
+ *lbmptr++ = 'D';
+ *lbmptr++ = 'Y';
+
+ bodylength = (int *)lbmptr;
+ lbmptr+=4; // leave space for length
+
+ memcpy (lbmptr,data,width*height);
+ lbmptr += width*height;
+
+ length = lbmptr-(byte *)bodylength-4;
+ *bodylength = BigLong(length);
+ if (length&1)
+ *lbmptr++ = 0; // pad chunk to even offset
+
+//
+// done
+//
+ length = lbmptr-(byte *)formlength-4;
+ *formlength = BigLong(length);
+ if (length&1)
+ *lbmptr++ = 0; // pad chunk to even offset
+
+//
+// write output file
+//
+ SaveFile (filename, lbm, lbmptr-lbm);
+ free (lbm);
+}
+
diff --git a/qutils/COMMON/LBMLIB.H b/qutils/COMMON/LBMLIB.H
new file mode 100644
index 0000000..316338c
--- /dev/null
+++ b/qutils/COMMON/LBMLIB.H
@@ -0,0 +1,42 @@
+// lbmlib.h
+
+typedef unsigned char UBYTE;
+typedef short WORD;
+typedef unsigned short UWORD;
+typedef long LONG;
+
+typedef enum
+{
+ ms_none,
+ ms_mask,
+ ms_transcolor,
+ ms_lasso
+} mask_t;
+
+typedef enum
+{
+ cm_none,
+ cm_rle1
+} compress_t;
+
+typedef struct
+{
+ UWORD w,h;
+ WORD x,y;
+ UBYTE nPlanes;
+ UBYTE masking;
+ UBYTE compression;
+ UBYTE pad1;
+ UWORD transparentColor;
+ UBYTE xAspect,yAspect;
+ WORD pageWidth,pageHeight;
+} bmhd_t;
+
+extern bmhd_t bmhd; // will be in native byte order
+
+
+void LoadLBM (char *filename, byte **picture, byte **palette);
+
+void WriteLBMfile (char *filename, byte *data, int width, int height
+ , byte *palette);
+
diff --git a/qutils/COMMON/MATHLIB.C b/qutils/COMMON/MATHLIB.C
new file mode 100644
index 0000000..bcdd167
--- /dev/null
+++ b/qutils/COMMON/MATHLIB.C
@@ -0,0 +1,109 @@
+// mathlib.c -- math primitives
+
+#include "cmdlib.h"
+#include "mathlib.h"
+
+vec3_t vec3_origin = {0,0,0};
+
+
+double VectorLength(vec3_t v)
+{
+ int i;
+ double length;
+
+ length = 0;
+ for (i=0 ; i< 3 ; i++)
+ length += v[i]*v[i];
+ length = sqrt (length); // FIXME
+
+ return length;
+}
+
+qboolean VectorCompare (vec3_t v1, vec3_t v2)
+{
+ int i;
+
+ for (i=0 ; i<3 ; i++)
+ if (fabs(v1[i]-v2[i]) > EQUAL_EPSILON)
+ return false;
+
+ return true;
+}
+
+vec_t Q_rint (vec_t in)
+{
+ return floor (in + 0.5);
+}
+
+void VectorMA (vec3_t va, double scale, vec3_t vb, vec3_t vc)
+{
+ vc[0] = va[0] + scale*vb[0];
+ vc[1] = va[1] + scale*vb[1];
+ vc[2] = va[2] + scale*vb[2];
+}
+
+void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross)
+{
+ cross[0] = v1[1]*v2[2] - v1[2]*v2[1];
+ cross[1] = v1[2]*v2[0] - v1[0]*v2[2];
+ cross[2] = v1[0]*v2[1] - v1[1]*v2[0];
+}
+
+vec_t _DotProduct (vec3_t v1, vec3_t v2)
+{
+ return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
+}
+
+void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out)
+{
+ out[0] = va[0]-vb[0];
+ out[1] = va[1]-vb[1];
+ out[2] = va[2]-vb[2];
+}
+
+void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out)
+{
+ out[0] = va[0]+vb[0];
+ out[1] = va[1]+vb[1];
+ out[2] = va[2]+vb[2];
+}
+
+void _VectorCopy (vec3_t in, vec3_t out)
+{
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+}
+
+vec_t VectorNormalize (vec3_t v)
+{
+ int i;
+ double length;
+
+ length = 0;
+ for (i=0 ; i< 3 ; i++)
+ length += v[i]*v[i];
+ length = sqrt (length);
+ if (length == 0)
+ return 0;
+
+ for (i=0 ; i< 3 ; i++)
+ v[i] /= length;
+
+ return length;
+}
+
+void VectorInverse (vec3_t v)
+{
+ v[0] = -v[0];
+ v[1] = -v[1];
+ v[2] = -v[2];
+}
+
+void VectorScale (vec3_t v, vec_t scale, vec3_t out)
+{
+ out[0] = v[0] * scale;
+ out[1] = v[1] * scale;
+ out[2] = v[2] * scale;
+}
+
diff --git a/qutils/COMMON/MATHLIB.H b/qutils/COMMON/MATHLIB.H
new file mode 100644
index 0000000..d4311dc
--- /dev/null
+++ b/qutils/COMMON/MATHLIB.H
@@ -0,0 +1,48 @@
+#ifndef __MATHLIB__
+#define __MATHLIB__
+
+// mathlib.h
+
+#include <math.h>
+
+#ifdef DOUBLEVEC_T
+typedef double vec_t;
+#else
+typedef float vec_t;
+#endif
+typedef vec_t vec3_t[3];
+
+#define SIDE_FRONT 0
+#define SIDE_ON 2
+#define SIDE_BACK 1
+#define SIDE_CROSS -2
+
+#define Q_PI 3.14159265358979323846
+
+extern vec3_t vec3_origin;
+
+#define EQUAL_EPSILON 0.001
+
+qboolean VectorCompare (vec3_t v1, vec3_t v2);
+
+#define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2])
+#define VectorSubtract(a,b,c) {c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];}
+#define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];}
+#define VectorCopy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];}
+
+vec_t Q_rint (vec_t in);
+vec_t _DotProduct (vec3_t v1, vec3_t v2);
+void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out);
+void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out);
+void _VectorCopy (vec3_t in, vec3_t out);
+
+double VectorLength(vec3_t v);
+
+void VectorMA (vec3_t va, double scale, vec3_t vb, vec3_t vc);
+
+void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross);
+vec_t VectorNormalize (vec3_t v);
+void VectorInverse (vec3_t v);
+void VectorScale (vec3_t v, vec_t scale, vec3_t out);
+
+#endif
diff --git a/qutils/COMMON/POLYLIB.C b/qutils/COMMON/POLYLIB.C
new file mode 100644
index 0000000..a45293a
--- /dev/null
+++ b/qutils/COMMON/POLYLIB.C
@@ -0,0 +1,400 @@
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "polylib.h"
+
+#define BOGUS_RANGE 8192
+
+/*
+=============
+AllocWinding
+=============
+*/
+winding_t *AllocWinding (int points)
+{
+ winding_t *w;
+ int s;
+
+ s = sizeof(vec_t)*3*points + sizeof(int);
+ w = malloc (s);
+ memset (w, 0, s);
+ return w;
+}
+
+/*
+============
+RemoveColinearPoints
+============
+*/
+int c_removed;
+
+void RemoveColinearPoints (winding_t *w)
+{
+ int i, j, k;
+ vec3_t v1, v2;
+ int nump;
+ vec3_t p[MAX_POINTS_ON_WINDING];
+
+ nump = 0;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ j = (i+1)%w->numpoints;
+ k = (i+w->numpoints-1)%w->numpoints;
+ VectorSubtract (w->p[j], w->p[i], v1);
+ VectorSubtract (w->p[i], w->p[k], v2);
+ VectorNormalize(v1);
+ VectorNormalize(v2);
+ if (DotProduct(v1, v2) < 0.999)
+ {
+ VectorCopy (w->p[i], p[nump]);
+ nump++;
+ }
+ }
+
+ if (nump == w->numpoints)
+ return;
+
+ c_removed += w->numpoints - nump;
+ w->numpoints = nump;
+ memcpy (w->p, p, nump*sizeof(p[0]));
+}
+
+/*
+============
+WindingPlane
+============
+*/
+void WindingPlane (winding_t *w, vec3_t normal, vec_t *dist)
+{
+ vec3_t v1, v2;
+
+ VectorSubtract (w->p[1], w->p[0], v1);
+ VectorSubtract (w->p[2], w->p[0], v2);
+ CrossProduct (v1, v2, normal);
+ VectorNormalize (normal);
+ *dist = DotProduct (w->p[0], normal);
+
+}
+
+/*
+=============
+WindingArea
+=============
+*/
+vec_t WindingArea (winding_t *w)
+{
+ int i;
+ vec3_t d1, d2, cross;
+ vec_t total;
+
+ total = 0;
+ for (i=2 ; i<w->numpoints ; i++)
+ {
+ VectorSubtract (w->p[i-1], w->p[0], d1);
+ VectorSubtract (w->p[i], w->p[0], d2);
+ CrossProduct (d1, d2, cross);
+ total += 0.5 * VectorLength ( cross );
+ }
+ return total;
+}
+
+/*
+=============
+WindingCenter
+=============
+*/
+void WindingCenter (winding_t *w, vec3_t center)
+{
+ int i;
+ vec3_t d1, d2, cross;
+ float scale;
+
+ VectorCopy (vec3_origin, center);
+ for (i=0 ; i<w->numpoints ; i++)
+ VectorAdd (w->p[i], center, center);
+
+ scale = 1.0/w->numpoints;
+ VectorScale (center, scale, center);
+}
+
+/*
+=================
+BaseWindingForPlane
+=================
+*/
+winding_t *BaseWindingForPlane (vec3_t normal, float dist)
+{
+ int i, x;
+ vec_t max, v;
+ vec3_t org, vright, vup;
+ winding_t *w;
+
+// find the major axis
+
+ max = -BOGUS_RANGE;
+ x = -1;
+ for (i=0 ; i<3; i++)
+ {
+ v = fabs(normal[i]);
+ if (v > max)
+ {
+ x = i;
+ max = v;
+ }
+ }
+ if (x==-1)
+ Error ("BaseWindingForPlane: no axis found");
+
+ VectorCopy (vec3_origin, vup);
+ switch (x)
+ {
+ case 0:
+ case 1:
+ vup[2] = 1;
+ break;
+ case 2:
+ vup[0] = 1;
+ break;
+ }
+
+ v = DotProduct (vup, normal);
+ VectorMA (vup, -v, normal, vup);
+ VectorNormalize (vup);
+
+ VectorScale (normal, dist, org);
+
+ CrossProduct (vup, normal, vright);
+
+ VectorScale (vup, 8192, vup);
+ VectorScale (vright, 8192, vright);
+
+// project a really big axis aligned box onto the plane
+ w = AllocWinding (4);
+
+ VectorSubtract (org, vright, w->p[0]);
+ VectorAdd (w->p[0], vup, w->p[0]);
+
+ VectorAdd (org, vright, w->p[1]);
+ VectorAdd (w->p[1], vup, w->p[1]);
+
+ VectorAdd (org, vright, w->p[2]);
+ VectorSubtract (w->p[2], vup, w->p[2]);
+
+ VectorSubtract (org, vright, w->p[3]);
+ VectorSubtract (w->p[3], vup, w->p[3]);
+
+ w->numpoints = 4;
+
+ return w;
+}
+
+/*
+==================
+CopyWinding
+==================
+*/
+winding_t *CopyWinding (winding_t *w)
+{
+ int size;
+ winding_t *c;
+
+ size = (int)((winding_t *)0)->p[w->numpoints];
+ c = malloc (size);
+ memcpy (c, w, size);
+ return c;
+}
+
+
+/*
+=============
+ClipWinding
+=============
+*/
+void ClipWinding (winding_t *in, vec3_t normal, vec_t dist,
+ winding_t **front, winding_t **back)
+{
+ vec_t dists[MAX_POINTS_ON_WINDING+4];
+ int sides[MAX_POINTS_ON_WINDING+4];
+ int counts[3];
+ vec_t dot;
+ int i, j;
+ vec_t *p1, *p2;
+ vec3_t mid;
+ winding_t *f, *b;
+ int maxpts;
+
+ counts[0] = counts[1] = counts[2] = 0;
+
+// determine sides for each point
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ dot = DotProduct (in->p[i], normal);
+ dot -= dist;
+ dists[i] = dot;
+ if (dot > ON_EPSILON)
+ sides[i] = SIDE_FRONT;
+ else if (dot < -ON_EPSILON)
+ sides[i] = SIDE_BACK;
+ else
+ {
+ sides[i] = SIDE_ON;
+ }
+ counts[sides[i]]++;
+ }
+ sides[i] = sides[0];
+ dists[i] = dists[0];
+
+ *front = *back = NULL;
+
+ if (!counts[0])
+ {
+ *back = CopyWinding (in);
+ return;
+ }
+ if (!counts[1])
+ {
+ *front = CopyWinding (in);
+ return;
+ }
+
+ maxpts = in->numpoints+4; // can't use counts[0]+2 because
+ // of fp grouping errors
+
+ *front = f = AllocWinding (maxpts);
+ *back = b = AllocWinding (maxpts);
+
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ p1 = in->p[i];
+
+ if (sides[i] == SIDE_ON)
+ {
+ VectorCopy (p1, f->p[f->numpoints]);
+ f->numpoints++;
+ VectorCopy (p1, b->p[b->numpoints]);
+ b->numpoints++;
+ continue;
+ }
+
+ if (sides[i] == SIDE_FRONT)
+ {
+ VectorCopy (p1, f->p[f->numpoints]);
+ f->numpoints++;
+ }
+ if (sides[i] == SIDE_BACK)
+ {
+ VectorCopy (p1, b->p[b->numpoints]);
+ b->numpoints++;
+ }
+
+ if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
+ continue;
+
+ // generate a split point
+ p2 = in->p[(i+1)%in->numpoints];
+
+ dot = dists[i] / (dists[i]-dists[i+1]);
+ for (j=0 ; j<3 ; j++)
+ { // avoid round off error when possible
+ if (normal[j] == 1)
+ mid[j] = dist;
+ else if (normal[j] == -1)
+ mid[j] = -dist;
+ else
+ mid[j] = p1[j] + dot*(p2[j]-p1[j]);
+ }
+
+ VectorCopy (mid, f->p[f->numpoints]);
+ f->numpoints++;
+ VectorCopy (mid, b->p[b->numpoints]);
+ b->numpoints++;
+ }
+
+ if (f->numpoints > maxpts || b->numpoints > maxpts)
+ Error ("ClipWinding: points exceeded estimate");
+ if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING)
+ Error ("ClipWinding: MAX_POINTS_ON_WINDING");
+}
+
+/*
+=================
+ChopWinding
+
+Returns the fragment of in that is on the front side
+of the cliping plane. The original is freed.
+=================
+*/
+winding_t *ChopWinding (winding_t *in, vec3_t normal, vec_t dist)
+{
+ winding_t *f, *b;
+
+ ClipWinding (in, normal, dist, &f, &b);
+ free (in);
+ if (b)
+ free (b);
+ return f;
+}
+
+/*
+=================
+CheckWinding
+
+=================
+*/
+void CheckWinding (winding_t *w)
+{
+ int i, j;
+ vec_t *p1, *p2;
+ vec_t d, edgedist;
+ vec3_t dir, edgenormal, facenormal;
+ vec_t area;
+ vec_t facedist;
+
+ if (w->numpoints < 3)
+ Error ("CheckFace: %i points",w->numpoints);
+
+ area = WindingArea(w);
+ if (area < 1)
+ Error ("CheckFace: %f area", area);
+
+ WindingPlane (w, facenormal, &facedist);
+
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ p1 = w->p[i];
+
+ for (j=0 ; j<3 ; j++)
+ if (p1[j] > BOGUS_RANGE || p1[j] < -BOGUS_RANGE)
+ Error ("CheckFace: BUGUS_RANGE: %f",p1[j]);
+
+ j = i+1 == w->numpoints ? 0 : i+1;
+
+ // check the point is on the face plane
+ d = DotProduct (p1, facenormal) - facedist;
+ if (d < -ON_EPSILON || d > ON_EPSILON)
+ Error ("CheckFace: point off plane");
+
+ // check the edge isn't degenerate
+ p2 = w->p[j];
+ VectorSubtract (p2, p1, dir);
+
+ if (VectorLength (dir) < ON_EPSILON)
+ Error ("CheckFace: degenerate edge");
+
+ CrossProduct (facenormal, dir, edgenormal);
+ VectorNormalize (edgenormal);
+ edgedist = DotProduct (p1, edgenormal);
+ edgedist += ON_EPSILON;
+
+ // all other points must be on front side
+ for (j=0 ; j<w->numpoints ; j++)
+ {
+ if (j == i)
+ continue;
+ d = DotProduct (w->p[j], edgenormal);
+ if (d > edgedist)
+ Error ("CheckFace: non-convex");
+ }
+ }
+}
+
diff --git a/qutils/COMMON/POLYLIB.H b/qutils/COMMON/POLYLIB.H
new file mode 100644
index 0000000..7f0794d
--- /dev/null
+++ b/qutils/COMMON/POLYLIB.H
@@ -0,0 +1,22 @@
+
+typedef struct
+{
+ int numpoints;
+ vec3_t p[4]; // variable sized
+} winding_t;
+
+#define MAX_POINTS_ON_WINDING 64
+
+#define ON_EPSILON 0.1
+
+winding_t *AllocWinding (int points);
+vec_t WindingArea (winding_t *w);
+void WindingCenter (winding_t *w, vec3_t center);
+void ClipWinding (winding_t *in, vec3_t normal, vec_t dist,
+ winding_t **front, winding_t **back);
+winding_t *ChopWinding (winding_t *in, vec3_t normal, vec_t dist);
+winding_t *CopyWinding (winding_t *w);
+winding_t *BaseWindingForPlane (vec3_t normal, float dist);
+void CheckWinding (winding_t *w);
+void WindingPlane (winding_t *w, vec3_t normal, vec_t *dist);
+void RemoveColinearPoints (winding_t *w);
diff --git a/qutils/COMMON/SCRIPLIB.C b/qutils/COMMON/SCRIPLIB.C
new file mode 100644
index 0000000..6f158f6
--- /dev/null
+++ b/qutils/COMMON/SCRIPLIB.C
@@ -0,0 +1,185 @@
+// scriplib.c
+
+#include "cmdlib.h"
+#include "scriplib.h"
+
+/*
+=============================================================================
+
+ PARSING STUFF
+
+=============================================================================
+*/
+
+char token[MAXTOKEN];
+char *scriptbuffer,*script_p,*scriptend_p;
+int grabbed;
+int scriptline;
+qboolean endofscript;
+qboolean tokenready; // only true if UnGetToken was just called
+
+/*
+==============
+=
+= LoadScriptFile
+=
+==============
+*/
+
+void LoadScriptFile (char *filename)
+{
+ int size;
+
+ size = LoadFile (filename, (void **)&scriptbuffer);
+
+ script_p = scriptbuffer;
+ scriptend_p = script_p + size;
+ scriptline = 1;
+ endofscript = false;
+ tokenready = false;
+}
+
+
+/*
+==============
+=
+= UnGetToken
+=
+= Signals that the current token was not used, and should be reported
+= for the next GetToken. Note that
+
+GetToken (true);
+UnGetToken ();
+GetToken (false);
+
+= could cross a line boundary.
+=
+==============
+*/
+
+void UnGetToken (void)
+{
+ tokenready = true;
+}
+
+
+/*
+==============
+GetToken
+==============
+*/
+qboolean GetToken (qboolean crossline)
+{
+ char *token_p;
+
+ if (tokenready) // is a token allready waiting?
+ {
+ tokenready = false;
+ return true;
+ }
+
+ if (script_p >= scriptend_p)
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete\n",scriptline);
+ endofscript = true;
+ return false;
+ }
+
+//
+// skip space
+//
+skipspace:
+ while (*script_p <= 32)
+ {
+ if (script_p >= scriptend_p)
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete\n",scriptline);
+ endofscript = true;
+ return true;
+ }
+ if (*script_p++ == '\n')
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete\n",scriptline);
+ scriptline++;
+ }
+ }
+
+ if (script_p >= scriptend_p)
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete\n",scriptline);
+ endofscript = true;
+ return true;
+ }
+
+ if (*script_p == ';' || *script_p == '#') // semicolon is comment field
+ { // also make # a comment field
+ if (!crossline)
+ Error ("Line %i is incomplete\n",scriptline);
+ while (*script_p++ != '\n')
+ if (script_p >= scriptend_p)
+ {
+ endofscript = true;
+ return false;
+ }
+ goto skipspace;
+ }
+
+//
+// copy token
+//
+ token_p = token;
+
+ while ( *script_p > 32 && *script_p != ';')
+ {
+ *token_p++ = *script_p++;
+ if (script_p == scriptend_p)
+ break;
+ if (token_p == &token[MAXTOKEN])
+ Error ("Token too large on line %i\n",scriptline);
+ }
+
+ *token_p = 0;
+ return true;
+}
+
+
+/*
+==============
+=
+= TokenAvailable
+=
+= Returns true if there is another token on the line
+=
+==============
+*/
+
+qboolean TokenAvailable (void)
+{
+ char *search_p;
+
+ search_p = script_p;
+
+ if (search_p >= scriptend_p)
+ return false;
+
+ while ( *search_p <= 32)
+ {
+ if (*search_p == '\n')
+ return false;
+ search_p++;
+ if (search_p == scriptend_p)
+ return false;
+
+ }
+
+ if (*search_p == ';')
+ return false;
+
+ return true;
+}
+
+
diff --git a/qutils/COMMON/SCRIPLIB.H b/qutils/COMMON/SCRIPLIB.H
new file mode 100644
index 0000000..95673f3
--- /dev/null
+++ b/qutils/COMMON/SCRIPLIB.H
@@ -0,0 +1,21 @@
+// scriplib.h
+
+#ifndef __CMDLIB__
+#include "cmdlib.h"
+#endif
+
+#define MAXTOKEN 128
+
+extern char token[MAXTOKEN];
+extern char *scriptbuffer,*script_p,*scriptend_p;
+extern int grabbed;
+extern int scriptline;
+extern qboolean endofscript;
+
+
+void LoadScriptFile (char *filename);
+qboolean GetToken (qboolean crossline);
+void UnGetToken (void);
+qboolean TokenAvailable (void);
+
+
diff --git a/qutils/COMMON/THREADS.C b/qutils/COMMON/THREADS.C
new file mode 100644
index 0000000..6545b50
--- /dev/null
+++ b/qutils/COMMON/THREADS.C
@@ -0,0 +1,239 @@
+
+#include "cmdlib.h"
+#include "threads.h"
+
+#define MAX_THREADS 64
+
+int dispatch;
+int workcount;
+int oldf;
+qboolean pacifier;
+
+/*
+=============
+GetThreadWork
+
+=============
+*/
+int GetThreadWork (void)
+{
+ int r;
+ int f;
+
+ ThreadLock ();
+
+ if (dispatch == workcount)
+ {
+ ThreadUnlock ();
+ return -1;
+ }
+
+ f = 10*dispatch / workcount;
+ if (f != oldf)
+ {
+ oldf = f;
+ if (pacifier)
+ printf ("%i...", f);
+ }
+
+ r = dispatch;
+ dispatch++;
+ ThreadUnlock ();
+
+ return r;
+}
+
+
+
+/*
+===================================================================
+
+WIN32
+
+===================================================================
+*/
+#ifdef WIN32
+
+#define USED
+
+#include <windows.h>
+
+int numthreads = 1;
+CRITICAL_SECTION crit;
+
+void ThreadLock (void)
+{
+ EnterCriticalSection (&crit);
+}
+
+void ThreadUnlock (void)
+{
+ LeaveCriticalSection (&crit);
+}
+
+/*
+=============
+RunThreadsOn
+=============
+*/
+void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int))
+{
+ int threadid[MAX_THREADS];
+ HANDLE threadhandle[MAX_THREADS];
+ int i;
+
+ dispatch = 0;
+ workcount = workcnt;
+ oldf = -1;
+ pacifier = showpacifier;
+
+ //
+ // run threads in parallel
+ //
+ InitializeCriticalSection (&crit);
+ for (i=0 ; i<numthreads ; i++)
+ {
+ threadhandle[i] = CreateThread(
+ NULL, // LPSECURITY_ATTRIBUTES lpsa,
+ 0, // DWORD cbStack,
+ (LPTHREAD_START_ROUTINE)func, // LPTHREAD_START_ROUTINE lpStartAddr,
+ (LPVOID)i, // LPVOID lpvThreadParm,
+ 0, // DWORD fdwCreate,
+ &threadid[i]);
+ }
+
+ for (i=0 ; i<numthreads ; i++)
+ WaitForSingleObject (threadhandle[i], INFINITE);
+ DeleteCriticalSection (&crit);
+ if (pacifier)
+ printf ("\n");
+}
+
+
+#endif
+
+/*
+===================================================================
+
+OSF1
+
+===================================================================
+*/
+
+#ifdef __osf__
+#define USED
+
+int numthreads = 4;
+
+#include <pthread.h>
+
+pthread_mutex_t *my_mutex;
+
+void ThreadLock (void)
+{
+ if (my_mutex)
+ pthread_mutex_lock (my_mutex);
+}
+
+void ThreadUnlock (void)
+{
+ if (my_mutex)
+ pthread_mutex_unlock (my_mutex);
+}
+
+
+/*
+=============
+RunThreadsOn
+=============
+*/
+void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int))
+{
+ int i;
+ pthread_t work_threads[MAX_THREADS];
+ pthread_addr_t status;
+ pthread_attr_t attrib;
+ pthread_mutexattr_t mattrib;
+
+ dispatch = 0;
+ workcount = workcnt;
+ oldf = -1;
+ pacifier = showpacifier;
+
+ if (!my_mutex)
+ {
+ my_mutex = malloc (sizeof(*my_mutex));
+ if (pthread_mutexattr_create (&mattrib) == -1)
+ Error ("pthread_mutex_attr_create failed");
+ if (pthread_mutexattr_setkind_np (&mattrib, MUTEX_FAST_NP) == -1)
+ Error ("pthread_mutexattr_setkind_np failed");
+ if (pthread_mutex_init (my_mutex, mattrib) == -1)
+ Error ("pthread_mutex_init failed");
+ }
+
+ if (pthread_attr_create (&attrib) == -1)
+ Error ("pthread_attr_create failed");
+ if (pthread_attr_setstacksize (&attrib, 0x100000) == -1)
+ Error ("pthread_attr_setstacksize failed");
+
+ for (i=0 ; i<numthreads ; i++)
+ {
+ if (pthread_create(&work_threads[i], attrib
+ , (pthread_startroutine_t)func, (pthread_addr_t)i) == -1)
+ Error ("pthread_create failed");
+ }
+
+ for (i=0 ; i<numthreads ; i++)
+ {
+ if (pthread_join (work_threads[i], &status) == -1)
+ Error ("pthread_join failed");
+ }
+
+ if (pacifier)
+ printf ("\n");
+}
+
+
+#endif
+
+/*
+=======================================================================
+
+ SINGLE THREAD
+
+=======================================================================
+*/
+
+#ifndef USED
+
+int numthreads = 1;
+
+void ThreadLock (void)
+{
+}
+
+void ThreadUnlock (void)
+{
+}
+
+/*
+=============
+RunThreadsOn
+=============
+*/
+void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int))
+{
+ int i;
+
+ dispatch = 0;
+ workcount = workcnt;
+ oldf = -1;
+ pacifier = showpacifier;
+
+ func(0);
+
+ if (pacifier)
+ printf ("\n");
+}
+
+#endif
diff --git a/qutils/COMMON/THREADS.H b/qutils/COMMON/THREADS.H
new file mode 100644
index 0000000..197f57a
--- /dev/null
+++ b/qutils/COMMON/THREADS.H
@@ -0,0 +1,8 @@
+
+extern int numthreads;
+
+int GetThreadWork (void);
+void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int));
+void ThreadLock (void);
+void ThreadUnlock (void);
+
diff --git a/qutils/COMMON/TRILIB.C b/qutils/COMMON/TRILIB.C
new file mode 100644
index 0000000..9118039
--- /dev/null
+++ b/qutils/COMMON/TRILIB.C
@@ -0,0 +1,170 @@
+//
+// trilib.c: library for loading triangles from an Alias triangle file
+//
+
+#include <stdio.h>
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "trilib.h"
+
+// on disk representation of a face
+
+
+#define FLOAT_START 99999.0
+#define FLOAT_END -FLOAT_START
+#define MAGIC 123322
+
+//#define NOISY 1
+
+typedef struct {
+ float v[3];
+} vector;
+
+typedef struct
+{
+ vector n; /* normal */
+ vector p; /* point */
+ vector c; /* color */
+ float u; /* u */
+ float v; /* v */
+} aliaspoint_t;
+
+typedef struct {
+ aliaspoint_t pt[3];
+} tf_triangle;
+
+
+void ByteSwapTri (tf_triangle *tri)
+{
+ int i;
+
+ for (i=0 ; i<sizeof(tf_triangle)/4 ; i++)
+ {
+ ((int *)tri)[i] = BigLong (((int *)tri)[i]);
+ }
+}
+
+void LoadTriangleList (char *filename, triangle_t **pptri, int *numtriangles)
+{
+ FILE *input;
+ float start;
+ char name[256], tex[256];
+ int i, count, magic;
+ tf_triangle tri;
+ triangle_t *ptri;
+ int iLevel;
+ int exitpattern;
+ float t;
+
+
+ t = -FLOAT_START;
+ *((unsigned char *)&exitpattern + 0) = *((unsigned char *)&t + 3);
+ *((unsigned char *)&exitpattern + 1) = *((unsigned char *)&t + 2);
+ *((unsigned char *)&exitpattern + 2) = *((unsigned char *)&t + 1);
+ *((unsigned char *)&exitpattern + 3) = *((unsigned char *)&t + 0);
+
+ if ((input = fopen(filename, "rb")) == 0) {
+ fprintf(stderr,"reader: could not open file '%s'\n", filename);
+ exit(0);
+ }
+
+ iLevel = 0;
+
+ fread(&magic, sizeof(int), 1, input);
+ if (BigLong(magic) != MAGIC) {
+ fprintf(stderr,"File is not a Alias object separated triangle file, magic number is wrong.\n");
+ exit(0);
+ }
+
+ ptri = malloc (MAXTRIANGLES * sizeof(triangle_t));
+
+ *pptri = ptri;
+
+ while (feof(input) == 0) {
+ fread(&start, sizeof(float), 1, input);
+ *(int *)&start = BigLong(*(int *)&start);
+ if (*(int *)&start != exitpattern)
+ {
+ if (start == FLOAT_START) {
+ /* Start of an object or group of objects. */
+ i = -1;
+ do {
+ /* There are probably better ways to read a string from */
+ /* a file, but this does allow you to do error checking */
+ /* (which I'm not doing) on a per character basis. */
+ ++i;
+ fread( &(name[i]), sizeof( char ), 1, input);
+ } while( name[i] != '\0' );
+
+ // indent();
+ // fprintf(stdout,"OBJECT START: %s\n",name);
+ fread( &count, sizeof(int), 1, input);
+ count = BigLong(count);
+ ++iLevel;
+ if (count != 0) {
+ // indent();
+
+ // fprintf(stdout,"NUMBER OF TRIANGLES: %d\n",count);
+
+ i = -1;
+ do {
+ ++i;
+ fread( &(tex[i]), sizeof( char ), 1, input);
+ } while( tex[i] != '\0' );
+
+ // indent();
+ // fprintf(stdout," Object texture name: '%s'\n",tex);
+ }
+
+ /* Else (count == 0) this is the start of a group, and */
+ /* no texture name is present. */
+ }
+ else if (start == FLOAT_END) {
+ /* End of an object or group. Yes, the name should be */
+ /* obvious from context, but it is in here just to be */
+ /* safe and to provide a little extra information for */
+ /* those who do not wish to write a recursive reader. */
+ /* Mia culpa. */
+ --iLevel;
+ i = -1;
+ do {
+ ++i;
+ fread( &(name[i]), sizeof( char ), 1, input);
+ } while( name[i] != '\0' );
+
+ // indent();
+ // fprintf(stdout,"OBJECT END: %s\n",name);
+ continue;
+ }
+ }
+
+//
+// read the triangles
+//
+ for (i = 0; i < count; ++i) {
+ int j;
+
+ fread( &tri, sizeof(tf_triangle), 1, input );
+ ByteSwapTri (&tri);
+ for (j=0 ; j<3 ; j++)
+ {
+ int k;
+
+ for (k=0 ; k<3 ; k++)
+ {
+ ptri->verts[j][k] = tri.pt[j].p.v[k];
+ }
+ }
+
+ ptri++;
+
+ if ((ptri - *pptri) >= MAXTRIANGLES)
+ Error ("Error: too many triangles; increase MAXTRIANGLES\n");
+ }
+ }
+
+ *numtriangles = ptri - *pptri;
+
+ fclose (input);
+}
+
diff --git a/qutils/COMMON/TRILIB.H b/qutils/COMMON/TRILIB.H
new file mode 100644
index 0000000..84e02f0
--- /dev/null
+++ b/qutils/COMMON/TRILIB.H
@@ -0,0 +1,11 @@
+//
+// trilib.h: header file for loading triangles from an Alias triangle file
+//
+#define MAXTRIANGLES 2048
+
+typedef struct {
+ vec3_t verts[3];
+} triangle_t;
+
+void LoadTriangleList (char *filename, triangle_t **pptri, int *numtriangles);
+
diff --git a/qutils/COMMON/WADLIB.C b/qutils/COMMON/WADLIB.C
new file mode 100644
index 0000000..63e4339
--- /dev/null
+++ b/qutils/COMMON/WADLIB.C
@@ -0,0 +1,328 @@
+// wad2lib.c
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+//#include <sys/file.h>
+#include <stdarg.h>
+
+#ifdef NeXT
+#include <libc.h>
+#endif
+#include "cmdlib.h"
+#include "wadlib.h"
+
+/*
+============================================================================
+
+ WAD READING
+
+============================================================================
+*/
+
+
+lumpinfo_t *lumpinfo; // location of each lump on disk
+int numlumps;
+
+wadinfo_t header;
+FILE *wadhandle;
+
+
+/*
+====================
+W_OpenWad
+====================
+*/
+void W_OpenWad (char *filename)
+{
+ lumpinfo_t *lump_p;
+ unsigned i;
+ int length;
+
+//
+// open the file and add to directory
+//
+ wadhandle = SafeOpenRead (filename);
+ SafeRead (wadhandle, &header, sizeof(header));
+
+ if (strncmp(header.identification,"WAD2",4))
+ Error ("Wad file %s doesn't have WAD2 id\n",filename);
+
+ header.numlumps = LittleLong(header.numlumps);
+ header.infotableofs = LittleLong(header.infotableofs);
+
+ numlumps = header.numlumps;
+
+ length = numlumps*sizeof(lumpinfo_t);
+ lumpinfo = malloc (length);
+ lump_p = lumpinfo;
+
+ fseek (wadhandle, header.infotableofs, SEEK_SET);
+ SafeRead (wadhandle, lumpinfo, length);
+
+//
+// Fill in lumpinfo
+//
+
+ for (i=0 ; i<numlumps ; i++,lump_p++)
+ {
+ lump_p->filepos = LittleLong(lump_p->filepos);
+ lump_p->size = LittleLong(lump_p->size);
+ }
+}
+
+
+
+void CleanupName (char *in, char *out)
+{
+ int i;
+
+ for (i=0 ; i<sizeof( ((lumpinfo_t *)0)->name ) ; i++ )
+ {
+ if (!in[i])
+ break;
+
+ out[i] = toupper(in[i]);
+ }
+
+ for ( ; i<sizeof( ((lumpinfo_t *)0)->name ); i++ )
+ out[i] = 0;
+}
+
+
+/*
+====================
+W_CheckNumForName
+
+Returns -1 if name not found
+====================
+*/
+int W_CheckNumForName (char *name)
+{
+ char cleanname[16];
+ int v1,v2, v3, v4;
+ int i;
+ lumpinfo_t *lump_p;
+
+ CleanupName (name, cleanname);
+
+// make the name into four integers for easy compares
+
+ v1 = *(int *)cleanname;
+ v2 = *(int *)&cleanname[4];
+ v3 = *(int *)&cleanname[8];
+ v4 = *(int *)&cleanname[12];
+
+// find it
+
+ lump_p = lumpinfo;
+ for (i=0 ; i<numlumps ; i++, lump_p++)
+ {
+ if ( *(int *)lump_p->name == v1
+ && *(int *)&lump_p->name[4] == v2
+ && *(int *)&lump_p->name[8] == v3
+ && *(int *)&lump_p->name[12] == v4)
+ return i;
+ }
+
+ return -1;
+}
+
+
+/*
+====================
+W_GetNumForName
+
+Calls W_CheckNumForName, but bombs out if not found
+====================
+*/
+int W_GetNumForName (char *name)
+{
+ int i;
+
+ i = W_CheckNumForName (name);
+ if (i != -1)
+ return i;
+
+ Error ("W_GetNumForName: %s not found!",name);
+ return -1;
+}
+
+
+/*
+====================
+W_LumpLength
+
+Returns the buffer size needed to load the given lump
+====================
+*/
+int W_LumpLength (int lump)
+{
+ if (lump >= numlumps)
+ Error ("W_LumpLength: %i >= numlumps",lump);
+ return lumpinfo[lump].size;
+}
+
+
+/*
+====================
+W_ReadLumpNum
+
+Loads the lump into the given buffer, which must be >= W_LumpLength()
+====================
+*/
+void W_ReadLumpNum (int lump, void *dest)
+{
+ lumpinfo_t *l;
+
+ if (lump >= numlumps)
+ Error ("W_ReadLump: %i >= numlumps",lump);
+ l = lumpinfo+lump;
+
+ fseek (wadhandle, l->filepos, SEEK_SET);
+ SafeRead (wadhandle, dest, l->size);
+}
+
+
+
+/*
+====================
+W_LoadLumpNum
+====================
+*/
+void *W_LoadLumpNum (int lump)
+{
+ void *buf;
+
+ if ((unsigned)lump >= numlumps)
+ Error ("W_CacheLumpNum: %i >= numlumps",lump);
+
+ buf = malloc (W_LumpLength (lump));
+ W_ReadLumpNum (lump, buf);
+
+ return buf;
+}
+
+
+/*
+====================
+W_LoadLumpName
+====================
+*/
+void *W_LoadLumpName (char *name)
+{
+ return W_LoadLumpNum (W_GetNumForName(name));
+}
+
+
+/*
+===============================================================================
+
+ WAD CREATION
+
+===============================================================================
+*/
+
+FILE *outwad;
+
+lumpinfo_t outinfo[4096];
+int outlumps;
+
+short (*wadshort) (short l);
+int (*wadlong) (int l);
+
+/*
+===============
+NewWad
+===============
+*/
+
+void NewWad (char *pathname, qboolean bigendien)
+{
+ outwad = SafeOpenWrite (pathname);
+ fseek (outwad, sizeof(wadinfo_t), SEEK_SET);
+ memset (outinfo, 0, sizeof(outinfo));
+
+ if (bigendien)
+ {
+ wadshort = BigShort;
+ wadlong = BigLong;
+ }
+ else
+ {
+ wadshort = LittleShort;
+ wadlong = LittleLong;
+ }
+
+ outlumps = 0;
+}
+
+
+/*
+===============
+AddLump
+===============
+*/
+
+void AddLump (char *name, void *buffer, int length, int type, int compress)
+{
+ lumpinfo_t *info;
+ int ofs;
+
+ info = &outinfo[outlumps];
+ outlumps++;
+
+ memset (info,0,sizeof(info));
+
+ strcpy (info->name, name);
+ strupr (info->name);
+
+ ofs = ftell(outwad);
+ info->filepos = wadlong(ofs);
+ info->size = info->disksize = wadlong(length);
+ info->type = type;
+ info->compression = compress;
+
+// FIXME: do compression
+
+ SafeWrite (outwad, buffer, length);
+}
+
+
+/*
+===============
+WriteWad
+===============
+*/
+
+void WriteWad (void)
+{
+ wadinfo_t header;
+ int ofs;
+
+// write the lumpingo
+ ofs = ftell(outwad);
+
+ SafeWrite (outwad, outinfo, outlumps*sizeof(lumpinfo_t) );
+
+// write the header
+
+// a program will be able to tell the ednieness of a wad by the id
+ header.identification[0] = 'W';
+ header.identification[1] = 'A';
+ header.identification[2] = 'D';
+ header.identification[3] = '2';
+
+ header.numlumps = wadlong(outlumps);
+ header.infotableofs = wadlong(ofs);
+
+ fseek (outwad, 0, SEEK_SET);
+ SafeWrite (outwad, &header, sizeof(header));
+ fclose (outwad);
+}
+
+
diff --git a/qutils/COMMON/WADLIB.H b/qutils/COMMON/WADLIB.H
new file mode 100644
index 0000000..d9fecf8
--- /dev/null
+++ b/qutils/COMMON/WADLIB.H
@@ -0,0 +1,53 @@
+// wadlib.h
+
+//
+// wad reading
+//
+
+#define CMP_NONE 0
+#define CMP_LZSS 1
+
+#define TYP_NONE 0
+#define TYP_LABEL 1
+#define TYP_LUMPY 64 // 64 + grab command number
+
+typedef struct
+{
+ char identification[4]; // should be WAD2 or 2DAW
+ int numlumps;
+ int infotableofs;
+} wadinfo_t;
+
+
+typedef struct
+{
+ int filepos;
+ int disksize;
+ int size; // uncompressed
+ char type;
+ char compression;
+ char pad1, pad2;
+ char name[16]; // must be null terminated
+} lumpinfo_t;
+
+extern lumpinfo_t *lumpinfo; // location of each lump on disk
+extern int numlumps;
+extern wadinfo_t header;
+
+void W_OpenWad (char *filename);
+int W_CheckNumForName (char *name);
+int W_GetNumForName (char *name);
+int W_LumpLength (int lump);
+void W_ReadLumpNum (int lump, void *dest);
+void *W_LoadLumpNum (int lump);
+void *W_LoadLumpName (char *name);
+
+void CleanupName (char *in, char *out);
+
+//
+// wad creation
+//
+void NewWad (char *pathname, qboolean bigendien);
+void AddLump (char *name, void *buffer, int length, int type, int compress);
+void WriteWad (void);
+
diff --git a/qutils/INSTALL.BAT b/qutils/INSTALL.BAT
new file mode 100644
index 0000000..93463a8
--- /dev/null
+++ b/qutils/INSTALL.BAT
@@ -0,0 +1,43 @@
+cd qlumpy
+nmake /f "qlumpy.mak" CFG="qlumpy - Win32 Release"
+copy release\qlumpy.exe \quake\bin_nt
+
+cd ..\texmake
+nmake /f "texmake.mak" CFG="texmake - Win32 Release"
+copy release\texmake.exe \quake\bin_nt
+
+cd ..\modelgen
+nmake /f "modelgen.mak" CFG="modelgen - Win32 Release"
+copy release\modelgen.exe \quake\bin_nt
+
+cd ..\sprgen
+nmake /f "sprgen.mak" CFG="sprgen - Win32 Release"
+copy release\sprgen.exe \quake\bin_nt
+
+
+cd ..\qbsp
+nmake /f "qbsp.mak" CFG="qbsp - Win32 Release"
+copy release\qbsp.exe \quake\bin_nt
+
+cd ..\light
+nmake /f "light.mak" CFG="light - Win32 Release"
+copy release\light.exe \quake\bin_nt
+
+cd ..\vis
+nmake /f "vis.mak" CFG="vis - Win32 Release"
+copy release\vis.exe \quake\bin_nt
+
+cd ..\bspinfo
+nmake /f "bspinfo.mak" CFG="bspinfo - Win32 Release"
+copy release\bspinfo.exe \quake\bin_nt
+
+
+cd ..\qcc
+nmake /f "qcc.mak" CFG="qcc - Win32 Release"
+copy release\qcc.exe \quake\bin_nt
+
+cd ..\qfiles
+nmake /f "qfiles.mak" CFG="qfiles - Win32 Release"
+copy release\qfiles.exe \quake\bin_nt
+
+cd ..
diff --git a/qutils/LIGHT/ENTITIES.C b/qutils/LIGHT/ENTITIES.C
new file mode 100644
index 0000000..185198f
--- /dev/null
+++ b/qutils/LIGHT/ENTITIES.C
@@ -0,0 +1,278 @@
+// entities.c
+
+#include "light.h"
+
+entity_t entities[MAX_MAP_ENTITIES];
+int num_entities;
+
+/*
+==============================================================================
+
+ENTITY FILE PARSING
+
+If a light has a targetname, generate a unique style in the 32-63 range
+==============================================================================
+*/
+
+int numlighttargets;
+char lighttargets[32][64];
+
+int LightStyleForTargetname (char *targetname, qboolean alloc)
+{
+ int i;
+
+ for (i=0 ; i<numlighttargets ; i++)
+ if (!strcmp (lighttargets[i], targetname))
+ return 32 + i;
+ if (!alloc)
+ return -1;
+ strcpy (lighttargets[i], targetname);
+ numlighttargets++;
+ return numlighttargets-1 + 32;
+}
+
+
+/*
+==================
+MatchTargets
+==================
+*/
+void MatchTargets (void)
+{
+ int i,j;
+
+ for (i=0 ; i<num_entities ; i++)
+ {
+ if (!entities[i].target[0])
+ continue;
+
+ for (j=0 ; j<num_entities ; j++)
+ if (!strcmp(entities[j].targetname, entities[i].target))
+ {
+ entities[i].targetent = &entities[j];
+ break;
+ }
+ if (j==num_entities)
+ {
+ printf ("WARNING: entity at (%i,%i,%i) (%s) has unmatched target\n", (int)entities[i].origin[0], (int)entities[i].origin[1], (int)entities[i].origin[2], entities[i].classname);
+ continue;
+ }
+
+// set the style on the source ent for switchable lights
+ if (entities[j].style)
+ {
+ char s[16];
+
+ entities[i].style = entities[j].style;
+ sprintf (s,"%i", entities[i].style);
+ SetKeyValue (&entities[i], "style", s);
+ }
+ }
+}
+
+
+/*
+==================
+LoadEntities
+==================
+*/
+void LoadEntities (void)
+{
+ char *data;
+ entity_t *entity;
+ char key[64];
+ epair_t *epair;
+ double vec[3];
+ int i;
+
+ data = dentdata;
+//
+// start parsing
+//
+ num_entities = 0;
+
+// go through all the entities
+ while (1)
+ {
+ // parse the opening brace
+ data = COM_Parse (data);
+ if (!data)
+ break;
+ if (com_token[0] != '{')
+ Error ("LoadEntities: found %s when expecting {",com_token);
+
+ if (num_entities == MAX_MAP_ENTITIES)
+ Error ("LoadEntities: MAX_MAP_ENTITIES");
+ entity = &entities[num_entities];
+ num_entities++;
+
+ // go through all the keys in this entity
+ while (1)
+ {
+ int c;
+
+ // parse key
+ data = COM_Parse (data);
+ if (!data)
+ Error ("LoadEntities: EOF without closing brace");
+ if (!strcmp(com_token,"}"))
+ break;
+ strcpy (key, com_token);
+
+ // parse value
+ data = COM_Parse (data);
+ if (!data)
+ Error ("LoadEntities: EOF without closing brace");
+ c = com_token[0];
+ if (c == '}')
+ Error ("LoadEntities: closing brace without data");
+
+ epair = malloc (sizeof(epair_t));
+ memset (epair, 0, sizeof(epair));
+ strcpy (epair->key, key);
+ strcpy (epair->value, com_token);
+ epair->next = entity->epairs;
+ entity->epairs = epair;
+
+ if (!strcmp(key, "classname"))
+ strcpy (entity->classname, com_token);
+ else if (!strcmp(key, "target"))
+ strcpy (entity->target, com_token);
+ else if (!strcmp(key, "targetname"))
+ strcpy (entity->targetname, com_token);
+ else if (!strcmp(key, "origin"))
+ {
+ // scan into doubles, then assign
+ // which makes it vec_t size independent
+ if (sscanf(com_token, "%lf %lf %lf",
+ &vec[0], &vec[1], &vec[2]) != 3)
+ Error ("LoadEntities: not 3 values for origin");
+ for (i=0 ; i<3 ; i++)
+ entity->origin[i] = vec[i];
+ }
+ else if (!strncmp(key, "light", 5) || !strcmp (key, "_light") )
+ {
+ entity->light = atof(com_token);
+ }
+ else if (!strcmp(key, "style"))
+ {
+ entity->style = atof(com_token);
+ if ((unsigned)entity->style > 254)
+ Error ("Bad light style %i (must be 0-254)", entity->style);
+ }
+ else if (!strcmp(key, "angle"))
+ {
+ entity->angle = atof(com_token);
+ }
+
+ }
+
+ // all fields have been parsed
+ if (!strncmp (entity->classname, "light", 5) && !entity->light)
+ entity->light = DEFAULTLIGHTLEVEL;
+
+ if (!strcmp (entity->classname, "light"))
+ {
+ if (entity->targetname[0] && !entity->style)
+ {
+ char s[16];
+
+ entity->style = LightStyleForTargetname (entity->targetname, true);
+ sprintf (s,"%i", entity->style);
+ SetKeyValue (entity, "style", s);
+ }
+ }
+ }
+
+ printf ("%d entities read\n", num_entities);
+
+ MatchTargets ();
+}
+
+char *ValueForKey (entity_t *ent, char *key)
+{
+ epair_t *ep;
+
+ for (ep=ent->epairs ; ep ; ep=ep->next)
+ if (!strcmp (ep->key, key) )
+ return ep->value;
+ return "";
+}
+
+void SetKeyValue (entity_t *ent, char *key, char *value)
+{
+ epair_t *ep;
+
+ for (ep=ent->epairs ; ep ; ep=ep->next)
+ if (!strcmp (ep->key, key) )
+ {
+ strcpy (ep->value, value);
+ return;
+ }
+ ep = malloc (sizeof(*ep));
+ ep->next = ent->epairs;
+ ent->epairs = ep;
+ strcpy (ep->key, key);
+ strcpy (ep->value, value);
+}
+
+float FloatForKey (entity_t *ent, char *key)
+{
+ char *k;
+
+ k = ValueForKey (ent, key);
+ return atof(k);
+}
+
+void GetVectorForKey (entity_t *ent, char *key, vec3_t vec)
+{
+ char *k;
+
+ k = ValueForKey (ent, key);
+ sscanf (k, "%lf %lf %lf", &vec[0], &vec[1], &vec[2]);
+}
+
+
+
+/*
+================
+WriteEntitiesToString
+================
+*/
+void WriteEntitiesToString (void)
+{
+ char *buf, *end;
+ epair_t *ep;
+ char line[128];
+ int i;
+
+ buf = dentdata;
+ end = buf;
+ *end = 0;
+
+ printf ("%i switchable light styles\n", numlighttargets);
+
+ for (i=0 ; i<num_entities ; i++)
+ {
+ ep = entities[i].epairs;
+ if (!ep)
+ continue; // ent got removed
+
+ strcat (end,"{\n");
+ end += 2;
+
+ for (ep = entities[i].epairs ; ep ; ep=ep->next)
+ {
+ sprintf (line, "\"%s\" \"%s\"\n", ep->key, ep->value);
+ strcat (end, line);
+ end += strlen(line);
+ }
+ strcat (end,"}\n");
+ end += 2;
+
+ if (end > buf + MAX_MAP_ENTSTRING)
+ Error ("Entity text too long");
+ }
+ entdatasize = end - buf + 1;
+}
+
diff --git a/qutils/LIGHT/ENTITIES.H b/qutils/LIGHT/ENTITIES.H
new file mode 100644
index 0000000..2ef2e68
--- /dev/null
+++ b/qutils/LIGHT/ENTITIES.H
@@ -0,0 +1,33 @@
+
+#define DEFAULTLIGHTLEVEL 300
+
+typedef struct epair_s
+{
+ struct epair_s *next;
+ char key[MAX_KEY];
+ char value[MAX_VALUE];
+} epair_t;
+
+typedef struct entity_s
+{
+ char classname[64];
+ vec3_t origin;
+ float angle;
+ int light;
+ int style;
+ char target[32];
+ char targetname[32];
+ struct epair_s *epairs;
+ struct entity_s *targetent;
+} entity_t;
+
+extern entity_t entities[MAX_MAP_ENTITIES];
+extern int num_entities;
+
+char *ValueForKey (entity_t *ent, char *key);
+void SetKeyValue (entity_t *ent, char *key, char *value);
+float FloatForKey (entity_t *ent, char *key);
+void GetVectorForKey (entity_t *ent, char *key, vec3_t vec);
+
+void LoadEntities (void);
+void WriteEntitiesToString (void);
diff --git a/qutils/LIGHT/LIGHT.C b/qutils/LIGHT/LIGHT.C
new file mode 100644
index 0000000..52abcd4
--- /dev/null
+++ b/qutils/LIGHT/LIGHT.C
@@ -0,0 +1,146 @@
+// lighting.c
+
+#include "light.h"
+
+/*
+
+NOTES
+-----
+
+*/
+
+float scaledist = 1.0;
+float scalecos = 0.5;
+float rangescale = 0.5;
+
+byte *filebase, *file_p, *file_end;
+
+dmodel_t *bspmodel;
+int bspfileface; // next surface to dispatch
+
+vec3_t bsp_origin;
+
+qboolean extrasamples;
+
+float minlights[MAX_MAP_FACES];
+
+
+byte *GetFileSpace (int size)
+{
+ byte *buf;
+
+ LOCK;
+ file_p = (byte *)(((long)file_p + 3)&~3);
+ buf = file_p;
+ file_p += size;
+ UNLOCK;
+ if (file_p > file_end)
+ Error ("GetFileSpace: overrun");
+ return buf;
+}
+
+
+void LightThread (void *junk)
+{
+ int i;
+
+ while (1)
+ {
+ LOCK;
+ i = bspfileface++;
+ UNLOCK;
+ if (i >= numfaces)
+ return;
+
+ LightFace (i);
+ }
+}
+
+/*
+=============
+LightWorld
+=============
+*/
+void LightWorld (void)
+{
+ filebase = file_p = dlightdata;
+ file_end = filebase + MAX_MAP_LIGHTING;
+
+ RunThreadsOn (LightThread);
+
+ lightdatasize = file_p - filebase;
+
+ printf ("lightdatasize: %i\n", lightdatasize);
+}
+
+
+/*
+========
+main
+
+light modelfile
+========
+*/
+int main (int argc, char **argv)
+{
+ int i;
+ double start, end;
+ char source[1024];
+
+ printf ("----- LightFaces ----\n");
+
+ for (i=1 ; i<argc ; i++)
+ {
+ if (!strcmp(argv[i],"-threads"))
+ {
+ numthreads = atoi (argv[i+1]);
+ i++;
+ }
+ else if (!strcmp(argv[i],"-extra"))
+ {
+ extrasamples = true;
+ printf ("extra sampling enabled\n");
+ }
+ else if (!strcmp(argv[i],"-dist"))
+ {
+ scaledist = atof (argv[i+1]);
+ i++;
+ }
+ else if (!strcmp(argv[i],"-range"))
+ {
+ rangescale = atof (argv[i+1]);
+ i++;
+ }
+ else if (argv[i][0] == '-')
+ Error ("Unknown option \"%s\"", argv[i]);
+ else
+ break;
+ }
+
+ if (i != argc - 1)
+ Error ("usage: light [-threads num] [-extra] bspfile");
+
+ InitThreads ();
+
+ start = I_FloatTime ();
+
+ strcpy (source, argv[i]);
+ StripExtension (source);
+ DefaultExtension (source, ".bsp");
+
+ LoadBSPFile (source);
+ LoadEntities ();
+
+ MakeTnodes (&dmodels[0]);
+
+ LightWorld ();
+
+ WriteEntitiesToString ();
+ WriteBSPFile (source);
+
+ end = I_FloatTime ();
+ printf ("%5.1f seconds elapsed\n", end-start);
+
+ return 0;
+}
+
diff --git a/qutils/LIGHT/LIGHT.H b/qutils/LIGHT/LIGHT.H
new file mode 100644
index 0000000..9dcdff6
--- /dev/null
+++ b/qutils/LIGHT/LIGHT.H
@@ -0,0 +1,38 @@
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "bspfile.h"
+#include "entities.h"
+#include "threads.h"
+
+#define ON_EPSILON 0.1
+
+#define MAXLIGHTS 1024
+
+void LoadNodes (char *file);
+qboolean TestLine (vec3_t start, vec3_t stop);
+
+void LightFace (int surfnum);
+void LightLeaf (dleaf_t *leaf);
+
+void MakeTnodes (dmodel_t *bm);
+
+extern float scaledist;
+extern float scalecos;
+extern float rangescale;
+
+extern int c_culldistplane, c_proper;
+
+byte *GetFileSpace (int size);
+extern byte *filebase;
+
+extern vec3_t bsp_origin;
+extern vec3_t bsp_xvector;
+extern vec3_t bsp_yvector;
+
+void TransformSample (vec3_t in, vec3_t out);
+void RotateSample (vec3_t in, vec3_t out);
+
+extern qboolean extrasamples;
+
+extern float minlights[MAX_MAP_FACES];
diff --git a/qutils/LIGHT/LIGHT.MAK b/qutils/LIGHT/LIGHT.MAK
new file mode 100644
index 0000000..1933706
--- /dev/null
+++ b/qutils/LIGHT/LIGHT.MAK
@@ -0,0 +1,407 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+!IF "$(CFG)" == ""
+CFG=light - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to light - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "light - Win32 Release" && "$(CFG)" != "light - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "light.mak" CFG="light - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "light - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "light - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "light - Win32 Debug"
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "light - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\light.exe"
+
+CLEAN :
+ -@erase ".\Release\light.exe"
+ -@erase ".\Release\trilib.obj"
+ -@erase ".\Release\threads.obj"
+ -@erase ".\Release\mathlib.obj"
+ -@erase ".\Release\light.obj"
+ -@erase ".\Release\entities.obj"
+ -@erase ".\Release\bspfile.obj"
+ -@erase ".\Release\ltface.obj"
+ -@erase ".\Release\cmdlib.obj"
+ -@erase ".\Release\trace.obj"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /ML /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/light.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\Release/
+CPP_SBRS=
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/light.bsc"
+BSC32_SBRS=
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:console /incremental:no\
+ /pdb:"$(OUTDIR)/light.pdb" /machine:I386 /out:"$(OUTDIR)/light.exe"
+LINK32_OBJS= \
+ ".\Release\trilib.obj" \
+ ".\Release\threads.obj" \
+ ".\Release\mathlib.obj" \
+ ".\Release\light.obj" \
+ ".\Release\entities.obj" \
+ ".\Release\bspfile.obj" \
+ ".\Release\ltface.obj" \
+ ".\Release\cmdlib.obj" \
+ ".\Release\trace.obj"
+
+"$(OUTDIR)\light.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "light - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\light.exe"
+
+CLEAN :
+ -@erase ".\Debug\vc40.pdb"
+ -@erase ".\Debug\vc40.idb"
+ -@erase ".\Debug\light.exe"
+ -@erase ".\Debug\threads.obj"
+ -@erase ".\Debug\trilib.obj"
+ -@erase ".\Debug\bspfile.obj"
+ -@erase ".\Debug\light.obj"
+ -@erase ".\Debug\trace.obj"
+ -@erase ".\Debug\entities.obj"
+ -@erase ".\Debug\mathlib.obj"
+ -@erase ".\Debug\ltface.obj"
+ -@erase ".\Debug\cmdlib.obj"
+ -@erase ".\Debug\light.ilk"
+ -@erase ".\Debug\light.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MLd /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/light.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\Debug/
+CPP_SBRS=
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/light.bsc"
+BSC32_SBRS=
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:console /incremental:yes\
+ /pdb:"$(OUTDIR)/light.pdb" /debug /machine:I386 /out:"$(OUTDIR)/light.exe"
+LINK32_OBJS= \
+ ".\Debug\threads.obj" \
+ ".\Debug\trilib.obj" \
+ ".\Debug\bspfile.obj" \
+ ".\Debug\light.obj" \
+ ".\Debug\trace.obj" \
+ ".\Debug\entities.obj" \
+ ".\Debug\mathlib.obj" \
+ ".\Debug\ltface.obj" \
+ ".\Debug\cmdlib.obj"
+
+"$(OUTDIR)\light.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+################################################################################
+# Begin Target
+
+# Name "light - Win32 Release"
+# Name "light - Win32 Debug"
+
+!IF "$(CFG)" == "light - Win32 Release"
+
+!ELSEIF "$(CFG)" == "light - Win32 Debug"
+
+!ENDIF
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\trace.c
+
+!IF "$(CFG)" == "light - Win32 Release"
+
+DEP_CPP_TRACE=\
+ ".\light.h"\
+
+
+"$(INTDIR)\trace.obj" : $(SOURCE) $(DEP_CPP_TRACE) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "light - Win32 Debug"
+
+DEP_CPP_TRACE=\
+ ".\light.h"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+ ".\..\common\bspfile.h"\
+ ".\entities.h"\
+ ".\threads.h"\
+
+
+"$(INTDIR)\trace.obj" : $(SOURCE) $(DEP_CPP_TRACE) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\threads.c
+DEP_CPP_THREA=\
+ ".\..\common\cmdlib.h"\
+ ".\threads.h"\
+
+
+"$(INTDIR)\threads.obj" : $(SOURCE) $(DEP_CPP_THREA) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\ltface.c
+
+!IF "$(CFG)" == "light - Win32 Release"
+
+DEP_CPP_LTFAC=\
+ ".\light.h"\
+
+
+"$(INTDIR)\ltface.obj" : $(SOURCE) $(DEP_CPP_LTFAC) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "light - Win32 Debug"
+
+DEP_CPP_LTFAC=\
+ ".\light.h"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+ ".\..\common\bspfile.h"\
+ ".\entities.h"\
+ ".\threads.h"\
+
+
+"$(INTDIR)\ltface.obj" : $(SOURCE) $(DEP_CPP_LTFAC) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\light.c
+
+!IF "$(CFG)" == "light - Win32 Release"
+
+DEP_CPP_LIGHT=\
+ ".\light.h"\
+
+
+"$(INTDIR)\light.obj" : $(SOURCE) $(DEP_CPP_LIGHT) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "light - Win32 Debug"
+
+DEP_CPP_LIGHT=\
+ ".\light.h"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+ ".\..\common\bspfile.h"\
+ ".\entities.h"\
+ ".\threads.h"\
+
+
+"$(INTDIR)\light.obj" : $(SOURCE) $(DEP_CPP_LIGHT) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\entities.c
+
+!IF "$(CFG)" == "light - Win32 Release"
+
+DEP_CPP_ENTIT=\
+ ".\light.h"\
+
+
+"$(INTDIR)\entities.obj" : $(SOURCE) $(DEP_CPP_ENTIT) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "light - Win32 Debug"
+
+DEP_CPP_ENTIT=\
+ ".\light.h"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+ ".\..\common\bspfile.h"\
+ ".\entities.h"\
+ ".\threads.h"\
+
+
+"$(INTDIR)\entities.obj" : $(SOURCE) $(DEP_CPP_ENTIT) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\trilib.c
+DEP_CPP_TRILI=\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+ ".\..\common\trilib.h"\
+
+
+"$(INTDIR)\trilib.obj" : $(SOURCE) $(DEP_CPP_TRILI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\mathlib.c
+DEP_CPP_MATHL=\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\cmdlib.c
+DEP_CPP_CMDLI=\
+ ".\..\common\cmdlib.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+ {$(INCLUDE)}"\sys\STAT.H"\
+
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\bspfile.c
+DEP_CPP_BSPFI=\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+ ".\..\common\bspfile.h"\
+
+
+"$(INTDIR)\bspfile.obj" : $(SOURCE) $(DEP_CPP_BSPFI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+# End Target
+# End Project
+################################################################################
diff --git a/qutils/LIGHT/LIGHT.MDP b/qutils/LIGHT/LIGHT.MDP
new file mode 100644
index 0000000..e387ff4
--- /dev/null
+++ b/qutils/LIGHT/LIGHT.MDP
Binary files differ
diff --git a/qutils/LIGHT/LIGHT.NCB b/qutils/LIGHT/LIGHT.NCB
new file mode 100644
index 0000000..e07f5af
--- /dev/null
+++ b/qutils/LIGHT/LIGHT.NCB
Binary files differ
diff --git a/qutils/LIGHT/LTFACE.C b/qutils/LIGHT/LTFACE.C
new file mode 100644
index 0000000..0b22931
--- /dev/null
+++ b/qutils/LIGHT/LTFACE.C
@@ -0,0 +1,588 @@
+
+#include "light.h"
+
+/*
+============
+CastRay
+
+Returns the distance between the points, or -1 if blocked
+=============
+*/
+vec_t CastRay (vec3_t p1, vec3_t p2)
+{
+ int i;
+ vec_t t;
+ qboolean trace;
+
+ trace = TestLine (p1, p2);
+
+ if (!trace)
+ return -1; // ray was blocked
+
+ t = 0;
+ for (i=0 ; i< 3 ; i++)
+ t += (p2[i]-p1[i]) * (p2[i]-p1[i]);
+
+ if (t == 0)
+ t = 1; // don't blow up...
+ return sqrt(t);
+}
+
+/*
+===============================================================================
+
+SAMPLE POINT DETERMINATION
+
+void SetupBlock (dface_t *f) Returns with surfpt[] set
+
+This is a little tricky because the lightmap covers more area than the face.
+If done in the straightforward fashion, some of the
+sample points will be inside walls or on the other side of walls, causing
+false shadows and light bleeds.
+
+To solve this, I only consider a sample point valid if a line can be drawn
+between it and the exact midpoint of the face. If invalid, it is adjusted
+towards the center until it is valid.
+
+(this doesn't completely work)
+
+===============================================================================
+*/
+
+#define SINGLEMAP (18*18*4)
+
+typedef struct
+{
+ vec_t lightmaps[MAXLIGHTMAPS][SINGLEMAP];
+ int numlightstyles;
+ vec_t *light;
+ vec_t facedist;
+ vec3_t facenormal;
+
+ int numsurfpt;
+ vec3_t surfpt[SINGLEMAP];
+
+ vec3_t texorg;
+ vec3_t worldtotex[2]; // s = (world - texorg) . worldtotex[0]
+ vec3_t textoworld[2]; // world = texorg + s * textoworld[0]
+
+ vec_t exactmins[2], exactmaxs[2];
+
+ int texmins[2], texsize[2];
+ int lightstyles[256];
+ int surfnum;
+ dface_t *face;
+} lightinfo_t;
+
+
+/*
+================
+CalcFaceVectors
+
+Fills in texorg, worldtotex. and textoworld
+================
+*/
+void CalcFaceVectors (lightinfo_t *l)
+{
+ texinfo_t *tex;
+ int i, j;
+ vec3_t texnormal;
+ float distscale;
+ vec_t dist, len;
+
+ tex = &texinfo[l->face->texinfo];
+
+// convert from float to vec_t
+ for (i=0 ; i<2 ; i++)
+ for (j=0 ; j<3 ; j++)
+ l->worldtotex[i][j] = tex->vecs[i][j];
+
+// calculate a normal to the texture axis. points can be moved along this
+// without changing their S/T
+ texnormal[0] = tex->vecs[1][1]*tex->vecs[0][2]
+ - tex->vecs[1][2]*tex->vecs[0][1];
+ texnormal[1] = tex->vecs[1][2]*tex->vecs[0][0]
+ - tex->vecs[1][0]*tex->vecs[0][2];
+ texnormal[2] = tex->vecs[1][0]*tex->vecs[0][1]
+ - tex->vecs[1][1]*tex->vecs[0][0];
+ VectorNormalize (texnormal);
+
+// flip it towards plane normal
+ distscale = DotProduct (texnormal, l->facenormal);
+ if (!distscale)
+ Error ("Texture axis perpendicular to face");
+ if (distscale < 0)
+ {
+ distscale = -distscale;
+ VectorSubtract (vec3_origin, texnormal, texnormal);
+ }
+
+// distscale is the ratio of the distance along the texture normal to
+// the distance along the plane normal
+ distscale = 1/distscale;
+
+ for (i=0 ; i<2 ; i++)
+ {
+ len = VectorLength (l->worldtotex[i]);
+ dist = DotProduct (l->worldtotex[i], l->facenormal);
+ dist *= distscale;
+ VectorMA (l->worldtotex[i], -dist, texnormal, l->textoworld[i]);
+ VectorScale (l->textoworld[i], (1/len)*(1/len), l->textoworld[i]);
+ }
+
+
+// calculate texorg on the texture plane
+ for (i=0 ; i<3 ; i++)
+ l->texorg[i] = -tex->vecs[0][3]* l->textoworld[0][i] - tex->vecs[1][3] * l->textoworld[1][i];
+
+// project back to the face plane
+ dist = DotProduct (l->texorg, l->facenormal) - l->facedist - 1;
+ dist *= distscale;
+ VectorMA (l->texorg, -dist, texnormal, l->texorg);
+
+}
+
+/*
+================
+CalcFaceExtents
+
+Fills in s->texmins[] and s->texsize[]
+also sets exactmins[] and exactmaxs[]
+================
+*/
+void CalcFaceExtents (lightinfo_t *l)
+{
+ dface_t *s;
+ vec_t mins[2], maxs[2], val;
+ int i,j, e;
+ dvertex_t *v;
+ texinfo_t *tex;
+
+ s = l->face;
+
+ mins[0] = mins[1] = 999999;
+ maxs[0] = maxs[1] = -99999;
+
+ tex = &texinfo[s->texinfo];
+
+ for (i=0 ; i<s->numedges ; i++)
+ {
+ e = dsurfedges[s->firstedge+i];
+ if (e >= 0)
+ v = dvertexes + dedges[e].v[0];
+ else
+ v = dvertexes + dedges[-e].v[1];
+
+ for (j=0 ; j<2 ; j++)
+ {
+ val = v->point[0] * tex->vecs[j][0] +
+ v->point[1] * tex->vecs[j][1] +
+ v->point[2] * tex->vecs[j][2] +
+ tex->vecs[j][3];
+ if (val < mins[j])
+ mins[j] = val;
+ if (val > maxs[j])
+ maxs[j] = val;
+ }
+ }
+
+ for (i=0 ; i<2 ; i++)
+ {
+ l->exactmins[i] = mins[i];
+ l->exactmaxs[i] = maxs[i];
+
+ mins[i] = floor(mins[i]/16);
+ maxs[i] = ceil(maxs[i]/16);
+
+ l->texmins[i] = mins[i];
+ l->texsize[i] = maxs[i] - mins[i];
+ if (l->texsize[i] > 17)
+ Error ("Bad surface extents");
+ }
+}
+
+/*
+=================
+CalcPoints
+
+For each texture aligned grid point, back project onto the plane
+to get the world xyz value of the sample point
+=================
+*/
+int c_bad;
+void CalcPoints (lightinfo_t *l)
+{
+ int i;
+ int s, t, j;
+ int w, h, step;
+ vec_t starts, startt, us, ut;
+ vec_t *surf;
+ vec_t mids, midt;
+ vec3_t facemid, move;
+
+//
+// fill in surforg
+// the points are biased towards the center of the surface
+// to help avoid edge cases just inside walls
+//
+ surf = l->surfpt[0];
+ mids = (l->exactmaxs[0] + l->exactmins[0])/2;
+ midt = (l->exactmaxs[1] + l->exactmins[1])/2;
+
+ for (j=0 ; j<3 ; j++)
+ facemid[j] = l->texorg[j] + l->textoworld[0][j]*mids + l->textoworld[1][j]*midt;
+
+ if (extrasamples)
+ { // extra filtering
+ h = (l->texsize[1]+1)*2;
+ w = (l->texsize[0]+1)*2;
+ starts = (l->texmins[0]-0.5)*16;
+ startt = (l->texmins[1]-0.5)*16;
+ step = 8;
+ }
+ else
+ {
+ h = l->texsize[1]+1;
+ w = l->texsize[0]+1;
+ starts = l->texmins[0]*16;
+ startt = l->texmins[1]*16;
+ step = 16;
+ }
+
+ l->numsurfpt = w * h;
+ for (t=0 ; t<h ; t++)
+ {
+ for (s=0 ; s<w ; s++, surf+=3)
+ {
+ us = starts + s*step;
+ ut = startt + t*step;
+
+ // if a line can be traced from surf to facemid, the point is good
+ for (i=0 ; i<6 ; i++)
+ {
+ // calculate texture point
+ for (j=0 ; j<3 ; j++)
+ surf[j] = l->texorg[j] + l->textoworld[0][j]*us
+ + l->textoworld[1][j]*ut;
+
+ if (CastRay (facemid, surf) != -1)
+ break; // got it
+ if (i & 1)
+ {
+ if (us > mids)
+ {
+ us -= 8;
+ if (us < mids)
+ us = mids;
+ }
+ else
+ {
+ us += 8;
+ if (us > mids)
+ us = mids;
+ }
+ }
+ else
+ {
+ if (ut > midt)
+ {
+ ut -= 8;
+ if (ut < midt)
+ ut = midt;
+ }
+ else
+ {
+ ut += 8;
+ if (ut > midt)
+ ut = midt;
+ }
+ }
+
+ // move surf 8 pixels towards the center
+ VectorSubtract (facemid, surf, move);
+ VectorNormalize (move);
+ VectorMA (surf, 8, move, surf);
+ }
+ if (i == 2)
+ c_bad++;
+ }
+ }
+
+}
+
+
+/*
+===============================================================================
+
+FACE LIGHTING
+
+===============================================================================
+*/
+
+int c_culldistplane, c_proper;
+
+/*
+================
+SingleLightFace
+================
+*/
+void SingleLightFace (entity_t *light, lightinfo_t *l)
+{
+ vec_t dist;
+ vec3_t incoming;
+ vec_t angle;
+ vec_t add;
+ vec_t *surf;
+ qboolean hit;
+ int mapnum;
+ int size;
+ int c, i;
+ vec3_t rel;
+ vec3_t spotvec;
+ vec_t falloff;
+ vec_t *lightsamp;
+
+ VectorSubtract (light->origin, bsp_origin, rel);
+ dist = scaledist * (DotProduct (rel, l->facenormal) - l->facedist);
+
+// don't bother with lights behind the surface
+ if (dist <= 0)
+ return;
+
+// don't bother with light too far away
+ if (dist > light->light)
+ {
+ c_culldistplane++;
+ return;
+ }
+
+ if (light->targetent)
+ {
+ VectorSubtract (light->targetent->origin, light->origin, spotvec);
+ VectorNormalize (spotvec);
+ if (!light->angle)
+ falloff = -cos(20*Q_PI/180);
+ else
+ falloff = -cos(light->angle/2*Q_PI/180);
+ }
+ else
+ falloff = 0; // shut up compiler warnings
+
+ mapnum = 0;
+ for (mapnum=0 ; mapnum<l->numlightstyles ; mapnum++)
+ if (l->lightstyles[mapnum] == light->style)
+ break;
+ lightsamp = l->lightmaps[mapnum];
+ if (mapnum == l->numlightstyles)
+ { // init a new light map
+ if (mapnum == MAXLIGHTMAPS)
+ {
+ printf ("WARNING: Too many light styles on a face\n");
+ return;
+ }
+ size = (l->texsize[1]+1)*(l->texsize[0]+1);
+ for (i=0 ; i<size ; i++)
+ lightsamp[i] = 0;
+ }
+
+//
+// check it for real
+//
+ hit = false;
+ c_proper++;
+
+ surf = l->surfpt[0];
+ for (c=0 ; c<l->numsurfpt ; c++, surf+=3)
+ {
+ dist = CastRay(light->origin, surf)*scaledist;
+ if (dist < 0)
+ continue; // light doesn't reach
+
+ VectorSubtract (light->origin, surf, incoming);
+ VectorNormalize (incoming);
+ angle = DotProduct (incoming, l->facenormal);
+ if (light->targetent)
+ { // spotlight cutoff
+ if (DotProduct (spotvec, incoming) > falloff)
+ continue;
+ }
+
+ angle = (1.0-scalecos) + scalecos*angle;
+ add = light->light - dist;
+ add *= angle;
+ if (add < 0)
+ continue;
+ lightsamp[c] += add;
+ if (lightsamp[c] > 1) // ignore real tiny lights
+ hit = true;
+ }
+
+ if (mapnum == l->numlightstyles && hit)
+ {
+ l->lightstyles[mapnum] = light->style;
+ l->numlightstyles++; // the style has some real data now
+ }
+}
+
+/*
+============
+FixMinlight
+============
+*/
+void FixMinlight (lightinfo_t *l)
+{
+ int i, j;
+ float minlight;
+
+ minlight = minlights[l->surfnum];
+
+// if minlight is set, there must be a style 0 light map
+ if (!minlight)
+ return;
+
+ for (i=0 ; i< l->numlightstyles ; i++)
+ {
+ if (l->lightstyles[i] == 0)
+ break;
+ }
+ if (i == l->numlightstyles)
+ {
+ if (l->numlightstyles == MAXLIGHTMAPS)
+ return; // oh well..
+ for (j=0 ; j<l->numsurfpt ; j++)
+ l->lightmaps[i][j] = minlight;
+ l->lightstyles[i] = 0;
+ l->numlightstyles++;
+ }
+ else
+ {
+ for (j=0 ; j<l->numsurfpt ; j++)
+ if ( l->lightmaps[i][j] < minlight)
+ l->lightmaps[i][j] = minlight;
+ }
+}
+
+
+/*
+============
+LightFace
+============
+*/
+void LightFace (int surfnum)
+{
+ dface_t *f;
+ lightinfo_t l;
+ int s, t;
+ int i,j,c;
+ vec_t total;
+ int size;
+ int lightmapwidth, lightmapsize;
+ byte *out;
+ vec_t *light;
+ int w, h;
+
+ f = dfaces + surfnum;
+
+//
+// some surfaces don't need lightmaps
+//
+ f->lightofs = -1;
+ for (j=0 ; j<MAXLIGHTMAPS ; j++)
+ f->styles[j] = 255;
+
+ if ( texinfo[f->texinfo].flags & TEX_SPECIAL)
+ { // non-lit texture
+ return;
+ }
+
+ memset (&l, 0, sizeof(l));
+ l.surfnum = surfnum;
+ l.face = f;
+
+//
+// rotate plane
+//
+ VectorCopy (dplanes[f->planenum].normal, l.facenormal);
+ l.facedist = dplanes[f->planenum].dist;
+ if (f->side)
+ {
+ VectorSubtract (vec3_origin, l.facenormal, l.facenormal);
+ l.facedist = -l.facedist;
+ }
+
+
+
+ CalcFaceVectors (&l);
+ CalcFaceExtents (&l);
+ CalcPoints (&l);
+
+ lightmapwidth = l.texsize[0]+1;
+
+ size = lightmapwidth*(l.texsize[1]+1);
+ if (size > SINGLEMAP)
+ Error ("Bad lightmap size");
+
+ for (i=0 ; i<MAXLIGHTMAPS ; i++)
+ l.lightstyles[i] = 255;
+
+//
+// cast all lights
+//
+ l.numlightstyles = 0;
+ for (i=0 ; i<num_entities ; i++)
+ {
+ if (entities[i].light)
+ SingleLightFace (&entities[i], &l);
+ }
+
+ FixMinlight (&l);
+
+ if (!l.numlightstyles)
+ { // no light hitting it
+ return;
+ }
+
+//
+// save out the values
+//
+ for (i=0 ; i <MAXLIGHTMAPS ; i++)
+ f->styles[i] = l.lightstyles[i];
+
+ lightmapsize = size*l.numlightstyles;
+
+ out = GetFileSpace (lightmapsize);
+ f->lightofs = out - filebase;
+
+// extra filtering
+ h = (l.texsize[1]+1)*2;
+ w = (l.texsize[0]+1)*2;
+
+ for (i=0 ; i< l.numlightstyles ; i++)
+ {
+ if (l.lightstyles[i] == 0xff)
+ Error ("Wrote empty lightmap");
+ light = l.lightmaps[i];
+ c = 0;
+ for (t=0 ; t<=l.texsize[1] ; t++)
+ for (s=0 ; s<=l.texsize[0] ; s++, c++)
+ {
+ if (extrasamples)
+ { // filtered sample
+ total = light[t*2*w+s*2] + light[t*2*w+s*2+1]
+ + light[(t*2+1)*w+s*2] + light[(t*2+1)*w+s*2+1];
+ total *= 0.25;
+ }
+ else
+ total = light[c];
+ total *= rangescale; // scale before clamping
+ if (total > 255)
+ total = 255;
+ if (total < 0)
+ Error ("light < 0");
+ *out++ = total;
+ }
+ }
+
+
+}
+
diff --git a/qutils/LIGHT/THREADS.C b/qutils/LIGHT/THREADS.C
new file mode 100644
index 0000000..522d6d9
--- /dev/null
+++ b/qutils/LIGHT/THREADS.C
@@ -0,0 +1,66 @@
+
+#include "cmdlib.h"
+#include "threads.h"
+
+#ifdef __alpha
+int numthreads = 4;
+pthread_mutex_t *my_mutex;
+#else
+int numthreads = 1;
+#endif
+
+void InitThreads (void)
+{
+#ifdef __alpha
+ pthread_mutexattr_t mattrib;
+
+ my_mutex = malloc (sizeof(*my_mutex));
+ if (pthread_mutexattr_create (&mattrib) == -1)
+ Error ("pthread_mutex_attr_create failed");
+ if (pthread_mutexattr_setkind_np (&mattrib, MUTEX_FAST_NP) == -1)
+ Error ("pthread_mutexattr_setkind_np failed");
+ if (pthread_mutex_init (my_mutex, mattrib) == -1)
+ Error ("pthread_mutex_init failed");
+#endif
+}
+
+/*
+===============
+RunThreadsOn
+===============
+*/
+void RunThreadsOn ( threadfunc_t func )
+{
+#ifdef __alpha
+ pthread_t work_threads[256];
+ pthread_addr_t status;
+ pthread_attr_t attrib;
+ int i;
+
+ if (numthreads == 1)
+ {
+ func (NULL);
+ return;
+ }
+
+ if (pthread_attr_create (&attrib) == -1)
+ Error ("pthread_attr_create failed");
+ if (pthread_attr_setstacksize (&attrib, 0x100000) == -1)
+ Error ("pthread_attr_setstacksize failed");
+
+ for (i=0 ; i<numthreads ; i++)
+ {
+ if (pthread_create(&work_threads[i], attrib
+ , (pthread_startroutine_t)func, (pthread_addr_t)i) == -1)
+ Error ("pthread_create failed");
+ }
+
+ for (i=0 ; i<numthreads ; i++)
+ {
+ if (pthread_join (work_threads[i], &status) == -1)
+ Error ("pthread_join failed");
+ }
+#else
+ func (NULL);
+#endif
+}
diff --git a/qutils/LIGHT/THREADS.H b/qutils/LIGHT/THREADS.H
new file mode 100644
index 0000000..8be043f
--- /dev/null
+++ b/qutils/LIGHT/THREADS.H
@@ -0,0 +1,17 @@
+
+#ifdef __alpha
+#include <pthread.h>
+extern pthread_mutex_t *my_mutex;
+#define LOCK pthread_mutex_lock (my_mutex)
+#define UNLOCK pthread_mutex_unlock (my_mutex)
+#else
+#define LOCK
+#define UNLOCK
+#endif
+
+extern int numthreads;
+
+typedef void (threadfunc_t) (void *);
+
+void InitThreads (void);
+void RunThreadsOn ( threadfunc_t func );
diff --git a/qutils/LIGHT/TRACE.C b/qutils/LIGHT/TRACE.C
new file mode 100644
index 0000000..ded4924
--- /dev/null
+++ b/qutils/LIGHT/TRACE.C
@@ -0,0 +1,197 @@
+// trace.c
+
+#include "light.h"
+
+typedef struct tnode_s
+{
+ int type;
+ vec3_t normal;
+ float dist;
+ int children[2];
+ int pad;
+} tnode_t;
+
+tnode_t *tnodes, *tnode_p;
+
+/*
+==============
+MakeTnode
+
+Converts the disk node structure into the efficient tracing structure
+==============
+*/
+void MakeTnode (int nodenum)
+{
+ tnode_t *t;
+ dplane_t *plane;
+ int i;
+ dnode_t *node;
+
+ t = tnode_p++;
+
+ node = dnodes + nodenum;
+ plane = dplanes + node->planenum;
+
+ t->type = plane->type;
+ VectorCopy (plane->normal, t->normal);
+ t->dist = plane->dist;
+
+ for (i=0 ; i<2 ; i++)
+ {
+ if (node->children[i] < 0)
+ t->children[i] = dleafs[-node->children[i] - 1].contents;
+ else
+ {
+ t->children[i] = tnode_p - tnodes;
+ MakeTnode (node->children[i]);
+ }
+ }
+
+}
+
+
+/*
+=============
+MakeTnodes
+
+Loads the node structure out of a .bsp file to be used for light occlusion
+=============
+*/
+void MakeTnodes (dmodel_t *bm)
+{
+ tnode_p = tnodes = malloc(numnodes * sizeof(tnode_t));
+
+ MakeTnode (0);
+}
+
+
+
+/*
+==============================================================================
+
+LINE TRACING
+
+The major lighting operation is a point to point visibility test, performed
+by recursive subdivision of the line by the BSP tree.
+
+==============================================================================
+*/
+
+typedef struct
+{
+ vec3_t backpt;
+ int side;
+ int node;
+} tracestack_t;
+
+
+/*
+==============
+TestLine
+==============
+*/
+qboolean TestLine (vec3_t start, vec3_t stop)
+{
+ int node;
+ float front, back;
+ tracestack_t *tstack_p;
+ int side;
+ float frontx,fronty, frontz, backx, backy, backz;
+ tracestack_t tracestack[64];
+ tnode_t *tnode;
+
+ frontx = start[0];
+ fronty = start[1];
+ frontz = start[2];
+ backx = stop[0];
+ backy = stop[1];
+ backz = stop[2];
+
+ tstack_p = tracestack;
+ node = 0;
+
+ while (1)
+ {
+ while (node < 0 && node != CONTENTS_SOLID)
+ {
+ // pop up the stack for a back side
+ tstack_p--;
+ if (tstack_p < tracestack)
+ return true;
+ node = tstack_p->node;
+
+ // set the hit point for this plane
+
+ frontx = backx;
+ fronty = backy;
+ frontz = backz;
+
+ // go down the back side
+
+ backx = tstack_p->backpt[0];
+ backy = tstack_p->backpt[1];
+ backz = tstack_p->backpt[2];
+
+ node = tnodes[tstack_p->node].children[!tstack_p->side];
+ }
+
+ if (node == CONTENTS_SOLID)
+ return false; // DONE!
+
+ tnode = &tnodes[node];
+
+ switch (tnode->type)
+ {
+ case PLANE_X:
+ front = frontx - tnode->dist;
+ back = backx - tnode->dist;
+ break;
+ case PLANE_Y:
+ front = fronty - tnode->dist;
+ back = backy - tnode->dist;
+ break;
+ case PLANE_Z:
+ front = frontz - tnode->dist;
+ back = backz - tnode->dist;
+ break;
+ default:
+ front = (frontx*tnode->normal[0] + fronty*tnode->normal[1] + frontz*tnode->normal[2]) - tnode->dist;
+ back = (backx*tnode->normal[0] + backy*tnode->normal[1] + backz*tnode->normal[2]) - tnode->dist;
+ break;
+ }
+
+ if (front > -ON_EPSILON && back > -ON_EPSILON)
+// if (front > 0 && back > 0)
+ {
+ node = tnode->children[0];
+ continue;
+ }
+
+ if (front < ON_EPSILON && back < ON_EPSILON)
+// if (front <= 0 && back <= 0)
+ {
+ node = tnode->children[1];
+ continue;
+ }
+
+ side = front < 0;
+
+ front = front / (front-back);
+
+ tstack_p->node = node;
+ tstack_p->side = side;
+ tstack_p->backpt[0] = backx;
+ tstack_p->backpt[1] = backy;
+ tstack_p->backpt[2] = backz;
+
+ tstack_p++;
+
+ backx = frontx + front*(backx-frontx);
+ backy = fronty + front*(backy-fronty);
+ backz = frontz + front*(backz-frontz);
+
+ node = tnode->children[side];
+ }
+}
+
+
diff --git a/qutils/MODELGEN/ANORMS.H b/qutils/MODELGEN/ANORMS.H
new file mode 100644
index 0000000..adc6908
--- /dev/null
+++ b/qutils/MODELGEN/ANORMS.H
@@ -0,0 +1,162 @@
+{-0.525731, 0.000000, 0.850651},
+{-0.442863, 0.238856, 0.864188},
+{-0.295242, 0.000000, 0.955423},
+{-0.309017, 0.500000, 0.809017},
+{-0.162460, 0.262866, 0.951056},
+{0.000000, 0.000000, 1.000000},
+{0.000000, 0.850651, 0.525731},
+{-0.147621, 0.716567, 0.681718},
+{0.147621, 0.716567, 0.681718},
+{0.000000, 0.525731, 0.850651},
+{0.309017, 0.500000, 0.809017},
+{0.525731, 0.000000, 0.850651},
+{0.295242, 0.000000, 0.955423},
+{0.442863, 0.238856, 0.864188},
+{0.162460, 0.262866, 0.951056},
+{-0.681718, 0.147621, 0.716567},
+{-0.809017, 0.309017, 0.500000},
+{-0.587785, 0.425325, 0.688191},
+{-0.850651, 0.525731, 0.000000},
+{-0.864188, 0.442863, 0.238856},
+{-0.716567, 0.681718, 0.147621},
+{-0.688191, 0.587785, 0.425325},
+{-0.500000, 0.809017, 0.309017},
+{-0.238856, 0.864188, 0.442863},
+{-0.425325, 0.688191, 0.587785},
+{-0.716567, 0.681718, -0.147621},
+{-0.500000, 0.809017, -0.309017},
+{-0.525731, 0.850651, 0.000000},
+{0.000000, 0.850651, -0.525731},
+{-0.238856, 0.864188, -0.442863},
+{0.000000, 0.955423, -0.295242},
+{-0.262866, 0.951056, -0.162460},
+{0.000000, 1.000000, 0.000000},
+{0.000000, 0.955423, 0.295242},
+{-0.262866, 0.951056, 0.162460},
+{0.238856, 0.864188, 0.442863},
+{0.262866, 0.951056, 0.162460},
+{0.500000, 0.809017, 0.309017},
+{0.238856, 0.864188, -0.442863},
+{0.262866, 0.951056, -0.162460},
+{0.500000, 0.809017, -0.309017},
+{0.850651, 0.525731, 0.000000},
+{0.716567, 0.681718, 0.147621},
+{0.716567, 0.681718, -0.147621},
+{0.525731, 0.850651, 0.000000},
+{0.425325, 0.688191, 0.587785},
+{0.864188, 0.442863, 0.238856},
+{0.688191, 0.587785, 0.425325},
+{0.809017, 0.309017, 0.500000},
+{0.681718, 0.147621, 0.716567},
+{0.587785, 0.425325, 0.688191},
+{0.955423, 0.295242, 0.000000},
+{1.000000, 0.000000, 0.000000},
+{0.951056, 0.162460, 0.262866},
+{0.850651, -0.525731, 0.000000},
+{0.955423, -0.295242, 0.000000},
+{0.864188, -0.442863, 0.238856},
+{0.951056, -0.162460, 0.262866},
+{0.809017, -0.309017, 0.500000},
+{0.681718, -0.147621, 0.716567},
+{0.850651, 0.000000, 0.525731},
+{0.864188, 0.442863, -0.238856},
+{0.809017, 0.309017, -0.500000},
+{0.951056, 0.162460, -0.262866},
+{0.525731, 0.000000, -0.850651},
+{0.681718, 0.147621, -0.716567},
+{0.681718, -0.147621, -0.716567},
+{0.850651, 0.000000, -0.525731},
+{0.809017, -0.309017, -0.500000},
+{0.864188, -0.442863, -0.238856},
+{0.951056, -0.162460, -0.262866},
+{0.147621, 0.716567, -0.681718},
+{0.309017, 0.500000, -0.809017},
+{0.425325, 0.688191, -0.587785},
+{0.442863, 0.238856, -0.864188},
+{0.587785, 0.425325, -0.688191},
+{0.688191, 0.587785, -0.425325},
+{-0.147621, 0.716567, -0.681718},
+{-0.309017, 0.500000, -0.809017},
+{0.000000, 0.525731, -0.850651},
+{-0.525731, 0.000000, -0.850651},
+{-0.442863, 0.238856, -0.864188},
+{-0.295242, 0.000000, -0.955423},
+{-0.162460, 0.262866, -0.951056},
+{0.000000, 0.000000, -1.000000},
+{0.295242, 0.000000, -0.955423},
+{0.162460, 0.262866, -0.951056},
+{-0.442863, -0.238856, -0.864188},
+{-0.309017, -0.500000, -0.809017},
+{-0.162460, -0.262866, -0.951056},
+{0.000000, -0.850651, -0.525731},
+{-0.147621, -0.716567, -0.681718},
+{0.147621, -0.716567, -0.681718},
+{0.000000, -0.525731, -0.850651},
+{0.309017, -0.500000, -0.809017},
+{0.442863, -0.238856, -0.864188},
+{0.162460, -0.262866, -0.951056},
+{0.238856, -0.864188, -0.442863},
+{0.500000, -0.809017, -0.309017},
+{0.425325, -0.688191, -0.587785},
+{0.716567, -0.681718, -0.147621},
+{0.688191, -0.587785, -0.425325},
+{0.587785, -0.425325, -0.688191},
+{0.000000, -0.955423, -0.295242},
+{0.000000, -1.000000, 0.000000},
+{0.262866, -0.951056, -0.162460},
+{0.000000, -0.850651, 0.525731},
+{0.000000, -0.955423, 0.295242},
+{0.238856, -0.864188, 0.442863},
+{0.262866, -0.951056, 0.162460},
+{0.500000, -0.809017, 0.309017},
+{0.716567, -0.681718, 0.147621},
+{0.525731, -0.850651, 0.000000},
+{-0.238856, -0.864188, -0.442863},
+{-0.500000, -0.809017, -0.309017},
+{-0.262866, -0.951056, -0.162460},
+{-0.850651, -0.525731, 0.000000},
+{-0.716567, -0.681718, -0.147621},
+{-0.716567, -0.681718, 0.147621},
+{-0.525731, -0.850651, 0.000000},
+{-0.500000, -0.809017, 0.309017},
+{-0.238856, -0.864188, 0.442863},
+{-0.262866, -0.951056, 0.162460},
+{-0.864188, -0.442863, 0.238856},
+{-0.809017, -0.309017, 0.500000},
+{-0.688191, -0.587785, 0.425325},
+{-0.681718, -0.147621, 0.716567},
+{-0.442863, -0.238856, 0.864188},
+{-0.587785, -0.425325, 0.688191},
+{-0.309017, -0.500000, 0.809017},
+{-0.147621, -0.716567, 0.681718},
+{-0.425325, -0.688191, 0.587785},
+{-0.162460, -0.262866, 0.951056},
+{0.442863, -0.238856, 0.864188},
+{0.162460, -0.262866, 0.951056},
+{0.309017, -0.500000, 0.809017},
+{0.147621, -0.716567, 0.681718},
+{0.000000, -0.525731, 0.850651},
+{0.425325, -0.688191, 0.587785},
+{0.587785, -0.425325, 0.688191},
+{0.688191, -0.587785, 0.425325},
+{-0.955423, 0.295242, 0.000000},
+{-0.951056, 0.162460, 0.262866},
+{-1.000000, 0.000000, 0.000000},
+{-0.850651, 0.000000, 0.525731},
+{-0.955423, -0.295242, 0.000000},
+{-0.951056, -0.162460, 0.262866},
+{-0.864188, 0.442863, -0.238856},
+{-0.951056, 0.162460, -0.262866},
+{-0.809017, 0.309017, -0.500000},
+{-0.864188, -0.442863, -0.238856},
+{-0.951056, -0.162460, -0.262866},
+{-0.809017, -0.309017, -0.500000},
+{-0.681718, 0.147621, -0.716567},
+{-0.681718, -0.147621, -0.716567},
+{-0.850651, 0.000000, -0.525731},
+{-0.688191, 0.587785, -0.425325},
+{-0.587785, 0.425325, -0.688191},
+{-0.425325, 0.688191, -0.587785},
+{-0.425325, -0.688191, -0.587785},
+{-0.587785, -0.425325, -0.688191},
+{-0.688191, -0.587785, -0.425325},
diff --git a/qutils/MODELGEN/MODELGEN.C b/qutils/MODELGEN/MODELGEN.C
new file mode 100644
index 0000000..2d1b478
--- /dev/null
+++ b/qutils/MODELGEN/MODELGEN.C
@@ -0,0 +1,1212 @@
+//
+// modelgen.c: generates a .mdl file from a base triangle file (.tri), a
+// texture containing front and back skins (.lbm), and a series of frame
+// triangle files (.tri). Result is stored in
+// /raid/quake/models/<scriptname>.mdl.
+//
+
+#define INCLUDELIBS
+
+#include <sys/stat.h>
+
+#include "modelgen.h"
+
+#define MAXVERTS 2048
+#define MAXFRAMES 256
+#define MAXSKINS 100
+
+
+typedef struct {
+ aliasframetype_t type; // single frame or group of frames
+ void *pdata; // either a daliasframe_t or group info
+ float interval; // only used for frames in groups
+ int numgroupframes; // only used by group headers
+ char name[16];
+} aliaspackage_t;
+
+typedef struct {
+ aliasskintype_t type; // single skin or group of skiins
+ void *pdata; // either a daliasskinframe_t or group info
+ float interval; // only used for skins in groups
+ int numgroupskins; // only used by group headers
+} aliasskinpackage_t;
+
+typedef struct {
+ int numnormals;
+ float normals[20][3];
+} vertexnormals;
+
+
+typedef struct {
+ vec3_t v;
+ int lightnormalindex;
+} trivert_t;
+
+//============================================================================
+
+trivert_t verts[MAXFRAMES][MAXVERTS];
+mdl_t model;
+
+char file1[1024];
+char skinname[1024];
+char qbasename[1024];
+float scale, scale_up = 1.0;
+vec3_t mins, maxs;
+vec3_t framesmins, framesmaxs;
+vec3_t adjust;
+
+aliaspackage_t frames[MAXFRAMES];
+
+aliasskinpackage_t skins[MAXSKINS];
+
+//
+// base frame info
+//
+vec3_t baseverts[MAXVERTS];
+stvert_t stverts[MAXVERTS];
+dtriangle_t triangles[MAXTRIANGLES];
+int degenerate[MAXTRIANGLES];
+
+
+char cdpartial[256];
+char cddir[256];
+
+int framecount, skincount;
+qboolean cdset;
+int degeneratetris;
+int firstframe = 1;
+float totsize, averagesize;
+
+vertexnormals vnorms[MAXVERTS];
+
+#define NUMVERTEXNORMALS 162
+
+float avertexnormals[NUMVERTEXNORMALS][3] = {
+#include "anorms.h"
+};
+
+trivertx_t tarray[MAXVERTS];
+
+char outname[1024];
+
+
+void ClearModel (void)
+{
+ memset (&model, 0, sizeof(model));
+ model.synctype = ST_RAND; // default
+ framecount = skincount = 0;
+
+ scale = 0;
+ scale_up = 1.0;
+
+ VectorCopy (vec3_origin, adjust);
+ VectorCopy (vec3_origin, mins);
+ VectorCopy (vec3_origin, maxs);
+ VectorCopy (vec3_origin, framesmins);
+ VectorCopy (vec3_origin, framesmaxs);
+
+ degeneratetris = 0;
+ cdset = false;
+ firstframe = 1;
+ totsize = 0.0;
+}
+
+
+/*
+============
+WriteFrame
+============
+*/
+void WriteFrame (FILE *modelouthandle, int framenum)
+{
+ int j, k;
+ trivert_t *pframe;
+ daliasframe_t aframe;
+ float v;
+
+ pframe = verts[framenum];
+
+ strcpy (aframe.name, frames[framenum].name);
+
+ for (j=0 ; j<3 ; j++)
+ {
+ aframe.bboxmin.v[j] = 255;
+ aframe.bboxmax.v[j] = 0;
+ }
+
+ for (j=0 ; j<model.numverts ; j++)
+ {
+ // all of these are byte values, so no need to deal with endianness
+ tarray[j].lightnormalindex = pframe[j].lightnormalindex;
+
+ if (tarray[j].lightnormalindex > NUMVERTEXNORMALS)
+ Error ("invalid lightnormalindex %d\n",
+ tarray[j].lightnormalindex);
+
+ for (k=0 ; k<3 ; k++)
+ {
+ // scale to byte values & min/max check
+ v = (pframe[j].v[k] - model.scale_origin[k]) / model.scale[k];
+
+ tarray[j].v[k] = v;
+
+ if (tarray[j].v[k] < aframe.bboxmin.v[k])
+ {
+ aframe.bboxmin.v[k] = tarray[j].v[k];
+ }
+ if (tarray[j].v[k] > aframe.bboxmax.v[k])
+ {
+ aframe.bboxmax.v[k] = tarray[j].v[k];
+ }
+
+
+ }
+ }
+
+ SafeWrite (modelouthandle, &aframe, sizeof (aframe));
+
+ SafeWrite (modelouthandle, &tarray[0],
+ model.numverts * sizeof(tarray[0]));
+}
+
+
+/*
+============
+WriteGroupBBox
+============
+*/
+void WriteGroupBBox (FILE *modelouthandle, int numframes, int curframe)
+{
+ int i, j, k;
+ daliasgroup_t dagroup;
+ trivert_t *pframe;
+
+
+ dagroup.numframes = LittleLong (numframes);
+
+ for (i=0 ; i<3 ; i++)
+ {
+ dagroup.bboxmin.v[i] = 255;
+ dagroup.bboxmax.v[i] = 0;
+ }
+
+ for (i=0 ; i<numframes ; i++)
+ {
+ pframe = (trivert_t *)frames[curframe].pdata;
+
+ for (j=0 ; j<model.numverts ; j++)
+ {
+ for (k=0 ; k<3 ; k++)
+ {
+ // scale to byte values & min/max check
+ tarray[j].v[k] = (pframe[j].v[k] - model.scale_origin[k]) /
+ model.scale[k];
+ if (tarray[j].v[k] < dagroup.bboxmin.v[k])
+ dagroup.bboxmin.v[k] = tarray[j].v[k];
+ if (tarray[j].v[k] > dagroup.bboxmax.v[k])
+ dagroup.bboxmax.v[k] = tarray[j].v[k];
+ }
+ }
+
+ curframe++;
+ }
+
+ SafeWrite (modelouthandle, &dagroup, sizeof(dagroup));
+}
+
+
+/*
+============
+WriteModel
+============
+*/
+void WriteModelFile (FILE *modelouthandle)
+{
+ int i, curframe, curskin;
+ float dist[3];
+ mdl_t modeltemp;
+
+// Calculate the bounding box for this model
+ for (i=0 ; i<3 ; i++)
+ {
+ printf ("framesmins[%d]: %f, framesmaxs[%d]: %f\n",
+ i, framesmins[i], i, framesmaxs[i]);
+ if (fabs (framesmins[i]) > fabs (framesmaxs[i]))
+ dist[i] = framesmins[i];
+ else
+ dist[i] = framesmaxs[i];
+
+ model.scale[i] = (framesmaxs[i] - framesmins[i]) / 255.9;
+ model.scale_origin[i] = framesmins[i];
+ }
+
+ model.boundingradius = sqrt(dist[0] * dist[0] +
+ dist[1] * dist[1] +
+ dist[2] * dist[2]);
+
+//
+// write out the model header
+//
+ modeltemp.ident = LittleLong (IDPOLYHEADER);
+ modeltemp.version = LittleLong (ALIAS_VERSION);
+ modeltemp.boundingradius = LittleFloat (model.boundingradius);
+
+ for (i=0 ; i<3 ; i++)
+ {
+ modeltemp.scale[i] = LittleFloat (model.scale[i]);
+ modeltemp.scale_origin[i] = LittleFloat (model.scale_origin[i]);
+ modeltemp.eyeposition[i] = LittleFloat (model.eyeposition[i] +
+ adjust[i]);
+ }
+
+ modeltemp.flags = LittleLong (model.flags);
+ modeltemp.numskins = LittleLong (model.numskins);
+ modeltemp.skinwidth = LittleLong (model.skinwidth);
+ modeltemp.skinheight = LittleLong (model.skinheight);
+ modeltemp.numverts = LittleLong (model.numverts);
+ modeltemp.numtris = LittleLong (model.numtris - degeneratetris);
+ modeltemp.numframes = LittleLong (model.numframes);
+ modeltemp.synctype = LittleFloat (model.synctype);
+ averagesize = totsize / model.numtris;
+ modeltemp.size = LittleFloat (averagesize);
+
+ SafeWrite (modelouthandle, &modeltemp, sizeof(model));
+
+//
+// write out the skins
+//
+ curskin = 0;
+
+ for (i=0 ; i<model.numskins ; i++)
+ {
+ SafeWrite (modelouthandle, &skins[curskin].type,
+ sizeof(skins[curskin].type));
+
+ SafeWrite (modelouthandle, skins[curskin].pdata,
+ model.skinwidth * model.skinheight);
+
+ curskin++;
+ }
+
+//
+// write out the base model (the s & t coordinates for the vertices)
+//
+ for (i=0 ; i<model.numverts ; i++)
+ {
+ if (stverts[i].onseam == 3)
+ {
+ stverts[i].onseam = LittleLong (ALIAS_ONSEAM);
+ }
+ else
+ {
+ stverts[i].onseam = LittleLong (0);
+ }
+
+ stverts[i].s = LittleLong (stverts[i].s);
+ stverts[i].t = LittleLong (stverts[i].t);
+ }
+
+ SafeWrite (modelouthandle, stverts, model.numverts * sizeof(stverts[0]));
+
+//
+// write out the triangles
+//
+ for (i=0 ; i<model.numtris ; i++)
+ {
+ int j;
+ dtriangle_t tri;
+
+ if (!degenerate[i])
+ {
+ tri.facesfront = LittleLong (triangles[i].facesfront);
+
+ for (j=0 ; j<3 ; j++)
+ {
+ tri.vertindex[j] = LittleLong (triangles[i].vertindex[j]);
+ }
+
+ SafeWrite (modelouthandle,
+ &tri,
+ sizeof(tri));
+ }
+ }
+
+//
+// write out the frames
+//
+ curframe = 0;
+
+ for (i=0 ; i<model.numframes ; i++)
+ {
+ SafeWrite (modelouthandle, &frames[curframe].type,
+ sizeof(frames[curframe].type));
+
+ if (frames[curframe].type == ALIAS_SINGLE)
+ {
+ //
+ // single (non-grouped) frame
+ //
+ WriteFrame (modelouthandle, curframe);
+ curframe++;
+ }
+ else
+ {
+ int j, numframes, groupframe;
+ float totinterval;
+
+ groupframe = curframe;
+ curframe++;
+ numframes = frames[groupframe].numgroupframes;
+
+ //
+ // set and write the group header
+ //
+ WriteGroupBBox (modelouthandle, numframes, curframe);
+
+ //
+ // write the interval array
+ //
+ totinterval = 0.0;
+
+ for (j=0 ; j<numframes ; j++)
+ {
+ daliasinterval_t temp;
+
+ totinterval += frames[groupframe+1+j].interval;
+ temp.interval = LittleFloat (totinterval);
+
+ SafeWrite (modelouthandle, &temp, sizeof(temp));
+ }
+
+ for (j=0 ; j<numframes ; j++)
+ {
+ WriteFrame (modelouthandle, curframe);
+ curframe++;
+ }
+ }
+ }
+}
+
+
+/*
+===============
+WriteModel
+===============
+*/
+void WriteModel (void)
+{
+ FILE *modelouthandle;
+//
+// write the model output file
+//
+ if (!framecount)
+ {
+ printf ("no frames grabbed, no file generated\n");
+ return;
+ }
+
+ if (!skincount)
+ Error ("frames with no skins\n");
+
+ StripExtension (outname);
+ strcat (outname, ".mdl");
+
+ printf ("---------------------\n");
+ printf ("writing %s:\n", outname);
+ modelouthandle = SafeOpenWrite (outname);
+
+ WriteModelFile (modelouthandle);
+
+ printf ("%4d frame(s)\n", model.numframes);
+ printf ("%4d ungrouped frame(s), including group headers\n", framecount);
+ printf ("%4d skin(s)\n", model.numskins);
+ printf ("%4d degenerate triangles(s) removed\n", degeneratetris);
+ printf ("%4d triangles emitted\n", model.numtris - degeneratetris);
+ printf ("pixels per triangle %f\n", averagesize);
+
+ printf ("file size: %d\n", (int)ftell (modelouthandle) );
+ printf ("---------------------\n");
+
+ fclose (modelouthandle);
+
+ ClearModel ();
+}
+
+
+/*
+============
+SetSkinValues
+
+Called for the base frame
+============
+*/
+void SetSkinValues (void)
+{
+ int i;
+ float v;
+ int width, height, iwidth, iheight, skinwidth;
+ float basex, basey;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ mins[i] = 9999999;
+ maxs[i] = -9999999;
+ }
+
+ for (i=0 ; i<model.numverts ; i++)
+ {
+ int j;
+
+ stverts[i].onseam = 0;
+
+ for (j=0 ; j<3 ; j++)
+ {
+ v = baseverts[i][j];
+ if (v < mins[j])
+ mins[j] = v;
+ if (v > maxs[j])
+ maxs[j] = v;
+ }
+ }
+
+ for (i=0 ; i<3 ; i++)
+ {
+ mins[i] = floor(mins[i]);
+ maxs[i] = ceil(maxs[i]);
+ }
+
+ width = maxs[0] - mins[0];
+ height = maxs[2] - mins[2];
+
+ printf ("width: %i height: %i\n",width, height);
+
+ scale = 8;
+ if (width*scale >= 150)
+ scale = 150.0 / width;
+ if (height*scale >= 190)
+ scale = 190.0 / height;
+ iwidth = ceil(width*scale) + 4;
+ iheight = ceil(height*scale) + 4;
+
+ printf ("scale: %f\n",scale);
+ printf ("iwidth: %i iheight: %i\n",iwidth, iheight);
+
+//
+// determine which side of each triangle to map the texture to
+//
+ for (i=0 ; i<model.numtris ; i++)
+ {
+ int j;
+ vec3_t vtemp1, vtemp2, normal;
+
+ VectorSubtract (baseverts[triangles[i].vertindex[0]],
+ baseverts[triangles[i].vertindex[1]],
+ vtemp1);
+ VectorSubtract (baseverts[triangles[i].vertindex[2]],
+ baseverts[triangles[i].vertindex[1]],
+ vtemp2);
+ CrossProduct (vtemp1, vtemp2, normal);
+
+ if (normal[1] > 0)
+ {
+ basex = iwidth + 2;
+ triangles[i].facesfront = 0;
+ }
+ else
+ {
+ basex = 2;
+ triangles[i].facesfront = 1;
+ }
+ basey = 2;
+
+ for (j=0 ; j<3 ; j++)
+ {
+ float *pbasevert;
+ stvert_t *pstvert;
+
+ pbasevert = baseverts[triangles[i].vertindex[j]];
+ pstvert = &stverts[triangles[i].vertindex[j]];
+
+ if (triangles[i].facesfront)
+ {
+ pstvert->onseam |= 1;
+ }
+ else
+ {
+ pstvert->onseam |= 2;
+ }
+
+ if ((triangles[i].facesfront) || ((pstvert->onseam & 1) == 0))
+ {
+ // we want the front s value for seam vertices
+ pstvert->s = Q_rint((pbasevert[0] - mins[0]) * scale + basex);
+ pstvert->t = Q_rint((maxs[2] - pbasevert[2]) * scale + basey);
+ }
+ }
+ }
+
+// make the width a multiple of 4; some hardware requires this, and it ensures
+// dword alignment for each scan
+ skinwidth = iwidth*2;
+ model.skinwidth = (skinwidth + 3) & ~3;
+ model.skinheight = iheight;
+
+ printf ("skin width: %i (unpadded width %i) skin height: %i\n",
+ model.skinwidth, skinwidth, model.skinheight);
+}
+
+
+/*
+=================
+Cmd_Base
+=================
+*/
+void Cmd_Base (void)
+{
+ triangle_t *ptri;
+ int i, j, k;
+ int time1;
+
+ GetToken (false);
+ strcpy (qbasename, token);
+
+ sprintf (file1, "%s/%s.tri", cdpartial, token);
+ ExpandPathAndArchive (file1);
+
+ sprintf (file1, "%s/%s.tri", cddir, token);
+ time1 = FileTime (file1);
+ if (time1 == -1)
+ Error ("%s doesn't exist", file1);
+
+//
+// load the base triangles
+//
+ LoadTriangleList (file1, &ptri, &model.numtris);
+ printf("NUMBER OF TRIANGLES (including degenerate triangles): %d\n",
+ model.numtris);
+
+//
+// run through all the base triangles, storing each unique vertex in the
+// base vertex list and setting the indirect triangles to point to the base
+// vertices
+//
+ for (i=0 ; i<model.numtris ; i++)
+ {
+ if (VectorCompare (ptri[i].verts[0], ptri[i].verts[1]) ||
+ VectorCompare (ptri[i].verts[1], ptri[i].verts[2]) ||
+ VectorCompare (ptri[i].verts[2], ptri[i].verts[0]))
+ {
+ degeneratetris++;
+ degenerate[i] = 1;
+ }
+ else
+ {
+ degenerate[i] = 0;
+ }
+
+ for (j=0 ; j<3 ; j++)
+ {
+ for (k=0 ; k<model.numverts ; k++)
+ if (VectorCompare (ptri[i].verts[j], baseverts[k]))
+ break; // this vertex is already in the base vertex list
+
+ if (k == model.numverts)
+ {
+ //
+ // new vertex
+ //
+ VectorCopy (ptri[i].verts[j], baseverts[model.numverts]);
+ model.numverts++;
+ }
+
+ triangles[i].vertindex[j] = k;
+ }
+ }
+
+ printf ("NUMBER OF VERTEXES: %i\n", model.numverts);
+
+//
+// calculate s & t for each vertex, and set the skin width and height
+//
+ SetSkinValues ();
+}
+
+
+/*
+===============
+Cmd_Skin
+===============
+*/
+void Cmd_Skin (void)
+{
+ byte *ppal;
+ byte *pskinbitmap;
+ byte *ptemp1, *ptemp2;
+ int i;
+ int time1;
+
+ GetToken (false);
+ strcpy (skinname, token);
+
+ sprintf (file1, "%s/%s.lbm", cdpartial, token);
+ ExpandPathAndArchive (file1);
+
+ sprintf (file1, "%s/%s.lbm", cddir, token);
+ time1 = FileTime (file1);
+ if (time1 == -1)
+ Error ("%s not found", file1);
+
+ if (TokenAvailable ())
+ {
+ GetToken (false);
+ skins[skincount].interval = atof (token);
+ if (skins[skincount].interval <= 0.0)
+ Error ("Non-positive interval");
+ }
+ else
+ {
+ skins[skincount].interval = 0.1;
+ }
+
+//
+// load in the skin .lbm file
+//
+ LoadLBM (file1, &pskinbitmap, &ppal);
+
+//
+// now copy the part of the texture we care about, since LBMs are always
+// loaded as 320x200 bitmaps
+//
+ skins[skincount].pdata =
+ malloc (model.skinwidth * model.skinheight);
+
+ if (!skins[skincount].pdata)
+ Error ("couldn't get memory for skin texture");
+
+ ptemp1 = skins[skincount].pdata;
+ ptemp2 = pskinbitmap;
+
+ for (i=0 ; i<model.skinheight ; i++)
+ {
+ memcpy (ptemp1, ptemp2, model.skinwidth);
+ ptemp1 += model.skinwidth;
+ ptemp2 += 320;
+ }
+
+ skincount++;
+
+ if (skincount > MAXSKINS)
+ Error ("Too many skins; increase MAXSKINS");
+}
+
+
+/*
+===============
+GrabFrame
+===============
+*/
+void GrabFrame (char *frame, int isgroup)
+{
+ triangle_t *ptri;
+ int i, j;
+ trivert_t *ptrivert;
+ int numtris;
+ int time1;
+
+ sprintf (file1, "%s/%s.tri", cdpartial, frame);
+ ExpandPathAndArchive (file1);
+
+ sprintf (file1, "%s/%s.tri",cddir, frame);
+ time1 = FileTime (file1);
+ if (time1 == -1)
+ Error ("%s does not exist",file1);
+
+ printf ("grabbing %s\n", file1);
+ frames[framecount].interval = 0.1;
+ strcpy (frames[framecount].name, frame);
+
+//
+// load the frame
+//
+ LoadTriangleList (file1, &ptri, &numtris);
+
+ if (numtris != model.numtris)
+ Error ("number of triangles doesn't match\n");
+
+// set the intervals
+ if (isgroup && TokenAvailable ())
+ {
+ GetToken (false);
+ frames[framecount].interval = atof (token);
+ if (frames[framecount].interval <= 0.0)
+ Error ("Non-positive interval %s %f", token,
+ frames[framecount].interval);
+ }
+ else
+ {
+ frames[framecount].interval = 0.1;
+ }
+
+//
+// allocate storage for the frame's vertices
+//
+ ptrivert = verts[framecount];
+
+ frames[framecount].pdata = ptrivert;
+ frames[framecount].type = ALIAS_SINGLE;
+
+ for (i=0 ; i<model.numverts ; i++)
+ {
+ vnorms[i].numnormals = 0;
+ }
+
+//
+// store the frame's vertices in the same order as the base. This assumes the
+// triangles and vertices in this frame are in exactly the same order as in the
+// base
+//
+ for (i=0 ; i<numtris ; i++)
+ {
+ vec3_t vtemp1, vtemp2, normal;
+ float ftemp;
+
+ if (degenerate[i])
+ continue;
+
+ if (firstframe)
+ {
+ VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);
+ VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);
+ VectorScale (vtemp1, scale_up, vtemp1);
+ VectorScale (vtemp2, scale_up, vtemp2);
+ CrossProduct (vtemp1, vtemp2, normal);
+
+ totsize += sqrt (normal[0] * normal[0] +
+ normal[1] * normal[1] +
+ normal[2] * normal[2]) / 2.0;
+ }
+
+ VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);
+ VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);
+ CrossProduct (vtemp1, vtemp2, normal);
+
+ VectorNormalize (normal);
+
+ // rotate the normal so the model faces down the positive x axis
+ ftemp = normal[0];
+ normal[0] = -normal[1];
+ normal[1] = ftemp;
+
+ for (j=0 ; j<3 ; j++)
+ {
+ int k;
+ int vertindex;
+
+ vertindex = triangles[i].vertindex[j];
+
+ // rotate the vertices so the model faces down the positive x axis
+ // also adjust the vertices to the desired origin
+ ptrivert[vertindex].v[0] = ((-ptri[i].verts[j][1]) * scale_up) +
+ adjust[0];
+ ptrivert[vertindex].v[1] = (ptri[i].verts[j][0] * scale_up) +
+ adjust[1];
+ ptrivert[vertindex].v[2] = (ptri[i].verts[j][2] * scale_up) +
+ adjust[2];
+
+ for (k=0 ; k<3 ; k++)
+ {
+ if (ptrivert[vertindex].v[k] < framesmins[k])
+ framesmins[k] = ptrivert[vertindex].v[k];
+
+ if (ptrivert[vertindex].v[k] > framesmaxs[k])
+ framesmaxs[k] = ptrivert[vertindex].v[k];
+ }
+
+ VectorCopy (normal,
+ vnorms[vertindex].
+ normals[vnorms[vertindex].numnormals]);
+
+ vnorms[vertindex].numnormals++;
+ }
+ }
+
+//
+// calculate the vertex normals, match them to the template list, and store the
+// index of the best match
+//
+ for (i=0 ; i<model.numverts ; i++)
+ {
+ int j;
+ vec3_t v;
+ float maxdot;
+ int maxdotindex;
+
+ if (vnorms[i].numnormals > 0)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ int k;
+
+ v[j] = 0;
+
+ for (k=0 ; k<vnorms[i].numnormals ; k++)
+ {
+ v[j] += vnorms[i].normals[k][j];
+ }
+
+ v[j] /= vnorms[i].numnormals;
+ }
+ }
+ else
+ {
+ Error ("Vertex with no non-degenerate triangles attached");
+ }
+
+ VectorNormalize (v);
+
+ maxdot = -999999.0;
+ maxdotindex = -1;
+
+ for (j=0 ; j<NUMVERTEXNORMALS ; j++)
+ {
+ float dot;
+
+ dot = DotProduct (v, avertexnormals[j]);
+ if (dot > maxdot)
+ {
+ maxdot = dot;
+ maxdotindex = j;
+ }
+ }
+
+ ptrivert[i].lightnormalindex = maxdotindex;
+ }
+
+ framecount++;
+
+ if (framecount >= MAXFRAMES)
+ Error ("Too many frames; increase MAXFRAMES");
+
+ free (ptri);
+ firstframe = 0;
+}
+
+/*
+===============
+Cmd_Frame
+===============
+*/
+void Cmd_Frame (int isgroup)
+{
+ while (TokenAvailable())
+ {
+ GetToken (false);
+ GrabFrame (token, isgroup);
+
+ if (!isgroup)
+ model.numframes++;
+ }
+}
+
+/*
+===============
+Cmd_SkinGroupStart
+===============
+*/
+void Cmd_SkinGroupStart (void)
+{
+ int groupskin;
+
+ groupskin = skincount++;
+ if (skincount >= MAXFRAMES)
+ Error ("Too many skins; increase MAXSKINS");
+
+ skins[groupskin].type = ALIAS_SKIN_GROUP;
+ skins[groupskin].numgroupskins = 0;
+
+ while (1)
+ {
+ GetToken (true);
+ if (endofscript)
+ Error ("End of file during group");
+
+ if (!strcmp (token, "$skin"))
+ {
+ Cmd_Skin ();
+ skins[groupskin].numgroupskins++;
+ }
+ else if (!strcmp (token, "$skingroupend"))
+ {
+ break;
+ }
+ else
+ {
+ Error ("$skin or $skingroupend expected\n");
+ }
+
+ }
+
+ if (skins[groupskin].numgroupskins == 0)
+ Error ("Empty group\n");
+}
+
+
+/*
+===============
+Cmd_FrameGroupStart
+===============
+*/
+void Cmd_FrameGroupStart (void)
+{
+ int groupframe;
+
+ groupframe = framecount++;
+ if (framecount >= MAXFRAMES)
+ Error ("Too many frames; increase MAXFRAMES");
+
+ frames[groupframe].type = ALIAS_GROUP;
+ frames[groupframe].numgroupframes = 0;
+
+ while (1)
+ {
+ GetToken (true);
+ if (endofscript)
+ Error ("End of file during group");
+
+ if (!strcmp (token, "$frame"))
+ {
+ Cmd_Frame (1);
+ }
+ else if (!strcmp (token, "$framegroupend"))
+ {
+ break;
+ }
+ else
+ {
+ Error ("$frame or $framegroupend expected\n");
+ }
+
+ }
+
+ frames[groupframe].numgroupframes += framecount - groupframe - 1;
+
+ if (frames[groupframe].numgroupframes == 0)
+ Error ("Empty group\n");
+}
+
+
+/*
+=================
+Cmd_Origin
+=================
+*/
+void Cmd_Origin (void)
+{
+
+// rotate points into frame of reference so model points down the positive x
+// axis
+ GetToken (false);
+ adjust[1] = -atof (token);
+
+ GetToken (false);
+ adjust[0] = atof (token);
+
+ GetToken (false);
+ adjust[2] = -atof (token);
+}
+
+
+/*
+=================
+Cmd_Eyeposition
+=================
+*/
+void Cmd_Eyeposition (void)
+{
+
+// rotate points into frame of reference so model points down the positive x
+// axis
+ GetToken (false);
+ model.eyeposition[1] = atof (token);
+
+ GetToken (false);
+ model.eyeposition[0] = -atof (token);
+
+ GetToken (false);
+ model.eyeposition[2] = atof (token);
+}
+
+
+/*
+=================
+Cmd_ScaleUp
+=================
+*/
+void Cmd_ScaleUp (void)
+{
+
+ GetToken (false);
+ scale_up = atof (token);
+}
+
+/*
+=================
+Cmd_Flags
+=================
+*/
+void Cmd_Flags (void)
+{
+ GetToken (false);
+ model.flags = atoi (token);
+}
+
+
+/*
+=================
+Cmd_Modelname
+=================
+*/
+void Cmd_Modelname (void)
+{
+ WriteModel ();
+ GetToken (false);
+ strcpy (outname, token);
+}
+
+
+/*
+===============
+ParseScript
+===============
+*/
+void ParseScript (void)
+{
+ while (1)
+ {
+ do
+ { // look for a line starting with a $ command
+ GetToken (true);
+ if (endofscript)
+ return;
+ if (token[0] == '$')
+ break;
+ while (TokenAvailable())
+ GetToken (false);
+ } while (1);
+
+ if (!strcmp (token, "$modelname"))
+ {
+ Cmd_Modelname ();
+ }
+ else if (!strcmp (token, "$base"))
+ {
+ Cmd_Base ();
+ }
+ else if (!strcmp (token, "$cd"))
+ {
+ if (cdset)
+ Error ("Two $cd in one model");
+ cdset = true;
+ GetToken (false);
+ strcpy (cdpartial, token);
+ strcpy (cddir, ExpandPath(token));
+ }
+ else if (!strcmp (token, "$sync"))
+ {
+ model.synctype = ST_SYNC;
+ }
+ else if (!strcmp (token, "$origin"))
+ {
+ Cmd_Origin ();
+ }
+ else if (!strcmp (token, "$eyeposition"))
+ {
+ Cmd_Eyeposition ();
+ }
+ else if (!strcmp (token, "$scale"))
+ {
+ Cmd_ScaleUp ();
+ }
+ else if (!strcmp (token, "$flags"))
+ {
+ Cmd_Flags ();
+ }
+ else if (!strcmp (token, "$frame"))
+ {
+ Cmd_Frame (0);
+ }
+ else if (!strcmp (token, "$skin"))
+ {
+ Cmd_Skin ();
+ model.numskins++;
+ }
+ else if (!strcmp (token, "$framegroupstart"))
+ {
+ Cmd_FrameGroupStart ();
+ model.numframes++;
+ }
+ else if (!strcmp (token, "$skingroupstart"))
+ {
+ Cmd_SkinGroupStart ();
+ model.numskins++;
+ }
+ else
+ {
+ Error ("bad command %s\n", token);
+ }
+
+ }
+}
+
+/*
+==============
+main
+==============
+*/
+int main (int argc, char **argv)
+{
+ int i;
+ char path[1024];
+
+ if (argc != 2 && argc != 4)
+ Error ("usage: modelgen [-archive directory] file.qc");
+
+ if (!strcmp(argv[1], "-archive"))
+ {
+ archive = true;
+ strcpy (archivedir, argv[2]);
+ printf ("Archiving source to: %s\n", archivedir);
+ i = 3;
+ }
+ else
+ i = 1;
+
+//
+// load the script
+//
+ strcpy (path, argv[i]);
+ DefaultExtension (path, ".qc");
+ SetQdirFromPath (path);
+ LoadScriptFile (path);
+
+//
+// parse it
+//
+ memset (&model, 0, sizeof(model));
+
+ for (i=0 ; i<3 ; i++)
+ {
+ framesmins[i] = 9999999;
+ framesmaxs[i] = -9999999;
+ }
+
+
+ ClearModel ();
+ strcpy (outname, argv[1]);
+
+ ParseScript ();
+ WriteModel ();
+
+ return 0;
+}
+
diff --git a/qutils/MODELGEN/MODELGEN.H b/qutils/MODELGEN/MODELGEN.H
new file mode 100644
index 0000000..386245e
--- /dev/null
+++ b/qutils/MODELGEN/MODELGEN.H
@@ -0,0 +1,113 @@
+//
+// modelgen.h: header file for model generation program
+//
+
+// *********************************************************
+// * This file must be identical in the modelgen directory *
+// * and in the Quake directory, because it's used to *
+// * pass data from one to the other via model files. *
+// *********************************************************
+
+#ifdef INCLUDELIBS
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#include "cmdlib.h"
+#include "scriplib.h"
+#include "mathlib.h"
+#include "trilib.h"
+#include "lbmlib.h"
+
+#endif
+
+#define ALIAS_VERSION 6
+
+#define ALIAS_ONSEAM 0x0020
+
+// must match definition in spritegn.h
+#ifndef SYNCTYPE_T
+#define SYNCTYPE_T
+typedef enum {ST_SYNC=0, ST_RAND } synctype_t;
+#endif
+
+typedef enum { ALIAS_SINGLE=0, ALIAS_GROUP } aliasframetype_t;
+
+typedef enum { ALIAS_SKIN_SINGLE=0, ALIAS_SKIN_GROUP } aliasskintype_t;
+
+typedef struct {
+ int ident;
+ int version;
+ vec3_t scale;
+ vec3_t scale_origin;
+ float boundingradius;
+ vec3_t eyeposition;
+ int numskins;
+ int skinwidth;
+ int skinheight;
+ int numverts;
+ int numtris;
+ int numframes;
+ synctype_t synctype;
+ int flags;
+ float size;
+} mdl_t;
+
+// TODO: could be shorts
+
+typedef struct {
+ int onseam;
+ int s;
+ int t;
+} stvert_t;
+
+typedef struct dtriangle_s {
+ int facesfront;
+ int vertindex[3];
+} dtriangle_t;
+
+#define DT_FACES_FRONT 0x0010
+
+
+typedef struct {
+ byte v[3];
+ byte lightnormalindex;
+} trivertx_t;
+
+typedef struct {
+ trivertx_t bboxmin; // lightnormal isn't used
+ trivertx_t bboxmax; // lightnormal isn't used
+ char name[16]; // frame name from grabbing
+} daliasframe_t;
+
+typedef struct {
+ int numframes;
+ trivertx_t bboxmin; // lightnormal isn't used
+ trivertx_t bboxmax; // lightnormal isn't used
+} daliasgroup_t;
+
+typedef struct {
+ int numskins;
+} daliasskingroup_t;
+
+typedef struct {
+ float interval;
+} daliasinterval_t;
+
+typedef struct {
+ float interval;
+} daliasskininterval_t;
+
+typedef struct {
+ aliasframetype_t type;
+} daliasframetype_t;
+
+typedef struct {
+ aliasskintype_t type;
+} daliasskintype_t;
+
+#define IDPOLYHEADER (('O'<<24)+('P'<<16)+('D'<<8)+'I')
+ // little-endian "IDPO"
+
diff --git a/qutils/MODELGEN/MODELGEN.MAK b/qutils/MODELGEN/MODELGEN.MAK
new file mode 100644
index 0000000..9b79469
--- /dev/null
+++ b/qutils/MODELGEN/MODELGEN.MAK
@@ -0,0 +1,294 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+!IF "$(CFG)" == ""
+CFG=modelgen - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to modelgen - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "modelgen - Win32 Release" && "$(CFG)" !=\
+ "modelgen - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "modelgen.mak" CFG="modelgen - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "modelgen - Win32 Release" (based on\
+ "Win32 (x86) Console Application")
+!MESSAGE "modelgen - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "modelgen - Win32 Debug"
+RSC=rc.exe
+CPP=cl.exe
+
+!IF "$(CFG)" == "modelgen - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\modelgen.exe"
+
+CLEAN :
+ -@erase ".\Release\modelgen.exe"
+ -@erase ".\Release\mathlib.obj"
+ -@erase ".\Release\cmdlib.obj"
+ -@erase ".\Release\lbmlib.obj"
+ -@erase ".\Release\trilib.obj"
+ -@erase ".\Release\scriplib.obj"
+ -@erase ".\Release\modelgen.obj"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /ML /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/modelgen.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\Release/
+CPP_SBRS=
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/modelgen.bsc"
+BSC32_SBRS=
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:console /incremental:no\
+ /pdb:"$(OUTDIR)/modelgen.pdb" /machine:I386 /out:"$(OUTDIR)/modelgen.exe"
+LINK32_OBJS= \
+ "$(INTDIR)/mathlib.obj" \
+ "$(INTDIR)/cmdlib.obj" \
+ "$(INTDIR)/lbmlib.obj" \
+ "$(INTDIR)/trilib.obj" \
+ "$(INTDIR)/scriplib.obj" \
+ "$(INTDIR)/modelgen.obj"
+
+"$(OUTDIR)\modelgen.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "modelgen - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\modelgen.exe"
+
+CLEAN :
+ -@erase ".\Debug\vc40.pdb"
+ -@erase ".\Debug\vc40.idb"
+ -@erase ".\Debug\modelgen.exe"
+ -@erase ".\Debug\trilib.obj"
+ -@erase ".\Debug\mathlib.obj"
+ -@erase ".\Debug\lbmlib.obj"
+ -@erase ".\Debug\modelgen.obj"
+ -@erase ".\Debug\scriplib.obj"
+ -@erase ".\Debug\cmdlib.obj"
+ -@erase ".\Debug\modelgen.ilk"
+ -@erase ".\Debug\modelgen.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MLd /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/modelgen.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\Debug/
+CPP_SBRS=
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/modelgen.bsc"
+BSC32_SBRS=
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:console /incremental:yes\
+ /pdb:"$(OUTDIR)/modelgen.pdb" /debug /machine:I386\
+ /out:"$(OUTDIR)/modelgen.exe"
+LINK32_OBJS= \
+ "$(INTDIR)/trilib.obj" \
+ "$(INTDIR)/mathlib.obj" \
+ "$(INTDIR)/lbmlib.obj" \
+ "$(INTDIR)/modelgen.obj" \
+ "$(INTDIR)/scriplib.obj" \
+ "$(INTDIR)/cmdlib.obj"
+
+"$(OUTDIR)\modelgen.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+################################################################################
+# Begin Target
+
+# Name "modelgen - Win32 Release"
+# Name "modelgen - Win32 Debug"
+
+!IF "$(CFG)" == "modelgen - Win32 Release"
+
+!ELSEIF "$(CFG)" == "modelgen - Win32 Debug"
+
+!ENDIF
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\modelgen.c
+DEP_CPP_MODEL=\
+ {$(INCLUDE)}"\sys\STAT.H"\
+ ".\modelgen.h"\
+ ".\anorms.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\scriplib.h"\
+ ".\..\common\mathlib.h"\
+ ".\..\common\trilib.h"\
+ ".\..\common\lbmlib.h"\
+
+
+"$(INTDIR)\modelgen.obj" : $(SOURCE) $(DEP_CPP_MODEL) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\cmdlib.c
+DEP_CPP_CMDLI=\
+ ".\..\common\cmdlib.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+ {$(INCLUDE)}"\sys\STAT.H"\
+
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\trilib.c
+DEP_CPP_TRILI=\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+ ".\..\common\trilib.h"\
+
+
+"$(INTDIR)\trilib.obj" : $(SOURCE) $(DEP_CPP_TRILI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\scriplib.c
+DEP_CPP_SCRIP=\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\scriplib.h"\
+
+
+"$(INTDIR)\scriplib.obj" : $(SOURCE) $(DEP_CPP_SCRIP) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\mathlib.c
+DEP_CPP_MATHL=\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\lbmlib.c
+DEP_CPP_LBMLI=\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\lbmlib.h"\
+
+
+"$(INTDIR)\lbmlib.obj" : $(SOURCE) $(DEP_CPP_LBMLI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+# End Target
+# End Project
+################################################################################
diff --git a/qutils/MODELGEN/MODELGEN.MDP b/qutils/MODELGEN/MODELGEN.MDP
new file mode 100644
index 0000000..00dfd25
--- /dev/null
+++ b/qutils/MODELGEN/MODELGEN.MDP
Binary files differ
diff --git a/qutils/MODELGEN/MODELGEN.NCB b/qutils/MODELGEN/MODELGEN.NCB
new file mode 100644
index 0000000..aa8fab1
--- /dev/null
+++ b/qutils/MODELGEN/MODELGEN.NCB
Binary files differ
diff --git a/qutils/QBSP/BRUSH.C b/qutils/QBSP/BRUSH.C
new file mode 100644
index 0000000..1a3b3ab
--- /dev/null
+++ b/qutils/QBSP/BRUSH.C
@@ -0,0 +1,870 @@
+// brush.c
+
+#include "bsp5.h"
+
+int numbrushplanes;
+plane_t planes[MAX_MAP_PLANES];
+
+int numbrushfaces;
+mface_t faces[128]; // beveled clipping hull can generate many extra
+
+
+/*
+=================
+CheckFace
+
+Note: this will not catch 0 area polygons
+=================
+*/
+void CheckFace (face_t *f)
+{
+ int i, j;
+ vec_t *p1, *p2;
+ vec_t d, edgedist;
+ vec3_t dir, edgenormal, facenormal;
+
+ if (f->numpoints < 3)
+ Error ("CheckFace: %i points",f->numpoints);
+
+ VectorCopy (planes[f->planenum].normal, facenormal);
+ if (f->planeside)
+ {
+ VectorSubtract (vec3_origin, facenormal, facenormal);
+ }
+
+ for (i=0 ; i<f->numpoints ; i++)
+ {
+ p1 = f->pts[i];
+
+ for (j=0 ; j<3 ; j++)
+ if (p1[j] > BOGUS_RANGE || p1[j] < -BOGUS_RANGE)
+ Error ("CheckFace: BUGUS_RANGE: %f",p1[j]);
+
+ j = i+1 == f->numpoints ? 0 : i+1;
+
+ // check the point is on the face plane
+ d = DotProduct (p1, planes[f->planenum].normal) - planes[f->planenum].dist;
+ if (d < -ON_EPSILON || d > ON_EPSILON)
+ Error ("CheckFace: point off plane");
+
+ // check the edge isn't degenerate
+ p2 = f->pts[j];
+ VectorSubtract (p2, p1, dir);
+
+ if (VectorLength (dir) < ON_EPSILON)
+ Error ("CheckFace: degenerate edge");
+
+ CrossProduct (facenormal, dir, edgenormal);
+ VectorNormalize (edgenormal);
+ edgedist = DotProduct (p1, edgenormal);
+ edgedist += ON_EPSILON;
+
+ // all other points must be on front side
+ for (j=0 ; j<f->numpoints ; j++)
+ {
+ if (j == i)
+ continue;
+ d = DotProduct (f->pts[j], edgenormal);
+ if (d > edgedist)
+ Error ("CheckFace: non-convex");
+ }
+ }
+}
+
+
+//===========================================================================
+
+/*
+=================
+ClearBounds
+=================
+*/
+void ClearBounds (brushset_t *bs)
+{
+ int i, j;
+
+ for (j=0 ; j<NUM_HULLS ; j++)
+ for (i=0 ; i<3 ; i++)
+ {
+ bs->mins[i] = 99999;
+ bs->maxs[i] = -99999;
+ }
+}
+
+/*
+=================
+AddToBounds
+=================
+*/
+void AddToBounds (brushset_t *bs, vec3_t v)
+{
+ int i;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if (v[i] < bs->mins[i])
+ bs->mins[i] = v[i];
+ if (v[i] > bs->maxs[i])
+ bs->maxs[i] = v[i];
+ }
+}
+
+//===========================================================================
+
+int PlaneTypeForNormal (vec3_t normal)
+{
+ float ax, ay, az;
+
+// NOTE: should these have an epsilon around 1.0?
+ if (normal[0] == 1.0)
+ return PLANE_X;
+ if (normal[1] == 1.0)
+ return PLANE_Y;
+ if (normal[2] == 1.0)
+ return PLANE_Z;
+ if (normal[0] == -1.0 ||
+ normal[1] == -1.0 ||
+ normal[2] == -1.0)
+ Error ("PlaneTypeForNormal: not a canonical vector");
+
+ ax = fabs(normal[0]);
+ ay = fabs(normal[1]);
+ az = fabs(normal[2]);
+
+ if (ax >= ay && ax >= az)
+ return PLANE_ANYX;
+ if (ay >= ax && ay >= az)
+ return PLANE_ANYY;
+ return PLANE_ANYZ;
+}
+
+#define DISTEPSILON 0.01
+#define ANGLEEPSILON 0.00001
+
+void NormalizePlane (plane_t *dp)
+{
+ vec_t ax, ay, az;
+
+ if (dp->normal[0] == -1.0)
+ {
+ dp->normal[0] = 1.0;
+ dp->dist = -dp->dist;
+ }
+ if (dp->normal[1] == -1.0)
+ {
+ dp->normal[1] = 1.0;
+ dp->dist = -dp->dist;
+ }
+ if (dp->normal[2] == -1.0)
+ {
+ dp->normal[2] = 1.0;
+ dp->dist = -dp->dist;
+ }
+
+ if (dp->normal[0] == 1.0)
+ {
+ dp->type = PLANE_X;
+ return;
+ }
+ if (dp->normal[1] == 1.0)
+ {
+ dp->type = PLANE_Y;
+ return;
+ }
+ if (dp->normal[2] == 1.0)
+ {
+ dp->type = PLANE_Z;
+ return;
+ }
+
+ ax = fabs(dp->normal[0]);
+ ay = fabs(dp->normal[1]);
+ az = fabs(dp->normal[2]);
+
+ if (ax >= ay && ax >= az)
+ dp->type = PLANE_ANYX;
+ else if (ay >= ax && ay >= az)
+ dp->type = PLANE_ANYY;
+ else
+ dp->type = PLANE_ANYZ;
+ if (dp->normal[dp->type-PLANE_ANYX] < 0)
+ {
+ VectorSubtract (vec3_origin, dp->normal, dp->normal);
+ dp->dist = -dp->dist;
+ }
+
+}
+
+/*
+===============
+FindPlane
+
+Returns a global plane number and the side that will be the front
+===============
+*/
+int FindPlane (plane_t *dplane, int *side)
+{
+ int i;
+ plane_t *dp, pl;
+ vec_t dot;
+
+ dot = VectorLength(dplane->normal);
+ if (dot < 1.0 - ANGLEEPSILON || dot > 1.0 + ANGLEEPSILON)
+ Error ("FindPlane: normalization error");
+
+ pl = *dplane;
+ NormalizePlane (&pl);
+ if (DotProduct(pl.normal, dplane->normal) > 0)
+ *side = 0;
+ else
+ *side = 1;
+
+ dp = planes;
+ for (i=0 ; i<numbrushplanes;i++, dp++)
+ {
+ dot = DotProduct (dp->normal, pl.normal);
+ if (dot > 1.0 - ANGLEEPSILON
+ && fabs(dp->dist - pl.dist) < DISTEPSILON )
+ { // regular match
+ return i;
+ }
+ }
+
+ if (numbrushplanes == MAX_MAP_PLANES)
+ Error ("numbrushplanes == MAX_MAP_PLANES");
+
+ planes[numbrushplanes] = pl;
+
+ numbrushplanes++;
+
+ return numbrushplanes-1;
+}
+
+
+/*
+===============
+FindPlane_old
+
+Returns a global plane number and the side that will be the front
+===============
+*/
+int FindPlane_old (plane_t *dplane, int *side)
+{
+ int i;
+ plane_t *dp;
+ vec_t dot, ax, ay, az;
+
+ dot = VectorLength(dplane->normal);
+ if (dot < 1.0 - ANGLEEPSILON || dot > 1.0 + ANGLEEPSILON)
+ Error ("FindPlane: normalization error");
+
+ dp = planes;
+
+ for (i=0 ; i<numbrushplanes;i++, dp++)
+ {
+ dot = DotProduct (dplane->normal, dp->normal);
+ if (dot > 1.0 - ANGLEEPSILON
+ && fabs(dplane->dist - dp->dist) < DISTEPSILON )
+ { // regular match
+ *side = 0;
+ return i;
+ }
+ if (dot < -1.0+ANGLEEPSILON
+ && fabs(dplane->dist + dp->dist) < DISTEPSILON )
+ { // inverse of vector
+ *side = 1;
+ return i;
+ }
+ }
+
+// allocate a new plane, flipping normal to a consistant direction
+// if needed
+ *dp = *dplane;
+
+ if (numbrushplanes == MAX_MAP_PLANES)
+ Error ("numbrushplanes == MAX_MAP_PLANES");
+ numbrushplanes++;
+
+ *side = 0;
+
+// NOTE: should these have an epsilon around 1.0?
+ if (dplane->normal[0] == 1.0)
+ dp->type = PLANE_X;
+ else if (dplane->normal[1] == 1.0)
+ dp->type = PLANE_Y;
+ else if (dplane->normal[2] == 1.0)
+ dp->type = PLANE_Z;
+ else if (dplane->normal[0] == -1.0)
+ {
+ dp->type = PLANE_X;
+ dp->normal[0] = 1.0;
+ dp->dist = -dp->dist;
+ *side = 1;
+ }
+ else if (dplane->normal[1] == -1.0)
+ {
+ dp->type = PLANE_Y;
+ dp->normal[1] = 1.0;
+ dp->dist = -dp->dist;
+ *side = 1;
+ }
+ else if (dplane->normal[2] == -1.0)
+ {
+ dp->type = PLANE_Z;
+ dp->normal[2] = 1.0;
+ dp->dist = -dp->dist;
+ *side = 1;
+ }
+ else
+ {
+ ax = fabs(dplane->normal[0]);
+ ay = fabs(dplane->normal[1]);
+ az = fabs(dplane->normal[2]);
+
+ if (ax >= ay && ax >= az)
+ dp->type = PLANE_ANYX;
+ else if (ay >= ax && ay >= az)
+ dp->type = PLANE_ANYY;
+ else
+ dp->type = PLANE_ANYZ;
+ if (dplane->normal[dp->type-PLANE_ANYX] < 0)
+ {
+ VectorSubtract (vec3_origin, dp->normal, dp->normal);
+ dp->dist = -dp->dist;
+ *side = 1;
+ }
+ }
+
+ return i;
+}
+
+
+
+/*
+=============================================================================
+
+ TURN BRUSHES INTO GROUPS OF FACES
+
+=============================================================================
+*/
+
+vec3_t brush_mins, brush_maxs;
+face_t *brush_faces;
+
+/*
+=================
+CreateBrushFaces
+=================
+*/
+#define ZERO_EPSILON 0.001
+void CreateBrushFaces (void)
+{
+ int i,j, k;
+ vec_t r;
+ face_t *f;
+ winding_t *w;
+ plane_t plane;
+ mface_t *mf;
+
+ brush_mins[0] = brush_mins[1] = brush_mins[2] = 99999;
+ brush_maxs[0] = brush_maxs[1] = brush_maxs[2] = -99999;
+
+ brush_faces = NULL;
+
+ for (i=0 ; i<numbrushfaces ; i++)
+ {
+ mf = &faces[i];
+
+ w = BaseWindingForPlane (&mf->plane);
+
+ for (j=0 ; j<numbrushfaces && w ; j++)
+ {
+ if (j == i)
+ continue;
+ // flip the plane, because we want to keep the back side
+ VectorSubtract (vec3_origin,faces[j].plane.normal, plane.normal);
+ plane.dist = -faces[j].plane.dist;
+
+ w = ClipWinding (w, &plane, false);
+ }
+
+ if (!w)
+ continue; // overcontrained plane
+
+ // this face is a keeper
+ f = AllocFace ();
+ f->numpoints = w->numpoints;
+ if (f->numpoints > MAXEDGES)
+ Error ("f->numpoints > MAXEDGES");
+
+ for (j=0 ; j<w->numpoints ; j++)
+ {
+ for (k=0 ; k<3 ; k++)
+ {
+ r = Q_rint (w->points[j][k]);
+ if ( fabs(w->points[j][k] - r) < ZERO_EPSILON)
+ f->pts[j][k] = r;
+ else
+ f->pts[j][k] = w->points[j][k];
+
+ if (f->pts[j][k] < brush_mins[k])
+ brush_mins[k] = f->pts[j][k];
+ if (f->pts[j][k] > brush_maxs[k])
+ brush_maxs[k] = f->pts[j][k];
+ }
+
+ }
+ FreeWinding (w);
+ f->texturenum = mf->texinfo;
+ f->planenum = FindPlane (&mf->plane, &f->planeside);
+ f->next = brush_faces;
+ brush_faces = f;
+ CheckFace (f);
+ }
+}
+
+
+
+/*
+==============================================================================
+
+BEVELED CLIPPING HULL GENERATION
+
+This is done by brute force, and could easily get a lot faster if anyone cares.
+==============================================================================
+*/
+
+vec3_t hull_size[3][2] = {
+{ {0, 0, 0}, {0, 0, 0} },
+{ {-16,-16,-32}, {16,16,24} },
+{ {-32,-32,-64}, {32,32,24} }
+
+};
+
+#define MAX_HULL_POINTS 32
+#define MAX_HULL_EDGES 64
+
+int num_hull_points;
+vec3_t hull_points[MAX_HULL_POINTS];
+vec3_t hull_corners[MAX_HULL_POINTS*8];
+int num_hull_edges;
+int hull_edges[MAX_HULL_EDGES][2];
+
+/*
+============
+AddBrushPlane
+=============
+*/
+void AddBrushPlane (plane_t *plane)
+{
+ int i;
+ plane_t *pl;
+ float l;
+
+ if (numbrushfaces == MAX_FACES)
+ Error ("AddBrushPlane: numbrushfaces == MAX_FACES");
+ l = VectorLength (plane->normal);
+ if (l < 0.999 || l > 1.001)
+ Error ("AddBrushPlane: bad normal");
+
+ for (i=0 ; i<numbrushfaces ; i++)
+ {
+ pl = &faces[i].plane;
+ if (VectorCompare (pl->normal, plane->normal)
+ && fabs(pl->dist - plane->dist) < ON_EPSILON )
+ return;
+ }
+ faces[i].plane = *plane;
+ faces[i].texinfo = faces[0].texinfo;
+ numbrushfaces++;
+}
+
+
+/*
+============
+TestAddPlane
+
+Adds the given plane to the brush description if all of the original brush
+vertexes can be put on the front side
+=============
+*/
+void TestAddPlane (plane_t *plane)
+{
+ int i, c;
+ vec_t d;
+ vec_t *corner;
+ plane_t flip;
+ vec3_t inv;
+ int counts[3];
+ plane_t *pl;
+
+// see if the plane has allready been added
+ for (i=0 ; i<numbrushfaces ; i++)
+ {
+ pl = &faces[i].plane;
+ if (VectorCompare (plane->normal, pl->normal) && fabs(plane->dist - pl->dist) < ON_EPSILON)
+ return;
+ VectorSubtract (vec3_origin, plane->normal, inv);
+ if (VectorCompare (inv, pl->normal) && fabs(plane->dist + pl->dist) < ON_EPSILON)
+ return;
+ }
+
+// check all the corner points
+ counts[0] = counts[1] = counts[2] = 0;
+ c = num_hull_points * 8;
+
+ corner = hull_corners[0];
+ for (i=0 ; i<c ; i++, corner += 3)
+ {
+ d = DotProduct (corner, plane->normal) - plane->dist;
+ if (d < -ON_EPSILON)
+ {
+ if (counts[0])
+ return;
+ counts[1]++;
+ }
+ else if (d > ON_EPSILON)
+ {
+ if (counts[1])
+ return;
+ counts[0]++;
+ }
+ else
+ counts[2]++;
+ }
+
+// the plane is a seperator
+
+ if (counts[0])
+ {
+ VectorSubtract (vec3_origin, plane->normal, flip.normal);
+ flip.dist = -plane->dist;
+ plane = &flip;
+ }
+
+ AddBrushPlane (plane);
+}
+
+/*
+============
+AddHullPoint
+
+Doesn't add if duplicated
+=============
+*/
+int AddHullPoint (vec3_t p, int hullnum)
+{
+ int i;
+ vec_t *c;
+ int x,y,z;
+
+ for (i=0 ; i<num_hull_points ; i++)
+ if (VectorCompare (p, hull_points[i]))
+ return i;
+
+ VectorCopy (p, hull_points[num_hull_points]);
+
+ c = hull_corners[i*8];
+
+ for (x=0 ; x<2 ; x++)
+ for (y=0 ; y<2 ; y++)
+ for (z=0; z<2 ; z++)
+ {
+ c[0] = p[0] + hull_size[hullnum][x][0];
+ c[1] = p[1] + hull_size[hullnum][y][1];
+ c[2] = p[2] + hull_size[hullnum][z][2];
+ c += 3;
+ }
+
+ if (num_hull_points == MAX_HULL_POINTS)
+ Error ("MAX_HULL_POINTS");
+
+ num_hull_points++;
+
+ return i;
+}
+
+
+/*
+============
+AddHullEdge
+
+Creates all of the hull planes around the given edge, if not done allready
+=============
+*/
+void AddHullEdge (vec3_t p1, vec3_t p2, int hullnum)
+{
+ int pt1, pt2;
+ int i;
+ int a, b, c, d, e;
+ vec3_t edgevec, planeorg, planevec;
+ plane_t plane;
+ vec_t l;
+
+ pt1 = AddHullPoint (p1, hullnum);
+ pt2 = AddHullPoint (p2, hullnum);
+
+ for (i=0 ; i<num_hull_edges ; i++)
+ if ( (hull_edges[i][0] == pt1 && hull_edges[i][1] == pt2)
+ || (hull_edges[i][0] == pt2 && hull_edges[i][1] == pt1) )
+ return; // allread added
+
+ if (num_hull_edges == MAX_HULL_EDGES)
+ Error ("MAX_HULL_EDGES");
+
+ hull_edges[i][0] = pt1;
+ hull_edges[i][1] = pt2;
+ num_hull_edges++;
+
+ VectorSubtract (p1, p2, edgevec);
+ VectorNormalize (edgevec);
+
+ for (a=0 ; a<3 ; a++)
+ {
+ b = (a+1)%3;
+ c = (a+2)%3;
+ for (d=0 ; d<=1 ; d++)
+ for (e=0 ; e<=1 ; e++)
+ {
+ VectorCopy (p1, planeorg);
+ planeorg[b] += hull_size[hullnum][d][b];
+ planeorg[c] += hull_size[hullnum][e][c];
+
+ VectorCopy (vec3_origin, planevec);
+ planevec[a] = 1;
+
+ CrossProduct (planevec, edgevec, plane.normal);
+ l = VectorLength (plane.normal);
+ if (l < 1-ANGLEEPSILON || l > 1+ANGLEEPSILON)
+ continue;
+ plane.dist = DotProduct (planeorg, plane.normal);
+ TestAddPlane (&plane);
+ }
+ }
+
+
+}
+
+/*
+============
+ExpandBrush
+=============
+*/
+void ExpandBrush (int hullnum)
+{
+ int i, x, s;
+ vec3_t corner;
+ face_t *f;
+ plane_t plane, *p;
+
+ num_hull_points = 0;
+ num_hull_edges = 0;
+
+// create all the hull points
+ for (f=brush_faces ; f ; f=f->next)
+ for (i=0 ; i<f->numpoints ; i++)
+ AddHullPoint (f->pts[i], hullnum);
+
+// expand all of the planes
+ for (i=0 ; i<numbrushfaces ; i++)
+ {
+ p = &faces[i].plane;
+ VectorCopy (vec3_origin, corner);
+ for (x=0 ; x<3 ; x++)
+ {
+ if (p->normal[x] > 0)
+ corner[x] = hull_size[hullnum][1][x];
+ else if (p->normal[x] < 0)
+ corner[x] = hull_size[hullnum][0][x];
+ }
+ p->dist += DotProduct (corner, p->normal);
+ }
+
+// add any axis planes not contained in the brush to bevel off corners
+ for (x=0 ; x<3 ; x++)
+ for (s=-1 ; s<=1 ; s+=2)
+ {
+ // add the plane
+ VectorCopy (vec3_origin, plane.normal);
+ plane.normal[x] = s;
+ if (s == -1)
+ plane.dist = -brush_mins[x] + -hull_size[hullnum][0][x];
+ else
+ plane.dist = brush_maxs[x] + hull_size[hullnum][1][x];
+ AddBrushPlane (&plane);
+ }
+
+// add all of the edge bevels
+ for (f=brush_faces ; f ; f=f->next)
+ for (i=0 ; i<f->numpoints ; i++)
+ AddHullEdge (f->pts[i], f->pts[(i+1)%f->numpoints], hullnum);
+}
+
+//============================================================================
+
+
+/*
+===============
+LoadBrush
+
+Converts a mapbrush to a bsp brush
+===============
+*/
+brush_t *LoadBrush (mbrush_t *mb, int hullnum)
+{
+ brush_t *b;
+ int contents;
+ char *name;
+ mface_t *f;
+
+//
+// check texture name for attributes
+//
+ name = miptex[texinfo[mb->faces->texinfo].miptex];
+
+ if (!Q_strcasecmp(name, "clip") && hullnum == 0)
+ return NULL; // "clip" brushes don't show up in the draw hull
+
+ if (name[0] == '*' && worldmodel) // entities never use water merging
+ {
+ if (!Q_strncasecmp(name+1,"lava",4))
+ contents = CONTENTS_LAVA;
+ else if (!Q_strncasecmp(name+1,"slime",5))
+ contents = CONTENTS_SLIME;
+ else
+ contents = CONTENTS_WATER;
+ }
+ else if (!Q_strncasecmp (name, "sky",3) && worldmodel && hullnum == 0)
+ contents = CONTENTS_SKY;
+ else
+ contents = CONTENTS_SOLID;
+
+ if (hullnum && contents != CONTENTS_SOLID && contents != CONTENTS_SKY)
+ return NULL; // water brushes don't show up in clipping hulls
+
+// no seperate textures on clip hull
+
+//
+// create the faces
+//
+ brush_faces = NULL;
+
+ numbrushfaces = 0;
+ for (f=mb->faces ; f ; f=f->next)
+ {
+ faces[numbrushfaces] = *f;
+ if (hullnum)
+ faces[numbrushfaces].texinfo = 0;
+ numbrushfaces++;
+ }
+
+ CreateBrushFaces ();
+
+ if (!brush_faces)
+ {
+ printf ("WARNING: couldn't create brush faces\n");
+ return NULL;
+ }
+
+ if (hullnum)
+ {
+ ExpandBrush (hullnum);
+ CreateBrushFaces ();
+ }
+
+//
+// create the brush
+//
+ b = AllocBrush ();
+
+ b->contents = contents;
+ b->faces = brush_faces;
+ VectorCopy (brush_mins, b->mins);
+ VectorCopy (brush_maxs, b->maxs);
+
+ return b;
+}
+
+//=============================================================================
+
+
+/*
+============
+Brush_DrawAll
+
+============
+*/
+void Brush_DrawAll (brushset_t *bs)
+{
+ brush_t *b;
+ face_t *f;
+
+ for (b=bs->brushes ; b ; b=b->next)
+ for (f=b->faces ; f ; f=f->next)
+ Draw_DrawFace (f);
+}
+
+
+/*
+============
+Brush_LoadEntity
+============
+*/
+brushset_t *Brush_LoadEntity (entity_t *ent, int hullnum)
+{
+ brush_t *b, *next, *water, *other;
+ mbrush_t *mbr;
+ int numbrushes;
+ brushset_t *bset;
+
+ bset = malloc (sizeof(brushset_t));
+ memset (bset, 0, sizeof(brushset_t));
+ ClearBounds (bset);
+
+ numbrushes = 0;
+ other = water = NULL;
+
+ qprintf ("--- Brush_LoadEntity ---\n");
+
+ for (mbr = ent->brushes ; mbr ; mbr=mbr->next)
+ {
+ b = LoadBrush (mbr, hullnum);
+ if (!b)
+ continue;
+
+ numbrushes++;
+
+ if (b->contents != CONTENTS_SOLID)
+ {
+ b->next = water;
+ water = b;
+ }
+ else
+ {
+ b->next = other;
+ other = b;
+ }
+
+ AddToBounds (bset, b->mins);
+ AddToBounds (bset, b->maxs);
+ }
+
+// add all of the water textures at the start
+ for (b=water ; b ; b=next)
+ {
+ next = b->next;
+ b->next = other;
+ other = b;
+ }
+
+ bset->brushes = other;
+
+ brushset = bset;
+ Brush_DrawAll (bset);
+
+ qprintf ("%i brushes read\n",numbrushes);
+
+ return bset;
+}
+
+
diff --git a/qutils/QBSP/BSP5.H b/qutils/QBSP/BSP5.H
new file mode 100644
index 0000000..81093c1
--- /dev/null
+++ b/qutils/QBSP/BSP5.H
@@ -0,0 +1,297 @@
+
+// bsp5.h
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "bspfile.h"
+
+typedef struct
+{
+ vec3_t normal;
+ vec_t dist;
+ int type;
+} plane_t;
+
+
+#include "map.h"
+
+#define MAX_THREADS 4
+
+#define ON_EPSILON 0.05
+#define BOGUS_RANGE 18000
+
+// the exact bounding box of the brushes is expanded some for the headnode
+// volume. is this still needed?
+#define SIDESPACE 24
+
+//============================================================================
+
+
+typedef struct
+{
+ int numpoints;
+ vec3_t points[8]; // variable sized
+} winding_t;
+
+#define MAX_POINTS_ON_WINDING 64
+
+winding_t *BaseWindingForPlane (plane_t *p);
+void CheckWinding (winding_t *w);
+winding_t *NewWinding (int points);
+void FreeWinding (winding_t *w);
+winding_t *CopyWinding (winding_t *w);
+winding_t *ClipWinding (winding_t *in, plane_t *split, qboolean keepon);
+void DivideWinding (winding_t *in, plane_t *split, winding_t **front, winding_t **back);
+
+//============================================================================
+
+#define MAXEDGES 32
+#define MAXPOINTS 28 // don't let a base face get past this
+ // because it can be split more later
+
+typedef struct visfacet_s
+{
+ struct visfacet_s *next;
+
+ int planenum;
+ int planeside; // which side is the front of the face
+ int texturenum;
+ int contents[2]; // 0 = front side
+
+ struct visfacet_s *original; // face on node
+ int outputnumber; // only valid for original faces after
+ // write surfaces
+ int numpoints;
+ vec3_t pts[MAXEDGES]; // FIXME: change to use winding_t
+ int edges[MAXEDGES];
+} face_t;
+
+
+typedef struct surface_s
+{
+ struct surface_s *next;
+ struct surface_s *original; // before BSP cuts it up
+ int planenum;
+ int outputplanenum; // only valid after WriteSurfacePlanes
+ vec3_t mins, maxs;
+ qboolean onnode; // true if surface has already been used
+ // as a splitting node
+ face_t *faces; // links to all the faces on either side of the surf
+} surface_t;
+
+
+//
+// there is a node_t structure for every node and leaf in the bsp tree
+//
+#define PLANENUM_LEAF -1
+
+typedef struct node_s
+{
+ vec3_t mins,maxs; // bounding volume, not just points inside
+
+// information for decision nodes
+ int planenum; // -1 = leaf node
+ int outputplanenum; // only valid after WriteNodePlanes
+ int firstface; // decision node only
+ int numfaces; // decision node only
+ struct node_s *children[2]; // only valid for decision nodes
+ face_t *faces; // decision nodes only, list for both sides
+
+// information for leafs
+ int contents; // leaf nodes (0 for decision nodes)
+ face_t **markfaces; // leaf nodes only, point to node faces
+ struct portal_s *portals;
+ int visleafnum; // -1 = solid
+ int valid; // for flood filling
+ int occupied; // light number in leaf for outside filling
+} node_t;
+
+//=============================================================================
+
+// brush.c
+
+#define NUM_HULLS 2 // normal and +16
+
+#define NUM_CONTENTS 2 // solid and water
+
+typedef struct brush_s
+{
+ struct brush_s *next;
+ vec3_t mins, maxs;
+ face_t *faces;
+ int contents;
+} brush_t;
+
+typedef struct
+{
+ vec3_t mins, maxs;
+ brush_t *brushes; // NULL terminated list
+} brushset_t;
+
+extern int numbrushplanes;
+extern plane_t planes[MAX_MAP_PLANES];
+
+brushset_t *Brush_LoadEntity (entity_t *ent, int hullnum);
+int PlaneTypeForNormal (vec3_t normal);
+int FindPlane (plane_t *dplane, int *side);
+
+//=============================================================================
+
+// csg4.c
+
+// build surfaces is also used by GatherNodeFaces
+extern face_t *validfaces[MAX_MAP_PLANES];
+surface_t *BuildSurfaces (void);
+
+face_t *NewFaceFromFace (face_t *in);
+surface_t *CSGFaces (brushset_t *bs);
+void SplitFace (face_t *in, plane_t *split, face_t **front, face_t **back);
+
+//=============================================================================
+
+// solidbsp.c
+
+void DivideFacet (face_t *in, plane_t *split, face_t **front, face_t **back);
+void CalcSurfaceInfo (surface_t *surf);
+void SubdivideFace (face_t *f, face_t **prevptr);
+node_t *SolidBSP (surface_t *surfhead, qboolean midsplit);
+
+//=============================================================================
+
+// merge.c
+
+void MergePlaneFaces (surface_t *plane);
+face_t *MergeFaceToList (face_t *face, face_t *list);
+face_t *FreeMergeListScraps (face_t *merged);
+void MergeAll (surface_t *surfhead);
+
+//=============================================================================
+
+// surfaces.c
+
+extern int c_cornerverts;
+extern int c_tryedges;
+extern face_t *edgefaces[MAX_MAP_EDGES][2];
+
+extern int firstmodeledge;
+extern int firstmodelface;
+
+void SubdivideFaces (surface_t *surfhead);
+
+surface_t *GatherNodeFaces (node_t *headnode);
+
+void MakeFaceEdges (node_t *headnode);
+
+//=============================================================================
+
+// portals.c
+
+typedef struct portal_s
+{
+ int planenum;
+ node_t *nodes[2]; // [0] = front side of planenum
+ struct portal_s *next[2];
+ winding_t *winding;
+} portal_t;
+
+extern node_t outside_node; // portals outside the world face this
+
+void PortalizeWorld (node_t *headnode);
+void WritePortalfile (node_t *headnode);
+void FreeAllPortals (node_t *node);
+
+//=============================================================================
+
+// region.c
+
+void GrowNodeRegions (node_t *headnode);
+
+//=============================================================================
+
+// tjunc.c
+
+void tjunc (node_t *headnode);
+
+//=============================================================================
+
+// writebsp.c
+
+void WriteNodePlanes (node_t *headnode);
+void WriteClipNodes (node_t *headnode);
+void WriteDrawNodes (node_t *headnode);
+
+void BumpModel (int hullnum);
+int FindFinalPlane (dplane_t *p);
+
+void BeginBSPFile (void);
+void FinishBSPFile (void);
+
+//=============================================================================
+
+// draw.c
+
+void Draw_ClearBounds (void);
+void Draw_AddToBounds (vec3_t v);
+void Draw_DrawFace (face_t *f);
+void Draw_ClearWindow (void);
+void Draw_SetRed (void);
+void Draw_SetGrey (void);
+void Draw_SetBlack (void);
+void DrawPoint (vec3_t v);
+
+void Draw_SetColor (int c);
+void SetColor (int c);
+void DrawPortal (portal_t *p);
+void DrawLeaf (node_t *l, int color);
+void DrawBrush (brush_t *b);
+
+void DrawWinding (winding_t *w);
+void DrawTri (vec3_t p1, vec3_t p2, vec3_t p3);
+
+//=============================================================================
+
+// outside.c
+
+qboolean FillOutside (node_t *node);
+
+//=============================================================================
+
+extern qboolean drawflag;
+extern qboolean nofill;
+extern qboolean notjunc;
+extern qboolean noclip;
+extern qboolean verbose;
+
+extern int subdivide_size;
+
+extern int hullnum;
+
+extern brushset_t *brushset;
+
+void qprintf (char *fmt, ...); // only prints if verbose
+
+extern int valid;
+
+extern char portfilename[1024];
+extern char bspfilename[1024];
+extern char pointfilename[1024];
+
+extern qboolean worldmodel;
+
+
+// misc functions
+
+face_t *AllocFace (void);
+void FreeFace (face_t *f);
+
+struct portal_s *AllocPortal (void);
+void FreePortal (struct portal_s *p);
+
+surface_t *AllocSurface (void);
+void FreeSurface (surface_t *s);
+
+node_t *AllocNode (void);
+struct brush_s *AllocBrush (void);
+
+//=============================================================================
+
diff --git a/qutils/QBSP/CSG4.C b/qutils/QBSP/CSG4.C
new file mode 100644
index 0000000..97646de
--- /dev/null
+++ b/qutils/QBSP/CSG4.C
@@ -0,0 +1,464 @@
+// csg4.c
+
+#include "bsp5.h"
+
+/*
+
+NOTES
+-----
+Brushes that touch still need to be split at the cut point to make a tjunction
+
+*/
+
+face_t *validfaces[MAX_MAP_PLANES];
+
+
+face_t *inside, *outside;
+int brushfaces;
+int csgfaces;
+int csgmergefaces;
+
+void DrawList (face_t *list)
+{
+ for ( ; list ; list=list->next)
+ Draw_DrawFace (list);
+}
+
+
+/*
+==================
+NewFaceFromFace
+
+Duplicates the non point information of a face, used by SplitFace and
+MergeFace.
+==================
+*/
+face_t *NewFaceFromFace (face_t *in)
+{
+ face_t *newf;
+
+ newf = AllocFace ();
+
+ newf->planenum = in->planenum;
+ newf->texturenum = in->texturenum;
+ newf->planeside = in->planeside;
+ newf->original = in->original;
+ newf->contents[0] = in->contents[0];
+ newf->contents[1] = in->contents[1];
+
+ return newf;
+}
+
+
+/*
+==================
+SplitFace
+
+==================
+*/
+void SplitFace (face_t *in, plane_t *split, face_t **front, face_t **back)
+{
+ vec_t dists[MAXEDGES+1];
+ int sides[MAXEDGES+1];
+ int counts[3];
+ vec_t dot;
+ int i, j;
+ face_t *newf, *new2;
+ vec_t *p1, *p2;
+ vec3_t mid;
+
+ if (in->numpoints < 0)
+ Error ("SplitFace: freed face");
+ counts[0] = counts[1] = counts[2] = 0;
+
+// determine sides for each point
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ dot = DotProduct (in->pts[i], split->normal);
+ dot -= split->dist;
+ dists[i] = dot;
+ if (dot > ON_EPSILON)
+ sides[i] = SIDE_FRONT;
+ else if (dot < -ON_EPSILON)
+ sides[i] = SIDE_BACK;
+ else
+ sides[i] = SIDE_ON;
+ counts[sides[i]]++;
+ }
+ sides[i] = sides[0];
+ dists[i] = dists[0];
+
+ if (!counts[0])
+ {
+ *front = NULL;
+ *back = in;
+ return;
+ }
+ if (!counts[1])
+ {
+ *front = in;
+ *back = NULL;
+ return;
+ }
+
+ *back = newf = NewFaceFromFace (in);
+ *front = new2 = NewFaceFromFace (in);
+
+// distribute the points and generate splits
+
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ if (newf->numpoints > MAXEDGES || new2->numpoints > MAXEDGES)
+ Error ("SplitFace: numpoints > MAXEDGES");
+
+ p1 = in->pts[i];
+
+ if (sides[i] == SIDE_ON)
+ {
+ VectorCopy (p1, newf->pts[newf->numpoints]);
+ newf->numpoints++;
+ VectorCopy (p1, new2->pts[new2->numpoints]);
+ new2->numpoints++;
+ continue;
+ }
+
+ if (sides[i] == SIDE_FRONT)
+ {
+ VectorCopy (p1, new2->pts[new2->numpoints]);
+ new2->numpoints++;
+ }
+ else
+ {
+ VectorCopy (p1, newf->pts[newf->numpoints]);
+ newf->numpoints++;
+ }
+
+ if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
+ continue;
+
+ // generate a split point
+ p2 = in->pts[(i+1)%in->numpoints];
+
+ dot = dists[i] / (dists[i]-dists[i+1]);
+ for (j=0 ; j<3 ; j++)
+ { // avoid round off error when possible
+ if (split->normal[j] == 1)
+ mid[j] = split->dist;
+ else if (split->normal[j] == -1)
+ mid[j] = -split->dist;
+ else
+ mid[j] = p1[j] + dot*(p2[j]-p1[j]);
+ }
+
+ VectorCopy (mid, newf->pts[newf->numpoints]);
+ newf->numpoints++;
+ VectorCopy (mid, new2->pts[new2->numpoints]);
+ new2->numpoints++;
+ }
+
+ if (newf->numpoints > MAXEDGES || new2->numpoints > MAXEDGES)
+ Error ("SplitFace: numpoints > MAXEDGES");
+
+#if 0
+CheckFace (newf);
+CheckFace (new2);
+#endif
+
+// free the original face now that is is represented by the fragments
+ FreeFace (in);
+}
+
+/*
+=================
+ClipInside
+
+Clips all of the faces in the inside list, possibly moving them to the
+outside list or spliting it into a piece in each list.
+
+Faces exactly on the plane will stay inside unless overdrawn by later brush
+
+frontside is the side of the plane that holds the outside list
+=================
+*/
+void ClipInside (int splitplane, int frontside, qboolean precedence)
+{
+ face_t *f, *next;
+ face_t *frags[2];
+ face_t *insidelist;
+ plane_t *split;
+
+ split = &planes[splitplane];
+
+ insidelist = NULL;
+ for (f=inside ; f ; f=next)
+ {
+ next = f->next;
+
+ if (f->planenum == splitplane)
+ { // exactly on, handle special
+ if ( frontside != f->planeside || precedence )
+ { // allways clip off opposite faceing
+ frags[frontside] = NULL;
+ frags[!frontside] = f;
+ }
+ else
+ { // leave it on the outside
+ frags[frontside] = f;
+ frags[!frontside] = NULL;
+ }
+ }
+ else
+ { // proper split
+ SplitFace (f, split, &frags[0], &frags[1]);
+ }
+
+ if (frags[frontside])
+ {
+ frags[frontside]->next = outside;
+ outside = frags[frontside];
+ }
+ if (frags[!frontside])
+ {
+ frags[!frontside]->next = insidelist;
+ insidelist = frags[!frontside];
+ }
+ }
+
+ inside = insidelist;
+}
+
+
+/*
+==================
+SaveOutside
+
+Saves all of the faces in the outside list to the bsp plane list
+==================
+*/
+void SaveOutside (qboolean mirror)
+{
+ face_t *f , *next, *newf;
+ int i;
+ int planenum;
+
+ for (f=outside ; f ; f=next)
+ {
+ next = f->next;
+ csgfaces++;
+ Draw_DrawFace (f);
+ planenum = f->planenum;
+
+ if (mirror)
+ {
+ newf = NewFaceFromFace (f);
+
+ newf->numpoints = f->numpoints;
+ newf->planeside = f->planeside ^ 1; // reverse side
+ newf->contents[0] = f->contents[1];
+ newf->contents[1] = f->contents[0];
+
+ for (i=0 ; i<f->numpoints ; i++) // add points backwards
+ {
+ VectorCopy (f->pts[f->numpoints-1-i], newf->pts[i]);
+ }
+ }
+ else
+ newf = NULL;
+
+ validfaces[planenum] = MergeFaceToList(f, validfaces[planenum]);
+ if (newf)
+ validfaces[planenum] = MergeFaceToList(newf, validfaces[planenum]);
+
+ validfaces[planenum] = FreeMergeListScraps (validfaces[planenum]);
+ }
+}
+
+/*
+==================
+FreeInside
+
+Free all the faces that got clipped out
+==================
+*/
+void FreeInside (int contents)
+{
+ face_t *f, *next;
+
+ for (f=inside ; f ; f=next)
+ {
+ next = f->next;
+
+ if (contents != CONTENTS_SOLID)
+ {
+ f->contents[0] = contents;
+ f->next = outside;
+ outside = f;
+ }
+ else
+ FreeFace (f);
+ }
+}
+
+
+//==========================================================================
+
+/*
+==================
+BuildSurfaces
+
+Returns a chain of all the external surfaces with one or more visible
+faces.
+==================
+*/
+surface_t *BuildSurfaces (void)
+{
+ face_t **f;
+ face_t *count;
+ int i;
+ surface_t *s;
+ surface_t *surfhead;
+
+ surfhead = NULL;
+
+ f = validfaces;
+ for (i=0 ; i<numbrushplanes ; i++, f++)
+ {
+ if (!*f)
+ continue; // nothing left on this plane
+
+// create a new surface to hold the faces on this plane
+ s = AllocSurface ();
+ s->planenum = i;
+ s->next = surfhead;
+ surfhead = s;
+ s->faces = *f;
+ for (count = s->faces ; count ; count=count->next)
+ csgmergefaces++;
+ CalcSurfaceInfo (s); // bounding box and flags
+ }
+
+ return surfhead;
+}
+
+//==========================================================================
+
+/*
+==================
+CopyFacesToOutside
+==================
+*/
+void CopyFacesToOutside (brush_t *b)
+{
+ face_t *f, *newf;
+
+ outside = NULL;
+
+ for (f=b->faces ; f ; f=f->next)
+ {
+ brushfaces++;
+#if 0
+{
+ int i;
+
+ for (i=0 ; i<f->numpoints ; i++)
+ printf ("(%f,%f,%f) ",f->pts[i][0], f->pts[i][1], f->pts[i][2]);
+ printf ("\n");
+}
+#endif
+ newf = AllocFace ();
+ *newf = *f;
+ newf->next = outside;
+ newf->contents[0] = CONTENTS_EMPTY;
+ newf->contents[1] = b->contents;
+ outside = newf;
+ }
+}
+
+
+/*
+==================
+CSGFaces
+
+Returns a list of surfaces containing aall of the faces
+==================
+*/
+surface_t *CSGFaces (brushset_t *bs)
+{
+ brush_t *b1, *b2;
+ int i;
+ qboolean overwrite;
+ face_t *f;
+ surface_t *surfhead;
+
+ qprintf ("---- CSGFaces ----\n");
+
+ memset (validfaces, 0, sizeof(validfaces));
+
+ csgfaces = brushfaces = csgmergefaces = 0;
+
+ Draw_ClearWindow ();
+
+//
+// do the solid faces
+//
+ for (b1=bs->brushes ; b1 ; b1 = b1->next)
+ {
+ // set outside to a copy of the brush's faces
+ CopyFacesToOutside (b1);
+
+ overwrite = false;
+
+ for (b2=bs->brushes ; b2 ; b2 = b2->next)
+ {
+ // see if b2 needs to clip a chunk out of b1
+
+ if (b1==b2)
+ {
+ overwrite = true; // later brushes now overwrite
+ continue;
+ }
+
+ // check bounding box first
+ for (i=0 ; i<3 ; i++)
+ if (b1->mins[i] > b2->maxs[i] || b1->maxs[i] < b2->mins[i])
+ break;
+ if (i<3)
+ continue;
+
+ // divide faces by the planes of the new brush
+
+ inside = outside;
+ outside = NULL;
+
+ for (f=b2->faces ; f ; f=f->next)
+ ClipInside (f->planenum, f->planeside, overwrite);
+
+ // these faces are continued in another brush, so get rid of them
+ if (b1->contents == CONTENTS_SOLID && b2->contents <= CONTENTS_WATER)
+ FreeInside (b2->contents);
+ else
+ FreeInside (CONTENTS_SOLID);
+ }
+
+ // all of the faces left in outside are real surface faces
+ if (b1->contents != CONTENTS_SOLID)
+ SaveOutside (true); // mirror faces for inside view
+ else
+ SaveOutside (false);
+ }
+
+#if 0
+ if (!csgfaces)
+ Error ("No faces");
+#endif
+
+ surfhead = BuildSurfaces ();
+
+ qprintf ("%5i brushfaces\n", brushfaces);
+ qprintf ("%5i csgfaces\n", csgfaces);
+ qprintf ("%5i mergedfaces\n", csgmergefaces);
+
+ return surfhead;
+}
+
+
diff --git a/qutils/QBSP/MAKEFILE b/qutils/QBSP/MAKEFILE
new file mode 100644
index 0000000..5e6318a
--- /dev/null
+++ b/qutils/QBSP/MAKEFILE
@@ -0,0 +1,65 @@
+
+EXES = qbsp light vis bspinfo entmap visx
+NTEXES = qbsp.exe light.exe vis.exe bspinfo.exe entmap.exe visx.exe
+
+#==============================================================================
+
+EXT= .o
+
+all: $(EXES)
+
+clean:
+ rm *.o *.obj $(EXES) $(NTEXES)
+
+next:
+ make "CFLAGS = -g -Wall -I.."
+
+nextinstall:
+ make "CFLAGS = -O4 -g -Wall -I.. -arch i386 -arch hppa"
+ cp $(EXES) /LocalApps
+
+alpha:
+ make "CFLAGS = -g -I.." "LDFLAGS = -lm"
+
+alphainstall:
+ make "CFLAGS = -O4 -I.." "LDFLAGS = -lm"
+ cp $(EXES) /LocalApps
+
+nt:
+ nmake /nologo "CFLAGS = -nologo -Zi -DWIN32 -I.." "LDFLAGS = " "EXT = .obj"
+
+ntinstall:
+ nmake /nologo "CFLAGS = -nologo -Ox -G5 -DWIN32 -I.." "LDFLAGS = " "EXT = .obj"
+ cp $(NTEXES) f:\nt\id
+
+#==============================================================================
+
+
+QBSPFILES = region$(EXT) map$(EXT) brush$(EXT) cmdlib$(EXT) csg4$(EXT) surfaces$(EXT) mathlib$(EXT)\
+ merge$(EXT) outside$(EXT) portals$(EXT) qbsp$(EXT) solidbsp$(EXT) tjunc$(EXT)\
+ writebsp$(EXT) bspfile$(EXT) nodraw$(EXT)
+qbsp : $(QBSPFILES)
+ $(CC) $(CFLAGS) -o qbsp $(QBSPFILES)
+
+light : threads$(EXT) bspfile$(EXT) cmdlib$(EXT) light$(EXT) ltface$(EXT) mathlib$(EXT) trace$(EXT) entities$(EXT)
+ $(CC) $(CFLAGS) -o light threads$(EXT) bspfile$(EXT) cmdlib$(EXT) light$(EXT) ltface$(EXT) mathlib$(EXT) trace$(EXT) entities$(EXT)
+
+vis : vis$(EXT) flow$(EXT) cmdlib$(EXT) mathlib$(EXT) bspfile$(EXT) soundpvs$(EXT)
+ $(CC) $(CFLAGS) -o vis vis$(EXT) flow$(EXT) cmdlib$(EXT) mathlib$(EXT) bspfile$(EXT) soundpvs$(EXT)
+
+visx : visx$(EXT) flowx$(EXT) cmdlib$(EXT) mathlib$(EXT) bspfile$(EXT) soundpvs$(EXT)
+ $(CC) $(CFLAGS) -o visx visx$(EXT) flowx$(EXT) cmdlib$(EXT) mathlib$(EXT) bspfile$(EXT) soundpvs$(EXT)
+
+bspinfo : bspinfo$(EXT) bspfile$(EXT) cmdlib$(EXT)
+ $(CC) $(CFLAGS) -o bspinfo bspinfo$(EXT) bspfile$(EXT) cmdlib$(EXT)
+
+entmap : entmap$(EXT) cmdlib$(EXT)
+ $(CC) $(CFLAGS) -o entmap entmap$(EXT) cmdlib$(EXT)
+
+
+cmdlib$(EXT) : ../cmdlib.c
+ $(CC) $(CFLAGS) -c -o cmdlib$(EXT) ../cmdlib.c
+mathlib$(EXT) : ../mathlib.c
+ $(CC) $(CFLAGS) -c -o mathlib$(EXT) ../mathlib.c
+
+
diff --git a/qutils/QBSP/MAP.C b/qutils/QBSP/MAP.C
new file mode 100644
index 0000000..0aaa2f4
--- /dev/null
+++ b/qutils/QBSP/MAP.C
@@ -0,0 +1,579 @@
+// map.c
+
+#include "bsp5.h"
+
+int nummapbrushes;
+mbrush_t mapbrushes[MAX_MAP_BRUSHES];
+
+int num_entities;
+entity_t entities[MAX_MAP_ENTITIES];
+
+int nummiptex;
+char miptex[MAX_MAP_TEXINFO][16];
+
+//============================================================================
+
+/*
+===============
+FindMiptex
+
+===============
+*/
+int FindMiptex (char *name)
+{
+ int i;
+
+ for (i=0 ; i<nummiptex ; i++)
+ {
+ if (!strcmp (name, miptex[i]))
+ return i;
+ }
+ if (nummiptex == MAX_MAP_TEXINFO)
+ Error ("nummiptex == MAX_MAP_TEXINFO");
+ strcpy (miptex[i], name);
+ nummiptex++;
+ return i;
+}
+
+/*
+===============
+FindTexinfo
+
+Returns a global texinfo number
+===============
+*/
+int FindTexinfo (texinfo_t *t)
+{
+ int i, j;
+ texinfo_t *tex;
+
+// set the special flag
+ if (miptex[t->miptex][0] == '*'
+ || !Q_strncasecmp (miptex[t->miptex], "sky",3) )
+ t->flags |= TEX_SPECIAL;
+
+
+ tex = texinfo;
+ for (i=0 ; i<numtexinfo;i++, tex++)
+ {
+ if (t->miptex != tex->miptex)
+ continue;
+ if (t->flags != tex->flags)
+ continue;
+
+ for (j=0 ; j<8 ; j++)
+ if (t->vecs[0][j] != tex->vecs[0][j])
+ break;
+ if (j != 8)
+ continue;
+
+ return i;
+ }
+
+// allocate a new texture
+ if (numtexinfo == MAX_MAP_TEXINFO)
+ Error ("numtexinfo == MAX_MAP_TEXINFO");
+ texinfo[i] = *t;
+ numtexinfo++;
+
+ return i;
+}
+
+
+//============================================================================
+
+#define MAXTOKEN 128
+
+char token[MAXTOKEN];
+qboolean unget;
+char *script_p;
+int scriptline;
+
+void StartTokenParsing (char *data)
+{
+ scriptline = 1;
+ script_p = data;
+ unget = false;
+}
+
+qboolean GetToken (qboolean crossline)
+{
+ char *token_p;
+
+ if (unget) // is a token allready waiting?
+ return true;
+
+//
+// skip space
+//
+skipspace:
+ while (*script_p <= 32)
+ {
+ if (!*script_p)
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete",scriptline);
+ return false;
+ }
+ if (*script_p++ == '\n')
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete",scriptline);
+ scriptline++;
+ }
+ }
+
+ if (script_p[0] == '/' && script_p[1] == '/') // comment field
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete\n",scriptline);
+ while (*script_p++ != '\n')
+ if (!*script_p)
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete",scriptline);
+ return false;
+ }
+ goto skipspace;
+ }
+
+//
+// copy token
+//
+ token_p = token;
+
+ if (*script_p == '"')
+ {
+ script_p++;
+ while ( *script_p != '"' )
+ {
+ if (!*script_p)
+ Error ("EOF inside quoted token");
+ *token_p++ = *script_p++;
+ if (token_p > &token[MAXTOKEN-1])
+ Error ("Token too large on line %i",scriptline);
+ }
+ script_p++;
+ }
+ else while ( *script_p > 32 )
+ {
+ *token_p++ = *script_p++;
+ if (token_p > &token[MAXTOKEN-1])
+ Error ("Token too large on line %i",scriptline);
+ }
+
+ *token_p = 0;
+
+ return true;
+}
+
+void UngetToken ()
+{
+ unget = true;
+}
+
+
+//============================================================================
+
+entity_t *mapent;
+
+/*
+=================
+ParseEpair
+=================
+*/
+void ParseEpair (void)
+{
+ epair_t *e;
+
+ e = malloc (sizeof(epair_t));
+ memset (e, 0, sizeof(epair_t));
+ e->next = mapent->epairs;
+ mapent->epairs = e;
+
+ if (strlen(token) >= MAX_KEY-1)
+ Error ("ParseEpar: token too long");
+ e->key = copystring(token);
+ GetToken (false);
+ if (strlen(token) >= MAX_VALUE-1)
+ Error ("ParseEpar: token too long");
+ e->value = copystring(token);
+}
+
+//============================================================================
+
+
+/*
+==================
+textureAxisFromPlane
+==================
+*/
+vec3_t baseaxis[18] =
+{
+{0,0,1}, {1,0,0}, {0,-1,0}, // floor
+{0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling
+{1,0,0}, {0,1,0}, {0,0,-1}, // west wall
+{-1,0,0}, {0,1,0}, {0,0,-1}, // east wall
+{0,1,0}, {1,0,0}, {0,0,-1}, // south wall
+{0,-1,0}, {1,0,0}, {0,0,-1} // north wall
+};
+
+void TextureAxisFromPlane(plane_t *pln, vec3_t xv, vec3_t yv)
+{
+ int bestaxis;
+ float dot,best;
+ int i;
+
+ best = 0;
+ bestaxis = 0;
+
+ for (i=0 ; i<6 ; i++)
+ {
+ dot = DotProduct (pln->normal, baseaxis[i*3]);
+ if (dot > best)
+ {
+ best = dot;
+ bestaxis = i;
+ }
+ }
+
+ VectorCopy (baseaxis[bestaxis*3+1], xv);
+ VectorCopy (baseaxis[bestaxis*3+2], yv);
+}
+
+
+//=============================================================================
+
+
+/*
+=================
+ParseBrush
+=================
+*/
+void ParseBrush (void)
+{
+ mbrush_t *b;
+ mface_t *f, *f2;
+ vec3_t planepts[3];
+ vec3_t t1, t2, t3;
+ int i,j;
+ texinfo_t tx;
+ vec_t d;
+ float shift[2], rotate, scale[2];
+
+ b = &mapbrushes[nummapbrushes];
+ nummapbrushes++;
+ b->next = mapent->brushes;
+ mapent->brushes = b;
+
+ do
+ {
+ if (!GetToken (true))
+ break;
+ if (!strcmp (token, "}") )
+ break;
+
+ // read the three point plane definition
+ for (i=0 ; i<3 ; i++)
+ {
+ if (i != 0)
+ GetToken (true);
+ if (strcmp (token, "(") )
+ Error ("parsing brush");
+
+ for (j=0 ; j<3 ; j++)
+ {
+ GetToken (false);
+ planepts[i][j] = atoi(token);
+ }
+
+ GetToken (false);
+ if (strcmp (token, ")") )
+ Error ("parsing brush");
+
+ }
+
+ // read the texturedef
+ memset (&tx, 0, sizeof(tx));
+ GetToken (false);
+ tx.miptex = FindMiptex (token);
+ GetToken (false);
+ shift[0] = atoi(token);
+ GetToken (false);
+ shift[1] = atoi(token);
+ GetToken (false);
+ rotate = atoi(token);
+ GetToken (false);
+ scale[0] = atof(token);
+ GetToken (false);
+ scale[1] = atof(token);
+
+ // if the three points are all on a previous plane, it is a
+ // duplicate plane
+ for (f2 = b->faces ; f2 ; f2=f2->next)
+ {
+ for (i=0 ; i<3 ; i++)
+ {
+ d = DotProduct(planepts[i],f2->plane.normal) - f2->plane.dist;
+ if (d < -ON_EPSILON || d > ON_EPSILON)
+ break;
+ }
+ if (i==3)
+ break;
+ }
+ if (f2)
+ {
+ printf ("WARNING: brush with duplicate plane\n");
+ continue;
+ }
+
+ f = malloc(sizeof(mface_t));
+ f->next = b->faces;
+ b->faces = f;
+
+ // convert to a vector / dist plane
+ for (j=0 ; j<3 ; j++)
+ {
+ t1[j] = planepts[0][j] - planepts[1][j];
+ t2[j] = planepts[2][j] - planepts[1][j];
+ t3[j] = planepts[1][j];
+ }
+
+ CrossProduct(t1,t2, f->plane.normal);
+ if (VectorCompare (f->plane.normal, vec3_origin))
+ {
+ printf ("WARNING: brush plane with no normal\n");
+ b->faces = f->next;
+ free (f);
+ break;
+ }
+ VectorNormalize (f->plane.normal);
+ f->plane.dist = DotProduct (t3, f->plane.normal);
+
+ //
+ // fake proper texture vectors from QuakeEd style
+ //
+ {
+ vec3_t vecs[2];
+ int sv, tv;
+ float ang, sinv, cosv;
+ float ns, nt;
+
+ TextureAxisFromPlane(&f->plane, vecs[0], vecs[1]);
+
+ if (!scale[0])
+ scale[0] = 1;
+ if (!scale[1])
+ scale[1] = 1;
+
+
+ // rotate axis
+ if (rotate == 0)
+ { sinv = 0 ; cosv = 1; }
+ else if (rotate == 90)
+ { sinv = 1 ; cosv = 0; }
+ else if (rotate == 180)
+ { sinv = 0 ; cosv = -1; }
+ else if (rotate == 270)
+ { sinv = -1 ; cosv = 0; }
+ else
+ {
+ ang = rotate / 180 * Q_PI;
+ sinv = sin(ang);
+ cosv = cos(ang);
+ }
+
+ if (vecs[0][0])
+ sv = 0;
+ else if (vecs[0][1])
+ sv = 1;
+ else
+ sv = 2;
+
+ if (vecs[1][0])
+ tv = 0;
+ else if (vecs[1][1])
+ tv = 1;
+ else
+ tv = 2;
+
+ for (i=0 ; i<2 ; i++)
+ {
+ ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
+ nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
+ vecs[i][sv] = ns;
+ vecs[i][tv] = nt;
+ }
+
+ for (i=0 ; i<2 ; i++)
+ for (j=0 ; j<3 ; j++)
+ tx.vecs[i][j] = vecs[i][j] / scale[i];
+
+ tx.vecs[0][3] = shift[0];
+ tx.vecs[1][3] = shift[1];
+ }
+
+ // unique the texinfo
+ f->texinfo = FindTexinfo (&tx);
+ } while (1);
+}
+
+/*
+================
+ParseEntity
+================
+*/
+qboolean ParseEntity (void)
+{
+ if (!GetToken (true))
+ return false;
+
+ if (strcmp (token, "{") )
+ Error ("ParseEntity: { not found");
+
+ if (num_entities == MAX_MAP_ENTITIES)
+ Error ("num_entities == MAX_MAP_ENTITIES");
+
+ mapent = &entities[num_entities];
+ num_entities++;
+
+ do
+ {
+ if (!GetToken (true))
+ Error ("ParseEntity: EOF without closing brace");
+ if (!strcmp (token, "}") )
+ break;
+ if (!strcmp (token, "{") )
+ ParseBrush ();
+ else
+ ParseEpair ();
+ } while (1);
+
+ GetVectorForKey (mapent, "origin", mapent->origin);
+ return true;
+}
+
+/*
+================
+LoadMapFile
+================
+*/
+void LoadMapFile (char *filename)
+{
+ char *buf;
+
+ LoadFile (filename, (void **)&buf);
+
+ StartTokenParsing (buf);
+
+ num_entities = 0;
+
+ while (ParseEntity ())
+ {
+ }
+
+ free (buf);
+
+ qprintf ("--- LoadMapFile ---\n");
+ qprintf ("%s\n", filename);
+ qprintf ("%5i brushes\n", nummapbrushes);
+ qprintf ("%5i entities\n", num_entities);
+ qprintf ("%5i miptex\n", nummiptex);
+ qprintf ("%5i texinfo\n", numtexinfo);
+}
+
+void PrintEntity (entity_t *ent)
+{
+ epair_t *ep;
+
+ for (ep=ent->epairs ; ep ; ep=ep->next)
+ printf ("%20s : %s\n", ep->key, ep->value);
+}
+
+
+char *ValueForKey (entity_t *ent, char *key)
+{
+ epair_t *ep;
+
+ for (ep=ent->epairs ; ep ; ep=ep->next)
+ if (!strcmp (ep->key, key) )
+ return ep->value;
+ return "";
+}
+
+void SetKeyValue (entity_t *ent, char *key, char *value)
+{
+ epair_t *ep;
+
+ for (ep=ent->epairs ; ep ; ep=ep->next)
+ if (!strcmp (ep->key, key) )
+ {
+ free (ep->value);
+ ep->value = copystring(value);
+ return;
+ }
+ ep = malloc (sizeof(*ep));
+ ep->next = ent->epairs;
+ ent->epairs = ep;
+ ep->key = copystring(key);
+ ep->value = copystring(value);
+}
+
+float FloatForKey (entity_t *ent, char *key)
+{
+ char *k;
+
+ k = ValueForKey (ent, key);
+ return atof(k);
+}
+
+void GetVectorForKey (entity_t *ent, char *key, vec3_t vec)
+{
+ char *k;
+ double v1, v2, v3;
+
+ k = ValueForKey (ent, key);
+ v1 = v2 = v3 = 0;
+// scanf into doubles, then assign, so it is vec_t size independent
+ sscanf (k, "%lf %lf %lf", &v1, &v2, &v3);
+ vec[0] = v1;
+ vec[1] = v2;
+ vec[2] = v3;
+}
+
+
+void WriteEntitiesToString (void)
+{
+ char *buf, *end;
+ epair_t *ep;
+ char line[128];
+ int i;
+
+ buf = dentdata;
+ end = buf;
+ *end = 0;
+
+ for (i=0 ; i<num_entities ; i++)
+ {
+ ep = entities[i].epairs;
+ if (!ep)
+ continue; // ent got removed
+
+ strcat (end,"{\n");
+ end += 2;
+
+ for (ep = entities[i].epairs ; ep ; ep=ep->next)
+ {
+ sprintf (line, "\"%s\" \"%s\"\n", ep->key, ep->value);
+ strcat (end, line);
+ end += strlen(line);
+ }
+ strcat (end,"}\n");
+ end += 2;
+
+ if (end > buf + MAX_MAP_ENTSTRING)
+ Error ("Entity text too long");
+ }
+ entdatasize = end - buf + 1;
+}
+
diff --git a/qutils/QBSP/MAP.H b/qutils/QBSP/MAP.H
new file mode 100644
index 0000000..ce1224c
--- /dev/null
+++ b/qutils/QBSP/MAP.H
@@ -0,0 +1,50 @@
+
+
+#define MAX_FACES 16
+typedef struct mface_s
+{
+ struct mface_s *next;
+ plane_t plane;
+ int texinfo;
+} mface_t;
+
+typedef struct mbrush_s
+{
+ struct mbrush_s *next;
+ mface_t *faces;
+} mbrush_t;
+
+typedef struct epair_s
+{
+ struct epair_s *next;
+ char *key;
+ char *value;
+} epair_t;
+
+typedef struct
+{
+ vec3_t origin;
+ mbrush_t *brushes;
+ epair_t *epairs;
+} entity_t;
+
+extern int nummapbrushes;
+extern mbrush_t mapbrushes[MAX_MAP_BRUSHES];
+
+extern int num_entities;
+extern entity_t entities[MAX_MAP_ENTITIES];
+
+extern int nummiptex;
+extern char miptex[MAX_MAP_TEXINFO][16];
+
+void LoadMapFile (char *filename);
+
+int FindMiptex (char *name);
+
+void PrintEntity (entity_t *ent);
+char *ValueForKey (entity_t *ent, char *key);
+void SetKeyValue (entity_t *ent, char *key, char *value);
+float FloatForKey (entity_t *ent, char *key);
+void GetVectorForKey (entity_t *ent, char *key, vec3_t vec);
+
+void WriteEntitiesToString (void);
diff --git a/qutils/QBSP/MERGE.C b/qutils/QBSP/MERGE.C
new file mode 100644
index 0000000..54e3298
--- /dev/null
+++ b/qutils/QBSP/MERGE.C
@@ -0,0 +1,277 @@
+// merge.c
+
+#include "bsp5.h"
+
+
+#define CONTINUOUS_EPSILON 0.001
+
+/*
+================
+CheckColinear
+
+================
+*/
+void CheckColinear (face_t *f)
+{
+ int i, j;
+ vec3_t v1, v2;
+
+ for (i=0 ; i<f->numpoints ;i++)
+ {
+// skip the point if the vector from the previous point is the same
+// as the vector to the next point
+ j = (i - 1 < 0) ? f->numpoints - 1 : i - 1;
+ VectorSubtract (f->pts[i], f->pts[j], v1);
+ VectorNormalize (v1);
+
+ j = (i + 1 == f->numpoints) ? 0 : i + 1;
+ VectorSubtract (f->pts[j], f->pts[i], v2);
+ VectorNormalize (v2);
+
+ if (VectorCompare (v1, v2))
+ Error ("Colinear edge");
+ }
+
+}
+
+
+/*
+=============
+TryMerge
+
+If two polygons share a common edge and the edges that meet at the
+common points are both inside the other polygons, merge them
+
+Returns NULL if the faces couldn't be merged, or the new face.
+The originals will NOT be freed.
+=============
+*/
+face_t *TryMerge (face_t *f1, face_t *f2)
+{
+ vec_t *p1, *p2, *p3, *p4, *back;
+ face_t *newf;
+ int i, j, k, l;
+ vec3_t normal, delta, planenormal;
+ vec_t dot;
+ plane_t *plane;
+ qboolean keep1, keep2;
+
+ if (f1->numpoints == -1 || f2->numpoints == -1)
+ return NULL;
+ if (f1->planeside != f2->planeside)
+ return NULL;
+ if (f1->texturenum != f2->texturenum)
+ return NULL;
+ if (f1->contents[0] != f2->contents[0])
+ return NULL;
+ if (f1->contents[1] != f2->contents[1])
+ return NULL;
+
+//
+// find a common edge
+//
+ p1 = p2 = NULL; // stop compiler warning
+ j = 0; //
+
+ for (i=0 ; i<f1->numpoints ; i++)
+ {
+ p1 = f1->pts[i];
+ p2 = f1->pts[(i+1)%f1->numpoints];
+ for (j=0 ; j<f2->numpoints ; j++)
+ {
+ p3 = f2->pts[j];
+ p4 = f2->pts[(j+1)%f2->numpoints];
+ for (k=0 ; k<3 ; k++)
+ {
+ if (fabs(p1[k] - p4[k]) > EQUAL_EPSILON)
+ break;
+ if (fabs(p2[k] - p3[k]) > EQUAL_EPSILON)
+ break;
+ }
+ if (k==3)
+ break;
+ }
+ if (j < f2->numpoints)
+ break;
+ }
+
+ if (i == f1->numpoints)
+ return NULL; // no matching edges
+
+//
+// check slope of connected lines
+// if the slopes are colinear, the point can be removed
+//
+ plane = &planes[f1->planenum];
+ VectorCopy (plane->normal, planenormal);
+ if (f1->planeside)
+ VectorSubtract (vec3_origin, planenormal, planenormal);
+
+ back = f1->pts[(i+f1->numpoints-1)%f1->numpoints];
+ VectorSubtract (p1, back, delta);
+ CrossProduct (planenormal, delta, normal);
+ VectorNormalize (normal);
+
+ back = f2->pts[(j+2)%f2->numpoints];
+ VectorSubtract (back, p1, delta);
+ dot = DotProduct (delta, normal);
+ if (dot > CONTINUOUS_EPSILON)
+ return NULL; // not a convex polygon
+ keep1 = dot < -CONTINUOUS_EPSILON;
+
+ back = f1->pts[(i+2)%f1->numpoints];
+ VectorSubtract (back, p2, delta);
+ CrossProduct (planenormal, delta, normal);
+ VectorNormalize (normal);
+
+ back = f2->pts[(j+f2->numpoints-1)%f2->numpoints];
+ VectorSubtract (back, p2, delta);
+ dot = DotProduct (delta, normal);
+ if (dot > CONTINUOUS_EPSILON)
+ return NULL; // not a convex polygon
+ keep2 = dot < -CONTINUOUS_EPSILON;
+
+//
+// build the new polygon
+//
+ if (f1->numpoints + f2->numpoints > MAXEDGES)
+ {
+// Error ("TryMerge: too many edges!");
+ return NULL;
+ }
+
+ newf = NewFaceFromFace (f1);
+
+// copy first polygon
+ for (k=(i+1)%f1->numpoints ; k != i ; k=(k+1)%f1->numpoints)
+ {
+ if (k==(i+1)%f1->numpoints && !keep2)
+ continue;
+
+ VectorCopy (f1->pts[k], newf->pts[newf->numpoints]);
+ newf->numpoints++;
+ }
+
+// copy second polygon
+ for (l= (j+1)%f2->numpoints ; l != j ; l=(l+1)%f2->numpoints)
+ {
+ if (l==(j+1)%f2->numpoints && !keep1)
+ continue;
+ VectorCopy (f2->pts[l], newf->pts[newf->numpoints]);
+ newf->numpoints++;
+ }
+
+ return newf;
+}
+
+
+/*
+===============
+MergeFaceToList
+===============
+*/
+qboolean mergedebug;
+face_t *MergeFaceToList (face_t *face, face_t *list)
+{
+ face_t *newf, *f;
+
+ for (f=list ; f ; f=f->next)
+ {
+//CheckColinear (f);
+if (mergedebug)
+{
+Draw_ClearWindow ();
+Draw_DrawFace (face);
+Draw_DrawFace (f);
+Draw_SetBlack ();
+}
+ newf = TryMerge (face, f);
+ if (!newf)
+ continue;
+ FreeFace (face);
+ f->numpoints = -1; // merged out
+ return MergeFaceToList (newf, list);
+ }
+
+// didn't merge, so add at start
+ face->next = list;
+ return face;
+}
+
+
+/*
+===============
+FreeMergeListScraps
+===============
+*/
+face_t *FreeMergeListScraps (face_t *merged)
+{
+ face_t *head, *next;
+
+ head = NULL;
+ for ( ; merged ; merged = next)
+ {
+ next = merged->next;
+ if (merged->numpoints == -1)
+ FreeFace (merged);
+ else
+ {
+ merged->next = head;
+ head = merged;
+ }
+ }
+
+ return head;
+}
+
+
+/*
+===============
+MergePlaneFaces
+===============
+*/
+void MergePlaneFaces (surface_t *plane)
+{
+ face_t *f1, *next;
+ face_t *merged;
+
+ merged = NULL;
+
+ for (f1 = plane->faces ; f1 ; f1 = next)
+ {
+ next = f1->next;
+ merged = MergeFaceToList (f1, merged);
+ }
+
+// chain all of the non-empty faces to the plane
+ plane->faces = FreeMergeListScraps (merged);
+}
+
+
+/*
+============
+MergeAll
+============
+*/
+void MergeAll (surface_t *surfhead)
+{
+ surface_t *surf;
+ int mergefaces;
+ face_t *f;
+
+ printf ("---- MergeAll ----\n");
+
+ mergefaces = 0;
+ for (surf = surfhead ; surf ; surf=surf->next)
+ {
+ MergePlaneFaces (surf);
+Draw_ClearWindow ();
+ for (f=surf->faces ; f ; f=f->next)
+ {
+Draw_DrawFace (f);
+ mergefaces++;
+ }
+ }
+
+ printf ("%i mergefaces\n", mergefaces);
+}
diff --git a/qutils/QBSP/NODRAW.C b/qutils/QBSP/NODRAW.C
new file mode 100644
index 0000000..7fd8de9
--- /dev/null
+++ b/qutils/QBSP/NODRAW.C
@@ -0,0 +1,54 @@
+
+#include "bsp5.h"
+
+void Draw_ClearBounds (void)
+{
+}
+
+void Draw_AddToBounds (vec3_t v)
+{
+}
+
+void Draw_DrawFace (face_t *f)
+{
+}
+
+void Draw_ClearWindow (void)
+{
+}
+
+void Draw_SetRed (void)
+{
+}
+
+void Draw_SetGrey (void)
+{
+}
+
+void Draw_SetBlack (void)
+{
+}
+
+void DrawPoint (vec3_t v)
+{
+}
+
+void DrawLeaf (node_t *l, int color)
+{
+}
+
+void DrawBrush (brush_t *b)
+{
+}
+
+void DrawWinding (winding_t *w)
+{
+}
+
+void DrawTri (vec3_t p1, vec3_t p2, vec3_t p3)
+{
+}
+
+void DrawPortal (portal_t *portal)
+{
+}
diff --git a/qutils/QBSP/OUTSIDE.C b/qutils/QBSP/OUTSIDE.C
new file mode 100644
index 0000000..31eb662
--- /dev/null
+++ b/qutils/QBSP/OUTSIDE.C
@@ -0,0 +1,252 @@
+
+#include "bsp5.h"
+
+int outleafs;
+
+/*
+===========
+PointInLeaf
+===========
+*/
+node_t *PointInLeaf (node_t *node, vec3_t point)
+{
+ vec_t d;
+
+ if (node->contents)
+ return node;
+
+ d = DotProduct (planes[node->planenum].normal, point) - planes[node->planenum]. dist;
+
+ if (d > 0)
+ return PointInLeaf (node->children[0], point);
+
+ return PointInLeaf (node->children[1], point);
+}
+
+/*
+===========
+PlaceOccupant
+===========
+*/
+qboolean PlaceOccupant (int num, vec3_t point, node_t *headnode)
+{
+ node_t *n;
+
+ n = PointInLeaf (headnode, point);
+ if (n->contents == CONTENTS_SOLID)
+ return false;
+ n->occupied = num;
+ return true;
+}
+
+
+/*
+==============
+MarkLeakTrail
+==============
+*/
+portal_t *prevleaknode;
+FILE *leakfile;
+void MarkLeakTrail (portal_t *n2)
+{
+ int i, j;
+ vec3_t p1, p2, dir;
+ float len;
+ portal_t *n1;
+
+ if (hullnum)
+ return;
+
+ n1 = prevleaknode;
+ prevleaknode = n2;
+
+ if (!n1)
+ return;
+
+ VectorCopy (n2->winding->points[0], p1);
+ for (i=1 ; i< n2->winding->numpoints ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ p1[j] = (p1[j] + n2->winding->points[i][j]) / 2;
+ }
+
+ VectorCopy (n1->winding->points[0], p2);
+ for (i=1 ; i< n1->winding->numpoints ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ p2[j] = (p2[j] + n1->winding->points[i][j]) / 2;
+ }
+
+ VectorSubtract (p2, p1, dir);
+ len = VectorLength (dir);
+ VectorNormalize (dir);
+
+ while (len > 2)
+ {
+ fprintf (leakfile,"%f %f %f\n", p1[0], p1[1], p1[2]);
+ for (i=0 ; i<3 ; i++)
+ p1[i] += dir[i]*2;
+ len -= 2;
+ }
+}
+
+/*
+==================
+RecursiveFillOutside
+
+If fill is false, just check, don't fill
+Returns true if an occupied leaf is reached
+==================
+*/
+int hit_occupied;
+int backdraw;
+qboolean RecursiveFillOutside (node_t *l, qboolean fill)
+{
+ portal_t *p;
+ int s;
+
+ if (l->contents == CONTENTS_SOLID || l->contents == CONTENTS_SKY)
+ return false;
+
+ if (l->valid == valid)
+ return false;
+
+ if (l->occupied)
+ return true;
+
+ l->valid = valid;
+
+// fill it and it's neighbors
+ if (fill)
+ l->contents = CONTENTS_SOLID;
+ outleafs++;
+
+ for (p=l->portals ; p ; )
+ {
+ s = (p->nodes[0] == l);
+
+ if (RecursiveFillOutside (p->nodes[s], fill) )
+ { // leaked, so stop filling
+ if (backdraw-- > 0)
+ {
+ MarkLeakTrail (p);
+ DrawLeaf (l, 2);
+ }
+ return true;
+ }
+ p = p->next[!s];
+ }
+
+ return false;
+}
+
+/*
+==================
+ClearOutFaces
+
+==================
+*/
+void ClearOutFaces (node_t *node)
+{
+ face_t **fp;
+
+ if (node->planenum != -1)
+ {
+ ClearOutFaces (node->children[0]);
+ ClearOutFaces (node->children[1]);
+ return;
+ }
+ if (node->contents != CONTENTS_SOLID)
+ return;
+
+ for (fp=node->markfaces ; *fp ; fp++)
+ {
+ // mark all the original faces that are removed
+ (*fp)->numpoints = 0;
+ }
+ node->faces = NULL;
+}
+
+
+//=============================================================================
+
+/*
+===========
+FillOutside
+
+===========
+*/
+qboolean FillOutside (node_t *node)
+{
+ int s;
+ vec_t *v;
+ int i;
+ qboolean inside;
+
+ qprintf ("----- FillOutside ----\n");
+
+ if (nofill)
+ {
+ printf ("skipped\n");
+ return false;
+ }
+
+ inside = false;
+ for (i=1 ; i<num_entities ; i++)
+ {
+ if (!VectorCompare(entities[i].origin, vec3_origin))
+ {
+ if (PlaceOccupant (i, entities[i].origin, node))
+ inside = true;
+ }
+ }
+
+ if (!inside)
+ {
+ printf ("Hullnum %i: No entities in empty space -- no filling performed\n", hullnum);
+ return false;
+ }
+
+ s = !(outside_node.portals->nodes[1] == &outside_node);
+
+// first check to see if an occupied leaf is hit
+ outleafs = 0;
+ valid++;
+
+ prevleaknode = NULL;
+
+ if (!hullnum)
+ {
+ leakfile = fopen (pointfilename, "w");
+ if (!leakfile)
+ Error ("Couldn't open %s\n", pointfilename);
+ }
+
+ if (RecursiveFillOutside (outside_node.portals->nodes[s], false))
+ {
+ v = entities[hit_occupied].origin;
+ qprintf ("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
+ qprintf ("reached occupant at: (%4.0f,%4.0f,%4.0f)\n"
+ , v[0], v[1], v[2]);
+ qprintf ("no filling performed\n");
+ if (!hullnum)
+ fclose (leakfile);
+ qprintf ("leak file written to %s\n", pointfilename);
+ qprintf ("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
+ return false;
+ }
+ if (!hullnum)
+ fclose (leakfile);
+
+// now go back and fill things in
+ valid++;
+ RecursiveFillOutside (outside_node.portals->nodes[s], true);
+
+// remove faces from filled in leafs
+ ClearOutFaces (node);
+
+ qprintf ("%4i outleafs\n", outleafs);
+ return true;
+}
+
+
diff --git a/qutils/QBSP/PORTALS.C b/qutils/QBSP/PORTALS.C
new file mode 100644
index 0000000..1286cbb
--- /dev/null
+++ b/qutils/QBSP/PORTALS.C
@@ -0,0 +1,576 @@
+
+#include "bsp5.h"
+
+
+node_t outside_node; // portals outside the world face this
+
+//=============================================================================
+
+/*
+=============
+AddPortalToNodes
+=============
+*/
+void AddPortalToNodes (portal_t *p, node_t *front, node_t *back)
+{
+ if (p->nodes[0] || p->nodes[1])
+ Error ("AddPortalToNode: allready included");
+
+ p->nodes[0] = front;
+ p->next[0] = front->portals;
+ front->portals = p;
+
+ p->nodes[1] = back;
+ p->next[1] = back->portals;
+ back->portals = p;
+}
+
+
+/*
+=============
+RemovePortalFromNode
+=============
+*/
+void RemovePortalFromNode (portal_t *portal, node_t *l)
+{
+ portal_t **pp, *t;
+
+// remove reference to the current portal
+ pp = &l->portals;
+ while (1)
+ {
+ t = *pp;
+ if (!t)
+ Error ("RemovePortalFromNode: portal not in leaf");
+
+ if ( t == portal )
+ break;
+
+ if (t->nodes[0] == l)
+ pp = &t->next[0];
+ else if (t->nodes[1] == l)
+ pp = &t->next[1];
+ else
+ Error ("RemovePortalFromNode: portal not bounding leaf");
+ }
+
+ if (portal->nodes[0] == l)
+ {
+ *pp = portal->next[0];
+ portal->nodes[0] = NULL;
+ }
+ else if (portal->nodes[1] == l)
+ {
+ *pp = portal->next[1];
+ portal->nodes[1] = NULL;
+ }
+}
+
+//============================================================================
+
+void PrintPortal (portal_t *p)
+{
+ int i;
+ winding_t *w;
+
+ w = p->winding;
+ for (i=0 ; i<w->numpoints ; i++)
+ printf ("(%5.0f,%5.0f,%5.0f)\n",w->points[i][0]
+ , w->points[i][1], w->points[i][2]);
+}
+
+/*
+================
+MakeHeadnodePortals
+
+The created portals will face the global outside_node
+================
+*/
+void MakeHeadnodePortals (node_t *node)
+{
+ vec3_t bounds[2];
+ int i, j, n;
+ portal_t *p, *portals[6];
+ plane_t bplanes[6], *pl;
+ int side;
+
+ Draw_ClearWindow ();
+
+// pad with some space so there will never be null volume leafs
+ for (i=0 ; i<3 ; i++)
+ {
+ bounds[0][i] = brushset->mins[i] - SIDESPACE;
+ bounds[1][i] = brushset->maxs[i] + SIDESPACE;
+ }
+
+ outside_node.contents = CONTENTS_SOLID;
+ outside_node.portals = NULL;
+
+ for (i=0 ; i<3 ; i++)
+ for (j=0 ; j<2 ; j++)
+ {
+ n = j*3 + i;
+
+ p = AllocPortal ();
+ portals[n] = p;
+
+ pl = &bplanes[n];
+ memset (pl, 0, sizeof(*pl));
+ if (j)
+ {
+ pl->normal[i] = -1;
+ pl->dist = -bounds[j][i];
+ }
+ else
+ {
+ pl->normal[i] = 1;
+ pl->dist = bounds[j][i];
+ }
+ p->planenum = FindPlane (pl, &side);
+
+ p->winding = BaseWindingForPlane (pl);
+ if (side)
+ AddPortalToNodes (p, &outside_node, node);
+ else
+ AddPortalToNodes (p, node, &outside_node);
+ }
+
+// clip the basewindings by all the other planes
+ for (i=0 ; i<6 ; i++)
+ {
+ for (j=0 ; j<6 ; j++)
+ {
+ if (j == i)
+ continue;
+ portals[i]->winding = ClipWinding (portals[i]->winding, &bplanes[j], true);
+ }
+ }
+}
+
+//============================================================================
+
+void CheckWindingInNode (winding_t *w, node_t *node)
+{
+ int i, j;
+
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ if (w->points[i][j] < node->mins[j] - 1
+ || w->points[i][j] > node->maxs[j] + 1)
+ {
+ printf ("WARNING: CheckWindingInNode: outside\n");
+ return;
+ }
+ }
+}
+
+void CheckWindingArea (winding_t *w)
+{
+ int i;
+ float total, add;
+ vec3_t v1, v2, cross;
+
+ total = 0;
+ for (i=1 ; i<w->numpoints ; i++)
+ {
+ VectorSubtract (w->points[i], w->points[0], v1);
+ VectorSubtract (w->points[i+1], w->points[0], v2);
+ CrossProduct (v1, v2, cross);
+ add = VectorLength (cross);
+ total += add*0.5;
+ }
+ if (total < 16)
+ printf ("WARNING: winding area %f\n", total);
+}
+
+
+void PlaneFromWinding (winding_t *w, plane_t *plane)
+{
+ vec3_t v1, v2;
+
+// calc plane
+ VectorSubtract (w->points[2], w->points[1], v1);
+ VectorSubtract (w->points[0], w->points[1], v2);
+ CrossProduct (v2, v1, plane->normal);
+ VectorNormalize (plane->normal);
+ plane->dist = DotProduct (w->points[0], plane->normal);
+}
+
+void CheckLeafPortalConsistancy (node_t *node)
+{
+ int side, side2;
+ portal_t *p, *p2;
+ plane_t plane, plane2;
+ int i;
+ winding_t *w;
+ float dist;
+
+ side = side2 = 0; // quiet compiler warning
+
+ for (p = node->portals ; p ; p = p->next[side])
+ {
+ if (p->nodes[0] == node)
+ side = 0;
+ else if (p->nodes[1] == node)
+ side = 1;
+ else
+ Error ("CutNodePortals_r: mislinked portal");
+ CheckWindingInNode (p->winding, node);
+ CheckWindingArea (p->winding);
+
+ // check that the side orders are correct
+ plane = planes[p->planenum];
+ PlaneFromWinding (p->winding, &plane2);
+
+ for (p2 = node->portals ; p2 ; p2 = p2->next[side2])
+ {
+ if (p2->nodes[0] == node)
+ side2 = 0;
+ else if (p2->nodes[1] == node)
+ side2 = 1;
+ else
+ Error ("CutNodePortals_r: mislinked portal");
+ w = p2->winding;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ dist = DotProduct (w->points[i], plane.normal) - plane.dist;
+ if ( (side == 0 && dist < -1) || (side == 1 && dist > 1) )
+ {
+ printf ("WARNING: portal siding direction is wrong\n");
+ return;
+ }
+ }
+
+ }
+ }
+}
+
+
+/*
+================
+CutNodePortals_r
+
+================
+*/
+void CutNodePortals_r (node_t *node)
+{
+ plane_t *plane, clipplane;
+ node_t *f, *b, *other_node;
+ portal_t *p, *new_portal, *next_portal;
+ winding_t *w, *frontwinding, *backwinding;
+ int side;
+
+// CheckLeafPortalConsistancy (node);
+
+//
+// seperate the portals on node into it's children
+//
+ if (node->contents)
+ {
+ return; // at a leaf, no more dividing
+ }
+
+ plane = &planes[node->planenum];
+
+ f = node->children[0];
+ b = node->children[1];
+
+//
+// create the new portal by taking the full plane winding for the cutting plane
+// and clipping it by all of the planes from the other portals
+//
+ new_portal = AllocPortal ();
+ new_portal->planenum = node->planenum;
+
+ w = BaseWindingForPlane (&planes[node->planenum]);
+ side = 0; // shut up compiler warning
+ for (p = node->portals ; p ; p = p->next[side])
+ {
+ clipplane = planes[p->planenum];
+ if (p->nodes[0] == node)
+ side = 0;
+ else if (p->nodes[1] == node)
+ {
+ clipplane.dist = -clipplane.dist;
+ VectorSubtract (vec3_origin, clipplane.normal, clipplane.normal);
+ side = 1;
+ }
+ else
+ Error ("CutNodePortals_r: mislinked portal");
+
+ w = ClipWinding (w, &clipplane, true);
+ if (!w)
+ {
+ printf ("WARNING: CutNodePortals_r:new portal was clipped away\n");
+ break;
+ }
+ }
+
+ if (w)
+ {
+ // if the plane was not clipped on all sides, there was an error
+ new_portal->winding = w;
+ AddPortalToNodes (new_portal, f, b);
+ }
+
+//
+// partition the portals
+//
+ for (p = node->portals ; p ; p = next_portal)
+ {
+ if (p->nodes[0] == node)
+ side = 0;
+ else if (p->nodes[1] == node)
+ side = 1;
+ else
+ Error ("CutNodePortals_r: mislinked portal");
+ next_portal = p->next[side];
+
+ other_node = p->nodes[!side];
+ RemovePortalFromNode (p, p->nodes[0]);
+ RemovePortalFromNode (p, p->nodes[1]);
+
+//
+// cut the portal into two portals, one on each side of the cut plane
+//
+ DivideWinding (p->winding, plane, &frontwinding, &backwinding);
+
+ if (!frontwinding)
+ {
+ if (side == 0)
+ AddPortalToNodes (p, b, other_node);
+ else
+ AddPortalToNodes (p, other_node, b);
+ continue;
+ }
+ if (!backwinding)
+ {
+ if (side == 0)
+ AddPortalToNodes (p, f, other_node);
+ else
+ AddPortalToNodes (p, other_node, f);
+ continue;
+ }
+
+ // the winding is split
+ new_portal = AllocPortal ();
+ *new_portal = *p;
+ new_portal->winding = backwinding;
+ FreeWinding (p->winding);
+ p->winding = frontwinding;
+
+ if (side == 0)
+ {
+ AddPortalToNodes (p, f, other_node);
+ AddPortalToNodes (new_portal, b, other_node);
+ }
+ else
+ {
+ AddPortalToNodes (p, other_node, f);
+ AddPortalToNodes (new_portal, other_node, b);
+ }
+ }
+
+DrawLeaf (f,1);
+DrawLeaf (b,2);
+
+ CutNodePortals_r (f);
+ CutNodePortals_r (b);
+
+}
+
+
+/*
+==================
+PortalizeWorld
+
+Builds the exact polyhedrons for the nodes and leafs
+==================
+*/
+void PortalizeWorld (node_t *headnode)
+{
+ qprintf ("----- portalize ----\n");
+
+ MakeHeadnodePortals (headnode);
+ CutNodePortals_r (headnode);
+}
+
+
+/*
+==================
+FreeAllPortals
+
+==================
+*/
+void FreeAllPortals (node_t *node)
+{
+ portal_t *p, *nextp;
+
+ if (!node->contents)
+ {
+ FreeAllPortals (node->children[0]);
+ FreeAllPortals (node->children[1]);
+ }
+
+ for (p=node->portals ; p ; p=nextp)
+ {
+ if (p->nodes[0] == node)
+ nextp = p->next[0];
+ else
+ nextp = p->next[1];
+ RemovePortalFromNode (p, p->nodes[0]);
+ RemovePortalFromNode (p, p->nodes[1]);
+ FreeWinding (p->winding);
+ FreePortal (p);
+ }
+}
+
+/*
+==============================================================================
+
+PORTAL FILE GENERATION
+
+==============================================================================
+*/
+
+#define PORTALFILE "PRT1"
+
+FILE *pf;
+int num_visleafs; // leafs the player can be in
+int num_visportals;
+
+void WriteFloat (FILE *f, vec_t v)
+{
+ if ( fabs(v - Q_rint(v)) < 0.001 )
+ fprintf (f,"%i ",(int)Q_rint(v));
+ else
+ fprintf (f,"%f ",v);
+}
+
+void WritePortalFile_r (node_t *node)
+{
+ int i;
+ portal_t *p;
+ winding_t *w;
+ plane_t *pl, plane2;
+
+ if (!node->contents)
+ {
+ WritePortalFile_r (node->children[0]);
+ WritePortalFile_r (node->children[1]);
+ return;
+ }
+
+ if (node->contents == CONTENTS_SOLID)
+ return;
+
+ for (p = node->portals ; p ; )
+ {
+ w = p->winding;
+ if (w && p->nodes[0] == node
+ && p->nodes[0]->contents == p->nodes[1]->contents)
+ {
+ // write out to the file
+
+ // sometimes planes get turned around when they are very near
+ // the changeover point between different axis. interpret the
+ // plane the same way vis will, and flip the side orders if needed
+ pl = &planes[p->planenum];
+ PlaneFromWinding (w, &plane2);
+ if ( DotProduct (pl->normal, plane2.normal) < 0.99 )
+ { // backwards...
+ fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[1]->visleafnum, p->nodes[0]->visleafnum);
+ }
+ else
+ fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[0]->visleafnum, p->nodes[1]->visleafnum);
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ fprintf (pf,"(");
+ WriteFloat (pf, w->points[i][0]);
+ WriteFloat (pf, w->points[i][1]);
+ WriteFloat (pf, w->points[i][2]);
+ fprintf (pf,") ");
+ }
+ fprintf (pf,"\n");
+ }
+
+ if (p->nodes[0] == node)
+ p = p->next[0];
+ else
+ p = p->next[1];
+ }
+
+}
+
+/*
+================
+NumberLeafs_r
+================
+*/
+void NumberLeafs_r (node_t *node)
+{
+ portal_t *p;
+
+ if (!node->contents)
+ { // decision node
+ node->visleafnum = -99;
+ NumberLeafs_r (node->children[0]);
+ NumberLeafs_r (node->children[1]);
+ return;
+ }
+
+ Draw_ClearWindow ();
+ DrawLeaf (node, 1);
+
+ if (node->contents == CONTENTS_SOLID)
+ { // solid block, viewpoint never inside
+ node->visleafnum = -1;
+ return;
+ }
+
+ node->visleafnum = num_visleafs++;
+
+ for (p = node->portals ; p ; )
+ {
+ if (p->nodes[0] == node) // only write out from first leaf
+ {
+ if (p->nodes[0]->contents == p->nodes[1]->contents)
+ num_visportals++;
+ p = p->next[0];
+ }
+ else
+ p = p->next[1];
+ }
+
+}
+
+
+/*
+================
+WritePortalfile
+================
+*/
+void WritePortalfile (node_t *headnode)
+{
+// set the visleafnum field in every leaf and count the total number of portals
+ num_visleafs = 0;
+ num_visportals = 0;
+ NumberLeafs_r (headnode);
+
+// write the file
+ printf ("writing %s\n", portfilename);
+ pf = fopen (portfilename, "w");
+ if (!pf)
+ Error ("Error opening %s", portfilename);
+
+ fprintf (pf, "%s\n", PORTALFILE);
+ fprintf (pf, "%i\n", num_visleafs);
+ fprintf (pf, "%i\n", num_visportals);
+
+ WritePortalFile_r (headnode);
+
+ fclose (pf);
+}
+
+
diff --git a/qutils/QBSP/QBSP.C b/qutils/QBSP/QBSP.C
new file mode 100644
index 0000000..064fb3a
--- /dev/null
+++ b/qutils/QBSP/QBSP.C
@@ -0,0 +1,1030 @@
+// bsp5.c
+
+#include "bsp5.h"
+
+//
+// command line flags
+//
+qboolean drawflag;
+qboolean nofill;
+qboolean notjunc;
+qboolean noclip;
+qboolean onlyents;
+qboolean verbose = true;
+qboolean allverbose;
+qboolean usehulls;
+
+int subdivide_size = 240;
+
+brushset_t *brushset;
+
+int valid;
+
+char bspfilename[1024];
+char pointfilename[1024];
+char portfilename[1024];
+char hullfilename[1024];
+
+char *argv0; // changed after fork();
+
+qboolean worldmodel;
+
+int hullnum;
+
+//===========================================================================
+
+void qprintf (char *fmt, ...)
+{
+ va_list argptr;
+
+ if (!verbose)
+ return;
+
+ va_start (argptr, fmt);
+ vprintf (fmt,argptr);
+ va_end (argptr);
+}
+
+/*
+=================
+BaseWindingForPlane
+=================
+*/
+winding_t *BaseWindingForPlane (plane_t *p)
+{
+ int i, x;
+ vec_t max, v;
+ vec3_t org, vright, vup;
+ winding_t *w;
+
+// find the major axis
+
+ max = -BOGUS_RANGE;
+ x = -1;
+ for (i=0 ; i<3; i++)
+ {
+ v = fabs(p->normal[i]);
+ if (v > max)
+ {
+ x = i;
+ max = v;
+ }
+ }
+ if (x==-1)
+ Error ("BaseWindingForPlane: no axis found");
+
+ VectorCopy (vec3_origin, vup);
+ switch (x)
+ {
+ case 0:
+ case 1:
+ vup[2] = 1;
+ break;
+ case 2:
+ vup[0] = 1;
+ break;
+ }
+
+ v = DotProduct (vup, p->normal);
+ VectorMA (vup, -v, p->normal, vup);
+ VectorNormalize (vup);
+
+ VectorScale (p->normal, p->dist, org);
+
+ CrossProduct (vup, p->normal, vright);
+
+ VectorScale (vup, 8192, vup);
+ VectorScale (vright, 8192, vright);
+
+// project a really big axis aligned box onto the plane
+ w = NewWinding (4);
+
+ VectorSubtract (org, vright, w->points[0]);
+ VectorAdd (w->points[0], vup, w->points[0]);
+
+ VectorAdd (org, vright, w->points[1]);
+ VectorAdd (w->points[1], vup, w->points[1]);
+
+ VectorAdd (org, vright, w->points[2]);
+ VectorSubtract (w->points[2], vup, w->points[2]);
+
+ VectorSubtract (org, vright, w->points[3]);
+ VectorSubtract (w->points[3], vup, w->points[3]);
+
+ w->numpoints = 4;
+
+ return w;
+}
+
+
+
+/*
+==================
+CopyWinding
+==================
+*/
+winding_t *CopyWinding (winding_t *w)
+{
+ int size;
+ winding_t *c;
+
+ size = (int)((winding_t *)0)->points[w->numpoints];
+ c = malloc (size);
+ memcpy (c, w, size);
+ return c;
+}
+
+
+
+/*
+==================
+CheckWinding
+
+Check for possible errors
+==================
+*/
+void CheckWinding (winding_t *w)
+{
+}
+
+
+/*
+==================
+ClipWinding
+
+Clips the winding to the plane, returning the new winding on the positive side
+Frees the input winding.
+If keepon is true, an exactly on-plane winding will be saved, otherwise
+it will be clipped away.
+==================
+*/
+winding_t *ClipWinding (winding_t *in, plane_t *split, qboolean keepon)
+{
+ vec_t dists[MAX_POINTS_ON_WINDING];
+ int sides[MAX_POINTS_ON_WINDING];
+ int counts[3];
+ vec_t dot;
+ int i, j;
+ vec_t *p1, *p2;
+ vec3_t mid;
+ winding_t *neww;
+ int maxpts;
+
+ counts[0] = counts[1] = counts[2] = 0;
+
+// determine sides for each point
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ dot = DotProduct (in->points[i], split->normal);
+ dot -= split->dist;
+ dists[i] = dot;
+ if (dot > ON_EPSILON)
+ sides[i] = SIDE_FRONT;
+ else if (dot < -ON_EPSILON)
+ sides[i] = SIDE_BACK;
+ else
+ {
+ sides[i] = SIDE_ON;
+ }
+ counts[sides[i]]++;
+ }
+ sides[i] = sides[0];
+ dists[i] = dists[0];
+
+ if (keepon && !counts[0] && !counts[1])
+ return in;
+
+ if (!counts[0])
+ {
+ FreeWinding (in);
+ return NULL;
+ }
+ if (!counts[1])
+ return in;
+
+ maxpts = in->numpoints+4; // can't use counts[0]+2 because
+ // of fp grouping errors
+ neww = NewWinding (maxpts);
+
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ p1 = in->points[i];
+
+ if (sides[i] == SIDE_ON)
+ {
+ VectorCopy (p1, neww->points[neww->numpoints]);
+ neww->numpoints++;
+ continue;
+ }
+
+ if (sides[i] == SIDE_FRONT)
+ {
+ VectorCopy (p1, neww->points[neww->numpoints]);
+ neww->numpoints++;
+ }
+
+ if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
+ continue;
+
+ // generate a split point
+ p2 = in->points[(i+1)%in->numpoints];
+
+ dot = dists[i] / (dists[i]-dists[i+1]);
+ for (j=0 ; j<3 ; j++)
+ { // avoid round off error when possible
+ if (split->normal[j] == 1)
+ mid[j] = split->dist;
+ else if (split->normal[j] == -1)
+ mid[j] = -split->dist;
+ else
+ mid[j] = p1[j] + dot*(p2[j]-p1[j]);
+ }
+
+ VectorCopy (mid, neww->points[neww->numpoints]);
+ neww->numpoints++;
+ }
+
+ if (neww->numpoints > maxpts)
+ Error ("ClipWinding: points exceeded estimate");
+
+// free the original winding
+ FreeWinding (in);
+
+ return neww;
+}
+
+
+/*
+==================
+DivideWinding
+
+Divides a winding by a plane, producing one or two windings. The
+original winding is not damaged or freed. If only on one side, the
+returned winding will be the input winding. If on both sides, two
+new windings will be created.
+==================
+*/
+void DivideWinding (winding_t *in, plane_t *split, winding_t **front, winding_t **back)
+{
+ vec_t dists[MAX_POINTS_ON_WINDING];
+ int sides[MAX_POINTS_ON_WINDING];
+ int counts[3];
+ vec_t dot;
+ int i, j;
+ vec_t *p1, *p2;
+ vec3_t mid;
+ winding_t *f, *b;
+ int maxpts;
+
+ counts[0] = counts[1] = counts[2] = 0;
+
+// determine sides for each point
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ dot = DotProduct (in->points[i], split->normal);
+ dot -= split->dist;
+ dists[i] = dot;
+ if (dot > ON_EPSILON)
+ sides[i] = SIDE_FRONT;
+ else if (dot < -ON_EPSILON)
+ sides[i] = SIDE_BACK;
+ else
+ {
+ sides[i] = SIDE_ON;
+ }
+ counts[sides[i]]++;
+ }
+ sides[i] = sides[0];
+ dists[i] = dists[0];
+
+ *front = *back = NULL;
+
+ if (!counts[0])
+ {
+ *back = in;
+ return;
+ }
+ if (!counts[1])
+ {
+ *front = in;
+ return;
+ }
+
+ maxpts = in->numpoints+4; // can't use counts[0]+2 because
+ // of fp grouping errors
+
+ *front = f = NewWinding (maxpts);
+ *back = b = NewWinding (maxpts);
+
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ p1 = in->points[i];
+
+ if (sides[i] == SIDE_ON)
+ {
+ VectorCopy (p1, f->points[f->numpoints]);
+ f->numpoints++;
+ VectorCopy (p1, b->points[b->numpoints]);
+ b->numpoints++;
+ continue;
+ }
+
+ if (sides[i] == SIDE_FRONT)
+ {
+ VectorCopy (p1, f->points[f->numpoints]);
+ f->numpoints++;
+ }
+ if (sides[i] == SIDE_BACK)
+ {
+ VectorCopy (p1, b->points[b->numpoints]);
+ b->numpoints++;
+ }
+
+ if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
+ continue;
+
+ // generate a split point
+ p2 = in->points[(i+1)%in->numpoints];
+
+ dot = dists[i] / (dists[i]-dists[i+1]);
+ for (j=0 ; j<3 ; j++)
+ { // avoid round off error when possible
+ if (split->normal[j] == 1)
+ mid[j] = split->dist;
+ else if (split->normal[j] == -1)
+ mid[j] = -split->dist;
+ else
+ mid[j] = p1[j] + dot*(p2[j]-p1[j]);
+ }
+
+ VectorCopy (mid, f->points[f->numpoints]);
+ f->numpoints++;
+ VectorCopy (mid, b->points[b->numpoints]);
+ b->numpoints++;
+ }
+
+ if (f->numpoints > maxpts || b->numpoints > maxpts)
+ Error ("ClipWinding: points exceeded estimate");
+}
+
+
+//===========================================================================
+
+int c_activefaces, c_peakfaces;
+int c_activesurfaces, c_peaksurfaces;
+int c_activewindings, c_peakwindings;
+int c_activeportals, c_peakportals;
+
+void PrintMemory (void)
+{
+ printf ("faces : %6i (%6i)\n", c_activefaces, c_peakfaces);
+ printf ("surfaces: %6i (%6i)\n", c_activesurfaces, c_peaksurfaces);
+ printf ("windings: %6i (%6i)\n", c_activewindings, c_peakwindings);
+ printf ("portals : %6i (%6i)\n", c_activeportals, c_peakportals);
+}
+
+/*
+==================
+NewWinding
+==================
+*/
+winding_t *NewWinding (int points)
+{
+ winding_t *w;
+ int size;
+
+ if (points > MAX_POINTS_ON_WINDING)
+ Error ("NewWinding: %i points", points);
+
+ c_activewindings++;
+ if (c_activewindings > c_peakwindings)
+ c_peakwindings = c_activewindings;
+
+ size = (int)((winding_t *)0)->points[points];
+ w = malloc (size);
+ memset (w, 0, size);
+
+ return w;
+}
+
+
+void FreeWinding (winding_t *w)
+{
+ c_activewindings--;
+ free (w);
+}
+
+
+
+/*
+===========
+AllocFace
+===========
+*/
+face_t *AllocFace (void)
+{
+ face_t *f;
+
+ c_activefaces++;
+ if (c_activefaces > c_peakfaces)
+ c_peakfaces = c_activefaces;
+
+ f = malloc (sizeof(face_t));
+ memset (f, 0, sizeof(face_t));
+ f->planenum = -1;
+
+ return f;
+}
+
+
+void FreeFace (face_t *f)
+{
+ c_activefaces--;
+// memset (f,0xff,sizeof(face_t));
+ free (f);
+}
+
+
+/*
+===========
+AllocSurface
+===========
+*/
+surface_t *AllocSurface (void)
+{
+ surface_t *s;
+
+ s = malloc (sizeof(surface_t));
+ memset (s, 0, sizeof(surface_t));
+
+ c_activesurfaces++;
+ if (c_activesurfaces > c_peaksurfaces)
+ c_peaksurfaces = c_activesurfaces;
+
+ return s;
+}
+
+void FreeSurface (surface_t *s)
+{
+ c_activesurfaces--;
+ free (s);
+}
+
+/*
+===========
+AllocPortal
+===========
+*/
+portal_t *AllocPortal (void)
+{
+ portal_t *p;
+
+ c_activeportals++;
+ if (c_activeportals > c_peakportals)
+ c_peakportals = c_activeportals;
+
+ p = malloc (sizeof(portal_t));
+ memset (p, 0, sizeof(portal_t));
+
+ return p;
+}
+
+void FreePortal (portal_t *p)
+{
+ c_activeportals--;
+ free (p);
+}
+
+
+/*
+===========
+AllocNode
+===========
+*/
+node_t *AllocNode (void)
+{
+ node_t *n;
+
+ n = malloc (sizeof(node_t));
+ memset (n, 0, sizeof(node_t));
+
+ return n;
+}
+
+/*
+===========
+AllocBrush
+===========
+*/
+brush_t *AllocBrush (void)
+{
+ brush_t *b;
+
+ b = malloc (sizeof(brush_t));
+ memset (b, 0, sizeof(brush_t));
+
+ return b;
+}
+
+//===========================================================================
+
+/*
+===============
+ProcessEntity
+===============
+*/
+void ProcessEntity (int entnum)
+{
+ entity_t *ent;
+ char mod[80];
+ surface_t *surfs;
+ node_t *nodes;
+ brushset_t *bs;
+
+
+ ent = &entities[entnum];
+ if (!ent->brushes)
+ return; // non-bmodel entity
+
+ if (entnum > 0)
+ {
+ worldmodel = false;
+ if (entnum == 1)
+ qprintf ("--- Internal Entities ---\n");
+ sprintf (mod, "*%i", nummodels);
+ if (verbose)
+ PrintEntity (ent);
+
+ if (hullnum == 0)
+ printf ("MODEL: %s\n", mod);
+ SetKeyValue (ent, "model", mod);
+ }
+ else
+ worldmodel = true;
+
+
+//
+// take the brush_ts and clip off all overlapping and contained faces,
+// leaving a perfect skin of the model with no hidden faces
+//
+ bs = Brush_LoadEntity (ent, hullnum);
+
+ if (!bs->brushes)
+ {
+ PrintEntity (ent);
+ Error ("Entity with no valid brushes");
+ }
+
+ brushset = bs;
+ surfs = CSGFaces (bs);
+
+ if (hullnum != 0)
+ {
+ nodes = SolidBSP (surfs, true);
+ if (entnum == 0 && !nofill) // assume non-world bmodels are simple
+ {
+ PortalizeWorld (nodes);
+ if (FillOutside (nodes))
+ {
+ surfs = GatherNodeFaces (nodes);
+ nodes = SolidBSP (surfs, false); // make a really good tree
+ }
+ FreeAllPortals (nodes);
+ }
+ WriteNodePlanes (nodes);
+ WriteClipNodes (nodes);
+ BumpModel (hullnum);
+ }
+ else
+ {
+ //
+ // SolidBSP generates a node tree
+ //
+ // if not the world, make a good tree first
+ // the world is just going to make a bad tree
+ // because the outside filling will force a regeneration later
+ nodes = SolidBSP (surfs, entnum == 0);
+
+ //
+ // build all the portals in the bsp tree
+ // some portals are solid polygons, and some are paths to other leafs
+ //
+ if (entnum == 0 && !nofill) // assume non-world bmodels are simple
+ {
+ PortalizeWorld (nodes);
+
+ if (FillOutside (nodes))
+ {
+ FreeAllPortals (nodes);
+
+ // get the remaining faces together into surfaces again
+ surfs = GatherNodeFaces (nodes);
+
+ // merge polygons
+ MergeAll (surfs);
+
+ // make a really good tree
+ nodes = SolidBSP (surfs, false);
+
+ // make the real portals for vis tracing
+ PortalizeWorld (nodes);
+
+ // save portal file for vis tracing
+ WritePortalfile (nodes);
+
+ // fix tjunctions
+ tjunc (nodes);
+ }
+ FreeAllPortals (nodes);
+ }
+
+ WriteNodePlanes (nodes);
+ MakeFaceEdges (nodes);
+ WriteDrawNodes (nodes);
+ }
+}
+
+/*
+=================
+UpdateEntLump
+
+=================
+*/
+void UpdateEntLump (void)
+{
+ int m, entnum;
+ char mod[80];
+
+ m = 1;
+ for (entnum = 1 ; entnum < num_entities ; entnum++)
+ {
+ if (!entities[entnum].brushes)
+ continue;
+ sprintf (mod, "*%i", m);
+ SetKeyValue (&entities[entnum], "model", mod);
+ m++;
+ }
+
+ printf ("Updating entities lump...\n");
+ LoadBSPFile (bspfilename);
+ WriteEntitiesToString();
+ WriteBSPFile (bspfilename);
+}
+
+/*
+=================
+WriteClipHull
+
+Write the clipping hull out to a text file so the parent process can get it
+=================
+*/
+void WriteClipHull (void)
+{
+ FILE *f;
+ int i;
+ dplane_t *p;
+ dclipnode_t *d;
+
+ hullfilename[strlen(hullfilename)-1] = '0' + hullnum;
+
+ qprintf ("---- WriteClipHull ----\n");
+ qprintf ("Writing %s\n", hullfilename);
+
+ f = fopen (hullfilename, "w");
+ if (!f)
+ Error ("Couldn't open %s", hullfilename);
+
+ fprintf (f, "%i\n", nummodels);
+
+ for (i=0 ; i<nummodels ; i++)
+ fprintf (f, "%i\n", dmodels[i].headnode[hullnum]);
+
+ fprintf (f, "\n%i\n", numclipnodes);
+
+ for (i=0 ; i<numclipnodes ; i++)
+ {
+ d = &dclipnodes[i];
+ p = &dplanes[d->planenum];
+ // the node number is only written out for human readability
+ fprintf (f, "%5i : %f %f %f %f : %5i %5i\n", i, p->normal[0], p->normal[1], p->normal[2], p->dist, d->children[0], d->children[1]);
+ }
+
+ fclose (f);
+}
+
+/*
+=================
+ReadClipHull
+
+Read the files written out by the child processes
+=================
+*/
+void ReadClipHull (int hullnum)
+{
+ FILE *f;
+ int i, j, n;
+ int firstclipnode;
+ dplane_t p;
+ dclipnode_t *d;
+ int c1, c2;
+ float f1, f2, f3, f4;
+ int junk;
+ vec3_t norm;
+
+ hullfilename[strlen(hullfilename)-1] = '0' + hullnum;
+
+ f = fopen (hullfilename, "r");
+ if (!f)
+ Error ("Couldn't open %s", hullfilename);
+
+ if (fscanf (f,"%i\n", &n) != 1)
+ Error ("Error parsing %s", hullfilename);
+
+ if (n != nummodels)
+ Error ("ReadClipHull: hull had %i models, base had %i", n, nummodels);
+
+ for (i=0 ; i<n ; i++)
+ {
+ fscanf (f, "%i\n", &j);
+ dmodels[i].headnode[hullnum] = numclipnodes + j;
+ }
+
+
+ fscanf (f,"\n%i\n", &n);
+ firstclipnode = numclipnodes;
+
+ for (i=0 ; i<n ; i++)
+ {
+ if (numclipnodes == MAX_MAP_CLIPNODES)
+ Error ("ReadClipHull: MAX_MAP_CLIPNODES");
+ d = &dclipnodes[numclipnodes];
+ numclipnodes++;
+ if (fscanf (f,"%i : %f %f %f %f : %i %i\n", &junk, &f1, &f2, &f3, &f4, &c1, &c2) != 7)
+ Error ("Error parsing %s", hullfilename);
+
+
+ p.normal[0] = f1;
+ p.normal[1] = f2;
+ p.normal[2] = f3;
+ p.dist = f4;
+
+ norm[0] = f1; norm[1] = f2; norm[2] = f3; // vec_t precision
+ p.type = PlaneTypeForNormal (norm);
+
+ d->children[0] = c1 >= 0 ? c1 + firstclipnode : c1;
+ d->children[1] = c2 >= 0 ? c2 + firstclipnode : c2;
+ d->planenum = FindFinalPlane (&p);
+ }
+
+}
+
+/*
+=================
+CreateSingleHull
+
+=================
+*/
+void CreateSingleHull (void)
+{
+ int entnum;
+
+// for each entity in the map file that has geometry
+ for (entnum = 0 ; entnum < num_entities ; entnum++)
+ {
+ ProcessEntity (entnum);
+ if (!allverbose)
+ verbose = false; // don't print rest of entities
+ }
+
+ if (hullnum)
+ WriteClipHull ();
+}
+
+/*
+=================
+CreateHulls
+
+=================
+*/
+void CreateHulls (void)
+{
+// commanded to create a single hull only
+ if (hullnum)
+ {
+ CreateSingleHull ();
+ exit (0);
+ }
+
+// commanded to use the allready existing hulls 1 and 2
+ if (usehulls)
+ {
+ CreateSingleHull ();
+ return;
+ }
+
+// commanded to ignore the hulls altogether
+ if (noclip)
+ {
+ CreateSingleHull ();
+ return;
+ }
+
+
+// create all the hulls
+
+#ifdef __alpha
+ printf ("forking hull processes...\n");
+// fork a process for each clipping hull
+ fflush (stdout);
+ if (!fork ())
+ {
+ hullnum = 1;
+ verbose = false;
+ drawflag = false;
+ sprintf (argv0, "HUL%i", hullnum);
+ }
+ else if (!fork ())
+ {
+ hullnum = 2;
+ verbose = false;
+ drawflag = false;
+ sprintf (argv0, "HUL%i", hullnum);
+ }
+ CreateSingleHull ();
+
+ if (hullnum)
+ exit (0);
+
+ wait (NULL); // wait for clip hull process to finish
+ wait (NULL); // wait for clip hull process to finish
+
+#else
+// create the hulls sequentially
+ printf ("building hulls sequentially...\n");
+
+ hullnum = 1;
+ CreateSingleHull ();
+
+ nummodels = 0;
+ numplanes = 0;
+ numclipnodes = 0;
+ hullnum = 2;
+ CreateSingleHull ();
+
+ nummodels = 0;
+ numplanes = 0;
+ numclipnodes = 0;
+ hullnum = 0;
+ CreateSingleHull ();
+#endif
+
+}
+
+/*
+=================
+ProcessFile
+
+=================
+*/
+void ProcessFile (char *sourcebase, char *bspfilename1)
+{
+// create filenames
+ strcpy (bspfilename, bspfilename1);
+ StripExtension (bspfilename);
+ strcat (bspfilename, ".bsp");
+
+ strcpy (hullfilename, bspfilename1);
+ StripExtension (hullfilename);
+ strcat (hullfilename, ".h0");
+
+ strcpy (portfilename, bspfilename1);
+ StripExtension (portfilename);
+ strcat (portfilename, ".prt");
+
+ strcpy (pointfilename, bspfilename1);
+ StripExtension (pointfilename);
+ strcat (pointfilename, ".pts");
+
+ if (!onlyents)
+ {
+ remove (bspfilename);
+ if (!usehulls)
+ {
+ hullfilename[strlen(hullfilename)-1] = '1';
+ remove (hullfilename);
+ hullfilename[strlen(hullfilename)-1] = '2';
+ remove (hullfilename);
+ }
+ remove (portfilename);
+ remove (pointfilename);
+ }
+
+// load brushes and entities
+ LoadMapFile (sourcebase);
+ if (onlyents)
+ {
+ UpdateEntLump ();
+ return;
+ }
+
+// init the tables to be shared by all models
+ BeginBSPFile ();
+
+// the clipping hulls will be written out to text files by forked processes
+ CreateHulls ();
+
+ ReadClipHull (1);
+ ReadClipHull (2);
+
+ WriteEntitiesToString();
+ FinishBSPFile ();
+}
+
+
+/*
+==================
+main
+
+==================
+*/
+int main (int argc, char **argv)
+{
+ int i;
+ double start, end;
+ char sourcename[1024];
+ char destname[1024];
+
+// malloc_debug (15);
+
+//
+// check command line flags
+//
+ for (i=1 ; i<argc ; i++)
+ {
+ if (argv[i][0] != '-')
+ break;
+ else if (!strcmp (argv[i],"-draw"))
+ drawflag = true;
+ else if (!strcmp (argv[i],"-notjunc"))
+ notjunc = true;
+ else if (!strcmp (argv[i],"-nofill"))
+ nofill = true;
+ else if (!strcmp (argv[i],"-noclip"))
+ noclip = true;
+ else if (!strcmp (argv[i],"-onlyents"))
+ onlyents = true;
+ else if (!strcmp (argv[i],"-verbose"))
+ allverbose = true;
+ else if (!strcmp (argv[i],"-usehulls"))
+ usehulls = true; // don't fork -- use the existing files
+ else if (!strcmp (argv[i],"-hullnum"))
+ {
+ hullnum = atoi(argv[i+1]);
+ i++;
+ }
+ else if (!strcmp (argv[i],"-subdivide"))
+ {
+ subdivide_size = atoi(argv[i+1]);
+ i++;
+ }
+ else
+ Error ("qbsp: Unknown option '%s'", argv[i]);
+ }
+
+ if (i != argc - 2 && i != argc - 1)
+ Error ("usage: qbsp [options] sourcefile [destfile]\noptions: -nojunc -nofill -threads[124] -draw -onlyents -verbose -proj <projectpath>");
+
+ SetQdirFromPath (argv[i]);
+
+//
+// let forked processes change name for ps status
+//
+ argv0 = argv[0];
+
+
+//
+// create destination name if not specified
+//
+ strcpy (sourcename, argv[i]);
+ DefaultExtension (sourcename, ".map");
+
+ if (i != argc - 2)
+ {
+ strcpy (destname, argv[i]);
+ StripExtension (destname);
+ strcat (destname, ".bsp");
+ printf ("outputfile: %s\n", destname);
+ }
+ else
+ strcpy (destname, argv[i+1]);
+
+//
+// do it!
+//
+ start = I_FloatTime ();
+ ProcessFile (sourcename, destname);
+ end = I_FloatTime ();
+ printf ("%5.1f seconds elapsed\n", end-start);
+
+ return 0;
+}
diff --git a/qutils/QBSP/QBSP.MAK b/qutils/QBSP/QBSP.MAK
new file mode 100644
index 0000000..ac0e9ae
--- /dev/null
+++ b/qutils/QBSP/QBSP.MAK
@@ -0,0 +1,527 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+!IF "$(CFG)" == ""
+CFG=qbsp - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to qbsp - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "qbsp - Win32 Release" && "$(CFG)" != "qbsp - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "qbsp.mak" CFG="qbsp - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "qbsp - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "qbsp - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "qbsp - Win32 Debug"
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "qbsp - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\qbsp.exe"
+
+CLEAN :
+ -@erase ".\Release\qbsp.exe"
+ -@erase ".\Release\mathlib.obj"
+ -@erase ".\Release\solidbsp.obj"
+ -@erase ".\Release\portals.obj"
+ -@erase ".\Release\surfaces.obj"
+ -@erase ".\Release\nodraw.obj"
+ -@erase ".\Release\cmdlib.obj"
+ -@erase ".\Release\csg4.obj"
+ -@erase ".\Release\brush.obj"
+ -@erase ".\Release\merge.obj"
+ -@erase ".\Release\map.obj"
+ -@erase ".\Release\region.obj"
+ -@erase ".\Release\bspfile.obj"
+ -@erase ".\Release\writebsp.obj"
+ -@erase ".\Release\outside.obj"
+ -@erase ".\Release\qbsp.obj"
+ -@erase ".\Release\tjunc.obj"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /ML /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/qbsp.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\Release/
+CPP_SBRS=
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qbsp.bsc"
+BSC32_SBRS=
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:console /incremental:no\
+ /pdb:"$(OUTDIR)/qbsp.pdb" /machine:I386 /out:"$(OUTDIR)/qbsp.exe"
+LINK32_OBJS= \
+ ".\Release\mathlib.obj" \
+ ".\Release\solidbsp.obj" \
+ ".\Release\portals.obj" \
+ ".\Release\surfaces.obj" \
+ ".\Release\nodraw.obj" \
+ ".\Release\cmdlib.obj" \
+ ".\Release\csg4.obj" \
+ ".\Release\brush.obj" \
+ ".\Release\merge.obj" \
+ ".\Release\map.obj" \
+ ".\Release\region.obj" \
+ ".\Release\bspfile.obj" \
+ ".\Release\writebsp.obj" \
+ ".\Release\outside.obj" \
+ ".\Release\qbsp.obj" \
+ ".\Release\tjunc.obj"
+
+"$(OUTDIR)\qbsp.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "qbsp - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\qbsp.exe"
+
+CLEAN :
+ -@erase ".\Debug\vc40.pdb"
+ -@erase ".\Debug\vc40.idb"
+ -@erase ".\Debug\qbsp.exe"
+ -@erase ".\Debug\region.obj"
+ -@erase ".\Debug\mathlib.obj"
+ -@erase ".\Debug\csg4.obj"
+ -@erase ".\Debug\portals.obj"
+ -@erase ".\Debug\surfaces.obj"
+ -@erase ".\Debug\tjunc.obj"
+ -@erase ".\Debug\nodraw.obj"
+ -@erase ".\Debug\outside.obj"
+ -@erase ".\Debug\map.obj"
+ -@erase ".\Debug\bspfile.obj"
+ -@erase ".\Debug\solidbsp.obj"
+ -@erase ".\Debug\brush.obj"
+ -@erase ".\Debug\merge.obj"
+ -@erase ".\Debug\qbsp.obj"
+ -@erase ".\Debug\cmdlib.obj"
+ -@erase ".\Debug\writebsp.obj"
+ -@erase ".\Debug\qbsp.ilk"
+ -@erase ".\Debug\qbsp.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MLd /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/qbsp.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\Debug/
+CPP_SBRS=
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qbsp.bsc"
+BSC32_SBRS=
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:console /incremental:yes\
+ /pdb:"$(OUTDIR)/qbsp.pdb" /debug /machine:I386 /out:"$(OUTDIR)/qbsp.exe"
+LINK32_OBJS= \
+ ".\Debug\region.obj" \
+ ".\Debug\mathlib.obj" \
+ ".\Debug\csg4.obj" \
+ ".\Debug\portals.obj" \
+ ".\Debug\surfaces.obj" \
+ ".\Debug\tjunc.obj" \
+ ".\Debug\nodraw.obj" \
+ ".\Debug\outside.obj" \
+ ".\Debug\map.obj" \
+ ".\Debug\bspfile.obj" \
+ ".\Debug\solidbsp.obj" \
+ ".\Debug\brush.obj" \
+ ".\Debug\merge.obj" \
+ ".\Debug\qbsp.obj" \
+ ".\Debug\cmdlib.obj" \
+ ".\Debug\writebsp.obj"
+
+"$(OUTDIR)\qbsp.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+################################################################################
+# Begin Target
+
+# Name "qbsp - Win32 Release"
+# Name "qbsp - Win32 Debug"
+
+!IF "$(CFG)" == "qbsp - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qbsp - Win32 Debug"
+
+!ENDIF
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\writebsp.c
+DEP_CPP_WRITE=\
+ ".\bsp5.h"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+ ".\..\common\bspfile.h"\
+ ".\map.h"\
+
+
+"$(INTDIR)\writebsp.obj" : $(SOURCE) $(DEP_CPP_WRITE) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\tjunc.c
+DEP_CPP_TJUNC=\
+ ".\bsp5.h"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+ ".\..\common\bspfile.h"\
+ ".\map.h"\
+
+
+"$(INTDIR)\tjunc.obj" : $(SOURCE) $(DEP_CPP_TJUNC) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\surfaces.c
+DEP_CPP_SURFA=\
+ ".\bsp5.h"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+ ".\..\common\bspfile.h"\
+ ".\map.h"\
+
+
+"$(INTDIR)\surfaces.obj" : $(SOURCE) $(DEP_CPP_SURFA) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\solidbsp.c
+DEP_CPP_SOLID=\
+ ".\bsp5.h"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+ ".\..\common\bspfile.h"\
+ ".\map.h"\
+
+
+"$(INTDIR)\solidbsp.obj" : $(SOURCE) $(DEP_CPP_SOLID) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\region.c
+DEP_CPP_REGIO=\
+ ".\bsp5.h"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+ ".\..\common\bspfile.h"\
+ ".\map.h"\
+
+
+"$(INTDIR)\region.obj" : $(SOURCE) $(DEP_CPP_REGIO) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp.c
+DEP_CPP_QBSP_=\
+ ".\bsp5.h"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+ ".\..\common\bspfile.h"\
+ ".\map.h"\
+
+
+"$(INTDIR)\qbsp.obj" : $(SOURCE) $(DEP_CPP_QBSP_) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\portals.c
+DEP_CPP_PORTA=\
+ ".\bsp5.h"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+ ".\..\common\bspfile.h"\
+ ".\map.h"\
+
+
+"$(INTDIR)\portals.obj" : $(SOURCE) $(DEP_CPP_PORTA) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\outside.c
+DEP_CPP_OUTSI=\
+ ".\bsp5.h"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+ ".\..\common\bspfile.h"\
+ ".\map.h"\
+
+
+"$(INTDIR)\outside.obj" : $(SOURCE) $(DEP_CPP_OUTSI) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\nodraw.c
+DEP_CPP_NODRA=\
+ ".\bsp5.h"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+ ".\..\common\bspfile.h"\
+ ".\map.h"\
+
+
+"$(INTDIR)\nodraw.obj" : $(SOURCE) $(DEP_CPP_NODRA) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\merge.c
+DEP_CPP_MERGE=\
+ ".\bsp5.h"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+ ".\..\common\bspfile.h"\
+ ".\map.h"\
+
+
+"$(INTDIR)\merge.obj" : $(SOURCE) $(DEP_CPP_MERGE) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\map.c
+DEP_CPP_MAP_C=\
+ ".\bsp5.h"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+ ".\..\common\bspfile.h"\
+ ".\map.h"\
+
+
+"$(INTDIR)\map.obj" : $(SOURCE) $(DEP_CPP_MAP_C) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\csg4.c
+DEP_CPP_CSG4_=\
+ ".\bsp5.h"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+ ".\..\common\bspfile.h"\
+ ".\map.h"\
+
+
+"$(INTDIR)\csg4.obj" : $(SOURCE) $(DEP_CPP_CSG4_) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\brush.c
+DEP_CPP_BRUSH=\
+ ".\bsp5.h"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+ ".\..\common\bspfile.h"\
+ ".\map.h"\
+
+
+"$(INTDIR)\brush.obj" : $(SOURCE) $(DEP_CPP_BRUSH) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\bspfile.c
+DEP_CPP_BSPFI=\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+ ".\..\common\bspfile.h"\
+
+
+"$(INTDIR)\bspfile.obj" : $(SOURCE) $(DEP_CPP_BSPFI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\cmdlib.c
+DEP_CPP_CMDLI=\
+ ".\..\common\cmdlib.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+ {$(INCLUDE)}"\sys\STAT.H"\
+
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\mathlib.c
+DEP_CPP_MATHL=\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\mathlib.h"\
+
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\mathlib.h
+
+!IF "$(CFG)" == "qbsp - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qbsp - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\cmdlib.h
+
+!IF "$(CFG)" == "qbsp - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qbsp - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\bspfile.h
+
+!IF "$(CFG)" == "qbsp - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qbsp - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+# End Target
+# End Project
+################################################################################
diff --git a/qutils/QBSP/QBSP.MDP b/qutils/QBSP/QBSP.MDP
new file mode 100644
index 0000000..4cdb631
--- /dev/null
+++ b/qutils/QBSP/QBSP.MDP
Binary files differ
diff --git a/qutils/QBSP/QBSP.NCB b/qutils/QBSP/QBSP.NCB
new file mode 100644
index 0000000..3c66e82
--- /dev/null
+++ b/qutils/QBSP/QBSP.NCB
Binary files differ
diff --git a/qutils/QBSP/REGION.C b/qutils/QBSP/REGION.C
new file mode 100644
index 0000000..8f28c78
--- /dev/null
+++ b/qutils/QBSP/REGION.C
@@ -0,0 +1,511 @@
+// region.h
+
+#include "bsp5.h"
+
+/*
+
+input
+-----
+vertexes
+edges
+faces
+
+output
+------
+smaller set of vertexes
+smaller set of edges
+regions
+? triangulated regions
+face to region mapping numbers
+
+*/
+
+#define MAX_EDGES_IN_REGION 32
+
+int firstedge;
+
+vec3_t region_mins, region_maxs;
+
+void AddPointToRegion (vec3_t p)
+{
+ int i;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if (p[i] < region_mins[i])
+ region_mins[i] = p[i];
+ if (p[i] > region_maxs[i])
+ region_maxs[i] = p[i];
+ }
+}
+
+void ClearRegionSize (void)
+{
+ region_mins[0] = region_mins[1] = region_mins[2] = 9999;
+ region_maxs[0] = region_maxs[1] = region_maxs[2] = -9999;
+}
+
+void AddFaceToRegionSize (face_t *f)
+{
+ int i;
+
+ for (i=0 ; i<f->numpoints ; i++)
+ AddPointToRegion (f->pts[i]);
+}
+
+/*
+==============
+CanJoinFaces
+==============
+*/
+qboolean CanJoinFaces (face_t *f, face_t *f2)
+{
+ vec3_t oldmins, oldmaxs;
+ int i;
+
+ if (f2->planenum != f->planenum
+ || f2->planeside != f->planeside
+ || f2->texturenum != f->texturenum)
+ return false;
+ if (f2->outputnumber != -1)
+ return false;
+ if (f2->contents[0] != f->contents[0])
+ { // does this ever happen? theyy shouldn't share.
+ printf ("CanJoinFaces: edge with different contents");
+ return false;
+ }
+
+// check size constraints
+ if ( ! (texinfo[f->texturenum].flags & TEX_SPECIAL) )
+ {
+ VectorCopy (region_mins, oldmins);
+ VectorCopy (region_maxs, oldmaxs);
+ AddFaceToRegionSize (f2);
+ for (i=0 ; i<3 ; i++)
+ {
+ if (region_maxs[i] - region_mins[i] > 240)
+ {
+ VectorCopy (oldmins, region_mins);
+ VectorCopy (oldmaxs, region_maxs);
+ return false;
+ }
+ }
+ }
+ else
+ {
+ if (numsurfedges - firstedge + f2->numpoints > MAX_EDGES_IN_REGION)
+ return false; // a huge water or sky polygon
+ }
+
+// check edge count constraints
+ return true;
+}
+
+
+/*
+==============
+RecursiveGrowRegion
+==============
+*/
+void RecursiveGrowRegion (dface_t *r, face_t *f)
+{
+ int e;
+ face_t *f2;
+ int i;
+
+ if (f->outputnumber == numfaces)
+ return;
+
+ if (f->outputnumber != -1)
+ Error ("RecursiveGrowRegion: region collision");
+ f->outputnumber = numfaces;
+
+// add edges
+ for (i=0 ; i<f->numpoints ; i++)
+ {
+ e = f->edges[i];
+ if (!edgefaces[abs(e)][0])
+ continue; // edge has allready been removed
+ if (e > 0)
+ f2 = edgefaces[e][1];
+ else
+ f2 = edgefaces[-e][0];
+ if (f2 && f2->outputnumber == numfaces)
+ {
+ edgefaces[abs(e)][0] = NULL;
+ edgefaces[abs(e)][1] = NULL;
+ continue; // allready merged
+ }
+ if (f2 && CanJoinFaces (f, f2))
+ { // remove the edge and merge the faces
+ edgefaces[abs(e)][0] = NULL;
+ edgefaces[abs(e)][1] = NULL;
+ RecursiveGrowRegion (r, f2);
+ }
+ else
+ {
+ // emit a surfedge
+ if (numsurfedges == MAX_MAP_SURFEDGES)
+ Error ("numsurfedges == MAX_MAP_SURFEDGES");
+ dsurfedges[numsurfedges] = e;
+ numsurfedges++;
+ }
+ }
+
+}
+
+void PrintDface (int f)
+{ // for debugging
+ dface_t *df;
+ dedge_t *e;
+ int i, n;
+
+ df = &dfaces[f];
+ for (i=0 ; i<df->numedges ; i++)
+ {
+ n = dsurfedges[df->firstedge+i];
+ e = &dedges[abs(n)];
+ if (n < 0)
+ printf ("%5i = %5i : %5i\n", n, e->v[1], e->v[0]);
+ else
+ printf ("%5i = %5i : %5i\n", n, e->v[0], e->v[1]);
+ }
+}
+
+void FindVertexUse (int v)
+{ // for debugging
+ int i, j, n;
+ dface_t *df;
+ dedge_t *e;
+
+ for (i=firstmodelface ; i<numfaces ; i++)
+ {
+ df = &dfaces[i];
+ for (j=0 ; j<df->numedges ; j++)
+ {
+ n = dsurfedges[df->firstedge+j];
+ e = &dedges[abs(n)];
+ if (e->v[0] == v || e->v[1] == v)
+ {
+ printf ("on face %i\n", i);
+ break;
+ }
+ }
+ }
+}
+
+void FindEdgeUse (int v)
+{ // for debugging
+ int i, j, n;
+ dface_t *df;
+
+ for (i=firstmodelface ; i<numfaces ; i++)
+ {
+ df = &dfaces[i];
+ for (j=0 ; j<df->numedges ; j++)
+ {
+ n = dsurfedges[df->firstedge+j];
+ if (n == v || -n == v)
+ {
+ printf ("on face %i\n", i);
+ break;
+ }
+ }
+ }
+}
+
+/*
+================
+HealEdges
+
+Extends e1 so that it goes all the way to e2, and removes all references
+to e2
+================
+*/
+int edgemapping[MAX_MAP_EDGES];
+void HealEdges (int e1, int e2)
+{
+ int i, j, n, saved;
+ dface_t *df;
+ dedge_t *ed, *ed2;
+ vec3_t v1, v2;
+ dface_t *found[2];
+ int foundj[2];
+
+return;
+ e1 = edgemapping[e1];
+ e2 = edgemapping[e2];
+
+// extend e1 to e2
+ ed = &dedges[e1];
+ ed2 = &dedges[e2];
+ VectorSubtract (dvertexes[ed->v[1]].point, dvertexes[ed->v[0]].point, v1);
+ VectorNormalize (v1);
+
+ if (ed->v[0] == ed2->v[0])
+ ed->v[0] = ed2->v[1];
+ else if (ed->v[0] == ed2->v[1])
+ ed->v[0] = ed2->v[0];
+ else if (ed->v[1] == ed2->v[0])
+ ed->v[1] = ed2->v[1];
+ else if (ed->v[1] == ed2->v[1])
+ ed->v[1] = ed2->v[0];
+ else
+ Error ("HealEdges: edges don't meet");
+
+ VectorSubtract (dvertexes[ed->v[1]].point, dvertexes[ed->v[0]].point, v2);
+ VectorNormalize (v2);
+
+ if (!VectorCompare (v1, v2))
+ Error ("HealEdges: edges not colinear");
+
+ edgemapping[e2] = e1;
+ saved = 0;
+
+// remove all uses of e2
+ for (i=firstmodelface ; i<numfaces ; i++)
+ {
+ df = &dfaces[i];
+ for (j=0 ; j<df->numedges ; j++)
+ {
+ n = dsurfedges[df->firstedge+j];
+ if (n == e2 || n == -e2)
+ {
+ found[saved] = df;
+ foundj[saved] = j;
+ saved++;
+ break;
+ }
+ }
+ }
+
+ if (saved != 2)
+ printf ("WARNING: didn't find both faces for a saved edge\n");
+ else
+ {
+ for (i=0 ; i<2 ; i++)
+ { // remove this edge
+ df = found[i];
+ j = foundj[i];
+ for (j++ ; j<df->numedges ; j++)
+ dsurfedges[df->firstedge+j-1] =
+ dsurfedges[df->firstedge+j];
+ dsurfedges[df->firstedge+j-1] = 0;
+ df->numedges--;
+ }
+
+
+ edgefaces[e2][0] = edgefaces[e2][1] = NULL;
+ }
+}
+
+typedef struct
+{
+ int numedges;
+ int edges[2];
+} checkpoint_t;
+
+checkpoint_t checkpoints[MAX_MAP_VERTS];
+
+/*
+==============
+RemoveColinearEdges
+==============
+*/
+void RemoveColinearEdges (void)
+{
+ int i,j, v;
+ int c0, c1, c2, c3;
+ checkpoint_t *cp;
+
+// no edges remapped yet
+ for (i=0 ; i<numedges ; i++)
+ edgemapping[i] = i;
+
+// find vertexes that only have two edges
+ memset (checkpoints, 0, sizeof(checkpoints));
+
+ for (i=firstmodeledge ; i<numedges ; i++)
+ {
+ if (!edgefaces[i][0])
+ continue; // removed
+ for (j=0 ; j<2 ; j++)
+ {
+ v = dedges[i].v[j];
+ cp = &checkpoints[v];
+ if (cp->numedges<2)
+ cp->edges[cp->numedges] = i;
+ cp->numedges++;
+ }
+ }
+
+// if a vertex only has two edges and they are colinear, it can be removed
+ c0 = c1 = c2 = c3 = 0;
+
+ for (i=0 ; i<numvertexes ; i++)
+ {
+ cp = &checkpoints[i];
+ switch (cp->numedges)
+ {
+ case 0:
+ c0++;
+ break;
+ case 1:
+ c1++;
+ break;
+ case 2:
+ c2++;
+ HealEdges (cp->edges[0], cp->edges[1]);
+ break;
+ default:
+ c3++;
+ break;
+ }
+ }
+
+// qprintf ("%5i c0\n", c0);
+// qprintf ("%5i c1\n", c1);
+// qprintf ("%5i c2\n", c2);
+// qprintf ("%5i c3+\n", c3);
+ qprintf ("%5i deges removed by tjunction healing\n", c2);
+}
+
+/*
+==============
+CountRealNumbers
+==============
+*/
+void CountRealNumbers (void)
+{
+ int i;
+ int c;
+
+ qprintf ("%5i regions\n", numfaces-firstmodelface);
+
+ c = 0;
+ for (i=firstmodelface ; i<numfaces ; i++)
+ c += dfaces[i].numedges;
+ qprintf ("%5i real marksurfaces\n", c);
+
+ c = 0;
+ for (i=firstmodeledge ; i<numedges ; i++)
+ if (edgefaces[i][0])
+ c++; // not removed
+
+ qprintf ("%5i real edges\n", c);
+}
+
+//=============================================================================
+
+/*
+==============
+GrowNodeRegion_r
+==============
+*/
+void GrowNodeRegion_r (node_t *node)
+{
+ dface_t *r;
+ face_t *f;
+ int i;
+
+ if (node->planenum == PLANENUM_LEAF)
+ return;
+
+ node->firstface = numfaces;
+
+ for (f=node->faces ; f ; f=f->next)
+ {
+// if (f->outputnumber != -1)
+// continue; // allready grown into an earlier region
+
+ // emit a region
+
+ if (numfaces == MAX_MAP_FACES)
+ Error ("MAX_MAP_FACES");
+ f->outputnumber = numfaces;
+ r = &dfaces[numfaces];
+
+ r->planenum = node->outputplanenum;
+ r->side = f->planeside;
+ r->texinfo = f->texturenum;
+ for (i=0 ; i<MAXLIGHTMAPS ; i++)
+ r->styles[i] = 255;
+ r->lightofs = -1;
+
+ // add the face and mergable neighbors to it
+
+#if 0
+ ClearRegionSize ();
+ AddFaceToRegionSize (f);
+ RecursiveGrowRegion (r, f);
+#endif
+ r->firstedge = firstedge = numsurfedges;
+ for (i=0 ; i<f->numpoints ; i++)
+ {
+ if (numsurfedges == MAX_MAP_SURFEDGES)
+ Error ("numsurfedges == MAX_MAP_SURFEDGES");
+ dsurfedges[numsurfedges] = f->edges[i];
+ numsurfedges++;
+ }
+
+ r->numedges = numsurfedges - r->firstedge;
+
+ numfaces++;
+ }
+
+ node->numfaces = numfaces - node->firstface;
+
+ GrowNodeRegion_r (node->children[0]);
+ GrowNodeRegion_r (node->children[1]);
+}
+
+
+/*
+==============
+GrowNodeRegions
+==============
+*/
+void GrowNodeRegions (node_t *headnode)
+{
+ qprintf ("---- GrowRegions ----\n");
+
+ GrowNodeRegion_r (headnode);
+
+//RemoveColinearEdges ();
+ CountRealNumbers ();
+}
+
+/*
+===============================================================================
+
+Turn the faces on a plane into optimal non-convex regions
+The edges may still be split later as a result of tjunctions
+
+typedef struct
+{
+ vec3_t dir;
+ vec3_t origin;
+ vec3_t p[2];
+}
+for all faces
+ for all edges
+ for all edges so far
+ if overlap
+ split
+
+
+===============================================================================
+*/
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/qutils/QBSP/SOLIDBSP.C b/qutils/QBSP/SOLIDBSP.C
new file mode 100644
index 0000000..dae404b
--- /dev/null
+++ b/qutils/QBSP/SOLIDBSP.C
@@ -0,0 +1,754 @@
+// solidbsp.c
+
+#include "bsp5.h"
+
+int leaffaces;
+int nodefaces;
+int splitnodes;
+
+int c_solid, c_empty, c_water;
+
+qboolean usemidsplit;
+
+//============================================================================
+
+/*
+==================
+FaceSide
+
+For BSP hueristic
+==================
+*/
+int FaceSide (face_t *in, plane_t *split)
+{
+ int frontcount, backcount;
+ vec_t dot;
+ int i;
+ vec_t *p;
+
+
+ frontcount = backcount = 0;
+
+// axial planes are fast
+ if (split->type < 3)
+ for (i=0, p = in->pts[0]+split->type ; i<in->numpoints ; i++, p+=3)
+ {
+ if (*p > split->dist + ON_EPSILON)
+ {
+ if (backcount)
+ return SIDE_ON;
+ frontcount = 1;
+ }
+ else if (*p < split->dist - ON_EPSILON)
+ {
+ if (frontcount)
+ return SIDE_ON;
+ backcount = 1;
+ }
+ }
+ else
+// sloping planes take longer
+ for (i=0, p = in->pts[0] ; i<in->numpoints ; i++, p+=3)
+ {
+ dot = DotProduct (p, split->normal);
+ dot -= split->dist;
+ if (dot > ON_EPSILON)
+ {
+ if (backcount)
+ return SIDE_ON;
+ frontcount = 1;
+ }
+ else if (dot < -ON_EPSILON)
+ {
+ if (frontcount)
+ return SIDE_ON;
+ backcount = 1;
+ }
+ }
+
+ if (!frontcount)
+ return SIDE_BACK;
+ if (!backcount)
+ return SIDE_FRONT;
+
+ return SIDE_ON;
+}
+
+/*
+==================
+ChooseMidPlaneFromList
+
+The clipping hull BSP doesn't worry about avoiding splits
+==================
+*/
+surface_t *ChooseMidPlaneFromList (surface_t *surfaces, vec3_t mins, vec3_t maxs)
+{
+ int j,l;
+ surface_t *p, *bestsurface;
+ vec_t bestvalue, value, dist;
+ plane_t *plane;
+
+//
+// pick the plane that splits the least
+//
+ bestvalue = 6*8192*8192;
+ bestsurface = NULL;
+
+ for (p=surfaces ; p ; p=p->next)
+ {
+ if (p->onnode)
+ continue;
+
+ plane = &planes[p->planenum];
+
+ // check for axis aligned surfaces
+ l = plane->type;
+ if (l > PLANE_Z)
+ continue;
+
+ //
+ // calculate the split metric along axis l, smaller values are better
+ //
+ value = 0;
+
+ dist = plane->dist * plane->normal[l];
+ for (j=0 ; j<3 ; j++)
+ {
+ if (j == l)
+ {
+ value += (maxs[l]-dist)*(maxs[l]-dist);
+ value += (dist-mins[l])*(dist-mins[l]);
+ }
+ else
+ value += 2*(maxs[j]-mins[j])*(maxs[j]-mins[j]);
+ }
+
+ if (value > bestvalue)
+ continue;
+
+ //
+ // currently the best!
+ //
+ bestvalue = value;
+ bestsurface = p;
+ }
+
+ if (!bestsurface)
+ {
+ for (p=surfaces ; p ; p=p->next)
+ if (!p->onnode)
+ return p; // first valid surface
+ Error ("ChooseMidPlaneFromList: no valid planes");
+ }
+
+ return bestsurface;
+}
+
+
+
+/*
+==================
+ChoosePlaneFromList
+
+The real BSP hueristic
+==================
+*/
+surface_t *ChoosePlaneFromList (surface_t *surfaces, vec3_t mins, vec3_t maxs, qboolean usefloors)
+{
+ int j,k,l;
+ surface_t *p, *p2, *bestsurface;
+ vec_t bestvalue, bestdistribution, value, dist;
+ plane_t *plane;
+ face_t *f;
+
+//
+// pick the plane that splits the least
+//
+ bestvalue = 99999;
+ bestsurface = NULL;
+ bestdistribution = 9e30;
+
+ for (p=surfaces ; p ; p=p->next)
+ {
+ if (p->onnode)
+ continue;
+
+ plane = &planes[p->planenum];
+ k = 0;
+
+ if (!usefloors && plane->normal[2] == 1)
+ continue;
+
+ for (p2=surfaces ; p2 ; p2=p2->next)
+ {
+ if (p2 == p)
+ continue;
+ if (p2->onnode)
+ continue;
+
+ for (f=p2->faces ; f ; f=f->next)
+ {
+ if (FaceSide (f, plane) == SIDE_ON)
+ {
+ k++;
+ if (k >= bestvalue)
+ break;
+ }
+
+ }
+ if (k > bestvalue)
+ break;
+ }
+
+ if (k > bestvalue)
+ continue;
+
+ // if equal numbers, axial planes win, then decide on spatial subdivision
+
+ if (k < bestvalue || (k == bestvalue && plane->type < PLANE_ANYX) )
+ {
+ // check for axis aligned surfaces
+ l = plane->type;
+
+ if (l <= PLANE_Z)
+ { // axial aligned
+ //
+ // calculate the split metric along axis l
+ //
+ value = 0;
+
+ for (j=0 ; j<3 ; j++)
+ {
+ if (j == l)
+ {
+ dist = plane->dist * plane->normal[l];
+ value += (maxs[l]-dist)*(maxs[l]-dist);
+ value += (dist-mins[l])*(dist-mins[l]);
+ }
+ else
+ value += 2*(maxs[j]-mins[j])*(maxs[j]-mins[j]);
+ }
+
+ if (value > bestdistribution && k == bestvalue)
+ continue;
+ bestdistribution = value;
+ }
+ //
+ // currently the best!
+ //
+ bestvalue = k;
+ bestsurface = p;
+ }
+
+ }
+
+
+ return bestsurface;
+}
+
+
+/*
+==================
+SelectPartition
+
+Selects a surface from a linked list of surfaces to split the group on
+returns NULL if the surface list can not be divided any more (a leaf)
+==================
+*/
+surface_t *SelectPartition (surface_t *surfaces)
+{
+ int i,j;
+ vec3_t mins, maxs;
+ surface_t *p, *bestsurface;
+
+//
+// count onnode surfaces
+//
+ i = 0;
+ bestsurface = NULL;
+ for (p=surfaces ; p ; p=p->next)
+ if (!p->onnode)
+ {
+ i++;
+ bestsurface = p;
+ }
+
+ if (i==0)
+ return NULL;
+
+ if (i==1)
+ return bestsurface; // this is a final split
+
+//
+// calculate a bounding box of the entire surfaceset
+//
+ for (i=0 ; i<3 ; i++)
+ {
+ mins[i] = 99999;
+ maxs[i] = -99999;
+ }
+
+ for (p=surfaces ; p ; p=p->next)
+ for (j=0 ; j<3 ; j++)
+ {
+ if (p->mins[j] < mins[j])
+ mins[j] = p->mins[j];
+ if (p->maxs[j] > maxs[j])
+ maxs[j] = p->maxs[j];
+ }
+
+ if (usemidsplit) // do fast way for clipping hull
+ return ChooseMidPlaneFromList (surfaces, mins, maxs);
+
+// do slow way to save poly splits for drawing hull
+#if 0
+ bestsurface = ChoosePlaneFromList (surfaces, mins, maxs, false);
+ if (bestsurface)
+ return bestsurface;
+#endif
+ return ChoosePlaneFromList (surfaces, mins, maxs, true);
+}
+
+//============================================================================
+
+/*
+=================
+CalcSurfaceInfo
+
+Calculates the bounding box
+=================
+*/
+void CalcSurfaceInfo (surface_t *surf)
+{
+ int i,j;
+ face_t *f;
+
+ if (!surf->faces)
+ Error ("CalcSurfaceInfo: surface without a face");
+
+//
+// calculate a bounding box
+//
+ for (i=0 ; i<3 ; i++)
+ {
+ surf->mins[i] = 99999;
+ surf->maxs[i] = -99999;
+ }
+
+ for (f=surf->faces ; f ; f=f->next)
+ {
+if (f->contents[0] >= 0 || f->contents[1] >= 0)
+Error ("Bad contents");
+ for (i=0 ; i<f->numpoints ; i++)
+ for (j=0 ; j<3 ; j++)
+ {
+ if (f->pts[i][j] < surf->mins[j])
+ surf->mins[j] = f->pts[i][j];
+ if (f->pts[i][j] > surf->maxs[j])
+ surf->maxs[j] = f->pts[i][j];
+ }
+ }
+}
+
+
+
+/*
+==================
+DividePlane
+==================
+*/
+void DividePlane (surface_t *in, plane_t *split, surface_t **front, surface_t **back)
+{
+ face_t *facet, *next;
+ face_t *frontlist, *backlist;
+ face_t *frontfrag, *backfrag;
+ surface_t *news;
+ plane_t *inplane;
+
+ inplane = &planes[in->planenum];
+
+// parallel case is easy
+ if (VectorCompare (inplane->normal, split->normal))
+ {
+// check for exactly on node
+ if (inplane->dist == split->dist)
+ { // divide the facets to the front and back sides
+ news = AllocSurface ();
+ *news = *in;
+
+ facet=in->faces;
+ in->faces = NULL;
+ news->faces = NULL;
+ in->onnode = news->onnode = true;
+
+ for ( ; facet ; facet=next)
+ {
+ next = facet->next;
+ if (facet->planeside == 1)
+ {
+ facet->next = news->faces;
+ news->faces = facet;
+ }
+ else
+ {
+ facet->next = in->faces;
+ in->faces = facet;
+ }
+ }
+
+ if (in->faces)
+ *front = in;
+ else
+ *front = NULL;
+ if (news->faces)
+ *back = news;
+ else
+ *back = NULL;
+ return;
+ }
+
+ if (inplane->dist > split->dist)
+ {
+ *front = in;
+ *back = NULL;
+ }
+ else
+ {
+ *front = NULL;
+ *back = in;
+ }
+ return;
+ }
+
+// do a real split. may still end up entirely on one side
+// OPTIMIZE: use bounding box for fast test
+ frontlist = NULL;
+ backlist = NULL;
+
+ for (facet = in->faces ; facet ; facet = next)
+ {
+ next = facet->next;
+ SplitFace (facet, split, &frontfrag, &backfrag);
+ if (frontfrag)
+ {
+ frontfrag->next = frontlist;
+ frontlist = frontfrag;
+ }
+ if (backfrag)
+ {
+ backfrag->next = backlist;
+ backlist = backfrag;
+ }
+ }
+
+// if nothing actually got split, just move the in plane
+
+ if (frontlist == NULL)
+ {
+ *front = NULL;
+ *back = in;
+ in->faces = backlist;
+ return;
+ }
+
+ if (backlist == NULL)
+ {
+ *front = in;
+ *back = NULL;
+ in->faces = frontlist;
+ return;
+ }
+
+
+// stuff got split, so allocate one new plane and reuse in
+ news = AllocSurface ();
+ *news = *in;
+ news->faces = backlist;
+ *back = news;
+
+ in->faces = frontlist;
+ *front = in;
+
+// recalc bboxes and flags
+ CalcSurfaceInfo (news);
+ CalcSurfaceInfo (in);
+}
+
+/*
+==================
+DivideNodeBounds
+==================
+*/
+void DivideNodeBounds (node_t *node, plane_t *split)
+{
+ VectorCopy (node->mins, node->children[0]->mins);
+ VectorCopy (node->mins, node->children[1]->mins);
+ VectorCopy (node->maxs, node->children[0]->maxs);
+ VectorCopy (node->maxs, node->children[1]->maxs);
+
+// OPTIMIZE: sloping cuts can give a better bbox than this...
+ if (split->type > 2)
+ return;
+
+ node->children[0]->mins[split->type] =
+ node->children[1]->maxs[split->type] = split->dist;
+}
+
+/*
+==================
+LinkConvexFaces
+
+Determines the contents of the leaf and creates the final list of
+original faces that have some fragment inside this leaf
+==================
+*/
+void LinkConvexFaces (surface_t *planelist, node_t *leafnode)
+{
+ face_t *f, *next;
+ surface_t *surf, *pnext;
+ int i, count;
+
+ leafnode->faces = NULL;
+ leafnode->contents = 0;
+ leafnode->planenum = -1;
+
+ count = 0;
+ for ( surf = planelist ; surf ; surf = surf->next)
+ {
+ for (f = surf->faces ; f ; f=f->next)
+ {
+ count++;
+ if (!leafnode->contents)
+ leafnode->contents = f->contents[0];
+ else if (leafnode->contents != f->contents[0])
+ Error ("Mixed face contents in leafnode");
+ }
+ }
+
+ if (!leafnode->contents)
+ leafnode->contents = CONTENTS_SOLID;
+
+ switch (leafnode->contents)
+ {
+ case CONTENTS_EMPTY:
+ c_empty++;
+ break;
+ case CONTENTS_SOLID:
+ c_solid++;
+ break;
+ case CONTENTS_WATER:
+ case CONTENTS_SLIME:
+ case CONTENTS_LAVA:
+ case CONTENTS_SKY:
+ c_water++;
+ break;
+ default:
+ Error ("LinkConvexFaces: bad contents number");
+ }
+
+//
+// write the list of faces, and free the originals
+//
+ leaffaces += count;
+ leafnode->markfaces = malloc(sizeof(face_t *)*(count+1));
+ i = 0;
+ for ( surf = planelist ; surf ; surf = pnext)
+ {
+ pnext = surf->next;
+ for (f = surf->faces ; f ; f=next)
+ {
+ next = f->next;
+ leafnode->markfaces[i] = f->original;
+ i++;
+ FreeFace (f);
+ }
+ FreeSurface (surf);
+ }
+ leafnode->markfaces[i] = NULL; // sentinal
+}
+
+
+/*
+==================
+LinkNodeFaces
+
+Returns a duplicated list of all faces on surface
+==================
+*/
+face_t *LinkNodeFaces (surface_t *surface)
+{
+ face_t *f, *new, **prevptr;
+ face_t *list;
+
+ list = NULL;
+
+
+// subdivide
+ prevptr = &surface->faces;
+ while (1)
+ {
+ f = *prevptr;
+ if (!f)
+ break;
+ SubdivideFace (f, prevptr);
+ f = *prevptr;
+ prevptr = &f->next;
+ }
+
+// copy
+ for (f=surface->faces ; f ; f=f->next)
+ {
+ nodefaces++;
+ new = AllocFace ();
+ *new = *f;
+ f->original = new;
+ new->next = list;
+ list = new;
+ }
+
+ return list;
+}
+
+
+/*
+==================
+PartitionSurfaces
+==================
+*/
+void PartitionSurfaces (surface_t *surfaces, node_t *node)
+{
+ surface_t *split, *p, *next;
+ surface_t *frontlist, *backlist;
+ surface_t *frontfrag, *backfrag;
+ plane_t *splitplane;
+
+ split = SelectPartition (surfaces);
+ if (!split)
+ { // this is a leaf node
+ node->planenum = PLANENUM_LEAF;
+ LinkConvexFaces (surfaces, node);
+ return;
+ }
+
+ splitnodes++;
+ node->faces = LinkNodeFaces (split);
+ node->children[0] = AllocNode ();
+ node->children[1] = AllocNode ();
+ node->planenum = split->planenum;
+
+ splitplane = &planes[split->planenum];
+
+ DivideNodeBounds (node, splitplane);
+
+
+//
+// multiple surfaces, so split all the polysurfaces into front and back lists
+//
+ frontlist = NULL;
+ backlist = NULL;
+
+ for (p=surfaces ; p ; p=next)
+ {
+ next = p->next;
+ DividePlane (p, splitplane, &frontfrag, &backfrag);
+ if (frontfrag && backfrag)
+ {
+ // the plane was split, which may expose oportunities to merge
+ // adjacent faces into a single face
+// MergePlaneFaces (frontfrag);
+// MergePlaneFaces (backfrag);
+ }
+
+ if (frontfrag)
+ {
+ if (!frontfrag->faces)
+ Error ("surface with no faces");
+ frontfrag->next = frontlist;
+ frontlist = frontfrag;
+ }
+ if (backfrag)
+ {
+ if (!backfrag->faces)
+ Error ("surface with no faces");
+ backfrag->next = backlist;
+ backlist = backfrag;
+ }
+ }
+
+ PartitionSurfaces (frontlist, node->children[0]);
+ PartitionSurfaces (backlist, node->children[1]);
+}
+
+/*
+==================
+DrawSurface
+==================
+*/
+void DrawSurface (surface_t *surf)
+{
+ face_t *f;
+
+ for (f=surf->faces ; f ; f=f->next)
+ Draw_DrawFace (f);
+}
+
+/*
+==================
+DrawSurfaceList
+==================
+*/
+void DrawSurfaceList (surface_t *surf)
+{
+ Draw_ClearWindow ();
+ while (surf)
+ {
+ DrawSurface (surf);
+ surf = surf->next;
+ }
+}
+
+/*
+==================
+SolidBSP
+==================
+*/
+node_t *SolidBSP (surface_t *surfhead, qboolean midsplit)
+{
+ int i;
+ node_t *headnode;
+
+ qprintf ("----- SolidBSP -----\n");
+
+ headnode = AllocNode ();
+ usemidsplit = midsplit;
+
+//
+// calculate a bounding box for the entire model
+//
+ for (i=0 ; i<3 ; i++)
+ {
+ headnode->mins[i] = brushset->mins[i] - SIDESPACE;
+ headnode->maxs[i] = brushset->maxs[i] + SIDESPACE;
+ }
+
+//
+// recursively partition everything
+//
+ Draw_ClearWindow ();
+ splitnodes = 0;
+ leaffaces = 0;
+ nodefaces = 0;
+ c_solid = c_empty = c_water = 0;
+
+ PartitionSurfaces (surfhead, headnode);
+
+ qprintf ("%5i split nodes\n", splitnodes);
+ qprintf ("%5i solid leafs\n", c_solid);
+ qprintf ("%5i empty leafs\n", c_empty);
+ qprintf ("%5i water leafs\n", c_water);
+ qprintf ("%5i leaffaces\n",leaffaces);
+ qprintf ("%5i nodefaces\n", nodefaces);
+
+ return headnode;
+}
+
diff --git a/qutils/QBSP/SURFACES.C b/qutils/QBSP/SURFACES.C
new file mode 100644
index 0000000..082493b
--- /dev/null
+++ b/qutils/QBSP/SURFACES.C
@@ -0,0 +1,512 @@
+// divide.h
+
+#include "bsp5.h"
+
+
+surface_t newcopy_t;
+
+/*
+a surface has all of the faces that could be drawn on a given plane
+
+the outside filling stage can remove some of them so a better bsp can be generated
+
+*/
+
+int subdivides;
+
+
+/*
+===============
+SubdivideFace
+
+If the face is >256 in either texture direction, carve a valid sized
+piece off and insert the remainder in the next link
+===============
+*/
+void SubdivideFace (face_t *f, face_t **prevptr)
+{
+ float mins, maxs;
+ vec_t v;
+ int axis, i;
+ plane_t plane;
+ face_t *front, *back, *next;
+ texinfo_t *tex;
+
+// special (non-surface cached) faces don't need subdivision
+ tex = &texinfo[f->texturenum];
+
+ if ( tex->flags & TEX_SPECIAL)
+ return;
+
+
+ for (axis = 0 ; axis < 2 ; axis++)
+ {
+ while (1)
+ {
+ mins = 9999;
+ maxs = -9999;
+
+ for (i=0 ; i<f->numpoints ; i++)
+ {
+ v = DotProduct (f->pts[i], tex->vecs[axis]);
+ if (v < mins)
+ mins = v;
+ if (v > maxs)
+ maxs = v;
+ }
+
+ if (maxs - mins <= subdivide_size)
+ break;
+
+ // split it
+ subdivides++;
+
+ VectorCopy (tex->vecs[axis], plane.normal);
+ v = VectorLength (plane.normal);
+ VectorNormalize (plane.normal);
+ plane.dist = (mins + subdivide_size - 16)/v;
+ next = f->next;
+ SplitFace (f, &plane, &front, &back);
+ if (!front || !back)
+ Error ("SubdivideFace: didn't split the polygon");
+ *prevptr = back;
+ back->next = front;
+ front->next = next;
+ f = back;
+ }
+ }
+}
+
+
+/*
+================
+SubdivideFaces
+================
+*/
+void SubdivideFaces (surface_t *surfhead)
+{
+ surface_t *surf;
+ face_t *f , **prevptr;
+
+ qprintf ("--- SubdivideFaces ---\n");
+
+ subdivides = 0;
+
+ for (surf = surfhead ; surf ; surf=surf->next)
+ {
+ prevptr = &surf->faces;
+ while (1)
+ {
+ f = *prevptr;
+ if (!f)
+ break;
+ SubdivideFace (f, prevptr);
+ f = *prevptr;
+ prevptr = &f->next;
+ }
+ }
+
+ qprintf ("%i faces added by subdivision\n", subdivides);
+
+}
+
+
+/*
+=============================================================================
+
+GatherNodeFaces
+
+Frees the current node tree and returns a new chain of the surfaces that
+have inside faces.
+=============================================================================
+*/
+
+void GatherNodeFaces_r (node_t *node)
+{
+ face_t *f, *next;
+
+ if (node->planenum != PLANENUM_LEAF)
+ {
+//
+// decision node
+//
+ for (f=node->faces ; f ; f=next)
+ {
+ next = f->next;
+ if (!f->numpoints)
+ { // face was removed outside
+ FreeFace (f);
+ }
+ else
+ {
+ f->next = validfaces[f->planenum];
+ validfaces[f->planenum] = f;
+ }
+ }
+
+ GatherNodeFaces_r (node->children[0]);
+ GatherNodeFaces_r (node->children[1]);
+
+ free (node);
+ }
+ else
+ {
+//
+// leaf node
+//
+ free (node);
+ }
+}
+
+/*
+================
+GatherNodeFaces
+
+================
+*/
+surface_t *GatherNodeFaces (node_t *headnode)
+{
+ memset (validfaces, 0, sizeof(validfaces));
+ GatherNodeFaces_r (headnode);
+ return BuildSurfaces ();
+}
+
+//===========================================================================
+
+typedef struct hashvert_s
+{
+ struct hashvert_s *next;
+ vec3_t point;
+ int num;
+ int numplanes; // for corner determination
+ int planenums[2];
+ int numedges;
+} hashvert_t;
+
+#define POINT_EPSILON 0.01
+
+int c_cornerverts;
+
+hashvert_t hvertex[MAX_MAP_VERTS];
+hashvert_t *hvert_p;
+
+face_t *edgefaces[MAX_MAP_EDGES][2];
+int firstmodeledge = 1;
+int firstmodelface;
+
+//============================================================================
+
+#define NUM_HASH 4096
+
+hashvert_t *hashverts[NUM_HASH];
+
+static vec3_t hash_min, hash_scale;
+
+static void InitHash (void)
+{
+ vec3_t size;
+ vec_t volume;
+ vec_t scale;
+ int newsize[2];
+ int i;
+
+ memset (hashverts, 0, sizeof(hashverts));
+
+ for (i=0 ; i<3 ; i++)
+ {
+ hash_min[i] = -8000;
+ size[i] = 16000;
+ }
+
+ volume = size[0]*size[1];
+
+ scale = sqrt(volume / NUM_HASH);
+
+ newsize[0] = size[0] / scale;
+ newsize[1] = size[1] / scale;
+
+ hash_scale[0] = newsize[0] / size[0];
+ hash_scale[1] = newsize[1] / size[1];
+ hash_scale[2] = newsize[1];
+
+ hvert_p = hvertex;
+}
+
+static unsigned HashVec (vec3_t vec)
+{
+ unsigned h;
+
+ h = hash_scale[0] * (vec[0] - hash_min[0]) * hash_scale[2]
+ + hash_scale[1] * (vec[1] - hash_min[1]);
+ if ( h >= NUM_HASH)
+ return NUM_HASH - 1;
+ return h;
+}
+
+
+/*
+=============
+GetVertex
+=============
+*/
+int GetVertex (vec3_t in, int planenum)
+{
+ int h;
+ int i;
+ hashvert_t *hv;
+ vec3_t vert;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if ( fabs(in[i] - Q_rint(in[i])) < 0.001)
+ vert[i] = Q_rint(in[i]);
+ else
+ vert[i] = in[i];
+ }
+
+ h = HashVec (vert);
+
+ for (hv=hashverts[h] ; hv ; hv=hv->next)
+ {
+ if ( fabs(hv->point[0]-vert[0])<POINT_EPSILON
+ && fabs(hv->point[1]-vert[1])<POINT_EPSILON
+ && fabs(hv->point[2]-vert[2])<POINT_EPSILON )
+ {
+ hv->numedges++;
+ if (hv->numplanes == 3)
+ return hv->num; // allready known to be a corner
+ for (i=0 ; i<hv->numplanes ; i++)
+ if (hv->planenums[i] == planenum)
+ return hv->num; // allready know this plane
+ if (hv->numplanes == 2)
+ c_cornerverts++;
+ else
+ hv->planenums[hv->numplanes] = planenum;
+ hv->numplanes++;
+ return hv->num;
+ }
+ }
+
+ hv = hvert_p;
+ hv->numedges = 1;
+ hv->numplanes = 1;
+ hv->planenums[0] = planenum;
+ hv->next = hashverts[h];
+ hashverts[h] = hv;
+ VectorCopy (vert, hv->point);
+ hv->num = numvertexes;
+ if (hv->num==MAX_MAP_VERTS)
+ Error ("GetVertex: MAX_MAP_VERTS");
+ hvert_p++;
+
+// emit a vertex
+ if (numvertexes == MAX_MAP_VERTS)
+ Error ("numvertexes == MAX_MAP_VERTS");
+
+ dvertexes[numvertexes].point[0] = vert[0];
+ dvertexes[numvertexes].point[1] = vert[1];
+ dvertexes[numvertexes].point[2] = vert[2];
+ numvertexes++;
+
+ return hv->num;
+}
+
+//===========================================================================
+
+
+/*
+==================
+GetEdge
+
+Don't allow four way edges
+==================
+*/
+int c_tryedges;
+
+int GetEdge (vec3_t p1, vec3_t p2, face_t *f)
+{
+ int v1, v2;
+ dedge_t *edge;
+ int i;
+
+ if (!f->contents[0])
+ Error ("GetEdge: 0 contents");
+
+ c_tryedges++;
+ v1 = GetVertex (p1, f->planenum);
+ v2 = GetVertex (p2, f->planenum);
+ for (i=firstmodeledge ; i < numedges ; i++)
+ {
+ edge = &dedges[i];
+ if (v1 == edge->v[1] && v2 == edge->v[0]
+ && !edgefaces[i][1]
+ && edgefaces[i][0]->contents[0] == f->contents[0])
+ {
+ edgefaces[i][1] = f;
+ return -i;
+ }
+ }
+
+// emit an edge
+ if (numedges == MAX_MAP_EDGES)
+ Error ("numedges == MAX_MAP_EDGES");
+ edge = &dedges[numedges];
+ numedges++;
+ edge->v[0] = v1;
+ edge->v[1] = v2;
+ edgefaces[i][0] = f;
+
+ return i;
+}
+
+
+/*
+==================
+FindFaceEdges
+==================
+*/
+void FindFaceEdges (face_t *face)
+{
+ int i;
+
+ face->outputnumber = -1;
+ if (face->numpoints > MAXEDGES)
+ Error ("WriteFace: %i points", face->numpoints);
+
+ for (i=0; i<face->numpoints ; i++)
+ face->edges[i] = GetEdge
+ (face->pts[i], face->pts[(i+1)%face->numpoints], face);
+}
+
+/*
+=============
+CheckVertexes
+// debugging
+=============
+*/
+void CheckVertexes (void)
+{
+ int cb, c0, c1, c2, c3;
+ hashvert_t *hv;
+
+ cb = c0 = c1 = c2 = c3 = 0;
+ for (hv=hvertex ; hv!=hvert_p ; hv++)
+ {
+ if (hv->numedges < 0 || hv->numedges & 1)
+ cb++;
+ else if (!hv->numedges)
+ c0++;
+ else if (hv->numedges == 2)
+ c1++;
+ else if (hv->numedges == 4)
+ c2++;
+ else
+ c3++;
+ }
+
+ qprintf ("%5i bad edge points\n", cb);
+ qprintf ("%5i 0 edge points\n", c0);
+ qprintf ("%5i 2 edge points\n", c1);
+ qprintf ("%5i 4 edge points\n", c2);
+ qprintf ("%5i 6+ edge points\n", c3);
+}
+
+/*
+=============
+CheckEdges
+// debugging
+=============
+*/
+void CheckEdges (void)
+{
+ dedge_t *edge;
+ int i;
+ dvertex_t *d1, *d2;
+ face_t *f1, *f2;
+ int c_nonconvex;
+ int c_multitexture;
+
+ c_nonconvex = c_multitexture = 0;
+
+// CheckVertexes ();
+
+ for (i=1 ; i < numedges ; i++)
+ {
+ edge = &dedges[i];
+ if (!edgefaces[i][1])
+ {
+ d1 = &dvertexes[edge->v[0]];
+ d2 = &dvertexes[edge->v[1]];
+ qprintf ("unshared edge at: (%8.2f, %8.2f, %8.2f) (%8.2f, %8.2f, %8.2f)\n",d1->point[0], d1->point[1], d1->point[2], d2->point[0], d2->point[1], d2->point[2]);
+ }
+ else
+ {
+ f1 = edgefaces[i][0];
+ f2 = edgefaces[i][1];
+ if (f1->planeside != f2->planeside)
+ continue;
+ if (f1->planenum != f2->planenum)
+ continue;
+
+ // on the same plane, might be discardable
+ if (f1->texturenum == f2->texturenum)
+ {
+ hvertex[edge->v[0]].numedges-=2;
+ hvertex[edge->v[1]].numedges-=2;
+ c_nonconvex++;
+ }
+ else
+ c_multitexture++;
+ }
+ }
+
+// qprintf ("%5i edges\n", i);
+// qprintf ("%5i c_nonconvex\n", c_nonconvex);
+// qprintf ("%5i c_multitexture\n", c_multitexture);
+
+// CheckVertexes ();
+}
+
+
+/*
+================
+MakeFaceEdges_r
+================
+*/
+void MakeFaceEdges_r (node_t *node)
+{
+ face_t *f;
+
+ if (node->planenum == PLANENUM_LEAF)
+ return;
+
+ for (f=node->faces ; f ; f=f->next)
+ FindFaceEdges (f);
+
+ MakeFaceEdges_r (node->children[0]);
+ MakeFaceEdges_r (node->children[1]);
+}
+
+/*
+================
+MakeFaceEdges
+================
+*/
+void MakeFaceEdges (node_t *headnode)
+{
+ qprintf ("----- MakeFaceEdges -----\n");
+
+ InitHash ();
+ c_tryedges = 0;
+ c_cornerverts = 0;
+
+ MakeFaceEdges_r (headnode);
+
+// CheckEdges ();
+
+ GrowNodeRegions (headnode);
+
+ firstmodeledge = numedges;
+ firstmodelface = numfaces;
+}
+
diff --git a/qutils/QBSP/TJUNC.C b/qutils/QBSP/TJUNC.C
new file mode 100644
index 0000000..35d34e4
--- /dev/null
+++ b/qutils/QBSP/TJUNC.C
@@ -0,0 +1,506 @@
+// tjunc.c
+
+#include "bsp5.h"
+
+
+typedef struct wvert_s
+{
+ vec_t t;
+ struct wvert_s *prev, *next;
+} wvert_t;
+
+typedef struct wedge_s
+{
+ struct wedge_s *next;
+ vec3_t dir;
+ vec3_t origin;
+ wvert_t head;
+} wedge_t;
+
+int numwedges, numwverts;
+int tjuncs;
+int tjuncfaces;
+
+#define MAXWVERTS 0x20000
+#define MAXWEDGES 0x10000
+
+
+wvert_t wverts[MAXWVERTS];
+wedge_t wedges[MAXWEDGES];
+
+
+void PrintFace (face_t *f)
+{
+ int i;
+
+ for (i=0 ; i<f->numpoints ; i++)
+ printf ("(%5.2f, %5.2f, %5.2f)\n", f->pts[i][0], f->pts[i][1], f->pts[i][2]);
+}
+
+//============================================================================
+
+#define NUM_HASH 1024
+
+wedge_t *wedge_hash[NUM_HASH];
+
+static vec3_t hash_min, hash_scale;
+
+static void InitHash (vec3_t mins, vec3_t maxs)
+{
+ vec3_t size;
+ vec_t volume;
+ vec_t scale;
+ int newsize[2];
+
+ VectorCopy (mins, hash_min);
+ VectorSubtract (maxs, mins, size);
+ memset (wedge_hash, 0, sizeof(wedge_hash));
+
+ volume = size[0]*size[1];
+
+ scale = sqrt(volume / NUM_HASH);
+
+ newsize[0] = size[0] / scale;
+ newsize[1] = size[1] / scale;
+
+ hash_scale[0] = newsize[0] / size[0];
+ hash_scale[1] = newsize[1] / size[1];
+ hash_scale[2] = newsize[1];
+}
+
+static unsigned HashVec (vec3_t vec)
+{
+ unsigned h;
+
+ h = hash_scale[0] * (vec[0] - hash_min[0]) * hash_scale[2]
+ + hash_scale[1] * (vec[1] - hash_min[1]);
+ if ( h >= NUM_HASH)
+ return NUM_HASH - 1;
+ return h;
+}
+
+//============================================================================
+
+void CanonicalVector (vec3_t vec)
+{
+ VectorNormalize (vec);
+ if (vec[0] > EQUAL_EPSILON)
+ return;
+ else if (vec[0] < -EQUAL_EPSILON)
+ {
+ VectorSubtract (vec3_origin, vec, vec);
+ return;
+ }
+ else
+ vec[0] = 0;
+
+ if (vec[1] > EQUAL_EPSILON)
+ return;
+ else if (vec[1] < -EQUAL_EPSILON)
+ {
+ VectorSubtract (vec3_origin, vec, vec);
+ return;
+ }
+ else
+ vec[1] = 0;
+
+ if (vec[2] > EQUAL_EPSILON)
+ return;
+ else if (vec[2] < -EQUAL_EPSILON)
+ {
+ VectorSubtract (vec3_origin, vec, vec);
+ return;
+ }
+ else
+ vec[2] = 0;
+ Error ("CanonicalVector: degenerate");
+}
+
+wedge_t *FindEdge (vec3_t p1, vec3_t p2, vec_t *t1, vec_t *t2)
+{
+ vec3_t origin;
+ vec3_t dir;
+ wedge_t *w;
+ vec_t temp;
+ int h;
+
+ VectorSubtract (p2, p1, dir);
+ CanonicalVector (dir);
+
+ *t1 = DotProduct (p1, dir);
+ *t2 = DotProduct (p2, dir);
+
+ VectorMA (p1, -*t1, dir, origin);
+
+ if (*t1 > *t2)
+ {
+ temp = *t1;
+ *t1 = *t2;
+ *t2 = temp;
+ }
+
+ h = HashVec (origin);
+
+ for (w = wedge_hash[h] ; w ; w=w->next)
+ {
+ temp = w->origin[0] - origin[0];
+ if (temp < -EQUAL_EPSILON || temp > EQUAL_EPSILON)
+ continue;
+ temp = w->origin[1] - origin[1];
+ if (temp < -EQUAL_EPSILON || temp > EQUAL_EPSILON)
+ continue;
+ temp = w->origin[2] - origin[2];
+ if (temp < -EQUAL_EPSILON || temp > EQUAL_EPSILON)
+ continue;
+
+ temp = w->dir[0] - dir[0];
+ if (temp < -EQUAL_EPSILON || temp > EQUAL_EPSILON)
+ continue;
+ temp = w->dir[1] - dir[1];
+ if (temp < -EQUAL_EPSILON || temp > EQUAL_EPSILON)
+ continue;
+ temp = w->dir[2] - dir[2];
+ if (temp < -EQUAL_EPSILON || temp > EQUAL_EPSILON)
+ continue;
+
+ return w;
+ }
+
+ if (numwedges == MAXWEDGES)
+ Error ("FindEdge: numwedges == MAXWEDGES");
+ w = &wedges[numwedges];
+ numwedges++;
+
+ w->next = wedge_hash[h];
+ wedge_hash[h] = w;
+
+ VectorCopy (origin, w->origin);
+ VectorCopy (dir, w->dir);
+ w->head.next = w->head.prev = &w->head;
+ w->head.t = 99999;
+ return w;
+}
+
+
+/*
+===============
+AddVert
+
+===============
+*/
+#define T_EPSILON 0.01
+
+void AddVert (wedge_t *w, vec_t t)
+{
+ wvert_t *v, *newv;
+
+ v = w->head.next;
+ do
+ {
+ if (fabs(v->t - t) < T_EPSILON)
+ return;
+ if (v->t > t)
+ break;
+ v = v->next;
+ } while (1);
+
+// insert a new wvert before v
+ if (numwverts == MAXWVERTS)
+ Error ("AddVert: numwverts == MAXWVERTS");
+
+ newv = &wverts[numwverts];
+ numwverts++;
+
+ newv->t = t;
+ newv->next = v;
+ newv->prev = v->prev;
+ v->prev->next = newv;
+ v->prev = newv;
+}
+
+
+/*
+===============
+AddEdge
+
+===============
+*/
+void AddEdge (vec3_t p1, vec3_t p2)
+{
+ wedge_t *w;
+ vec_t t1, t2;
+
+ w = FindEdge(p1, p2, &t1, &t2);
+ AddVert (w, t1);
+ AddVert (w, t2);
+}
+
+/*
+===============
+AddFaceEdges
+
+===============
+*/
+void AddFaceEdges (face_t *f)
+{
+ int i, j;
+
+ for (i=0 ; i < f->numpoints ; i++)
+ {
+ j = (i+1)%f->numpoints;
+ AddEdge (f->pts[i], f->pts[j]);
+ }
+}
+
+
+//============================================================================
+
+// a specially allocated face that can hold hundreds of edges if needed
+byte superfacebuf[8192];
+face_t *superface = (face_t *)superfacebuf;
+
+void FixFaceEdges (face_t *f);
+
+face_t *newlist;
+
+void SplitFaceForTjunc (face_t *f, face_t *original)
+{
+ int i;
+ face_t *new, *chain;
+ vec3_t dir, test;
+ vec_t v;
+ int firstcorner, lastcorner;
+
+ chain = NULL;
+ do
+ {
+ if (f->numpoints <= MAXPOINTS)
+ { // the face is now small enough without more cutting
+ // so copy it back to the original
+ *original = *f;
+ original->original = chain;
+ original->next = newlist;
+ newlist = original;
+ return;
+ }
+
+ tjuncfaces++;
+
+restart:
+ // find the last corner
+ VectorSubtract (f->pts[f->numpoints-1], f->pts[0], dir);
+ VectorNormalize (dir);
+ for (lastcorner=f->numpoints-1 ; lastcorner > 0 ; lastcorner--)
+ {
+ VectorSubtract (f->pts[lastcorner-1], f->pts[lastcorner], test);
+ VectorNormalize (test);
+ v = DotProduct (test, dir);
+ if (v < 0.9999 || v > 1.00001)
+ {
+ break;
+ }
+ }
+
+ // find the first corner
+ VectorSubtract (f->pts[1], f->pts[0], dir);
+ VectorNormalize (dir);
+ for (firstcorner=1 ; firstcorner < f->numpoints-1 ; firstcorner++)
+ {
+ VectorSubtract (f->pts[firstcorner+1], f->pts[firstcorner], test);
+ VectorNormalize (test);
+ v = DotProduct (test, dir);
+ if (v < 0.9999 || v > 1.00001)
+ {
+ break;
+ }
+ }
+
+ if (firstcorner+2 >= MAXPOINTS)
+ {
+ // rotate the point winding
+ VectorCopy (f->pts[0], test);
+ for (i=1 ; i<f->numpoints ; i++)
+ {
+ VectorCopy (f->pts[i], f->pts[i-1]);
+ }
+ VectorCopy (test, f->pts[f->numpoints-1]);
+ goto restart;
+ }
+
+
+ // cut off as big a piece as possible, less than MAXPOINTS, and not
+ // past lastcorner
+
+ new = NewFaceFromFace (f);
+ if (f->original)
+ Error ("SplitFaceForTjunc: f->original");
+
+ new->original = chain;
+ chain = new;
+ new->next = newlist;
+ newlist = new;
+ if (f->numpoints - firstcorner <= MAXPOINTS)
+ new->numpoints = firstcorner+2;
+ else if (lastcorner+2 < MAXPOINTS &&
+ f->numpoints - lastcorner <= MAXPOINTS)
+ new->numpoints = lastcorner+2;
+ else
+ new->numpoints = MAXPOINTS;
+
+ for (i=0 ; i<new->numpoints ; i++)
+ {
+ VectorCopy (f->pts[i], new->pts[i]);
+ }
+
+
+ for (i=new->numpoints-1 ; i<f->numpoints ; i++)
+ {
+ VectorCopy (f->pts[i], f->pts[i-(new->numpoints-2)]);
+ }
+ f->numpoints -= (new->numpoints-2);
+ } while (1);
+
+}
+
+
+/*
+===============
+FixFaceEdges
+
+===============
+*/
+void FixFaceEdges (face_t *f)
+{
+ int i, j, k;
+ wedge_t *w;
+ wvert_t *v;
+ vec_t t1, t2;
+
+ *superface = *f;
+
+restart:
+ for (i=0 ; i < superface->numpoints ; i++)
+ {
+ j = (i+1)%superface->numpoints;
+
+ w = FindEdge (superface->pts[i], superface->pts[j], &t1, &t2);
+
+ for (v=w->head.next ; v->t < t1 + T_EPSILON ; v = v->next)
+ {
+ }
+
+ if (v->t < t2-T_EPSILON)
+ {
+ tjuncs++;
+ // insert a new vertex here
+ for (k = superface->numpoints ; k> j ; k--)
+ {
+ VectorCopy (superface->pts[k-1], superface->pts[k]);
+ }
+ VectorMA (w->origin, v->t, w->dir, superface->pts[j]);
+ superface->numpoints++;
+ goto restart;
+ }
+ }
+
+
+ if (superface->numpoints <= MAXPOINTS)
+ {
+ *f = *superface;
+ f->next = newlist;
+ newlist = f;
+ return;
+ }
+
+// the face needs to be split into multiple faces because of too many edges
+
+ SplitFaceForTjunc (superface, f);
+
+}
+
+
+//============================================================================
+
+void tjunc_find_r (node_t *node)
+{
+ face_t *f;
+
+ if (node->planenum == PLANENUM_LEAF)
+ return;
+
+ for (f=node->faces ; f ; f=f->next)
+ AddFaceEdges (f);
+
+ tjunc_find_r (node->children[0]);
+ tjunc_find_r (node->children[1]);
+}
+
+void tjunc_fix_r (node_t *node)
+{
+ face_t *f, *next;
+
+ if (node->planenum == PLANENUM_LEAF)
+ return;
+
+ newlist = NULL;
+
+ for (f=node->faces ; f ; f=next)
+ {
+ next = f->next;
+ FixFaceEdges (f);
+ }
+
+ node->faces = newlist;
+
+ tjunc_fix_r (node->children[0]);
+ tjunc_fix_r (node->children[1]);
+}
+
+/*
+===========
+tjunc
+
+===========
+*/
+void tjunc (node_t *headnode)
+{
+ vec3_t maxs, mins;
+ int i;
+
+ qprintf ("---- tjunc ----\n");
+
+ if (notjunc)
+ return;
+
+//
+// identify all points on common edges
+//
+
+// origin points won't allways be inside the map, so extend the hash area
+ for (i=0 ; i<3 ; i++)
+ {
+ if ( fabs(brushset->maxs[i]) > fabs(brushset->mins[i]) )
+ maxs[i] = fabs(brushset->maxs[i]);
+ else
+ maxs[i] = fabs(brushset->mins[i]);
+ }
+ VectorSubtract (vec3_origin, maxs, mins);
+
+ InitHash (mins, maxs);
+
+ numwedges = numwverts = 0;
+
+ tjunc_find_r (headnode);
+
+ qprintf ("%i world edges %i edge points\n", numwedges, numwverts);
+
+//
+// add extra vertexes on edges where needed
+//
+ tjuncs = tjuncfaces = 0;
+
+ tjunc_fix_r (headnode);
+
+ qprintf ("%i edges added by tjunctions\n", tjuncs);
+ qprintf ("%i faces added by tjunctions\n", tjuncfaces);
+}
diff --git a/qutils/QBSP/WRITEBSP.C b/qutils/QBSP/WRITEBSP.C
new file mode 100644
index 0000000..0d06232
--- /dev/null
+++ b/qutils/QBSP/WRITEBSP.C
@@ -0,0 +1,523 @@
+
+#include "bsp5.h"
+
+
+int headclipnode;
+int firstface;
+
+//===========================================================================
+
+/*
+==================
+FindFinalPlane
+
+Used to find plane index numbers for clip nodes read from child processes
+==================
+*/
+int FindFinalPlane (dplane_t *p)
+{
+ int i;
+ dplane_t *dplane;
+
+ for (i=0, dplane = dplanes ; i<numplanes ; i++, dplane++)
+ {
+ if (p->type != dplane->type)
+ continue;
+ if (p->dist != dplane->dist)
+ continue;
+ if (p->normal[0] != dplane->normal[0])
+ continue;
+ if (p->normal[1] != dplane->normal[1])
+ continue;
+ if (p->normal[2] != dplane->normal[2])
+ continue;
+ return i;
+ }
+
+//
+// new plane
+//
+ if (numplanes == MAX_MAP_PLANES)
+ Error ("numplanes == MAX_MAP_PLANES");
+ dplane = &dplanes[numplanes];
+ *dplane = *p;
+ numplanes++;
+
+ return numplanes - 1;
+}
+
+
+
+int planemapping[MAX_MAP_PLANES];
+
+void WriteNodePlanes_r (node_t *node)
+{
+ plane_t *plane;
+ dplane_t *dplane;
+
+ if (node->planenum == -1)
+ return;
+ if (planemapping[node->planenum] == -1)
+ { // a new plane
+ planemapping[node->planenum] = numplanes;
+
+ if (numplanes == MAX_MAP_PLANES)
+ Error ("numplanes == MAX_MAP_PLANES");
+ plane = &planes[node->planenum];
+ dplane = &dplanes[numplanes];
+ dplane->normal[0] = plane->normal[0];
+ dplane->normal[1] = plane->normal[1];
+ dplane->normal[2] = plane->normal[2];
+ dplane->dist = plane->dist;
+ dplane->type = plane->type;
+
+ numplanes++;
+ }
+
+ node->outputplanenum = planemapping[node->planenum];
+
+ WriteNodePlanes_r (node->children[0]);
+ WriteNodePlanes_r (node->children[1]);
+}
+
+/*
+==================
+WriteNodePlanes
+
+==================
+*/
+void WriteNodePlanes (node_t *nodes)
+{
+ memset (planemapping,-1, sizeof(planemapping));
+ WriteNodePlanes_r (nodes);
+}
+
+//===========================================================================
+
+/*
+==================
+WriteClipNodes_r
+
+==================
+*/
+int WriteClipNodes_r (node_t *node)
+{
+ int i, c;
+ dclipnode_t *cn;
+ int num;
+
+// FIXME: free more stuff?
+ if (node->planenum == -1)
+ {
+ num = node->contents;
+ free (node);
+ return num;
+ }
+
+// emit a clipnode
+ c = numclipnodes;
+ cn = &dclipnodes[numclipnodes];
+ numclipnodes++;
+ cn->planenum = node->outputplanenum;
+ for (i=0 ; i<2 ; i++)
+ cn->children[i] = WriteClipNodes_r(node->children[i]);
+
+ free (node);
+ return c;
+}
+
+/*
+==================
+WriteClipNodes
+
+Called after the clipping hull is completed. Generates a disk format
+representation and frees the original memory.
+==================
+*/
+void WriteClipNodes (node_t *nodes)
+{
+ headclipnode = numclipnodes;
+ WriteClipNodes_r (nodes);
+}
+
+//===========================================================================
+
+/*
+==================
+WriteLeaf
+==================
+*/
+void WriteLeaf (node_t *node)
+{
+ face_t **fp, *f;
+ dleaf_t *leaf_p;
+
+// emit a leaf
+ leaf_p = &dleafs[numleafs];
+ numleafs++;
+
+ leaf_p->contents = node->contents;
+
+//
+// write bounding box info
+//
+ VectorCopy (node->mins, leaf_p->mins);
+ VectorCopy (node->maxs, leaf_p->maxs);
+
+ leaf_p->visofs = -1; // no vis info yet
+
+//
+// write the marksurfaces
+//
+ leaf_p->firstmarksurface = nummarksurfaces;
+
+ for (fp=node->markfaces ; *fp ; fp++)
+ {
+ // emit a marksurface
+ if (nummarksurfaces == MAX_MAP_MARKSURFACES)
+ Error ("nummarksurfaces == MAX_MAP_MARKSURFACES");
+ f = *fp;
+ do
+ {
+ dmarksurfaces[nummarksurfaces] = f->outputnumber;
+ nummarksurfaces++;
+ f=f->original; // grab tjunction split faces
+ } while (f);
+ }
+
+ leaf_p->nummarksurfaces = nummarksurfaces - leaf_p->firstmarksurface;
+}
+
+
+/*
+==================
+WriteDrawNodes_r
+==================
+*/
+void WriteDrawNodes_r (node_t *node)
+{
+ dnode_t *n;
+ int i;
+
+// emit a node
+ if (numnodes == MAX_MAP_NODES)
+ Error ("numnodes == MAX_MAP_NODES");
+ n = &dnodes[numnodes];
+ numnodes++;
+
+ VectorCopy (node->mins, n->mins);
+ VectorCopy (node->maxs, n->maxs);
+
+ n->planenum = node->outputplanenum;
+ n->firstface = node->firstface;
+ n->numfaces = node->numfaces;
+
+//
+// recursively output the other nodes
+//
+
+ for (i=0 ; i<2 ; i++)
+ {
+ if (node->children[i]->planenum == -1)
+ {
+ if (node->children[i]->contents == CONTENTS_SOLID)
+ n->children[i] = -1;
+ else
+ {
+ n->children[i] = -(numleafs + 1);
+ WriteLeaf (node->children[i]);
+ }
+ }
+ else
+ {
+ n->children[i] = numnodes;
+ WriteDrawNodes_r (node->children[i]);
+ }
+ }
+}
+
+/*
+==================
+WriteDrawNodes
+==================
+*/
+void WriteDrawNodes (node_t *headnode)
+{
+ int i;
+ int start;
+ dmodel_t *bm;
+
+#if 0
+ if (headnode->contents < 0)
+ Error ("FinishBSPModel: empty model");
+#endif
+
+// emit a model
+ if (nummodels == MAX_MAP_MODELS)
+ Error ("nummodels == MAX_MAP_MODELS");
+ bm = &dmodels[nummodels];
+ nummodels++;
+
+ bm->headnode[0] = numnodes;
+ bm->firstface = firstface;
+ bm->numfaces = numfaces - firstface;
+ firstface = numfaces;
+
+ start = numleafs;
+
+ if (headnode->contents < 0)
+ WriteLeaf (headnode);
+ else
+ WriteDrawNodes_r (headnode);
+ bm->visleafs = numleafs - start;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ bm->mins[i] = headnode->mins[i] + SIDESPACE + 1; // remove the padding
+ bm->maxs[i] = headnode->maxs[i] - SIDESPACE - 1;
+ }
+// FIXME: are all the children decendant of padded nodes?
+}
+
+
+/*
+==================
+BumpModel
+
+Used by the clipping hull processes that only need to store headclipnode
+==================
+*/
+void BumpModel (int hullnum)
+{
+ dmodel_t *bm;
+
+// emit a model
+ if (nummodels == MAX_MAP_MODELS)
+ Error ("nummodels == MAX_MAP_MODELS");
+ bm = &dmodels[nummodels];
+ nummodels++;
+
+ bm->headnode[hullnum] = headclipnode;
+}
+
+//=============================================================================
+
+typedef struct
+{
+ char identification[4]; // should be WAD2
+ int numlumps;
+ int infotableofs;
+} wadinfo_t;
+
+
+typedef struct
+{
+ int filepos;
+ int disksize;
+ int size; // uncompressed
+ char type;
+ char compression;
+ char pad1, pad2;
+ char name[16]; // must be null terminated
+} lumpinfo_t;
+
+FILE *texfile;
+wadinfo_t wadinfo;
+lumpinfo_t *lumpinfo;
+
+void CleanupName (char *in, char *out)
+{
+ int i;
+
+ for (i=0 ; i< 16 ; i++ )
+ {
+ if (!in[i])
+ break;
+
+ out[i] = toupper(in[i]);
+ }
+
+ for ( ; i< 16 ; i++ )
+ out[i] = 0;
+}
+
+
+/*
+=================
+TEX_InitFromWad
+=================
+*/
+void TEX_InitFromWad (char *path)
+{
+ int i;
+
+ texfile = SafeOpenRead (path);
+ SafeRead (texfile, &wadinfo, sizeof(wadinfo));
+ if (strncmp (wadinfo.identification, "WAD2", 4))
+ Error ("TEX_InitFromWad: %s isn't a wadfile",path);
+ wadinfo.numlumps = LittleLong(wadinfo.numlumps);
+ wadinfo.infotableofs = LittleLong(wadinfo.infotableofs);
+ fseek (texfile, wadinfo.infotableofs, SEEK_SET);
+ lumpinfo = malloc(wadinfo.numlumps*sizeof(lumpinfo_t));
+ SafeRead (texfile, lumpinfo, wadinfo.numlumps*sizeof(lumpinfo_t));
+
+ for (i=0 ; i<wadinfo.numlumps ; i++)
+ {
+ CleanupName (lumpinfo[i].name, lumpinfo[i].name);
+ lumpinfo[i].filepos = LittleLong(lumpinfo[i].filepos);
+ lumpinfo[i].disksize = LittleLong(lumpinfo[i].disksize);
+ }
+}
+
+/*
+==================
+LoadLump
+==================
+*/
+int LoadLump (char *name, byte *dest)
+{
+ int i;
+ char cname[16];
+
+ CleanupName (name, cname);
+
+ for (i=0 ; i<wadinfo.numlumps ; i++)
+ {
+ if (!strcmp(cname, lumpinfo[i].name))
+ {
+ fseek (texfile, lumpinfo[i].filepos, SEEK_SET);
+ SafeRead (texfile, dest, lumpinfo[i].disksize);
+ return lumpinfo[i].disksize;
+ }
+ }
+
+ printf ("WARNING: texture %s not found\n", name);
+ return 0;
+}
+
+
+/*
+==================
+AddAnimatingTextures
+==================
+*/
+void AddAnimatingTextures (void)
+{
+ int base;
+ int i, j, k;
+ char name[32];
+
+ base = nummiptex;
+
+ for (i=0 ; i<base ; i++)
+ {
+ if (miptex[i][0] != '+')
+ continue;
+ strcpy (name, miptex[i]);
+
+ for (j=0 ; j<20 ; j++)
+ {
+ if (j < 10)
+ name[1] = '0'+j;
+ else
+ name[1] = 'A'+j-10; // alternate animation
+
+
+ // see if this name exists in the wadfile
+ for (k=0 ; k<wadinfo.numlumps ; k++)
+ if (!strcmp(name, lumpinfo[k].name))
+ {
+ FindMiptex (name); // add to the miptex list
+ break;
+ }
+ }
+ }
+
+ printf ("added %i texture frames\n", nummiptex - base);
+}
+
+/*
+==================
+WriteMiptex
+==================
+*/
+void WriteMiptex (void)
+{
+ int i, len;
+ byte *data;
+ dmiptexlump_t *l;
+ char *path;
+ char fullpath[1024];
+
+ path = ValueForKey (&entities[0], "_wad");
+ if (!path || !path[0])
+ {
+ path = ValueForKey (&entities[0], "wad");
+ if (!path || !path[0])
+ {
+ printf ("WARNING: no wadfile specified\n");
+ texdatasize = 0;
+ return;
+ }
+ }
+
+ sprintf (fullpath, "%s/%s", gamedir, path);
+
+ TEX_InitFromWad (fullpath);
+
+ AddAnimatingTextures ();
+
+ l = (dmiptexlump_t *)dtexdata;
+ data = (byte *)&l->dataofs[nummiptex];
+ l->nummiptex = nummiptex;
+ for (i=0 ; i<nummiptex ; i++)
+ {
+ l->dataofs[i] = data - (byte *)l;
+ len = LoadLump (miptex[i], data);
+ if (data + len - dtexdata >= MAX_MAP_MIPTEX)
+ Error ("Textures exceeded MAX_MAP_MIPTEX");
+ if (!len)
+ l->dataofs[i] = -1; // didn't find the texture
+ data += len;
+ }
+
+ texdatasize = data - dtexdata;
+}
+
+//===========================================================================
+
+
+/*
+==================
+BeginBSPFile
+==================
+*/
+void BeginBSPFile (void)
+{
+// edge 0 is not used, because 0 can't be negated
+ numedges = 1;
+
+// leaf 0 is common solid with no faces
+ numleafs = 1;
+ dleafs[0].contents = CONTENTS_SOLID;
+
+ firstface = 0;
+}
+
+
+/*
+==================
+FinishBSPFile
+==================
+*/
+void FinishBSPFile (void)
+{
+ printf ("--- FinishBSPFile ---\n");
+ printf ("WriteBSPFile: %s\n", bspfilename);
+
+ WriteMiptex ();
+
+ PrintBSPFileSizes ();
+ WriteBSPFile (bspfilename);
+}
+
diff --git a/qutils/QCC/MAKEFILE b/qutils/QCC/MAKEFILE
new file mode 100644
index 0000000..c07c8e5
--- /dev/null
+++ b/qutils/QCC/MAKEFILE
@@ -0,0 +1,42 @@
+
+EXES = qcc
+NTEXES = qcc.exe
+
+#==============================================================================
+
+EXT= .o
+
+all: $(EXES)
+
+clean:
+ rm *.o *.obj $(EXES) $(NTEXES)
+
+next:
+ make "CFLAGS = -g -Wall -I.."
+
+nextinstall:
+ make "CFLAGS = -O4 -g -Wall -I.. -arch i386 -arch hppa"
+ cp $(EXES) /LocalApps
+
+alpha:
+ make "CFLAGS = -g -I.." "LDFLAGS = -lm"
+
+alphainstall:
+ make "CFLAGS = -O4 -I.." "LDFLAGS = -lm"
+ cp $(EXES) /LocalApps
+
+nt:
+ nmake /nologo "CFLAGS = -nologo -Zi -DWIN32 -I.." "LDFLAGS = " "EXT = .obj"
+
+ntinstall:
+ nmake /nologo "CFLAGS = -nologo -Ox -G5 -DWIN32 -I.." "LDFLAGS = " "EXT = .obj"
+ cp $(NTEXES) f:\nt\id
+
+#==============================================================================
+
+QCCFILES = qcc$(EXT) pr_lex$(EXT) pr_comp$(EXT) cmdlib$(EXT)
+qcc : $(QCCFILES)
+ $(CC) $(CFLAGS) -o qcc $(QCCFILES)
+
+cmdlib$(EXT) : ../cmdlib.c
+ $(CC) $(CFLAGS) -c -o cmdlib$(EXT) ../cmdlib.c
diff --git a/qutils/QCC/PR_COMP.C b/qutils/QCC/PR_COMP.C
new file mode 100644
index 0000000..7c49599
--- /dev/null
+++ b/qutils/QCC/PR_COMP.C
@@ -0,0 +1,936 @@
+
+#include "qcc.h"
+
+
+pr_info_t pr;
+def_t *pr_global_defs[MAX_REGS]; // to find def for a global variable
+int pr_edict_size;
+
+//========================================
+
+def_t *pr_scope; // the function being parsed, or NULL
+qboolean pr_dumpasm;
+string_t s_file; // filename for function definition
+
+int locals_end; // for tracking local variables vs temps
+
+jmp_buf pr_parse_abort; // longjump with this on parse error
+
+void PR_ParseDefs (void);
+
+//========================================
+
+
+opcode_t pr_opcodes[] =
+{
+ {"<DONE>", "DONE", -1, false, &def_entity, &def_field, &def_void},
+
+ {"*", "MUL_F", 2, false, &def_float, &def_float, &def_float},
+ {"*", "MUL_V", 2, false, &def_vector, &def_vector, &def_float},
+ {"*", "MUL_FV", 2, false, &def_float, &def_vector, &def_vector},
+ {"*", "MUL_VF", 2, false, &def_vector, &def_float, &def_vector},
+
+ {"/", "DIV", 2, false, &def_float, &def_float, &def_float},
+
+ {"+", "ADD_F", 3, false, &def_float, &def_float, &def_float},
+ {"+", "ADD_V", 3, false, &def_vector, &def_vector, &def_vector},
+
+ {"-", "SUB_F", 3, false, &def_float, &def_float, &def_float},
+ {"-", "SUB_V", 3, false, &def_vector, &def_vector, &def_vector},
+
+ {"==", "EQ_F", 4, false, &def_float, &def_float, &def_float},
+ {"==", "EQ_V", 4, false, &def_vector, &def_vector, &def_float},
+ {"==", "EQ_S", 4, false, &def_string, &def_string, &def_float},
+ {"==", "EQ_E", 4, false, &def_entity, &def_entity, &def_float},
+ {"==", "EQ_FNC", 4, false, &def_function, &def_function, &def_float},
+
+ {"!=", "NE_F", 4, false, &def_float, &def_float, &def_float},
+ {"!=", "NE_V", 4, false, &def_vector, &def_vector, &def_float},
+ {"!=", "NE_S", 4, false, &def_string, &def_string, &def_float},
+ {"!=", "NE_E", 4, false, &def_entity, &def_entity, &def_float},
+ {"!=", "NE_FNC", 4, false, &def_function, &def_function, &def_float},
+
+ {"<=", "LE", 4, false, &def_float, &def_float, &def_float},
+ {">=", "GE", 4, false, &def_float, &def_float, &def_float},
+ {"<", "LT", 4, false, &def_float, &def_float, &def_float},
+ {">", "GT", 4, false, &def_float, &def_float, &def_float},
+
+ {".", "INDIRECT", 1, false, &def_entity, &def_field, &def_float},
+ {".", "INDIRECT", 1, false, &def_entity, &def_field, &def_vector},
+ {".", "INDIRECT", 1, false, &def_entity, &def_field, &def_string},
+ {".", "INDIRECT", 1, false, &def_entity, &def_field, &def_entity},
+ {".", "INDIRECT", 1, false, &def_entity, &def_field, &def_field},
+ {".", "INDIRECT", 1, false, &def_entity, &def_field, &def_function},
+
+ {".", "ADDRESS", 1, false, &def_entity, &def_field, &def_pointer},
+
+ {"=", "STORE_F", 5, true, &def_float, &def_float, &def_float},
+ {"=", "STORE_V", 5, true, &def_vector, &def_vector, &def_vector},
+ {"=", "STORE_S", 5, true, &def_string, &def_string, &def_string},
+ {"=", "STORE_ENT", 5, true, &def_entity, &def_entity, &def_entity},
+ {"=", "STORE_FLD", 5, true, &def_field, &def_field, &def_field},
+ {"=", "STORE_FNC", 5, true, &def_function, &def_function, &def_function},
+
+ {"=", "STOREP_F", 5, true, &def_pointer, &def_float, &def_float},
+ {"=", "STOREP_V", 5, true, &def_pointer, &def_vector, &def_vector},
+ {"=", "STOREP_S", 5, true, &def_pointer, &def_string, &def_string},
+ {"=", "STOREP_ENT", 5, true, &def_pointer, &def_entity, &def_entity},
+ {"=", "STOREP_FLD", 5, true, &def_pointer, &def_field, &def_field},
+ {"=", "STOREP_FNC", 5, true, &def_pointer, &def_function, &def_function},
+
+ {"<RETURN>", "RETURN", -1, false, &def_void, &def_void, &def_void},
+
+ {"!", "NOT_F", -1, false, &def_float, &def_void, &def_float},
+ {"!", "NOT_V", -1, false, &def_vector, &def_void, &def_float},
+ {"!", "NOT_S", -1, false, &def_vector, &def_void, &def_float},
+ {"!", "NOT_ENT", -1, false, &def_entity, &def_void, &def_float},
+ {"!", "NOT_FNC", -1, false, &def_function, &def_void, &def_float},
+
+ {"<IF>", "IF", -1, false, &def_float, &def_float, &def_void},
+ {"<IFNOT>", "IFNOT", -1, false, &def_float, &def_float, &def_void},
+
+// calls returns REG_RETURN
+ {"<CALL0>", "CALL0", -1, false, &def_function, &def_void, &def_void},
+ {"<CALL1>", "CALL1", -1, false, &def_function, &def_void, &def_void},
+ {"<CALL2>", "CALL2", -1, false, &def_function, &def_void, &def_void},
+ {"<CALL3>", "CALL3", -1, false, &def_function, &def_void, &def_void},
+ {"<CALL4>", "CALL4", -1, false, &def_function, &def_void, &def_void},
+ {"<CALL5>", "CALL5", -1, false, &def_function, &def_void, &def_void},
+ {"<CALL6>", "CALL6", -1, false, &def_function, &def_void, &def_void},
+ {"<CALL7>", "CALL7", -1, false, &def_function, &def_void, &def_void},
+ {"<CALL8>", "CALL8", -1, false, &def_function, &def_void, &def_void},
+
+ {"<STATE>", "STATE", -1, false, &def_float, &def_float, &def_void},
+
+ {"<GOTO>", "GOTO", -1, false, &def_float, &def_void, &def_void},
+
+ {"&&", "AND", 6, false, &def_float, &def_float, &def_float},
+ {"||", "OR", 6, false, &def_float, &def_float, &def_float},
+
+ {"&", "BITAND", 2, false, &def_float, &def_float, &def_float},
+ {"|", "BITOR", 2, false, &def_float, &def_float, &def_float},
+
+ {NULL}
+};
+
+#define TOP_PRIORITY 6
+#define NOT_PRIORITY 4
+
+def_t *PR_Expression (int priority);
+
+def_t junkdef;
+
+//===========================================================================
+
+
+/*
+============
+PR_Statement
+
+Emits a primitive statement, returning the var it places it's value in
+============
+*/
+def_t *PR_Statement ( opcode_t *op, def_t *var_a, def_t *var_b)
+{
+ dstatement_t *statement;
+ def_t *var_c;
+
+ statement = &statements[numstatements];
+ numstatements++;
+
+ statement_linenums[statement-statements] = pr_source_line;
+ statement->op = op - pr_opcodes;
+ statement->a = var_a ? var_a->ofs : 0;
+ statement->b = var_b ? var_b->ofs : 0;
+ if (op->type_c == &def_void || op->right_associative)
+ {
+ var_c = NULL;
+ statement->c = 0; // ifs, gotos, and assignments
+ // don't need vars allocated
+ }
+ else
+ { // allocate result space
+ var_c = malloc (sizeof(def_t));
+ memset (var_c, 0, sizeof(def_t));
+ var_c->ofs = numpr_globals;
+ var_c->type = op->type_c->type;
+
+ statement->c = numpr_globals;
+ numpr_globals += type_size[op->type_c->type->type];
+ }
+
+ if (op->right_associative)
+ return var_a;
+ return var_c;
+}
+
+/*
+============
+PR_ParseImmediate
+
+Looks for a preexisting constant
+============
+*/
+def_t *PR_ParseImmediate (void)
+{
+ def_t *cn;
+
+// check for a constant with the same value
+ for (cn=pr.def_head.next ; cn ; cn=cn->next)
+ {
+ if (!cn->initialized)
+ continue;
+ if (cn->type != pr_immediate_type)
+ continue;
+ if (pr_immediate_type == &type_string)
+ {
+ if (!strcmp(G_STRING(cn->ofs), pr_immediate_string) )
+ {
+ PR_Lex ();
+ return cn;
+ }
+ }
+ else if (pr_immediate_type == &type_float)
+ {
+ if ( G_FLOAT(cn->ofs) == pr_immediate._float )
+ {
+ PR_Lex ();
+ return cn;
+ }
+ }
+ else if (pr_immediate_type == &type_vector)
+ {
+ if ( ( G_FLOAT(cn->ofs) == pr_immediate.vector[0] )
+ && ( G_FLOAT(cn->ofs+1) == pr_immediate.vector[1] )
+ && ( G_FLOAT(cn->ofs+2) == pr_immediate.vector[2] ) )
+ {
+ PR_Lex ();
+ return cn;
+ }
+ }
+ else
+ PR_ParseError ("weird immediate type");
+ }
+
+// allocate a new one
+ cn = malloc (sizeof(def_t));
+ cn->next = NULL;
+
+ pr.def_tail->next = cn;
+ pr.def_tail = cn;
+
+ cn->search_next = pr.search;
+ pr.search = cn;
+
+ cn->type = pr_immediate_type;
+ cn->name = "IMMEDIATE";
+ cn->initialized = 1;
+ cn->scope = NULL; // always share immediates
+
+// copy the immediate to the global area
+ cn->ofs = numpr_globals;
+ pr_global_defs[cn->ofs] = cn;
+ numpr_globals += type_size[pr_immediate_type->type];
+ if (pr_immediate_type == &type_string)
+ pr_immediate.string = CopyString (pr_immediate_string);
+
+ memcpy (pr_globals + cn->ofs, &pr_immediate, 4*type_size[pr_immediate_type->type]);
+
+ PR_Lex ();
+
+ return cn;
+}
+
+
+void PrecacheSound (def_t *e, int ch)
+{
+ char *n;
+ int i;
+
+ if (!e->ofs)
+ return;
+ n = G_STRING(e->ofs);
+ for (i=0 ; i<numsounds ; i++)
+ if (!strcmp(n, precache_sounds[i]))
+ return;
+ if (numsounds == MAX_SOUNDS)
+ Error ("PrecacheSound: numsounds == MAX_SOUNDS");
+ strcpy (precache_sounds[i], n);
+ if (ch >= '1' && ch <= '9')
+ precache_sounds_block[i] = ch - '0';
+ else
+ precache_sounds_block[i] = 1;
+ numsounds++;
+}
+
+void PrecacheModel (def_t *e, int ch)
+{
+ char *n;
+ int i;
+
+ if (!e->ofs)
+ return;
+ n = G_STRING(e->ofs);
+ for (i=0 ; i<nummodels ; i++)
+ if (!strcmp(n, precache_models[i]))
+ return;
+ if (numsounds == MAX_SOUNDS)
+ Error ("PrecacheModels: numsounds == MAX_SOUNDS");
+ strcpy (precache_models[i], n);
+ if (ch >= '1' && ch <= '9')
+ precache_models_block[i] = ch - '0';
+ else
+ precache_models_block[i] = 1;
+ nummodels++;
+}
+
+void PrecacheFile (def_t *e, int ch)
+{
+ char *n;
+ int i;
+
+ if (!e->ofs)
+ return;
+ n = G_STRING(e->ofs);
+ for (i=0 ; i<numfiles ; i++)
+ if (!strcmp(n, precache_files[i]))
+ return;
+ if (numfiles == MAX_FILES)
+ Error ("PrecacheFile: numfiles == MAX_FILES");
+ strcpy (precache_files[i], n);
+ if (ch >= '1' && ch <= '9')
+ precache_files_block[i] = ch - '0';
+ else
+ precache_files_block[i] = 1;
+ numfiles++;
+}
+
+/*
+============
+PR_ParseFunctionCall
+============
+*/
+def_t *PR_ParseFunctionCall (def_t *func)
+{
+ def_t *e;
+ int arg;
+ type_t *t;
+
+ t = func->type;
+
+ if (t->type != ev_function)
+ PR_ParseError ("not a function");
+
+// copy the arguments to the global parameter variables
+ arg = 0;
+ if (!PR_Check(")"))
+ {
+ do
+ {
+ if (t->num_parms != -1 && arg >= t->num_parms)
+ PR_ParseError ("too many parameters");
+ e = PR_Expression (TOP_PRIORITY);
+
+ if (arg == 0 && func->name)
+ {
+ // save information for model and sound caching
+ if (!strncmp(func->name,"precache_sound", 14))
+ PrecacheSound (e, func->name[14]);
+ else if (!strncmp(func->name,"precache_model", 14))
+ PrecacheModel (e, func->name[14]);
+ else if (!strncmp(func->name,"precache_file", 13))
+ PrecacheFile (e, func->name[13]);
+ }
+
+ if (t->num_parms != -1 && ( e->type != t->parm_types[arg] ) )
+ PR_ParseError ("type mismatch on parm %i", arg);
+ // a vector copy will copy everything
+ def_parms[arg].type = t->parm_types[arg];
+ PR_Statement (&pr_opcodes[OP_STORE_V], e, &def_parms[arg]);
+ arg++;
+ } while (PR_Check (","));
+
+ if (t->num_parms != -1 && arg != t->num_parms)
+ PR_ParseError ("too few parameters");
+ PR_Expect (")");
+ }
+ if (arg >8)
+ PR_ParseError ("More than eight parameters");
+
+
+ PR_Statement (&pr_opcodes[OP_CALL0+arg], func, 0);
+
+ def_ret.type = t->aux_type;
+ return &def_ret;
+}
+
+/*
+============
+PR_ParseValue
+
+Returns the global ofs for the current token
+============
+*/
+def_t *PR_ParseValue (void)
+{
+ def_t *d;
+ char *name;
+
+// if the token is an immediate, allocate a constant for it
+ if (pr_token_type == tt_immediate)
+ return PR_ParseImmediate ();
+
+ name = PR_ParseName ();
+
+// look through the defs
+ d = PR_GetDef (NULL, name, pr_scope, false);
+ if (!d)
+ PR_ParseError ("Unknown value \"%s\"", name);
+ return d;
+}
+
+
+/*
+============
+PR_Term
+============
+*/
+def_t *PR_Term (void)
+{
+ def_t *e, *e2;
+ etype_t t;
+
+ if (PR_Check ("!"))
+ {
+ e = PR_Expression (NOT_PRIORITY);
+ t = e->type->type;
+ if (t == ev_float)
+ e2 = PR_Statement (&pr_opcodes[OP_NOT_F], e, 0);
+ else if (t == ev_string)
+ e2 = PR_Statement (&pr_opcodes[OP_NOT_S], e, 0);
+ else if (t == ev_entity)
+ e2 = PR_Statement (&pr_opcodes[OP_NOT_ENT], e, 0);
+ else if (t == ev_vector)
+ e2 = PR_Statement (&pr_opcodes[OP_NOT_V], e, 0);
+ else if (t == ev_function)
+ e2 = PR_Statement (&pr_opcodes[OP_NOT_FNC], e, 0);
+ else
+ {
+ e2 = NULL; // shut up compiler warning;
+ PR_ParseError ("type mismatch for !");
+ }
+ return e2;
+ }
+
+ if (PR_Check ("("))
+ {
+ e = PR_Expression (TOP_PRIORITY);
+ PR_Expect (")");
+ return e;
+ }
+
+ return PR_ParseValue ();
+}
+
+/*
+==============
+PR_Expression
+==============
+*/
+
+def_t *PR_Expression (int priority)
+{
+ opcode_t *op, *oldop;
+ def_t *e, *e2;
+ etype_t type_a, type_b, type_c;
+
+ if (priority == 0)
+ return PR_Term ();
+
+ e = PR_Expression (priority-1);
+
+ while (1)
+ {
+ if (priority == 1 && PR_Check ("(") )
+ return PR_ParseFunctionCall (e);
+
+ for (op=pr_opcodes ; op->name ; op++)
+ {
+ if (op->priority != priority)
+ continue;
+ if (!PR_Check (op->name))
+ continue;
+ if ( op->right_associative )
+ {
+ // if last statement is an indirect, change it to an address of
+ if ( (unsigned)(statements[numstatements-1].op - OP_LOAD_F) < 6 )
+ {
+ statements[numstatements-1].op = OP_ADDRESS;
+ def_pointer.type->aux_type = e->type;
+ e->type = def_pointer.type;
+ }
+ e2 = PR_Expression (priority);
+ }
+ else
+ e2 = PR_Expression (priority-1);
+
+ // type check
+ type_a = e->type->type;
+ type_b = e2->type->type;
+
+ if (op->name[0] == '.')// field access gets type from field
+ {
+ if (e2->type->aux_type)
+ type_c = e2->type->aux_type->type;
+ else
+ type_c = -1; // not a field
+ }
+ else
+ type_c = ev_void;
+
+ oldop = op;
+ while (type_a != op->type_a->type->type
+ || type_b != op->type_b->type->type
+ || (type_c != ev_void && type_c != op->type_c->type->type) )
+ {
+ op++;
+ if (!op->name || strcmp (op->name , oldop->name))
+ PR_ParseError ("type mismatch for %s", oldop->name);
+ }
+
+ if (type_a == ev_pointer && type_b != e->type->aux_type->type)
+ PR_ParseError ("type mismatch for %s", op->name);
+
+
+ if (op->right_associative)
+ e = PR_Statement (op, e2, e);
+ else
+ e = PR_Statement (op, e, e2);
+
+ if (type_c != ev_void) // field access gets type from field
+ e->type = e2->type->aux_type;
+
+ break;
+ }
+ if (!op->name)
+ break; // next token isn't at this priority level
+ }
+
+ return e;
+}
+
+
+/*
+============
+PR_ParseStatement
+
+============
+*/
+void PR_ParseStatement (void)
+{
+ def_t *e;
+ dstatement_t *patch1, *patch2;
+
+ if (PR_Check ("{"))
+ {
+ do
+ {
+ PR_ParseStatement ();
+ } while (!PR_Check ("}"));
+ return;
+ }
+
+ if (PR_Check("return"))
+ {
+ if (PR_Check (";"))
+ {
+ PR_Statement (&pr_opcodes[OP_RETURN], 0, 0);
+ return;
+ }
+ e = PR_Expression (TOP_PRIORITY);
+ PR_Expect (";");
+ PR_Statement (&pr_opcodes[OP_RETURN], e, 0);
+ return;
+ }
+
+ if (PR_Check("while"))
+ {
+ PR_Expect ("(");
+ patch2 = &statements[numstatements];
+ e = PR_Expression (TOP_PRIORITY);
+ PR_Expect (")");
+ patch1 = &statements[numstatements];
+ PR_Statement (&pr_opcodes[OP_IFNOT], e, 0);
+ PR_ParseStatement ();
+ junkdef.ofs = patch2 - &statements[numstatements];
+ PR_Statement (&pr_opcodes[OP_GOTO], &junkdef, 0);
+ patch1->b = &statements[numstatements] - patch1;
+ return;
+ }
+
+ if (PR_Check("do"))
+ {
+ patch1 = &statements[numstatements];
+ PR_ParseStatement ();
+ PR_Expect ("while");
+ PR_Expect ("(");
+ e = PR_Expression (TOP_PRIORITY);
+ PR_Expect (")");
+ PR_Expect (";");
+ junkdef.ofs = patch1 - &statements[numstatements];
+ PR_Statement (&pr_opcodes[OP_IF], e, &junkdef);
+ return;
+ }
+
+ if (PR_Check("local"))
+ {
+ PR_ParseDefs ();
+ locals_end = numpr_globals;
+ return;
+ }
+
+ if (PR_Check("if"))
+ {
+ PR_Expect ("(");
+ e = PR_Expression (TOP_PRIORITY);
+ PR_Expect (")");
+
+ patch1 = &statements[numstatements];
+ PR_Statement (&pr_opcodes[OP_IFNOT], e, 0);
+
+ PR_ParseStatement ();
+
+ if (PR_Check ("else"))
+ {
+ patch2 = &statements[numstatements];
+ PR_Statement (&pr_opcodes[OP_GOTO], 0, 0);
+ patch1->b = &statements[numstatements] - patch1;
+ PR_ParseStatement ();
+ patch2->a = &statements[numstatements] - patch2;
+ }
+ else
+ patch1->b = &statements[numstatements] - patch1;
+
+ return;
+ }
+
+ PR_Expression (TOP_PRIORITY);
+ PR_Expect (";");
+}
+
+
+/*
+==============
+PR_ParseState
+
+States are special functions made for convenience. They automatically
+set frame, nextthink (implicitly), and think (allowing forward definitions).
+
+// void() name = [framenum, nextthink] {code}
+// expands to:
+// function void name ()
+// {
+// self.frame=framenum;
+// self.nextthink = time + 0.1;
+// self.think = nextthink
+// <code>
+// };
+==============
+*/
+void PR_ParseState (void)
+{
+ char *name;
+ def_t *s1, *def;
+
+ if (pr_token_type != tt_immediate || pr_immediate_type != &type_float)
+ PR_ParseError ("state frame must be a number");
+ s1 = PR_ParseImmediate ();
+
+ PR_Expect (",");
+
+ name = PR_ParseName ();
+ def = PR_GetDef (&type_function, name,0, true);
+
+ PR_Expect ("]");
+
+ PR_Statement (&pr_opcodes[OP_STATE], s1, def);
+}
+
+/*
+============
+PR_ParseImmediateStatements
+
+Parse a function body
+============
+*/
+function_t *PR_ParseImmediateStatements (type_t *type)
+{
+ int i;
+ function_t *f;
+ def_t *defs[MAX_PARMS];
+
+ f = malloc (sizeof(function_t));
+
+//
+// check for builtin function definition #1, #2, etc
+//
+ if (PR_Check ("#"))
+ {
+ if (pr_token_type != tt_immediate
+ || pr_immediate_type != &type_float
+ || pr_immediate._float != (int)pr_immediate._float)
+ PR_ParseError ("Bad builtin immediate");
+ f->builtin = (int)pr_immediate._float;
+ PR_Lex ();
+ return f;
+ }
+
+ f->builtin = 0;
+//
+// define the parms
+//
+ for (i=0 ; i<type->num_parms ; i++)
+ {
+ defs[i] = PR_GetDef (type->parm_types[i], pr_parm_names[i], pr_scope, true);
+ f->parm_ofs[i] = defs[i]->ofs;
+ if (i > 0 && f->parm_ofs[i] < f->parm_ofs[i-1])
+ Error ("bad parm order");
+ }
+
+ f->code = numstatements;
+
+//
+// check for a state opcode
+//
+ if (PR_Check ("["))
+ PR_ParseState ();
+
+//
+// parse regular statements
+//
+ PR_Expect ("{");
+
+ while (!PR_Check("}"))
+ PR_ParseStatement ();
+
+// emit an end of statements opcode
+ PR_Statement (pr_opcodes, 0,0);
+
+
+ return f;
+}
+
+/*
+============
+PR_GetDef
+
+If type is NULL, it will match any type
+If allocate is true, a new def will be allocated if it can't be found
+============
+*/
+def_t *PR_GetDef (type_t *type, char *name, def_t *scope, qboolean allocate)
+{
+ def_t *def, **old;
+ char element[MAX_NAME];
+
+// see if the name is already in use
+ old = &pr.search;
+ for (def = *old ; def ; old=&def->search_next,def = *old)
+ if (!strcmp(def->name,name) )
+ {
+ if ( def->scope && def->scope != scope)
+ continue; // in a different function
+
+ if (type && def->type != type)
+ PR_ParseError ("Type mismatch on redeclaration of %s",name);
+
+ // move to head of list to find fast next time
+ *old = def->search_next;
+ def->search_next = pr.search;
+ pr.search = def;
+ return def;
+ }
+
+ if (!allocate)
+ return NULL;
+
+// allocate a new def
+ def = malloc (sizeof(def_t));
+ memset (def, 0, sizeof(*def));
+ def->next = NULL;
+ pr.def_tail->next = def;
+ pr.def_tail = def;
+
+ def->search_next = pr.search;
+ pr.search = def;
+
+ def->name = malloc (strlen(name)+1);
+ strcpy (def->name, name);
+ def->type = type;
+
+ def->scope = scope;
+
+ def->ofs = numpr_globals;
+ pr_global_defs[numpr_globals] = def;
+
+//
+// make automatic defs for the vectors elements
+// .origin can be accessed as .origin_x, .origin_y, and .origin_z
+//
+ if (type->type == ev_vector)
+ {
+ sprintf (element, "%s_x",name);
+ PR_GetDef (&type_float, element, scope, true);
+
+ sprintf (element, "%s_y",name);
+ PR_GetDef (&type_float, element, scope, true);
+
+ sprintf (element, "%s_z",name);
+ PR_GetDef (&type_float, element, scope, true);
+ }
+ else
+ numpr_globals += type_size[type->type];
+
+ if (type->type == ev_field)
+ {
+ *(int *)&pr_globals[def->ofs] = pr.size_fields;
+
+ if (type->aux_type->type == ev_vector)
+ {
+ sprintf (element, "%s_x",name);
+ PR_GetDef (&type_floatfield, element, scope, true);
+
+ sprintf (element, "%s_y",name);
+ PR_GetDef (&type_floatfield, element, scope, true);
+
+ sprintf (element, "%s_z",name);
+ PR_GetDef (&type_floatfield, element, scope, true);
+ }
+ else
+ pr.size_fields += type_size[type->aux_type->type];
+ }
+
+// if (pr_dumpasm)
+// PR_PrintOfs (def->ofs);
+
+ return def;
+}
+
+/*
+================
+PR_ParseDefs
+
+Called at the outer layer and when a local statement is hit
+================
+*/
+void PR_ParseDefs (void)
+{
+ char *name;
+ type_t *type;
+ def_t *def;
+ function_t *f;
+ dfunction_t *df;
+ int i;
+ int locals_start;
+
+ type = PR_ParseType ();
+
+ if (pr_scope && (type->type == ev_field || type->type == ev_function) )
+ PR_ParseError ("Fields and functions must be global");
+
+ do
+ {
+ name = PR_ParseName ();
+
+ def = PR_GetDef (type, name, pr_scope, true);
+
+// check for an initialization
+ if ( PR_Check ("=") )
+ {
+ if (def->initialized)
+ PR_ParseError ("%s redeclared", name);
+
+ if (type->type == ev_function)
+ {
+ locals_start = locals_end = numpr_globals;
+ pr_scope = def;
+ f = PR_ParseImmediateStatements (type);
+ pr_scope = NULL;
+ def->initialized = 1;
+ G_FUNCTION(def->ofs) = numfunctions;
+ f->def = def;
+// if (pr_dumpasm)
+// PR_PrintFunction (def);
+
+ // fill in the dfunction
+ df = &functions[numfunctions];
+ numfunctions++;
+ if (f->builtin)
+ df->first_statement = -f->builtin;
+ else
+ df->first_statement = f->code;
+ df->s_name = CopyString (f->def->name);
+ df->s_file = s_file;
+ df->numparms = f->def->type->num_parms;
+ df->locals = locals_end - locals_start;
+ df->parm_start = locals_start;
+ for (i=0 ; i<df->numparms ; i++)
+ df->parm_size[i] = type_size[f->def->type->parm_types[i]->type];
+
+ continue;
+ }
+ else if (pr_immediate_type != type)
+ PR_ParseError ("wrong immediate type for %s", name);
+
+ def->initialized = 1;
+ memcpy (pr_globals + def->ofs, &pr_immediate, 4*type_size[pr_immediate_type->type]);
+ PR_Lex ();
+ }
+
+ } while (PR_Check (","));
+
+ PR_Expect (";");
+}
+
+/*
+============
+PR_CompileFile
+
+compiles the 0 terminated text, adding defintions to the pr structure
+============
+*/
+qboolean PR_CompileFile (char *string, char *filename)
+{
+ if (!pr.memory)
+ Error ("PR_CompileFile: Didn't clear");
+
+ PR_ClearGrabMacros (); // clear the frame macros
+
+ pr_file_p = string;
+ s_file = CopyString (filename);
+
+ pr_source_line = 0;
+
+ PR_NewLine ();
+
+ PR_Lex (); // read first token
+
+ while (pr_token_type != tt_eof)
+ {
+ if (setjmp(pr_parse_abort))
+ {
+ if (++pr_error_count > MAX_ERRORS)
+ return false;
+ PR_SkipToSemicolon ();
+ if (pr_token_type == tt_eof)
+ return false;
+ }
+
+ pr_scope = NULL; // outside all functions
+
+ PR_ParseDefs ();
+ }
+
+ return (pr_error_count == 0);
+}
+
diff --git a/qutils/QCC/PR_COMP.H b/qutils/QCC/PR_COMP.H
new file mode 100644
index 0000000..c31947c
--- /dev/null
+++ b/qutils/QCC/PR_COMP.H
@@ -0,0 +1,161 @@
+
+// this file is shared by quake and qcc
+
+typedef int func_t;
+typedef int string_t;
+
+typedef enum {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer} etype_t;
+
+
+#define OFS_NULL 0
+#define OFS_RETURN 1
+#define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors
+#define OFS_PARM1 7
+#define OFS_PARM2 10
+#define OFS_PARM3 13
+#define OFS_PARM4 16
+#define OFS_PARM5 19
+#define OFS_PARM6 22
+#define OFS_PARM7 25
+#define RESERVED_OFS 28
+
+
+enum {
+ OP_DONE,
+ OP_MUL_F,
+ OP_MUL_V,
+ OP_MUL_FV,
+ OP_MUL_VF,
+ OP_DIV_F,
+ OP_ADD_F,
+ OP_ADD_V,
+ OP_SUB_F,
+ OP_SUB_V,
+
+ OP_EQ_F,
+ OP_EQ_V,
+ OP_EQ_S,
+ OP_EQ_E,
+ OP_EQ_FNC,
+
+ OP_NE_F,
+ OP_NE_V,
+ OP_NE_S,
+ OP_NE_E,
+ OP_NE_FNC,
+
+ OP_LE,
+ OP_GE,
+ OP_LT,
+ OP_GT,
+
+ OP_LOAD_F,
+ OP_LOAD_V,
+ OP_LOAD_S,
+ OP_LOAD_ENT,
+ OP_LOAD_FLD,
+ OP_LOAD_FNC,
+
+ OP_ADDRESS,
+
+ OP_STORE_F,
+ OP_STORE_V,
+ OP_STORE_S,
+ OP_STORE_ENT,
+ OP_STORE_FLD,
+ OP_STORE_FNC,
+
+ OP_STOREP_F,
+ OP_STOREP_V,
+ OP_STOREP_S,
+ OP_STOREP_ENT,
+ OP_STOREP_FLD,
+ OP_STOREP_FNC,
+
+ OP_RETURN,
+ OP_NOT_F,
+ OP_NOT_V,
+ OP_NOT_S,
+ OP_NOT_ENT,
+ OP_NOT_FNC,
+ OP_IF,
+ OP_IFNOT,
+ OP_CALL0,
+ OP_CALL1,
+ OP_CALL2,
+ OP_CALL3,
+ OP_CALL4,
+ OP_CALL5,
+ OP_CALL6,
+ OP_CALL7,
+ OP_CALL8,
+ OP_STATE,
+ OP_GOTO,
+ OP_AND,
+ OP_OR,
+
+ OP_BITAND,
+ OP_BITOR
+};
+
+
+typedef struct statement_s
+{
+ unsigned short op;
+ short a,b,c;
+} dstatement_t;
+
+typedef struct
+{
+ unsigned short type; // if DEF_SAVEGLOBGAL bit is set
+ // the variable needs to be saved in savegames
+ unsigned short ofs;
+ int s_name;
+} ddef_t;
+#define DEF_SAVEGLOBGAL (1<<15)
+
+#define MAX_PARMS 8
+
+typedef struct
+{
+ int first_statement; // negative numbers are builtins
+ int parm_start;
+ int locals; // total ints of parms + locals
+
+ int profile; // runtime
+
+ int s_name;
+ int s_file; // source file defined in
+
+ int numparms;
+ byte parm_size[MAX_PARMS];
+} dfunction_t;
+
+
+#define PROG_VERSION 6
+typedef struct
+{
+ int version;
+ int crc; // check of header file
+
+ int ofs_statements;
+ int numstatements; // statement 0 is an error
+
+ int ofs_globaldefs;
+ int numglobaldefs;
+
+ int ofs_fielddefs;
+ int numfielddefs;
+
+ int ofs_functions;
+ int numfunctions; // function 0 is an empty
+
+ int ofs_strings;
+ int numstrings; // first string is a null string
+
+ int ofs_globals;
+ int numglobals;
+
+ int entityfields;
+} dprograms_t;
+
diff --git a/qutils/QCC/PR_LEX.C b/qutils/QCC/PR_LEX.C
new file mode 100644
index 0000000..10983bb
--- /dev/null
+++ b/qutils/QCC/PR_LEX.C
@@ -0,0 +1,677 @@
+
+#include "qcc.h"
+
+int pr_source_line;
+
+char *pr_file_p;
+char *pr_line_start; // start of current source line
+
+int pr_bracelevel;
+
+char pr_token[2048];
+token_type_t pr_token_type;
+type_t *pr_immediate_type;
+eval_t pr_immediate;
+
+char pr_immediate_string[2048];
+
+int pr_error_count;
+
+char *pr_punctuation[] =
+// longer symbols must be before a shorter partial match
+{"&&", "||", "<=", ">=","==", "!=", ";", ",", "!", "*", "/", "(", ")", "-", "+", "=", "[", "]", "{", "}", "...", ".", "<", ">" , "#" , "&" , "|" , NULL};
+
+// simple types. function types are dynamically allocated
+type_t type_void = {ev_void, &def_void};
+type_t type_string = {ev_string, &def_string};
+type_t type_float = {ev_float, &def_float};
+type_t type_vector = {ev_vector, &def_vector};
+type_t type_entity = {ev_entity, &def_entity};
+type_t type_field = {ev_field, &def_field};
+type_t type_function = {ev_function, &def_function,NULL,&type_void};
+// type_function is a void() function used for state defs
+type_t type_pointer = {ev_pointer, &def_pointer};
+
+type_t type_floatfield = {ev_field, &def_field, NULL, &type_float};
+
+int type_size[8] = {1,1,1,3,1,1,1,1};
+
+def_t def_void = {&type_void, "temp"};
+def_t def_string = {&type_string, "temp"};
+def_t def_float = {&type_float, "temp"};
+def_t def_vector = {&type_vector, "temp"};
+def_t def_entity = {&type_entity, "temp"};
+def_t def_field = {&type_field, "temp"};
+def_t def_function = {&type_function, "temp"};
+def_t def_pointer = {&type_pointer, "temp"};
+
+def_t def_ret, def_parms[MAX_PARMS];
+
+def_t *def_for_type[8] = {&def_void, &def_string, &def_float, &def_vector, &def_entity, &def_field, &def_function, &def_pointer};
+
+void PR_LexWhitespace (void);
+
+
+/*
+==============
+PR_PrintNextLine
+==============
+*/
+void PR_PrintNextLine (void)
+{
+ char *t;
+
+ printf ("%3i:",pr_source_line);
+ for (t=pr_line_start ; *t && *t != '\n' ; t++)
+ printf ("%c",*t);
+ printf ("\n");
+}
+
+/*
+==============
+PR_NewLine
+
+Call at start of file and when *pr_file_p == '\n'
+==============
+*/
+void PR_NewLine (void)
+{
+ qboolean m;
+
+ if (*pr_file_p == '\n')
+ {
+ pr_file_p++;
+ m = true;
+ }
+ else
+ m = false;
+
+ pr_source_line++;
+ pr_line_start = pr_file_p;
+
+// if (pr_dumpasm)
+// PR_PrintNextLine ();
+ if (m)
+ pr_file_p--;
+}
+
+/*
+==============
+PR_LexString
+
+Parses a quoted string
+==============
+*/
+void PR_LexString (void)
+{
+ int c;
+ int len;
+
+ len = 0;
+ pr_file_p++;
+ do
+ {
+ c = *pr_file_p++;
+ if (!c)
+ PR_ParseError ("EOF inside quote");
+ if (c=='\n')
+ PR_ParseError ("newline inside quote");
+ if (c=='\\')
+ { // escape char
+ c = *pr_file_p++;
+ if (!c)
+ PR_ParseError ("EOF inside quote");
+ if (c == 'n')
+ c = '\n';
+ else if (c == '"')
+ c = '"';
+ else
+ PR_ParseError ("Unknown escape char");
+ }
+ else if (c=='\"')
+ {
+ pr_token[len] = 0;
+ pr_token_type = tt_immediate;
+ pr_immediate_type = &type_string;
+ strcpy (pr_immediate_string, pr_token);
+ return;
+ }
+ pr_token[len] = c;
+ len++;
+ } while (1);
+}
+
+/*
+==============
+PR_LexNumber
+==============
+*/
+float PR_LexNumber (void)
+{
+ int c;
+ int len;
+
+ len = 0;
+ c = *pr_file_p;
+ do
+ {
+ pr_token[len] = c;
+ len++;
+ pr_file_p++;
+ c = *pr_file_p;
+ } while ((c >= '0' && c<= '9') || c == '.');
+ pr_token[len] = 0;
+ return atof (pr_token);
+}
+
+/*
+==============
+PR_LexVector
+
+Parses a single quoted vector
+==============
+*/
+void PR_LexVector (void)
+{
+ int i;
+
+ pr_file_p++;
+ pr_token_type = tt_immediate;
+ pr_immediate_type = &type_vector;
+ for (i=0 ; i<3 ; i++)
+ {
+ pr_immediate.vector[i] = PR_LexNumber ();
+ PR_LexWhitespace ();
+ }
+ if (*pr_file_p != '\'')
+ PR_ParseError ("Bad vector");
+ pr_file_p++;
+}
+
+/*
+==============
+PR_LexName
+
+Parses an identifier
+==============
+*/
+void PR_LexName (void)
+{
+ int c;
+ int len;
+
+ len = 0;
+ c = *pr_file_p;
+ do
+ {
+ pr_token[len] = c;
+ len++;
+ pr_file_p++;
+ c = *pr_file_p;
+ } while ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
+ || (c >= '0' && c <= '9'));
+ pr_token[len] = 0;
+ pr_token_type = tt_name;
+}
+
+/*
+==============
+PR_LexPunctuation
+==============
+*/
+void PR_LexPunctuation (void)
+{
+ int i;
+ int len;
+ char *p;
+
+ pr_token_type = tt_punct;
+
+ for (i=0 ; (p = pr_punctuation[i]) != NULL ; i++)
+ {
+ len = strlen(p);
+ if (!strncmp(p, pr_file_p, len) )
+ {
+ strcpy (pr_token, p);
+ if (p[0] == '{')
+ pr_bracelevel++;
+ else if (p[0] == '}')
+ pr_bracelevel--;
+ pr_file_p += len;
+ return;
+ }
+ }
+
+ PR_ParseError ("Unknown punctuation");
+}
+
+
+/*
+==============
+PR_LexWhitespace
+==============
+*/
+void PR_LexWhitespace (void)
+{
+ int c;
+
+ while (1)
+ {
+ // skip whitespace
+ while ( (c = *pr_file_p) <= ' ')
+ {
+ if (c=='\n')
+ PR_NewLine ();
+ if (c == 0)
+ return; // end of file
+ pr_file_p++;
+ }
+
+ // skip // comments
+ if (c=='/' && pr_file_p[1] == '/')
+ {
+ while (*pr_file_p && *pr_file_p != '\n')
+ pr_file_p++;
+ PR_NewLine();
+ pr_file_p++;
+ continue;
+ }
+
+ // skip /* */ comments
+ if (c=='/' && pr_file_p[1] == '*')
+ {
+ do
+ {
+ pr_file_p++;
+ if (pr_file_p[0]=='\n')
+ PR_NewLine();
+ if (pr_file_p[1] == 0)
+ return;
+ } while (pr_file_p[-1] != '*' || pr_file_p[0] != '/');
+ pr_file_p++;
+ continue;
+ }
+
+ break; // a real character has been found
+ }
+}
+
+//============================================================================
+
+#define MAX_FRAMES 256
+
+char pr_framemacros[MAX_FRAMES][16];
+int pr_nummacros;
+
+void PR_ClearGrabMacros (void)
+{
+ pr_nummacros = 0;
+}
+
+void PR_FindMacro (void)
+{
+ int i;
+
+ for (i=0 ; i<pr_nummacros ; i++)
+ if (!strcmp (pr_token, pr_framemacros[i]))
+ {
+ sprintf (pr_token,"%d", i);
+ pr_token_type = tt_immediate;
+ pr_immediate_type = &type_float;
+ pr_immediate._float = i;
+ return;
+ }
+ PR_ParseError ("Unknown frame macro $%s", pr_token);
+}
+
+// just parses text, returning false if an eol is reached
+qboolean PR_SimpleGetToken (void)
+{
+ int c;
+ int i;
+
+// skip whitespace
+ while ( (c = *pr_file_p) <= ' ')
+ {
+ if (c=='\n' || c == 0)
+ return false;
+ pr_file_p++;
+ }
+
+ i = 0;
+ while ( (c = *pr_file_p) > ' ' && c != ',' && c != ';')
+ {
+ pr_token[i] = c;
+ i++;
+ pr_file_p++;
+ }
+ pr_token[i] = 0;
+ return true;
+}
+
+void PR_ParseFrame (void)
+{
+ while (PR_SimpleGetToken ())
+ {
+ strcpy (pr_framemacros[pr_nummacros], pr_token);
+ pr_nummacros++;
+ }
+}
+
+/*
+==============
+PR_LexGrab
+
+Deals with counting sequence numbers and replacing frame macros
+==============
+*/
+void PR_LexGrab (void)
+{
+ pr_file_p++; // skip the $
+ if (!PR_SimpleGetToken ())
+ PR_ParseError ("hanging $");
+
+// check for $frame
+ if (!strcmp (pr_token, "frame"))
+ {
+ PR_ParseFrame ();
+ PR_Lex ();
+ }
+// ignore other known $commands
+ else if (!strcmp (pr_token, "cd")
+ || !strcmp (pr_token, "origin")
+ || !strcmp (pr_token, "base")
+ || !strcmp (pr_token, "flags")
+ || !strcmp (pr_token, "scale")
+ || !strcmp (pr_token, "skin") )
+ { // skip to end of line
+ while (PR_SimpleGetToken ())
+ ;
+ PR_Lex ();
+ }
+// look for a frame name macro
+ else
+ PR_FindMacro ();
+}
+
+//============================================================================
+
+/*
+==============
+PR_Lex
+
+Sets pr_token, pr_token_type, and possibly pr_immediate and pr_immediate_type
+==============
+*/
+void PR_Lex (void)
+{
+ int c;
+
+ pr_token[0] = 0;
+
+ if (!pr_file_p)
+ {
+ pr_token_type = tt_eof;
+ return;
+ }
+
+ PR_LexWhitespace ();
+
+ c = *pr_file_p;
+
+ if (!c)
+ {
+ pr_token_type = tt_eof;
+ return;
+ }
+
+// handle quoted strings as a unit
+ if (c == '\"')
+ {
+ PR_LexString ();
+ return;
+ }
+
+// handle quoted vectors as a unit
+ if (c == '\'')
+ {
+ PR_LexVector ();
+ return;
+ }
+
+// if the first character is a valid identifier, parse until a non-id
+// character is reached
+ if ( (c >= '0' && c <= '9') || ( c=='-' && pr_file_p[1]>='0' && pr_file_p[1] <='9') )
+ {
+ pr_token_type = tt_immediate;
+ pr_immediate_type = &type_float;
+ pr_immediate._float = PR_LexNumber ();
+ return;
+ }
+
+ if ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' )
+ {
+ PR_LexName ();
+ return;
+ }
+
+ if (c == '$')
+ {
+ PR_LexGrab ();
+ return;
+ }
+
+// parse symbol strings until a non-symbol is found
+ PR_LexPunctuation ();
+}
+
+//=============================================================================
+
+/*
+============
+PR_ParseError
+
+Aborts the current file load
+============
+*/
+void PR_ParseError (char *error, ...)
+{
+ va_list argptr;
+ char string[1024];
+
+ va_start (argptr,error);
+ vsprintf (string,error,argptr);
+ va_end (argptr);
+
+ printf ("%s:%i:%s\n", strings + s_file, pr_source_line, string);
+
+ longjmp (pr_parse_abort, 1);
+}
+
+
+/*
+=============
+PR_Expect
+
+Issues an error if the current token isn't equal to string
+Gets the next token
+=============
+*/
+void PR_Expect (char *string)
+{
+ if (strcmp (string, pr_token))
+ PR_ParseError ("expected %s, found %s",string, pr_token);
+ PR_Lex ();
+}
+
+
+/*
+=============
+PR_Check
+
+Returns true and gets the next token if the current token equals string
+Returns false and does nothing otherwise
+=============
+*/
+qboolean PR_Check (char *string)
+{
+ if (strcmp (string, pr_token))
+ return false;
+
+ PR_Lex ();
+ return true;
+}
+
+/*
+============
+PR_ParseName
+
+Checks to see if the current token is a valid name
+============
+*/
+char *PR_ParseName (void)
+{
+ static char ident[MAX_NAME];
+
+ if (pr_token_type != tt_name)
+ PR_ParseError ("not a name");
+ if (strlen(pr_token) >= MAX_NAME-1)
+ PR_ParseError ("name too long");
+ strcpy (ident, pr_token);
+ PR_Lex ();
+
+ return ident;
+}
+
+/*
+============
+PR_FindType
+
+Returns a preexisting complex type that matches the parm, or allocates
+a new one and copies it out.
+============
+*/
+type_t *PR_FindType (type_t *type)
+{
+ def_t *def;
+ type_t *check;
+ int i;
+
+ for (check = pr.types ; check ; check = check->next)
+ {
+ if (check->type != type->type
+ || check->aux_type != type->aux_type
+ || check->num_parms != type->num_parms)
+ continue;
+
+ for (i=0 ; i< type->num_parms ; i++)
+ if (check->parm_types[i] != type->parm_types[i])
+ break;
+
+ if (i == type->num_parms)
+ return check;
+ }
+
+// allocate a new one
+ check = malloc (sizeof (*check));
+ *check = *type;
+ check->next = pr.types;
+ pr.types = check;
+
+// allocate a generic def for the type, so fields can reference it
+ def = malloc (sizeof(def_t));
+ def->name = "COMPLEX TYPE";
+ def->type = check;
+ check->def = def;
+ return check;
+}
+
+
+/*
+============
+PR_SkipToSemicolon
+
+For error recovery, also pops out of nested braces
+============
+*/
+void PR_SkipToSemicolon (void)
+{
+ do
+ {
+ if (!pr_bracelevel && PR_Check (";"))
+ return;
+ PR_Lex ();
+ } while (pr_token[0]); // eof will return a null token
+}
+
+
+/*
+============
+PR_ParseType
+
+Parses a variable type, including field and functions types
+============
+*/
+char pr_parm_names[MAX_PARMS][MAX_NAME];
+
+type_t *PR_ParseType (void)
+{
+ type_t new;
+ type_t *type;
+ char *name;
+
+ if (PR_Check ("."))
+ {
+ memset (&new, 0, sizeof(new));
+ new.type = ev_field;
+ new.aux_type = PR_ParseType ();
+ return PR_FindType (&new);
+ }
+
+ if (!strcmp (pr_token, "float") )
+ type = &type_float;
+ else if (!strcmp (pr_token, "vector") )
+ type = &type_vector;
+ else if (!strcmp (pr_token, "float") )
+ type = &type_float;
+ else if (!strcmp (pr_token, "entity") )
+ type = &type_entity;
+ else if (!strcmp (pr_token, "string") )
+ type = &type_string;
+ else if (!strcmp (pr_token, "void") )
+ type = &type_void;
+ else
+ {
+ PR_ParseError ("\"%s\" is not a type", pr_token);
+ type = &type_float; // shut up compiler warning
+ }
+ PR_Lex ();
+
+ if (!PR_Check ("("))
+ return type;
+
+// function type
+ memset (&new, 0, sizeof(new));
+ new.type = ev_function;
+ new.aux_type = type; // return type
+ new.num_parms = 0;
+ if (!PR_Check (")"))
+ {
+ if (PR_Check ("..."))
+ new.num_parms = -1; // variable args
+ else
+ do
+ {
+ type = PR_ParseType ();
+ name = PR_ParseName ();
+ strcpy (pr_parm_names[new.num_parms], name);
+ new.parm_types[new.num_parms] = type;
+ new.num_parms++;
+ } while (PR_Check (","));
+
+ PR_Expect (")");
+ }
+
+ return PR_FindType (&new);
+}
+
diff --git a/qutils/QCC/QCC b/qutils/QCC/QCC
new file mode 100644
index 0000000..ceb89d2
--- /dev/null
+++ b/qutils/QCC/QCC
Binary files differ
diff --git a/qutils/QCC/QCC.C b/qutils/QCC/QCC.C
new file mode 100644
index 0000000..319330d
--- /dev/null
+++ b/qutils/QCC/QCC.C
@@ -0,0 +1,808 @@
+
+#include "qcc.h"
+
+char sourcedir[1024];
+char destfile[1024];
+
+float pr_globals[MAX_REGS];
+int numpr_globals;
+
+char strings[MAX_STRINGS];
+int strofs;
+
+dstatement_t statements[MAX_STATEMENTS];
+int numstatements;
+int statement_linenums[MAX_STATEMENTS];
+
+dfunction_t functions[MAX_FUNCTIONS];
+int numfunctions;
+
+ddef_t globals[MAX_GLOBALS];
+int numglobaldefs;
+
+ddef_t fields[MAX_FIELDS];
+int numfielddefs;
+
+char precache_sounds[MAX_SOUNDS][MAX_DATA_PATH];
+int precache_sounds_block[MAX_SOUNDS];
+int numsounds;
+
+char precache_models[MAX_MODELS][MAX_DATA_PATH];
+int precache_models_block[MAX_SOUNDS];
+int nummodels;
+
+char precache_files[MAX_FILES][MAX_DATA_PATH];
+int precache_files_block[MAX_SOUNDS];
+int numfiles;
+
+/*
+============
+WriteFiles
+
+ Generates files.dat, which contains all of the
+ data files actually used by the game, to be
+ processed by qfiles.exe
+============
+*/
+void WriteFiles (void)
+{
+ FILE *f;
+ int i;
+ char filename[1024];
+
+ sprintf (filename, "%sfiles.dat", sourcedir);
+ f = fopen (filename, "w");
+ if (!f)
+ Error ("Couldn't open %s", filename);
+
+ fprintf (f, "%i\n", numsounds);
+ for (i=0 ; i<numsounds ; i++)
+ fprintf (f, "%i %s\n", precache_sounds_block[i],
+ precache_sounds[i]);
+
+ fprintf (f, "%i\n", nummodels);
+ for (i=0 ; i<nummodels ; i++)
+ fprintf (f, "%i %s\n", precache_models_block[i],
+ precache_models[i]);
+
+ fprintf (f, "%i\n", numfiles);
+ for (i=0 ; i<numfiles ; i++)
+ fprintf (f, "%i %s\n", precache_files_block[i],
+ precache_files[i]);
+
+ fclose (f);
+}
+
+
+// CopyString returns an offset from the string heap
+int CopyString (char *str)
+{
+ int old;
+
+ old = strofs;
+ strcpy (strings+strofs, str);
+ strofs += strlen(str)+1;
+ return old;
+}
+
+void PrintStrings (void)
+{
+ int i, l, j;
+
+ for (i=0 ; i<strofs ; i += l)
+ {
+ l = strlen(strings+i) + 1;
+ printf ("%5i : ",i);
+ for (j=0 ; j<l ; j++)
+ {
+ if (strings[i+j] == '\n')
+ {
+ putchar ('\\');
+ putchar ('n');
+ }
+ else
+ putchar (strings[i+j]);
+ }
+ printf ("\n");
+ }
+}
+
+
+void PrintFunctions (void)
+{
+ int i,j;
+ dfunction_t *d;
+
+ for (i=0 ; i<numfunctions ; i++)
+ {
+ d = &functions[i];
+ printf ("%s : %s : %i %i (", strings + d->s_file, strings + d->s_name, d->first_statement, d->parm_start);
+ for (j=0 ; j<d->numparms ; j++)
+ printf ("%i ",d->parm_size[j]);
+ printf (")\n");
+ }
+}
+
+void PrintFields (void)
+{
+ int i;
+ ddef_t *d;
+
+ for (i=0 ; i<numfielddefs ; i++)
+ {
+ d = &fields[i];
+ printf ("%5i : (%i) %s\n", d->ofs, d->type, strings + d->s_name);
+ }
+}
+
+void PrintGlobals (void)
+{
+ int i;
+ ddef_t *d;
+
+ for (i=0 ; i<numglobaldefs ; i++)
+ {
+ d = &globals[i];
+ printf ("%5i : (%i) %s\n", d->ofs, d->type, strings + d->s_name);
+ }
+}
+
+
+void InitData (void)
+{
+ int i;
+
+ numstatements = 1;
+ strofs = 1;
+ numfunctions = 1;
+ numglobaldefs = 1;
+ numfielddefs = 1;
+
+ def_ret.ofs = OFS_RETURN;
+ for (i=0 ; i<MAX_PARMS ; i++)
+ def_parms[i].ofs = OFS_PARM0 + 3*i;
+}
+
+
+void WriteData (int crc)
+{
+ def_t *def;
+ ddef_t *dd;
+ dprograms_t progs;
+ FILE *h;
+ int i;
+
+ for (def = pr.def_head.next ; def ; def = def->next)
+ {
+ if (def->type->type == ev_function)
+ {
+// df = &functions[numfunctions];
+// numfunctions++;
+
+ }
+ else if (def->type->type == ev_field)
+ {
+ dd = &fields[numfielddefs];
+ numfielddefs++;
+ dd->type = def->type->aux_type->type;
+ dd->s_name = CopyString (def->name);
+ dd->ofs = G_INT(def->ofs);
+ }
+ dd = &globals[numglobaldefs];
+ numglobaldefs++;
+ dd->type = def->type->type;
+ if ( !def->initialized
+ && def->type->type != ev_function
+ && def->type->type != ev_field
+ && def->scope == NULL)
+ dd->type |= DEF_SAVEGLOBGAL;
+ dd->s_name = CopyString (def->name);
+ dd->ofs = def->ofs;
+ }
+
+//PrintStrings ();
+//PrintFunctions ();
+//PrintFields ();
+//PrintGlobals ();
+strofs = (strofs+3)&~3;
+
+ printf ("%6i strofs\n", strofs);
+ printf ("%6i numstatements\n", numstatements);
+ printf ("%6i numfunctions\n", numfunctions);
+ printf ("%6i numglobaldefs\n", numglobaldefs);
+ printf ("%6i numfielddefs\n", numfielddefs);
+ printf ("%6i numpr_globals\n", numpr_globals);
+
+ h = SafeOpenWrite (destfile);
+ SafeWrite (h, &progs, sizeof(progs));
+
+ progs.ofs_strings = ftell (h);
+ progs.numstrings = strofs;
+ SafeWrite (h, strings, strofs);
+
+ progs.ofs_statements = ftell (h);
+ progs.numstatements = numstatements;
+ for (i=0 ; i<numstatements ; i++)
+ {
+ statements[i].op = LittleShort(statements[i].op);
+ statements[i].a = LittleShort(statements[i].a);
+ statements[i].b = LittleShort(statements[i].b);
+ statements[i].c = LittleShort(statements[i].c);
+ }
+ SafeWrite (h, statements, numstatements*sizeof(dstatement_t));
+
+ progs.ofs_functions = ftell (h);
+ progs.numfunctions = numfunctions;
+ for (i=0 ; i<numfunctions ; i++)
+ {
+ functions[i].first_statement = LittleLong (functions[i].first_statement);
+ functions[i].parm_start = LittleLong (functions[i].parm_start);
+ functions[i].s_name = LittleLong (functions[i].s_name);
+ functions[i].s_file = LittleLong (functions[i].s_file);
+ functions[i].numparms = LittleLong (functions[i].numparms);
+ functions[i].locals = LittleLong (functions[i].locals);
+ }
+ SafeWrite (h, functions, numfunctions*sizeof(dfunction_t));
+
+ progs.ofs_globaldefs = ftell (h);
+ progs.numglobaldefs = numglobaldefs;
+ for (i=0 ; i<numglobaldefs ; i++)
+ {
+ globals[i].type = LittleShort (globals[i].type);
+ globals[i].ofs = LittleShort (globals[i].ofs);
+ globals[i].s_name = LittleLong (globals[i].s_name);
+ }
+ SafeWrite (h, globals, numglobaldefs*sizeof(ddef_t));
+
+ progs.ofs_fielddefs = ftell (h);
+ progs.numfielddefs = numfielddefs;
+ for (i=0 ; i<numfielddefs ; i++)
+ {
+ fields[i].type = LittleShort (fields[i].type);
+ fields[i].ofs = LittleShort (fields[i].ofs);
+ fields[i].s_name = LittleLong (fields[i].s_name);
+ }
+ SafeWrite (h, fields, numfielddefs*sizeof(ddef_t));
+
+ progs.ofs_globals = ftell (h);
+ progs.numglobals = numpr_globals;
+ for (i=0 ; i<numpr_globals ; i++)
+ ((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]);
+ SafeWrite (h, pr_globals, numpr_globals*4);
+
+ printf ("%6i TOTAL SIZE\n", (int)ftell (h));
+
+ progs.entityfields = pr.size_fields;
+
+ progs.version = PROG_VERSION;
+ progs.crc = crc;
+
+// byte swap the header and write it out
+ for (i=0 ; i<sizeof(progs)/4 ; i++)
+ ((int *)&progs)[i] = LittleLong ( ((int *)&progs)[i] );
+ fseek (h, 0, SEEK_SET);
+ SafeWrite (h, &progs, sizeof(progs));
+ fclose (h);
+
+}
+
+
+
+/*
+===============
+PR_String
+
+Returns a string suitable for printing (no newlines, max 60 chars length)
+===============
+*/
+char *PR_String (char *string)
+{
+ static char buf[80];
+ char *s;
+
+ s = buf;
+ *s++ = '"';
+ while (string && *string)
+ {
+ if (s == buf + sizeof(buf) - 2)
+ break;
+ if (*string == '\n')
+ {
+ *s++ = '\\';
+ *s++ = 'n';
+ }
+ else if (*string == '"')
+ {
+ *s++ = '\\';
+ *s++ = '"';
+ }
+ else
+ *s++ = *string;
+ string++;
+ if (s - buf > 60)
+ {
+ *s++ = '.';
+ *s++ = '.';
+ *s++ = '.';
+ break;
+ }
+ }
+ *s++ = '"';
+ *s++ = 0;
+ return buf;
+}
+
+
+
+def_t *PR_DefForFieldOfs (gofs_t ofs)
+{
+ def_t *d;
+
+ for (d=pr.def_head.next ; d ; d=d->next)
+ {
+ if (d->type->type != ev_field)
+ continue;
+ if (*((int *)&pr_globals[d->ofs]) == ofs)
+ return d;
+ }
+ Error ("PR_DefForFieldOfs: couldn't find %i",ofs);
+ return NULL;
+}
+
+/*
+============
+PR_ValueString
+
+Returns a string describing *data in a type specific manner
+=============
+*/
+char *PR_ValueString (etype_t type, void *val)
+{
+ static char line[256];
+ def_t *def;
+ dfunction_t *f;
+
+ switch (type)
+ {
+ case ev_string:
+ sprintf (line, "%s", PR_String(strings + *(int *)val));
+ break;
+ case ev_entity:
+ sprintf (line, "entity %i", *(int *)val);
+ break;
+ case ev_function:
+ f = functions + *(int *)val;
+ if (!f)
+ sprintf (line, "undefined function");
+ else
+ sprintf (line, "%s()", strings + f->s_name);
+ break;
+ case ev_field:
+ def = PR_DefForFieldOfs ( *(int *)val );
+ sprintf (line, ".%s", def->name);
+ break;
+ case ev_void:
+ sprintf (line, "void");
+ break;
+ case ev_float:
+ sprintf (line, "%5.1f", *(float *)val);
+ break;
+ case ev_vector:
+ sprintf (line, "'%5.1f %5.1f %5.1f'", ((float *)val)[0], ((float *)val)[1], ((float *)val)[2]);
+ break;
+ case ev_pointer:
+ sprintf (line, "pointer");
+ break;
+ default:
+ sprintf (line, "bad type %i", type);
+ break;
+ }
+
+ return line;
+}
+
+/*
+============
+PR_GlobalString
+
+Returns a string with a description and the contents of a global,
+padded to 20 field width
+============
+*/
+char *PR_GlobalStringNoContents (gofs_t ofs)
+{
+ int i;
+ def_t *def;
+ void *val;
+ static char line[128];
+
+ val = (void *)&pr_globals[ofs];
+ def = pr_global_defs[ofs];
+ if (!def)
+// Error ("PR_GlobalString: no def for %i", ofs);
+ sprintf (line,"%i(???)", ofs);
+ else
+ sprintf (line,"%i(%s)", ofs, def->name);
+
+ i = strlen(line);
+ for ( ; i<16 ; i++)
+ strcat (line," ");
+ strcat (line," ");
+
+ return line;
+}
+
+char *PR_GlobalString (gofs_t ofs)
+{
+ char *s;
+ int i;
+ def_t *def;
+ void *val;
+ static char line[128];
+
+ val = (void *)&pr_globals[ofs];
+ def = pr_global_defs[ofs];
+ if (!def)
+ return PR_GlobalStringNoContents(ofs);
+ if (def->initialized && def->type->type != ev_function)
+ {
+ s = PR_ValueString (def->type->type, &pr_globals[ofs]);
+ sprintf (line,"%i(%s)", ofs, s);
+ }
+ else
+ sprintf (line,"%i(%s)", ofs, def->name);
+
+ i = strlen(line);
+ for ( ; i<16 ; i++)
+ strcat (line," ");
+ strcat (line," ");
+
+ return line;
+}
+
+/*
+============
+PR_PrintOfs
+============
+*/
+void PR_PrintOfs (gofs_t ofs)
+{
+ printf ("%s\n",PR_GlobalString(ofs));
+}
+
+/*
+=================
+PR_PrintStatement
+=================
+*/
+void PR_PrintStatement (dstatement_t *s)
+{
+ int i;
+
+ printf ("%4i : %4i : %s ", (int)(s - statements), statement_linenums[s-statements], pr_opcodes[s->op].opname);
+ i = strlen(pr_opcodes[s->op].opname);
+ for ( ; i<10 ; i++)
+ printf (" ");
+
+ if (s->op == OP_IF || s->op == OP_IFNOT)
+ printf ("%sbranch %i",PR_GlobalString(s->a),s->b);
+ else if (s->op == OP_GOTO)
+ {
+ printf ("branch %i",s->a);
+ }
+ else if ( (unsigned)(s->op - OP_STORE_F) < 6)
+ {
+ printf ("%s",PR_GlobalString(s->a));
+ printf ("%s", PR_GlobalStringNoContents(s->b));
+ }
+ else
+ {
+ if (s->a)
+ printf ("%s",PR_GlobalString(s->a));
+ if (s->b)
+ printf ("%s",PR_GlobalString(s->b));
+ if (s->c)
+ printf ("%s", PR_GlobalStringNoContents(s->c));
+ }
+ printf ("\n");
+}
+
+
+/*
+============
+PR_PrintDefs
+============
+*/
+void PR_PrintDefs (void)
+{
+ def_t *d;
+
+ for (d=pr.def_head.next ; d ; d=d->next)
+ PR_PrintOfs (d->ofs);
+}
+
+
+/*
+==============
+PR_BeginCompilation
+
+called before compiling a batch of files, clears the pr struct
+==============
+*/
+void PR_BeginCompilation (void *memory, int memsize)
+{
+ int i;
+
+ pr.memory = memory;
+ pr.max_memory = memsize;
+
+ numpr_globals = RESERVED_OFS;
+ pr.def_tail = &pr.def_head;
+
+ for (i=0 ; i<RESERVED_OFS ; i++)
+ pr_global_defs[i] = &def_void;
+
+// link the function type in so state forward declarations match proper type
+ pr.types = &type_function;
+ type_function.next = NULL;
+ pr_error_count = 0;
+}
+
+/*
+==============
+PR_FinishCompilation
+
+called after all files are compiled to check for errors
+Returns false if errors were detected.
+==============
+*/
+qboolean PR_FinishCompilation (void)
+{
+ def_t *d;
+ qboolean errors;
+
+ errors = false;
+
+// check to make sure all functions prototyped have code
+ for (d=pr.def_head.next ; d ; d=d->next)
+ {
+ if (d->type->type == ev_function && !d->scope)// function parms are ok
+ {
+// f = G_FUNCTION(d->ofs);
+// if (!f || (!f->code && !f->builtin) )
+ if (!d->initialized)
+ {
+ printf ("function %s was not defined\n",d->name);
+ errors = true;
+ }
+ }
+ }
+
+ return !errors;
+}
+
+//=============================================================================
+
+/*
+============
+PR_WriteProgdefs
+
+Writes the global and entity structures out
+Returns a crc of the header, to be stored in the progs file for comparison
+at load time.
+============
+*/
+int PR_WriteProgdefs (char *filename)
+{
+ def_t *d;
+ FILE *f;
+ unsigned short crc;
+ int c;
+
+ printf ("writing %s\n", filename);
+ f = fopen (filename, "w");
+
+// print global vars until the first field is defined
+ fprintf (f,"\n/* file generated by qcc, do not modify */\n\ntypedef struct\n{\tint\tpad[%i];\n", RESERVED_OFS);
+ for (d=pr.def_head.next ; d ; d=d->next)
+ {
+ if (!strcmp (d->name, "end_sys_globals"))
+ break;
+
+ switch (d->type->type)
+ {
+ case ev_float:
+ fprintf (f, "\tfloat\t%s;\n",d->name);
+ break;
+ case ev_vector:
+ fprintf (f, "\tvec3_t\t%s;\n",d->name);
+ d=d->next->next->next; // skip the elements
+ break;
+ case ev_string:
+ fprintf (f,"\tstring_t\t%s;\n",d->name);
+ break;
+ case ev_function:
+ fprintf (f,"\tfunc_t\t%s;\n",d->name);
+ break;
+ case ev_entity:
+ fprintf (f,"\tint\t%s;\n",d->name);
+ break;
+ default:
+ fprintf (f,"\tint\t%s;\n",d->name);
+ break;
+ }
+ }
+ fprintf (f,"} globalvars_t;\n\n");
+
+// print all fields
+ fprintf (f,"typedef struct\n{\n");
+ for (d=pr.def_head.next ; d ; d=d->next)
+ {
+ if (!strcmp (d->name, "end_sys_fields"))
+ break;
+
+ if (d->type->type != ev_field)
+ continue;
+
+ switch (d->type->aux_type->type)
+ {
+ case ev_float:
+ fprintf (f,"\tfloat\t%s;\n",d->name);
+ break;
+ case ev_vector:
+ fprintf (f,"\tvec3_t\t%s;\n",d->name);
+ d=d->next->next->next; // skip the elements
+ break;
+ case ev_string:
+ fprintf (f,"\tstring_t\t%s;\n",d->name);
+ break;
+ case ev_function:
+ fprintf (f,"\tfunc_t\t%s;\n",d->name);
+ break;
+ case ev_entity:
+ fprintf (f,"\tint\t%s;\n",d->name);
+ break;
+ default:
+ fprintf (f,"\tint\t%s;\n",d->name);
+ break;
+ }
+ }
+ fprintf (f,"} entvars_t;\n\n");
+
+ fclose (f);
+
+// do a crc of the file
+ CRC_Init (&crc);
+ f = fopen (filename, "r+");
+ while ((c = fgetc(f)) != EOF)
+ CRC_ProcessByte (&crc, (byte)c);
+
+ fprintf (f,"#define PROGHEADER_CRC %i\n", crc);
+ fclose (f);
+
+ return crc;
+}
+
+
+void PrintFunction (char *name)
+{
+ int i;
+ dstatement_t *ds;
+ dfunction_t *df;
+
+ for (i=0 ; i<numfunctions ; i++)
+ if (!strcmp (name, strings + functions[i].s_name))
+ break;
+ if (i==numfunctions)
+ Error ("No function names \"%s\"", name);
+ df = functions + i;
+
+ printf ("Statements for %s:\n", name);
+ ds = statements + df->first_statement;
+ while (1)
+ {
+ PR_PrintStatement (ds);
+ if (!ds->op)
+ break;
+ ds++;
+ }
+}
+
+
+//============================================================================
+
+/*
+============
+main
+============
+*/
+void main (int argc, char **argv)
+{
+ char *src;
+ char *src2;
+ char filename[1024];
+ int p, crc;
+ double start, stop;
+
+ start = I_FloatTime ();
+
+ myargc = argc;
+ myargv = argv;
+
+ if ( CheckParm ("-?") || CheckParm ("-help"))
+ {
+ printf ("qcc looks for progs.src in the current directory.\n");
+ printf ("to look in a different directory: qcc -src <directory>\n");
+ printf ("to build a clean data tree: qcc -copy <srcdir> <destdir>\n");
+ printf ("to build a clean pak file: qcc -pak <srcdir> <packfile>\n");
+ printf ("to bsp all bmodels: qcc -bspmodels <gamedir>\n");
+ return;
+ }
+
+ p = CheckParm ("-src");
+ if (p && p < argc-1 )
+ {
+ strcpy (sourcedir, argv[p+1]);
+ strcat (sourcedir, "/");
+ printf ("Source directory: %s\n", sourcedir);
+ }
+ else
+ strcpy (sourcedir, "");
+
+ InitData ();
+
+ sprintf (filename, "%sprogs.src", sourcedir);
+ LoadFile (filename, (void *)&src);
+
+ src = COM_Parse (src);
+ if (!src)
+ Error ("No destination filename. qcc -help for info.\n");
+ strcpy (destfile, com_token);
+ printf ("outputfile: %s\n", destfile);
+
+ pr_dumpasm = false;
+
+ PR_BeginCompilation (malloc (0x100000), 0x100000);
+
+// compile all the files
+ do
+ {
+ src = COM_Parse(src);
+ if (!src)
+ break;
+ sprintf (filename, "%s%s", sourcedir, com_token);
+ printf ("compiling %s\n", filename);
+ LoadFile (filename, (void *)&src2);
+
+ if (!PR_CompileFile (src2, filename) )
+ exit (1);
+
+ } while (1);
+
+ if (!PR_FinishCompilation ())
+ Error ("compilation errors");
+
+ p = CheckParm ("-asm");
+ if (p)
+ {
+ for (p++ ; p<argc ; p++)
+ {
+ if (argv[p][0] == '-')
+ break;
+ PrintFunction (argv[p]);
+ }
+ }
+
+
+// write progdefs.h
+ crc = PR_WriteProgdefs ("progdefs.h");
+
+// write data file
+ WriteData (crc);
+
+// write files.dat
+ WriteFiles ();
+
+ stop = I_FloatTime ();
+ printf ("%i seconds elapsed.\n", (int)(stop-start));
+}
diff --git a/qutils/QCC/QCC.H b/qutils/QCC/QCC.H
new file mode 100644
index 0000000..72b5649
--- /dev/null
+++ b/qutils/QCC/QCC.H
@@ -0,0 +1,436 @@
+
+#include "cmdlib.h"
+#include <stdio.h>
+#include <setjmp.h>
+
+#include "pr_comp.h"
+
+/*
+
+TODO:
+
+"stopped at 10 errors"
+
+other pointer types for models and clients?
+
+compact string heap?
+
+allways initialize all variables to something safe
+
+the def->type->type arrangement is really silly.
+
+return type checking
+
+parm count type checking
+
+immediate overflow checking
+
+pass the first two parms in call->b and call->c
+
+*/
+
+/*
+
+comments
+--------
+// comments discard text until the end of line
+/ * * / comments discard all enclosed text (spaced out on this line because this documentation is in a regular C comment block, and typing them in normally causes a parse error)
+
+code structure
+--------------
+A definition is:
+ <type> <name> [ = <immediate>] {, <name> [ = <immediate>] };
+
+
+types
+-----
+simple types: void, float, vector, string, or entity
+ float width, height;
+ string name;
+ entity self, other;
+
+vector types:
+ vector org; // also creates org_x, org_y, and org_z float defs
+
+
+A function type is specified as: simpletype ( type name {,type name} )
+The names are ignored except when the function is initialized.
+ void() think;
+ entity() FindTarget;
+ void(vector destination, float speed, void() callback) SUB_CalcMove;
+ void(...) dprint; // variable argument builtin
+
+A field type is specified as: .type
+ .vector origin;
+ .string netname;
+ .void() think, touch, use;
+
+
+names
+-----
+Names are a maximum of 64 characters, must begin with A-Z,a-z, or _, and can continue with those characters or 0-9.
+
+There are two levels of scoping: global, and function. The parameter list of a function and any vars declared inside a function with the "local" statement are only visible within that function,
+
+
+immediates
+----------
+Float immediates must begin with 0-9 or minus sign. .5 is illegal.
+
+A parsing ambiguity is present with negative constants. "a-5" will be parsed as "a", then "-5", causing an error. Seperate the - from the digits with a space "a - 5" to get the proper behavior.
+ 12
+ 1.6
+ 0.5
+ -100
+
+Vector immediates are three float immediates enclosed in single quotes.
+ '0 0 0'
+ '20.5 -10 0.00001'
+
+String immediates are characters enclosed in double quotes. The string cannot contain explicit newlines, but the escape character \n can embed one. The \" escape can be used to include a quote in the string.
+ "maps/jrwiz1.bsp"
+ "sound/nin/pain.wav"
+ "ouch!\n"
+
+Code immediates are statements enclosed in {} braces.
+statement:
+ { <multiple statements> }
+ <expression>;
+ local <type> <name> [ = <immediate>] {, <name> [ = <immediate>] };
+ return <expression>;
+ if ( <expression> ) <statement> [ else <statement> ];
+ while ( <expression> ) <statement>;
+ do <statement> while ( <expression> );
+ <function name> ( <function parms> );
+
+expression:
+ combiations of names and these operators with standard C precedence:
+ "&&", "||", "<=", ">=","==", "!=", "!", "*", "/", "-", "+", "=", ".", "<", ">", "&", "|"
+ Parenthesis can be used to alter order of operation.
+ The & and | operations perform integral bit ops on floats
+
+A built in function immediate is a number sign followed by an integer.
+ #1
+ #12
+
+
+compilation
+-----------
+Source files are processed sequentially without dumping any state, so if a defs file is the first one processed, the definitions will be available to all other files.
+
+The language is strongly typed and there are no casts.
+
+Anything that is initialized is assumed to be constant, and will have immediates folded into it. If you change the value, your program will malfunction. All uninitialized globals will be saved to savegame files.
+
+Functions cannot have more than eight parameters.
+
+Error recovery during compilation is minimal. It will skip to the next global definition, so you will never see more than one error at a time in a given function. All compilation aborts after ten error messages.
+
+Names can be defined multiple times until they are defined with an initialization, allowing functions to be prototyped before their definition.
+
+void() MyFunction; // the prototype
+
+void() MyFunction = // the initialization
+{
+ dprint ("we're here\n");
+};
+
+
+entities and fields
+-------------------
+
+
+execution
+---------
+Code execution is initiated by C code in quake from two main places: the timed think routines for periodic control, and the touch function when two objects impact each other.
+
+There are three global variables that are set before beginning code execution:
+ entity world; // the server's world object, which holds all global
+ // state for the server, like the deathmatch flags
+ // and the body ques.
+ entity self; // the entity the function is executing for
+ entity other; // the other object in an impact, not used for thinks
+ float time; // the current game time. Note that because the
+ // entities in the world are simulated sequentially,
+ // time is NOT strictly increasing. An impact late
+ // in one entity's time slice may set time higher
+ // than the think function of the next entity.
+ // The difference is limited to 0.1 seconds.
+Execution is also caused by a few uncommon events, like the addition of a new client to an existing server.
+
+There is a runnaway counter that stops a program if 100000 statements are executed, assuming it is in an infinite loop.
+
+It is acceptable to change the system set global variables. This is usually done to pose as another entity by changing self and calling a function.
+
+The interpretation is fairly efficient, but it is still over an order of magnitude slower than compiled C code. All time consuming operations should be made into built in functions.
+
+A profile counter is kept for each function, and incremented for each interpreted instruction inside that function. The "profile" console command in Quake will dump out the top 10 functions, then clear all the counters. The "profile all" command will dump sorted stats for every function that has been executed.
+
+
+afunc ( 4, bfunc(1,2,3));
+will fail because there is a shared parameter marshaling area, which will cause the 1 from bfunc to overwrite the 4 allready placed in parm0. When a function is called, it copies the parms from the globals into it's privately scoped variables, so there is no collision when calling another function.
+
+total = factorial(3) + factorial(4);
+Will fail because the return value from functions is held in a single global area. If this really gets on your nerves, tell me and I can work around it at a slight performance and space penalty by allocating a new register for the function call and copying it out.
+
+
+built in functions
+------------------
+void(string text) dprint;
+Prints the string to the server console.
+
+void(entity client, string text) cprint;
+Prints a message to a specific client.
+
+void(string text) bprint;
+Broadcast prints a message to all clients on the current server.
+
+entity() spawn;
+Returns a totally empty entity. You can manually set everything up, or just set the origin and call one of the existing entity setup functions.
+
+entity(entity start, .string field, string match) find;
+Searches the server entity list beginning at start, looking for an entity that has entity.field = match. To start at the beginning of the list, pass world. World is returned when the end of the list is reached.
+
+<FIXME: define all the other functions...>
+
+
+gotchas
+-------
+
+The && and || operators DO NOT EARLY OUT like C!
+
+Don't confuse single quoted vectors with double quoted strings
+
+The function declaration syntax takes a little getting used to.
+
+Don't forget the ; after the trailing brace of a function initialization.
+
+Don't forget the "local" before defining local variables.
+
+There are no ++ / -- operators, or operate/assign operators.
+
+*/
+
+//=============================================================================
+
+// offsets are allways multiplied by 4 before using
+typedef int gofs_t; // offset in global data block
+typedef struct function_s function_t;
+
+#define MAX_PARMS 8
+
+typedef struct type_s
+{
+ etype_t type;
+ struct def_s *def; // a def that points to this type
+ struct type_s *next;
+// function types are more complex
+ struct type_s *aux_type; // return type or field type
+ int num_parms; // -1 = variable args
+ struct type_s *parm_types[MAX_PARMS]; // only [num_parms] allocated
+} type_t;
+
+typedef struct def_s
+{
+ type_t *type;
+ char *name;
+ struct def_s *next;
+ struct def_s *search_next; // for finding faster
+ gofs_t ofs;
+ struct def_s *scope; // function the var was defined in, or NULL
+ int initialized; // 1 when a declaration included "= immediate"
+} def_t;
+
+//============================================================================
+
+// pr_loc.h -- program local defs
+
+#define MAX_ERRORS 10
+
+#define MAX_NAME 64 // chars long
+
+#define MAX_REGS 16384
+
+//=============================================================================
+
+typedef union eval_s
+{
+ string_t string;
+ float _float;
+ float vector[3];
+ func_t function;
+ int _int;
+ union eval_s *ptr;
+} eval_t;
+
+extern int type_size[8];
+extern def_t *def_for_type[8];
+
+extern type_t type_void, type_string, type_float, type_vector, type_entity, type_field, type_function, type_pointer, type_floatfield;
+
+extern def_t def_void, def_string, def_float, def_vector, def_entity, def_field, def_function, def_pointer;
+
+struct function_s
+{
+ int builtin; // if non 0, call an internal function
+ int code; // first statement
+ char *file; // source file with definition
+ int file_line;
+ struct def_s *def;
+ int parm_ofs[MAX_PARMS]; // allways contiguous, right?
+};
+
+
+//
+// output generated by prog parsing
+//
+typedef struct
+{
+ char *memory;
+ int max_memory;
+ int current_memory;
+ type_t *types;
+
+ def_t def_head; // unused head of linked list
+ def_t *def_tail; // add new defs after this and move it
+ def_t *search; // search chain through defs
+
+ int size_fields;
+} pr_info_t;
+
+extern pr_info_t pr;
+
+typedef struct
+{
+ char *name;
+ char *opname;
+ float priority;
+ qboolean right_associative;
+ def_t *type_a, *type_b, *type_c;
+} opcode_t;
+
+//============================================================================
+
+
+extern opcode_t pr_opcodes[99]; // sized by initialization
+
+extern qboolean pr_dumpasm;
+
+extern def_t *pr_global_defs[MAX_REGS]; // to find def for a global variable
+
+typedef enum {
+tt_eof, // end of file reached
+tt_name, // an alphanumeric name token
+tt_punct, // code punctuation
+tt_immediate, // string, float, vector
+} token_type_t;
+
+extern char pr_token[2048];
+extern token_type_t pr_token_type;
+extern type_t *pr_immediate_type;
+extern eval_t pr_immediate;
+
+void PR_PrintStatement (dstatement_t *s);
+
+void PR_Lex (void);
+// reads the next token into pr_token and classifies its type
+
+type_t *PR_ParseType (void);
+char *PR_ParseName (void);
+
+qboolean PR_Check (char *string);
+void PR_Expect (char *string);
+void PR_ParseError (char *error, ...);
+
+
+extern jmp_buf pr_parse_abort; // longjump with this on parse error
+extern int pr_source_line;
+extern char *pr_file_p;
+
+void *PR_Malloc (int size);
+
+
+#define OFS_NULL 0
+#define OFS_RETURN 1
+#define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors
+#define OFS_PARM1 7
+#define OFS_PARM2 10
+#define OFS_PARM3 13
+#define OFS_PARM4 16
+#define RESERVED_OFS 28
+
+
+extern def_t *pr_scope;
+extern int pr_error_count;
+
+void PR_NewLine (void);
+def_t *PR_GetDef (type_t *type, char *name, def_t *scope, qboolean allocate);
+
+void PR_PrintDefs (void);
+
+void PR_SkipToSemicolon (void);
+
+extern char pr_parm_names[MAX_PARMS][MAX_NAME];
+extern qboolean pr_trace;
+
+#define G_FLOAT(o) (pr_globals[o])
+#define G_INT(o) (*(int *)&pr_globals[o])
+#define G_VECTOR(o) (&pr_globals[o])
+#define G_STRING(o) (strings + *(string_t *)&pr_globals[o])
+#define G_FUNCTION(o) (*(func_t *)&pr_globals[o])
+
+char *PR_ValueString (etype_t type, void *val);
+
+void PR_ClearGrabMacros (void);
+
+qboolean PR_CompileFile (char *string, char *filename);
+
+extern qboolean pr_dumpasm;
+
+extern string_t s_file; // filename for function definition
+
+extern def_t def_ret, def_parms[MAX_PARMS];
+
+//=============================================================================
+
+#define MAX_STRINGS 500000
+#define MAX_GLOBALS 16384
+#define MAX_FIELDS 1024
+#define MAX_STATEMENTS 65536
+#define MAX_FUNCTIONS 8192
+
+#define MAX_SOUNDS 1024
+#define MAX_MODELS 1024
+#define MAX_FILES 1024
+#define MAX_DATA_PATH 64
+
+extern char strings[MAX_STRINGS];
+extern int strofs;
+
+extern dstatement_t statements[MAX_STATEMENTS];
+extern int numstatements;
+extern int statement_linenums[MAX_STATEMENTS];
+
+extern dfunction_t functions[MAX_FUNCTIONS];
+extern int numfunctions;
+
+extern float pr_globals[MAX_REGS];
+extern int numpr_globals;
+
+extern char pr_immediate_string[2048];
+
+extern char precache_sounds[MAX_SOUNDS][MAX_DATA_PATH];
+extern int precache_sounds_block[MAX_SOUNDS];
+extern int numsounds;
+
+extern char precache_models[MAX_MODELS][MAX_DATA_PATH];
+extern int precache_models_block[MAX_SOUNDS];
+extern int nummodels;
+
+extern char precache_files[MAX_FILES][MAX_DATA_PATH];
+extern int precache_files_block[MAX_SOUNDS];
+extern int numfiles;
+
+int CopyString (char *str);
+
+
diff --git a/qutils/QCC/QCC.MAK b/qutils/QCC/QCC.MAK
new file mode 100644
index 0000000..e408f6a
--- /dev/null
+++ b/qutils/QCC/QCC.MAK
@@ -0,0 +1,249 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+!IF "$(CFG)" == ""
+CFG=qcc - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to qcc - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "qcc - Win32 Release" && "$(CFG)" != "qcc - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "qcc.mak" CFG="qcc - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "qcc - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "qcc - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "qcc - Win32 Debug"
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "qcc - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\qcc.exe"
+
+CLEAN :
+ -@erase ".\Release\qcc.exe"
+ -@erase ".\Release\qcc.obj"
+ -@erase ".\Release\pr_comp.obj"
+ -@erase ".\Release\pr_lex.obj"
+ -@erase ".\Release\cmdlib.obj"
+ -@erase ".\Release\vc40.pdb"
+ -@erase ".\Release\qcc.map"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /GX /Zi /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /ML /GX /Zi /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/qcc.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\Release/
+CPP_SBRS=
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qcc.bsc"
+BSC32_SBRS=
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /profile /map /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:console /profile /map:"$(INTDIR)/qcc.map"\
+ /machine:I386 /out:"$(OUTDIR)/qcc.exe"
+LINK32_OBJS= \
+ "$(INTDIR)/qcc.obj" \
+ "$(INTDIR)/pr_comp.obj" \
+ "$(INTDIR)/pr_lex.obj" \
+ "$(INTDIR)/cmdlib.obj"
+
+"$(OUTDIR)\qcc.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "qcc - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\qcc.exe"
+
+CLEAN :
+ -@erase ".\Debug\vc40.pdb"
+ -@erase ".\Debug\vc40.idb"
+ -@erase ".\Debug\qcc.exe"
+ -@erase ".\Debug\pr_lex.obj"
+ -@erase ".\Debug\qcc.obj"
+ -@erase ".\Debug\cmdlib.obj"
+ -@erase ".\Debug\pr_comp.obj"
+ -@erase ".\Debug\qcc.map"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MLd /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/qcc.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\Debug/
+CPP_SBRS=
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qcc.bsc"
+BSC32_SBRS=
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /profile /map /debug /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:console /profile /map:"$(INTDIR)/qcc.map"\
+ /debug /machine:I386 /out:"$(OUTDIR)/qcc.exe"
+LINK32_OBJS= \
+ "$(INTDIR)/pr_lex.obj" \
+ "$(INTDIR)/qcc.obj" \
+ "$(INTDIR)/cmdlib.obj" \
+ "$(INTDIR)/pr_comp.obj"
+
+"$(OUTDIR)\qcc.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+################################################################################
+# Begin Target
+
+# Name "qcc - Win32 Release"
+# Name "qcc - Win32 Debug"
+
+!IF "$(CFG)" == "qcc - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qcc - Win32 Debug"
+
+!ENDIF
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\pr_comp.c
+DEP_CPP_PR_CO=\
+ ".\qcc.h"\
+ ".\..\common\cmdlib.h"\
+ ".\pr_comp.h"\
+
+
+"$(INTDIR)\pr_comp.obj" : $(SOURCE) $(DEP_CPP_PR_CO) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qcc.c
+DEP_CPP_QCC_C=\
+ ".\qcc.h"\
+ ".\..\common\cmdlib.h"\
+ ".\pr_comp.h"\
+
+
+"$(INTDIR)\qcc.obj" : $(SOURCE) $(DEP_CPP_QCC_C) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\pr_lex.c
+DEP_CPP_PR_LE=\
+ ".\qcc.h"\
+ ".\..\common\cmdlib.h"\
+ ".\pr_comp.h"\
+
+
+"$(INTDIR)\pr_lex.obj" : $(SOURCE) $(DEP_CPP_PR_LE) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\cmdlib.c
+DEP_CPP_CMDLI=\
+ ".\..\common\cmdlib.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+ {$(INCLUDE)}"\sys\STAT.H"\
+
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+# End Target
+# End Project
+################################################################################
diff --git a/qutils/QCC/QCC.MDP b/qutils/QCC/QCC.MDP
new file mode 100644
index 0000000..7ba5715
--- /dev/null
+++ b/qutils/QCC/QCC.MDP
Binary files differ
diff --git a/qutils/QCC/QCC.NCB b/qutils/QCC/QCC.NCB
new file mode 100644
index 0000000..b95ef20
--- /dev/null
+++ b/qutils/QCC/QCC.NCB
Binary files differ
diff --git a/qutils/QCC/QCC.PDB b/qutils/QCC/QCC.PDB
new file mode 100644
index 0000000..7686f24
--- /dev/null
+++ b/qutils/QCC/QCC.PDB
Binary files differ
diff --git a/qutils/QCC/VC40.PDB b/qutils/QCC/VC40.PDB
new file mode 100644
index 0000000..a16cd52
--- /dev/null
+++ b/qutils/QCC/VC40.PDB
Binary files differ
diff --git a/qutils/QFILES/QFILES.C b/qutils/QFILES/QFILES.C
new file mode 100644
index 0000000..cb732f0
--- /dev/null
+++ b/qutils/QFILES/QFILES.C
@@ -0,0 +1,251 @@
+
+#include "cmdlib.h"
+
+#define MAX_SOUNDS 1024
+#define MAX_MODELS 1024
+#define MAX_FILES 1024
+
+#define MAX_DATA_PATH 64
+
+
+char precache_sounds[MAX_SOUNDS][MAX_DATA_PATH];
+int precache_sounds_block[MAX_SOUNDS];
+int numsounds;
+
+char precache_models[MAX_MODELS][MAX_DATA_PATH];
+int precache_models_block[MAX_SOUNDS];
+int nummodels;
+
+char precache_files[MAX_FILES][MAX_DATA_PATH];
+int precache_files_block[MAX_SOUNDS];
+int numfiles;
+
+
+typedef struct
+{
+ char name[56];
+ int filepos, filelen;
+} packfile_t;
+
+typedef struct
+{
+ char id[4];
+ int dirofs;
+ int dirlen;
+} packheader_t;
+
+packfile_t pfiles[4096], *pf;
+FILE *packhandle;
+int packbytes;
+
+
+/*
+===========
+PackFile
+
+Copy a file into the pak file
+===========
+*/
+void PackFile (char *src, char *name)
+{
+ FILE *in;
+ int remaining, count;
+ char buf[4096];
+
+ if ( (byte *)pf - (byte *)pfiles > sizeof(pfiles) )
+ Error ("Too many files in pak file");
+
+ in = SafeOpenRead (src);
+ remaining = filelength (in);
+
+ pf->filepos = LittleLong (ftell (packhandle));
+ pf->filelen = LittleLong (remaining);
+ strcpy (pf->name, name);
+ printf ("%64s : %7i\n", pf->name, remaining);
+
+ packbytes += remaining;
+
+ while (remaining)
+ {
+ if (remaining < sizeof(buf))
+ count = remaining;
+ else
+ count = sizeof(buf);
+ SafeRead (in, buf, count);
+ SafeWrite (packhandle, buf, count);
+ remaining -= count;
+ }
+
+ fclose (in);
+ pf++;
+}
+
+
+/*
+===========
+CopyQFiles
+===========
+*/
+void CopyQFiles (int blocknum)
+{
+ int i, p;
+ char srcfile[1024];
+ char destfile[1024];
+ char name[1024];
+ packheader_t header;
+ int dirlen;
+ unsigned short crc;
+
+ // create a pak file
+ pf = pfiles;
+
+ sprintf (destfile, "%spak%i.pak", gamedir, blocknum);
+ packhandle = SafeOpenWrite (destfile);
+ SafeWrite (packhandle, &header, sizeof(header));
+
+ blocknum++;
+
+ for (i=0 ; i<numsounds ; i++)
+ {
+ if (precache_sounds_block[i] != blocknum)
+ continue;
+ sprintf (name, "sound/%s", precache_sounds[i]);
+ sprintf (srcfile,"%s%s",gamedir, name);
+ PackFile (srcfile, name);
+ }
+ for (i=0 ; i<nummodels ; i++)
+ {
+ if (precache_models_block[i] != blocknum)
+ continue;
+ sprintf (srcfile,"%s%s",gamedir, precache_models[i]);
+ PackFile (srcfile, precache_models[i]);
+ }
+ for (i=0 ; i<numfiles ; i++)
+ {
+ if (precache_files_block[i] != blocknum)
+ continue;
+ sprintf (srcfile,"%s%s",gamedir, precache_files[i]);
+ PackFile (srcfile, precache_files[i]);
+ }
+
+ header.id[0] = 'P';
+ header.id[1] = 'A';
+ header.id[2] = 'C';
+ header.id[3] = 'K';
+ dirlen = (byte *)pf - (byte *)pfiles;
+ header.dirofs = LittleLong(ftell (packhandle));
+ header.dirlen = LittleLong(dirlen);
+
+ SafeWrite (packhandle, pfiles, dirlen);
+
+ fseek (packhandle, 0, SEEK_SET);
+ SafeWrite (packhandle, &header, sizeof(header));
+ fclose (packhandle);
+
+// do a crc of the file
+ CRC_Init (&crc);
+ for (i=0 ; i<dirlen ; i++)
+ CRC_ProcessByte (&crc, ((byte *)pfiles)[i]);
+
+ i = pf - pfiles;
+ printf ("%i files packed in %i bytes (%i crc)\n",i, packbytes, crc);
+}
+
+
+/*
+=================
+BspModels
+
+Runs qbsp and light on all of the models with a .bsp extension
+=================
+*/
+void BspModels (void)
+{
+ int p;
+ int i;
+ char *m;
+ char cmd[1024];
+ char name[256];
+
+ for (i=0 ; i<nummodels ; i++)
+ {
+ m = precache_models[i];
+ if (strcmp(m+strlen(m)-4, ".bsp"))
+ continue;
+ strcpy (name, m);
+ name[strlen(m)-4] = 0;
+
+ sprintf (cmd, "qbsp %s%s",gamedir, name);
+ system (cmd);
+ sprintf (cmd, "light -extra %s%s", gamedir, name);
+ system (cmd);
+ }
+}
+
+/*
+=============
+ReadFiles
+=============
+*/
+int ReadFiles (void)
+{
+ FILE *f;
+ int i;
+
+ f = SafeOpenRead ("files.dat");
+
+ fscanf (f, "%i\n", &numsounds);
+ for (i=0 ; i<numsounds ; i++)
+ fscanf (f, "%i %s\n", &precache_sounds_block[i],
+ precache_sounds[i]);
+
+ fscanf (f, "%i\n", &nummodels);
+ for (i=0 ; i<nummodels ; i++)
+ fscanf (f, "%i %s\n", &precache_models_block[i],
+ precache_models[i]);
+
+ fscanf (f, "%i\n", &numfiles);
+ for (i=0 ; i<numfiles ; i++)
+ fscanf (f, "%i %s\n", &precache_files_block[i],
+ precache_files[i]);
+
+ fclose (f);
+
+ printf ("%3i sounds\n", numsounds);
+ printf ("%3i models\n", nummodels);
+ printf ("%3i files\n", numfiles);
+}
+
+
+/*
+=============
+main
+=============
+*/
+int main (int argc, char **argv)
+{
+ if (argc == 1)
+ {
+ printf ("qfiles -pak <0 / 1> : build a .pak file\n");
+ printf ("qfiles -bspmodels : regenerates all brush models\n");
+ exit (1);
+ }
+
+ SetQdirFromPath ("");
+
+ ReadFiles ();
+
+ if (!strcmp (argv[1], "-pak"))
+ {
+ CopyQFiles (atoi(argv[2]));
+ }
+ else if (!strcmp (argv[1], "-bspmodels"))
+ {
+ BspModels ();
+ }
+ else
+ Error ("unknown command: %s", argv[1]);
+
+ return 0;
+}
+
diff --git a/qutils/QFILES/QFILES.MAK b/qutils/QFILES/QFILES.MAK
new file mode 100644
index 0000000..f9f4a4f
--- /dev/null
+++ b/qutils/QFILES/QFILES.MAK
@@ -0,0 +1,221 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+!IF "$(CFG)" == ""
+CFG=qfiles - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to qfiles - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "qfiles - Win32 Release" && "$(CFG)" != "qfiles - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "qfiles.mak" CFG="qfiles - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "qfiles - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "qfiles - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+################################################################################
+# Begin Project
+RSC=rc.exe
+CPP=cl.exe
+
+!IF "$(CFG)" == "qfiles - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\qfiles.exe"
+
+CLEAN :
+ -@erase ".\Release\qfiles.exe"
+ -@erase ".\Release\qfiles.obj"
+ -@erase ".\Release\cmdlib.obj"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /W1 /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /ML /W1 /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/qfiles.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\Release/
+CPP_SBRS=
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qfiles.bsc"
+BSC32_SBRS=
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:console /incremental:no\
+ /pdb:"$(OUTDIR)/qfiles.pdb" /machine:I386 /out:"$(OUTDIR)/qfiles.exe"
+LINK32_OBJS= \
+ "$(INTDIR)/qfiles.obj" \
+ "$(INTDIR)/cmdlib.obj"
+
+"$(OUTDIR)\qfiles.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "qfiles - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\qfiles.exe"
+
+CLEAN :
+ -@erase ".\Debug\qfiles.exe"
+ -@erase ".\Debug\qfiles.obj"
+ -@erase ".\Debug\cmdlib.obj"
+ -@erase ".\Debug\qfiles.ilk"
+ -@erase ".\Debug\qfiles.pdb"
+ -@erase ".\Debug\vc40.pdb"
+ -@erase ".\Debug\vc40.idb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /W1 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MLd /W1 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG"\
+ /D "_CONSOLE" /Fp"$(INTDIR)/qfiles.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\Debug/
+CPP_SBRS=
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qfiles.bsc"
+BSC32_SBRS=
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:console /incremental:yes\
+ /pdb:"$(OUTDIR)/qfiles.pdb" /debug /machine:I386 /out:"$(OUTDIR)/qfiles.exe"
+LINK32_OBJS= \
+ "$(INTDIR)/qfiles.obj" \
+ "$(INTDIR)/cmdlib.obj"
+
+"$(OUTDIR)\qfiles.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+################################################################################
+# Begin Target
+
+# Name "qfiles - Win32 Release"
+# Name "qfiles - Win32 Debug"
+
+!IF "$(CFG)" == "qfiles - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qfiles - Win32 Debug"
+
+!ENDIF
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\qfiles.c
+DEP_CPP_QFILE=\
+ ".\..\common\cmdlib.h"\
+
+
+"$(INTDIR)\qfiles.obj" : $(SOURCE) $(DEP_CPP_QFILE) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\cmdlib.c
+DEP_CPP_CMDLI=\
+ ".\..\common\cmdlib.h"\
+ {$(INCLUDE)}"\sys\types.h"\
+ {$(INCLUDE)}"\sys\stat.h"\
+
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\cmdlib.h
+
+!IF "$(CFG)" == "qfiles - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qfiles - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+# End Target
+# End Project
+################################################################################
diff --git a/qutils/QFILES/QFILES.MDP b/qutils/QFILES/QFILES.MDP
new file mode 100644
index 0000000..52b2e99
--- /dev/null
+++ b/qutils/QFILES/QFILES.MDP
Binary files differ
diff --git a/qutils/QFILES/QFILES.NCB b/qutils/QFILES/QFILES.NCB
new file mode 100644
index 0000000..cea1e95
--- /dev/null
+++ b/qutils/QFILES/QFILES.NCB
Binary files differ
diff --git a/qutils/QLUMPY/QLUMPY.C b/qutils/QLUMPY/QLUMPY.C
new file mode 100644
index 0000000..3604e09
--- /dev/null
+++ b/qutils/QLUMPY/QLUMPY.C
@@ -0,0 +1,301 @@
+#define VERSION "2.0"
+#include "qlumpy.h"
+
+
+#define MAXLUMP 0x50000 // biggest possible lump
+
+
+byte *byteimage, *lbmpalette;
+int byteimagewidth, byteimageheight;
+
+char basepath[1024];
+char lumpname[16];
+
+char destfile[1024];
+
+byte *lumpbuffer, *lump_p;
+
+qboolean savesingle;
+qboolean outputcreated;
+
+/*
+=============================================================================
+
+ MAIN
+
+=============================================================================
+*/
+
+void GrabRaw (void);
+void GrabPalette (void);
+void GrabPic (void);
+void GrabMip (void);
+void GrabColormap (void);
+void GrabColormap2 (void);
+
+typedef struct
+{
+ char *name;
+ void (*function) (void);
+} command_t;
+
+command_t commands[] =
+{
+ {"palette",GrabPalette},
+ {"colormap",GrabColormap},
+ {"qpic",GrabPic},
+ {"miptex",GrabMip},
+ {"raw",GrabRaw},
+
+ {"colormap2",GrabColormap2},
+
+ {NULL,NULL} // list terminator
+};
+
+
+
+/*
+==============
+LoadScreen
+==============
+*/
+void LoadScreen (char *name)
+{
+ char *expanded;
+
+ expanded = ExpandPathAndArchive (name);
+
+ printf ("grabbing from %s...\n",expanded);
+ LoadLBM (expanded, &byteimage, &lbmpalette);
+
+ byteimagewidth = bmhd.w;
+ byteimageheight = bmhd.h;
+}
+
+
+/*
+================
+CreateOutput
+================
+*/
+void CreateOutput (void)
+{
+ outputcreated = true;
+//
+// create the output wadfile file
+//
+ NewWad (destfile, false); // create a new wadfile
+}
+
+/*
+===============
+WriteLump
+===============
+*/
+void WriteLump (int type, int compression)
+{
+ int size;
+
+ if (!outputcreated)
+ CreateOutput ();
+
+//
+// dword align the size
+//
+ while ((int)lump_p&3)
+ *lump_p++ = 0;
+
+ size = lump_p - lumpbuffer;
+ if (size > MAXLUMP)
+ Error ("Lump size exceeded %d, memory corrupted!",MAXLUMP);
+
+//
+// write the grabbed lump to the wadfile
+//
+ AddLump (lumpname,lumpbuffer,size,type, compression);
+}
+
+/*
+===========
+WriteFile
+
+Save as a seperate file instead of as a wadfile lump
+===========
+*/
+void WriteFile (void)
+{
+ char filename[1024];
+ char *exp;
+
+ sprintf (filename,"%s/%s.lmp", destfile, lumpname);
+ exp = ExpandPath(filename);
+ printf ("saved %s\n", exp);
+ SaveFile (exp, lumpbuffer, lump_p-lumpbuffer);
+}
+
+/*
+================
+ParseScript
+================
+*/
+void ParseScript (void)
+{
+ int cmd;
+ int size;
+
+ do
+ {
+ //
+ // get a command / lump name
+ //
+ GetToken (true);
+ if (endofscript)
+ break;
+ if (!Q_strcasecmp (token,"$LOAD"))
+ {
+ GetToken (false);
+ LoadScreen (token);
+ continue;
+ }
+
+ if (!Q_strcasecmp (token,"$DEST"))
+ {
+ GetToken (false);
+ strcpy (destfile, ExpandPath(token));
+ continue;
+ }
+
+ if (!Q_strcasecmp (token,"$SINGLEDEST"))
+ {
+ GetToken (false);
+ strcpy (destfile, token);
+ savesingle = true;
+ continue;
+ }
+
+
+ //
+ // new lump
+ //
+ if (strlen(token) >= sizeof(lumpname) )
+ Error ("\"%s\" is too long to be a lump name",token);
+ memset (lumpname,0,sizeof(lumpname));
+ strcpy (lumpname, token);
+ for (size=0 ; size<sizeof(lumpname) ; size++)
+ lumpname[size] = tolower(lumpname[size]);
+
+ //
+ // get the grab command
+ //
+ lump_p = lumpbuffer;
+
+ GetToken (false);
+
+ //
+ // call a routine to grab some data and put it in lumpbuffer
+ // with lump_p pointing after the last byte to be saved
+ //
+ for (cmd=0 ; commands[cmd].name ; cmd++)
+ if ( !Q_strcasecmp(token,commands[cmd].name) )
+ {
+ commands[cmd].function ();
+ break;
+ }
+
+ if ( !commands[cmd].name )
+ Error ("Unrecognized token '%s' at line %i",token,scriptline);
+
+ grabbed++;
+
+ if (savesingle)
+ WriteFile ();
+ else
+ WriteLump (TYP_LUMPY+cmd, 0);
+
+ } while (script_p < scriptend_p);
+}
+
+/*
+=================
+ProcessLumpyScript
+
+Loads a script file, then grabs everything from it
+=================
+*/
+void ProcessLumpyScript (char *basename)
+{
+ char script[256];
+
+ printf ("qlumpy script: %s\n",basename);
+
+//
+// create default destination directory
+//
+ strcpy (destfile, ExpandPath(basename));
+ StripExtension (destfile);
+ strcat (destfile,".wad"); // unless the script overrides, save in cwd
+
+//
+// save in a wadfile by default
+//
+ savesingle = false;
+ grabbed = 0;
+ outputcreated = false;
+
+
+//
+// read in the script file
+//
+ strcpy (script, basename);
+ DefaultExtension (script, ".ls");
+ LoadScriptFile (script);
+
+ strcpy (basepath, basename);
+
+ ParseScript (); // execute load / grab commands
+
+ if (!savesingle)
+ {
+ WriteWad (); // write out the wad directory
+ printf ("%i lumps grabbed in a wad file\n",grabbed);
+ }
+ else
+ printf ("%i lumps written seperately\n",grabbed);
+}
+
+
+/*
+==============================
+main
+==============================
+*/
+int main (int argc, char **argv)
+{
+ int i;
+
+ printf ("\nqlumpy "VERSION" by John Carmack, copyright (c) 1994 Id Software\n");
+
+ if (argc == 1)
+ Error ("qlumpy [-archive directory] scriptfile [scriptfile ...]");
+
+ lumpbuffer = malloc (MAXLUMP);
+
+ if (!strcmp(argv[1], "-archive"))
+ {
+ archive = true;
+ strcpy (archivedir, argv[2]);
+ printf ("Archiving source to: %s\n", archivedir);
+ i = 3;
+ }
+ else
+ i = 1;
+
+
+ for ( ; i<argc ; i++)
+ {
+ SetQdirFromPath (argv[i]);
+ ProcessLumpyScript (argv[i]);
+ }
+
+ return 0;
+}
diff --git a/qutils/QLUMPY/QLUMPY.H b/qutils/QLUMPY/QLUMPY.H
new file mode 100644
index 0000000..6b800d5
--- /dev/null
+++ b/qutils/QLUMPY/QLUMPY.H
@@ -0,0 +1,16 @@
+#include "cmdlib.h"
+#include "scriplib.h"
+#include "lbmlib.h"
+#include "wadlib.h"
+
+
+extern byte *byteimage, *lbmpalette;
+extern int byteimagewidth, byteimageheight;
+
+#define SCRN(x,y) (*(byteimage+(y)*byteimagewidth+x))
+
+extern byte *lump_p;
+extern byte *lumpbuffer;
+
+extern char lumpname[];
+
diff --git a/qutils/QLUMPY/QLUMPY.MAK b/qutils/QLUMPY/QLUMPY.MAK
new file mode 100644
index 0000000..2abae44
--- /dev/null
+++ b/qutils/QLUMPY/QLUMPY.MAK
@@ -0,0 +1,324 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+!IF "$(CFG)" == ""
+CFG=qlumpy - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to qlumpy - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "qlumpy - Win32 Release" && "$(CFG)" != "qlumpy - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "qlumpy.mak" CFG="qlumpy - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "qlumpy - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "qlumpy - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "qlumpy - Win32 Debug"
+RSC=rc.exe
+CPP=cl.exe
+
+!IF "$(CFG)" == "qlumpy - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\qlumpy.exe"
+
+CLEAN :
+ -@erase ".\Release\qlumpy.exe"
+ -@erase ".\Release\quakegrb.obj"
+ -@erase ".\Release\qlumpy.obj"
+ -@erase ".\Release\wadlib.obj"
+ -@erase ".\Release\scriplib.obj"
+ -@erase ".\Release\cmdlib.obj"
+ -@erase ".\Release\lbmlib.obj"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /ML /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/qlumpy.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\Release/
+CPP_SBRS=
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qlumpy.bsc"
+BSC32_SBRS=
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:console /incremental:no\
+ /pdb:"$(OUTDIR)/qlumpy.pdb" /machine:I386 /out:"$(OUTDIR)/qlumpy.exe"
+LINK32_OBJS= \
+ ".\Release\quakegrb.obj" \
+ ".\Release\qlumpy.obj" \
+ ".\Release\wadlib.obj" \
+ ".\Release\scriplib.obj" \
+ ".\Release\cmdlib.obj" \
+ ".\Release\lbmlib.obj"
+
+"$(OUTDIR)\qlumpy.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "qlumpy - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\qlumpy.exe"
+
+CLEAN :
+ -@erase ".\Debug\vc40.pdb"
+ -@erase ".\Debug\vc40.idb"
+ -@erase ".\Debug\qlumpy.exe"
+ -@erase ".\Debug\lbmlib.obj"
+ -@erase ".\Debug\scriplib.obj"
+ -@erase ".\Debug\cmdlib.obj"
+ -@erase ".\Debug\qlumpy.obj"
+ -@erase ".\Debug\quakegrb.obj"
+ -@erase ".\Debug\wadlib.obj"
+ -@erase ".\Debug\qlumpy.ilk"
+ -@erase ".\Debug\qlumpy.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MLd /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/qlumpy.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\Debug/
+CPP_SBRS=
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qlumpy.bsc"
+BSC32_SBRS=
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:console /incremental:yes\
+ /pdb:"$(OUTDIR)/qlumpy.pdb" /debug /machine:I386 /out:"$(OUTDIR)/qlumpy.exe"
+LINK32_OBJS= \
+ ".\Debug\lbmlib.obj" \
+ ".\Debug\scriplib.obj" \
+ ".\Debug\cmdlib.obj" \
+ ".\Debug\qlumpy.obj" \
+ ".\Debug\quakegrb.obj" \
+ ".\Debug\wadlib.obj"
+
+"$(OUTDIR)\qlumpy.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+################################################################################
+# Begin Target
+
+# Name "qlumpy - Win32 Release"
+# Name "qlumpy - Win32 Debug"
+
+!IF "$(CFG)" == "qlumpy - Win32 Release"
+
+!ELSEIF "$(CFG)" == "qlumpy - Win32 Debug"
+
+!ENDIF
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\quakegrb.c
+
+!IF "$(CFG)" == "qlumpy - Win32 Release"
+
+DEP_CPP_QUAKE=\
+ ".\qlumpy.h"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\scriplib.h"\
+ ".\..\common\lbmlib.h"\
+ ".\..\common\wadlib.h"\
+
+
+"$(INTDIR)\quakegrb.obj" : $(SOURCE) $(DEP_CPP_QUAKE) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qlumpy - Win32 Debug"
+
+DEP_CPP_QUAKE=\
+ ".\qlumpy.h"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\scriplib.h"\
+ ".\..\common\lbmlib.h"\
+
+
+"$(INTDIR)\quakegrb.obj" : $(SOURCE) $(DEP_CPP_QUAKE) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qlumpy.c
+
+!IF "$(CFG)" == "qlumpy - Win32 Release"
+
+DEP_CPP_QLUMP=\
+ ".\qlumpy.h"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\scriplib.h"\
+ ".\..\common\lbmlib.h"\
+ ".\..\common\wadlib.h"\
+
+
+"$(INTDIR)\qlumpy.obj" : $(SOURCE) $(DEP_CPP_QLUMP) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "qlumpy - Win32 Debug"
+
+DEP_CPP_QLUMP=\
+ ".\qlumpy.h"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\scriplib.h"\
+ ".\..\common\lbmlib.h"\
+
+
+"$(INTDIR)\qlumpy.obj" : $(SOURCE) $(DEP_CPP_QLUMP) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\cmdlib.c
+DEP_CPP_CMDLI=\
+ ".\..\common\cmdlib.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+ {$(INCLUDE)}"\sys\STAT.H"\
+
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\scriplib.c
+DEP_CPP_SCRIP=\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\scriplib.h"\
+
+
+"$(INTDIR)\scriplib.obj" : $(SOURCE) $(DEP_CPP_SCRIP) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\wadlib.c
+DEP_CPP_WADLI=\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+ {$(INCLUDE)}"\sys\STAT.H"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\wadlib.h"\
+
+
+"$(INTDIR)\wadlib.obj" : $(SOURCE) $(DEP_CPP_WADLI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\lbmlib.c
+DEP_CPP_LBMLI=\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\lbmlib.h"\
+
+
+"$(INTDIR)\lbmlib.obj" : $(SOURCE) $(DEP_CPP_LBMLI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+# End Target
+# End Project
+################################################################################
diff --git a/qutils/QLUMPY/QLUMPY.MDP b/qutils/QLUMPY/QLUMPY.MDP
new file mode 100644
index 0000000..0124817
--- /dev/null
+++ b/qutils/QLUMPY/QLUMPY.MDP
Binary files differ
diff --git a/qutils/QLUMPY/QLUMPY.NCB b/qutils/QLUMPY/QLUMPY.NCB
new file mode 100644
index 0000000..fb52503
--- /dev/null
+++ b/qutils/QLUMPY/QLUMPY.NCB
Binary files differ
diff --git a/qutils/QLUMPY/QUAKEGRB.C b/qutils/QLUMPY/QUAKEGRB.C
new file mode 100644
index 0000000..d01dc5a
--- /dev/null
+++ b/qutils/QLUMPY/QUAKEGRB.C
@@ -0,0 +1,523 @@
+#include "qlumpy.h"
+
+typedef struct
+{
+ short ofs, length;
+} row_t;
+
+typedef struct
+{
+ int width, height;
+ int widthbits, heightbits;
+ unsigned char data[4];
+} qtex_t;
+
+typedef struct
+{
+ int width, height;
+ byte data[4]; // variably sized
+} qpic_t;
+
+
+#define SCRN(x,y) (*(byteimage+(y)*byteimagewidth+x))
+
+
+/*
+==============
+GrabRaw
+
+filename RAW x y width height
+==============
+*/
+void GrabRaw (void)
+{
+ int x,y,xl,yl,xh,yh,w,h;
+ byte *screen_p;
+ int linedelta;
+
+ GetToken (false);
+ xl = atoi (token);
+ GetToken (false);
+ yl = atoi (token);
+ GetToken (false);
+ w = atoi (token);
+ GetToken (false);
+ h = atoi (token);
+
+ xh = xl+w;
+ yh = yl+h;
+
+ screen_p = byteimage + yl*byteimagewidth + xl;
+ linedelta = byteimagewidth - w;
+
+ for (y=yl ; y<yh ; y++)
+ {
+ for (x=xl ; x<xh ; x++)
+ {
+ *lump_p++ = *screen_p;
+ *screen_p++ = 0;
+ }
+ screen_p += linedelta;
+ }
+}
+
+
+
+/*
+==============
+GrabPalette
+
+filename PALETTE [startcolor endcolor]
+==============
+*/
+void GrabPalette (void)
+{
+ int start,end,length;
+
+ if (TokenAvailable())
+ {
+ GetToken (false);
+ start = atoi (token);
+ GetToken (false);
+ end = atoi (token);
+ }
+ else
+ {
+ start = 0;
+ end = 255;
+ }
+
+ length = 3*(end-start+1);
+ memcpy (lump_p, lbmpalette+start*3, length);
+ lump_p += length;
+}
+
+
+/*
+==============
+GrabPic
+
+filename qpic x y width height
+==============
+*/
+void GrabPic (void)
+{
+ int x,y,xl,yl,xh,yh;
+ int width;
+ byte transcolor;
+ qpic_t *header;
+
+ GetToken (false);
+ xl = atoi (token);
+ GetToken (false);
+ yl = atoi (token);
+ GetToken (false);
+ xh = xl-1+atoi (token);
+ GetToken (false);
+ yh = yl-1+atoi (token);
+
+ if (xh<xl || yh<yl || xl < 0 || yl<0 || xh>319 || yh>199)
+ Error ("GrabPic: Bad size: %i, %i, %i, %i",xl,yl,xh,yh);
+
+ transcolor = 255;
+
+
+//
+// fill in header
+//
+ header = (qpic_t *)lump_p;
+ width = xh-xl+1;
+ header->width = LittleLong(width);
+ header->height = LittleLong(yh-yl+1);
+
+//
+// start grabbing posts
+//
+ lump_p = (byte *)header->data;
+
+ for (y=yl ; y<= yh ; y++)
+ for (x=xl ; x<=xh ; x++)
+ *lump_p++ = SCRN(x,y);
+}
+
+/*
+=============================================================================
+
+COLORMAP GRABBING
+
+=============================================================================
+*/
+
+/*
+===============
+BestColor
+===============
+*/
+byte BestColor (int r, int g, int b, int start, int stop)
+{
+ int i;
+ int dr, dg, db;
+ int bestdistortion, distortion;
+ int bestcolor;
+ byte *pal;
+
+//
+// let any color go to 0 as a last resort
+//
+ bestdistortion = ( (int)r*r + (int)g*g + (int)b*b )*2;
+ bestcolor = 0;
+
+ pal = lbmpalette + start*3;
+ for (i=start ; i<= stop ; i++)
+ {
+ dr = r - (int)pal[0];
+ dg = g - (int)pal[1];
+ db = b - (int)pal[2];
+ pal += 3;
+ distortion = dr*dr + dg*dg + db*db;
+ if (distortion < bestdistortion)
+ {
+ if (!distortion)
+ return i; // perfect match
+
+ bestdistortion = distortion;
+ bestcolor = i;
+ }
+ }
+
+ return bestcolor;
+}
+
+
+/*
+==============
+GrabColormap
+
+filename COLORMAP levels fullbrights
+the first map is an identiy 0-255
+the final map is all black except for the fullbrights
+the remaining maps are evenly spread
+fullbright colors start at the top of the palette.
+==============
+*/
+void GrabColormap (void)
+{
+ int levels, brights;
+ int l, c;
+ float frac, red, green, blue;
+
+ GetToken (false);
+ levels = atoi (token);
+ GetToken (false);
+ brights = atoi (token);
+
+// identity lump
+ for (l=0 ; l<256 ; l++)
+ *lump_p++ = l;
+
+// shaded levels
+ for (l=1;l<levels;l++)
+ {
+ frac = 1.0 - (float)l/(levels-1);
+ for (c=0 ; c<256-brights ; c++)
+ {
+ red = lbmpalette[c*3];
+ green = lbmpalette[c*3+1];
+ blue = lbmpalette[c*3+2];
+
+ red = (int)(red*frac+0.5);
+ green = (int)(green*frac+0.5);
+ blue = (int)(blue*frac+0.5);
+
+//
+// note: 254 instead of 255 because 255 is the transparent color, and we
+// don't want anything remapping to that
+//
+ *lump_p++ = BestColor(red,green,blue, 0, 254);
+ }
+ for ( ; c<256 ; c++)
+ *lump_p++ = c;
+ }
+
+ *lump_p++ = brights;
+}
+
+/*
+==============
+GrabColormap2
+
+experimental -- not used by quake
+
+filename COLORMAP2 range levels fullbrights
+fullbright colors start at the top of the palette.
+Range can be greater than 1 to allow overbright color tables.
+
+the first map is all 0
+the last (levels-1) map is at range
+==============
+*/
+void GrabColormap2 (void)
+{
+ int levels, brights;
+ int l, c;
+ float frac, red, green, blue;
+ float range;
+
+ GetToken (false);
+ range = atof (token);
+ GetToken (false);
+ levels = atoi (token);
+ GetToken (false);
+ brights = atoi (token);
+
+// shaded levels
+ for (l=0;l<levels;l++)
+ {
+ frac = range - range*(float)l/(levels-1);
+ for (c=0 ; c<256-brights ; c++)
+ {
+ red = lbmpalette[c*3];
+ green = lbmpalette[c*3+1];
+ blue = lbmpalette[c*3+2];
+
+ red = (int)(red*frac+0.5);
+ green = (int)(green*frac+0.5);
+ blue = (int)(blue*frac+0.5);
+
+//
+// note: 254 instead of 255 because 255 is the transparent color, and we
+// don't want anything remapping to that
+//
+ *lump_p++ = BestColor(red,green,blue, 0, 254);
+ }
+
+ // fullbrights allways stay the same
+ for ( ; c<256 ; c++)
+ *lump_p++ = c;
+ }
+
+ *lump_p++ = brights;
+}
+
+/*
+=============================================================================
+
+MIPTEX GRABBING
+
+=============================================================================
+*/
+
+typedef struct
+{
+ char name[16];
+ unsigned width, height;
+ unsigned offsets[4]; // four mip maps stored
+} miptex_t;
+
+byte pixdata[256];
+
+int d_red, d_green, d_blue;
+
+/*
+=============
+AveragePixels
+=============
+*/
+byte AveragePixels (int count)
+{
+ int r,g,b;
+ int i;
+ int vis;
+ int pix;
+ int dr, dg, db;
+ int bestdistortion, distortion;
+ int bestcolor;
+ byte *pal;
+ int fullbright;
+ int e;
+
+ vis = 0;
+ r = g = b = 0;
+ fullbright = 0;
+ for (i=0 ; i<count ; i++)
+ {
+ pix = pixdata[i];
+ if (pix == 255)
+ fullbright = 2;
+ else if (pix >= 240)
+ {
+return pix;
+ if (!fullbright)
+ {
+ fullbright = true;
+ r = 0;
+ g = 0;
+ b = 0;
+ }
+ }
+ else
+ {
+ if (fullbright)
+ continue;
+ }
+
+ r += lbmpalette[pix*3];
+ g += lbmpalette[pix*3+1];
+ b += lbmpalette[pix*3+2];
+ vis++;
+ }
+
+ if (fullbright == 2)
+ return 255;
+
+ r /= vis;
+ g /= vis;
+ b /= vis;
+
+ if (!fullbright)
+ {
+ r += d_red;
+ g += d_green;
+ b += d_blue;
+ }
+
+//
+// find the best color
+//
+ bestdistortion = r*r + g*g + b*b;
+ bestcolor = 0;
+ if (fullbright)
+ {
+ i = 240;
+ e = 255;
+ }
+ else
+ {
+ i = 0;
+ e = 240;
+ }
+
+ for ( ; i< e ; i++)
+ {
+ pix = i; //pixdata[i];
+
+ pal = lbmpalette + pix*3;
+
+ dr = r - (int)pal[0];
+ dg = g - (int)pal[1];
+ db = b - (int)pal[2];
+
+ distortion = dr*dr + dg*dg + db*db;
+ if (distortion < bestdistortion)
+ {
+ if (!distortion)
+ {
+ d_red = d_green = d_blue = 0; // no distortion yet
+ return pix; // perfect match
+ }
+
+ bestdistortion = distortion;
+ bestcolor = pix;
+ }
+ }
+
+ if (!fullbright)
+ { // error diffusion
+ pal = lbmpalette + bestcolor*3;
+ d_red = r - (int)pal[0];
+ d_green = g - (int)pal[1];
+ d_blue = b - (int)pal[2];
+ }
+
+ return bestcolor;
+}
+
+
+/*
+==============
+GrabMip
+
+filename MIP x y width height
+must be multiples of sixteen
+==============
+*/
+void GrabMip (void)
+{
+ int x,y,xl,yl,xh,yh,w,h;
+ byte *screen_p, *source;
+ int linedelta;
+ miptex_t *qtex;
+ int miplevel, mipstep;
+ int xx, yy, pix;
+ int count;
+
+ GetToken (false);
+ xl = atoi (token);
+ GetToken (false);
+ yl = atoi (token);
+ GetToken (false);
+ w = atoi (token);
+ GetToken (false);
+ h = atoi (token);
+
+ if ( (w & 15) || (h & 15) )
+ Error ("line %i: miptex sizes must be multiples of 16", scriptline);
+
+ xh = xl+w;
+ yh = yl+h;
+
+ qtex = (miptex_t *)lump_p;
+ qtex->width = LittleLong(w);
+ qtex->height = LittleLong(h);
+ strcpy (qtex->name, lumpname);
+
+ lump_p = (byte *)&qtex->offsets[4];
+
+ screen_p = byteimage + yl*byteimagewidth + xl;
+ linedelta = byteimagewidth - w;
+
+ source = lump_p;
+ qtex->offsets[0] = LittleLong(lump_p - (byte *)qtex);
+
+ for (y=yl ; y<yh ; y++)
+ {
+ for (x=xl ; x<xh ; x++)
+ {
+ pix = *screen_p;
+ *screen_p++ = 0;
+ if (pix == 255)
+ pix = 0;
+ *lump_p++ = pix;
+ }
+ screen_p += linedelta;
+ }
+
+//
+// subsample for greater mip levels
+//
+ d_red = d_green = d_blue = 0; // no distortion yet
+
+ for (miplevel = 1 ; miplevel<4 ; miplevel++)
+ {
+ qtex->offsets[miplevel] = LittleLong(lump_p - (byte *)qtex);
+
+ mipstep = 1<<miplevel;
+ for (y=0 ; y<h ; y+=mipstep)
+ {
+
+ for (x = 0 ; x<w ; x+= mipstep)
+ {
+ count = 0;
+ for (yy=0 ; yy<mipstep ; yy++)
+ for (xx=0 ; xx<mipstep ; xx++)
+ {
+ pixdata[count] = source[ (y+yy)*w + x + xx ];
+ count++;
+ }
+ *lump_p++ = AveragePixels (count);
+ }
+ }
+ }
+
+
+}
diff --git a/qutils/README.TXT b/qutils/README.TXT
new file mode 100644
index 0000000..38d6f4e
--- /dev/null
+++ b/qutils/README.TXT
@@ -0,0 +1,148 @@
+
+This is the readme from our most recent licensed developer CD. Not all of it is applicable to this source upload, because the map editor, source data, and game source code have not been made freely available (gotta have some reason to charge lots of $$$ for it...), but it is the best documentation I have.
+
+-- John Carmack
+
+
+
+
+Quake Development CD 9/4/96
+---------------------------
+
+Included is all of the source data and utilities necessary to generate all of the data distributed with quake, and the main executable itself. You can modify the data in place, or copy the data you wish to modify to an addon directory and work from there.
+
+The win-32 tools have not been extensively tested yet, because we still do most of our work on unix.
+
+
+Completely building Quake code and data:
+---------------------------------------
+
+This process can take quite some time on a slow machine. I am omiting the steps to rebuild all the maps, otherwise it would take all day (literally).
+
+Install VC++ and MASM. You don't need MASM if you are going to use DJGPP to compile the dos version instead of using the windows version.
+
+Copy the contents of the quake development cd to /quake on any drive. The directory MUST be called "quake", because that string is searched for by the utilities to provide compatability between unix directories mounted inside a tree, and windows directories mounted on a drive letter. You can move it off of root, but it will require changes in a few batch files.
+
+Add drive:\quake\bin_nt to your path.
+
+cd \quake\utils
+install // compiles all of the utilities and copies them to \quake\bin_nt
+
+cd \quake\id1\gfx
+foreach %i in (*.ls) do qlumpy %i // regrab all 2d graphics
+// gfx.ls : graphics that are statically loaded: the small font, status bar stuff, etc
+// cached.ls : graphics that are dynamically cached: menus, console background, etc
+// the other .ls files are texture paletes for map editing
+
+cd \quake\id1\progs
+sprgen sprites.qc // regrab the sprites used in the 3d world (all three of them)
+
+foreach %i in (*.qc) do modelgen %i // regrab all 3d models
+// many of the .qc files do not actually specify a model, but
+// running them through modelgen is harmless
+
+qcc // rebuild progs.dat and files.dat
+
+qfiles -bspmodels // reads files.dat and runs qbsp and light on all external
+ // brush models (health boxes, ammo boxes, etc)
+qfiles -pak 0 // builds \quake\id1\pak0.pak
+qfiles -pak 1 // builds \quake\id1\pak1.pak
+// note that you should not leave the pak files in your development directory, because
+// you won't be able to override any of the contents. If you are doing your work
+// in an add-on directory, it isn't a problem, and the pak files will load faster
+// than the discrete files.
+
+cd \quake\code
+mw // a batch file that compiles the windows version of quake
+q +map newmap // a batch file that runs "quake -basedir \quake +map newmap"
+
+
+
+
+the bsp tools
+-------------
+
+The bsp tools are usually run straight from the map editor, but they can also be called from the command line.
+
+cd \quake\id1\maps
+qbsp dm1 // processes dm1.map into dm1.bsp
+light dm1 // generates lightmaps for dm1.bsp. If you run "light -extra dm1", it will make smoother shadow
+ // edges by oversampling.
+vis dm1 // generates a potentially visible set (PVS) structure for dm1.bsp. This will only work if
+ // the map is leak-free. You can run "vis -fast dm1" to generate a rough PVS without
+ // spending very much time.
+bspinfo newmap // dumps the stats on newmap
+
+QuakeEd
+-------
+You are not expected to be able to figure out how to use QuakeEd from the (nonexistant) documentation we have. You get one full day with one of our map designers for tutoring.
+
+If you want to try it out anyway:
+
+cd \quake\id1 // the directory that contains the quake.qe3 project file
+qe3 // see quakeed.txt for a box-room walkthrough
+
+QuakeEd is still undergoing development. The version included in bin_nt is a newer version than the source included in utils. I don't have the current source here right now.
+
+Expect new versions over the next few weeks.
+
+
+The main source code:
+--------------------
+You can use the djgpp compiler (http://www.delorie.com) to rebuild quake for dos. We used a cross-compiler built for our Digital Unix alpha system that works very rapidly, but the dos hosted compiler is quite slow.
+
+Our reccomended procedure is to forget about dos and just work with the windows version for code changes.
+
+Currently at id we compile for three different platforms: NEXTSTEP, dos, and windows. The code also compiles for linux, but that is not part of our regular process. The C code is totally portable, but the assembly code was writen for GAS, which was unfreindly for windows development. Michael wrote a GAS to MASM translator to allow the assembly code to compile under windows. We still consider
+the GAS code (.s) to be the master, and derive the masm (.asm) code from it inside the makefile. If you are never going to touch
+the assembly code (we don't reccoment you do), or you are willing to take full responsibility for it, you can throw out the .s files
+and just use the .asm.
+
+The direct-sound driver is not very good right now. You may want to run with "-nosound".
+
+
+The utilities source:
+--------------------
+
+Each utility has a seperate directory of code with a VC++ project file. They all share several code files in the "common" directory. The NT versions of these utilities have not been very extensively tested, as we still use DEC Unix for most of our work (soon to change). The two source files you are most likely to change are: common/lbmlib.c to load a more common graphics format, like pcx, and common/trilib.c to load a 3D format other than Alias object seperated triangles.
+
+
+qe3 : The map editor. Designed for use on open GL accelerated systems such as intergraph or glint-TX based systems, but it will still run on the basic NT software version. REQUIRES A 3-BUTTON MOUSE!
+
+qbsp / light / vis : these utilities are called directly from the map editor to process .map files into .bsp files. They can be executed by hand if desired.
+
+bspinfo : a command line utility that will dump the count and size statistics on a .bsp file.
+
+qlumpy : the 2-D graphics grabber. Grabs graphics off of .lbm pictures. Used for grabbing the 2d graphics used by quake (status bar stuff, fonts, etc), and also used for grabbing the textures to be viewed in qe3 and extracted by qbsp. Qlumpy script files have the default extension ".ls" (LumpyScript).
+
+qcc : the Quake-C compiler. Reads "progs.src", then compiles all of the files listed there. Generates "progs.dat" for use by quake at runtime, "progdefs.h" for use at compile time, and "files.dat" to be used as input for qfiles.exe.
+
+qfiles : Builds pak files based on the contents of "files.dat" writen out by qcc. It can also regenerate all of the .bsp models used in a project, which is required if any changes to the file format have been made.
+
+sprgen : the sprite model grabber. Grabs 2d graphics and creates a .spr file.
+
+modelgen : the 3-D model grabber. Combines skin graphics with 3d frames to produce a .mdl file. The commands are parsed out of .qc files that can also be read by qcc, so a single source can both generate and use the data.
+
+texmake : creates 2d wireframe outlines of a 3d model that can be drawn on to give a texture to a model. This is only done once per model, or when the base frame changes.
+Example:
+cd \quake\id1\models\torch
+texmake base // reads base.tri and creates the graphic base.lbm
+copy base.lbm skin.lbm // never work on the base skin, it might get overwritten
+cd \quake\id1\progs
+modelgen torch.qc // creates torch.mdl out of files in \quake\id1\models\torch
+
+
+
+Continuing development work at id:
+------------------------------
+winquake : work is still being done on the direct-X drivers for quake.
+
+qe3 : the NT editor does not yet have full functionality for texture positioning and entity connecting.
+
+qrad : a radiosity replacement for light.exe. Instead of placing light entities, certain textures automatically become light emiters. The light bounces off of surfaces, so a single light panel can light all sides of a room.
+
+qcsg / qbsp / qwrite : qbsp.exe is being broken up into multiple programs to reduce memory usage and provide a better means for experimentation. It should get faster, as well.
+
+visx : a faster replacement for vis.
+
+
diff --git a/qutils/SPRGEN/SPRGEN.C b/qutils/SPRGEN/SPRGEN.C
new file mode 100644
index 0000000..409df0d
--- /dev/null
+++ b/qutils/SPRGEN/SPRGEN.C
@@ -0,0 +1,526 @@
+//
+// spritegen.c: generates a .spr file from a series of .lbm frame files.
+// Result is stored in /raid/quake/id1/sprites/<scriptname>.spr.
+//
+
+#define INCLUDELIBS
+
+
+#ifdef NeXT
+#include <libc.h>
+#endif
+
+#include "spritegn.h"
+
+#define MAX_BUFFER_SIZE 0x100000
+#define MAX_FRAMES 1000
+
+dsprite_t sprite;
+byte *byteimage, *lbmpalette;
+int byteimagewidth, byteimageheight;
+byte *lumpbuffer, *plump;
+char spritedir[1024];
+char spriteoutname[1024];
+int framesmaxs[2];
+int framecount;
+
+typedef struct {
+ spriteframetype_t type; // single frame or group of frames
+ void *pdata; // either a dspriteframe_t or group info
+ float interval; // only used for frames in groups
+ int numgroupframes; // only used by group headers
+} spritepackage_t;
+
+spritepackage_t frames[MAX_FRAMES];
+
+void FinishSprite (void);
+void Cmd_Spritename (void);
+
+
+/*
+============
+WriteFrame
+============
+*/
+void WriteFrame (FILE *spriteouthandle, int framenum)
+{
+ dspriteframe_t *pframe;
+ dspriteframe_t frametemp;
+
+ pframe = (dspriteframe_t *)frames[framenum].pdata;
+ frametemp.origin[0] = LittleLong (pframe->origin[0]);
+ frametemp.origin[1] = LittleLong (pframe->origin[1]);
+ frametemp.width = LittleLong (pframe->width);
+ frametemp.height = LittleLong (pframe->height);
+
+ SafeWrite (spriteouthandle, &frametemp, sizeof (frametemp));
+ SafeWrite (spriteouthandle,
+ (byte *)(pframe + 1),
+ pframe->height * pframe->width);
+}
+
+
+/*
+============
+WriteSprite
+============
+*/
+void WriteSprite (FILE *spriteouthandle)
+{
+ int i, groupframe, curframe;
+ dsprite_t spritetemp;
+
+ sprite.boundingradius = sqrt (((framesmaxs[0] >> 1) *
+ (framesmaxs[0] >> 1)) +
+ ((framesmaxs[1] >> 1) *
+ (framesmaxs[1] >> 1)));
+
+//
+// write out the sprite header
+//
+ spritetemp.type = LittleLong (sprite.type);
+ spritetemp.boundingradius = LittleFloat (sprite.boundingradius);
+ spritetemp.width = LittleLong (framesmaxs[0]);
+ spritetemp.height = LittleLong (framesmaxs[1]);
+ spritetemp.numframes = LittleLong (sprite.numframes);
+ spritetemp.beamlength = LittleFloat (sprite.beamlength);
+ spritetemp.synctype = LittleFloat (sprite.synctype);
+ spritetemp.version = LittleLong (SPRITE_VERSION);
+ spritetemp.ident = LittleLong (IDSPRITEHEADER);
+
+ SafeWrite (spriteouthandle, &spritetemp, sizeof(spritetemp));
+
+//
+// write out the frames
+//
+ curframe = 0;
+
+ for (i=0 ; i<sprite.numframes ; i++)
+ {
+ SafeWrite (spriteouthandle, &frames[curframe].type,
+ sizeof(frames[curframe].type));
+
+ if (frames[curframe].type == SPR_SINGLE)
+ {
+ //
+ // single (non-grouped) frame
+ //
+ WriteFrame (spriteouthandle, curframe);
+ curframe++;
+ }
+ else
+ {
+ int j, numframes;
+ dspritegroup_t dsgroup;
+ float totinterval;
+
+ groupframe = curframe;
+ curframe++;
+ numframes = frames[groupframe].numgroupframes;
+
+ //
+ // set and write the group header
+ //
+ dsgroup.numframes = LittleLong (numframes);
+
+ SafeWrite (spriteouthandle, &dsgroup, sizeof(dsgroup));
+
+ //
+ // write the interval array
+ //
+ totinterval = 0.0;
+
+ for (j=0 ; j<numframes ; j++)
+ {
+ dspriteinterval_t temp;
+
+ totinterval += frames[groupframe+1+j].interval;
+ temp.interval = LittleFloat (totinterval);
+
+ SafeWrite (spriteouthandle, &temp, sizeof(temp));
+ }
+
+ for (j=0 ; j<numframes ; j++)
+ {
+ WriteFrame (spriteouthandle, curframe);
+ curframe++;
+ }
+ }
+ }
+}
+
+
+/*
+============
+ExecCommand
+============
+*/
+int cmdsrun;
+
+void ExecCommand (char *cmd, ...)
+{
+ int ret;
+ char cmdline[1024];
+ va_list argptr;
+
+ cmdsrun++;
+
+ va_start (argptr, cmd);
+ vsprintf (cmdline,cmd,argptr);
+ va_end (argptr);
+
+// printf ("=============================================================\n");
+// printf ("spritegen: %s\n",cmdline);
+ fflush (stdout);
+ ret = system (cmdline);
+// printf ("=============================================================\n");
+
+ if (ret)
+ Error ("spritegen: exiting due to error");
+}
+
+/*
+==============
+LoadScreen
+==============
+*/
+void LoadScreen (char *name)
+{
+ printf ("grabbing from %s...\n",name);
+ LoadLBM (name, &byteimage, &lbmpalette);
+
+ byteimagewidth = bmhd.w;
+ byteimageheight = bmhd.h;
+}
+
+
+/*
+===============
+Cmd_Type
+===============
+*/
+void Cmd_Type (void)
+{
+ GetToken (false);
+ if (!strcmp (token, "vp_parallel_upright"))
+ sprite.type = SPR_VP_PARALLEL_UPRIGHT;
+ else if (!strcmp (token, "facing_upright"))
+ sprite.type = SPR_FACING_UPRIGHT;
+ else if (!strcmp (token, "vp_parallel"))
+ sprite.type = SPR_VP_PARALLEL;
+ else if (!strcmp (token, "oriented"))
+ sprite.type = SPR_ORIENTED;
+ else if (!strcmp (token, "vp_parallel_oriented"))
+ sprite.type = SPR_VP_PARALLEL_ORIENTED;
+ else
+ Error ("Bad sprite type\n");
+}
+
+
+/*
+===============
+Cmd_Beamlength
+===============
+*/
+void Cmd_Beamlength ()
+{
+ GetToken (false);
+ sprite.beamlength = atof (token);
+}
+
+
+/*
+===============
+Cmd_Load
+===============
+*/
+void Cmd_Load (void)
+{
+ GetToken (false);
+ LoadScreen (ExpandPathAndArchive(token));
+}
+
+
+/*
+===============
+Cmd_Frame
+===============
+*/
+void Cmd_Frame ()
+{
+ int x,y,xl,yl,xh,yh,w,h;
+ byte *screen_p, *source;
+ int linedelta;
+ dspriteframe_t *pframe;
+ int pix;
+
+ GetToken (false);
+ xl = atoi (token);
+ GetToken (false);
+ yl = atoi (token);
+ GetToken (false);
+ w = atoi (token);
+ GetToken (false);
+ h = atoi (token);
+
+ if ((xl & 0x07) || (yl & 0x07) || (w & 0x07) || (h & 0x07))
+ Error ("Sprite dimensions not multiples of 8\n");
+
+ if ((w > 255) || (h > 255))
+ Error ("Sprite has a dimension longer than 255");
+
+ xh = xl+w;
+ yh = yl+h;
+
+ pframe = (dspriteframe_t *)plump;
+ frames[framecount].pdata = pframe;
+ frames[framecount].type = SPR_SINGLE;
+
+ if (TokenAvailable ())
+ {
+ GetToken (false);
+ frames[framecount].interval = atof (token);
+ if (frames[framecount].interval <= 0.0)
+ Error ("Non-positive interval");
+ }
+ else
+ {
+ frames[framecount].interval = 0.1;
+ }
+
+ if (TokenAvailable ())
+ {
+ GetToken (false);
+ pframe->origin[0] = -atoi (token);
+ GetToken (false);
+ pframe->origin[1] = atoi (token);
+ }
+ else
+ {
+ pframe->origin[0] = -(w >> 1);
+ pframe->origin[1] = h >> 1;
+ }
+
+ pframe->width = w;
+ pframe->height = h;
+
+ if (w > framesmaxs[0])
+ framesmaxs[0] = w;
+
+ if (h > framesmaxs[1])
+ framesmaxs[1] = h;
+
+ plump = (byte *)(pframe + 1);
+
+ screen_p = byteimage + yl*byteimagewidth + xl;
+ linedelta = byteimagewidth - w;
+
+ source = plump;
+
+ for (y=yl ; y<yh ; y++)
+ {
+ for (x=xl ; x<xh ; x++)
+ {
+ pix = *screen_p;
+ *screen_p++ = 0;
+// if (pix == 255)
+// pix = 0;
+ *plump++ = pix;
+ }
+ screen_p += linedelta;
+ }
+
+ framecount++;
+ if (framecount >= MAX_FRAMES)
+ Error ("Too many frames; increase MAX_FRAMES\n");
+}
+
+
+/*
+===============
+Cmd_GroupStart
+===============
+*/
+void Cmd_GroupStart (void)
+{
+ int groupframe;
+
+ groupframe = framecount++;
+
+ frames[groupframe].type = SPR_GROUP;
+ frames[groupframe].numgroupframes = 0;
+
+ while (1)
+ {
+ GetToken (true);
+ if (endofscript)
+ Error ("End of file during group");
+
+ if (!strcmp (token, "$frame"))
+ {
+ Cmd_Frame ();
+ frames[groupframe].numgroupframes++;
+ }
+ else if (!strcmp (token, "$load"))
+ {
+ Cmd_Load ();
+ }
+ else if (!strcmp (token, "$groupend"))
+ {
+ break;
+ }
+ else
+ {
+ Error ("$frame, $load, or $groupend expected\n");
+ }
+
+ }
+
+ if (frames[groupframe].numgroupframes == 0)
+ Error ("Empty group\n");
+}
+
+
+/*
+===============
+ParseScript
+===============
+*/
+void ParseScript (void)
+{
+ while (1)
+ {
+ GetToken (true);
+ if (endofscript)
+ break;
+
+ if (!strcmp (token, "$load"))
+ {
+ Cmd_Load ();
+ }
+ if (!strcmp (token, "$spritename"))
+ {
+ Cmd_Spritename ();
+ }
+ else if (!strcmp (token, "$type"))
+ {
+ Cmd_Type ();
+ }
+ else if (!strcmp (token, "$beamlength"))
+ {
+ Cmd_Beamlength ();
+ }
+ else if (!strcmp (token, "$sync"))
+ {
+ sprite.synctype = ST_SYNC;
+ }
+ else if (!strcmp (token, "$frame"))
+ {
+ Cmd_Frame ();
+ sprite.numframes++;
+ }
+ else if (!strcmp (token, "$load"))
+ {
+ Cmd_Load ();
+ }
+ else if (!strcmp (token, "$groupstart"))
+ {
+ Cmd_GroupStart ();
+ sprite.numframes++;
+ }
+ }
+}
+
+/*
+==============
+Cmd_Spritename
+==============
+*/
+void Cmd_Spritename (void)
+{
+ if (sprite.numframes)
+ FinishSprite ();
+
+ GetToken (false);
+ sprintf (spriteoutname, "%s%s.spr", spritedir, token);
+ memset (&sprite, 0, sizeof(sprite));
+ framecount = 0;
+
+ framesmaxs[0] = -9999999;
+ framesmaxs[1] = -9999999;
+
+ lumpbuffer = malloc (MAX_BUFFER_SIZE * 2); // *2 for padding
+ if (!lumpbuffer)
+ Error ("Couldn't get buffer memory");
+
+ plump = lumpbuffer;
+ sprite.synctype = ST_RAND; // default
+}
+
+/*
+==============
+FinishSprite
+==============
+*/
+void FinishSprite (void)
+{
+ FILE *spriteouthandle;
+
+ if (sprite.numframes == 0)
+ Error ("no frames\n");
+
+ if (!strlen(spriteoutname))
+ Error ("Didn't name sprite file");
+
+ if ((plump - lumpbuffer) > MAX_BUFFER_SIZE)
+ Error ("Sprite package too big; increase MAX_BUFFER_SIZE");
+
+ spriteouthandle = SafeOpenWrite (spriteoutname);
+ printf ("saving in %s\n", spriteoutname);
+ WriteSprite (spriteouthandle);
+ fclose (spriteouthandle);
+
+ printf ("spritegen: successful\n");
+ printf ("%d frame(s)\n", sprite.numframes);
+ printf ("%d ungrouped frame(s), including group headers\n", framecount);
+
+ spriteoutname[0] = 0; // clear for a new sprite
+}
+
+/*
+==============
+main
+
+==============
+*/
+int main (int argc, char **argv)
+{
+ int i;
+
+ if (argc != 2 && argc != 4)
+ Error ("usage: spritegen [-archive directory] file.qc");
+
+ if (!strcmp(argv[1], "-archive"))
+ {
+ archive = true;
+ strcpy (archivedir, argv[2]);
+ printf ("Archiving source to: %s\n", archivedir);
+ i = 3;
+ }
+ else
+ i = 1;
+
+ SetQdirFromPath (argv[i]);
+ ExtractFilePath (argv[i], spritedir); // chop the filename
+
+//
+// load the script
+//
+ LoadScriptFile (argv[i]);
+
+ ParseScript ();
+ FinishSprite ();
+
+ return 0;
+}
+
diff --git a/qutils/SPRGEN/SPRGEN.MAK b/qutils/SPRGEN/SPRGEN.MAK
new file mode 100644
index 0000000..c7982f3
--- /dev/null
+++ b/qutils/SPRGEN/SPRGEN.MAK
@@ -0,0 +1,300 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+!IF "$(CFG)" == ""
+CFG=sprgen - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to sprgen - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "sprgen - Win32 Release" && "$(CFG)" != "sprgen - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "sprgen.mak" CFG="sprgen - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "sprgen - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "sprgen - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "sprgen - Win32 Debug"
+RSC=rc.exe
+CPP=cl.exe
+
+!IF "$(CFG)" == "sprgen - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\sprgen.exe"
+
+CLEAN :
+ -@erase ".\Release\sprgen.exe"
+ -@erase ".\Release\scriplib.obj"
+ -@erase ".\Release\sprgen.obj"
+ -@erase ".\Release\cmdlib.obj"
+ -@erase ".\Release\lbmlib.obj"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /ML /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/sprgen.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\Release/
+CPP_SBRS=
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/sprgen.bsc"
+BSC32_SBRS=
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:console /incremental:no\
+ /pdb:"$(OUTDIR)/sprgen.pdb" /machine:I386 /out:"$(OUTDIR)/sprgen.exe"
+LINK32_OBJS= \
+ ".\Release\scriplib.obj" \
+ ".\Release\sprgen.obj" \
+ ".\Release\cmdlib.obj" \
+ ".\Release\lbmlib.obj"
+
+"$(OUTDIR)\sprgen.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "sprgen - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\sprgen.exe"
+
+CLEAN :
+ -@erase ".\Debug\vc40.pdb"
+ -@erase ".\Debug\vc40.idb"
+ -@erase ".\Debug\sprgen.exe"
+ -@erase ".\Debug\cmdlib.obj"
+ -@erase ".\Debug\scriplib.obj"
+ -@erase ".\Debug\lbmlib.obj"
+ -@erase ".\Debug\sprgen.obj"
+ -@erase ".\Debug\sprgen.ilk"
+ -@erase ".\Debug\sprgen.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MLd /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/sprgen.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\Debug/
+CPP_SBRS=
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/sprgen.bsc"
+BSC32_SBRS=
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:console /incremental:yes\
+ /pdb:"$(OUTDIR)/sprgen.pdb" /debug /machine:I386 /out:"$(OUTDIR)/sprgen.exe"
+LINK32_OBJS= \
+ ".\Debug\cmdlib.obj" \
+ ".\Debug\scriplib.obj" \
+ ".\Debug\lbmlib.obj" \
+ ".\Debug\sprgen.obj"
+
+"$(OUTDIR)\sprgen.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+################################################################################
+# Begin Target
+
+# Name "sprgen - Win32 Release"
+# Name "sprgen - Win32 Debug"
+
+!IF "$(CFG)" == "sprgen - Win32 Release"
+
+!ELSEIF "$(CFG)" == "sprgen - Win32 Debug"
+
+!ENDIF
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\sprgen.c
+
+!IF "$(CFG)" == "sprgen - Win32 Release"
+
+DEP_CPP_SPRGE=\
+ ".\spritegn.h"\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\scriplib.h"\
+ ".\..\common\lbmlib.h"\
+
+
+"$(INTDIR)\sprgen.obj" : $(SOURCE) $(DEP_CPP_SPRGE) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "sprgen - Win32 Debug"
+
+DEP_CPP_SPRGE=\
+ ".\spritegn.h"\
+ ".\..\common\cmdlib.h"\
+
+
+"$(INTDIR)\sprgen.obj" : $(SOURCE) $(DEP_CPP_SPRGE) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\cmdlib.c
+DEP_CPP_CMDLI=\
+ ".\..\common\cmdlib.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+ {$(INCLUDE)}"\sys\STAT.H"\
+
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\scriplib.c
+DEP_CPP_SCRIP=\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\scriplib.h"\
+
+
+"$(INTDIR)\scriplib.obj" : $(SOURCE) $(DEP_CPP_SCRIP) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\lbmlib.c
+DEP_CPP_LBMLI=\
+ ".\..\common\cmdlib.h"\
+ ".\..\common\lbmlib.h"\
+
+
+"$(INTDIR)\lbmlib.obj" : $(SOURCE) $(DEP_CPP_LBMLI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\scriplib.h
+
+!IF "$(CFG)" == "sprgen - Win32 Release"
+
+!ELSEIF "$(CFG)" == "sprgen - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\lbmlib.h
+
+!IF "$(CFG)" == "sprgen - Win32 Release"
+
+!ELSEIF "$(CFG)" == "sprgen - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\cmdlib.h
+
+!IF "$(CFG)" == "sprgen - Win32 Release"
+
+!ELSEIF "$(CFG)" == "sprgen - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+# End Target
+# End Project
+################################################################################
diff --git a/qutils/SPRGEN/SPRGEN.MDP b/qutils/SPRGEN/SPRGEN.MDP
new file mode 100644
index 0000000..900e8ac
--- /dev/null
+++ b/qutils/SPRGEN/SPRGEN.MDP
Binary files differ
diff --git a/qutils/SPRGEN/SPRGEN.NCB b/qutils/SPRGEN/SPRGEN.NCB
new file mode 100644
index 0000000..cf2cd4f
--- /dev/null
+++ b/qutils/SPRGEN/SPRGEN.NCB
Binary files differ
diff --git a/qutils/SPRGEN/SPRITEGN.H b/qutils/SPRGEN/SPRITEGN.H
new file mode 100644
index 0000000..6427d60
--- /dev/null
+++ b/qutils/SPRGEN/SPRITEGN.H
@@ -0,0 +1,88 @@
+//
+// spritegn.h: header file for sprite generation program
+//
+
+// **********************************************************
+// * This file must be identical in the spritegen directory *
+// * and in the Quake directory, because it's used to *
+// * pass data from one to the other via .spr files. *
+// **********************************************************
+
+//-------------------------------------------------------
+// This program generates .spr sprite package files.
+// The format of the files is as follows:
+//
+// dsprite_t file header structure
+// <repeat dsprite_t.numframes times>
+// <if spritegroup, repeat dspritegroup_t.numframes times>
+// dspriteframe_t frame header structure
+// sprite bitmap
+// <else (single sprite frame)>
+// dspriteframe_t frame header structure
+// sprite bitmap
+// <endrepeat>
+//-------------------------------------------------------
+
+#ifdef INCLUDELIBS
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#include "cmdlib.h"
+#include "scriplib.h"
+#include "lbmlib.h"
+
+#endif
+
+#define SPRITE_VERSION 1
+
+// must match definition in modelgen.h
+#ifndef SYNCTYPE_T
+#define SYNCTYPE_T
+typedef enum {ST_SYNC=0, ST_RAND } synctype_t;
+#endif
+
+// TODO: shorten these?
+typedef struct {
+ int ident;
+ int version;
+ int type;
+ float boundingradius;
+ int width;
+ int height;
+ int numframes;
+ float beamlength;
+ synctype_t synctype;
+} dsprite_t;
+
+#define SPR_VP_PARALLEL_UPRIGHT 0
+#define SPR_FACING_UPRIGHT 1
+#define SPR_VP_PARALLEL 2
+#define SPR_ORIENTED 3
+#define SPR_VP_PARALLEL_ORIENTED 4
+
+typedef struct {
+ int origin[2];
+ int width;
+ int height;
+} dspriteframe_t;
+
+typedef struct {
+ int numframes;
+} dspritegroup_t;
+
+typedef struct {
+ float interval;
+} dspriteinterval_t;
+
+typedef enum { SPR_SINGLE=0, SPR_GROUP } spriteframetype_t;
+
+typedef struct {
+ spriteframetype_t type;
+} dspriteframetype_t;
+
+#define IDSPRITEHEADER (('P'<<24)+('S'<<16)+('D'<<8)+'I')
+ // little-endian "IDSP"
+
diff --git a/qutils/SPRGEN/S_BUBBLE.SPR b/qutils/SPRGEN/S_BUBBLE.SPR
new file mode 100644
index 0000000..b9b0e8b
--- /dev/null
+++ b/qutils/SPRGEN/S_BUBBLE.SPR
Binary files differ
diff --git a/qutils/SPRGEN/S_EXPLOD.SPR b/qutils/SPRGEN/S_EXPLOD.SPR
new file mode 100644
index 0000000..c69a18d
--- /dev/null
+++ b/qutils/SPRGEN/S_EXPLOD.SPR
Binary files differ
diff --git a/qutils/SPRGEN/S_LIGHT.SPR b/qutils/SPRGEN/S_LIGHT.SPR
new file mode 100644
index 0000000..90b4820
--- /dev/null
+++ b/qutils/SPRGEN/S_LIGHT.SPR
Binary files differ
diff --git a/qutils/TEXMAKE/TEXMAKE.C b/qutils/TEXMAKE/TEXMAKE.C
new file mode 100644
index 0000000..4a10cf9
--- /dev/null
+++ b/qutils/TEXMAKE/TEXMAKE.C
@@ -0,0 +1,222 @@
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "lbmlib.h"
+#include "trilib.h"
+
+
+triangle_t *faces;
+int numfaces;
+
+byte pic[64000];
+byte *palette;
+
+int width, height;
+int iwidth, iheight;
+
+float scale;
+
+char texname[20];
+
+
+/*
+================
+BoundFaces
+================
+*/
+vec3_t mins, maxs;
+
+void BoundFaces (void)
+{
+ int i,j,k;
+ triangle_t *pol;
+ float v;
+
+ for (i=0 ; i<numfaces ; i++)
+ {
+ pol = &faces[i];
+ for (j=0 ; j<3 ; j++)
+ for (k=0 ; k<3 ; k++)
+ {
+ v = pol->verts[j][k];
+ if (v<mins[k])
+ mins[k] = v;
+ if (v>maxs[k])
+ maxs[k] = v;
+ }
+ }
+
+ for (i=0 ; i<3 ; i++)
+ {
+ mins[i] = floor(mins[i]);
+ maxs[i] = ceil(maxs[i]);
+ }
+
+ width = maxs[0] - mins[0];
+ height = maxs[2] - mins[2];
+
+ printf ("width: %i height: %i\n",width, height);
+
+ scale = 8;
+ if (width*scale >= 150)
+ scale = 150.0 / width;
+ if (height*scale >= 190)
+ scale = 190.0 / height;
+ iwidth = ceil(width*scale) + 4;
+ iheight = ceil(height*scale) + 4;
+
+ printf ("scale: %f\n",scale);
+ printf ("iwidth: %i iheight: %i\n",iwidth, iheight);
+}
+
+
+/*
+============
+DrawLine
+
+Draw a fat line
+============
+*/
+void DrawLine (int x1, int y1, int x2, int y2)
+{
+ int dx, dy;
+ int adx, ady;
+ int count;
+ float xfrac, yfrac, xstep, ystep;
+ unsigned sx, sy;
+ float u, v;
+
+ dx = x2 - x1;
+ dy = y2 - y1;
+ adx = abs(dx);
+ ady = abs(dy);
+
+ count = adx > ady ? adx : ady;
+ count ++;
+
+ if (count > 300)
+ return; // don't ever hang up on bad data
+
+ xfrac = x1;
+ yfrac = y1;
+
+ xstep = (float)dx / count;
+ ystep = (float)dy / count;
+
+ do
+ {
+ for (u=-0.1 ; u<=0.9 ; u+=0.999)
+ for (v=-0.1 ; v<=0.9 ; v+=0.999)
+ {
+ sx = xfrac+u;
+ sy = yfrac+v;
+ if (sx < 320 && sy < 200)
+ pic[sy*320+sx] = 255;
+ }
+
+ xfrac += xstep;
+ yfrac += ystep;
+ count--;
+ } while (count > 0);
+}
+
+
+/*
+============
+AddFace
+============
+*/
+void AddFace (triangle_t *f)
+{
+ vec3_t v1, v2, normal;
+ int basex, basey;
+ int i, j;
+ int coords[3][2];
+
+//
+// determine which side to map the teture to
+//
+ VectorSubtract (f->verts[0], f->verts[1], v1);
+ VectorSubtract (f->verts[2], f->verts[1], v2);
+ CrossProduct (v1, v2, normal);
+
+ if (normal[1] > 0)
+ basex = iwidth + 2;
+ else
+ basex = 2;
+ basey = 2;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ coords[i][0] = Q_rint((f->verts[i][0] - mins[0])*scale + basex);
+ coords[i][1] = Q_rint( (maxs[2] - f->verts[i][2])*scale + basey);
+ }
+
+//
+// draw lines
+//
+ for (i=0 ; i<3 ; i++)
+ {
+ j = (i+1)%3;
+ DrawLine (coords[i][0], coords[i][1],
+ coords[j][0], coords[j][1]);
+ }
+}
+
+
+/*
+============
+CalcPalette
+============
+*/
+void CalcPalette (void)
+{
+ byte *picture;
+ LoadLBM (ExpandPath("id1/gfx/gamepal.lbm"), &picture, &palette);
+}
+
+
+
+/*
+============
+main
+============
+*/
+void main (int argc, char **argv)
+{
+ int i;
+ char filename[1024];
+
+ if (argc == 1)
+ Error ("texmake polfile[.idpol]\nGenerates polfile.lbm and polfile_t.idpol\n");
+
+//
+// read the polfile
+//
+ strcpy (filename, argv[1]);
+ DefaultExtension (filename, ".tri");
+ SetQdirFromPath (filename);
+ LoadTriangleList (filename, &faces, &numfaces);
+ printf ("numfaces: %i\n",numfaces);
+
+//
+// generate the texture coordinates
+//
+ BoundFaces ();
+
+//
+// generate the lbm
+//
+ for (i=0 ; i<numfaces ; i++)
+ AddFace (&faces[i]);
+
+//
+// save the lbm
+//
+ strcpy (filename, argv[1]);
+ StripExtension (filename);
+ strcat (filename, ".lbm");
+
+ printf ("output file: %s\n",filename);
+ CalcPalette ();
+ WriteLBMfile (filename, pic, 320, 200, palette);
+}
diff --git a/qutils/TEXMAKE/TEXMAKE.MAK b/qutils/TEXMAKE/TEXMAKE.MAK
new file mode 100644
index 0000000..00ec345
--- /dev/null
+++ b/qutils/TEXMAKE/TEXMAKE.MAK
@@ -0,0 +1,269 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+!IF "$(CFG)" == ""
+CFG=texmake - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to texmake - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "texmake - Win32 Release" && "$(CFG)" !=\
+ "texmake - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "texmake.mak" CFG="texmake - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "texmake - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "texmake - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "texmake - Win32 Debug"
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "texmake - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\texmake.exe"
+
+CLEAN :
+ -@erase ".\Release\texmake.exe"
+ -@erase ".\Release\mathlib.obj"
+ -@erase ".\Release\cmdlib.obj"
+ -@erase ".\Release\lbmlib.obj"
+ -@erase ".\Release\trilib.obj"
+ -@erase ".\Release\texmake.obj"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /ML /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/texmake.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\Release/
+CPP_SBRS=
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/texmake.bsc"
+BSC32_SBRS=
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:console /incremental:no\
+ /pdb:"$(OUTDIR)/texmake.pdb" /machine:I386 /out:"$(OUTDIR)/texmake.exe"
+LINK32_OBJS= \
+ ".\Release\mathlib.obj" \
+ ".\Release\cmdlib.obj" \
+ ".\Release\lbmlib.obj" \
+ ".\Release\trilib.obj" \
+ ".\Release\texmake.obj"
+
+"$(OUTDIR)\texmake.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "texmake - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\texmake.exe"
+
+CLEAN :
+ -@erase ".\Debug\vc40.pdb"
+ -@erase ".\Debug\vc40.idb"
+ -@erase ".\Debug\texmake.exe"
+ -@erase ".\Debug\cmdlib.obj"
+ -@erase ".\Debug\lbmlib.obj"
+ -@erase ".\Debug\trilib.obj"
+ -@erase ".\Debug\mathlib.obj"
+ -@erase ".\Debug\texmake.obj"
+ -@erase ".\Debug\texmake.ilk"
+ -@erase ".\Debug\texmake.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MLd /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/texmake.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\Debug/
+CPP_SBRS=
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/texmake.bsc"
+BSC32_SBRS=
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:console /incremental:yes\
+ /pdb:"$(OUTDIR)/texmake.pdb" /debug /machine:I386 /out:"$(OUTDIR)/texmake.exe"
+LINK32_OBJS= \
+ ".\Debug\cmdlib.obj" \
+ ".\Debug\lbmlib.obj" \
+ ".\Debug\trilib.obj" \
+ ".\Debug\mathlib.obj" \
+ ".\Debug\texmake.obj"
+
+"$(OUTDIR)\texmake.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CP