aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorzakk <zakk@edf5b092-35ff-0310-97b2-ce42778d08ea>2005-08-26 04:48:05 +0000
committerzakk <zakk@edf5b092-35ff-0310-97b2-ce42778d08ea>2005-08-26 04:48:05 +0000
commit952c5c128f9efaea89d41d882c4ea3ade7df4591 (patch)
tree91b84d9be7afad7e99ac64a640a65b6cb5081900 /common
parentc2c2e0d25d6cdb7d42d7dc981a863f65f94f281d (diff)
downloadioquake3-aero-952c5c128f9efaea89d41d882c4ea3ade7df4591.tar.gz
ioquake3-aero-952c5c128f9efaea89d41d882c4ea3ade7df4591.zip
Itsa me, quake3io!
git-svn-id: svn://svn.icculus.org/quake3/trunk@2 edf5b092-35ff-0310-97b2-ce42778d08ea
Diffstat (limited to 'common')
-rwxr-xr-xcommon/aselib.c933
-rwxr-xr-xcommon/aselib.h31
-rwxr-xr-xcommon/bspfile.c564
-rwxr-xr-xcommon/bspfile.h118
-rwxr-xr-xcommon/cmdlib.c1201
-rwxr-xr-xcommon/cmdlib.h160
-rwxr-xr-xcommon/imagelib.c1164
-rwxr-xr-xcommon/imagelib.h44
-rwxr-xr-xcommon/l3dslib.c300
-rwxr-xr-xcommon/l3dslib.h26
-rwxr-xr-xcommon/mathlib.c434
-rwxr-xr-xcommon/mathlib.h96
-rwxr-xr-xcommon/md4.c277
-rwxr-xr-xcommon/mutex.c197
-rwxr-xr-xcommon/mutex.h28
-rwxr-xr-xcommon/polylib.c740
-rwxr-xr-xcommon/polylib.h57
-rwxr-xr-xcommon/polyset.h51
-rwxr-xr-xcommon/qfiles.h489
-rwxr-xr-xcommon/scriplib.c375
-rwxr-xr-xcommon/scriplib.h55
-rwxr-xr-xcommon/surfaceflags.h72
-rwxr-xr-xcommon/threads.c441
-rwxr-xr-xcommon/threads.h31
-rwxr-xr-xcommon/trilib.c231
-rwxr-xr-xcommon/trilib.h26
26 files changed, 8141 insertions, 0 deletions
diff --git a/common/aselib.c b/common/aselib.c
new file mode 100755
index 0000000..5706c9e
--- /dev/null
+++ b/common/aselib.c
@@ -0,0 +1,933 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "aselib.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define MAX_ASE_MATERIALS 32
+#define MAX_ASE_OBJECTS 64
+#define MAX_ASE_ANIMATIONS 32
+#define MAX_ASE_ANIMATION_FRAMES 512
+
+#define VERBOSE( x ) { if ( ase.verbose ) { printf x ; } }
+
+typedef struct
+{
+ float x, y, z;
+ float nx, ny, nz;
+ float s, t;
+} aseVertex_t;
+
+typedef struct
+{
+ float s, t;
+} aseTVertex_t;
+
+typedef int aseFace_t[3];
+
+typedef struct
+{
+ int numFaces;
+ int numVertexes;
+ int numTVertexes;
+
+ int timeValue;
+
+ aseVertex_t *vertexes;
+ aseTVertex_t *tvertexes;
+ aseFace_t *faces, *tfaces;
+
+ int currentFace, currentVertex;
+} aseMesh_t;
+
+typedef struct
+{
+ int numFrames;
+ aseMesh_t frames[MAX_ASE_ANIMATION_FRAMES];
+
+ int currentFrame;
+} aseMeshAnimation_t;
+
+typedef struct
+{
+ char name[128];
+} aseMaterial_t;
+
+/*
+** contains the animate sequence of a single surface
+** using a single material
+*/
+typedef struct
+{
+ char name[128];
+
+ int materialRef;
+ int numAnimations;
+
+ aseMeshAnimation_t anim;
+
+} aseGeomObject_t;
+
+typedef struct
+{
+ int numMaterials;
+ aseMaterial_t materials[MAX_ASE_MATERIALS];
+ aseGeomObject_t objects[MAX_ASE_OBJECTS];
+
+ char *buffer;
+ char *curpos;
+ int len;
+
+ int currentObject;
+ qboolean verbose;
+ qboolean grabAnims;
+
+} ase_t;
+
+static char s_token[1024];
+static ase_t ase;
+
+static void ASE_Process( void );
+static void ASE_FreeGeomObject( int ndx );
+
+/*
+** ASE_Load
+*/
+void ASE_Load( const char *filename, qboolean verbose, qboolean grabAnims )
+{
+ FILE *fp = fopen( filename, "rb" );
+
+ if ( !fp )
+ Error( "File not found '%s'", filename );
+
+ memset( &ase, 0, sizeof( ase ) );
+
+ ase.verbose = verbose;
+ ase.grabAnims = grabAnims;
+ ase.len = Q_filelength( fp );
+
+ ase.curpos = ase.buffer = malloc( ase.len );
+
+ printf( "Processing '%s'\n", filename );
+
+ if ( fread( ase.buffer, ase.len, 1, fp ) != 1 )
+ {
+ fclose( fp );
+ Error( "fread() != -1 for '%s'", filename );
+ }
+
+ fclose( fp );
+
+ ASE_Process();
+}
+
+/*
+** ASE_Free
+*/
+void ASE_Free( void )
+{
+ int i;
+
+ for ( i = 0; i < ase.currentObject; i++ )
+ {
+ ASE_FreeGeomObject( i );
+ }
+}
+
+/*
+** ASE_GetNumSurfaces
+*/
+int ASE_GetNumSurfaces( void )
+{
+ return ase.currentObject;
+}
+
+/*
+** ASE_GetSurfaceName
+*/
+const char *ASE_GetSurfaceName( int which )
+{
+ aseGeomObject_t *pObject = &ase.objects[which];
+
+ if ( !pObject->anim.numFrames )
+ return 0;
+
+ return pObject->name;
+}
+
+/*
+** ASE_GetSurfaceAnimation
+**
+** Returns an animation (sequence of polysets)
+*/
+polyset_t *ASE_GetSurfaceAnimation( int which, int *pNumFrames, int skipFrameStart, int skipFrameEnd, int maxFrames )
+{
+ aseGeomObject_t *pObject = &ase.objects[which];
+ polyset_t *psets;
+ int numFramesInAnimation;
+ int numFramesToKeep;
+ int i, f;
+
+ if ( !pObject->anim.numFrames )
+ return 0;
+
+ if ( pObject->anim.numFrames > maxFrames && maxFrames != -1 )
+ {
+ numFramesInAnimation = maxFrames;
+ }
+ else
+ {
+ numFramesInAnimation = pObject->anim.numFrames;
+ if ( maxFrames != -1 )
+ printf( "WARNING: ASE_GetSurfaceAnimation maxFrames > numFramesInAnimation\n" );
+ }
+
+ if ( skipFrameEnd != -1 )
+ numFramesToKeep = numFramesInAnimation - ( skipFrameEnd - skipFrameStart + 1 );
+ else
+ numFramesToKeep = numFramesInAnimation;
+
+ *pNumFrames = numFramesToKeep;
+
+ psets = calloc( sizeof( polyset_t ) * numFramesToKeep, 1 );
+
+ for ( f = 0, i = 0; i < numFramesInAnimation; i++ )
+ {
+ int t;
+ aseMesh_t *pMesh = &pObject->anim.frames[i];
+
+ if ( skipFrameStart != -1 )
+ {
+ if ( i >= skipFrameStart && i <= skipFrameEnd )
+ continue;
+ }
+
+ strcpy( psets[f].name, pObject->name );
+ strcpy( psets[f].materialname, ase.materials[pObject->materialRef].name );
+
+ psets[f].triangles = calloc( sizeof( triangle_t ) * pObject->anim.frames[i].numFaces, 1 );
+ psets[f].numtriangles = pObject->anim.frames[i].numFaces;
+
+ for ( t = 0; t < pObject->anim.frames[i].numFaces; t++ )
+ {
+ int k;
+
+ for ( k = 0; k < 3; k++ )
+ {
+ psets[f].triangles[t].verts[k][0] = pMesh->vertexes[pMesh->faces[t][k]].x;
+ psets[f].triangles[t].verts[k][1] = pMesh->vertexes[pMesh->faces[t][k]].y;
+ psets[f].triangles[t].verts[k][2] = pMesh->vertexes[pMesh->faces[t][k]].z;
+
+ if ( pMesh->tvertexes && pMesh->tfaces )
+ {
+ psets[f].triangles[t].texcoords[k][0] = pMesh->tvertexes[pMesh->tfaces[t][k]].s;
+ psets[f].triangles[t].texcoords[k][1] = pMesh->tvertexes[pMesh->tfaces[t][k]].t;
+ }
+
+ }
+ }
+
+ f++;
+ }
+
+ return psets;
+}
+
+static void ASE_FreeGeomObject( int ndx )
+{
+ aseGeomObject_t *pObject;
+ int i;
+
+ pObject = &ase.objects[ndx];
+
+ for ( i = 0; i < pObject->anim.numFrames; i++ )
+ {
+ if ( pObject->anim.frames[i].vertexes )
+ {
+ free( pObject->anim.frames[i].vertexes );
+ }
+ if ( pObject->anim.frames[i].tvertexes )
+ {
+ free( pObject->anim.frames[i].tvertexes );
+ }
+ if ( pObject->anim.frames[i].faces )
+ {
+ free( pObject->anim.frames[i].faces );
+ }
+ if ( pObject->anim.frames[i].tfaces )
+ {
+ free( pObject->anim.frames[i].tfaces );
+ }
+ }
+
+ memset( pObject, 0, sizeof( *pObject ) );
+}
+
+static aseMesh_t *ASE_GetCurrentMesh( void )
+{
+ aseGeomObject_t *pObject;
+
+ if ( ase.currentObject >= MAX_ASE_OBJECTS )
+ {
+ Error( "Too many GEOMOBJECTs" );
+ return 0; // never called
+ }
+
+ pObject = &ase.objects[ase.currentObject];
+
+ if ( pObject->anim.currentFrame >= MAX_ASE_ANIMATION_FRAMES )
+ {
+ Error( "Too many MESHes" );
+ return 0;
+ }
+
+ return &pObject->anim.frames[pObject->anim.currentFrame];
+}
+
+static int CharIsTokenDelimiter( int ch )
+{
+ if ( ch <= 32 )
+ return 1;
+ return 0;
+}
+
+static int ASE_GetToken( qboolean restOfLine )
+{
+ int i = 0;
+
+ if ( ase.buffer == 0 )
+ return 0;
+
+ if ( ( ase.curpos - ase.buffer ) == ase.len )
+ return 0;
+
+ // skip over crap
+ while ( ( ( ase.curpos - ase.buffer ) < ase.len ) &&
+ ( *ase.curpos <= 32 ) )
+ {
+ ase.curpos++;
+ }
+
+ while ( ( ase.curpos - ase.buffer ) < ase.len )
+ {
+ s_token[i] = *ase.curpos;
+
+ ase.curpos++;
+ i++;
+
+ if ( ( CharIsTokenDelimiter( s_token[i-1] ) && !restOfLine ) ||
+ ( ( s_token[i-1] == '\n' ) || ( s_token[i-1] == '\r' ) ) )
+ {
+ s_token[i-1] = 0;
+ break;
+ }
+ }
+
+ s_token[i] = 0;
+
+ return 1;
+}
+
+static void ASE_ParseBracedBlock( void (*parser)( const char *token ) )
+{
+ int indent = 0;
+
+ while ( ASE_GetToken( qfalse ) )
+ {
+ if ( !strcmp( s_token, "{" ) )
+ {
+ indent++;
+ }
+ else if ( !strcmp( s_token, "}" ) )
+ {
+ --indent;
+ if ( indent == 0 )
+ break;
+ else if ( indent < 0 )
+ Error( "Unexpected '}'" );
+ }
+ else
+ {
+ if ( parser )
+ parser( s_token );
+ }
+ }
+}
+
+static void ASE_SkipEnclosingBraces( void )
+{
+ int indent = 0;
+
+ while ( ASE_GetToken( qfalse ) )
+ {
+ if ( !strcmp( s_token, "{" ) )
+ {
+ indent++;
+ }
+ else if ( !strcmp( s_token, "}" ) )
+ {
+ indent--;
+ if ( indent == 0 )
+ break;
+ else if ( indent < 0 )
+ Error( "Unexpected '}'" );
+ }
+ }
+}
+
+static void ASE_SkipRestOfLine( void )
+{
+ ASE_GetToken( qtrue );
+}
+
+static void ASE_KeyMAP_DIFFUSE( const char *token )
+{
+ char buffer[1024], buff1[1024], buff2[1024];
+ char *buf1, *buf2;
+ int i = 0, count;
+
+ if ( !strcmp( token, "*BITMAP" ) )
+ {
+ ASE_GetToken( qfalse );
+
+ strcpy( buffer, s_token + 1 );
+ if ( strchr( buffer, '"' ) )
+ *strchr( buffer, '"' ) = 0;
+
+ while ( buffer[i] )
+ {
+ if ( buffer[i] == '\\' )
+ buffer[i] = '/';
+ i++;
+ }
+
+ buf1 = buffer;
+ buf2 = gamedir;
+ // need to compare win32 volumes to potential unix junk
+ //
+ if ( (gamedir[1] == ':' && (buffer[0] == '/' && buffer[1] == '/')) ||
+ (buffer[1] == ':' && (gamedir[0] == '/' && gamedir[1] == '/')) ) {
+ if (buffer[1] == ':') {
+ buf1 = buffer + 2;
+ buf2 = gamedir + 2;
+ } else {
+ buf1 = gamedir + 2;
+ buf2 = buffer +2;
+ }
+ count = 0;
+ while (*buf2 && count < 2) {
+ if (*buf2 == '/') {
+ count++;
+ }
+ buf2++;
+ }
+ }
+ strcpy(buff1, buf1);
+ strlwr(buff1);
+ strcpy(buff2, buf2);
+ strlwr(buff2);
+ if ( strstr( buff2, buff1 + 2 ) )
+ {
+ strcpy( ase.materials[ase.numMaterials].name, strstr( buff2, buff1 + 2 ) + strlen( buff1 ) - 2 );
+ }
+ else
+ {
+ sprintf( ase.materials[ase.numMaterials].name, "(not converted: '%s')", buffer );
+ printf( "WARNING: illegal material name '%s'\n", buffer );
+ }
+ }
+ else
+ {
+ }
+}
+
+static void ASE_KeyMATERIAL( const char *token )
+{
+ if ( !strcmp( token, "*MAP_DIFFUSE" ) )
+ {
+ ASE_ParseBracedBlock( ASE_KeyMAP_DIFFUSE );
+ }
+ else
+ {
+ }
+}
+
+static void ASE_KeyMATERIAL_LIST( const char *token )
+{
+ if ( !strcmp( token, "*MATERIAL_COUNT" ) )
+ {
+ ASE_GetToken( qfalse );
+ VERBOSE( ( "..num materials: %s\n", s_token ) );
+ if ( atoi( s_token ) > MAX_ASE_MATERIALS )
+ {
+ Error( "Too many materials!" );
+ }
+ ase.numMaterials = 0;
+ }
+ else if ( !strcmp( token, "*MATERIAL" ) )
+ {
+ VERBOSE( ( "..material %d ", ase.numMaterials ) );
+ ASE_ParseBracedBlock( ASE_KeyMATERIAL );
+ ase.numMaterials++;
+ }
+}
+
+static void ASE_KeyMESH_VERTEX_LIST( const char *token )
+{
+ aseMesh_t *pMesh = ASE_GetCurrentMesh();
+
+ if ( !strcmp( token, "*MESH_VERTEX" ) )
+ {
+ ASE_GetToken( qfalse ); // skip number
+
+ ASE_GetToken( qfalse );
+ pMesh->vertexes[pMesh->currentVertex].y = atof( s_token );
+
+ ASE_GetToken( qfalse );
+ pMesh->vertexes[pMesh->currentVertex].x = -atof( s_token );
+
+ ASE_GetToken( qfalse );
+ pMesh->vertexes[pMesh->currentVertex].z = atof( s_token );
+
+ pMesh->currentVertex++;
+
+ if ( pMesh->currentVertex > pMesh->numVertexes )
+ {
+ Error( "pMesh->currentVertex >= pMesh->numVertexes" );
+ }
+ }
+ else
+ {
+ Error( "Unknown token '%s' while parsing MESH_VERTEX_LIST", token );
+ }
+}
+
+static void ASE_KeyMESH_FACE_LIST( const char *token )
+{
+ aseMesh_t *pMesh = ASE_GetCurrentMesh();
+
+ if ( !strcmp( token, "*MESH_FACE" ) )
+ {
+ ASE_GetToken( qfalse ); // skip face number
+
+ ASE_GetToken( qfalse ); // skip label
+ ASE_GetToken( qfalse ); // first vertex
+ pMesh->faces[pMesh->currentFace][0] = atoi( s_token );
+
+ ASE_GetToken( qfalse ); // skip label
+ ASE_GetToken( qfalse ); // second vertex
+ pMesh->faces[pMesh->currentFace][2] = atoi( s_token );
+
+ ASE_GetToken( qfalse ); // skip label
+ ASE_GetToken( qfalse ); // third vertex
+ pMesh->faces[pMesh->currentFace][1] = atoi( s_token );
+
+ ASE_GetToken( qtrue );
+
+/*
+ if ( ( p = strstr( s_token, "*MESH_MTLID" ) ) != 0 )
+ {
+ p += strlen( "*MESH_MTLID" ) + 1;
+ mtlID = atoi( p );
+ }
+ else
+ {
+ Error( "No *MESH_MTLID found for face!" );
+ }
+*/
+
+ pMesh->currentFace++;
+ }
+ else
+ {
+ Error( "Unknown token '%s' while parsing MESH_FACE_LIST", token );
+ }
+}
+
+static void ASE_KeyTFACE_LIST( const char *token )
+{
+ aseMesh_t *pMesh = ASE_GetCurrentMesh();
+
+ if ( !strcmp( token, "*MESH_TFACE" ) )
+ {
+ int a, b, c;
+
+ ASE_GetToken( qfalse );
+
+ ASE_GetToken( qfalse );
+ a = atoi( s_token );
+ ASE_GetToken( qfalse );
+ c = atoi( s_token );
+ ASE_GetToken( qfalse );
+ b = atoi( s_token );
+
+ pMesh->tfaces[pMesh->currentFace][0] = a;
+ pMesh->tfaces[pMesh->currentFace][1] = b;
+ pMesh->tfaces[pMesh->currentFace][2] = c;
+
+ pMesh->currentFace++;
+ }
+ else
+ {
+ Error( "Unknown token '%s' in MESH_TFACE", token );
+ }
+}
+
+static void ASE_KeyMESH_TVERTLIST( const char *token )
+{
+ aseMesh_t *pMesh = ASE_GetCurrentMesh();
+
+ if ( !strcmp( token, "*MESH_TVERT" ) )
+ {
+ char u[80], v[80], w[80];
+
+ ASE_GetToken( qfalse );
+
+ ASE_GetToken( qfalse );
+ strcpy( u, s_token );
+
+ ASE_GetToken( qfalse );
+ strcpy( v, s_token );
+
+ ASE_GetToken( qfalse );
+ strcpy( w, s_token );
+
+ pMesh->tvertexes[pMesh->currentVertex].s = atof( u );
+ pMesh->tvertexes[pMesh->currentVertex].t = 1.0f - atof( v );
+
+ pMesh->currentVertex++;
+
+ if ( pMesh->currentVertex > pMesh->numTVertexes )
+ {
+ Error( "pMesh->currentVertex > pMesh->numTVertexes" );
+ }
+ }
+ else
+ {
+ Error( "Unknown token '%s' while parsing MESH_TVERTLIST" );
+ }
+}
+
+static void ASE_KeyMESH( const char *token )
+{
+ aseMesh_t *pMesh = ASE_GetCurrentMesh();
+
+ if ( !strcmp( token, "*TIMEVALUE" ) )
+ {
+ ASE_GetToken( qfalse );
+
+ pMesh->timeValue = atoi( s_token );
+ VERBOSE( ( ".....timevalue: %d\n", pMesh->timeValue ) );
+ }
+ else if ( !strcmp( token, "*MESH_NUMVERTEX" ) )
+ {
+ ASE_GetToken( qfalse );
+
+ pMesh->numVertexes = atoi( s_token );
+ VERBOSE( ( ".....TIMEVALUE: %d\n", pMesh->timeValue ) );
+ VERBOSE( ( ".....num vertexes: %d\n", pMesh->numVertexes ) );
+ }
+ else if ( !strcmp( token, "*MESH_NUMFACES" ) )
+ {
+ ASE_GetToken( qfalse );
+
+ pMesh->numFaces = atoi( s_token );
+ VERBOSE( ( ".....num faces: %d\n", pMesh->numFaces ) );
+ }
+ else if ( !strcmp( token, "*MESH_NUMTVFACES" ) )
+ {
+ ASE_GetToken( qfalse );
+
+ if ( atoi( s_token ) != pMesh->numFaces )
+ {
+ Error( "MESH_NUMTVFACES != MESH_NUMFACES" );
+ }
+ }
+ else if ( !strcmp( token, "*MESH_NUMTVERTEX" ) )
+ {
+ ASE_GetToken( qfalse );
+
+ pMesh->numTVertexes = atoi( s_token );
+ VERBOSE( ( ".....num tvertexes: %d\n", pMesh->numTVertexes ) );
+ }
+ else if ( !strcmp( token, "*MESH_VERTEX_LIST" ) )
+ {
+ pMesh->vertexes = calloc( sizeof( aseVertex_t ) * pMesh->numVertexes, 1 );
+ pMesh->currentVertex = 0;
+ VERBOSE( ( ".....parsing MESH_VERTEX_LIST\n" ) );
+ ASE_ParseBracedBlock( ASE_KeyMESH_VERTEX_LIST );
+ }
+ else if ( !strcmp( token, "*MESH_TVERTLIST" ) )
+ {
+ pMesh->currentVertex = 0;
+ pMesh->tvertexes = calloc( sizeof( aseTVertex_t ) * pMesh->numTVertexes, 1 );
+ VERBOSE( ( ".....parsing MESH_TVERTLIST\n" ) );
+ ASE_ParseBracedBlock( ASE_KeyMESH_TVERTLIST );
+ }
+ else if ( !strcmp( token, "*MESH_FACE_LIST" ) )
+ {
+ pMesh->faces = calloc( sizeof( aseFace_t ) * pMesh->numFaces, 1 );
+ pMesh->currentFace = 0;
+ VERBOSE( ( ".....parsing MESH_FACE_LIST\n" ) );
+ ASE_ParseBracedBlock( ASE_KeyMESH_FACE_LIST );
+ }
+ else if ( !strcmp( token, "*MESH_TFACELIST" ) )
+ {
+ pMesh->tfaces = calloc( sizeof( aseFace_t ) * pMesh->numFaces, 1 );
+ pMesh->currentFace = 0;
+ VERBOSE( ( ".....parsing MESH_TFACE_LIST\n" ) );
+ ASE_ParseBracedBlock( ASE_KeyTFACE_LIST );
+ }
+ else if ( !strcmp( token, "*MESH_NORMALS" ) )
+ {
+ ASE_ParseBracedBlock( 0 );
+ }
+}
+
+static void ASE_KeyMESH_ANIMATION( const char *token )
+{
+ aseMesh_t *pMesh = ASE_GetCurrentMesh();
+
+ // loads a single animation frame
+ if ( !strcmp( token, "*MESH" ) )
+ {
+ VERBOSE( ( "...found MESH\n" ) );
+ assert( pMesh->faces == 0 );
+ assert( pMesh->vertexes == 0 );
+ assert( pMesh->tvertexes == 0 );
+ memset( pMesh, 0, sizeof( *pMesh ) );
+
+ ASE_ParseBracedBlock( ASE_KeyMESH );
+
+ if ( ++ase.objects[ase.currentObject].anim.currentFrame == MAX_ASE_ANIMATION_FRAMES )
+ {
+ Error( "Too many animation frames" );
+ }
+ }
+ else
+ {
+ Error( "Unknown token '%s' while parsing MESH_ANIMATION", token );
+ }
+}
+
+static void ASE_KeyGEOMOBJECT( const char *token )
+{
+ if ( !strcmp( token, "*NODE_NAME" ) )
+ {
+ char *name = ase.objects[ase.currentObject].name;
+
+ ASE_GetToken( qtrue );
+ VERBOSE( ( " %s\n", s_token ) );
+ strcpy( ase.objects[ase.currentObject].name, s_token + 1 );
+ if ( strchr( ase.objects[ase.currentObject].name, '"' ) )
+ *strchr( ase.objects[ase.currentObject].name, '"' ) = 0;
+
+ if ( strstr( name, "tag" ) == name )
+ {
+ while ( strchr( name, '_' ) != strrchr( name, '_' ) )
+ {
+ *strrchr( name, '_' ) = 0;
+ }
+ while ( strrchr( name, ' ' ) )
+ {
+ *strrchr( name, ' ' ) = 0;
+ }
+ }
+ }
+ else if ( !strcmp( token, "*NODE_PARENT" ) )
+ {
+ ASE_SkipRestOfLine();
+ }
+ // ignore unused data blocks
+ else if ( !strcmp( token, "*NODE_TM" ) ||
+ !strcmp( token, "*TM_ANIMATION" ) )
+ {
+ ASE_ParseBracedBlock( 0 );
+ }
+ // ignore regular meshes that aren't part of animation
+ else if ( !strcmp( token, "*MESH" ) && !ase.grabAnims )
+ {
+/*
+ if ( strstr( ase.objects[ase.currentObject].name, "tag_" ) == ase.objects[ase.currentObject].name )
+ {
+ s_forceStaticMesh = true;
+ ASE_ParseBracedBlock( ASE_KeyMESH );
+ s_forceStaticMesh = false;
+ }
+*/
+ ASE_ParseBracedBlock( ASE_KeyMESH );
+ if ( ++ase.objects[ase.currentObject].anim.currentFrame == MAX_ASE_ANIMATION_FRAMES )
+ {
+ Error( "Too many animation frames" );
+ }
+ ase.objects[ase.currentObject].anim.numFrames = ase.objects[ase.currentObject].anim.currentFrame;
+ ase.objects[ase.currentObject].numAnimations++;
+/*
+ // ignore meshes that aren't part of animations if this object isn't a
+ // a tag
+ else
+ {
+ ASE_ParseBracedBlock( 0 );
+ }
+*/
+ }
+ // according to spec these are obsolete
+ else if ( !strcmp( token, "*MATERIAL_REF" ) )
+ {
+ ASE_GetToken( qfalse );
+
+ ase.objects[ase.currentObject].materialRef = atoi( s_token );
+ }
+ // loads a sequence of animation frames
+ else if ( !strcmp( token, "*MESH_ANIMATION" ) )
+ {
+ if ( ase.grabAnims )
+ {
+ VERBOSE( ( "..found MESH_ANIMATION\n" ) );
+
+ if ( ase.objects[ase.currentObject].numAnimations )
+ {
+ Error( "Multiple MESH_ANIMATIONS within a single GEOM_OBJECT" );
+ }
+ ASE_ParseBracedBlock( ASE_KeyMESH_ANIMATION );
+ ase.objects[ase.currentObject].anim.numFrames = ase.objects[ase.currentObject].anim.currentFrame;
+ ase.objects[ase.currentObject].numAnimations++;
+ }
+ else
+ {
+ ASE_SkipEnclosingBraces();
+ }
+ }
+ // skip unused info
+ else if ( !strcmp( token, "*PROP_MOTIONBLUR" ) ||
+ !strcmp( token, "*PROP_CASTSHADOW" ) ||
+ !strcmp( token, "*PROP_RECVSHADOW" ) )
+ {
+ ASE_SkipRestOfLine();
+ }
+}
+
+static void ConcatenateObjects( aseGeomObject_t *pObjA, aseGeomObject_t *pObjB )
+{
+}
+
+static void CollapseObjects( void )
+{
+ int i;
+ int numObjects = ase.currentObject;
+
+ for ( i = 0; i < numObjects; i++ )
+ {
+ int j;
+
+ // skip tags
+ if ( strstr( ase.objects[i].name, "tag" ) == ase.objects[i].name )
+ {
+ continue;
+ }
+
+ if ( !ase.objects[i].numAnimations )
+ {
+ continue;
+ }
+
+ for ( j = i + 1; j < numObjects; j++ )
+ {
+ if ( strstr( ase.objects[j].name, "tag" ) == ase.objects[j].name )
+ {
+ continue;
+ }
+ if ( ase.objects[i].materialRef == ase.objects[j].materialRef )
+ {
+ if ( ase.objects[j].numAnimations )
+ {
+ ConcatenateObjects( &ase.objects[i], &ase.objects[j] );
+ }
+ }
+ }
+ }
+}
+
+/*
+** ASE_Process
+*/
+static void ASE_Process( void )
+{
+ while ( ASE_GetToken( qfalse ) )
+ {
+ if ( !strcmp( s_token, "*3DSMAX_ASCIIEXPORT" ) ||
+ !strcmp( s_token, "*COMMENT" ) )
+ {
+ ASE_SkipRestOfLine();
+ }
+ else if ( !strcmp( s_token, "*SCENE" ) )
+ ASE_SkipEnclosingBraces();
+ else if ( !strcmp( s_token, "*MATERIAL_LIST" ) )
+ {
+ VERBOSE( ("MATERIAL_LIST\n") );
+
+ ASE_ParseBracedBlock( ASE_KeyMATERIAL_LIST );
+ }
+ else if ( !strcmp( s_token, "*GEOMOBJECT" ) )
+ {
+ VERBOSE( ("GEOMOBJECT" ) );
+
+ ASE_ParseBracedBlock( ASE_KeyGEOMOBJECT );
+
+ if ( strstr( ase.objects[ase.currentObject].name, "Bip" ) ||
+ strstr( ase.objects[ase.currentObject].name, "ignore_" ) )
+ {
+ ASE_FreeGeomObject( ase.currentObject );
+ VERBOSE( ( "(discarding BIP/ignore object)\n" ) );
+ }
+ else if ( ( strstr( ase.objects[ase.currentObject].name, "h_" ) != ase.objects[ase.currentObject].name ) &&
+ ( strstr( ase.objects[ase.currentObject].name, "l_" ) != ase.objects[ase.currentObject].name ) &&
+ ( strstr( ase.objects[ase.currentObject].name, "u_" ) != ase.objects[ase.currentObject].name ) &&
+ ( strstr( ase.objects[ase.currentObject].name, "tag" ) != ase.objects[ase.currentObject].name ) &&
+ ase.grabAnims )
+ {
+ VERBOSE( ( "(ignoring improperly labeled object '%s')\n", ase.objects[ase.currentObject].name ) );
+ ASE_FreeGeomObject( ase.currentObject );
+ }
+ else
+ {
+ if ( ++ase.currentObject == MAX_ASE_OBJECTS )
+ {
+ Error( "Too many GEOMOBJECTs" );
+ }
+ }
+ }
+ else if ( s_token[0] )
+ {
+ printf( "Unknown token '%s'\n", s_token );
+ }
+ }
+
+ if ( !ase.currentObject )
+ Error( "No animation data!" );
+
+ CollapseObjects();
+}
+
diff --git a/common/aselib.h b/common/aselib.h
new file mode 100755
index 0000000..31bc0e6
--- /dev/null
+++ b/common/aselib.h
@@ -0,0 +1,31 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "polyset.h"
+
+void ASE_Load( const char *filename, qboolean verbose, qboolean meshanims );
+int ASE_GetNumSurfaces( void );
+polyset_t *ASE_GetSurfaceAnimation( int ndx, int *numFrames, int skipFrameStart, int skipFrameEnd, int maxFrames );
+const char *ASE_GetSurfaceName( int ndx );
+void ASE_Free( void );
diff --git a/common/bspfile.c b/common/bspfile.c
new file mode 100755
index 0000000..d9d4e04
--- /dev/null
+++ b/common/bspfile.c
@@ -0,0 +1,564 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "bspfile.h"
+#include "scriplib.h"
+
+void GetLeafNums (void);
+
+//=============================================================================
+
+int nummodels;
+dmodel_t dmodels[MAX_MAP_MODELS];
+
+int numShaders;
+dshader_t dshaders[MAX_MAP_SHADERS];
+
+int entdatasize;
+char dentdata[MAX_MAP_ENTSTRING];
+
+int numleafs;
+dleaf_t dleafs[MAX_MAP_LEAFS];
+
+int numplanes;
+dplane_t dplanes[MAX_MAP_PLANES];
+
+int numnodes;
+dnode_t dnodes[MAX_MAP_NODES];
+
+int numleafsurfaces;
+int dleafsurfaces[MAX_MAP_LEAFFACES];
+
+int numleafbrushes;
+int dleafbrushes[MAX_MAP_LEAFBRUSHES];
+
+int numbrushes;
+dbrush_t dbrushes[MAX_MAP_BRUSHES];
+
+int numbrushsides;
+dbrushside_t dbrushsides[MAX_MAP_BRUSHSIDES];
+
+int numLightBytes;
+byte lightBytes[MAX_MAP_LIGHTING];
+
+int numGridPoints;
+byte gridData[MAX_MAP_LIGHTGRID];
+
+int numVisBytes;
+byte visBytes[MAX_MAP_VISIBILITY];
+
+int numDrawVerts;
+drawVert_t drawVerts[MAX_MAP_DRAW_VERTS];
+
+int numDrawIndexes;
+int drawIndexes[MAX_MAP_DRAW_INDEXES];
+
+int numDrawSurfaces;
+dsurface_t drawSurfaces[MAX_MAP_DRAW_SURFS];
+
+int numFogs;
+dfog_t dfogs[MAX_MAP_FOGS];
+
+//=============================================================================
+
+/*
+=============
+SwapBlock
+
+If all values are 32 bits, this can be used to swap everything
+=============
+*/
+void SwapBlock( int *block, int sizeOfBlock ) {
+ int i;
+
+ sizeOfBlock >>= 2;
+ for ( i = 0 ; i < sizeOfBlock ; i++ ) {
+ block[i] = LittleLong( block[i] );
+ }
+}
+
+/*
+=============
+SwapBSPFile
+
+Byte swaps all data in a bsp file.
+=============
+*/
+void SwapBSPFile( void ) {
+ int i;
+
+ // models
+ SwapBlock( (int *)dmodels, nummodels * sizeof( dmodels[0] ) );
+
+ // shaders (don't swap the name)
+ for ( i = 0 ; i < numShaders ; i++ ) {
+ dshaders[i].contentFlags = LittleLong( dshaders[i].contentFlags );
+ dshaders[i].surfaceFlags = LittleLong( dshaders[i].surfaceFlags );
+ }
+
+ // planes
+ SwapBlock( (int *)dplanes, numplanes * sizeof( dplanes[0] ) );
+
+ // nodes
+ SwapBlock( (int *)dnodes, numnodes * sizeof( dnodes[0] ) );
+
+ // leafs
+ SwapBlock( (int *)dleafs, numleafs * sizeof( dleafs[0] ) );
+
+ // leaffaces
+ SwapBlock( (int *)dleafsurfaces, numleafsurfaces * sizeof( dleafsurfaces[0] ) );
+
+ // leafbrushes
+ SwapBlock( (int *)dleafbrushes, numleafbrushes * sizeof( dleafbrushes[0] ) );
+
+ // brushes
+ SwapBlock( (int *)dbrushes, numbrushes * sizeof( dbrushes[0] ) );
+
+ // brushsides
+ SwapBlock( (int *)dbrushsides, numbrushsides * sizeof( dbrushsides[0] ) );
+
+ // vis
+ ((int *)&visBytes)[0] = LittleLong( ((int *)&visBytes)[0] );
+ ((int *)&visBytes)[1] = LittleLong( ((int *)&visBytes)[1] );
+
+ // drawverts (don't swap colors )
+ for ( i = 0 ; i < numDrawVerts ; i++ ) {
+ drawVerts[i].lightmap[0] = LittleFloat( drawVerts[i].lightmap[0] );
+ drawVerts[i].lightmap[1] = LittleFloat( drawVerts[i].lightmap[1] );
+ drawVerts[i].st[0] = LittleFloat( drawVerts[i].st[0] );
+ drawVerts[i].st[1] = LittleFloat( drawVerts[i].st[1] );
+ drawVerts[i].xyz[0] = LittleFloat( drawVerts[i].xyz[0] );
+ drawVerts[i].xyz[1] = LittleFloat( drawVerts[i].xyz[1] );
+ drawVerts[i].xyz[2] = LittleFloat( drawVerts[i].xyz[2] );
+ drawVerts[i].normal[0] = LittleFloat( drawVerts[i].normal[0] );
+ drawVerts[i].normal[1] = LittleFloat( drawVerts[i].normal[1] );
+ drawVerts[i].normal[2] = LittleFloat( drawVerts[i].normal[2] );
+ }
+
+ // drawindexes
+ SwapBlock( (int *)drawIndexes, numDrawIndexes * sizeof( drawIndexes[0] ) );
+
+ // drawsurfs
+ SwapBlock( (int *)drawSurfaces, numDrawSurfaces * sizeof( drawSurfaces[0] ) );
+
+ // fogs
+ for ( i = 0 ; i < numFogs ; i++ ) {
+ dfogs[i].brushNum = LittleLong( dfogs[i].brushNum );
+ dfogs[i].visibleSide = LittleLong( dfogs[i].visibleSide );
+ }
+}
+
+
+
+/*
+=============
+CopyLump
+=============
+*/
+int CopyLump( dheader_t *header, 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( const char *filename ) {
+ dheader_t *header;
+
+ // load the file header
+ LoadFile (filename, (void **)&header);
+
+ // swap the header
+ SwapBlock( (int *)header, sizeof(*header) );
+
+ if ( header->ident != BSP_IDENT ) {
+ Error( "%s is not a IBSP file", filename );
+ }
+ if ( header->version != BSP_VERSION ) {
+ Error( "%s is version %i, not %i", filename, header->version, BSP_VERSION );
+ }
+
+ numShaders = CopyLump( header, LUMP_SHADERS, dshaders, sizeof(dshader_t) );
+ nummodels = CopyLump( header, LUMP_MODELS, dmodels, sizeof(dmodel_t) );
+ numplanes = CopyLump( header, LUMP_PLANES, dplanes, sizeof(dplane_t) );
+ numleafs = CopyLump( header, LUMP_LEAFS, dleafs, sizeof(dleaf_t) );
+ numnodes = CopyLump( header, LUMP_NODES, dnodes, sizeof(dnode_t) );
+ numleafsurfaces = CopyLump( header, LUMP_LEAFSURFACES, dleafsurfaces, sizeof(dleafsurfaces[0]) );
+ numleafbrushes = CopyLump( header, LUMP_LEAFBRUSHES, dleafbrushes, sizeof(dleafbrushes[0]) );
+ numbrushes = CopyLump( header, LUMP_BRUSHES, dbrushes, sizeof(dbrush_t) );
+ numbrushsides = CopyLump( header, LUMP_BRUSHSIDES, dbrushsides, sizeof(dbrushside_t) );
+ numDrawVerts = CopyLump( header, LUMP_DRAWVERTS, drawVerts, sizeof(drawVert_t) );
+ numDrawSurfaces = CopyLump( header, LUMP_SURFACES, drawSurfaces, sizeof(dsurface_t) );
+ numFogs = CopyLump( header, LUMP_FOGS, dfogs, sizeof(dfog_t) );
+ numDrawIndexes = CopyLump( header, LUMP_DRAWINDEXES, drawIndexes, sizeof(drawIndexes[0]) );
+
+ numVisBytes = CopyLump( header, LUMP_VISIBILITY, visBytes, 1 );
+ numLightBytes = CopyLump( header, LUMP_LIGHTMAPS, lightBytes, 1 );
+ entdatasize = CopyLump( header, LUMP_ENTITIES, dentdata, 1);
+
+ numGridPoints = CopyLump( header, LUMP_LIGHTGRID, gridData, 8 );
+
+
+ free( header ); // everything has been copied out
+
+ // swap everything
+ SwapBSPFile();
+}
+
+
+//============================================================================
+
+/*
+=============
+AddLump
+=============
+*/
+void AddLump( FILE *bspfile, dheader_t *header, int lumpnum, const void *data, int len ) {
+ lump_t *lump;
+
+ lump = &header->lumps[lumpnum];
+
+ lump->fileofs = LittleLong( ftell(bspfile) );
+ lump->filelen = LittleLong( len );
+ SafeWrite( bspfile, data, (len+3)&~3 );
+}
+
+/*
+=============
+WriteBSPFile
+
+Swaps the bsp file in place, so it should not be referenced again
+=============
+*/
+void WriteBSPFile( const char *filename ) {
+ dheader_t outheader, *header;
+ FILE *bspfile;
+
+ header = &outheader;
+ memset( header, 0, sizeof(dheader_t) );
+
+ SwapBSPFile();
+
+ header->ident = LittleLong( BSP_IDENT );
+ header->version = LittleLong( BSP_VERSION );
+
+ bspfile = SafeOpenWrite( filename );
+ SafeWrite( bspfile, header, sizeof(dheader_t) ); // overwritten later
+
+ AddLump( bspfile, header, LUMP_SHADERS, dshaders, numShaders*sizeof(dshader_t) );
+ AddLump( bspfile, header, LUMP_PLANES, dplanes, numplanes*sizeof(dplane_t) );
+ AddLump( bspfile, header, LUMP_LEAFS, dleafs, numleafs*sizeof(dleaf_t) );
+ AddLump( bspfile, header, LUMP_NODES, dnodes, numnodes*sizeof(dnode_t) );
+ AddLump( bspfile, header, LUMP_BRUSHES, dbrushes, numbrushes*sizeof(dbrush_t) );
+ AddLump( bspfile, header, LUMP_BRUSHSIDES, dbrushsides, numbrushsides*sizeof(dbrushside_t) );
+ AddLump( bspfile, header, LUMP_LEAFSURFACES, dleafsurfaces, numleafsurfaces*sizeof(dleafsurfaces[0]) );
+ AddLump( bspfile, header, LUMP_LEAFBRUSHES, dleafbrushes, numleafbrushes*sizeof(dleafbrushes[0]) );
+ AddLump( bspfile, header, LUMP_MODELS, dmodels, nummodels*sizeof(dmodel_t) );
+ AddLump( bspfile, header, LUMP_DRAWVERTS, drawVerts, numDrawVerts*sizeof(drawVert_t) );
+ AddLump( bspfile, header, LUMP_SURFACES, drawSurfaces, numDrawSurfaces*sizeof(dsurface_t) );
+ AddLump( bspfile, header, LUMP_VISIBILITY, visBytes, numVisBytes );
+ AddLump( bspfile, header, LUMP_LIGHTMAPS, lightBytes, numLightBytes );
+ AddLump( bspfile, header, LUMP_LIGHTGRID, gridData, 8 * numGridPoints );
+ AddLump( bspfile, header, LUMP_ENTITIES, dentdata, entdatasize );
+ AddLump( bspfile, header, LUMP_FOGS, dfogs, numFogs * sizeof(dfog_t) );
+ AddLump( bspfile, header, LUMP_DRAWINDEXES, drawIndexes, numDrawIndexes * sizeof(drawIndexes[0]) );
+
+ fseek (bspfile, 0, SEEK_SET);
+ SafeWrite (bspfile, header, sizeof(dheader_t));
+ fclose (bspfile);
+}
+
+//============================================================================
+
+/*
+=============
+PrintBSPFileSizes
+
+Dumps info about current file
+=============
+*/
+void PrintBSPFileSizes( void ) {
+ if ( !num_entities ) {
+ ParseEntities();
+ }
+
+ printf ("%6i models %7i\n"
+ ,nummodels, (int)(nummodels*sizeof(dmodel_t)));
+ printf ("%6i shaders %7i\n"
+ ,numShaders, (int)(numShaders*sizeof(dshader_t)));
+ printf ("%6i brushes %7i\n"
+ ,numbrushes, (int)(numbrushes*sizeof(dbrush_t)));
+ printf ("%6i brushsides %7i\n"
+ ,numbrushsides, (int)(numbrushsides*sizeof(dbrushside_t)));
+ printf ("%6i fogs %7i\n"
+ ,numFogs, (int)(numFogs*sizeof(dfog_t)));
+ printf ("%6i planes %7i\n"
+ ,numplanes, (int)(numplanes*sizeof(dplane_t)));
+ printf ("%6i entdata %7i\n", num_entities, entdatasize);
+
+ printf ("\n");
+
+ printf ("%6i nodes %7i\n"
+ ,numnodes, (int)(numnodes*sizeof(dnode_t)));
+ printf ("%6i leafs %7i\n"
+ ,numleafs, (int)(numleafs*sizeof(dleaf_t)));
+ printf ("%6i leafsurfaces %7i\n"
+ ,numleafsurfaces, (int)(numleafsurfaces*sizeof(dleafsurfaces[0])));
+ printf ("%6i leafbrushes %7i\n"
+ ,numleafbrushes, (int)(numleafbrushes*sizeof(dleafbrushes[0])));
+ printf ("%6i drawverts %7i\n"
+ ,numDrawVerts, (int)(numDrawVerts*sizeof(drawVerts[0])));
+ printf ("%6i drawindexes %7i\n"
+ ,numDrawIndexes, (int)(numDrawIndexes*sizeof(drawIndexes[0])));
+ printf ("%6i drawsurfaces %7i\n"
+ ,numDrawSurfaces, (int)(numDrawSurfaces*sizeof(drawSurfaces[0])));
+
+ printf ("%6i lightmaps %7i\n"
+ ,numLightBytes / (LIGHTMAP_WIDTH*LIGHTMAP_HEIGHT*3), numLightBytes );
+ printf (" visibility %7i\n"
+ , numVisBytes );
+}
+
+
+//============================================
+
+int num_entities;
+entity_t entities[MAX_MAP_ENTITIES];
+
+void StripTrailing( char *e ) {
+ char *s;
+
+ s = e + strlen(e)-1;
+ while (s >= e && *s <= 32)
+ {
+ *s = 0;
+ s--;
+ }
+}
+
+/*
+=================
+ParseEpair
+=================
+*/
+epair_t *ParseEpair( void ) {
+ epair_t *e;
+
+ e = malloc( sizeof(epair_t) );
+ memset( e, 0, sizeof(epair_t) );
+
+ if ( strlen(token) >= MAX_KEY-1 ) {
+ Error ("ParseEpar: token too long");
+ }
+ e->key = copystring( token );
+ GetToken( qfalse );
+ if ( strlen(token) >= MAX_VALUE-1 ) {
+ Error ("ParseEpar: token too long");
+ }
+ e->value = copystring( token );
+
+ // strip trailing spaces that sometimes get accidentally
+ // added in the editor
+ StripTrailing( e->key );
+ StripTrailing( e->value );
+
+ return e;
+}
+
+
+/*
+================
+ParseEntity
+================
+*/
+qboolean ParseEntity( void ) {
+ epair_t *e;
+ entity_t *mapent;
+
+ if ( !GetToken (qtrue) ) {
+ return qfalse;
+ }
+
+ 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 (qtrue) ) {
+ Error ("ParseEntity: EOF without closing brace");
+ }
+ if ( !strcmp (token, "}") ) {
+ break;
+ }
+ e = ParseEpair ();
+ e->next = mapent->epairs;
+ mapent->epairs = e;
+ } while (1);
+
+ return qtrue;
+}
+
+/*
+================
+ParseEntities
+
+Parses the dentdata string into entities
+================
+*/
+void ParseEntities( void ) {
+ num_entities = 0;
+ ParseFromMemory( dentdata, entdatasize );
+
+ while ( ParseEntity () ) {
+ }
+}
+
+
+/*
+================
+UnparseEntities
+
+Generates the dentdata string from all the entities
+This allows the utilities to add or remove key/value pairs
+to the data created by the map editor.
+================
+*/
+void UnparseEntities( void ) {
+ char *buf, *end;
+ epair_t *ep;
+ char line[2048];
+ int i;
+ char key[1024], value[1024];
+
+ 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 ) {
+ strcpy (key, ep->key);
+ StripTrailing (key);
+ strcpy (value, ep->value);
+ StripTrailing (value);
+
+ sprintf (line, "\"%s\" \"%s\"\n", key, 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;
+}
+
+void PrintEntity( const entity_t *ent ) {
+ epair_t *ep;
+
+ printf ("------- entity %p -------\n", ent);
+ for (ep=ent->epairs ; ep ; ep=ep->next) {
+ printf( "%s = %s\n", ep->key, ep->value );
+ }
+
+}
+
+void SetKeyValue( entity_t *ent, const char *key, const 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);
+}
+
+const char *ValueForKey( const entity_t *ent, const char *key ) {
+ epair_t *ep;
+
+ for (ep=ent->epairs ; ep ; ep=ep->next) {
+ if (!strcmp (ep->key, key) ) {
+ return ep->value;
+ }
+ }
+ return "";
+}
+
+vec_t FloatForKey( const entity_t *ent, const char *key ) {
+ const char *k;
+
+ k = ValueForKey( ent, key );
+ return atof(k);
+}
+
+void GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec ) {
+ const char *k;
+ double v1, v2, v3;
+
+ k = ValueForKey (ent, key);
+
+ // scanf into doubles, then assign, so it is vec_t size independent
+ v1 = v2 = v3 = 0;
+ sscanf (k, "%lf %lf %lf", &v1, &v2, &v3);
+ vec[0] = v1;
+ vec[1] = v2;
+ vec[2] = v3;
+}
+
+
diff --git a/common/bspfile.h b/common/bspfile.h
new file mode 100755
index 0000000..fa136dd
--- /dev/null
+++ b/common/bspfile.h
@@ -0,0 +1,118 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+#ifdef _TTIMOBUILD
+#include "qfiles.h"
+#include "surfaceflags.h"
+#else
+#include "../code/qcommon/qfiles.h"
+#include "../code/game/surfaceflags.h"
+#endif
+
+extern int nummodels;
+extern dmodel_t dmodels[MAX_MAP_MODELS];
+
+extern int numShaders;
+extern dshader_t dshaders[MAX_MAP_MODELS];
+
+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 numnodes;
+extern dnode_t dnodes[MAX_MAP_NODES];
+
+extern int numleafsurfaces;
+extern int dleafsurfaces[MAX_MAP_LEAFFACES];
+
+extern int numleafbrushes;
+extern int dleafbrushes[MAX_MAP_LEAFBRUSHES];
+
+extern int numbrushes;
+extern dbrush_t dbrushes[MAX_MAP_BRUSHES];
+
+extern int numbrushsides;
+extern dbrushside_t dbrushsides[MAX_MAP_BRUSHSIDES];
+
+extern int numLightBytes;
+extern byte lightBytes[MAX_MAP_LIGHTING];
+
+extern int numGridPoints;
+extern byte gridData[MAX_MAP_LIGHTGRID];
+
+extern int numVisBytes;
+extern byte visBytes[MAX_MAP_VISIBILITY];
+
+extern int numDrawVerts;
+extern drawVert_t drawVerts[MAX_MAP_DRAW_VERTS];
+
+extern int numDrawIndexes;
+extern int drawIndexes[MAX_MAP_DRAW_INDEXES];
+
+extern int numDrawSurfaces;
+extern dsurface_t drawSurfaces[MAX_MAP_DRAW_SURFS];
+
+extern int numFogs;
+extern dfog_t dfogs[MAX_MAP_FOGS];
+
+void LoadBSPFile( const char *filename );
+void WriteBSPFile( const char *filename );
+void PrintBSPFileSizes( void );
+
+//===============
+
+
+typedef struct epair_s {
+ struct epair_s *next;
+ char *key;
+ char *value;
+} epair_t;
+
+typedef struct {
+ vec3_t origin;
+ struct bspbrush_s *brushes;
+ struct parseMesh_s *patches;
+ int firstDrawSurf;
+ epair_t *epairs;
+} entity_t;
+
+extern int num_entities;
+extern entity_t entities[MAX_MAP_ENTITIES];
+
+void ParseEntities( void );
+void UnparseEntities( void );
+
+void SetKeyValue( entity_t *ent, const char *key, const char *value );
+const char *ValueForKey( const entity_t *ent, const char *key );
+// will return "" if not present
+
+vec_t FloatForKey( const entity_t *ent, const char *key );
+void GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec );
+
+epair_t *ParseEpair( void );
+
+void PrintEntity( const entity_t *ent );
+
diff --git a/common/cmdlib.c b/common/cmdlib.c
new file mode 100755
index 0000000..794d4c0
--- /dev/null
+++ b/common/cmdlib.c
@@ -0,0 +1,1201 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+// cmdlib.c
+
+#include "cmdlib.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef WIN32
+#include <direct.h>
+#include <windows.h>
+#endif
+
+#ifdef NeXT
+#include <libc.h>
+#endif
+
+#define BASEDIRNAME "quake" // assumed to have a 2 or 3 following
+#define PATHSEPERATOR '/'
+
+// set these before calling CheckParm
+int myargc;
+char **myargv;
+
+char com_token[1024];
+qboolean com_eof;
+
+qboolean archive;
+char archivedir[1024];
+
+
+/*
+===================
+ExpandWildcards
+
+Mimic unix command line expansion
+===================
+*/
+#define MAX_EX_ARGC 1024
+int ex_argc;
+char *ex_argv[MAX_EX_ARGC];
+#ifdef _WIN32
+#include "io.h"
+void ExpandWildcards( int *argc, char ***argv )
+{
+ struct _finddata_t fileinfo;
+ int handle;
+ int i;
+ char filename[1024];
+ char filebase[1024];
+ char *path;
+
+ ex_argc = 0;
+ for (i=0 ; i<*argc ; i++)
+ {
+ path = (*argv)[i];
+ if ( path[0] == '-'
+ || ( !strstr(path, "*") && !strstr(path, "?") ) )
+ {
+ ex_argv[ex_argc++] = path;
+ continue;
+ }
+
+ handle = _findfirst (path, &fileinfo);
+ if (handle == -1)
+ return;
+
+ ExtractFilePath (path, filebase);
+
+ do
+ {
+ sprintf (filename, "%s%s", filebase, fileinfo.name);
+ ex_argv[ex_argc++] = copystring (filename);
+ } while (_findnext( handle, &fileinfo ) != -1);
+
+ _findclose (handle);
+ }
+
+ *argc = ex_argc;
+ *argv = ex_argv;
+}
+#else
+void ExpandWildcards (int *argc, char ***argv)
+{
+}
+#endif
+
+#ifdef WIN_ERROR
+#include <windows.h>
+/*
+=================
+Error
+
+For abnormal program terminations in windowed apps
+=================
+*/
+void Error( const char *error, ... )
+{
+ va_list argptr;
+ char text[1024];
+ char text2[1024];
+ int err;
+
+ err = GetLastError ();
+
+ va_start (argptr,error);
+ vsprintf (text, error,argptr);
+ va_end (argptr);
+
+ sprintf (text2, "%s\nGetLastError() = %i", text, err);
+ MessageBox(NULL, text2, "Error", 0 /* MB_OK */ );
+
+ exit (1);
+}
+
+#else
+/*
+=================
+Error
+
+For abnormal program terminations in console apps
+=================
+*/
+void Error( const char *error, ...)
+{
+ va_list argptr;
+
+ _printf ("\n************ ERROR ************\n");
+
+ va_start (argptr,error);
+ vprintf (error,argptr);
+ va_end (argptr);
+ _printf ("\r\n");
+
+ exit (1);
+}
+#endif
+
+// only printf if in verbose mode
+qboolean verbose = qfalse;
+void qprintf( const char *format, ... ) {
+ va_list argptr;
+
+ if (!verbose)
+ return;
+
+ va_start (argptr,format);
+ vprintf (format,argptr);
+ va_end (argptr);
+
+}
+
+#ifdef WIN32
+HWND hwndOut = NULL;
+qboolean lookedForServer = qfalse;
+UINT wm_BroadcastCommand = -1;
+#endif
+
+void _printf( const char *format, ... ) {
+ va_list argptr;
+ char text[4096];
+ ATOM a;
+
+ va_start (argptr,format);
+ vsprintf (text, format, argptr);
+ va_end (argptr);
+
+ printf(text);
+
+#ifdef WIN32
+ if (!lookedForServer) {
+ lookedForServer = qtrue;
+ hwndOut = FindWindow(NULL, "Q3Map Process Server");
+ if (hwndOut) {
+ wm_BroadcastCommand = RegisterWindowMessage( "Q3MPS_BroadcastCommand" );
+ }
+ }
+ if (hwndOut) {
+ a = GlobalAddAtom(text);
+ PostMessage(hwndOut, wm_BroadcastCommand, 0, (LPARAM)a);
+ }
+#endif
+}
+
+
+/*
+
+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];
+char writedir[1024];
+
+void SetQdirFromPath( const char *path )
+{
+ char temp[1024];
+ const char *c;
+ const char *sep;
+ int len, count;
+
+ if (!(path[0] == '/' || path[0] == '\\' || path[1] == ':'))
+ { // path is partial
+ Q_getwd (temp);
+ strcat (temp, path);
+ path = temp;
+ }
+
+ // search for "quake2" in path
+
+ len = strlen(BASEDIRNAME);
+ for (c=path+strlen(path)-1 ; c != path ; c--)
+ {
+ int i;
+
+ if (!Q_strncasecmp (c, BASEDIRNAME, len))
+ {
+ //
+ //strncpy (qdir, path, c+len+2-path);
+ // the +2 assumes a 2 or 3 following quake which is not the
+ // case with a retail install
+ // so we need to add up how much to the next separator
+ sep = c + len;
+ count = 1;
+ while (*sep && *sep != '/' && *sep != '\\')
+ {
+ sep++;
+ count++;
+ }
+ strncpy (qdir, path, c+len+count-path);
+ qprintf ("qdir: %s\n", qdir);
+ for ( i = 0; i < strlen( qdir ); i++ )
+ {
+ if ( qdir[i] == '\\' )
+ qdir[i] = '/';
+ }
+
+ c += len+count;
+ while (*c)
+ {
+ if (*c == '/' || *c == '\\')
+ {
+ strncpy (gamedir, path, c+1-path);
+
+ for ( i = 0; i < strlen( gamedir ); i++ )
+ {
+ if ( gamedir[i] == '\\' )
+ gamedir[i] = '/';
+ }
+
+ qprintf ("gamedir: %s\n", gamedir);
+
+ if ( !writedir[0] )
+ strcpy( writedir, gamedir );
+ else if ( writedir[strlen( writedir )-1] != '/' )
+ {
+ writedir[strlen( writedir )] = '/';
+ writedir[strlen( writedir )+1] = 0;
+ }
+
+ return;
+ }
+ c++;
+ }
+ Error ("No gamedir in %s", path);
+ return;
+ }
+ }
+ Error ("SetQdirFromPath: no '%s' in %s", BASEDIRNAME, path);
+}
+
+char *ExpandArg (const char *path)
+{
+ static char full[1024];
+
+ if (path[0] != '/' && path[0] != '\\' && path[1] != ':')
+ {
+ Q_getwd (full);
+ strcat (full, path);
+ }
+ else
+ strcpy (full, path);
+ return full;
+}
+
+char *ExpandPath (const char *path)
+{
+ static char full[1024];
+ if (!qdir)
+ Error ("ExpandPath called without qdir set");
+ if (path[0] == '/' || path[0] == '\\' || path[1] == ':') {
+ strcpy( full, path );
+ return full;
+ }
+ sprintf (full, "%s%s", qdir, path);
+ return full;
+}
+
+char *ExpandGamePath (const char *path)
+{
+ static char full[1024];
+ if (!qdir)
+ Error ("ExpandGamePath called without qdir set");
+ if (path[0] == '/' || path[0] == '\\' || path[1] == ':') {
+ strcpy( full, path );
+ return full;
+ }
+ sprintf (full, "%s%s", gamedir, path);
+ return full;
+}
+
+char *ExpandPathAndArchive (const char *path)
+{
+ char *expanded;
+ char archivename[1024];
+
+ expanded = ExpandPath (path);
+
+ if (archive)
+ {
+ sprintf (archivename, "%s/%s", archivedir, path);
+ QCopyFile (expanded, archivename);
+ }
+ return expanded;
+}
+
+
+char *copystring(const 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)
+{
+ int i = 0;
+
+#ifdef WIN32
+ _getcwd (out, 256);
+ strcat (out, "\\");
+#else
+ getwd (out);
+ strcat (out, "/");
+#endif
+
+ while ( out[i] != 0 )
+ {
+ if ( out[i] == '\\' )
+ out[i] = '/';
+ i++;
+ }
+}
+
+
+void Q_mkdir (const 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 (const 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 = qtrue;
+ 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 (const char *s1, const char *s2, int n)
+{
+ int c1, c2;
+
+ do
+ {
+ 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
+ }
+ } while (c1);
+
+ return 0; // strings are equal
+}
+
+int Q_stricmp (const char *s1, const 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 (const char *check)
+{
+ int i;
+
+ for (i = 1;i<myargc;i++)
+ {
+ if ( !Q_stricmp(check, myargv[i]) )
+ return i;
+ }
+
+ return 0;
+}
+
+
+
+/*
+================
+Q_filelength
+================
+*/
+int Q_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 (const char *filename)
+{
+ FILE *f;
+
+ f = fopen(filename, "wb");
+
+ if (!f)
+ Error ("Error opening %s: %s",filename,strerror(errno));
+
+ return f;
+}
+
+FILE *SafeOpenRead (const 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, const void *buffer, int count)
+{
+ if (fwrite (buffer, 1, count, f) != (size_t)count)
+ Error ("File write failure");
+}
+
+
+/*
+==============
+FileExists
+==============
+*/
+qboolean FileExists (const char *filename)
+{
+ FILE *f;
+
+ f = fopen (filename, "r");
+ if (!f)
+ return qfalse;
+ fclose (f);
+ return qtrue;
+}
+
+/*
+==============
+LoadFile
+==============
+*/
+int LoadFile( const char *filename, void **bufferptr )
+{
+ FILE *f;
+ int length;
+ void *buffer;
+
+ f = SafeOpenRead (filename);
+ length = Q_filelength (f);
+ buffer = malloc (length+1);
+ ((char *)buffer)[length] = 0;
+ SafeRead (f, buffer, length);
+ fclose (f);
+
+ *bufferptr = buffer;
+ return length;
+}
+
+
+/*
+==============
+LoadFileBlock
+-
+rounds up memory allocation to 4K boundry
+-
+==============
+*/
+int LoadFileBlock( const char *filename, void **bufferptr )
+{
+ FILE *f;
+ int length, nBlock, nAllocSize;
+ void *buffer;
+
+ f = SafeOpenRead (filename);
+ length = Q_filelength (f);
+ nAllocSize = length;
+ nBlock = nAllocSize % MEM_BLOCKSIZE;
+ if ( nBlock > 0) {
+ nAllocSize += MEM_BLOCKSIZE - nBlock;
+ }
+ buffer = malloc (nAllocSize+1);
+ memset(buffer, 0, nAllocSize+1);
+ SafeRead (f, buffer, length);
+ fclose (f);
+
+ *bufferptr = buffer;
+ return length;
+}
+
+
+/*
+==============
+TryLoadFile
+
+Allows failure
+==============
+*/
+int TryLoadFile (const char *filename, void **bufferptr)
+{
+ FILE *f;
+ int length;
+ void *buffer;
+
+ *bufferptr = NULL;
+
+ f = fopen (filename, "rb");
+ if (!f)
+ return -1;
+ length = Q_filelength (f);
+ buffer = malloc (length+1);
+ ((char *)buffer)[length] = 0;
+ SafeRead (f, buffer, length);
+ fclose (f);
+
+ *bufferptr = buffer;
+ return length;
+}
+
+
+/*
+==============
+SaveFile
+==============
+*/
+void SaveFile (const char *filename, const void *buffer, int count)
+{
+ FILE *f;
+
+ f = SafeOpenWrite (filename);
+ SafeWrite (f, buffer, count);
+ fclose (f);
+}
+
+
+
+void DefaultExtension (char *path, const char *extension)
+{
+ char *src;
+//
+// if path doesnt have a .EXT, append extension
+// (extension should include the .)
+//
+ src = path + strlen(path) - 1;
+
+ while (*src != '/' && *src != '\\' && src != path)
+ {
+ if (*src == '.')
+ return; // it has an extension
+ src--;
+ }
+
+ strcat (path, extension);
+}
+
+
+void DefaultPath (char *path, const 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
+====================
+*/
+// FIXME: should include the slash, otherwise
+// backing to an empty path will be wrong when appending a slash
+void ExtractFilePath (const char *path, char *dest)
+{
+ const char *src;
+
+ src = path + strlen(path) - 1;
+
+//
+// back up until a \ or the start
+//
+ while (src != path && *(src-1) != '\\' && *(src-1) != '/')
+ src--;
+
+ memcpy (dest, path, src-path);
+ dest[src-path] = 0;
+}
+
+void ExtractFileBase (const char *path, char *dest)
+{
+ const 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 (const char *path, char *dest)
+{
+ const 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 (const char *hex)
+{
+ const 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 (const 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 (const char *path)
+{
+ const char *ofs;
+ char c;
+ char dir[1024];
+
+#ifdef _WIN32
+ int olddrive = -1;
+
+ if ( path[1] == ':' )
+ {
+ olddrive = _getdrive();
+ _chdrive( toupper( path[0] ) - 'A' + 1 );
+ }
+#endif
+
+ if (path[1] == ':')
+ path += 2;
+
+ for (ofs = path+1 ; *ofs ; ofs++)
+ {
+ c = *ofs;
+ if (c == '/' || c == '\\')
+ { // create the directory
+ memcpy( dir, path, ofs - path );
+ dir[ ofs - path ] = 0;
+ Q_mkdir( dir );
+ }
+ }
+
+#ifdef _WIN32
+ if ( olddrive != -1 )
+ {
+ _chdrive( olddrive );
+ }
+#endif
+}
+
+
+/*
+============
+QCopyFile
+
+ Used to archive source files
+============
+*/
+void QCopyFile (const char *from, const char *to)
+{
+ void *buffer;
+ int length;
+
+ length = LoadFile (from, &buffer);
+ CreatePath (to);
+ SaveFile (to, buffer, length);
+ free (buffer);
+}
diff --git a/common/cmdlib.h b/common/cmdlib.h
new file mode 100755
index 0000000..68d211d
--- /dev/null
+++ b/common/cmdlib.h
@@ -0,0 +1,160 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+// cmdlib.h
+
+#ifndef __CMDLIB__
+#define __CMDLIB__
+
+#ifdef _WIN32
+#pragma warning(disable : 4244) // MIPS
+#pragma warning(disable : 4136) // X86
+#pragma warning(disable : 4051) // ALPHA
+
+#pragma warning(disable : 4018) // signed/unsigned mismatch
+#pragma warning(disable : 4305) // truncate from double to float
+
+#pragma check_stack(off)
+
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <time.h>
+#include <stdarg.h>
+
+#ifdef _WIN32
+
+#pragma intrinsic( memset, memcpy )
+
+#endif
+
+#ifndef __BYTEBOOL__
+#define __BYTEBOOL__
+typedef enum { qfalse, qtrue } qboolean;
+typedef unsigned char byte;
+#endif
+
+#define MAX_OS_PATH 1024
+#define MEM_BLOCKSIZE 4096
+
+// the dec offsetof macro doesnt 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( const char *s1, const char *s2, int n );
+int Q_stricmp( const char *s1, const char *s2 );
+void Q_getwd( char *out );
+
+int Q_filelength (FILE *f);
+int FileTime( const char *path );
+
+void Q_mkdir( const char *path );
+
+extern char qdir[1024];
+extern char gamedir[1024];
+extern char writedir[1024];
+void SetQdirFromPath( const char *path );
+char *ExpandArg( const char *path ); // from cmd line
+char *ExpandPath( const char *path ); // from scripts
+char *ExpandGamePath (const char *path);
+char *ExpandPathAndArchive( const char *path );
+
+
+double I_FloatTime( void );
+
+void Error( const char *error, ... );
+int CheckParm( const char *check );
+
+FILE *SafeOpenWrite( const char *filename );
+FILE *SafeOpenRead( const char *filename );
+void SafeRead (FILE *f, void *buffer, int count);
+void SafeWrite (FILE *f, const void *buffer, int count);
+
+int LoadFile( const char *filename, void **bufferptr );
+int LoadFileBlock( const char *filename, void **bufferptr );
+int TryLoadFile( const char *filename, void **bufferptr );
+void SaveFile( const char *filename, const void *buffer, int count );
+qboolean FileExists( const char *filename );
+
+void DefaultExtension( char *path, const char *extension );
+void DefaultPath( char *path, const char *basepath );
+void StripFilename( char *path );
+void StripExtension( char *path );
+
+void ExtractFilePath( const char *path, char *dest );
+void ExtractFileBase( const char *path, char *dest );
+void ExtractFileExtension( const char *path, char *dest );
+
+int ParseNum (const 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(const 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( const char *path );
+void QCopyFile( const char *from, const char *to );
+
+extern qboolean archive;
+extern char archivedir[1024];
+
+
+extern qboolean verbose;
+void qprintf( const char *format, ... );
+void _printf( const char *format, ... );
+
+void ExpandWildcards( int *argc, char ***argv );
+
+
+// for compression routines
+typedef struct
+{
+ void *data;
+ int count, width, height;
+} cblock_t;
+
+
+#endif
diff --git a/common/imagelib.c b/common/imagelib.c
new file mode 100755
index 0000000..d59c2d0
--- /dev/null
+++ b/common/imagelib.c
@@ -0,0 +1,1164 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+// imagelib.c
+
+#include "cmdlib.h"
+#include "imagelib.h"
+
+
+int fgetLittleShort (FILE *f)
+{
+ byte b1, b2;
+
+ b1 = fgetc(f);
+ b2 = fgetc(f);
+
+ return (short)(b1 + b2*256);
+}
+
+int fgetLittleLong (FILE *f)
+{
+ byte b1, b2, b3, b4;
+
+ b1 = fgetc(f);
+ b2 = fgetc(f);
+ b3 = fgetc(f);
+ b4 = fgetc(f);
+
+ return b1 + (b2<<8) + (b3<<16) + (b4<<24);
+}
+
+
+
+/*
+============================================================================
+
+ LBM STUFF
+
+============================================================================
+*/
+
+
+typedef unsigned char UBYTE;
+//conflicts with windows 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;
+ short x,y;
+ UBYTE nPlanes;
+ UBYTE masking;
+ UBYTE compression;
+ UBYTE pad1;
+ UWORD transparentColor;
+ UBYTE xAspect,yAspect;
+ short pageWidth,pageHeight;
+} bmhd_t;
+
+extern bmhd_t bmhd; // will be in native byte order
+
+
+
+#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;
+}
+
+
+/*
+=================
+LoadLBM
+=================
+*/
+void LoadLBM (const char *filename, byte **picture, byte **palette)
+{
+ byte *LBMbuffer, *picbuffer, *cmapbuffer;
+ int y;
+ byte *LBM_P, *LBMEND_P;
+ byte *pic_p;
+ byte *body_p;
+
+ int formtype,formlength;
+ int chunktype,chunklength;
+
+// qiet compiler warnings
+ picbuffer = NULL;
+ cmapbuffer = 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
+ //
+ Error ("%s is an interlaced LBM, not packed", filename);
+ }
+ break;
+ }
+
+ LBM_P += Align(chunklength);
+ }
+
+ free (LBMbuffer);
+
+ *picture = picbuffer;
+
+ if (palette)
+ *palette = cmapbuffer;
+}
+
+
+/*
+============================================================================
+
+ WRITE LBM
+
+============================================================================
+*/
+
+/*
+==============
+WriteLBMfile
+==============
+*/
+void WriteLBMfile (const 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);
+}
+
+
+/*
+============================================================================
+
+LOAD PCX
+
+============================================================================
+*/
+
+typedef struct
+{
+ char manufacturer;
+ char version;
+ char encoding;
+ char bits_per_pixel;
+ unsigned short xmin,ymin,xmax,ymax;
+ unsigned short hres,vres;
+ unsigned char palette[48];
+ char reserved;
+ char color_planes;
+ unsigned short bytes_per_line;
+ unsigned short palette_type;
+ char filler[58];
+ unsigned char data; // unbounded
+} pcx_t;
+
+
+/*
+==============
+LoadPCX
+==============
+*/
+void LoadPCX (const char *filename, byte **pic, byte **palette, int *width, int *height)
+{
+ byte *raw;
+ pcx_t *pcx;
+ int x, y;
+ int len;
+ int dataByte, runLength;
+ byte *out, *pix;
+
+ //
+ // load the file
+ //
+ len = LoadFile (filename, (void **)&raw);
+
+ //
+ // parse the PCX file
+ //
+ pcx = (pcx_t *)raw;
+ raw = &pcx->data;
+
+ pcx->xmin = LittleShort(pcx->xmin);
+ pcx->ymin = LittleShort(pcx->ymin);
+ pcx->xmax = LittleShort(pcx->xmax);
+ pcx->ymax = LittleShort(pcx->ymax);
+ pcx->hres = LittleShort(pcx->hres);
+ pcx->vres = LittleShort(pcx->vres);
+ pcx->bytes_per_line = LittleShort(pcx->bytes_per_line);
+ pcx->palette_type = LittleShort(pcx->palette_type);
+
+ if (pcx->manufacturer != 0x0a
+ || pcx->version != 5
+ || pcx->encoding != 1
+ || pcx->bits_per_pixel != 8
+ || pcx->xmax >= 640
+ || pcx->ymax >= 480)
+ Error ("Bad pcx file %s", filename);
+
+ if (palette)
+ {
+ *palette = malloc(768);
+ memcpy (*palette, (byte *)pcx + len - 768, 768);
+ }
+
+ if (width)
+ *width = pcx->xmax+1;
+ if (height)
+ *height = pcx->ymax+1;
+
+ if (!pic)
+ return;
+
+ out = malloc ( (pcx->ymax+1) * (pcx->xmax+1) );
+ if (!out)
+ Error ("Skin_Cache: couldn't allocate");
+
+ *pic = out;
+
+ pix = out;
+
+ for (y=0 ; y<=pcx->ymax ; y++, pix += pcx->xmax+1)
+ {
+ for (x=0 ; x<=pcx->xmax ; )
+ {
+ dataByte = *raw++;
+
+ if((dataByte & 0xC0) == 0xC0)
+ {
+ runLength = dataByte & 0x3F;
+ dataByte = *raw++;
+ }
+ else
+ runLength = 1;
+
+ // FIXME: this shouldn't happen, but it does. Are we decoding the file wrong?
+ // Truncate runLength so we don't overrun the end of the buffer
+ if ( ( y == pcx->ymax ) && ( x + runLength > pcx->xmax + 1 ) ) {
+ runLength = pcx->xmax - x + 1;
+ }
+
+ while(runLength-- > 0)
+ pix[x++] = dataByte;
+ }
+
+ }
+
+ if ( raw - (byte *)pcx > len)
+ Error ("PCX file %s was malformed", filename);
+
+ free (pcx);
+}
+
+/*
+==============
+WritePCXfile
+==============
+*/
+void WritePCXfile (const char *filename, byte *data,
+ int width, int height, byte *palette)
+{
+ int i, j, length;
+ pcx_t *pcx;
+ byte *pack;
+
+ pcx = malloc (width*height*2+1000);
+ memset (pcx, 0, sizeof(*pcx));
+
+ pcx->manufacturer = 0x0a; // PCX id
+ pcx->version = 5; // 256 color
+ pcx->encoding = 1; // uncompressed
+ pcx->bits_per_pixel = 8; // 256 color
+ pcx->xmin = 0;
+ pcx->ymin = 0;
+ pcx->xmax = LittleShort((short)(width-1));
+ pcx->ymax = LittleShort((short)(height-1));
+ pcx->hres = LittleShort((short)width);
+ pcx->vres = LittleShort((short)height);
+ pcx->color_planes = 1; // chunky image
+ pcx->bytes_per_line = LittleShort((short)width);
+ pcx->palette_type = LittleShort(1); // not a grey scale
+
+ // pack the image
+ pack = &pcx->data;
+
+ for (i=0 ; i<height ; i++)
+ {
+ for (j=0 ; j<width ; j++)
+ {
+ if ( (*data & 0xc0) != 0xc0)
+ *pack++ = *data++;
+ else
+ {
+ *pack++ = 0xc1;
+ *pack++ = *data++;
+ }
+ }
+ }
+
+ // write the palette
+ *pack++ = 0x0c; // palette ID byte
+ for (i=0 ; i<768 ; i++)
+ *pack++ = *palette++;
+
+// write output file
+ length = pack - (byte *)pcx;
+ SaveFile (filename, pcx, length);
+
+ free (pcx);
+}
+
+/*
+============================================================================
+
+LOAD BMP
+
+============================================================================
+*/
+
+
+/*
+
+// we can't just use these structures, because
+// compiler structure alignment will not be portable
+// on this unaligned stuff
+
+typedef struct tagBITMAPFILEHEADER { // bmfh
+ WORD bfType; // BM
+ DWORD bfSize;
+ WORD bfReserved1;
+ WORD bfReserved2;
+ DWORD bfOffBits;
+} BITMAPFILEHEADER;
+
+typedef struct tagBITMAPINFOHEADER{ // bmih
+ DWORD biSize;
+ LONG biWidth;
+ LONG biHeight;
+ WORD biPlanes;
+ WORD biBitCount
+ DWORD biCompression;
+ DWORD biSizeImage;
+ LONG biXPelsPerMeter;
+ LONG biYPelsPerMeter;
+ DWORD biClrUsed;
+ DWORD biClrImportant;
+} BITMAPINFOHEADER;
+
+typedef struct tagBITMAPINFO { // bmi
+ BITMAPINFOHEADER bmiHeader;
+ RGBQUAD bmiColors[1];
+} BITMAPINFO;
+
+typedef struct tagBITMAPCOREHEADER { // bmch
+ DWORD bcSize;
+ WORD bcWidth;
+ WORD bcHeight;
+ WORD bcPlanes;
+ WORD bcBitCount;
+} BITMAPCOREHEADER;
+
+typedef struct _BITMAPCOREINFO { // bmci
+ BITMAPCOREHEADER bmciHeader;
+ RGBTRIPLE bmciColors[1];
+} BITMAPCOREINFO;
+
+*/
+
+/*
+==============
+LoadBMP
+==============
+*/
+void LoadBMP (const char *filename, byte **pic, byte **palette, int *width, int *height)
+{
+ byte *out;
+ FILE *fin;
+ int i;
+ int bfSize;
+ int bfOffBits;
+ int structSize;
+ int bcWidth;
+ int bcHeight;
+ int bcPlanes;
+ int bcBitCount;
+ byte bcPalette[1024];
+ qboolean flipped;
+
+ fin = fopen (filename, "rb");
+ if (!fin) {
+ Error ("Couldn't read %s", filename);
+ }
+
+ i = fgetLittleShort (fin);
+ if (i != 'B' + ('M'<<8) ) {
+ Error ("%s is not a bmp file", filename);
+ }
+
+ bfSize = fgetLittleLong (fin);
+ fgetLittleShort(fin);
+ fgetLittleShort(fin);
+ bfOffBits = fgetLittleLong (fin);
+
+ // the size will tell us if it is a
+ // bitmapinfo or a bitmapcore
+ structSize = fgetLittleLong (fin);
+ if (structSize == 40) {
+ // bitmapinfo
+ bcWidth = fgetLittleLong(fin);
+ bcHeight= fgetLittleLong(fin);
+ bcPlanes = fgetLittleShort(fin);
+ bcBitCount = fgetLittleShort(fin);
+
+ fseek (fin, 24, SEEK_CUR);
+
+ if (palette) {
+ fread (bcPalette, 1, 1024, fin);
+ *palette = malloc(768);
+
+ for (i = 0 ; i < 256 ; i++) {
+ (*palette)[i * 3 + 0] = bcPalette[i * 4 + 2];
+ (*palette)[i * 3 + 1] = bcPalette[i * 4 + 1];
+ (*palette)[i * 3 + 2] = bcPalette[i * 4 + 0];
+ }
+ }
+ } else if (structSize == 12) {
+ // bitmapcore
+ bcWidth = fgetLittleShort(fin);
+ bcHeight= fgetLittleShort(fin);
+ bcPlanes = fgetLittleShort(fin);
+ bcBitCount = fgetLittleShort(fin);
+
+ if (palette) {
+ fread (bcPalette, 1, 768, fin);
+ *palette = malloc(768);
+
+ for (i = 0 ; i < 256 ; i++) {
+ (*palette)[i * 3 + 0] = bcPalette[i * 3 + 2];
+ (*palette)[i * 3 + 1] = bcPalette[i * 3 + 1];
+ (*palette)[i * 3 + 2] = bcPalette[i * 3 + 0];
+ }
+ }
+ } else {
+ Error ("%s had strange struct size", filename);
+ }
+
+ if (bcPlanes != 1) {
+ Error ("%s was not a single plane image", filename);
+ }
+
+ if (bcBitCount != 8) {
+ Error ("%s was not an 8 bit image", filename);
+ }
+
+ if (bcHeight < 0) {
+ bcHeight = -bcHeight;
+ flipped = qtrue;
+ } else {
+ flipped = qfalse;
+ }
+
+ if (width)
+ *width = bcWidth;
+ if (height)
+ *height = bcHeight;
+
+ if (!pic) {
+ fclose (fin);
+ return;
+ }
+
+ out = malloc ( bcWidth * bcHeight );
+ *pic = out;
+ fseek (fin, bfOffBits, SEEK_SET);
+
+ if (flipped) {
+ for (i = 0 ; i < bcHeight ; i++) {
+ fread (out + bcWidth * (bcHeight - 1 - i), 1, bcWidth, fin);
+ }
+ } else {
+ fread (out, 1, bcWidth*bcHeight, fin);
+ }
+
+ fclose (fin);
+}
+
+
+/*
+============================================================================
+
+LOAD IMAGE
+
+============================================================================
+*/
+
+/*
+==============
+Load256Image
+
+Will load either an lbm or pcx, depending on extension.
+Any of the return pointers can be NULL if you don't want them.
+==============
+*/
+void Load256Image (const char *name, byte **pixels, byte **palette,
+ int *width, int *height)
+{
+ char ext[128];
+
+ ExtractFileExtension (name, ext);
+ if (!Q_stricmp (ext, "lbm"))
+ {
+ LoadLBM (name, pixels, palette);
+ if (width)
+ *width = bmhd.w;
+ if (height)
+ *height = bmhd.h;
+ }
+ else if (!Q_stricmp (ext, "pcx"))
+ {
+ LoadPCX (name, pixels, palette, width, height);
+ }
+ else if (!Q_stricmp (ext, "bmp"))
+ {
+ LoadBMP (name, pixels, palette, width, height);
+ }
+ else
+ Error ("%s doesn't have a known image extension", name);
+}
+
+
+/*
+==============
+Save256Image
+
+Will save either an lbm or pcx, depending on extension.
+==============
+*/
+void Save256Image (const char *name, byte *pixels, byte *palette,
+ int width, int height)
+{
+ char ext[128];
+
+ ExtractFileExtension (name, ext);
+ if (!Q_stricmp (ext, "lbm"))
+ {
+ WriteLBMfile (name, pixels, width, height, palette);
+ }
+ else if (!Q_stricmp (ext, "pcx"))
+ {
+ WritePCXfile (name, pixels, width, height, palette);
+ }
+ else
+ Error ("%s doesn't have a known image extension", name);
+}
+
+
+
+
+/*
+============================================================================
+
+TARGA IMAGE
+
+============================================================================
+*/
+
+typedef struct _TargaHeader {
+ unsigned char id_length, colormap_type, image_type;
+ unsigned short colormap_index, colormap_length;
+ unsigned char colormap_size;
+ unsigned short x_origin, y_origin, width, height;
+ unsigned char pixel_size, attributes;
+} TargaHeader;
+
+/*
+=============
+LoadTGABuffer
+=============
+*/
+void LoadTGABuffer ( byte *buffer, byte **pic, int *width, int *height)
+{
+ int columns, rows, numPixels;
+ byte *pixbuf;
+ int row, column;
+ byte *buf_p;
+ TargaHeader targa_header;
+ byte *targa_rgba;
+
+ *pic = NULL;
+
+ buf_p = buffer;
+
+ targa_header.id_length = *buf_p++;
+ targa_header.colormap_type = *buf_p++;
+ targa_header.image_type = *buf_p++;
+
+ targa_header.colormap_index = LittleShort ( *(short *)buf_p );
+ buf_p += 2;
+ targa_header.colormap_length = LittleShort ( *(short *)buf_p );
+ buf_p += 2;
+ targa_header.colormap_size = *buf_p++;
+ targa_header.x_origin = LittleShort ( *(short *)buf_p );
+ buf_p += 2;
+ targa_header.y_origin = LittleShort ( *(short *)buf_p );
+ buf_p += 2;
+ targa_header.width = LittleShort ( *(short *)buf_p );
+ buf_p += 2;
+ targa_header.height = LittleShort ( *(short *)buf_p );
+ buf_p += 2;
+ targa_header.pixel_size = *buf_p++;
+ targa_header.attributes = *buf_p++;
+
+ if (targa_header.image_type!=2
+ && targa_header.image_type!=10
+ && targa_header.image_type != 3 )
+ {
+ Error("LoadTGA: Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported\n");
+ }
+
+ if ( targa_header.colormap_type != 0 )
+ {
+ Error("LoadTGA: colormaps not supported\n" );
+ }
+
+ if ( ( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) && targa_header.image_type != 3 )
+ {
+ Error("LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n");
+ }
+
+ columns = targa_header.width;
+ rows = targa_header.height;
+ numPixels = columns * rows;
+
+ if (width)
+ *width = columns;
+ if (height)
+ *height = rows;
+
+ targa_rgba = malloc (numPixels*4);
+ *pic = targa_rgba;
+
+ if (targa_header.id_length != 0)
+ buf_p += targa_header.id_length; // skip TARGA image comment
+
+ if ( targa_header.image_type==2 || targa_header.image_type == 3 )
+ {
+ // Uncompressed RGB or gray scale image
+ for(row=rows-1; row>=0; row--)
+ {
+ pixbuf = targa_rgba + row*columns*4;
+ for(column=0; column<columns; column++)
+ {
+ unsigned char red,green,blue,alphabyte;
+ switch (targa_header.pixel_size)
+ {
+
+ case 8:
+ blue = *buf_p++;
+ green = blue;
+ red = blue;
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = 255;
+ break;
+
+ case 24:
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = 255;
+ break;
+ case 32:
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ alphabyte = *buf_p++;
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = alphabyte;
+ break;
+ default:
+ //Error("LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );
+ break;
+ }
+ }
+ }
+ }
+ else if (targa_header.image_type==10) { // Runlength encoded RGB images
+ unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
+
+ red = 0;
+ green = 0;
+ blue = 0;
+ alphabyte = 0xff;
+
+ for(row=rows-1; row>=0; row--) {
+ pixbuf = targa_rgba + row*columns*4;
+ for(column=0; column<columns; ) {
+ packetHeader= *buf_p++;
+ packetSize = 1 + (packetHeader & 0x7f);
+ if (packetHeader & 0x80) { // run-length packet
+ switch (targa_header.pixel_size) {
+ case 24:
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ alphabyte = 255;
+ break;
+ case 32:
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ alphabyte = *buf_p++;
+ break;
+ default:
+ //Error("LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );
+ break;
+ }
+
+ for(j=0;j<packetSize;j++) {
+ *pixbuf++=red;
+ *pixbuf++=green;
+ *pixbuf++=blue;
+ *pixbuf++=alphabyte;
+ column++;
+ if (column==columns) { // run spans across rows
+ column=0;
+ if (row>0)
+ row--;
+ else
+ goto breakOut;
+ pixbuf = targa_rgba + row*columns*4;
+ }
+ }
+ }
+ else { // non run-length packet
+ for(j=0;j<packetSize;j++) {
+ switch (targa_header.pixel_size) {
+ case 24:
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = 255;
+ break;
+ case 32:
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ alphabyte = *buf_p++;
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = alphabyte;
+ break;
+ default:
+ //Sys_Printf("LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );
+ break;
+ }
+ column++;
+ if (column==columns) { // pixel packet run spans across rows
+ column=0;
+ if (row>0)
+ row--;
+ else
+ goto breakOut;
+ pixbuf = targa_rgba + row*columns*4;
+ }
+ }
+ }
+ }
+ breakOut:;
+ }
+ }
+
+ //free(buffer);
+}
+
+
+
+/*
+=============
+LoadTGA
+=============
+*/
+void LoadTGA (const char *name, byte **pixels, int *width, int *height)
+{
+ byte *buffer;
+ int nLen;
+ //
+ // load the file
+ //
+ nLen = LoadFile ( ( char * ) name, (void **)&buffer);
+ if (nLen == -1)
+ {
+ Error ("Couldn't read %s", name);
+ }
+
+ LoadTGABuffer(buffer, pixels, width, height);
+
+}
+
+
+/*
+================
+WriteTGA
+================
+*/
+void WriteTGA (const char *filename, byte *data, int width, int height) {
+ byte *buffer;
+ int i;
+ int c;
+ FILE *f;
+
+ buffer = malloc(width*height*4 + 18);
+ memset (buffer, 0, 18);
+ buffer[2] = 2; // uncompressed type
+ buffer[12] = width&255;
+ buffer[13] = width>>8;
+ buffer[14] = height&255;
+ buffer[15] = height>>8;
+ buffer[16] = 32; // pixel size
+
+ // swap rgb to bgr
+ c = 18 + width * height * 4;
+ for (i=18 ; i<c ; i+=4)
+ {
+ buffer[i] = data[i-18+2]; // blue
+ buffer[i+1] = data[i-18+1]; // green
+ buffer[i+2] = data[i-18+0]; // red
+ buffer[i+3] = data[i-18+3]; // alpha
+ }
+
+ f = fopen (filename, "wb");
+ fwrite (buffer, 1, c, f);
+ fclose (f);
+
+ free (buffer);
+}
+
+/*
+============================================================================
+
+LOAD32BITIMAGE
+
+============================================================================
+*/
+
+/*
+==============
+Load32BitImage
+
+Any of the return pointers can be NULL if you don't want them.
+==============
+*/
+void Load32BitImage (const char *name, unsigned **pixels, int *width, int *height)
+{
+ char ext[128];
+ byte *palette;
+ byte *pixels8;
+ byte *pixels32;
+ int size;
+ int i;
+ int v;
+
+ ExtractFileExtension (name, ext);
+ if (!Q_stricmp (ext, "tga")) {
+ LoadTGA (name, (byte **)pixels, width, height);
+ } else {
+ Load256Image (name, &pixels8, &palette, width, height);
+ if (!pixels) {
+ return;
+ }
+ size = *width * *height;
+ pixels32 = malloc(size * 4);
+ *pixels = (unsigned *)pixels32;
+ for (i = 0 ; i < size ; i++) {
+ v = pixels8[i];
+ pixels32[i*4 + 0] = palette[ v * 3 + 0 ];
+ pixels32[i*4 + 1] = palette[ v * 3 + 1 ];
+ pixels32[i*4 + 2] = palette[ v * 3 + 2 ];
+ pixels32[i*4 + 3] = 0xff;
+ }
+ }
+}
+
+
diff --git a/common/imagelib.h b/common/imagelib.h
new file mode 100755
index 0000000..57287ec
--- /dev/null
+++ b/common/imagelib.h
@@ -0,0 +1,44 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+// piclib.h
+
+
+void LoadLBM (const char *filename, byte **picture, byte **palette);
+void WriteLBMfile (const char *filename, byte *data, int width, int height
+ , byte *palette);
+void LoadPCX (const char *filename, byte **picture, byte **palette, int *width, int *height);
+void WritePCXfile (const char *filename, byte *data, int width, int height
+ , byte *palette);
+
+// loads / saves either lbm or pcx, depending on extension
+void Load256Image (const char *name, byte **pixels, byte **palette,
+ int *width, int *height);
+void Save256Image (const char *name, byte *pixels, byte *palette,
+ int width, int height);
+
+
+void LoadTGA (const char *filename, byte **pixels, int *width, int *height);
+void LoadTGABuffer ( byte *buffer, byte **pic, int *width, int *height);
+void WriteTGA (const char *filename, byte *data, int width, int height);
+
+void Load32BitImage (const char *name, unsigned **pixels, int *width, int *height);
+
diff --git a/common/l3dslib.c b/common/l3dslib.c
new file mode 100755
index 0000000..0faed60
--- /dev/null
+++ b/common/l3dslib.c
@@ -0,0 +1,300 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+// l3dslib.c: library for loading triangles from an Alias triangle file
+//
+
+#include <stdio.h>
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "trilib.h"
+#include "l3dslib.h"
+
+#define MAIN3DS 0x4D4D
+#define EDIT3DS 0x3D3D // this is the start of the editor config
+#define EDIT_OBJECT 0x4000
+#define OBJ_TRIMESH 0x4100
+#define TRI_VERTEXL 0x4110
+#define TRI_FACEL1 0x4120
+
+#define MAXVERTS 2000
+
+typedef struct {
+ int v[4];
+} tri;
+
+float fverts[MAXVERTS][3];
+tri tris[MAXTRIANGLES];
+
+int bytesread, level, numtris, totaltris;
+int vertsfound, trisfound;
+
+triangle_t *ptri;
+
+
+// Alias stores triangles as 3 explicit vertices in .tri files, so even though we
+// start out with a vertex pool and vertex indices for triangles, we have to convert
+// to raw, explicit triangles
+void StoreAliasTriangles (void)
+{
+ int i, j, k;
+
+ if ((totaltris + numtris) > MAXTRIANGLES)
+ Error ("Error: Too many triangles");
+
+ for (i=0; i<numtris ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ for (k=0 ; k<3 ; k++)
+ {
+ ptri[i+totaltris].verts[j][k] = fverts[tris[i].v[j]][k];
+ }
+ }
+ }
+
+ totaltris += numtris;
+ numtris = 0;
+ vertsfound = 0;
+ trisfound = 0;
+}
+
+
+int ParseVertexL (FILE *input)
+{
+ int i, j, startbytesread, numverts;
+ unsigned short tshort;
+
+ if (vertsfound)
+ Error ("Error: Multiple vertex chunks");
+
+ vertsfound = 1;
+ startbytesread = bytesread;
+
+ if (feof(input))
+ Error ("Error: unexpected end of file");
+
+ fread(&tshort, sizeof(tshort), 1, input);
+ bytesread += sizeof(tshort);
+ numverts = (int)tshort;
+
+ if (numverts > MAXVERTS)
+ Error ("Error: Too many vertices");
+
+ for (i=0 ; i<numverts ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ if (feof(input))
+ Error ("Error: unexpected end of file");
+
+ fread(&fverts[i][j], sizeof(float), 1, input);
+ bytesread += sizeof(float);
+ }
+ }
+
+ if (vertsfound && trisfound)
+ StoreAliasTriangles ();
+
+ return bytesread - startbytesread;
+}
+
+
+int ParseFaceL1 (FILE *input)
+{
+
+ int i, j, startbytesread;
+ unsigned short tshort;
+
+ if (trisfound)
+ Error ("Error: Multiple face chunks");
+
+ trisfound = 1;
+ startbytesread = bytesread;
+
+ if (feof(input))
+ Error ("Error: unexpected end of file");
+
+ fread(&tshort, sizeof(tshort), 1, input);
+ bytesread += sizeof(tshort);
+ numtris = (int)tshort;
+
+ if (numtris > MAXTRIANGLES)
+ Error ("Error: Too many triangles");
+
+ for (i=0 ; i<numtris ; i++)
+ {
+ for (j=0 ; j<4 ; j++)
+ {
+ if (feof(input))
+ Error ("Error: unexpected end of file");
+
+ fread(&tshort, sizeof(tshort), 1, input);
+ bytesread += sizeof(tshort);
+ tris[i].v[j] = (int)tshort;
+ }
+ }
+
+ if (vertsfound && trisfound)
+ StoreAliasTriangles ();
+
+ return bytesread - startbytesread;
+}
+
+
+int ParseChunk (FILE *input)
+{
+#define BLOCK_SIZE 4096
+ char temp[BLOCK_SIZE];
+ unsigned short type;
+ int i, length, w, t, retval;
+
+ level++;
+ retval = 0;
+
+// chunk type
+ if (feof(input))
+ Error ("Error: unexpected end of file");
+
+ fread(&type, sizeof(type), 1, input);
+ bytesread += sizeof(type);
+
+// chunk length
+ if (feof(input))
+ Error ("Error: unexpected end of file");
+
+ fread (&length, sizeof(length), 1, input);
+ bytesread += sizeof(length);
+ w = length - 6;
+
+// process chunk if we care about it, otherwise skip it
+ switch (type)
+ {
+ case TRI_VERTEXL:
+ w -= ParseVertexL (input);
+ goto ParseSubchunk;
+
+ case TRI_FACEL1:
+ w -= ParseFaceL1 (input);
+ goto ParseSubchunk;
+
+ case EDIT_OBJECT:
+ // read the name
+ i = 0;
+
+ do
+ {
+ if (feof(input))
+ Error ("Error: unexpected end of file");
+
+ fread (&temp[i], 1, 1, input);
+ i++;
+ w--;
+ bytesread++;
+ } while (temp[i-1]);
+
+ case MAIN3DS:
+ case OBJ_TRIMESH:
+ case EDIT3DS:
+ // parse through subchunks
+ParseSubchunk:
+ while (w > 0)
+ {
+ w -= ParseChunk (input);
+ }
+
+ retval = length;
+ goto Done;
+
+ default:
+ // skip other chunks
+ while (w > 0)
+ {
+ t = w;
+
+ if (t > BLOCK_SIZE)
+ t = BLOCK_SIZE;
+
+ if (feof(input))
+ Error ("Error: unexpected end of file");
+
+ fread (&temp, t, 1, input);
+ bytesread += t;
+
+ w -= t;
+ }
+
+ retval = length;
+ goto Done;
+ }
+
+Done:
+ level--;
+ return retval;
+}
+
+
+void Load3DSTriangleList (char *filename, triangle_t **pptri, int *numtriangles)
+{
+ FILE *input;
+ short int tshort;
+
+ bytesread = 0;
+ level = 0;
+ numtris = 0;
+ totaltris = 0;
+ vertsfound = 0;
+ trisfound = 0;
+
+ if ((input = fopen(filename, "rb")) == 0) {
+ fprintf(stderr,"reader: could not open file '%s'\n", filename);
+ exit(0);
+ }
+
+ fread(&tshort, sizeof(tshort), 1, input);
+
+// should only be MAIN3DS, but some files seem to start with EDIT3DS, with
+// no MAIN3DS
+ if ((tshort != MAIN3DS) && (tshort != EDIT3DS)) {
+ fprintf(stderr,"File is not a 3DS file.\n");
+ exit(0);
+ }
+
+// back to top of file so we can parse the first chunk descriptor
+ fseek(input, 0, SEEK_SET);
+
+ ptri = malloc (MAXTRIANGLES * sizeof(triangle_t));
+
+ *pptri = ptri;
+
+// parse through looking for the relevant chunk tree (MAIN3DS | EDIT3DS | EDIT_OBJECT |
+// OBJ_TRIMESH | {TRI_VERTEXL, TRI_FACEL1}) and skipping other chunks
+ ParseChunk (input);
+
+ if (vertsfound || trisfound)
+ Error ("Incomplete triangle set");
+
+ *numtriangles = totaltris;
+
+ fclose (input);
+}
+
diff --git a/common/l3dslib.h b/common/l3dslib.h
new file mode 100755
index 0000000..c98524c
--- /dev/null
+++ b/common/l3dslib.h
@@ -0,0 +1,26 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+// l3dslib.h: header file for loading triangles from a 3DS triangle file
+//
+void Load3DSTriangleList (char *filename, triangle_t **pptri, int *numtriangles);
+
diff --git a/common/mathlib.c b/common/mathlib.c
new file mode 100755
index 0000000..1435024
--- /dev/null
+++ b/common/mathlib.c
@@ -0,0 +1,434 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+// mathlib.c -- math primitives
+
+#include "cmdlib.h"
+#include "mathlib.h"
+
+#ifdef _WIN32
+//Improve floating-point consistency.
+//without this option weird floating point issues occur
+#pragma optimize( "p", on )
+#endif
+
+
+vec3_t vec3_origin = {0,0,0};
+
+/*
+** NormalToLatLong
+**
+** We use two byte encoded normals in some space critical applications.
+** Lat = 0 at (1,0,0) to 360 (-1,0,0), encoded in 8-bit sine table format
+** Lng = 0 at (0,0,1) to 180 (0,0,-1), encoded in 8-bit sine table format
+**
+*/
+void NormalToLatLong( const vec3_t normal, byte bytes[2] ) {
+ // check for singularities
+ if ( normal[0] == 0 && normal[1] == 0 ) {
+ if ( normal[2] > 0 ) {
+ bytes[0] = 0;
+ bytes[1] = 0; // lat = 0, long = 0
+ } else {
+ bytes[0] = 128;
+ bytes[1] = 0; // lat = 0, long = 128
+ }
+ } else {
+ int a, b;
+
+ a = RAD2DEG( atan2( normal[1], normal[0] ) ) * (255.0f / 360.0f );
+ a &= 0xff;
+
+ b = RAD2DEG( acos( normal[2] ) ) * ( 255.0f / 360.0f );
+ b &= 0xff;
+
+ bytes[0] = b; // longitude
+ bytes[1] = a; // lattitude
+ }
+}
+
+/*
+=====================
+PlaneFromPoints
+
+Returns false if the triangle is degenrate.
+The normal will point out of the clock for clockwise ordered points
+=====================
+*/
+qboolean PlaneFromPoints( vec4_t plane, const vec3_t a, const vec3_t b, const vec3_t c ) {
+ vec3_t d1, d2;
+
+ VectorSubtract( b, a, d1 );
+ VectorSubtract( c, a, d2 );
+ CrossProduct( d2, d1, plane );
+ if ( VectorNormalize( plane, plane ) == 0 ) {
+ return qfalse;
+ }
+
+ plane[3] = DotProduct( a, plane );
+ return qtrue;
+}
+
+/*
+================
+MakeNormalVectors
+
+Given a normalized forward vector, create two
+other perpendicular vectors
+================
+*/
+void MakeNormalVectors (vec3_t forward, vec3_t right, vec3_t up)
+{
+ float d;
+
+ // this rotate and negate guarantees a vector
+ // not colinear with the original
+ right[1] = -forward[0];
+ right[2] = forward[1];
+ right[0] = forward[2];
+
+ d = DotProduct (right, forward);
+ VectorMA (right, -d, forward, right);
+ VectorNormalize (right, right);
+ CrossProduct (right, forward, up);
+}
+
+
+void Vec10Copy( vec_t *in, vec_t *out ) {
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ out[3] = in[3];
+ out[4] = in[4];
+ out[5] = in[5];
+ out[6] = in[6];
+ out[7] = in[7];
+ out[8] = in[8];
+ out[9] = in[9];
+}
+
+
+void VectorRotate3x3( vec3_t v, float r[3][3], vec3_t d )
+{
+ d[0] = v[0] * r[0][0] + v[1] * r[1][0] + v[2] * r[2][0];
+ d[1] = v[0] * r[0][1] + v[1] * r[1][1] + v[2] * r[2][1];
+ d[2] = v[0] * r[0][2] + v[1] * r[1][2] + v[2] * r[2][2];
+}
+
+double VectorLength( const 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( const vec3_t v1, const vec3_t v2 ) {
+ int i;
+
+ for (i=0 ; i<3 ; i++)
+ if (fabs(v1[i]-v2[i]) > EQUAL_EPSILON)
+ return qfalse;
+
+ return qtrue;
+}
+
+vec_t Q_rint (vec_t in)
+{
+ return floor (in + 0.5);
+}
+
+void VectorMA( const vec3_t va, double scale, const 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( const vec3_t v1, const 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];
+}
+
+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;
+}
+
+vec_t VectorNormalize( const vec3_t in, vec3_t out ) {
+ vec_t length, ilength;
+
+ length = sqrt (in[0]*in[0] + in[1]*in[1] + in[2]*in[2]);
+ if (length == 0)
+ {
+ VectorClear (out);
+ return 0;
+ }
+
+ ilength = 1.0/length;
+ out[0] = in[0]*ilength;
+ out[1] = in[1]*ilength;
+ out[2] = in[2]*ilength;
+
+ return length;
+}
+
+vec_t ColorNormalize( const vec3_t in, vec3_t out ) {
+ float max, scale;
+
+ max = in[0];
+ if (in[1] > max)
+ max = in[1];
+ if (in[2] > max)
+ max = in[2];
+
+ if (max == 0) {
+ out[0] = out[1] = out[2] = 1.0;
+ return 0;
+ }
+
+ scale = 1.0 / max;
+
+ VectorScale (in, scale, out);
+
+ return max;
+}
+
+
+
+void VectorInverse (vec3_t v)
+{
+ v[0] = -v[0];
+ v[1] = -v[1];
+ v[2] = -v[2];
+}
+
+void ClearBounds (vec3_t mins, vec3_t maxs)
+{
+ mins[0] = mins[1] = mins[2] = 99999;
+ maxs[0] = maxs[1] = maxs[2] = -99999;
+}
+
+void AddPointToBounds( const vec3_t v, vec3_t mins, vec3_t maxs ) {
+ int i;
+ vec_t val;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ val = v[i];
+ if (val < mins[i])
+ mins[i] = val;
+ if (val > maxs[i])
+ maxs[i] = val;
+ }
+}
+
+
+/*
+=================
+PlaneTypeForNormal
+=================
+*/
+int PlaneTypeForNormal (vec3_t normal) {
+ if (normal[0] == 1.0 || normal[0] == -1.0)
+ return PLANE_X;
+ if (normal[1] == 1.0 || normal[1] == -1.0)
+ return PLANE_Y;
+ if (normal[2] == 1.0 || normal[2] == -1.0)
+ return PLANE_Z;
+
+ return PLANE_NON_AXIAL;
+}
+
+/*
+================
+MatrixMultiply
+================
+*/
+void MatrixMultiply(float in1[3][3], float in2[3][3], float out[3][3]) {
+ out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] +
+ in1[0][2] * in2[2][0];
+ out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] +
+ in1[0][2] * in2[2][1];
+ out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] +
+ in1[0][2] * in2[2][2];
+ out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +
+ in1[1][2] * in2[2][0];
+ out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +
+ in1[1][2] * in2[2][1];
+ out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] +
+ in1[1][2] * in2[2][2];
+ out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] +
+ in1[2][2] * in2[2][0];
+ out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] +
+ in1[2][2] * in2[2][1];
+ out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] +
+ in1[2][2] * in2[2][2];
+}
+
+void ProjectPointOnPlane( vec3_t dst, const vec3_t p, const vec3_t normal )
+{
+ float d;
+ vec3_t n;
+ float inv_denom;
+
+ inv_denom = 1.0F / DotProduct( normal, normal );
+
+ d = DotProduct( normal, p ) * inv_denom;
+
+ n[0] = normal[0] * inv_denom;
+ n[1] = normal[1] * inv_denom;
+ n[2] = normal[2] * inv_denom;
+
+ dst[0] = p[0] - d * n[0];
+ dst[1] = p[1] - d * n[1];
+ dst[2] = p[2] - d * n[2];
+}
+
+/*
+** assumes "src" is normalized
+*/
+void PerpendicularVector( vec3_t dst, const vec3_t src )
+{
+ int pos;
+ int i;
+ float minelem = 1.0F;
+ vec3_t tempvec;
+
+ /*
+ ** find the smallest magnitude axially aligned vector
+ */
+ for ( pos = 0, i = 0; i < 3; i++ )
+ {
+ if ( fabs( src[i] ) < minelem )
+ {
+ pos = i;
+ minelem = fabs( src[i] );
+ }
+ }
+ tempvec[0] = tempvec[1] = tempvec[2] = 0.0F;
+ tempvec[pos] = 1.0F;
+
+ /*
+ ** project the point onto the plane defined by src
+ */
+ ProjectPointOnPlane( dst, tempvec, src );
+
+ /*
+ ** normalize the result
+ */
+ VectorNormalize( dst, dst );
+}
+
+/*
+===============
+RotatePointAroundVector
+
+This is not implemented very well...
+===============
+*/
+void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point,
+ float degrees ) {
+ float m[3][3];
+ float im[3][3];
+ float zrot[3][3];
+ float tmpmat[3][3];
+ float rot[3][3];
+ int i;
+ vec3_t vr, vup, vf;
+ float rad;
+
+ vf[0] = dir[0];
+ vf[1] = dir[1];
+ vf[2] = dir[2];
+
+ PerpendicularVector( vr, dir );
+ CrossProduct( vr, vf, vup );
+
+ m[0][0] = vr[0];
+ m[1][0] = vr[1];
+ m[2][0] = vr[2];
+
+ m[0][1] = vup[0];
+ m[1][1] = vup[1];
+ m[2][1] = vup[2];
+
+ m[0][2] = vf[0];
+ m[1][2] = vf[1];
+ m[2][2] = vf[2];
+
+ memcpy( im, m, sizeof( im ) );
+
+ im[0][1] = m[1][0];
+ im[0][2] = m[2][0];
+ im[1][0] = m[0][1];
+ im[1][2] = m[2][1];
+ im[2][0] = m[0][2];
+ im[2][1] = m[1][2];
+
+ memset( zrot, 0, sizeof( zrot ) );
+ zrot[0][0] = zrot[1][1] = zrot[2][2] = 1.0F;
+
+ rad = DEG2RAD( degrees );
+ zrot[0][0] = cos( rad );
+ zrot[0][1] = sin( rad );
+ zrot[1][0] = -sin( rad );
+ zrot[1][1] = cos( rad );
+
+ MatrixMultiply( m, zrot, tmpmat );
+ MatrixMultiply( tmpmat, im, rot );
+
+ for ( i = 0; i < 3; i++ ) {
+ dst[i] = rot[i][0] * point[0] + rot[i][1] * point[1] + rot[i][2] * point[2];
+ }
+}
diff --git a/common/mathlib.h b/common/mathlib.h
new file mode 100755
index 0000000..7bd7158
--- /dev/null
+++ b/common/mathlib.h
@@ -0,0 +1,96 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+#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 vec2_t[3];
+typedef vec_t vec3_t[3];
+typedef vec_t vec4_t[4];
+
+#define SIDE_FRONT 0
+#define SIDE_ON 2
+#define SIDE_BACK 1
+#define SIDE_CROSS -2
+
+#define Q_PI 3.14159265358979323846
+#define DEG2RAD( a ) ( ( (a) * Q_PI ) / 180.0F )
+#define RAD2DEG( a ) ( ( (a) * 180.0f ) / Q_PI )
+
+extern vec3_t vec3_origin;
+
+#define EQUAL_EPSILON 0.001
+
+// plane types are used to speed some tests
+// 0-2 are axial planes
+#define PLANE_X 0
+#define PLANE_Y 1
+#define PLANE_Z 2
+#define PLANE_NON_AXIAL 3
+
+qboolean VectorCompare( const vec3_t v1, const 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];}
+#define VectorScale(a,b,c) {c[0]=b*a[0];c[1]=b*a[1];c[2]=b*a[2];}
+#define VectorClear(x) {x[0] = x[1] = x[2] = 0;}
+#define VectorNegate(x) {x[0]=-x[0];x[1]=-x[1];x[2]=-x[2];}
+void Vec10Copy( vec_t *in, vec_t *out );
+
+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);
+void _VectorScale (vec3_t v, vec_t scale, vec3_t out);
+
+double VectorLength( const vec3_t v );
+
+void VectorMA( const vec3_t va, double scale, const vec3_t vb, vec3_t vc );
+
+void CrossProduct( const vec3_t v1, const vec3_t v2, vec3_t cross );
+vec_t VectorNormalize( const vec3_t in, vec3_t out );
+vec_t ColorNormalize( const vec3_t in, vec3_t out );
+void VectorInverse (vec3_t v);
+
+void ClearBounds (vec3_t mins, vec3_t maxs);
+void AddPointToBounds( const vec3_t v, vec3_t mins, vec3_t maxs );
+
+qboolean PlaneFromPoints( vec4_t plane, const vec3_t a, const vec3_t b, const vec3_t c );
+
+void NormalToLatLong( const vec3_t normal, byte bytes[2] );
+
+int PlaneTypeForNormal (vec3_t normal);
+
+void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point,
+ float degrees );
+#endif
diff --git a/common/md4.c b/common/md4.c
new file mode 100755
index 0000000..37de7f7
--- /dev/null
+++ b/common/md4.c
@@ -0,0 +1,277 @@
+/* GLOBAL.H - RSAREF types and constants */
+
+#include <string.h>
+
+/* POINTER defines a generic pointer type */
+typedef unsigned char *POINTER;
+
+/* UINT2 defines a two byte word */
+typedef unsigned short int UINT2;
+
+/* UINT4 defines a four byte word */
+typedef unsigned long int UINT4;
+
+
+/* MD4.H - header file for MD4C.C */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991.
+
+All rights reserved.
+
+License to copy and use this software is granted provided that it is identified as the “RSA Data Security, Inc. MD4 Message-Digest Algorithm” in all material mentioning or referencing this software or this function.
+License is also granted to make and use derivative works provided that such works are identified as “derived from the RSA Data Security, Inc. MD4 Message-Digest Algorithm” in all material mentioning or referencing the derived work.
+RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided “as is” without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this documentation and/or software. */
+
+/* MD4 context. */
+typedef struct {
+ UINT4 state[4]; /* state (ABCD) */
+ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ unsigned char buffer[64]; /* input buffer */
+} MD4_CTX;
+
+void MD4Init (MD4_CTX *);
+void MD4Update (MD4_CTX *, unsigned char *, unsigned int);
+void MD4Final (unsigned char [16], MD4_CTX *);
+
+
+
+/* MD4C.C - RSA Data Security, Inc., MD4 message-digest algorithm */
+/* Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved.
+
+License to copy and use this software is granted provided that it is identified as the
+RSA Data Security, Inc. MD4 Message-Digest Algorithm
+ in all material mentioning or referencing this software or this function.
+License is also granted to make and use derivative works provided that such works are identified as
+derived from the RSA Data Security, Inc. MD4 Message-Digest Algorithm
+in all material mentioning or referencing the derived work.
+RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided
+as is without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this documentation and/or software. */
+
+/* Constants for MD4Transform routine. */
+#define S11 3
+#define S12 7
+#define S13 11
+#define S14 19
+#define S21 3
+#define S22 5
+#define S23 9
+#define S24 13
+#define S31 3
+#define S32 9
+#define S33 11
+#define S34 15
+
+static void MD4Transform (UINT4 [4], unsigned char [64]);
+static void Encode (unsigned char *, UINT4 *, unsigned int);
+static void Decode (UINT4 *, unsigned char *, unsigned int);
+static void MD4_memcpy (POINTER, POINTER, unsigned int);
+static void MD4_memset (POINTER, int, unsigned int);
+
+static unsigned char PADDING[64] = {
+0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G and H are basic MD4 functions. */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+
+/* ROTATE_LEFT rotates x left n bits. */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG and HH are transformations for rounds 1, 2 and 3 */
+/* Rotation is separate from addition to prevent recomputation */
+#define FF(a, b, c, d, x, s) {(a) += F ((b), (c), (d)) + (x); (a) = ROTATE_LEFT ((a), (s));}
+
+#define GG(a, b, c, d, x, s) {(a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; (a) = ROTATE_LEFT ((a), (s));}
+
+#define HH(a, b, c, d, x, s) {(a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; (a) = \
+ROTATE_LEFT ((a), (s)); }
+
+
+/* MD4 initialization. Begins an MD4 operation, writing a new context. */
+void MD4Init (MD4_CTX *context)
+{
+ context->count[0] = context->count[1] = 0;
+
+/* Load magic initialization constants.*/
+context->state[0] = 0x67452301;
+context->state[1] = 0xefcdab89;
+context->state[2] = 0x98badcfe;
+context->state[3] = 0x10325476;
+}
+
+/* MD4 block update operation. Continues an MD4 message-digest operation, processing another message block, and updating the context. */
+void MD4Update (MD4_CTX *context, unsigned char *input, unsigned int inputLen)
+{
+ unsigned int i, index, partLen;
+
+ /* Compute number of bytes mod 64 */
+ index = (unsigned int)((context->count[0] >> 3) & 0x3F);
+
+ /* Update number of bits */
+ if ((context->count[0] += ((UINT4)inputLen << 3))< ((UINT4)inputLen << 3))
+ context->count[1]++;
+
+ context->count[1] += ((UINT4)inputLen >> 29);
+
+ partLen = 64 - index;
+
+ /* Transform as many times as possible.*/
+ if (inputLen >= partLen)
+ {
+ memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen);
+ MD4Transform (context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ MD4Transform (context->state, &input[i]);
+
+ index = 0;
+ }
+ else
+ i = 0;
+
+ /* Buffer remaining input */
+ memcpy ((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i);
+}
+
+
+/* MD4 finalization. Ends an MD4 message-digest operation, writing the the message digest and zeroizing the context. */
+void MD4Final (unsigned char digest[16], MD4_CTX *context)
+{
+ unsigned char bits[8];
+ unsigned int index, padLen;
+
+ /* Save number of bits */
+ Encode (bits, context->count, 8);
+
+ /* Pad out to 56 mod 64.*/
+ index = (unsigned int)((context->count[0] >> 3) & 0x3f);
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ MD4Update (context, PADDING, padLen);
+
+ /* Append length (before padding) */
+ MD4Update (context, bits, 8);
+
+ /* Store state in digest */
+ Encode (digest, context->state, 16);
+
+ /* Zeroize sensitive information.*/
+ memset ((POINTER)context, 0, sizeof (*context));
+}
+
+
+/* MD4 basic transformation. Transforms state based on block. */
+static void MD4Transform (UINT4 state[4], unsigned char block[64])
+{
+ UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+ Decode (x, block, 64);
+
+/* Round 1 */
+FF (a, b, c, d, x[ 0], S11); /* 1 */
+FF (d, a, b, c, x[ 1], S12); /* 2 */
+FF (c, d, a, b, x[ 2], S13); /* 3 */
+FF (b, c, d, a, x[ 3], S14); /* 4 */
+FF (a, b, c, d, x[ 4], S11); /* 5 */
+FF (d, a, b, c, x[ 5], S12); /* 6 */
+FF (c, d, a, b, x[ 6], S13); /* 7 */
+FF (b, c, d, a, x[ 7], S14); /* 8 */
+FF (a, b, c, d, x[ 8], S11); /* 9 */
+FF (d, a, b, c, x[ 9], S12); /* 10 */
+FF (c, d, a, b, x[10], S13); /* 11 */
+FF (b, c, d, a, x[11], S14); /* 12 */
+FF (a, b, c, d, x[12], S11); /* 13 */
+FF (d, a, b, c, x[13], S12); /* 14 */
+FF (c, d, a, b, x[14], S13); /* 15 */
+FF (b, c, d, a, x[15], S14); /* 16 */
+
+/* Round 2 */
+GG (a, b, c, d, x[ 0], S21); /* 17 */
+GG (d, a, b, c, x[ 4], S22); /* 18 */
+GG (c, d, a, b, x[ 8], S23); /* 19 */
+GG (b, c, d, a, x[12], S24); /* 20 */
+GG (a, b, c, d, x[ 1], S21); /* 21 */
+GG (d, a, b, c, x[ 5], S22); /* 22 */
+GG (c, d, a, b, x[ 9], S23); /* 23 */
+GG (b, c, d, a, x[13], S24); /* 24 */
+GG (a, b, c, d, x[ 2], S21); /* 25 */
+GG (d, a, b, c, x[ 6], S22); /* 26 */
+GG (c, d, a, b, x[10], S23); /* 27 */
+GG (b, c, d, a, x[14], S24); /* 28 */
+GG (a, b, c, d, x[ 3], S21); /* 29 */
+GG (d, a, b, c, x[ 7], S22); /* 30 */
+GG (c, d, a, b, x[11], S23); /* 31 */
+GG (b, c, d, a, x[15], S24); /* 32 */
+
+/* Round 3 */
+HH (a, b, c, d, x[ 0], S31); /* 33 */
+HH (d, a, b, c, x[ 8], S32); /* 34 */
+HH (c, d, a, b, x[ 4], S33); /* 35 */
+HH (b, c, d, a, x[12], S34); /* 36 */
+HH (a, b, c, d, x[ 2], S31); /* 37 */
+HH (d, a, b, c, x[10], S32); /* 38 */
+HH (c, d, a, b, x[ 6], S33); /* 39 */
+HH (b, c, d, a, x[14], S34); /* 40 */
+HH (a, b, c, d, x[ 1], S31); /* 41 */
+HH (d, a, b, c, x[ 9], S32); /* 42 */
+HH (c, d, a, b, x[ 5], S33); /* 43 */
+HH (b, c, d, a, x[13], S34); /* 44 */
+HH (a, b, c, d, x[ 3], S31); /* 45 */
+HH (d, a, b, c, x[11], S32); /* 46 */
+HH (c, d, a, b, x[ 7], S33); /* 47 */
+HH (b, c, d, a, x[15], S34); /* 48 */
+
+state[0] += a;
+state[1] += b;
+state[2] += c;
+state[3] += d;
+
+ /* Zeroize sensitive information.*/
+ memset ((POINTER)x, 0, sizeof (x));
+}
+
+
+/* Encodes input (UINT4) into output (unsigned char). Assumes len is a multiple of 4. */
+static void Encode (unsigned char *output, UINT4 *input, unsigned int len)
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[j] = (unsigned char)(input[i] & 0xff);
+ output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+ output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+ output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+ }
+}
+
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is a multiple of 4. */
+static void Decode (UINT4 *output, unsigned char *input, unsigned int len)
+{
+unsigned int i, j;
+
+for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
+}
+
+//===================================================================
+
+unsigned Com_BlockChecksum (void *buffer, int length)
+{
+ int digest[4];
+ unsigned val;
+ MD4_CTX ctx;
+
+ MD4Init (&ctx);
+ MD4Update (&ctx, (unsigned char *)buffer, length);
+ MD4Final ( (unsigned char *)digest, &ctx);
+
+ val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3];
+
+ return val;
+}
diff --git a/common/mutex.c b/common/mutex.c
new file mode 100755
index 0000000..ee16031
--- /dev/null
+++ b/common/mutex.c
@@ -0,0 +1,197 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "threads.h"
+#include "mutex.h"
+
+/*
+===================================================================
+
+WIN32
+
+===================================================================
+*/
+#ifdef WIN32
+
+#define USED
+
+#include <windows.h>
+
+void MutexLock (mutex_t *m)
+{
+ CRITICAL_SECTION *crit;
+
+ if (!m)
+ return;
+ crit = (CRITICAL_SECTION *) m;
+ EnterCriticalSection (crit);
+}
+
+void MutexUnlock (mutex_t *m)
+{
+ CRITICAL_SECTION *crit;
+
+ if (!m)
+ return;
+ crit = (CRITICAL_SECTION *) m;
+ LeaveCriticalSection (crit);
+}
+
+mutex_t *MutexAlloc(void)
+{
+ CRITICAL_SECTION *crit;
+
+ if (numthreads == 1)
+ return NULL;
+ crit = (CRITICAL_SECTION *) malloc(sizeof(CRITICAL_SECTION));
+ InitializeCriticalSection (crit);
+ return (void *) crit;
+}
+
+#endif
+
+/*
+===================================================================
+
+OSF1
+
+===================================================================
+*/
+
+#ifdef __osf__
+#define USED
+
+#include <pthread.h>
+
+void MutexLock (mutex_t *m)
+{
+ pthread_mutex_t *my_mutex;
+
+ if (!m)
+ return;
+ my_mutex = (pthread_mutex_t *) m;
+ pthread_mutex_lock (my_mutex);
+}
+
+void MutexUnlock (mutex_t *m)
+{
+ pthread_mutex_t *my_mutex;
+
+ if (!m)
+ return;
+ my_mutex = (pthread_mutex_t *) m;
+ pthread_mutex_unlock (my_mutex);
+}
+
+mutex_t *MutexAlloc(void)
+{
+ pthread_mutex_t *my_mutex;
+ pthread_mutexattr_t mattrib;
+
+ if (numthreads == 1)
+ return NULL;
+ 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");
+ return (void *) my_mutex;
+}
+
+#endif
+
+/*
+===================================================================
+
+IRIX
+
+===================================================================
+*/
+
+#ifdef _MIPS_ISA
+#define USED
+
+#include <task.h>
+#include <abi_mutex.h>
+#include <sys/types.h>
+#include <sys/prctl.h>
+
+void MutexLock (mutex_t *m)
+{
+ abilock_t *lck;
+
+ if (!m)
+ return;
+ lck = (abilock_t *) m;
+ spin_lock (lck);
+}
+
+void MutexUnlock (mutex_t *m)
+{
+ abilock_t *lck;
+
+ if (!m)
+ return;
+ lck = (abilock_t *) m;
+ release_lock (lck);
+}
+
+mutex_t *MutexAlloc(void)
+{
+ abilock_t *lck;
+
+ if (numthreads == 1)
+ return NULL;
+ lck = (abilock_t *) malloc(sizeof(abilock_t));
+ init_lock (lck);
+ return (void *) lck;
+}
+
+#endif
+
+/*
+=======================================================================
+
+ SINGLE THREAD
+
+=======================================================================
+*/
+
+#ifndef USED
+
+void MutexLock (mutex_t *m)
+{
+}
+
+void MutexUnlock (mutex_t *m)
+{
+}
+
+mutex_t *MutexAlloc(void)
+{
+ return NULL;
+}
+
+#endif
diff --git a/common/mutex.h b/common/mutex.h
new file mode 100755
index 0000000..923f6bf
--- /dev/null
+++ b/common/mutex.h
@@ -0,0 +1,28 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+
+typedef void *mutex_t;
+
+void MutexLock (mutex_t *m);
+void MutexUnlock (mutex_t *m);
+mutex_t *MutexAlloc(void);
diff --git a/common/polylib.c b/common/polylib.c
new file mode 100755
index 0000000..c0e89a7
--- /dev/null
+++ b/common/polylib.c
@@ -0,0 +1,740 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "polylib.h"
+#include "qfiles.h"
+
+
+extern int numthreads;
+
+// counters are only bumped when running single threaded,
+// because they are an awefull coherence problem
+int c_active_windings;
+int c_peak_windings;
+int c_winding_allocs;
+int c_winding_points;
+
+#define BOGUS_RANGE WORLD_SIZE
+
+void pw(winding_t *w)
+{
+ int i;
+ for (i=0 ; i<w->numpoints ; i++)
+ printf ("(%5.1f, %5.1f, %5.1f)\n",w->p[i][0], w->p[i][1],w->p[i][2]);
+}
+
+
+/*
+=============
+AllocWinding
+=============
+*/
+winding_t *AllocWinding (int points)
+{
+ winding_t *w;
+ int s;
+
+ if (numthreads == 1)
+ {
+ c_winding_allocs++;
+ c_winding_points += points;
+ c_active_windings++;
+ if (c_active_windings > c_peak_windings)
+ c_peak_windings = c_active_windings;
+ }
+ s = sizeof(vec_t)*3*points + sizeof(int);
+ w = malloc (s);
+ memset (w, 0, s);
+ return w;
+}
+
+void FreeWinding (winding_t *w)
+{
+ if (*(unsigned *)w == 0xdeaddead)
+ Error ("FreeWinding: freed a freed winding");
+ *(unsigned *)w = 0xdeaddead;
+
+ if (numthreads == 1)
+ c_active_windings--;
+ free (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,v1);
+ VectorNormalize(v2,v2);
+ if (DotProduct(v1, v2) < 0.999)
+ {
+ VectorCopy (w->p[i], p[nump]);
+ nump++;
+ }
+ }
+
+ if (nump == w->numpoints)
+ return;
+
+ if (numthreads == 1)
+ 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 (v2, v1, normal);
+ VectorNormalize (normal, 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;
+}
+
+void WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs)
+{
+ vec_t v;
+ int i,j;
+
+ mins[0] = mins[1] = mins[2] = 99999;
+ maxs[0] = maxs[1] = maxs[2] = -99999;
+
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ v = w->p[i][j];
+ if (v < mins[j])
+ mins[j] = v;
+ if (v > maxs[j])
+ maxs[j] = v;
+ }
+ }
+}
+
+/*
+=============
+WindingCenter
+=============
+*/
+void WindingCenter (winding_t *w, vec3_t center)
+{
+ int i;
+ 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, vec_t 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, vup);
+
+ VectorScale (normal, dist, org);
+
+ CrossProduct (vup, normal, vright);
+
+ VectorScale (vup, MAX_WORLD_COORD, vup);
+ VectorScale (vright, MAX_WORLD_COORD, 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;
+
+ c = AllocWinding (w->numpoints);
+ size = (int)((winding_t *)0)->p[w->numpoints];
+ memcpy (c, w, size);
+ return c;
+}
+
+/*
+==================
+ReverseWinding
+==================
+*/
+winding_t *ReverseWinding (winding_t *w)
+{
+ int i;
+ winding_t *c;
+
+ c = AllocWinding (w->numpoints);
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ VectorCopy (w->p[w->numpoints-1-i], c->p[i]);
+ }
+ c->numpoints = w->numpoints;
+ return c;
+}
+
+
+/*
+=============
+ClipWindingEpsilon
+=============
+*/
+void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist,
+ vec_t epsilon, winding_t **front, winding_t **back)
+{
+ vec_t dists[MAX_POINTS_ON_WINDING+4];
+ int sides[MAX_POINTS_ON_WINDING+4];
+ int counts[3];
+ static vec_t dot; // VC 4.2 optimizer bug if not static
+ 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 > epsilon)
+ sides[i] = SIDE_FRONT;
+ else if (dot < -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; // cant 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");
+}
+
+
+/*
+=============
+ChopWindingInPlace
+=============
+*/
+void ChopWindingInPlace (winding_t **inout, vec3_t normal, vec_t dist, vec_t epsilon)
+{
+ winding_t *in;
+ vec_t dists[MAX_POINTS_ON_WINDING+4];
+ int sides[MAX_POINTS_ON_WINDING+4];
+ int counts[3];
+ static vec_t dot; // VC 4.2 optimizer bug if not static
+ int i, j;
+ vec_t *p1, *p2;
+ vec3_t mid;
+ winding_t *f;
+ int maxpts;
+
+ in = *inout;
+ 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 > epsilon)
+ sides[i] = SIDE_FRONT;
+ else if (dot < -epsilon)
+ sides[i] = SIDE_BACK;
+ else
+ {
+ sides[i] = SIDE_ON;
+ }
+ counts[sides[i]]++;
+ }
+ sides[i] = sides[0];
+ dists[i] = dists[0];
+
+ if (!counts[0])
+ {
+ FreeWinding (in);
+ *inout = NULL;
+ return;
+ }
+ if (!counts[1])
+ return; // inout stays the same
+
+ maxpts = in->numpoints+4; // cant use counts[0]+2 because
+ // of fp grouping errors
+
+ f = 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++;
+ continue;
+ }
+
+ if (sides[i] == SIDE_FRONT)
+ {
+ VectorCopy (p1, f->p[f->numpoints]);
+ f->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++;
+ }
+
+ if (f->numpoints > maxpts)
+ Error ("ClipWinding: points exceeded estimate");
+ if (f->numpoints > MAX_POINTS_ON_WINDING)
+ Error ("ClipWinding: MAX_POINTS_ON_WINDING");
+
+ FreeWinding (in);
+ *inout = f;
+}
+
+
+/*
+=================
+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;
+
+ ClipWindingEpsilon (in, normal, dist, ON_EPSILON, &f, &b);
+ FreeWinding (in);
+ if (b)
+ FreeWinding (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 ("CheckWinding: %i points",w->numpoints);
+
+ area = WindingArea(w);
+ if (area < 1)
+ Error ("CheckWinding: %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] > MAX_WORLD_COORD || p1[j] < MIN_WORLD_COORD)
+ 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 ("CheckWinding: point off plane");
+
+ // check the edge isnt degenerate
+ p2 = w->p[j];
+ VectorSubtract (p2, p1, dir);
+
+ if (VectorLength (dir) < ON_EPSILON)
+ Error ("CheckWinding: degenerate edge");
+
+ CrossProduct (facenormal, dir, edgenormal);
+ VectorNormalize (edgenormal, 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 ("CheckWinding: non-convex");
+ }
+ }
+}
+
+
+/*
+============
+WindingOnPlaneSide
+============
+*/
+int WindingOnPlaneSide (winding_t *w, vec3_t normal, vec_t dist)
+{
+ qboolean front, back;
+ int i;
+ vec_t d;
+
+ front = qfalse;
+ back = qfalse;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ d = DotProduct (w->p[i], normal) - dist;
+ if (d < -ON_EPSILON)
+ {
+ if (front)
+ return SIDE_CROSS;
+ back = qtrue;
+ continue;
+ }
+ if (d > ON_EPSILON)
+ {
+ if (back)
+ return SIDE_CROSS;
+ front = qtrue;
+ continue;
+ }
+ }
+
+ if (back)
+ return SIDE_BACK;
+ if (front)
+ return SIDE_FRONT;
+ return SIDE_ON;
+}
+
+
+/*
+=================
+AddWindingToConvexHull
+
+Both w and *hull are on the same plane
+=================
+*/
+#define MAX_HULL_POINTS 128
+void AddWindingToConvexHull( winding_t *w, winding_t **hull, vec3_t normal ) {
+ int i, j, k;
+ float *p, *copy;
+ vec3_t dir;
+ float d;
+ int numHullPoints, numNew;
+ vec3_t hullPoints[MAX_HULL_POINTS];
+ vec3_t newHullPoints[MAX_HULL_POINTS];
+ vec3_t hullDirs[MAX_HULL_POINTS];
+ qboolean hullSide[MAX_HULL_POINTS];
+ qboolean outside;
+
+ if ( !*hull ) {
+ *hull = CopyWinding( w );
+ return;
+ }
+
+ numHullPoints = (*hull)->numpoints;
+ memcpy( hullPoints, (*hull)->p, numHullPoints * sizeof(vec3_t) );
+
+ for ( i = 0 ; i < w->numpoints ; i++ ) {
+ p = w->p[i];
+
+ // calculate hull side vectors
+ for ( j = 0 ; j < numHullPoints ; j++ ) {
+ k = ( j + 1 ) % numHullPoints;
+
+ VectorSubtract( hullPoints[k], hullPoints[j], dir );
+ VectorNormalize( dir, dir );
+ CrossProduct( normal, dir, hullDirs[j] );
+ }
+
+ outside = qfalse;
+ for ( j = 0 ; j < numHullPoints ; j++ ) {
+ VectorSubtract( p, hullPoints[j], dir );
+ d = DotProduct( dir, hullDirs[j] );
+ if ( d >= ON_EPSILON ) {
+ outside = qtrue;
+ }
+ if ( d >= -ON_EPSILON ) {
+ hullSide[j] = qtrue;
+ } else {
+ hullSide[j] = qfalse;
+ }
+ }
+
+ // if the point is effectively inside, do nothing
+ if ( !outside ) {
+ continue;
+ }
+
+ // find the back side to front side transition
+ for ( j = 0 ; j < numHullPoints ; j++ ) {
+ if ( !hullSide[ j % numHullPoints ] && hullSide[ (j + 1) % numHullPoints ] ) {
+ break;
+ }
+ }
+ if ( j == numHullPoints ) {
+ continue;
+ }
+
+ // insert the point here
+ VectorCopy( p, newHullPoints[0] );
+ numNew = 1;
+
+ // copy over all points that aren't double fronts
+ j = (j+1)%numHullPoints;
+ for ( k = 0 ; k < numHullPoints ; k++ ) {
+ if ( hullSide[ (j+k) % numHullPoints ] && hullSide[ (j+k+1) % numHullPoints ] ) {
+ continue;
+ }
+ copy = hullPoints[ (j+k+1) % numHullPoints ];
+ VectorCopy( copy, newHullPoints[numNew] );
+ numNew++;
+ }
+
+ numHullPoints = numNew;
+ memcpy( hullPoints, newHullPoints, numHullPoints * sizeof(vec3_t) );
+ }
+
+ FreeWinding( *hull );
+ w = AllocWinding( numHullPoints );
+ w->numpoints = numHullPoints;
+ *hull = w;
+ memcpy( w->p, hullPoints, numHullPoints * sizeof(vec3_t) );
+}
+
+
diff --git a/common/polylib.h b/common/polylib.h
new file mode 100755
index 0000000..9f25a89
--- /dev/null
+++ b/common/polylib.h
@@ -0,0 +1,57 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+typedef struct
+{
+ int numpoints;
+ vec3_t p[4]; // variable sized
+} winding_t;
+
+#define MAX_POINTS_ON_WINDING 64
+
+// you can define on_epsilon in the makefile as tighter
+#ifndef ON_EPSILON
+#define ON_EPSILON 0.1
+#endif
+
+winding_t *AllocWinding (int points);
+vec_t WindingArea (winding_t *w);
+void WindingCenter (winding_t *w, vec3_t center);
+void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist,
+ vec_t epsilon, 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 *ReverseWinding (winding_t *w);
+winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist);
+void CheckWinding (winding_t *w);
+void WindingPlane (winding_t *w, vec3_t normal, vec_t *dist);
+void RemoveColinearPoints (winding_t *w);
+int WindingOnPlaneSide (winding_t *w, vec3_t normal, vec_t dist);
+void FreeWinding (winding_t *w);
+void WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs);
+
+void AddWindingToConvexHull( winding_t *w, winding_t **hull, vec3_t normal );
+
+void ChopWindingInPlace (winding_t **w, vec3_t normal, vec_t dist, vec_t epsilon);
+// frees the original if clipped
+
+void pw(winding_t *w);
diff --git a/common/polyset.h b/common/polyset.h
new file mode 100755
index 0000000..3ee8858
--- /dev/null
+++ b/common/polyset.h
@@ -0,0 +1,51 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+#ifndef __POLYSET_H__
+#define __POLYSET_H__
+
+#define POLYSET_MAXTRIANGLES 4096
+#define POLYSET_MAXPOLYSETS 64
+
+typedef float st_t[2];
+typedef float rgb_t[3];
+
+typedef struct {
+ vec3_t verts[3];
+ vec3_t normals[3];
+ st_t texcoords[3];
+} triangle_t;
+
+typedef struct
+{
+ char name[100];
+ char materialname[100];
+ triangle_t *triangles;
+ int numtriangles;
+} polyset_t;
+
+polyset_t *Polyset_LoadSets( const char *file, int *numpolysets, int maxTrisPerSet );
+polyset_t *Polyset_CollapseSets( polyset_t *psets, int numpolysets );
+polyset_t *Polyset_SplitSets( polyset_t *psets, int numpolysets, int *pNumNewPolysets, int maxTris );
+void Polyset_SnapSets( polyset_t *psets, int numpolysets );
+void Polyset_ComputeNormals( polyset_t *psets, int numpolysets );
+
+#endif
diff --git a/common/qfiles.h b/common/qfiles.h
new file mode 100755
index 0000000..095de25
--- /dev/null
+++ b/common/qfiles.h
@@ -0,0 +1,489 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+#ifndef __QFILES_H__
+#define __QFILES_H__
+
+//
+// qfiles.h: quake file formats
+// This file must be identical in the quake and utils directories
+//
+
+// surface geometry should not exceed these limits
+#define SHADER_MAX_VERTEXES 1000
+#define SHADER_MAX_INDEXES (6*SHADER_MAX_VERTEXES)
+
+
+// the maximum size of game reletive pathnames
+#define MAX_QPATH 64
+
+
+/*
+========================================================================
+
+QVM files
+
+========================================================================
+*/
+
+#define VM_MAGIC 0x12721444
+typedef struct {
+ int vmMagic;
+
+ int instructionCount;
+
+ int codeOffset;
+ int codeLength;
+
+ int dataOffset;
+ int dataLength;
+ int litLength; // ( dataLength - litLength ) should be byteswapped on load
+ int bssLength; // zero filled memory appended to datalength
+} vmHeader_t;
+
+/*
+========================================================================
+
+PCX files are used for 8 bit images
+
+========================================================================
+*/
+
+typedef struct {
+ char manufacturer;
+ char version;
+ char encoding;
+ char bits_per_pixel;
+ unsigned short xmin,ymin,xmax,ymax;
+ unsigned short hres,vres;
+ unsigned char palette[48];
+ char reserved;
+ char color_planes;
+ unsigned short bytes_per_line;
+ unsigned short palette_type;
+ char filler[58];
+ unsigned char data; // unbounded
+} pcx_t;
+
+
+/*
+========================================================================
+
+TGA files are used for 24/32 bit images
+
+========================================================================
+*/
+
+typedef struct _TargaHeader {
+ unsigned char id_length, colormap_type, image_type;
+ unsigned short colormap_index, colormap_length;
+ unsigned char colormap_size;
+ unsigned short x_origin, y_origin, width, height;
+ unsigned char pixel_size, attributes;
+} TargaHeader;
+
+
+
+/*
+========================================================================
+
+.MD3 triangle model file format
+
+========================================================================
+*/
+
+#define MD3_IDENT (('3'<<24)+('P'<<16)+('D'<<8)+'I')
+#define MD3_VERSION 15
+
+// limits
+#define MD3_MAX_LODS 4
+#define MD3_MAX_TRIANGLES 8192 // per surface
+#define MD3_MAX_VERTS 4096 // per surface
+#define MD3_MAX_SHADERS 256 // per surface
+#define MD3_MAX_FRAMES 1024 // per model
+#define MD3_MAX_SURFACES 32 // per model
+#define MD3_MAX_TAGS 16 // per frame
+
+// vertex scales
+#define MD3_XYZ_SCALE (1.0/64)
+
+typedef struct md3Frame_s {
+ vec3_t bounds[2];
+ vec3_t localOrigin;
+ float radius;
+ char name[16];
+} md3Frame_t;
+
+typedef struct md3Tag_s {
+ char name[MAX_QPATH]; // tag name
+ vec3_t origin;
+ vec3_t axis[3];
+} md3Tag_t;
+
+/*
+** md3Surface_t
+**
+** CHUNK SIZE
+** header sizeof( md3Surface_t )
+** shaders sizeof( md3Shader_t ) * numShaders
+** triangles[0] sizeof( md3Triangle_t ) * numTriangles
+** st sizeof( md3St_t ) * numVerts
+** XyzNormals sizeof( md3XyzNormal_t ) * numVerts * numFrames
+*/
+typedef struct {
+ int ident; //
+
+ char name[MAX_QPATH]; // polyset name
+
+ int flags;
+ int numFrames; // all surfaces in a model should have the same
+
+ int numShaders; // all surfaces in a model should have the same
+ int numVerts;
+
+ int numTriangles;
+ int ofsTriangles;
+
+ int ofsShaders; // offset from start of md3Surface_t
+ int ofsSt; // texture coords are common for all frames
+ int ofsXyzNormals; // numVerts * numFrames
+
+ int ofsEnd; // next surface follows
+} md3Surface_t;
+
+typedef struct {
+ char name[MAX_QPATH];
+ int shaderIndex; // for in-game use
+} md3Shader_t;
+
+typedef struct {
+ int indexes[3];
+} md3Triangle_t;
+
+typedef struct {
+ float st[2];
+} md3St_t;
+
+typedef struct {
+ short xyz[3];
+ short normal;
+} md3XyzNormal_t;
+
+typedef struct {
+ int ident;
+ int version;
+
+ char name[MAX_QPATH]; // model name
+
+ int flags;
+
+ int numFrames;
+ int numTags;
+ int numSurfaces;
+
+ int numSkins;
+
+ int ofsFrames; // offset for first frame
+ int ofsTags; // numFrames * numTags
+ int ofsSurfaces; // first surface, others follow
+
+ int ofsEnd; // end of file
+} md3Header_t;
+
+
+/*
+==============================================================================
+
+MD4 file format
+
+==============================================================================
+*/
+
+#define MD4_IDENT (('4'<<24)+('P'<<16)+('D'<<8)+'I')
+#define MD4_VERSION 1
+#define MD4_MAX_BONES 128
+
+typedef struct {
+ int boneIndex; // these are indexes into the boneReferences,
+ float boneWeight; // not the global per-frame bone list
+} md4Weight_t;
+
+typedef struct {
+ vec3_t vertex;
+ vec3_t normal;
+ float texCoords[2];
+ int numWeights;
+ md4Weight_t weights[1]; // variable sized
+} md4Vertex_t;
+
+typedef struct {
+ int indexes[3];
+} md4Triangle_t;
+
+typedef struct {
+ int ident;
+
+ char name[MAX_QPATH]; // polyset name
+ char shader[MAX_QPATH];
+ int shaderIndex; // for in-game use
+
+ int ofsHeader; // this will be a negative number
+
+ int numVerts;
+ int ofsVerts;
+
+ int numTriangles;
+ int ofsTriangles;
+
+ // Bone references are a set of ints representing all the bones
+ // present in any vertex weights for this surface. This is
+ // needed because a model may have surfaces that need to be
+ // drawn at different sort times, and we don't want to have
+ // to re-interpolate all the bones for each surface.
+ int numBoneReferences;
+ int ofsBoneReferences;
+
+ int ofsEnd; // next surface follows
+} md4Surface_t;
+
+typedef struct {
+ float matrix[3][4];
+} md4Bone_t;
+
+typedef struct {
+ vec3_t bounds[2]; // bounds of all surfaces of all LOD's for this frame
+ vec3_t localOrigin; // midpoint of bounds, used for sphere cull
+ float radius; // dist from localOrigin to corner
+ char name[16];
+ md4Bone_t bones[1]; // [numBones]
+} md4Frame_t;
+
+typedef struct {
+ int numSurfaces;
+ int ofsSurfaces; // first surface, others follow
+ int ofsEnd; // next lod follows
+} md4LOD_t;
+
+typedef struct {
+ int ident;
+ int version;
+
+ char name[MAX_QPATH]; // model name
+
+ // frames and bones are shared by all levels of detail
+ int numFrames;
+ int numBones;
+ int ofsFrames; // md4Frame_t[numFrames]
+
+ // each level of detail has completely separate sets of surfaces
+ int numLODs;
+ int ofsLODs;
+
+ int ofsEnd; // end of file
+} md4Header_t;
+
+
+/*
+==============================================================================
+
+ .BSP file format
+
+==============================================================================
+*/
+
+
+#define BSP_IDENT (('P'<<24)+('S'<<16)+('B'<<8)+'I')
+ // little-endian "IBSP"
+
+#define BSP_VERSION 46
+
+
+// there shouldn't be any problem with increasing these values at the
+// expense of more memory allocation in the utilities
+#define MAX_MAP_MODELS 0x400
+#define MAX_MAP_BRUSHES 0x8000
+#define MAX_MAP_ENTITIES 0x800
+#define MAX_MAP_ENTSTRING 0x40000
+#define MAX_MAP_SHADERS 0x400
+
+#define MAX_MAP_AREAS 0x100 // MAX_MAP_AREA_BYTES in q_shared must match!
+#define MAX_MAP_FOGS 0x100
+#define MAX_MAP_PLANES 0x20000
+#define MAX_MAP_NODES 0x20000
+#define MAX_MAP_BRUSHSIDES 0x20000
+#define MAX_MAP_LEAFS 0x20000
+#define MAX_MAP_LEAFFACES 0x20000
+#define MAX_MAP_LEAFBRUSHES 0x40000
+#define MAX_MAP_PORTALS 0x20000
+#define MAX_MAP_LIGHTING 0x800000
+#define MAX_MAP_LIGHTGRID 0x800000
+#define MAX_MAP_VISIBILITY 0x200000
+
+#define MAX_MAP_DRAW_SURFS 0x20000
+#define MAX_MAP_DRAW_VERTS 0x80000
+#define MAX_MAP_DRAW_INDEXES 0x80000
+
+
+// key / value pair sizes in the entities lump
+#define MAX_KEY 32
+#define MAX_VALUE 1024
+
+// the editor uses these predefined yaw angles to orient entities up or down
+#define ANGLE_UP -1
+#define ANGLE_DOWN -2
+
+#define LIGHTMAP_WIDTH 128
+#define LIGHTMAP_HEIGHT 128
+
+#define MAX_WORLD_COORD ( 128*1024 )
+#define MIN_WORLD_COORD ( -128*1024 )
+#define WORLD_SIZE ( MAX_WORLD_COORD - MIN_WORLD_COORD )
+
+//=============================================================================
+
+
+typedef struct {
+ int fileofs, filelen;
+} lump_t;
+
+#define LUMP_ENTITIES 0
+#define LUMP_SHADERS 1
+#define LUMP_PLANES 2
+#define LUMP_NODES 3
+#define LUMP_LEAFS 4
+#define LUMP_LEAFSURFACES 5
+#define LUMP_LEAFBRUSHES 6
+#define LUMP_MODELS 7
+#define LUMP_BRUSHES 8
+#define LUMP_BRUSHSIDES 9
+#define LUMP_DRAWVERTS 10
+#define LUMP_DRAWINDEXES 11
+#define LUMP_FOGS 12
+#define LUMP_SURFACES 13
+#define LUMP_LIGHTMAPS 14
+#define LUMP_LIGHTGRID 15
+#define LUMP_VISIBILITY 16
+#define HEADER_LUMPS 17
+
+typedef struct {
+ int ident;
+ int version;
+
+ lump_t lumps[HEADER_LUMPS];
+} dheader_t;
+
+typedef struct {
+ float mins[3], maxs[3];
+ int firstSurface, numSurfaces;
+ int firstBrush, numBrushes;
+} dmodel_t;
+
+typedef struct {
+ char shader[MAX_QPATH];
+ int surfaceFlags;
+ int contentFlags;
+} dshader_t;
+
+// planes x^1 is allways the opposite of plane x
+
+typedef struct {
+ float normal[3];
+ float dist;
+} dplane_t;
+
+typedef struct {
+ int planeNum;
+ int children[2]; // negative numbers are -(leafs+1), not nodes
+ int mins[3]; // for frustom culling
+ int maxs[3];
+} dnode_t;
+
+typedef struct {
+ int cluster; // -1 = opaque cluster (do I still store these?)
+ int area;
+
+ int mins[3]; // for frustum culling
+ int maxs[3];
+
+ int firstLeafSurface;
+ int numLeafSurfaces;
+
+ int firstLeafBrush;
+ int numLeafBrushes;
+} dleaf_t;
+
+typedef struct {
+ int planeNum; // positive plane side faces out of the leaf
+ int shaderNum;
+} dbrushside_t;
+
+typedef struct {
+ int firstSide;
+ int numSides;
+ int shaderNum; // the shader that determines the contents flags
+} dbrush_t;
+
+typedef struct {
+ char shader[MAX_QPATH];
+ int brushNum;
+ int visibleSide; // the brush side that ray tests need to clip against (-1 == none)
+} dfog_t;
+
+typedef struct {
+ vec3_t xyz;
+ float st[2];
+ float lightmap[2];
+ vec3_t normal;
+ byte color[4];
+} drawVert_t;
+
+typedef enum {
+ MST_BAD,
+ MST_PLANAR,
+ MST_PATCH,
+ MST_TRIANGLE_SOUP,
+ MST_FLARE
+} mapSurfaceType_t;
+
+typedef struct {
+ int shaderNum;
+ int fogNum;
+ int surfaceType;
+
+ int firstVert;
+ int numVerts;
+
+ int firstIndex;
+ int numIndexes;
+
+ int lightmapNum;
+ int lightmapX, lightmapY;
+ int lightmapWidth, lightmapHeight;
+
+ vec3_t lightmapOrigin;
+ vec3_t lightmapVecs[3]; // for patches, [0] and [1] are lodbounds
+
+ int patchWidth;
+ int patchHeight;
+} dsurface_t;
+
+
+#endif
diff --git a/common/scriplib.c b/common/scriplib.c
new file mode 100755
index 0000000..782a538
--- /dev/null
+++ b/common/scriplib.c
@@ -0,0 +1,375 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+// scriplib.c
+
+#include "cmdlib.h"
+#include "scriplib.h"
+
+/*
+=============================================================================
+
+ PARSING STUFF
+
+=============================================================================
+*/
+
+typedef struct
+{
+ char filename[1024];
+ char *buffer,*script_p,*end_p;
+ int line;
+} script_t;
+
+#define MAX_INCLUDES 8
+script_t scriptstack[MAX_INCLUDES];
+script_t *script;
+int scriptline;
+
+char token[MAXTOKEN];
+qboolean endofscript;
+qboolean tokenready; // only qtrue if UnGetToken was just called
+
+/*
+==============
+AddScriptToStack
+==============
+*/
+void AddScriptToStack( const char *filename ) {
+ int size;
+
+ script++;
+ if (script == &scriptstack[MAX_INCLUDES])
+ Error ("script file exceeded MAX_INCLUDES");
+ strcpy (script->filename, ExpandPath (filename) );
+
+ size = LoadFile (script->filename, (void **)&script->buffer);
+
+ printf ("entering %s\n", script->filename);
+
+ script->line = 1;
+
+ script->script_p = script->buffer;
+ script->end_p = script->buffer + size;
+}
+
+
+/*
+==============
+LoadScriptFile
+==============
+*/
+void LoadScriptFile( const char *filename ) {
+ script = scriptstack;
+ AddScriptToStack (filename);
+
+ endofscript = qfalse;
+ tokenready = qfalse;
+}
+
+
+/*
+==============
+ParseFromMemory
+==============
+*/
+void ParseFromMemory (char *buffer, int size)
+{
+ script = scriptstack;
+ script++;
+ if (script == &scriptstack[MAX_INCLUDES])
+ Error ("script file exceeded MAX_INCLUDES");
+ strcpy (script->filename, "memory buffer" );
+
+ script->buffer = buffer;
+ script->line = 1;
+ script->script_p = script->buffer;
+ script->end_p = script->buffer + size;
+
+ endofscript = qfalse;
+ tokenready = qfalse;
+}
+
+
+/*
+==============
+UnGetToken
+
+Signals that the current token was not used, and should be reported
+for the next GetToken. Note that
+
+GetToken (qtrue);
+UnGetToken ();
+GetToken (qfalse);
+
+could cross a line boundary.
+==============
+*/
+void UnGetToken (void)
+{
+ tokenready = qtrue;
+}
+
+
+qboolean EndOfScript (qboolean crossline)
+{
+ if (!crossline)
+ Error ("Line %i is incomplete\n",scriptline);
+
+ if (!strcmp (script->filename, "memory buffer"))
+ {
+ endofscript = qtrue;
+ return qfalse;
+ }
+
+ free (script->buffer);
+ if (script == scriptstack+1)
+ {
+ endofscript = qtrue;
+ return qfalse;
+ }
+ script--;
+ scriptline = script->line;
+ printf ("returning to %s\n", script->filename);
+ return GetToken (crossline);
+}
+
+/*
+==============
+GetToken
+==============
+*/
+qboolean GetToken (qboolean crossline)
+{
+ char *token_p;
+
+ if (tokenready) // is a token allready waiting?
+ {
+ tokenready = qfalse;
+ return qtrue;
+ }
+
+ if (script->script_p >= script->end_p)
+ return EndOfScript (crossline);
+
+//
+// skip space
+//
+skipspace:
+ while (*script->script_p <= 32)
+ {
+ if (script->script_p >= script->end_p)
+ return EndOfScript (crossline);
+ if (*script->script_p++ == '\n')
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete\n",scriptline);
+ scriptline = script->line++;
+ }
+ }
+
+ if (script->script_p >= script->end_p)
+ return EndOfScript (crossline);
+
+ // ; # // comments
+ if (*script->script_p == ';' || *script->script_p == '#'
+ || ( script->script_p[0] == '/' && script->script_p[1] == '/') )
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete\n",scriptline);
+ while (*script->script_p++ != '\n')
+ if (script->script_p >= script->end_p)
+ return EndOfScript (crossline);
+ scriptline = script->line++;
+ goto skipspace;
+ }
+
+ // /* */ comments
+ if (script->script_p[0] == '/' && script->script_p[1] == '*')
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete\n",scriptline);
+ script->script_p+=2;
+ while (script->script_p[0] != '*' && script->script_p[1] != '/')
+ {
+ if ( *script->script_p == '\n' ) {
+ scriptline = script->line++;
+ }
+ script->script_p++;
+ if (script->script_p >= script->end_p)
+ return EndOfScript (crossline);
+ }
+ script->script_p += 2;
+ goto skipspace;
+ }
+
+//
+// copy token
+//
+ token_p = token;
+
+ if (*script->script_p == '"')
+ {
+ // quoted token
+ script->script_p++;
+ while (*script->script_p != '"')
+ {
+ *token_p++ = *script->script_p++;
+ if (script->script_p == script->end_p)
+ break;
+ if (token_p == &token[MAXTOKEN])
+ Error ("Token too large on line %i\n",scriptline);
+ }
+ script->script_p++;
+ }
+ else // regular token
+ while ( *script->script_p > 32 && *script->script_p != ';')
+ {
+ *token_p++ = *script->script_p++;
+ if (script->script_p == script->end_p)
+ break;
+ if (token_p == &token[MAXTOKEN])
+ Error ("Token too large on line %i\n",scriptline);
+ }
+
+ *token_p = 0;
+
+ if (!strcmp (token, "$include"))
+ {
+ GetToken (qfalse);
+ AddScriptToStack (token);
+ return GetToken (crossline);
+ }
+
+ return qtrue;
+}
+
+
+/*
+==============
+TokenAvailable
+
+Returns qtrue if there is another token on the line
+==============
+*/
+qboolean TokenAvailable (void) {
+ int oldLine;
+ qboolean r;
+
+ oldLine = script->line;
+ r = GetToken( qtrue );
+ if ( !r ) {
+ return qfalse;
+ }
+ UnGetToken();
+ if ( oldLine == script->line ) {
+ return qtrue;
+ }
+ return qfalse;
+}
+
+
+//=====================================================================
+
+
+void MatchToken( char *match ) {
+ GetToken( qtrue );
+
+ if ( strcmp( token, match ) ) {
+ Error( "MatchToken( \"%s\" ) failed at line %i", match, scriptline );
+ }
+}
+
+
+void Parse1DMatrix (int x, vec_t *m) {
+ int i;
+
+ MatchToken( "(" );
+
+ for (i = 0 ; i < x ; i++) {
+ GetToken( qfalse );
+ m[i] = atof(token);
+ }
+
+ MatchToken( ")" );
+}
+
+void Parse2DMatrix (int y, int x, vec_t *m) {
+ int i;
+
+ MatchToken( "(" );
+
+ for (i = 0 ; i < y ; i++) {
+ Parse1DMatrix (x, m + i * x);
+ }
+
+ MatchToken( ")" );
+}
+
+void Parse3DMatrix (int z, int y, int x, vec_t *m) {
+ int i;
+
+ MatchToken( "(" );
+
+ for (i = 0 ; i < z ; i++) {
+ Parse2DMatrix (y, x, m + i * x*y);
+ }
+
+ MatchToken( ")" );
+}
+
+
+void Write1DMatrix (FILE *f, int x, vec_t *m) {
+ int i;
+
+ fprintf (f, "( ");
+ for (i = 0 ; i < x ; i++) {
+ if (m[i] == (int)m[i] ) {
+ fprintf (f, "%i ", (int)m[i]);
+ } else {
+ fprintf (f, "%f ", m[i]);
+ }
+ }
+ fprintf (f, ")");
+}
+
+void Write2DMatrix (FILE *f, int y, int x, vec_t *m) {
+ int i;
+
+ fprintf (f, "( ");
+ for (i = 0 ; i < y ; i++) {
+ Write1DMatrix (f, x, m + i*x);
+ fprintf (f, " ");
+ }
+ fprintf (f, ")\n");
+}
+
+
+void Write3DMatrix (FILE *f, int z, int y, int x, vec_t *m) {
+ int i;
+
+ fprintf (f, "(\n");
+ for (i = 0 ; i < z ; i++) {
+ Write2DMatrix (f, y, x, m + i*(x*y) );
+ }
+ fprintf (f, ")\n");
+}
+
diff --git a/common/scriplib.h b/common/scriplib.h
new file mode 100755
index 0000000..88274cb
--- /dev/null
+++ b/common/scriplib.h
@@ -0,0 +1,55 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+// scriplib.h
+
+#ifndef __CMDLIB__
+#include "cmdlib.h"
+#endif
+#ifndef __MATHLIB__
+#include "mathlib.h"
+#endif
+
+#define MAXTOKEN 1024
+
+extern char token[MAXTOKEN];
+extern char *scriptbuffer,*script_p,*scriptend_p;
+extern int grabbed;
+extern int scriptline;
+extern qboolean endofscript;
+
+
+void LoadScriptFile( const char *filename );
+void ParseFromMemory (char *buffer, int size);
+
+qboolean GetToken (qboolean crossline);
+void UnGetToken (void);
+qboolean TokenAvailable (void);
+
+void MatchToken( char *match );
+
+void Parse1DMatrix (int x, vec_t *m);
+void Parse2DMatrix (int y, int x, vec_t *m);
+void Parse3DMatrix (int z, int y, int x, vec_t *m);
+
+void Write1DMatrix (FILE *f, int x, vec_t *m);
+void Write2DMatrix (FILE *f, int y, int x, vec_t *m);
+void Write3DMatrix (FILE *f, int z, int y, int x, vec_t *m);
diff --git a/common/surfaceflags.h b/common/surfaceflags.h
new file mode 100755
index 0000000..b99cfda
--- /dev/null
+++ b/common/surfaceflags.h
@@ -0,0 +1,72 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+// This file must be identical in the quake and utils directories
+
+// contents flags are seperate bits
+// a given brush can contribute multiple content bits
+
+// these definitions also need to be in q_shared.h!
+
+#define CONTENTS_SOLID 1 // an eye is never valid in a solid
+#define CONTENTS_LAVA 8
+#define CONTENTS_SLIME 16
+#define CONTENTS_WATER 32
+#define CONTENTS_FOG 64
+
+#define CONTENTS_AREAPORTAL 0x8000
+
+#define CONTENTS_PLAYERCLIP 0x10000
+#define CONTENTS_MONSTERCLIP 0x20000
+//bot specific contents types
+#define CONTENTS_TELEPORTER 0x40000
+#define CONTENTS_JUMPPAD 0x80000
+#define CONTENTS_CLUSTERPORTAL 0x100000
+#define CONTENTS_DONOTENTER 0x200000
+
+#define CONTENTS_ORIGIN 0x1000000 // removed before bsping an entity
+
+#define CONTENTS_BODY 0x2000000 // should never be on a brush, only in game
+#define CONTENTS_CORPSE 0x4000000
+#define CONTENTS_DETAIL 0x8000000 // brushes not used for the bsp
+#define CONTENTS_STRUCTURAL 0x10000000 // brushes used for the bsp
+#define CONTENTS_TRANSLUCENT 0x20000000 // don't consume surface fragments inside
+#define CONTENTS_TRIGGER 0x40000000
+#define CONTENTS_NODROP 0x80000000 // don't leave bodies or items (death fog, lava)
+
+#define SURF_NODAMAGE 0x1 // never give falling damage
+#define SURF_SLICK 0x2 // effects game physics
+#define SURF_SKY 0x4 // lighting from environment map
+#define SURF_LADDER 0x8
+#define SURF_NOIMPACT 0x10 // don't make missile explosions
+#define SURF_NOMARKS 0x20 // don't leave missile marks
+#define SURF_FLESH 0x40 // make flesh sounds and effects
+#define SURF_NODRAW 0x80 // don't generate a drawsurface at all
+#define SURF_HINT 0x100 // make a primary bsp splitter
+#define SURF_SKIP 0x200 // completely ignore, allowing non-closed brushes
+#define SURF_NOLIGHTMAP 0x400 // surface doesn't need a lightmap
+#define SURF_POINTLIGHT 0x800 // generate lighting info at vertexes
+#define SURF_METALSTEPS 0x1000 // clanking footsteps
+#define SURF_NOSTEPS 0x2000 // no footstep sounds
+#define SURF_NONSOLID 0x4000 // don't collide against curves with this set
+#define SURF_LIGHTFILTER 0x8000 // act as a light filter during q3map -light
+#define SURF_ALPHASHADOW 0x10000 // do per-pixel light shadow casting in q3map
+#define SURF_NODLIGHT 0x20000 // never add dynamic lights
diff --git a/common/threads.c b/common/threads.c
new file mode 100755
index 0000000..e700dbb
--- /dev/null
+++ b/common/threads.c
@@ -0,0 +1,441 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "threads.h"
+
+#define MAX_THREADS 64
+
+int dispatch;
+int workcount;
+int oldf;
+qboolean pacifier;
+
+qboolean threaded;
+
+/*
+=============
+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;
+}
+
+
+void (*workfunction) (int);
+
+void ThreadWorkerFunction (int threadnum)
+{
+ int work;
+
+ while (1)
+ {
+ work = GetThreadWork ();
+ if (work == -1)
+ break;
+//_printf ("thread %i, work %i\n", threadnum, work);
+ workfunction(work);
+ }
+}
+
+void RunThreadsOnIndividual (int workcnt, qboolean showpacifier, void(*func)(int))
+{
+ if (numthreads == -1)
+ ThreadSetDefault ();
+ workfunction = func;
+ RunThreadsOn (workcnt, showpacifier, ThreadWorkerFunction);
+}
+
+
+/*
+===================================================================
+
+WIN32
+
+===================================================================
+*/
+#ifdef WIN32
+
+#define USED
+
+#include <windows.h>
+
+int numthreads = -1;
+CRITICAL_SECTION crit;
+static int enter;
+
+void ThreadSetDefault (void)
+{
+ SYSTEM_INFO info;
+
+ if (numthreads == -1) // not set manually
+ {
+ GetSystemInfo (&info);
+ numthreads = info.dwNumberOfProcessors;
+ if (numthreads < 1 || numthreads > 32)
+ numthreads = 1;
+ }
+
+ qprintf ("%i threads\n", numthreads);
+}
+
+
+void ThreadLock (void)
+{
+ if (!threaded)
+ return;
+ EnterCriticalSection (&crit);
+ if (enter)
+ Error ("Recursive ThreadLock\n");
+ enter = 1;
+}
+
+void ThreadUnlock (void)
+{
+ if (!threaded)
+ return;
+ if (!enter)
+ Error ("ThreadUnlock without lock\n");
+ enter = 0;
+ LeaveCriticalSection (&crit);
+}
+
+/*
+=============
+RunThreadsOn
+=============
+*/
+void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int))
+{
+ int threadid[MAX_THREADS];
+ HANDLE threadhandle[MAX_THREADS];
+ int i;
+ int start, end;
+
+ start = I_FloatTime ();
+ dispatch = 0;
+ workcount = workcnt;
+ oldf = -1;
+ pacifier = showpacifier;
+ threaded = qtrue;
+
+ //
+ // run threads in parallel
+ //
+ InitializeCriticalSection (&crit);
+
+ if (numthreads == 1)
+ { // use same thread
+ func (0);
+ }
+ else
+ {
+ 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);
+
+ threaded = qfalse;
+ end = I_FloatTime ();
+ if (pacifier)
+ _printf (" (%i)\n", end-start);
+}
+
+
+#endif
+
+/*
+===================================================================
+
+OSF1
+
+===================================================================
+*/
+
+#ifdef __osf__
+#define USED
+
+int numthreads = 4;
+
+void ThreadSetDefault (void)
+{
+ if (numthreads == -1) // not set manually
+ {
+ 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;
+ int start, end;
+
+ start = I_FloatTime ();
+ dispatch = 0;
+ workcount = workcnt;
+ oldf = -1;
+ pacifier = showpacifier;
+ threaded = qtrue;
+
+ if (pacifier)
+ setbuf (stdout, NULL);
+
+ 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");
+ }
+
+ threaded = qfalse;
+
+ end = I_FloatTime ();
+ if (pacifier)
+ _printf (" (%i)\n", end-start);
+}
+
+
+#endif
+
+/*
+===================================================================
+
+IRIX
+
+===================================================================
+*/
+
+#ifdef _MIPS_ISA
+#define USED
+
+#include <task.h>
+#include <abi_mutex.h>
+#include <sys/types.h>
+#include <sys/prctl.h>
+
+
+int numthreads = -1;
+abilock_t lck;
+
+void ThreadSetDefault (void)
+{
+ if (numthreads == -1)
+ numthreads = prctl(PR_MAXPPROCS);
+ _printf ("%i threads\n", numthreads);
+ usconfig (CONF_INITUSERS, numthreads);
+}
+
+
+void ThreadLock (void)
+{
+ spin_lock (&lck);
+}
+
+void ThreadUnlock (void)
+{
+ release_lock (&lck);
+}
+
+
+/*
+=============
+RunThreadsOn
+=============
+*/
+void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int))
+{
+ int i;
+ int pid[MAX_THREADS];
+ int start, end;
+
+ start = I_FloatTime ();
+ dispatch = 0;
+ workcount = workcnt;
+ oldf = -1;
+ pacifier = showpacifier;
+ threaded = qtrue;
+
+ if (pacifier)
+ setbuf (stdout, NULL);
+
+ init_lock (&lck);
+
+ for (i=0 ; i<numthreads-1 ; i++)
+ {
+ pid[i] = sprocsp ( (void (*)(void *, size_t))func, PR_SALL, (void *)i
+ , NULL, 0x200000); // 2 meg stacks
+ if (pid[i] == -1)
+ {
+ perror ("sproc");
+ Error ("sproc failed");
+ }
+ }
+
+ func(i);
+
+ for (i=0 ; i<numthreads-1 ; i++)
+ wait (NULL);
+
+ threaded = qfalse;
+
+ end = I_FloatTime ();
+ if (pacifier)
+ _printf (" (%i)\n", end-start);
+}
+
+
+#endif
+
+/*
+=======================================================================
+
+ SINGLE THREAD
+
+=======================================================================
+*/
+
+#ifndef USED
+
+int numthreads = 1;
+
+void ThreadSetDefault (void)
+{
+ numthreads = 1;
+}
+
+void ThreadLock (void)
+{
+}
+
+void ThreadUnlock (void)
+{
+}
+
+/*
+=============
+RunThreadsOn
+=============
+*/
+void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int))
+{
+ int i;
+ int start, end;
+
+ dispatch = 0;
+ workcount = workcnt;
+ oldf = -1;
+ pacifier = showpacifier;
+ start = I_FloatTime ();
+ func(0);
+
+ end = I_FloatTime ();
+ if (pacifier)
+ _printf (" (%i)\n", end-start);
+}
+
+#endif
diff --git a/common/threads.h b/common/threads.h
new file mode 100755
index 0000000..ba76dfb
--- /dev/null
+++ b/common/threads.h
@@ -0,0 +1,31 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+extern int numthreads;
+
+void ThreadSetDefault (void);
+int GetThreadWork (void);
+void RunThreadsOnIndividual (int workcnt, qboolean showpacifier, void(*func)(int));
+void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int));
+void ThreadLock (void);
+void ThreadUnlock (void);
+
diff --git a/common/trilib.c b/common/trilib.c
new file mode 100755
index 0000000..34f9e2e
--- /dev/null
+++ b/common/trilib.c
@@ -0,0 +1,231 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+// trilib.c: library for loading triangles from an Alias triangle file
+//
+
+#include <stdio.h>
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "polyset.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;
+
+
+static void ByteSwapTri (tf_triangle *tri)
+{
+ int i;
+
+ for (i=0 ; i<sizeof(tf_triangle)/4 ; i++)
+ {
+ ((int *)tri)[i] = BigLong (((int *)tri)[i]);
+ }
+}
+
+static void ReadPolysetGeometry( triangle_t *tripool, FILE *input, int count, triangle_t *ptri )
+{
+ tf_triangle tri;
+ int i;
+
+ 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->normals[j][k] = tri.pt[j].n.v[k];
+// ptri->colors[j][k] = tri.pt[j].c.v[k];
+ }
+
+ ptri->texcoords[j][0] = tri.pt[j].u;
+ ptri->texcoords[j][1] = tri.pt[j].v;
+ }
+
+ ptri++;
+ if ((ptri - tripool ) >= POLYSET_MAXTRIANGLES)
+ Error ("Error: too many triangles; increase POLYSET_MAXTRIANGLES\n");
+ }
+}
+
+void TRI_LoadPolysets( const char *filename, polyset_t **ppPSET, int *numpsets )
+{
+ FILE *input;
+ float start;
+ char name[256], tex[256];
+ int i, count, magic, pset = 0;
+ triangle_t *ptri;
+ polyset_t *pPSET;
+ 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)
+ Error ("reader: could not open file '%s'", filename);
+
+ iLevel = 0;
+
+ fread(&magic, sizeof(int), 1, input);
+ if (BigLong(magic) != MAGIC)
+ Error ("%s is not a Alias object separated triangle file, magic number is wrong.", filename);
+
+ pPSET = calloc( 1, POLYSET_MAXPOLYSETS * sizeof( polyset_t ) );
+ ptri = calloc( 1, POLYSET_MAXTRIANGLES * sizeof( triangle_t ) );
+
+ *ppPSET = pPSET;
+
+ while (feof(input) == 0) {
+ if (fread(&start, sizeof(float), 1, input) < 1)
+ break;
+ *(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' );
+
+ if ( i != 0 )
+ strncpy( pPSET[pset].name, name, sizeof( pPSET[pset].name ) - 1 );
+ else
+ strcpy( pPSET[pset].name , "(unnamed)" );
+ strlwr( pPSET[pset].name );
+
+// 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' );
+
+/*
+ if ( i != 0 )
+ strncpy( pPSET[pset].texname, tex, sizeof( pPSET[pset].texname ) - 1 );
+ else
+ strcpy( pPSET[pset].texname, "(unnamed)" );
+ strlwr( pPSET[pset].texname );
+*/
+
+// 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. */
+ /* Mea culpa. */
+ --iLevel;
+ i = -1;
+ do {
+ ++i;
+ fread( &(name[i]), sizeof( char ), 1, input);
+ } while( name[i] != '\0' );
+
+ if ( i != 0 )
+ strncpy( pPSET[pset].name, name, sizeof( pPSET[pset].name ) - 1 );
+ else
+ strcpy( pPSET[pset].name , "(unnamed)" );
+
+ strlwr( pPSET[pset].name );
+
+// indent();
+// fprintf(stdout,"OBJECT END: %s\n",name);
+ continue;
+ }
+ }
+
+//
+// read the triangles
+//
+ if ( count > 0 )
+ {
+ pPSET[pset].triangles = ptri;
+ ReadPolysetGeometry( pPSET[0].triangles, input, count, ptri );
+ ptri += count;
+ pPSET[pset].numtriangles = count;
+ if ( ++pset >= POLYSET_MAXPOLYSETS )
+ {
+ Error ("Error: too many polysets; increase POLYSET_MAXPOLYSETS\n");
+ }
+ }
+ }
+
+ *numpsets = pset;
+
+ fclose (input);
+}
+
diff --git a/common/trilib.h b/common/trilib.h
new file mode 100755
index 0000000..a823411
--- /dev/null
+++ b/common/trilib.h
@@ -0,0 +1,26 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+// trilib.h: header file for loading triangles from an Alias triangle file
+//
+void TRI_LoadPolysets( const char *filename, polyset_t **ppPSET, int *numpsets );
+