ref: de503b3834162b9de155772f18983c8c83907eb4
parent: be559d26383e23407fe568d178cef7273c411dbf
author: Travis Bradshaw <[email protected]>
date: Tue Jan 31 11:03:17 EST 2012
Just kind of shoving the QuakeWorld QuakeC source in here.
--- a/WinQuake/gl_test.c
+++ b/WinQuake/gl_test.c
@@ -1,0 +1,182 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program 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.
+
+This program 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 this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "quakedef.h"
+
+#ifdef GLTEST
+
+typedef struct
+{
+ plane_t *plane;
+ vec3_t origin;
+ vec3_t normal;
+ vec3_t up;
+ vec3_t right;
+ vec3_t reflect;
+ float length;
+} puff_t;
+
+#define MAX_PUFFS 64
+
+puff_t puffs[MAX_PUFFS];
+
+
+void Test_Init (void)
+{
+}
+
+
+
+plane_t junk;
+plane_t *HitPlane (vec3_t start, vec3_t end)
+{
+ trace_t trace;
+
+// fill in a default trace
+ memset (&trace, 0, sizeof(trace_t));
+ trace.fraction = 1;
+ trace.allsolid = true;
+ VectorCopy (end, trace.endpos);
+
+ SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
+
+ junk = trace.plane;
+ return &junk;
+}
+
+void Test_Spawn (vec3_t origin)
+{
+ int i;
+ puff_t *p;
+ vec3_t temp;
+ vec3_t normal;
+ vec3_t incoming;
+ plane_t *plane;
+ float d;
+
+ for (i=0,p=puffs ; i<MAX_PUFFS ; i++,p++)
+ {
+ if (p->length <= 0)
+ break;
+ }
+ if (i == MAX_PUFFS)
+ return;
+
+ VectorSubtract (r_refdef.vieworg, origin, incoming);
+ VectorSubtract (origin, incoming, temp);
+ plane = HitPlane (r_refdef.vieworg, temp);
+
+ VectorNormalize (incoming);
+ d = DotProduct (incoming, plane->normal);
+ VectorSubtract (vec3_origin, incoming, p->reflect);
+ VectorMA (p->reflect, d*2, plane->normal, p->reflect);
+
+ VectorCopy (origin, p->origin);
+ VectorCopy (plane->normal, p->normal);
+
+ CrossProduct (incoming, p->normal, p->up);
+
+ CrossProduct (p->up, p->normal, p->right);
+
+ p->length = 8;
+}
+
+void DrawPuff (puff_t *p)
+{
+ vec3_t pts[2][3];
+ int i, j;
+ float s, d;
+
+ for (i=0 ; i<2 ; i++)
+ {
+ if (i == 1)
+ {
+ s = 6;
+ d = p->length;
+ }
+ else
+ {
+ s = 2;
+ d = 0;
+ }
+
+ for (j=0 ; j<3 ; j++)
+ {
+ pts[i][0][j] = p->origin[j] + p->up[j]*s + p->reflect[j]*d;
+ pts[i][1][j] = p->origin[j] + p->right[j]*s + p->reflect[j]*d;
+ pts[i][2][j] = p->origin[j] + -p->right[j]*s + p->reflect[j]*d;
+ }
+ }
+
+ glColor3f (1, 0, 0);
+
+#if 0
+ glBegin (GL_LINES);
+ glVertex3fv (p->origin);
+ glVertex3f (p->origin[0] + p->length*p->reflect[0],
+ p->origin[1] + p->length*p->reflect[1],
+ p->origin[2] + p->length*p->reflect[2]);
+
+ glVertex3fv (pts[0][0]);
+ glVertex3fv (pts[1][0]);
+
+ glVertex3fv (pts[0][1]);
+ glVertex3fv (pts[1][1]);
+
+ glVertex3fv (pts[0][2]);
+ glVertex3fv (pts[1][2]);
+
+ glEnd ();
+#endif
+
+ glBegin (GL_QUADS);
+ for (i=0 ; i<3 ; i++)
+ {
+ j = (i+1)%3;
+ glVertex3fv (pts[0][j]);
+ glVertex3fv (pts[1][j]);
+ glVertex3fv (pts[1][i]);
+ glVertex3fv (pts[0][i]);
+ }
+ glEnd ();
+
+ glBegin (GL_TRIANGLES);
+ glVertex3fv (pts[1][0]);
+ glVertex3fv (pts[1][1]);
+ glVertex3fv (pts[1][2]);
+ glEnd ();
+
+ p->length -= host_frametime*2;
+}
+
+
+void Test_Draw (void)
+{
+ int i;
+ puff_t *p;
+
+ for (i=0, p=puffs ; i<MAX_PUFFS ; i++,p++)
+ {
+ if (p->length > 0)
+ DrawPuff (p);
+ }
+}
+
+#endif
--- /dev/null
+++ b/WinQuake/gl_warp.c
@@ -1,0 +1,1092 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program 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.
+
+This program 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 this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// gl_warp.c -- sky and water polygons
+
+#include "quakedef.h"
+
+extern model_t *loadmodel;
+
+int skytexturenum;
+
+int solidskytexture;
+int alphaskytexture;
+float speedscale; // for top sky and bottom sky
+
+msurface_t *warpface;
+
+extern cvar_t gl_subdivide_size;
+
+void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
+{
+ int i, j;
+ float *v;
+
+ mins[0] = mins[1] = mins[2] = 9999;
+ maxs[0] = maxs[1] = maxs[2] = -9999;
+ v = verts;
+ for (i=0 ; i<numverts ; i++)
+ for (j=0 ; j<3 ; j++, v++)
+ {
+ if (*v < mins[j])
+ mins[j] = *v;
+ if (*v > maxs[j])
+ maxs[j] = *v;
+ }
+}
+
+void SubdividePolygon (int numverts, float *verts)
+{
+ int i, j, k;
+ vec3_t mins, maxs;
+ float m;
+ float *v;
+ vec3_t front[64], back[64];
+ int f, b;
+ float dist[64];
+ float frac;
+ glpoly_t *poly;
+ float s, t;
+
+ if (numverts > 60)
+ Sys_Error ("numverts = %i", numverts);
+
+ BoundPoly (numverts, verts, mins, maxs);
+
+ for (i=0 ; i<3 ; i++)
+ {
+ m = (mins[i] + maxs[i]) * 0.5;
+ m = gl_subdivide_size.value * floor (m/gl_subdivide_size.value + 0.5);
+ if (maxs[i] - m < 8)
+ continue;
+ if (m - mins[i] < 8)
+ continue;
+
+ // cut it
+ v = verts + i;
+ for (j=0 ; j<numverts ; j++, v+= 3)
+ dist[j] = *v - m;
+
+ // wrap cases
+ dist[j] = dist[0];
+ v-=i;
+ VectorCopy (verts, v);
+
+ f = b = 0;
+ v = verts;
+ for (j=0 ; j<numverts ; j++, v+= 3)
+ {
+ if (dist[j] >= 0)
+ {
+ VectorCopy (v, front[f]);
+ f++;
+ }
+ if (dist[j] <= 0)
+ {
+ VectorCopy (v, back[b]);
+ b++;
+ }
+ if (dist[j] == 0 || dist[j+1] == 0)
+ continue;
+ if ( (dist[j] > 0) != (dist[j+1] > 0) )
+ {
+ // clip point
+ frac = dist[j] / (dist[j] - dist[j+1]);
+ for (k=0 ; k<3 ; k++)
+ front[f][k] = back[b][k] = v[k] + frac*(v[3+k] - v[k]);
+ f++;
+ b++;
+ }
+ }
+
+ SubdividePolygon (f, front[0]);
+ SubdividePolygon (b, back[0]);
+ return;
+ }
+
+ poly = Hunk_Alloc (sizeof(glpoly_t) + (numverts-4) * VERTEXSIZE*sizeof(float));
+ poly->next = warpface->polys;
+ warpface->polys = poly;
+ poly->numverts = numverts;
+ for (i=0 ; i<numverts ; i++, verts+= 3)
+ {
+ VectorCopy (verts, poly->verts[i]);
+ s = DotProduct (verts, warpface->texinfo->vecs[0]);
+ t = DotProduct (verts, warpface->texinfo->vecs[1]);
+ poly->verts[i][3] = s;
+ poly->verts[i][4] = t;
+ }
+}
+
+/*
+================
+GL_SubdivideSurface
+
+Breaks a polygon up along axial 64 unit
+boundaries so that turbulent and sky warps
+can be done reasonably.
+================
+*/
+void GL_SubdivideSurface (msurface_t *fa)
+{
+ vec3_t verts[64];
+ int numverts;
+ int i;
+ int lindex;
+ float *vec;
+ texture_t *t;
+
+ warpface = fa;
+
+ //
+ // convert edges back to a normal polygon
+ //
+ numverts = 0;
+ for (i=0 ; i<fa->numedges ; i++)
+ {
+ lindex = loadmodel->surfedges[fa->firstedge + i];
+
+ if (lindex > 0)
+ vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
+ else
+ vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
+ VectorCopy (vec, verts[numverts]);
+ numverts++;
+ }
+
+ SubdividePolygon (numverts, verts[0]);
+}
+
+//=========================================================
+
+
+
+// speed up sin calculations - Ed
+float turbsin[] =
+{
+ #include "gl_warp_sin.h"
+};
+#define TURBSCALE (256.0 / (2 * M_PI))
+
+/*
+=============
+EmitWaterPolys
+
+Does a water warp on the pre-fragmented glpoly_t chain
+=============
+*/
+void EmitWaterPolys (msurface_t *fa)
+{
+ glpoly_t *p;
+ float *v;
+ int i;
+ float s, t, os, ot;
+
+
+ for (p=fa->polys ; p ; p=p->next)
+ {
+ glBegin (GL_POLYGON);
+ for (i=0,v=p->verts[0] ; i<p->numverts ; i++, v+=VERTEXSIZE)
+ {
+ os = v[3];
+ ot = v[4];
+
+ s = os + turbsin[(int)((ot*0.125+realtime) * TURBSCALE) & 255];
+ s *= (1.0/64);
+
+ t = ot + turbsin[(int)((os*0.125+realtime) * TURBSCALE) & 255];
+ t *= (1.0/64);
+
+ glTexCoord2f (s, t);
+ glVertex3fv (v);
+ }
+ glEnd ();
+ }
+}
+
+
+
+
+/*
+=============
+EmitSkyPolys
+=============
+*/
+void EmitSkyPolys (msurface_t *fa)
+{
+ glpoly_t *p;
+ float *v;
+ int i;
+ float s, t;
+ vec3_t dir;
+ float length;
+
+ for (p=fa->polys ; p ; p=p->next)
+ {
+ glBegin (GL_POLYGON);
+ for (i=0,v=p->verts[0] ; i<p->numverts ; i++, v+=VERTEXSIZE)
+ {
+ VectorSubtract (v, r_origin, dir);
+ dir[2] *= 3; // flatten the sphere
+
+ length = dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2];
+ length = sqrt (length);
+ length = 6*63/length;
+
+ dir[0] *= length;
+ dir[1] *= length;
+
+ s = (speedscale + dir[0]) * (1.0/128);
+ t = (speedscale + dir[1]) * (1.0/128);
+
+ glTexCoord2f (s, t);
+ glVertex3fv (v);
+ }
+ glEnd ();
+ }
+}
+
+/*
+===============
+EmitBothSkyLayers
+
+Does a sky warp on the pre-fragmented glpoly_t chain
+This will be called for brushmodels, the world
+will have them chained together.
+===============
+*/
+void EmitBothSkyLayers (msurface_t *fa)
+{
+ int i;
+ int lindex;
+ float *vec;
+
+ GL_DisableMultitexture();
+
+ GL_Bind (solidskytexture);
+ speedscale = realtime*8;
+ speedscale -= (int)speedscale & ~127 ;
+
+ EmitSkyPolys (fa);
+
+ glEnable (GL_BLEND);
+ GL_Bind (alphaskytexture);
+ speedscale = realtime*16;
+ speedscale -= (int)speedscale & ~127 ;
+
+ EmitSkyPolys (fa);
+
+ glDisable (GL_BLEND);
+}
+
+#ifndef QUAKE2
+/*
+=================
+R_DrawSkyChain
+=================
+*/
+void R_DrawSkyChain (msurface_t *s)
+{
+ msurface_t *fa;
+
+ GL_DisableMultitexture();
+
+ // used when gl_texsort is on
+ GL_Bind(solidskytexture);
+ speedscale = realtime*8;
+ speedscale -= (int)speedscale & ~127 ;
+
+ for (fa=s ; fa ; fa=fa->texturechain)
+ EmitSkyPolys (fa);
+
+ glEnable (GL_BLEND);
+ GL_Bind (alphaskytexture);
+ speedscale = realtime*16;
+ speedscale -= (int)speedscale & ~127 ;
+
+ for (fa=s ; fa ; fa=fa->texturechain)
+ EmitSkyPolys (fa);
+
+ glDisable (GL_BLEND);
+}
+
+#endif
+
+/*
+=================================================================
+
+ Quake 2 environment sky
+
+=================================================================
+*/
+
+#ifdef QUAKE2
+
+
+#define SKY_TEX 2000
+
+/*
+=================================================================
+
+ PCX Loading
+
+=================================================================
+*/
+
+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 data; // unbounded
+} pcx_t;
+
+byte *pcx_rgb;
+
+/*
+============
+LoadPCX
+============
+*/
+void LoadPCX (FILE *f)
+{
+ pcx_t *pcx, pcxbuf;
+ byte palette[768];
+ byte *pix;
+ int x, y;
+ int dataByte, runLength;
+ int count;
+
+//
+// parse the PCX file
+//
+ fread (&pcxbuf, 1, sizeof(pcxbuf), f);
+
+ pcx = &pcxbuf;
+
+ if (pcx->manufacturer != 0x0a
+ || pcx->version != 5
+ || pcx->encoding != 1
+ || pcx->bits_per_pixel != 8
+ || pcx->xmax >= 320
+ || pcx->ymax >= 256)
+ {
+ Con_Printf ("Bad pcx file\n");
+ return;
+ }
+
+ // seek to palette
+ fseek (f, -768, SEEK_END);
+ fread (palette, 1, 768, f);
+
+ fseek (f, sizeof(pcxbuf) - 4, SEEK_SET);
+
+ count = (pcx->xmax+1) * (pcx->ymax+1);
+ pcx_rgb = malloc( count * 4);
+
+ for (y=0 ; y<=pcx->ymax ; y++)
+ {
+ pix = pcx_rgb + 4*y*(pcx->xmax+1);
+ for (x=0 ; x<=pcx->ymax ; )
+ {
+ dataByte = fgetc(f);
+
+ if((dataByte & 0xC0) == 0xC0)
+ {
+ runLength = dataByte & 0x3F;
+ dataByte = fgetc(f);
+ }
+ else
+ runLength = 1;
+
+ while(runLength-- > 0)
+ {
+ pix[0] = palette[dataByte*3];
+ pix[1] = palette[dataByte*3+1];
+ pix[2] = palette[dataByte*3+2];
+ pix[3] = 255;
+ pix += 4;
+ x++;
+ }
+ }
+ }
+}
+
+/*
+=========================================================
+
+TARGA LOADING
+
+=========================================================
+*/
+
+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;
+
+
+TargaHeader targa_header;
+byte *targa_rgba;
+
+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);
+}
+
+
+/*
+=============
+LoadTGA
+=============
+*/
+void LoadTGA (FILE *fin)
+{
+ int columns, rows, numPixels;
+ byte *pixbuf;
+ int row, column;
+
+ targa_header.id_length = fgetc(fin);
+ targa_header.colormap_type = fgetc(fin);
+ targa_header.image_type = fgetc(fin);
+
+ targa_header.colormap_index = fgetLittleShort(fin);
+ targa_header.colormap_length = fgetLittleShort(fin);
+ targa_header.colormap_size = fgetc(fin);
+ targa_header.x_origin = fgetLittleShort(fin);
+ targa_header.y_origin = fgetLittleShort(fin);
+ targa_header.width = fgetLittleShort(fin);
+ targa_header.height = fgetLittleShort(fin);
+ targa_header.pixel_size = fgetc(fin);
+ targa_header.attributes = fgetc(fin);
+
+ if (targa_header.image_type!=2
+ && targa_header.image_type!=10)
+ Sys_Error ("LoadTGA: Only type 2 and 10 targa RGB images supported\n");
+
+ if (targa_header.colormap_type !=0
+ || (targa_header.pixel_size!=32 && targa_header.pixel_size!=24))
+ Sys_Error ("Texture_LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n");
+
+ columns = targa_header.width;
+ rows = targa_header.height;
+ numPixels = columns * rows;
+
+ targa_rgba = malloc (numPixels*4);
+
+ if (targa_header.id_length != 0)
+ fseek(fin, targa_header.id_length, SEEK_CUR); // skip TARGA image comment
+
+ if (targa_header.image_type==2) { // Uncompressed, RGB images
+ 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 24:
+
+ blue = getc(fin);
+ green = getc(fin);
+ red = getc(fin);
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = 255;
+ break;
+ case 32:
+ blue = getc(fin);
+ green = getc(fin);
+ red = getc(fin);
+ alphabyte = getc(fin);
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = alphabyte;
+ break;
+ }
+ }
+ }
+ }
+ else if (targa_header.image_type==10) { // Runlength encoded RGB images
+ unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
+ for(row=rows-1; row>=0; row--) {
+ pixbuf = targa_rgba + row*columns*4;
+ for(column=0; column<columns; ) {
+ packetHeader=getc(fin);
+ packetSize = 1 + (packetHeader & 0x7f);
+ if (packetHeader & 0x80) { // run-length packet
+ switch (targa_header.pixel_size) {
+ case 24:
+ blue = getc(fin);
+ green = getc(fin);
+ red = getc(fin);
+ alphabyte = 255;
+ break;
+ case 32:
+ blue = getc(fin);
+ green = getc(fin);
+ red = getc(fin);
+ alphabyte = getc(fin);
+ 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 = getc(fin);
+ green = getc(fin);
+ red = getc(fin);
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = 255;
+ break;
+ case 32:
+ blue = getc(fin);
+ green = getc(fin);
+ red = getc(fin);
+ alphabyte = getc(fin);
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = alphabyte;
+ 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:;
+ }
+ }
+
+ fclose(fin);
+}
+
+/*
+==================
+R_LoadSkys
+==================
+*/
+char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"};
+void R_LoadSkys (void)
+{
+ int i;
+ FILE *f;
+ char name[64];
+
+ for (i=0 ; i<6 ; i++)
+ {
+ GL_Bind (SKY_TEX + i);
+ sprintf (name, "gfx/env/bkgtst%s.tga", suf[i]);
+ COM_FOpenFile (name, &f);
+ if (!f)
+ {
+ Con_Printf ("Couldn't load %s\n", name);
+ continue;
+ }
+ LoadTGA (f);
+// LoadPCX (f);
+
+ glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, targa_rgba);
+// glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, pcx_rgb);
+
+ free (targa_rgba);
+// free (pcx_rgb);
+
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ }
+}
+
+
+vec3_t skyclip[6] = {
+ {1,1,0},
+ {1,-1,0},
+ {0,-1,1},
+ {0,1,1},
+ {1,0,1},
+ {-1,0,1}
+};
+int c_sky;
+
+// 1 = s, 2 = t, 3 = 2048
+int st_to_vec[6][3] =
+{
+ {3,-1,2},
+ {-3,1,2},
+
+ {1,3,2},
+ {-1,-3,2},
+
+ {-2,-1,3}, // 0 degrees yaw, look straight up
+ {2,-1,-3} // look straight down
+
+// {-1,2,3},
+// {1,2,-3}
+};
+
+// s = [0]/[2], t = [1]/[2]
+int vec_to_st[6][3] =
+{
+ {-2,3,1},
+ {2,3,-1},
+
+ {1,3,2},
+ {-1,3,-2},
+
+ {-2,-1,3},
+ {-2,1,-3}
+
+// {-1,2,3},
+// {1,2,-3}
+};
+
+float skymins[2][6], skymaxs[2][6];
+
+void DrawSkyPolygon (int nump, vec3_t vecs)
+{
+ int i,j;
+ vec3_t v, av;
+ float s, t, dv;
+ int axis;
+ float *vp;
+
+ c_sky++;
+#if 0
+glBegin (GL_POLYGON);
+for (i=0 ; i<nump ; i++, vecs+=3)
+{
+ VectorAdd(vecs, r_origin, v);
+ glVertex3fv (v);
+}
+glEnd();
+return;
+#endif
+ // decide which face it maps to
+ VectorCopy (vec3_origin, v);
+ for (i=0, vp=vecs ; i<nump ; i++, vp+=3)
+ {
+ VectorAdd (vp, v, v);
+ }
+ av[0] = fabs(v[0]);
+ av[1] = fabs(v[1]);
+ av[2] = fabs(v[2]);
+ if (av[0] > av[1] && av[0] > av[2])
+ {
+ if (v[0] < 0)
+ axis = 1;
+ else
+ axis = 0;
+ }
+ else if (av[1] > av[2] && av[1] > av[0])
+ {
+ if (v[1] < 0)
+ axis = 3;
+ else
+ axis = 2;
+ }
+ else
+ {
+ if (v[2] < 0)
+ axis = 5;
+ else
+ axis = 4;
+ }
+
+ // project new texture coords
+ for (i=0 ; i<nump ; i++, vecs+=3)
+ {
+ j = vec_to_st[axis][2];
+ if (j > 0)
+ dv = vecs[j - 1];
+ else
+ dv = -vecs[-j - 1];
+
+ j = vec_to_st[axis][0];
+ if (j < 0)
+ s = -vecs[-j -1] / dv;
+ else
+ s = vecs[j-1] / dv;
+ j = vec_to_st[axis][1];
+ if (j < 0)
+ t = -vecs[-j -1] / dv;
+ else
+ t = vecs[j-1] / dv;
+
+ if (s < skymins[0][axis])
+ skymins[0][axis] = s;
+ if (t < skymins[1][axis])
+ skymins[1][axis] = t;
+ if (s > skymaxs[0][axis])
+ skymaxs[0][axis] = s;
+ if (t > skymaxs[1][axis])
+ skymaxs[1][axis] = t;
+ }
+}
+
+#define MAX_CLIP_VERTS 64
+void ClipSkyPolygon (int nump, vec3_t vecs, int stage)
+{
+ float *norm;
+ float *v;
+ qboolean front, back;
+ float d, e;
+ float dists[MAX_CLIP_VERTS];
+ int sides[MAX_CLIP_VERTS];
+ vec3_t newv[2][MAX_CLIP_VERTS];
+ int newc[2];
+ int i, j;
+
+ if (nump > MAX_CLIP_VERTS-2)
+ Sys_Error ("ClipSkyPolygon: MAX_CLIP_VERTS");
+ if (stage == 6)
+ { // fully clipped, so draw it
+ DrawSkyPolygon (nump, vecs);
+ return;
+ }
+
+ front = back = false;
+ norm = skyclip[stage];
+ for (i=0, v = vecs ; i<nump ; i++, v+=3)
+ {
+ d = DotProduct (v, norm);
+ if (d > ON_EPSILON)
+ {
+ front = true;
+ sides[i] = SIDE_FRONT;
+ }
+ else if (d < ON_EPSILON)
+ {
+ back = true;
+ sides[i] = SIDE_BACK;
+ }
+ else
+ sides[i] = SIDE_ON;
+ dists[i] = d;
+ }
+
+ if (!front || !back)
+ { // not clipped
+ ClipSkyPolygon (nump, vecs, stage+1);
+ return;
+ }
+
+ // clip it
+ sides[i] = sides[0];
+ dists[i] = dists[0];
+ VectorCopy (vecs, (vecs+(i*3)) );
+ newc[0] = newc[1] = 0;
+
+ for (i=0, v = vecs ; i<nump ; i++, v+=3)
+ {
+ switch (sides[i])
+ {
+ case SIDE_FRONT:
+ VectorCopy (v, newv[0][newc[0]]);
+ newc[0]++;
+ break;
+ case SIDE_BACK:
+ VectorCopy (v, newv[1][newc[1]]);
+ newc[1]++;
+ break;
+ case SIDE_ON:
+ VectorCopy (v, newv[0][newc[0]]);
+ newc[0]++;
+ VectorCopy (v, newv[1][newc[1]]);
+ newc[1]++;
+ break;
+ }
+
+ if (sides[i] == SIDE_ON || sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
+ continue;
+
+ d = dists[i] / (dists[i] - dists[i+1]);
+ for (j=0 ; j<3 ; j++)
+ {
+ e = v[j] + d*(v[j+3] - v[j]);
+ newv[0][newc[0]][j] = e;
+ newv[1][newc[1]][j] = e;
+ }
+ newc[0]++;
+ newc[1]++;
+ }
+
+ // continue
+ ClipSkyPolygon (newc[0], newv[0][0], stage+1);
+ ClipSkyPolygon (newc[1], newv[1][0], stage+1);
+}
+
+/*
+=================
+R_DrawSkyChain
+=================
+*/
+void R_DrawSkyChain (msurface_t *s)
+{
+ msurface_t *fa;
+
+ int i;
+ vec3_t verts[MAX_CLIP_VERTS];
+ glpoly_t *p;
+
+ c_sky = 0;
+ GL_Bind(solidskytexture);
+
+ // calculate vertex values for sky box
+
+ for (fa=s ; fa ; fa=fa->texturechain)
+ {
+ for (p=fa->polys ; p ; p=p->next)
+ {
+ for (i=0 ; i<p->numverts ; i++)
+ {
+ VectorSubtract (p->verts[i], r_origin, verts[i]);
+ }
+ ClipSkyPolygon (p->numverts, verts[0], 0);
+ }
+ }
+}
+
+
+/*
+==============
+R_ClearSkyBox
+==============
+*/
+void R_ClearSkyBox (void)
+{
+ int i;
+
+ for (i=0 ; i<6 ; i++)
+ {
+ skymins[0][i] = skymins[1][i] = 9999;
+ skymaxs[0][i] = skymaxs[1][i] = -9999;
+ }
+}
+
+
+void MakeSkyVec (float s, float t, int axis)
+{
+ vec3_t v, b;
+ int j, k;
+
+ b[0] = s*2048;
+ b[1] = t*2048;
+ b[2] = 2048;
+
+ for (j=0 ; j<3 ; j++)
+ {
+ k = st_to_vec[axis][j];
+ if (k < 0)
+ v[j] = -b[-k - 1];
+ else
+ v[j] = b[k - 1];
+ v[j] += r_origin[j];
+ }
+
+ // avoid bilerp seam
+ s = (s+1)*0.5;
+ t = (t+1)*0.5;
+
+ if (s < 1.0/512)
+ s = 1.0/512;
+ else if (s > 511.0/512)
+ s = 511.0/512;
+ if (t < 1.0/512)
+ t = 1.0/512;
+ else if (t > 511.0/512)
+ t = 511.0/512;
+
+ t = 1.0 - t;
+ glTexCoord2f (s, t);
+ glVertex3fv (v);
+}
+
+/*
+==============
+R_DrawSkyBox
+==============
+*/
+int skytexorder[6] = {0,2,1,3,4,5};
+void R_DrawSkyBox (void)
+{
+ int i, j, k;
+ vec3_t v;
+ float s, t;
+
+#if 0
+glEnable (GL_BLEND);
+glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+glColor4f (1,1,1,0.5);
+glDisable (GL_DEPTH_TEST);
+#endif
+ for (i=0 ; i<6 ; i++)
+ {
+ if (skymins[0][i] >= skymaxs[0][i]
+ || skymins[1][i] >= skymaxs[1][i])
+ continue;
+
+ GL_Bind (SKY_TEX+skytexorder[i]);
+#if 0
+skymins[0][i] = -1;
+skymins[1][i] = -1;
+skymaxs[0][i] = 1;
+skymaxs[1][i] = 1;
+#endif
+ glBegin (GL_QUADS);
+ MakeSkyVec (skymins[0][i], skymins[1][i], i);
+ MakeSkyVec (skymins[0][i], skymaxs[1][i], i);
+ MakeSkyVec (skymaxs[0][i], skymaxs[1][i], i);
+ MakeSkyVec (skymaxs[0][i], skymins[1][i], i);
+ glEnd ();
+ }
+#if 0
+glDisable (GL_BLEND);
+glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+glColor4f (1,1,1,0.5);
+glEnable (GL_DEPTH_TEST);
+#endif
+}
+
+
+#endif
+
+//===============================================================
+
+/*
+=============
+R_InitSky
+
+A sky texture is 256*128, with the right side being a masked overlay
+==============
+*/
+void R_InitSky (texture_t *mt)
+{
+ int i, j, p;
+ byte *src;
+ unsigned trans[128*128];
+ unsigned transpix;
+ int r, g, b;
+ unsigned *rgba;
+ extern int skytexturenum;
+
+ src = (byte *)mt + mt->offsets[0];
+
+ // make an average value for the back to avoid
+ // a fringe on the top level
+
+ r = g = b = 0;
+ for (i=0 ; i<128 ; i++)
+ for (j=0 ; j<128 ; j++)
+ {
+ p = src[i*256 + j + 128];
+ rgba = &d_8to24table[p];
+ trans[(i*128) + j] = *rgba;
+ r += ((byte *)rgba)[0];
+ g += ((byte *)rgba)[1];
+ b += ((byte *)rgba)[2];
+ }
+
+ ((byte *)&transpix)[0] = r/(128*128);
+ ((byte *)&transpix)[1] = g/(128*128);
+ ((byte *)&transpix)[2] = b/(128*128);
+ ((byte *)&transpix)[3] = 0;
+
+
+ if (!solidskytexture)
+ solidskytexture = texture_extension_number++;
+ GL_Bind (solidskytexture );
+ glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+
+ for (i=0 ; i<128 ; i++)
+ for (j=0 ; j<128 ; j++)
+ {
+ p = src[i*256 + j];
+ if (p == 0)
+ trans[(i*128) + j] = transpix;
+ else
+ trans[(i*128) + j] = d_8to24table[p];
+ }
+
+ if (!alphaskytexture)
+ alphaskytexture = texture_extension_number++;
+ GL_Bind(alphaskytexture);
+ glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+}
+
--- /dev/null
+++ b/WinQuake/glquake2.h
@@ -1,0 +1,209 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program 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.
+
+This program 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 this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// disable data conversion warnings
+
+#pragma warning(disable : 4244) // MIPS
+#pragma warning(disable : 4136) // X86
+#pragma warning(disable : 4051) // ALPHA
+
+#include <windows.h>
+
+#include <gl\gl.h>
+#include <gl\glu.h>
+
+void GL_BeginRendering (int *x, int *y, int *width, int *height);
+void GL_EndRendering (void);
+
+
+// Function prototypes for the Texture Object Extension routines
+typedef GLboolean (APIENTRY *ARETEXRESFUNCPTR)(GLsizei, const GLuint *,
+ const GLboolean *);
+typedef void (APIENTRY *BINDTEXFUNCPTR)(GLenum, GLuint);
+typedef void (APIENTRY *DELTEXFUNCPTR)(GLsizei, const GLuint *);
+typedef void (APIENTRY *GENTEXFUNCPTR)(GLsizei, GLuint *);
+typedef GLboolean (APIENTRY *ISTEXFUNCPTR)(GLuint);
+typedef void (APIENTRY *PRIORTEXFUNCPTR)(GLsizei, const GLuint *,
+ const GLclampf *);
+typedef void (APIENTRY *TEXSUBIMAGEPTR)(int, int, int, int, int, int, int, int, void *);
+
+extern BINDTEXFUNCPTR bindTexFunc;
+extern DELTEXFUNCPTR delTexFunc;
+extern TEXSUBIMAGEPTR TexSubImage2DFunc;
+
+extern int texture_extension_number;
+extern int texture_mode;
+
+extern float gldepthmin, gldepthmax;
+
+void GL_Upload32 (unsigned *data, int width, int height, qboolean mipmap, qboolean alpha, qboolean modulate);
+void GL_Upload8 (byte *data, int width, int height, qboolean mipmap, qboolean alpha, qboolean modulate);
+int GL_LoadTexture (char *identifier, int width, int height, byte *data, int mipmap, int alpha, int modulate);
+int GL_FindTexture (char *identifier);
+
+typedef struct
+{
+ float x, y, z;
+ float s, t;
+ float r, g, b;
+} glvert_t;
+
+extern glvert_t glv;
+
+extern int glx, gly, glwidth, glheight;
+
+extern PROC glArrayElementEXT;
+extern PROC glColorPointerEXT;
+extern PROC glTexturePointerEXT;
+extern PROC glVertexPointerEXT;
+
+
+// r_local.h -- private refresh defs
+
+#define MAXALIASVERTS 2000 // TODO: tune this
+
+#define ALIAS_BASE_SIZE_RATIO (1.0 / 11.0)
+ // normalizing factor so player model works out to about
+ // 1 pixel per triangle
+#define MAX_LBM_HEIGHT 480
+
+#define TILE_SIZE 128 // size of textures generated by R_GenTiledSurf
+
+#define SKYSHIFT 7
+#define SKYSIZE (1 << SKYSHIFT)
+#define SKYMASK (SKYSIZE - 1)
+
+#define BACKFACE_EPSILON 0.01
+
+
+void R_TimeRefresh_f (void);
+void R_ReadPointFile_f (void);
+texture_t *R_TextureAnimation (texture_t *base);
+
+typedef struct surfcache_s
+{
+ struct surfcache_s *next;
+ struct surfcache_s **owner; // NULL is an empty chunk of memory
+ int lightadj[MAXLIGHTMAPS]; // checked for strobe flush
+ int dlight;
+ int size; // including header
+ unsigned width;
+ unsigned height; // DEBUG only needed for debug
+ float mipscale;
+ struct texture_s *texture; // checked for animating textures
+ byte data[4]; // width*height elements
+} surfcache_t;
+
+
+typedef struct
+{
+ pixel_t *surfdat; // destination for generated surface
+ int rowbytes; // destination logical width in bytes
+ msurface_t *surf; // description for surface to generate
+ fixed8_t lightadj[MAXLIGHTMAPS];
+ // adjust for lightmap levels for dynamic lighting
+ texture_t *texture; // corrected for animating textures
+ int surfmip; // mipmapped ratio of surface texels / world pixels
+ int surfwidth; // in mipmapped texels
+ int surfheight; // in mipmapped texels
+} drawsurf_t;
+
+
+typedef enum {
+ pt_static, pt_grav, pt_slowgrav, pt_fire, pt_explode, pt_explode2, pt_blob, pt_blob2
+} ptype_t;
+
+// !!! if this is changed, it must be changed in d_ifacea.h too !!!
+typedef struct particle_s
+{
+// driver-usable fields
+ vec3_t org;
+ float color;
+// drivers never touch the following fields
+ struct particle_s *next;
+ vec3_t vel;
+ float ramp;
+ float die;
+ ptype_t type;
+} particle_t;
+
+
+//====================================================
+
+
+extern entity_t r_worldentity;
+extern qboolean r_cache_thrash; // compatability
+extern vec3_t modelorg, r_entorigin;
+extern entity_t *currententity;
+extern int r_visframecount; // ??? what difs?
+extern int r_framecount;
+extern mplane_t frustum[4];
+extern int c_brush_polys, c_alias_polys;
+
+
+//
+// view origin
+//
+extern vec3_t vup;
+extern vec3_t vpn;
+extern vec3_t vright;
+extern vec3_t r_origin;
+
+//
+// screen size info
+//
+extern refdef_t r_refdef;
+extern mleaf_t *r_viewleaf, *r_oldviewleaf;
+extern texture_t *r_notexture_mip;
+extern int d_lightstylevalue[256]; // 8.8 fraction of base light value
+
+extern qboolean envmap;
+extern int currenttexture;
+extern int particletexture;
+extern int playertextures;
+
+extern int skytexturenum; // index in cl.loadmodel, not gl texture object
+
+extern cvar_t r_drawentities;
+extern cvar_t r_drawworld;
+extern cvar_t r_drawviewmodel;
+extern cvar_t r_speeds;
+extern cvar_t r_waterwarp;
+extern cvar_t r_fullbright;
+extern cvar_t r_lightmap;
+extern cvar_t r_shadows;
+extern cvar_t r_dynamic;
+
+extern cvar_t gl_clear;
+extern cvar_t gl_cull;
+extern cvar_t gl_poly;
+extern cvar_t gl_texsort;
+extern cvar_t gl_smoothmodels;
+extern cvar_t gl_affinemodels;
+extern cvar_t gl_fogblend;
+extern cvar_t gl_polyblend;
+extern cvar_t gl_keeptjunctions;
+extern cvar_t gl_reporttjunctions;
+
+extern int gl_lightmap_format;
+extern int gl_solid_format;
+extern int gl_alpha_format;
+
+void R_TranslatePlayerSkin (int playernum);
+void GL_Bind (int texnum);
--- /dev/null
+++ b/WinQuake/host_cmd.c
@@ -1,0 +1,1925 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program 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.
+
+This program 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 this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "quakedef.h"
+
+extern cvar_t pausable;
+
+int current_skill;
+
+void Mod_Print (void);
+
+/*
+==================
+Host_Quit_f
+==================
+*/
+
+extern void M_Menu_Quit_f (void);
+
+void Host_Quit_f (void)
+{
+ if (key_dest != key_console && cls.state != ca_dedicated)
+ {
+ M_Menu_Quit_f ();
+ return;
+ }
+ CL_Disconnect ();
+ Host_ShutdownServer(false);
+
+ Sys_Quit ();
+}
+
+
+/*
+==================
+Host_Status_f
+==================
+*/
+void Host_Status_f (void)
+{
+ client_t *client;
+ int seconds;
+ int minutes;
+ int hours = 0;
+ int j;
+ void (*print) (char *fmt, ...);
+
+ if (cmd_source == src_command)
+ {
+ if (!sv.active)
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+ print = Con_Printf;
+ }
+ else
+ print = SV_ClientPrintf;
+
+ print ("host: %s\n", Cvar_VariableString ("hostname"));
+ print ("version: %4.2f\n", VERSION);
+ if (tcpipAvailable)
+ print ("tcp/ip: %s\n", my_tcpip_address);
+ if (ipxAvailable)
+ print ("ipx: %s\n", my_ipx_address);
+ print ("map: %s\n", sv.name);
+ print ("players: %i active (%i max)\n\n", net_activeconnections, svs.maxclients);
+ for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
+ {
+ if (!client->active)
+ continue;
+ seconds = (int)(net_time - client->netconnection->connecttime);
+ minutes = seconds / 60;
+ if (minutes)
+ {
+ seconds -= (minutes * 60);
+ hours = minutes / 60;
+ if (hours)
+ minutes -= (hours * 60);
+ }
+ else
+ hours = 0;
+ print ("#%-2u %-16.16s %3i %2i:%02i:%02i\n", j+1, client->name, (int)client->edict->v.frags, hours, minutes, seconds);
+ print (" %s\n", client->netconnection->address);
+ }
+}
+
+
+/*
+==================
+Host_God_f
+
+Sets client to godmode
+==================
+*/
+void Host_God_f (void)
+{
+ if (cmd_source == src_command)
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+
+ if (pr_global_struct->deathmatch && !host_client->privileged)
+ return;
+
+ sv_player->v.flags = (int)sv_player->v.flags ^ FL_GODMODE;
+ if (!((int)sv_player->v.flags & FL_GODMODE) )
+ SV_ClientPrintf ("godmode OFF\n");
+ else
+ SV_ClientPrintf ("godmode ON\n");
+}
+
+void Host_Notarget_f (void)
+{
+ if (cmd_source == src_command)
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+
+ if (pr_global_struct->deathmatch && !host_client->privileged)
+ return;
+
+ sv_player->v.flags = (int)sv_player->v.flags ^ FL_NOTARGET;
+ if (!((int)sv_player->v.flags & FL_NOTARGET) )
+ SV_ClientPrintf ("notarget OFF\n");
+ else
+ SV_ClientPrintf ("notarget ON\n");
+}
+
+qboolean noclip_anglehack;
+
+void Host_Noclip_f (void)
+{
+ if (cmd_source == src_command)
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+
+ if (pr_global_struct->deathmatch && !host_client->privileged)
+ return;
+
+ if (sv_player->v.movetype != MOVETYPE_NOCLIP)
+ {
+ noclip_anglehack = true;
+ sv_player->v.movetype = MOVETYPE_NOCLIP;
+ SV_ClientPrintf ("noclip ON\n");
+ }
+ else
+ {
+ noclip_anglehack = false;
+ sv_player->v.movetype = MOVETYPE_WALK;
+ SV_ClientPrintf ("noclip OFF\n");
+ }
+}
+
+/*
+==================
+Host_Fly_f
+
+Sets client to flymode
+==================
+*/
+void Host_Fly_f (void)
+{
+ if (cmd_source == src_command)
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+
+ if (pr_global_struct->deathmatch && !host_client->privileged)
+ return;
+
+ if (sv_player->v.movetype != MOVETYPE_FLY)
+ {
+ sv_player->v.movetype = MOVETYPE_FLY;
+ SV_ClientPrintf ("flymode ON\n");
+ }
+ else
+ {
+ sv_player->v.movetype = MOVETYPE_WALK;
+ SV_ClientPrintf ("flymode OFF\n");
+ }
+}
+
+
+/*
+==================
+Host_Ping_f
+
+==================
+*/
+void Host_Ping_f (void)
+{
+ int i, j;
+ float total;
+ client_t *client;
+
+ if (cmd_source == src_command)
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+
+ SV_ClientPrintf ("Client ping times:\n");
+ for (i=0, client = svs.clients ; i<svs.maxclients ; i++, client++)
+ {
+ if (!client->active)
+ continue;
+ total = 0;
+ for (j=0 ; j<NUM_PING_TIMES ; j++)
+ total+=client->ping_times[j];
+ total /= NUM_PING_TIMES;
+ SV_ClientPrintf ("%4i %s\n", (int)(total*1000), client->name);
+ }
+}
+
+/*
+===============================================================================
+
+SERVER TRANSITIONS
+
+===============================================================================
+*/
+
+
+/*
+======================
+Host_Map_f
+
+handle a
+map <servername>
+command from the console. Active clients are kicked off.
+======================
+*/
+void Host_Map_f (void)
+{
+ int i;
+ char name[MAX_QPATH];
+
+ if (cmd_source != src_command)
+ return;
+
+ cls.demonum = -1; // stop demo loop in case this fails
+
+ CL_Disconnect ();
+ Host_ShutdownServer(false);
+
+ key_dest = key_game; // remove console or menu
+ SCR_BeginLoadingPlaque ();
+
+ cls.mapstring[0] = 0;
+ for (i=0 ; i<Cmd_Argc() ; i++)
+ {
+ strcat (cls.mapstring, Cmd_Argv(i));
+ strcat (cls.mapstring, " ");
+ }
+ strcat (cls.mapstring, "\n");
+
+ svs.serverflags = 0; // haven't completed an episode yet
+ strcpy (name, Cmd_Argv(1));
+#ifdef QUAKE2
+ SV_SpawnServer (name, NULL);
+#else
+ SV_SpawnServer (name);
+#endif
+ if (!sv.active)
+ return;
+
+ if (cls.state != ca_dedicated)
+ {
+ strcpy (cls.spawnparms, "");
+
+ for (i=2 ; i<Cmd_Argc() ; i++)
+ {
+ strcat (cls.spawnparms, Cmd_Argv(i));
+ strcat (cls.spawnparms, " ");
+ }
+
+ Cmd_ExecuteString ("connect local", src_command);
+ }
+}
+
+/*
+==================
+Host_Changelevel_f
+
+Goes to a new map, taking all clients along
+==================
+*/
+void Host_Changelevel_f (void)
+{
+#ifdef QUAKE2
+ char level[MAX_QPATH];
+ char _startspot[MAX_QPATH];
+ char *startspot;
+
+ if (Cmd_Argc() < 2)
+ {
+ Con_Printf ("changelevel <levelname> : continue game on a new level\n");
+ return;
+ }
+ if (!sv.active || cls.demoplayback)
+ {
+ Con_Printf ("Only the server may changelevel\n");
+ return;
+ }
+
+ strcpy (level, Cmd_Argv(1));
+ if (Cmd_Argc() == 2)
+ startspot = NULL;
+ else
+ {
+ strcpy (_startspot, Cmd_Argv(2));
+ startspot = _startspot;
+ }
+
+ SV_SaveSpawnparms ();
+ SV_SpawnServer (level, startspot);
+#else
+ char level[MAX_QPATH];
+
+ if (Cmd_Argc() != 2)
+ {
+ Con_Printf ("changelevel <levelname> : continue game on a new level\n");
+ return;
+ }
+ if (!sv.active || cls.demoplayback)
+ {
+ Con_Printf ("Only the server may changelevel\n");
+ return;
+ }
+ SV_SaveSpawnparms ();
+ strcpy (level, Cmd_Argv(1));
+ SV_SpawnServer (level);
+#endif
+}
+
+/*
+==================
+Host_Restart_f
+
+Restarts the current server for a dead player
+==================
+*/
+void Host_Restart_f (void)
+{
+ char mapname[MAX_QPATH];
+#ifdef QUAKE2
+ char startspot[MAX_QPATH];
+#endif
+
+ if (cls.demoplayback || !sv.active)
+ return;
+
+ if (cmd_source != src_command)
+ return;
+ strcpy (mapname, sv.name); // must copy out, because it gets cleared
+ // in sv_spawnserver
+#ifdef QUAKE2
+ strcpy(startspot, sv.startspot);
+ SV_SpawnServer (mapname, startspot);
+#else
+ SV_SpawnServer (mapname);
+#endif
+}
+
+/*
+==================
+Host_Reconnect_f
+
+This command causes the client to wait for the signon messages again.
+This is sent just before a server changes levels
+==================
+*/
+void Host_Reconnect_f (void)
+{
+ SCR_BeginLoadingPlaque ();
+ cls.signon = 0; // need new connection messages
+}
+
+/*
+=====================
+Host_Connect_f
+
+User command to connect to server
+=====================
+*/
+void Host_Connect_f (void)
+{
+ char name[MAX_QPATH];
+
+ cls.demonum = -1; // stop demo loop in case this fails
+ if (cls.demoplayback)
+ {
+ CL_StopPlayback ();
+ CL_Disconnect ();
+ }
+ strcpy (name, Cmd_Argv(1));
+ CL_EstablishConnection (name);
+ Host_Reconnect_f ();
+}
+
+
+/*
+===============================================================================
+
+LOAD / SAVE GAME
+
+===============================================================================
+*/
+
+#define SAVEGAME_VERSION 5
+
+/*
+===============
+Host_SavegameComment
+
+Writes a SAVEGAME_COMMENT_LENGTH character comment describing the current
+===============
+*/
+void Host_SavegameComment (char *text)
+{
+ int i;
+ char kills[20];
+
+ for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
+ text[i] = ' ';
+ memcpy (text, cl.levelname, strlen(cl.levelname));
+ sprintf (kills,"kills:%3i/%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]);
+ memcpy (text+22, kills, strlen(kills));
+// convert space to _ to make stdio happy
+ for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
+ if (text[i] == ' ')
+ text[i] = '_';
+ text[SAVEGAME_COMMENT_LENGTH] = '\0';
+}
+
+
+/*
+===============
+Host_Savegame_f
+===============
+*/
+void Host_Savegame_f (void)
+{
+ char name[256];
+ FILE *f;
+ int i;
+ char comment[SAVEGAME_COMMENT_LENGTH+1];
+
+ if (cmd_source != src_command)
+ return;
+
+ if (!sv.active)
+ {
+ Con_Printf ("Not playing a local game.\n");
+ return;
+ }
+
+ if (cl.intermission)
+ {
+ Con_Printf ("Can't save in intermission.\n");
+ return;
+ }
+
+ if (svs.maxclients != 1)
+ {
+ Con_Printf ("Can't save multiplayer games.\n");
+ return;
+ }
+
+ if (Cmd_Argc() != 2)
+ {
+ Con_Printf ("save <savename> : save a game\n");
+ return;
+ }
+
+ if (strstr(Cmd_Argv(1), ".."))
+ {
+ Con_Printf ("Relative pathnames are not allowed.\n");
+ return;
+ }
+
+ for (i=0 ; i<svs.maxclients ; i++)
+ {
+ if (svs.clients[i].active && (svs.clients[i].edict->v.health <= 0) )
+ {
+ Con_Printf ("Can't savegame with a dead player\n");
+ return;
+ }
+ }
+
+ sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
+ COM_DefaultExtension (name, ".sav");
+
+ Con_Printf ("Saving game to %s...\n", name);
+ f = fopen (name, "w");
+ if (!f)
+ {
+ Con_Printf ("ERROR: couldn't open.\n");
+ return;
+ }
+
+ fprintf (f, "%i\n", SAVEGAME_VERSION);
+ Host_SavegameComment (comment);
+ fprintf (f, "%s\n", comment);
+ for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
+ fprintf (f, "%f\n", svs.clients->spawn_parms[i]);
+ fprintf (f, "%d\n", current_skill);
+ fprintf (f, "%s\n", sv.name);
+ fprintf (f, "%f\n",sv.time);
+
+// write the light styles
+
+ for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
+ {
+ if (sv.lightstyles[i])
+ fprintf (f, "%s\n", sv.lightstyles[i]);
+ else
+ fprintf (f,"m\n");
+ }
+
+
+ ED_WriteGlobals (f);
+ for (i=0 ; i<sv.num_edicts ; i++)
+ {
+ ED_Write (f, EDICT_NUM(i));
+ fflush (f);
+ }
+ fclose (f);
+ Con_Printf ("done.\n");
+}
+
+
+/*
+===============
+Host_Loadgame_f
+===============
+*/
+void Host_Loadgame_f (void)
+{
+ char name[MAX_OSPATH];
+ FILE *f;
+ char mapname[MAX_QPATH];
+ float time, tfloat;
+ char str[32768], *start;
+ int i, r;
+ edict_t *ent;
+ int entnum;
+ int version;
+ float spawn_parms[NUM_SPAWN_PARMS];
+
+ if (cmd_source != src_command)
+ return;
+
+ if (Cmd_Argc() != 2)
+ {
+ Con_Printf ("load <savename> : load a game\n");
+ return;
+ }
+
+ cls.demonum = -1; // stop demo loop in case this fails
+
+ sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
+ COM_DefaultExtension (name, ".sav");
+
+// we can't call SCR_BeginLoadingPlaque, because too much stack space has
+// been used. The menu calls it before stuffing loadgame command
+// SCR_BeginLoadingPlaque ();
+
+ Con_Printf ("Loading game from %s...\n", name);
+ f = fopen (name, "r");
+ if (!f)
+ {
+ Con_Printf ("ERROR: couldn't open.\n");
+ return;
+ }
+
+ fscanf (f, "%i\n", &version);
+ if (version != SAVEGAME_VERSION)
+ {
+ fclose (f);
+ Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
+ return;
+ }
+ fscanf (f, "%s\n", str);
+ for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
+ fscanf (f, "%f\n", &spawn_parms[i]);
+// this silliness is so we can load 1.06 save files, which have float skill values
+ fscanf (f, "%f\n", &tfloat);
+ current_skill = (int)(tfloat + 0.1);
+ Cvar_SetValue ("skill", (float)current_skill);
+
+#ifdef QUAKE2
+ Cvar_SetValue ("deathmatch", 0);
+ Cvar_SetValue ("coop", 0);
+ Cvar_SetValue ("teamplay", 0);
+#endif
+
+ fscanf (f, "%s\n",mapname);
+ fscanf (f, "%f\n",&time);
+
+ CL_Disconnect_f ();
+
+#ifdef QUAKE2
+ SV_SpawnServer (mapname, NULL);
+#else
+ SV_SpawnServer (mapname);
+#endif
+ if (!sv.active)
+ {
+ Con_Printf ("Couldn't load map\n");
+ return;
+ }
+ sv.paused = true; // pause until all clients connect
+ sv.loadgame = true;
+
+// load the light styles
+
+ for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
+ {
+ fscanf (f, "%s\n", str);
+ sv.lightstyles[i] = Hunk_Alloc (strlen(str)+1);
+ strcpy (sv.lightstyles[i], str);
+ }
+
+// load the edicts out of the savegame file
+ entnum = -1; // -1 is the globals
+ while (!feof(f))
+ {
+ for (i=0 ; i<sizeof(str)-1 ; i++)
+ {
+ r = fgetc (f);
+ if (r == EOF || !r)
+ break;
+ str[i] = r;
+ if (r == '}')
+ {
+ i++;
+ break;
+ }
+ }
+ if (i == sizeof(str)-1)
+ Sys_Error ("Loadgame buffer overflow");
+ str[i] = 0;
+ start = str;
+ start = COM_Parse(str);
+ if (!com_token[0])
+ break; // end of file
+ if (strcmp(com_token,"{"))
+ Sys_Error ("First token isn't a brace");
+
+ if (entnum == -1)
+ { // parse the global vars
+ ED_ParseGlobals (start);
+ }
+ else
+ { // parse an edict
+
+ ent = EDICT_NUM(entnum);
+ memset (&ent->v, 0, progs->entityfields * 4);
+ ent->free = false;
+ ED_ParseEdict (start, ent);
+
+ // link it into the bsp tree
+ if (!ent->free)
+ SV_LinkEdict (ent, false);
+ }
+
+ entnum++;
+ }
+
+ sv.num_edicts = entnum;
+ sv.time = time;
+
+ fclose (f);
+
+ for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
+ svs.clients->spawn_parms[i] = spawn_parms[i];
+
+ if (cls.state != ca_dedicated)
+ {
+ CL_EstablishConnection ("local");
+ Host_Reconnect_f ();
+ }
+}
+
+#ifdef QUAKE2
+void SaveGamestate()
+{
+ char name[256];
+ FILE *f;
+ int i;
+ char comment[SAVEGAME_COMMENT_LENGTH+1];
+ edict_t *ent;
+
+ sprintf (name, "%s/%s.gip", com_gamedir, sv.name);
+
+ Con_Printf ("Saving game to %s...\n", name);
+ f = fopen (name, "w");
+ if (!f)
+ {
+ Con_Printf ("ERROR: couldn't open.\n");
+ return;
+ }
+
+ fprintf (f, "%i\n", SAVEGAME_VERSION);
+ Host_SavegameComment (comment);
+ fprintf (f, "%s\n", comment);
+// for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
+// fprintf (f, "%f\n", svs.clients->spawn_parms[i]);
+ fprintf (f, "%f\n", skill.value);
+ fprintf (f, "%s\n", sv.name);
+ fprintf (f, "%f\n", sv.time);
+
+// write the light styles
+
+ for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
+ {
+ if (sv.lightstyles[i])
+ fprintf (f, "%s\n", sv.lightstyles[i]);
+ else
+ fprintf (f,"m\n");
+ }
+
+
+ for (i=svs.maxclients+1 ; i<sv.num_edicts ; i++)
+ {
+ ent = EDICT_NUM(i);
+ if ((int)ent->v.flags & FL_ARCHIVE_OVERRIDE)
+ continue;
+ fprintf (f, "%i\n",i);
+ ED_Write (f, ent);
+ fflush (f);
+ }
+ fclose (f);
+ Con_Printf ("done.\n");
+}
+
+int LoadGamestate(char *level, char *startspot)
+{
+ char name[MAX_OSPATH];
+ FILE *f;
+ char mapname[MAX_QPATH];
+ float time, sk;
+ char str[32768], *start;
+ int i, r;
+ edict_t *ent;
+ int entnum;
+ int version;
+// float spawn_parms[NUM_SPAWN_PARMS];
+
+ sprintf (name, "%s/%s.gip", com_gamedir, level);
+
+ Con_Printf ("Loading game from %s...\n", name);
+ f = fopen (name, "r");
+ if (!f)
+ {
+ Con_Printf ("ERROR: couldn't open.\n");
+ return -1;
+ }
+
+ fscanf (f, "%i\n", &version);
+ if (version != SAVEGAME_VERSION)
+ {
+ fclose (f);
+ Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
+ return -1;
+ }
+ fscanf (f, "%s\n", str);
+// for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
+// fscanf (f, "%f\n", &spawn_parms[i]);
+ fscanf (f, "%f\n", &sk);
+ Cvar_SetValue ("skill", sk);
+
+ fscanf (f, "%s\n",mapname);
+ fscanf (f, "%f\n",&time);
+
+ SV_SpawnServer (mapname, startspot);
+
+ if (!sv.active)
+ {
+ Con_Printf ("Couldn't load map\n");
+ return -1;
+ }
+
+// load the light styles
+ for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
+ {
+ fscanf (f, "%s\n", str);
+ sv.lightstyles[i] = Hunk_Alloc (strlen(str)+1);
+ strcpy (sv.lightstyles[i], str);
+ }
+
+// load the edicts out of the savegame file
+ while (!feof(f))
+ {
+ fscanf (f, "%i\n",&entnum);
+ for (i=0 ; i<sizeof(str)-1 ; i++)
+ {
+ r = fgetc (f);
+ if (r == EOF || !r)
+ break;
+ str[i] = r;
+ if (r == '}')
+ {
+ i++;
+ break;
+ }
+ }
+ if (i == sizeof(str)-1)
+ Sys_Error ("Loadgame buffer overflow");
+ str[i] = 0;
+ start = str;
+ start = COM_Parse(str);
+ if (!com_token[0])
+ break; // end of file
+ if (strcmp(com_token,"{"))
+ Sys_Error ("First token isn't a brace");
+
+ // parse an edict
+
+ ent = EDICT_NUM(entnum);
+ memset (&ent->v, 0, progs->entityfields * 4);
+ ent->free = false;
+ ED_ParseEdict (start, ent);
+
+ // link it into the bsp tree
+ if (!ent->free)
+ SV_LinkEdict (ent, false);
+ }
+
+// sv.num_edicts = entnum;
+ sv.time = time;
+ fclose (f);
+
+// for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
+// svs.clients->spawn_parms[i] = spawn_parms[i];
+
+ return 0;
+}
+
+// changing levels within a unit
+void Host_Changelevel2_f (void)
+{
+ char level[MAX_QPATH];
+ char _startspot[MAX_QPATH];
+ char *startspot;
+
+ if (Cmd_Argc() < 2)
+ {
+ Con_Printf ("changelevel2 <levelname> : continue game on a new level in the unit\n");
+ return;
+ }
+ if (!sv.active || cls.demoplayback)
+ {
+ Con_Printf ("Only the server may changelevel\n");
+ return;
+ }
+
+ strcpy (level, Cmd_Argv(1));
+ if (Cmd_Argc() == 2)
+ startspot = NULL;
+ else
+ {
+ strcpy (_startspot, Cmd_Argv(2));
+ startspot = _startspot;
+ }
+
+ SV_SaveSpawnparms ();
+
+ // save the current level's state
+ SaveGamestate ();
+
+ // try to restore the new level
+ if (LoadGamestate (level, startspot))
+ SV_SpawnServer (level, startspot);
+}
+#endif
+
+
+//============================================================================
+
+/*
+======================
+Host_Name_f
+======================
+*/
+void Host_Name_f (void)
+{
+ char *newName;
+
+ if (Cmd_Argc () == 1)
+ {
+ Con_Printf ("\"name\" is \"%s\"\n", cl_name.string);
+ return;
+ }
+ if (Cmd_Argc () == 2)
+ newName = Cmd_Argv(1);
+ else
+ newName = Cmd_Args();
+ newName[15] = 0;
+
+ if (cmd_source == src_command)
+ {
+ if (Q_strcmp(cl_name.string, newName) == 0)
+ return;
+ Cvar_Set ("_cl_name", newName);
+ if (cls.state == ca_connected)
+ Cmd_ForwardToServer ();
+ return;
+ }
+
+ if (host_client->name[0] && strcmp(host_client->name, "unconnected") )
+ if (Q_strcmp(host_client->name, newName) != 0)
+ Con_Printf ("%s renamed to %s\n", host_client->name, newName);
+ Q_strcpy (host_client->name, newName);
+ host_client->edict->v.netname = host_client->name - pr_strings;
+
+// send notification to all clients
+
+ MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
+ MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
+ MSG_WriteString (&sv.reliable_datagram, host_client->name);
+}
+
+
+void Host_Version_f (void)
+{
+ Con_Printf ("Version %4.2f\n", VERSION);
+ Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
+}
+
+#ifdef IDGODS
+void Host_Please_f (void)
+{
+ client_t *cl;
+ int j;
+
+ if (cmd_source != src_command)
+ return;
+
+ if ((Cmd_Argc () == 3) && Q_strcmp(Cmd_Argv(1), "#") == 0)
+ {
+ j = Q_atof(Cmd_Argv(2)) - 1;
+ if (j < 0 || j >= svs.maxclients)
+ return;
+ if (!svs.clients[j].active)
+ return;
+ cl = &svs.clients[j];
+ if (cl->privileged)
+ {
+ cl->privileged = false;
+ cl->edict->v.flags = (int)cl->edict->v.flags & ~(FL_GODMODE|FL_NOTARGET);
+ cl->edict->v.movetype = MOVETYPE_WALK;
+ noclip_anglehack = false;
+ }
+ else
+ cl->privileged = true;
+ }
+
+ if (Cmd_Argc () != 2)
+ return;
+
+ for (j=0, cl = svs.clients ; j<svs.maxclients ; j++, cl++)
+ {
+ if (!cl->active)
+ continue;
+ if (Q_strcasecmp(cl->name, Cmd_Argv(1)) == 0)
+ {
+ if (cl->privileged)
+ {
+ cl->privileged = false;
+ cl->edict->v.flags = (int)cl->edict->v.flags & ~(FL_GODMODE|FL_NOTARGET);
+ cl->edict->v.movetype = MOVETYPE_WALK;
+ noclip_anglehack = false;
+ }
+ else
+ cl->privileged = true;
+ break;
+ }
+ }
+}
+#endif
+
+
+void Host_Say(qboolean teamonly)
+{
+ client_t *client;
+ client_t *save;
+ int j;
+ char *p;
+ unsigned char text[64];
+ qboolean fromServer = false;
+
+ if (cmd_source == src_command)
+ {
+ if (cls.state == ca_dedicated)
+ {
+ fromServer = true;
+ teamonly = false;
+ }
+ else
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+ }
+
+ if (Cmd_Argc () < 2)
+ return;
+
+ save = host_client;
+
+ p = Cmd_Args();
+// remove quotes if present
+ if (*p == '"')
+ {
+ p++;
+ p[Q_strlen(p)-1] = 0;
+ }
+
+// turn on color set 1
+ if (!fromServer)
+ sprintf (text, "%c%s: ", 1, save->name);
+ else
+ sprintf (text, "%c<%s> ", 1, hostname.string);
+
+ j = sizeof(text) - 2 - Q_strlen(text); // -2 for /n and null terminator
+ if (Q_strlen(p) > j)
+ p[j] = 0;
+
+ strcat (text, p);
+ strcat (text, "\n");
+
+ for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++)
+ {
+ if (!client || !client->active || !client->spawned)
+ continue;
+ if (teamplay.value && teamonly && client->edict->v.team != save->edict->v.team)
+ continue;
+ host_client = client;
+ SV_ClientPrintf("%s", text);
+ }
+ host_client = save;
+
+ Sys_Printf("%s", &text[1]);
+}
+
+
+void Host_Say_f(void)
+{
+ Host_Say(false);
+}
+
+
+void Host_Say_Team_f(void)
+{
+ Host_Say(true);
+}
+
+
+void Host_Tell_f(void)
+{
+ client_t *client;
+ client_t *save;
+ int j;
+ char *p;
+ char text[64];
+
+ if (cmd_source == src_command)
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+
+ if (Cmd_Argc () < 3)
+ return;
+
+ Q_strcpy(text, host_client->name);
+ Q_strcat(text, ": ");
+
+ p = Cmd_Args();
+
+// remove quotes if present
+ if (*p == '"')
+ {
+ p++;
+ p[Q_strlen(p)-1] = 0;
+ }
+
+// check length & truncate if necessary
+ j = sizeof(text) - 2 - Q_strlen(text); // -2 for /n and null terminator
+ if (Q_strlen(p) > j)
+ p[j] = 0;
+
+ strcat (text, p);
+ strcat (text, "\n");
+
+ save = host_client;
+ for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++)
+ {
+ if (!client->active || !client->spawned)
+ continue;
+ if (Q_strcasecmp(client->name, Cmd_Argv(1)))
+ continue;
+ host_client = client;
+ SV_ClientPrintf("%s", text);
+ break;
+ }
+ host_client = save;
+}
+
+
+/*
+==================
+Host_Color_f
+==================
+*/
+void Host_Color_f(void)
+{
+ int top, bottom;
+ int playercolor;
+
+ if (Cmd_Argc() == 1)
+ {
+ Con_Printf ("\"color\" is \"%i %i\"\n", ((int)cl_color.value) >> 4, ((int)cl_color.value) & 0x0f);
+ Con_Printf ("color <0-13> [0-13]\n");
+ return;
+ }
+
+ if (Cmd_Argc() == 2)
+ top = bottom = atoi(Cmd_Argv(1));
+ else
+ {
+ top = atoi(Cmd_Argv(1));
+ bottom = atoi(Cmd_Argv(2));
+ }
+
+ top &= 15;
+ if (top > 13)
+ top = 13;
+ bottom &= 15;
+ if (bottom > 13)
+ bottom = 13;
+
+ playercolor = top*16 + bottom;
+
+ if (cmd_source == src_command)
+ {
+ Cvar_SetValue ("_cl_color", playercolor);
+ if (cls.state == ca_connected)
+ Cmd_ForwardToServer ();
+ return;
+ }
+
+ host_client->colors = playercolor;
+ host_client->edict->v.team = bottom + 1;
+
+// send notification to all clients
+ MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
+ MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
+ MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
+}
+
+/*
+==================
+Host_Kill_f
+==================
+*/
+void Host_Kill_f (void)
+{
+ if (cmd_source == src_command)
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+
+ if (sv_player->v.health <= 0)
+ {
+ SV_ClientPrintf ("Can't suicide -- allready dead!\n");
+ return;
+ }
+
+ pr_global_struct->time = sv.time;
+ pr_global_struct->self = EDICT_TO_PROG(sv_player);
+ PR_ExecuteProgram (pr_global_struct->ClientKill);
+}
+
+
+/*
+==================
+Host_Pause_f
+==================
+*/
+void Host_Pause_f (void)
+{
+
+ if (cmd_source == src_command)
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+ if (!pausable.value)
+ SV_ClientPrintf ("Pause not allowed.\n");
+ else
+ {
+ sv.paused ^= 1;
+
+ if (sv.paused)
+ {
+ SV_BroadcastPrintf ("%s paused the game\n", pr_strings + sv_player->v.netname);
+ }
+ else
+ {
+ SV_BroadcastPrintf ("%s unpaused the game\n",pr_strings + sv_player->v.netname);
+ }
+
+ // send notification to all clients
+ MSG_WriteByte (&sv.reliable_datagram, svc_setpause);
+ MSG_WriteByte (&sv.reliable_datagram, sv.paused);
+ }
+}
+
+//===========================================================================
+
+
+/*
+==================
+Host_PreSpawn_f
+==================
+*/
+void Host_PreSpawn_f (void)
+{
+ if (cmd_source == src_command)
+ {
+ Con_Printf ("prespawn is not valid from the console\n");
+ return;
+ }
+
+ if (host_client->spawned)
+ {
+ Con_Printf ("prespawn not valid -- allready spawned\n");
+ return;
+ }
+
+ SZ_Write (&host_client->message, sv.signon.data, sv.signon.cursize);
+ MSG_WriteByte (&host_client->message, svc_signonnum);
+ MSG_WriteByte (&host_client->message, 2);
+ host_client->sendsignon = true;
+}
+
+/*
+==================
+Host_Spawn_f
+==================
+*/
+void Host_Spawn_f (void)
+{
+ int i;
+ client_t *client;
+ edict_t *ent;
+
+ if (cmd_source == src_command)
+ {
+ Con_Printf ("spawn is not valid from the console\n");
+ return;
+ }
+
+ if (host_client->spawned)
+ {
+ Con_Printf ("Spawn not valid -- allready spawned\n");
+ return;
+ }
+
+// run the entrance script
+ if (sv.loadgame)
+ { // loaded games are fully inited allready
+ // if this is the last client to be connected, unpause
+ sv.paused = false;
+ }
+ else
+ {
+ // set up the edict
+ ent = host_client->edict;
+
+ memset (&ent->v, 0, progs->entityfields * 4);
+ ent->v.colormap = NUM_FOR_EDICT(ent);
+ ent->v.team = (host_client->colors & 15) + 1;
+ ent->v.netname = host_client->name - pr_strings;
+
+ // copy spawn parms out of the client_t
+
+ for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
+ (&pr_global_struct->parm1)[i] = host_client->spawn_parms[i];
+
+ // call the spawn function
+
+ pr_global_struct->time = sv.time;
+ pr_global_struct->self = EDICT_TO_PROG(sv_player);
+ PR_ExecuteProgram (pr_global_struct->ClientConnect);
+
+ if ((Sys_FloatTime() - host_client->netconnection->connecttime) <= sv.time)
+ Sys_Printf ("%s entered the game\n", host_client->name);
+
+ PR_ExecuteProgram (pr_global_struct->PutClientInServer);
+ }
+
+
+// send all current names, colors, and frag counts
+ SZ_Clear (&host_client->message);
+
+// send time of update
+ MSG_WriteByte (&host_client->message, svc_time);
+ MSG_WriteFloat (&host_client->message, sv.time);
+
+ for (i=0, client = svs.clients ; i<svs.maxclients ; i++, client++)
+ {
+ MSG_WriteByte (&host_client->message, svc_updatename);
+ MSG_WriteByte (&host_client->message, i);
+ MSG_WriteString (&host_client->message, client->name);
+ MSG_WriteByte (&host_client->message, svc_updatefrags);
+ MSG_WriteByte (&host_client->message, i);
+ MSG_WriteShort (&host_client->message, client->old_frags);
+ MSG_WriteByte (&host_client->message, svc_updatecolors);
+ MSG_WriteByte (&host_client->message, i);
+ MSG_WriteByte (&host_client->message, client->colors);
+ }
+
+// send all current light styles
+ for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
+ {
+ MSG_WriteByte (&host_client->message, svc_lightstyle);
+ MSG_WriteByte (&host_client->message, (char)i);
+ MSG_WriteString (&host_client->message, sv.lightstyles[i]);
+ }
+
+//
+// send some stats
+//
+ MSG_WriteByte (&host_client->message, svc_updatestat);
+ MSG_WriteByte (&host_client->message, STAT_TOTALSECRETS);
+ MSG_WriteLong (&host_client->message, pr_global_struct->total_secrets);
+
+ MSG_WriteByte (&host_client->message, svc_updatestat);
+ MSG_WriteByte (&host_client->message, STAT_TOTALMONSTERS);
+ MSG_WriteLong (&host_client->message, pr_global_struct->total_monsters);
+
+ MSG_WriteByte (&host_client->message, svc_updatestat);
+ MSG_WriteByte (&host_client->message, STAT_SECRETS);
+ MSG_WriteLong (&host_client->message, pr_global_struct->found_secrets);
+
+ MSG_WriteByte (&host_client->message, svc_updatestat);
+ MSG_WriteByte (&host_client->message, STAT_MONSTERS);
+ MSG_WriteLong (&host_client->message, pr_global_struct->killed_monsters);
+
+
+//
+// send a fixangle
+// Never send a roll angle, because savegames can catch the server
+// in a state where it is expecting the client to correct the angle
+// and it won't happen if the game was just loaded, so you wind up
+// with a permanent head tilt
+ ent = EDICT_NUM( 1 + (host_client - svs.clients) );
+ MSG_WriteByte (&host_client->message, svc_setangle);
+ for (i=0 ; i < 2 ; i++)
+ MSG_WriteAngle (&host_client->message, ent->v.angles[i] );
+ MSG_WriteAngle (&host_client->message, 0 );
+
+ SV_WriteClientdataToMessage (sv_player, &host_client->message);
+
+ MSG_WriteByte (&host_client->message, svc_signonnum);
+ MSG_WriteByte (&host_client->message, 3);
+ host_client->sendsignon = true;
+}
+
+/*
+==================
+Host_Begin_f
+==================
+*/
+void Host_Begin_f (void)
+{
+ if (cmd_source == src_command)
+ {
+ Con_Printf ("begin is not valid from the console\n");
+ return;
+ }
+
+ host_client->spawned = true;
+}
+
+//===========================================================================
+
+
+/*
+==================
+Host_Kick_f
+
+Kicks a user off of the server
+==================
+*/
+void Host_Kick_f (void)
+{
+ char *who;
+ char *message = NULL;
+ client_t *save;
+ int i;
+ qboolean byNumber = false;
+
+ if (cmd_source == src_command)
+ {
+ if (!sv.active)
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+ }
+ else if (pr_global_struct->deathmatch && !host_client->privileged)
+ return;
+
+ save = host_client;
+
+ if (Cmd_Argc() > 2 && Q_strcmp(Cmd_Argv(1), "#") == 0)
+ {
+ i = Q_atof(Cmd_Argv(2)) - 1;
+ if (i < 0 || i >= svs.maxclients)
+ return;
+ if (!svs.clients[i].active)
+ return;
+ host_client = &svs.clients[i];
+ byNumber = true;
+ }
+ else
+ {
+ for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
+ {
+ if (!host_client->active)
+ continue;
+ if (Q_strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
+ break;
+ }
+ }
+
+ if (i < svs.maxclients)
+ {
+ if (cmd_source == src_command)
+ if (cls.state == ca_dedicated)
+ who = "Console";
+ else
+ who = cl_name.string;
+ else
+ who = save->name;
+
+ // can't kick yourself!
+ if (host_client == save)
+ return;
+
+ if (Cmd_Argc() > 2)
+ {
+ message = COM_Parse(Cmd_Args());
+ if (byNumber)
+ {
+ message++; // skip the #
+ while (*message == ' ') // skip white space
+ message++;
+ message += Q_strlen(Cmd_Argv(2)); // skip the number
+ }
+ while (*message && *message == ' ')
+ message++;
+ }
+ if (message)
+ SV_ClientPrintf ("Kicked by %s: %s\n", who, message);
+ else
+ SV_ClientPrintf ("Kicked by %s\n", who);
+ SV_DropClient (false);
+ }
+
+ host_client = save;
+}
+
+/*
+===============================================================================
+
+DEBUGGING TOOLS
+
+===============================================================================
+*/
+
+/*
+==================
+Host_Give_f
+==================
+*/
+void Host_Give_f (void)
+{
+ char *t;
+ int v, w;
+ eval_t *val;
+
+ if (cmd_source == src_command)
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+
+ if (pr_global_struct->deathmatch && !host_client->privileged)
+ return;
+
+ t = Cmd_Argv(1);
+ v = atoi (Cmd_Argv(2));
+
+ switch (t[0])
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ // MED 01/04/97 added hipnotic give stuff
+ if (hipnotic)
+ {
+ if (t[0] == '6')
+ {
+ if (t[1] == 'a')
+ sv_player->v.items = (int)sv_player->v.items | HIT_PROXIMITY_GUN;
+ else
+ sv_player->v.items = (int)sv_player->v.items | IT_GRENADE_LAUNCHER;
+ }
+ else if (t[0] == '9')
+ sv_player->v.items = (int)sv_player->v.items | HIT_LASER_CANNON;
+ else if (t[0] == '0')
+ sv_player->v.items = (int)sv_player->v.items | HIT_MJOLNIR;
+ else if (t[0] >= '2')
+ sv_player->v.items = (int)sv_player->v.items | (IT_SHOTGUN << (t[0] - '2'));
+ }
+ else
+ {
+ if (t[0] >= '2')
+ sv_player->v.items = (int)sv_player->v.items | (IT_SHOTGUN << (t[0] - '2'));
+ }
+ break;
+
+ case 's':
+ if (rogue)
+ {
+ val = GetEdictFieldValue(sv_player, "ammo_shells1");
+ if (val)
+ val->_float = v;
+ }
+
+ sv_player->v.ammo_shells = v;
+ break;
+ case 'n':
+ if (rogue)
+ {
+ val = GetEdictFieldValue(sv_player, "ammo_nails1");
+ if (val)
+ {
+ val->_float = v;
+ if (sv_player->v.weapon <= IT_LIGHTNING)
+ sv_player->v.ammo_nails = v;
+ }
+ }
+ else
+ {
+ sv_player->v.ammo_nails = v;
+ }
+ break;
+ case 'l':
+ if (rogue)
+ {
+ val = GetEdictFieldValue(sv_player, "ammo_lava_nails");
+ if (val)
+ {
+ val->_float = v;
+ if (sv_player->v.weapon > IT_LIGHTNING)
+ sv_player->v.ammo_nails = v;
+ }
+ }
+ break;
+ case 'r':
+ if (rogue)
+ {
+ val = GetEdictFieldValue(sv_player, "ammo_rockets1");
+ if (val)
+ {
+ val->_float = v;
+ if (sv_player->v.weapon <= IT_LIGHTNING)
+ sv_player->v.ammo_rockets = v;
+ }
+ }
+ else
+ {
+ sv_player->v.ammo_rockets = v;
+ }
+ break;
+ case 'm':
+ if (rogue)
+ {
+ val = GetEdictFieldValue(sv_player, "ammo_multi_rockets");
+ if (val)
+ {
+ val->_float = v;
+ if (sv_player->v.weapon > IT_LIGHTNING)
+ sv_player->v.ammo_rockets = v;
+ }
+ }
+ break;
+ case 'h':
+ sv_player->v.health = v;
+ break;
+ case 'c':
+ if (rogue)
+ {
+ val = GetEdictFieldValue(sv_player, "ammo_cells1");
+ if (val)
+ {
+ val->_float = v;
+ if (sv_player->v.weapon <= IT_LIGHTNING)
+ sv_player->v.ammo_cells = v;
+ }
+ }
+ else
+ {
+ sv_player->v.ammo_cells = v;
+ }
+ break;
+ case 'p':
+ if (rogue)
+ {
+ val = GetEdictFieldValue(sv_player, "ammo_plasma");
+ if (val)
+ {
+ val->_float = v;
+ if (sv_player->v.weapon > IT_LIGHTNING)
+ sv_player->v.ammo_cells = v;
+ }
+ }
+ break;
+ }
+}
+
+edict_t *FindViewthing (void)
+{
+ int i;
+ edict_t *e;
+
+ for (i=0 ; i<sv.num_edicts ; i++)
+ {
+ e = EDICT_NUM(i);
+ if ( !strcmp (pr_strings + e->v.classname, "viewthing") )
+ return e;
+ }
+ Con_Printf ("No viewthing on map\n");
+ return NULL;
+}
+
+/*
+==================
+Host_Viewmodel_f
+==================
+*/
+void Host_Viewmodel_f (void)
+{
+ edict_t *e;
+ model_t *m;
+
+ e = FindViewthing ();
+ if (!e)
+ return;
+
+ m = Mod_ForName (Cmd_Argv(1), false);
+ if (!m)
+ {
+ Con_Printf ("Can't load %s\n", Cmd_Argv(1));
+ return;
+ }
+
+ e->v.frame = 0;
+ cl.model_precache[(int)e->v.modelindex] = m;
+}
+
+/*
+==================
+Host_Viewframe_f
+==================
+*/
+void Host_Viewframe_f (void)
+{
+ edict_t *e;
+ int f;
+ model_t *m;
+
+ e = FindViewthing ();
+ if (!e)
+ return;
+ m = cl.model_precache[(int)e->v.modelindex];
+
+ f = atoi(Cmd_Argv(1));
+ if (f >= m->numframes)
+ f = m->numframes-1;
+
+ e->v.frame = f;
+}
+
+
+void PrintFrameName (model_t *m, int frame)
+{
+ aliashdr_t *hdr;
+ maliasframedesc_t *pframedesc;
+
+ hdr = (aliashdr_t *)Mod_Extradata (m);
+ if (!hdr)
+ return;
+ pframedesc = &hdr->frames[frame];
+
+ Con_Printf ("frame %i: %s\n", frame, pframedesc->name);
+}
+
+/*
+==================
+Host_Viewnext_f
+==================
+*/
+void Host_Viewnext_f (void)
+{
+ edict_t *e;
+ model_t *m;
+
+ e = FindViewthing ();
+ if (!e)
+ return;
+ m = cl.model_precache[(int)e->v.modelindex];
+
+ e->v.frame = e->v.frame + 1;
+ if (e->v.frame >= m->numframes)
+ e->v.frame = m->numframes - 1;
+
+ PrintFrameName (m, e->v.frame);
+}
+
+/*
+==================
+Host_Viewprev_f
+==================
+*/
+void Host_Viewprev_f (void)
+{
+ edict_t *e;
+ model_t *m;
+
+ e = FindViewthing ();
+ if (!e)
+ return;
+
+ m = cl.model_precache[(int)e->v.modelindex];
+
+ e->v.frame = e->v.frame - 1;
+ if (e->v.frame < 0)
+ e->v.frame = 0;
+
+ PrintFrameName (m, e->v.frame);
+}
+
+/*
+===============================================================================
+
+DEMO LOOP CONTROL
+
+===============================================================================
+*/
+
+
+/*
+==================
+Host_Startdemos_f
+==================
+*/
+void Host_Startdemos_f (void)
+{
+ int i, c;
+
+ if (cls.state == ca_dedicated)
+ {
+ if (!sv.active)
+ Cbuf_AddText ("map start\n");
+ return;
+ }
+
+ c = Cmd_Argc() - 1;
+ if (c > MAX_DEMOS)
+ {
+ Con_Printf ("Max %i demos in demoloop\n", MAX_DEMOS);
+ c = MAX_DEMOS;
+ }
+ Con_Printf ("%i demo(s) in loop\n", c);
+
+ for (i=1 ; i<c+1 ; i++)
+ strncpy (cls.demos[i-1], Cmd_Argv(i), sizeof(cls.demos[0])-1);
+
+ if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
+ {
+ cls.demonum = 0;
+ CL_NextDemo ();
+ }
+ else
+ cls.demonum = -1;
+}
+
+
+/*
+==================
+Host_Demos_f
+
+Return to looping demos
+==================
+*/
+void Host_Demos_f (void)
+{
+ if (cls.state == ca_dedicated)
+ return;
+ if (cls.demonum == -1)
+ cls.demonum = 1;
+ CL_Disconnect_f ();
+ CL_NextDemo ();
+}
+
+/*
+==================
+Host_Stopdemo_f
+
+Return to looping demos
+==================
+*/
+void Host_Stopdemo_f (void)
+{
+ if (cls.state == ca_dedicated)
+ return;
+ if (!cls.demoplayback)
+ return;
+ CL_StopPlayback ();
+ CL_Disconnect ();
+}
+
+//=============================================================================
+
+/*
+==================
+Host_InitCommands
+==================
+*/
+void Host_InitCommands (void)
+{
+ Cmd_AddCommand ("status", Host_Status_f);
+ Cmd_AddCommand ("quit", Host_Quit_f);
+ Cmd_AddCommand ("god", Host_God_f);
+ Cmd_AddCommand ("notarget", Host_Notarget_f);
+ Cmd_AddCommand ("fly", Host_Fly_f);
+ Cmd_AddCommand ("map", Host_Map_f);
+ Cmd_AddCommand ("restart", Host_Restart_f);
+ Cmd_AddCommand ("changelevel", Host_Changelevel_f);
+#ifdef QUAKE2
+ Cmd_AddCommand ("changelevel2", Host_Changelevel2_f);
+#endif
+ Cmd_AddCommand ("connect", Host_Connect_f);
+ Cmd_AddCommand ("reconnect", Host_Reconnect_f);
+ Cmd_AddCommand ("name", Host_Name_f);
+ Cmd_AddCommand ("noclip", Host_Noclip_f);
+ Cmd_AddCommand ("version", Host_Version_f);
+#ifdef IDGODS
+ Cmd_AddCommand ("please", Host_Please_f);
+#endif
+ Cmd_AddCommand ("say", Host_Say_f);
+ Cmd_AddCommand ("say_team", Host_Say_Team_f);
+ Cmd_AddCommand ("tell", Host_Tell_f);
+ Cmd_AddCommand ("color", Host_Color_f);
+ Cmd_AddCommand ("kill", Host_Kill_f);
+ Cmd_AddCommand ("pause", Host_Pause_f);
+ Cmd_AddCommand ("spawn", Host_Spawn_f);
+ Cmd_AddCommand ("begin", Host_Begin_f);
+ Cmd_AddCommand ("prespawn", Host_PreSpawn_f);
+ Cmd_AddCommand ("kick", Host_Kick_f);
+ Cmd_AddCommand ("ping", Host_Ping_f);
+ Cmd_AddCommand ("load", Host_Loadgame_f);
+ Cmd_AddCommand ("save", Host_Savegame_f);
+ Cmd_AddCommand ("give", Host_Give_f);
+
+ Cmd_AddCommand ("startdemos", Host_Startdemos_f);
+ Cmd_AddCommand ("demos", Host_Demos_f);
+ Cmd_AddCommand ("stopdemo", Host_Stopdemo_f);
+
+ Cmd_AddCommand ("viewmodel", Host_Viewmodel_f);
+ Cmd_AddCommand ("viewframe", Host_Viewframe_f);
+ Cmd_AddCommand ("viewnext", Host_Viewnext_f);
+ Cmd_AddCommand ("viewprev", Host_Viewprev_f);
+
+ Cmd_AddCommand ("mcache", Mod_Print);
+}
--- /dev/null
+++ b/WinQuake/in_null.c
@@ -1,0 +1,39 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program 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.
+
+This program 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 this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// in_null.c -- for systems without a mouse
+
+#include "quakedef.h"
+
+void IN_Init (void)
+{
+}
+
+void IN_Shutdown (void)
+{
+}
+
+void IN_Commands (void)
+{
+}
+
+void IN_Move (usercmd_t *cmd)
+{
+}
+
--- /dev/null
+++ b/WinQuake/in_win.c
@@ -1,0 +1,1239 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program 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.
+
+This program 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 this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// in_win.c -- windows 95 mouse and joystick code
+// 02/21/97 JCB Added extended DirectInput code to support external controllers.
+
+#include <dinput.h>
+#include "quakedef.h"
+#include "winquake.h"
+#include "dosisms.h"
+
+#define DINPUT_BUFFERSIZE 16
+#define iDirectInputCreate(a,b,c,d) pDirectInputCreate(a,b,c,d)
+
+HRESULT (WINAPI *pDirectInputCreate)(HINSTANCE hinst, DWORD dwVersion,
+ LPDIRECTINPUT * lplpDirectInput, LPUNKNOWN punkOuter);
+
+// mouse variables
+cvar_t m_filter = {"m_filter","0"};
+
+int mouse_buttons;
+int mouse_oldbuttonstate;
+POINT current_pos;
+int mouse_x, mouse_y, old_mouse_x, old_mouse_y, mx_accum, my_accum;
+
+static qboolean restore_spi;
+static int originalmouseparms[3], newmouseparms[3] = {0, 0, 1};
+
+unsigned int uiWheelMessage;
+qboolean mouseactive;
+qboolean mouseinitialized;
+static qboolean mouseparmsvalid, mouseactivatetoggle;
+static qboolean mouseshowtoggle = 1;
+static qboolean dinput_acquired;
+
+static unsigned int mstate_di;
+
+// joystick defines and variables
+// where should defines be moved?
+#define JOY_ABSOLUTE_AXIS 0x00000000 // control like a joystick
+#define JOY_RELATIVE_AXIS 0x00000010 // control like a mouse, spinner, trackball
+#define JOY_MAX_AXES 6 // X, Y, Z, R, U, V
+#define JOY_AXIS_X 0
+#define JOY_AXIS_Y 1
+#define JOY_AXIS_Z 2
+#define JOY_AXIS_R 3
+#define JOY_AXIS_U 4
+#define JOY_AXIS_V 5
+
+enum _ControlList
+{
+ AxisNada = 0, AxisForward, AxisLook, AxisSide, AxisTurn
+};
+
+DWORD dwAxisFlags[JOY_MAX_AXES] =
+{
+ JOY_RETURNX, JOY_RETURNY, JOY_RETURNZ, JOY_RETURNR, JOY_RETURNU, JOY_RETURNV
+};
+
+DWORD dwAxisMap[JOY_MAX_AXES];
+DWORD dwControlMap[JOY_MAX_AXES];
+PDWORD pdwRawValue[JOY_MAX_AXES];
+
+// none of these cvars are saved over a session
+// this means that advanced controller configuration needs to be executed
+// each time. this avoids any problems with getting back to a default usage
+// or when changing from one controller to another. this way at least something
+// works.
+cvar_t in_joystick = {"joystick","0", true};
+cvar_t joy_name = {"joyname", "joystick"};
+cvar_t joy_advanced = {"joyadvanced", "0"};
+cvar_t joy_advaxisx = {"joyadvaxisx", "0"};
+cvar_t joy_advaxisy = {"joyadvaxisy", "0"};
+cvar_t joy_advaxisz = {"joyadvaxisz", "0"};
+cvar_t joy_advaxisr = {"joyadvaxisr", "0"};
+cvar_t joy_advaxisu = {"joyadvaxisu", "0"};
+cvar_t joy_advaxisv = {"joyadvaxisv", "0"};
+cvar_t joy_forwardthreshold = {"joyforwardthreshold", "0.15"};
+cvar_t joy_sidethreshold = {"joysidethreshold", "0.15"};
+cvar_t joy_pitchthreshold = {"joypitchthreshold", "0.15"};
+cvar_t joy_yawthreshold = {"joyyawthreshold", "0.15"};
+cvar_t joy_forwardsensitivity = {"joyforwardsensitivity", "-1.0"};
+cvar_t joy_sidesensitivity = {"joysidesensitivity", "-1.0"};
+cvar_t joy_pitchsensitivity = {"joypitchsensitivity", "1.0"};
+cvar_t joy_yawsensitivity = {"joyyawsensitivity", "-1.0"};
+cvar_t joy_wwhack1 = {"joywwhack1", "0.0"};
+cvar_t joy_wwhack2 = {"joywwhack2", "0.0"};
+
+qboolean joy_avail, joy_advancedinit, joy_haspov;
+DWORD joy_oldbuttonstate, joy_oldpovstate;
+
+int joy_id;
+DWORD joy_flags;
+DWORD joy_numbuttons;
+
+static LPDIRECTINPUT g_pdi;
+static LPDIRECTINPUTDEVICE g_pMouse;
+
+static JOYINFOEX ji;
+
+static HINSTANCE hInstDI;
+
+static qboolean dinput;
+
+typedef struct MYDATA {
+ LONG lX; // X axis goes here
+ LONG lY; // Y axis goes here
+ LONG lZ; // Z axis goes here
+ BYTE bButtonA; // One button goes here
+ BYTE bButtonB; // Another button goes here
+ BYTE bButtonC; // Another button goes here
+ BYTE bButtonD; // Another button goes here
+} MYDATA;
+
+static DIOBJECTDATAFORMAT rgodf[] = {
+ { &GUID_XAxis, FIELD_OFFSET(MYDATA, lX), DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,},
+ { &GUID_YAxis, FIELD_OFFSET(MYDATA, lY), DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,},
+ { &GUID_ZAxis, FIELD_OFFSET(MYDATA, lZ), 0x80000000 | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,},
+ { 0, FIELD_OFFSET(MYDATA, bButtonA), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
+ { 0, FIELD_OFFSET(MYDATA, bButtonB), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
+ { 0, FIELD_OFFSET(MYDATA, bButtonC), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
+ { 0, FIELD_OFFSET(MYDATA, bButtonD), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
+};
+
+#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
+
+static DIDATAFORMAT df = {
+ sizeof(DIDATAFORMAT), // this structure
+ sizeof(DIOBJECTDATAFORMAT), // size of object data format
+ DIDF_RELAXIS, // absolute axis coordinates
+ sizeof(MYDATA), // device data size
+ NUM_OBJECTS, // number of objects
+ rgodf, // and here they are
+};
+
+// forward-referenced functions
+void IN_StartupJoystick (void);
+void Joy_AdvancedUpdate_f (void);
+void IN_JoyMove (usercmd_t *cmd);
+
+
+/*
+===========
+Force_CenterView_f
+===========
+*/
+void Force_CenterView_f (void)
+{
+ cl.viewangles[PITCH] = 0;
+}
+
+
+/*
+===========
+IN_UpdateClipCursor
+===========
+*/
+void IN_UpdateClipCursor (void)
+{
+
+ if (mouseinitialized && mouseactive && !dinput)
+ {
+ ClipCursor (&window_rect);
+ }
+}
+
+
+/*
+===========
+IN_ShowMouse
+===========
+*/
+void IN_ShowMouse (void)
+{
+
+ if (!mouseshowtoggle)
+ {
+ ShowCursor (TRUE);
+ mouseshowtoggle = 1;
+ }
+}
+
+
+/*
+===========
+IN_HideMouse
+===========
+*/
+void IN_HideMouse (void)
+{
+
+ if (mouseshowtoggle)
+ {
+ ShowCursor (FALSE);
+ mouseshowtoggle = 0;
+ }
+}
+
+
+/*
+===========
+IN_ActivateMouse
+===========
+*/
+void IN_ActivateMouse (void)
+{
+
+ mouseactivatetoggle = true;
+
+ if (mouseinitialized)
+ {
+ if (dinput)
+ {
+ if (g_pMouse)
+ {
+ if (!dinput_acquired)
+ {
+ IDirectInputDevice_Acquire(g_pMouse);
+ dinput_acquired = true;
+ }
+ }
+ else
+ {
+ return;
+ }
+ }
+ else
+ {
+ if (mouseparmsvalid)
+ restore_spi = SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0);
+
+ SetCursorPos (window_center_x, window_center_y);
+ SetCapture (mainwindow);
+ ClipCursor (&window_rect);
+ }
+
+ mouseactive = true;
+ }
+}
+
+
+/*
+===========
+IN_SetQuakeMouseState
+===========
+*/
+void IN_SetQuakeMouseState (void)
+{
+ if (mouseactivatetoggle)
+ IN_ActivateMouse ();
+}
+
+
+/*
+===========
+IN_DeactivateMouse
+===========
+*/
+void IN_DeactivateMouse (void)
+{
+
+ mouseactivatetoggle = false;
+
+ if (mouseinitialized)
+ {
+ if (dinput)
+ {
+ if (g_pMouse)
+ {
+ if (dinput_acquired)
+ {
+ IDirectInputDevice_Unacquire(g_pMouse);
+ dinput_acquired = false;
+ }
+ }
+ }
+ else
+ {
+ if (restore_spi)
+ SystemParametersInfo (SPI_SETMOUSE, 0, originalmouseparms, 0);
+
+ ClipCursor (NULL);
+ ReleaseCapture ();
+ }
+
+ mouseactive = false;
+ }
+}
+
+
+/*
+===========
+IN_RestoreOriginalMouseState
+===========
+*/
+void IN_RestoreOriginalMouseState (void)
+{
+ if (mouseactivatetoggle)
+ {
+ IN_DeactivateMouse ();
+ mouseactivatetoggle = true;
+ }
+
+// try to redraw the cursor so it gets reinitialized, because sometimes it
+// has garbage after the mode switch
+ ShowCursor (TRUE);
+ ShowCursor (FALSE);
+}
+
+
+/*
+===========
+IN_InitDInput
+===========
+*/
+qboolean IN_InitDInput (void)
+{
+ HRESULT hr;
+ DIPROPDWORD dipdw = {
+ {
+ sizeof(DIPROPDWORD), // diph.dwSize
+ sizeof(DIPROPHEADER), // diph.dwHeaderSize
+ 0, // diph.dwObj
+ DIPH_DEVICE, // diph.dwHow
+ },
+ DINPUT_BUFFERSIZE, // dwData
+ };
+
+ if (!hInstDI)
+ {
+ hInstDI = LoadLibrary("dinput.dll");
+
+ if (hInstDI == NULL)
+ {
+ Con_SafePrintf ("Couldn't load dinput.dll\n");
+ return false;
+ }
+ }
+
+ if (!pDirectInputCreate)
+ {
+ pDirectInputCreate = (void *)GetProcAddress(hInstDI,"DirectInputCreateA");
+
+ if (!pDirectInputCreate)
+ {
+ Con_SafePrintf ("Couldn't get DI proc addr\n");
+ return false;
+ }
+ }
+
+// register with DirectInput and get an IDirectInput to play with.
+ hr = iDirectInputCreate(global_hInstance, DIRECTINPUT_VERSION, &g_pdi, NULL);
+
+ if (FAILED(hr))
+ {
+ return false;
+ }
+
+// obtain an interface to the system mouse device.
+ hr = IDirectInput_CreateDevice(g_pdi, &GUID_SysMouse, &g_pMouse, NULL);
+
+ if (FAILED(hr))
+ {
+ Con_SafePrintf ("Couldn't open DI mouse device\n");
+ return false;
+ }
+
+// set the data format to "mouse format".
+ hr = IDirectInputDevice_SetDataFormat(g_pMouse, &df);
+
+ if (FAILED(hr))
+ {
+ Con_SafePrintf ("Couldn't set DI mouse format\n");
+ return false;
+ }
+
+// set the cooperativity level.
+ hr = IDirectInputDevice_SetCooperativeLevel(g_pMouse, mainwindow,
+ DISCL_EXCLUSIVE | DISCL_FOREGROUND);
+
+ if (FAILED(hr))
+ {
+ Con_SafePrintf ("Couldn't set DI coop level\n");
+ return false;
+ }
+
+
+// set the buffer size to DINPUT_BUFFERSIZE elements.
+// the buffer size is a DWORD property associated with the device
+ hr = IDirectInputDevice_SetProperty(g_pMouse, DIPROP_BUFFERSIZE, &dipdw.diph);
+
+ if (FAILED(hr))
+ {
+ Con_SafePrintf ("Couldn't set DI buffersize\n");
+ return false;
+ }
+
+ return true;
+}
+
+
+/*
+===========
+IN_StartupMouse
+===========
+*/
+void IN_StartupMouse (void)
+{
+ HDC hdc;
+
+ if ( COM_CheckParm ("-nomouse") )
+ return;
+
+ mouseinitialized = true;
+
+ if (COM_CheckParm ("-dinput"))
+ {
+ dinput = IN_InitDInput ();
+
+ if (dinput)
+ {
+ Con_SafePrintf ("DirectInput initialized\n");
+ }
+ else
+ {
+ Con_SafePrintf ("DirectInput not initialized\n");
+ }
+ }
+
+ if (!dinput)
+ {
+ mouseparmsvalid = SystemParametersInfo (SPI_GETMOUSE, 0, originalmouseparms, 0);
+
+ if (mouseparmsvalid)
+ {
+ if ( COM_CheckParm ("-noforcemspd") )
+ newmouseparms[2] = originalmouseparms[2];
+
+ if ( COM_CheckParm ("-noforcemaccel") )
+ {
+ newmouseparms[0] = originalmouseparms[0];
+ newmouseparms[1] = originalmouseparms[1];
+ }
+
+ if ( COM_CheckParm ("-noforcemparms") )
+ {
+ newmouseparms[0] = originalmouseparms[0];
+ newmouseparms[1] = originalmouseparms[1];
+ newmouseparms[2] = originalmouseparms[2];
+ }
+ }
+ }
+
+ mouse_buttons = 3;
+
+// if a fullscreen video mode was set before the mouse was initialized,
+// set the mouse state appropriately
+ if (mouseactivatetoggle)
+ IN_ActivateMouse ();
+}
+
+
+/*
+===========
+IN_Init
+===========
+*/
+void IN_Init (void)
+{
+ // mouse variables
+ Cvar_RegisterVariable (&m_filter);
+
+ // joystick variables
+ Cvar_RegisterVariable (&in_joystick);
+ Cvar_RegisterVariable (&joy_name);
+ Cvar_RegisterVariable (&joy_advanced);
+ Cvar_RegisterVariable (&joy_advaxisx);
+ Cvar_RegisterVariable (&joy_advaxisy);
+ Cvar_RegisterVariable (&joy_advaxisz);
+ Cvar_RegisterVariable (&joy_advaxisr);
+ Cvar_RegisterVariable (&joy_advaxisu);
+ Cvar_RegisterVariable (&joy_advaxisv);
+ Cvar_RegisterVariable (&joy_forwardthreshold);
+ Cvar_RegisterVariable (&joy_sidethreshold);
+ Cvar_RegisterVariable (&joy_pitchthreshold);
+ Cvar_RegisterVariable (&joy_yawthreshold);
+ Cvar_RegisterVariable (&joy_forwardsensitivity);
+ Cvar_RegisterVariable (&joy_sidesensitivity);
+ Cvar_RegisterVariable (&joy_pitchsensitivity);
+ Cvar_RegisterVariable (&joy_yawsensitivity);
+ Cvar_RegisterVariable (&joy_wwhack1);
+ Cvar_RegisterVariable (&joy_wwhack2);
+
+ Cmd_AddCommand ("force_centerview", Force_CenterView_f);
+ Cmd_AddCommand ("joyadvancedupdate", Joy_AdvancedUpdate_f);
+
+ uiWheelMessage = RegisterWindowMessage ( "MSWHEEL_ROLLMSG" );
+
+ IN_StartupMouse ();
+ IN_StartupJoystick ();
+}
+
+/*
+===========
+IN_Shutdown
+===========
+*/
+void IN_Shutdown (void)
+{
+
+ IN_DeactivateMouse ();
+ IN_ShowMouse ();
+
+ if (g_pMouse)
+ {
+ IDirectInputDevice_Release(g_pMouse);
+ g_pMouse = NULL;
+ }
+
+ if (g_pdi)
+ {
+ IDirectInput_Release(g_pdi);
+ g_pdi = NULL;
+ }
+}
+
+
+/*
+===========
+IN_MouseEvent
+===========
+*/
+void IN_MouseEvent (int mstate)
+{
+ int i;
+
+ if (mouseactive && !dinput)
+ {
+ // perform button actions
+ for (i=0 ; i<mouse_buttons ; i++)
+ {
+ if ( (mstate & (1<<i)) &&
+ !(mouse_oldbuttonstate & (1<<i)) )
+ {
+ Key_Event (K_MOUSE1 + i, true);
+ }
+
+ if ( !(mstate & (1<<i)) &&
+ (mouse_oldbuttonstate & (1<<i)) )
+ {
+ Key_Event (K_MOUSE1 + i, false);
+ }
+ }
+
+ mouse_oldbuttonstate = mstate;
+ }
+}
+
+
+/*
+===========
+IN_MouseMove
+===========
+*/
+void IN_MouseMove (usercmd_t *cmd)
+{
+ int mx, my;
+ HDC hdc;
+ int i;
+ DIDEVICEOBJECTDATA od;
+ DWORD dwElements;
+ HRESULT hr;
+
+ if (!mouseactive)
+ return;
+
+ if (dinput)
+ {
+ mx = 0;
+ my = 0;
+
+ for (;;)
+ {
+ dwElements = 1;
+
+ hr = IDirectInputDevice_GetDeviceData(g_pMouse,
+ sizeof(DIDEVICEOBJECTDATA), &od, &dwElements, 0);
+
+ if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED))
+ {
+ dinput_acquired = true;
+ IDirectInputDevice_Acquire(g_pMouse);
+ break;
+ }
+
+ /* Unable to read data or no data available */
+ if (FAILED(hr) || dwElements == 0)
+ {
+ break;
+ }
+
+ /* Look at the element to see what happened */
+
+ switch (od.dwOfs)
+ {
+ case DIMOFS_X:
+ mx += od.dwData;
+ break;
+
+ case DIMOFS_Y:
+ my += od.dwData;
+ break;
+
+ case DIMOFS_BUTTON0:
+ if (od.dwData & 0x80)
+ mstate_di |= 1;
+ else
+ mstate_di &= ~1;
+ break;
+
+ case DIMOFS_BUTTON1:
+ if (od.dwData & 0x80)
+ mstate_di |= (1<<1);
+ else
+ mstate_di &= ~(1<<1);
+ break;
+
+ case DIMOFS_BUTTON2:
+ if (od.dwData & 0x80)
+ mstate_di |= (1<<2);
+ else
+ mstate_di &= ~(1<<2);
+ break;
+ }
+ }
+
+ // perform button actions
+ for (i=0 ; i<mouse_buttons ; i++)
+ {
+ if ( (mstate_di & (1<<i)) &&
+ !(mouse_oldbuttonstate & (1<<i)) )
+ {
+ Key_Event (K_MOUSE1 + i, true);
+ }
+
+ if ( !(mstate_di & (1<<i)) &&
+ (mouse_oldbuttonstate & (1<<i)) )
+ {
+ Key_Event (K_MOUSE1 + i, false);
+ }
+ }
+
+ mouse_oldbuttonstate = mstate_di;
+ }
+ else
+ {
+ GetCursorPos (¤t_pos);
+ mx = current_pos.x - window_center_x + mx_accum;
+ my = current_pos.y - window_center_y + my_accum;
+ mx_accum = 0;
+ my_accum = 0;
+ }
+
+//if (mx || my)
+// Con_DPrintf("mx=%d, my=%d\n", mx, my);
+
+ if (m_filter.value)
+ {
+ mouse_x = (mx + old_mouse_x) * 0.5;
+ mouse_y = (my + old_mouse_y) * 0.5;
+ }
+ else
+ {
+ mouse_x = mx;
+ mouse_y = my;
+ }
+
+ old_mouse_x = mx;
+ old_mouse_y = my;
+
+ mouse_x *= sensitivity.value;
+ mouse_y *= sensitivity.value;
+
+// add mouse X/Y movement to cmd
+ if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) ))
+ cmd->sidemove += m_side.value * mouse_x;
+ else
+ cl.viewangles[YAW] -= m_yaw.value * mouse_x;
+
+ if (in_mlook.state & 1)
+ V_StopPitchDrift ();
+
+ if ( (in_mlook.state & 1) && !(in_strafe.state & 1))
+ {
+ cl.viewangles[PITCH] += m_pitch.value * mouse_y;
+ if (cl.viewangles[PITCH] > 80)
+ cl.viewangles[PITCH] = 80;
+ if (cl.viewangles[PITCH] < -70)
+ cl.viewangles[PITCH] = -70;
+ }
+ else
+ {
+ if ((in_strafe.state & 1) && noclip_anglehack)
+ cmd->upmove -= m_forward.value * mouse_y;
+ else
+ cmd->forwardmove -= m_forward.value * mouse_y;
+ }
+
+// if the mouse has moved, force it to the center, so there's room to move
+ if (mx || my)
+ {
+ SetCursorPos (window_center_x, window_center_y);
+ }
+}
+
+
+/*
+===========
+IN_Move
+===========
+*/
+void IN_Move (usercmd_t *cmd)
+{
+
+ if (ActiveApp && !Minimized)
+ {
+ IN_MouseMove (cmd);
+ IN_JoyMove (cmd);
+ }
+}
+
+
+/*
+===========
+IN_Accumulate
+===========
+*/
+void IN_Accumulate (void)
+{
+ int mx, my;
+ HDC hdc;
+
+ if (mouseactive)
+ {
+ if (!dinput)
+ {
+ GetCursorPos (¤t_pos);
+
+ mx_accum += current_pos.x - window_center_x;
+ my_accum += current_pos.y - window_center_y;
+
+ // force the mouse to the center, so there's room to move
+ SetCursorPos (window_center_x, window_center_y);
+ }
+ }
+}
+
+
+/*
+===================
+IN_ClearStates
+===================
+*/
+void IN_ClearStates (void)
+{
+
+ if (mouseactive)
+ {
+ mx_accum = 0;
+ my_accum = 0;
+ mouse_oldbuttonstate = 0;
+ }
+}
+
+
+/*
+===============
+IN_StartupJoystick
+===============
+*/
+void IN_StartupJoystick (void)
+{
+ int i, numdevs;
+ JOYCAPS jc;
+ MMRESULT mmr;
+
+ // assume no joystick
+ joy_avail = false;
+
+ // abort startup if user requests no joystick
+ if ( COM_CheckParm ("-nojoy") )
+ return;
+
+ // verify joystick driver is present
+ if ((numdevs = joyGetNumDevs ()) == 0)
+ {
+ Con_Printf ("\njoystick not found -- driver not present\n\n");
+ return;
+ }
+
+ // cycle through the joystick ids for the first valid one
+ for (joy_id=0 ; joy_id<numdevs ; joy_id++)
+ {
+ memset (&ji, 0, sizeof(ji));
+ ji.dwSize = sizeof(ji);
+ ji.dwFlags = JOY_RETURNCENTERED;
+
+ if ((mmr = joyGetPosEx (joy_id, &ji)) == JOYERR_NOERROR)
+ break;
+ }
+
+ // abort startup if we didn't find a valid joystick
+ if (mmr != JOYERR_NOERROR)
+ {
+ Con_Printf ("\njoystick not found -- no valid joysticks (%x)\n\n", mmr);
+ return;
+ }
+
+ // get the capabilities of the selected joystick
+ // abort startup if command fails
+ memset (&jc, 0, sizeof(jc));
+ if ((mmr = joyGetDevCaps (joy_id, &jc, sizeof(jc))) != JOYERR_NOERROR)
+ {
+ Con_Printf ("\njoystick not found -- invalid joystick capabilities (%x)\n\n", mmr);
+ return;
+ }
+
+ // save the joystick's number of buttons and POV status
+ joy_numbuttons = jc.wNumButtons;
+ joy_haspov = jc.wCaps & JOYCAPS_HASPOV;
+
+ // old button and POV states default to no buttons pressed
+ joy_oldbuttonstate = joy_oldpovstate = 0;
+
+ // mark the joystick as available and advanced initialization not completed
+ // this is needed as cvars are not available during initialization
+
+ joy_avail = true;
+ joy_advancedinit = false;
+
+ Con_Printf ("\njoystick detected\n\n");
+}
+
+
+/*
+===========
+RawValuePointer
+===========
+*/
+PDWORD RawValuePointer (int axis)
+{
+ switch (axis)
+ {
+ case JOY_AXIS_X:
+ return &ji.dwXpos;
+ case JOY_AXIS_Y:
+ return &ji.dwYpos;
+ case JOY_AXIS_Z:
+ return &ji.dwZpos;
+ case JOY_AXIS_R:
+ return &ji.dwRpos;
+ case JOY_AXIS_U:
+ return &ji.dwUpos;
+ case JOY_AXIS_V:
+ return &ji.dwVpos;
+ }
+}
+
+
+/*
+===========
+Joy_AdvancedUpdate_f
+===========
+*/
+void Joy_AdvancedUpdate_f (void)
+{
+
+ // called once by IN_ReadJoystick and by user whenever an update is needed
+ // cvars are now available
+ int i;
+ DWORD dwTemp;
+
+ // initialize all the maps
+ for (i = 0; i < JOY_MAX_AXES; i++)
+ {
+ dwAxisMap[i] = AxisNada;
+ dwControlMap[i] = JOY_ABSOLUTE_AXIS;
+ pdwRawValue[i] = RawValuePointer(i);
+ }
+
+ if( joy_advanced.value == 0.0)
+ {
+ // default joystick initialization
+ // 2 axes only with joystick control
+ dwAxisMap[JOY_AXIS_X] = AxisTurn;
+ // dwControlMap[JOY_AXIS_X] = JOY_ABSOLUTE_AXIS;
+ dwAxisMap[JOY_AXIS_Y] = AxisForward;
+ // dwControlMap[JOY_AXIS_Y] = JOY_ABSOLUTE_AXIS;
+ }
+ else
+ {
+ if (Q_strcmp (joy_name.string, "joystick") != 0)
+ {
+ // notify user of advanced controller
+ Con_Printf ("\n%s configured\n\n", joy_name.string);
+ }
+
+ // advanced initialization here
+ // data supplied by user via joy_axisn cvars
+ dwTemp = (DWORD) joy_advaxisx.value;
+ dwAxisMap[JOY_AXIS_X] = dwTemp & 0x0000000f;
+ dwControlMap[JOY_AXIS_X] = dwTemp & JOY_RELATIVE_AXIS;
+ dwTemp = (DWORD) joy_advaxisy.value;
+ dwAxisMap[JOY_AXIS_Y] = dwTemp & 0x0000000f;
+ dwControlMap[JOY_AXIS_Y] = dwTemp & JOY_RELATIVE_AXIS;
+ dwTemp = (DWORD) joy_advaxisz.value;
+ dwAxisMap[JOY_AXIS_Z] = dwTemp & 0x0000000f;
+ dwControlMap[JOY_AXIS_Z] = dwTemp & JOY_RELATIVE_AXIS;
+ dwTemp = (DWORD) joy_advaxisr.value;
+ dwAxisMap[JOY_AXIS_R] = dwTemp & 0x0000000f;
+ dwControlMap[JOY_AXIS_R] = dwTemp & JOY_RELATIVE_AXIS;
+ dwTemp = (DWORD) joy_advaxisu.value;
+ dwAxisMap[JOY_AXIS_U] = dwTemp & 0x0000000f;
+ dwControlMap[JOY_AXIS_U] = dwTemp & JOY_RELATIVE_AXIS;
+ dwTemp = (DWORD) joy_advaxisv.value;
+ dwAxisMap[JOY_AXIS_V] = dwTemp & 0x0000000f;
+ dwControlMap[JOY_AXIS_V] = dwTemp & JOY_RELATIVE_AXIS;
+ }
+
+ // compute the axes to collect from DirectInput
+ joy_flags = JOY_RETURNCENTERED | JOY_RETURNBUTTONS | JOY_RETURNPOV;
+ for (i = 0; i < JOY_MAX_AXES; i++)
+ {
+ if (dwAxisMap[i] != AxisNada)
+ {
+ joy_flags |= dwAxisFlags[i];
+ }
+ }
+}
+
+
+/*
+===========
+IN_Commands
+===========
+*/
+void IN_Commands (void)
+{
+ int i, key_index;
+ DWORD buttonstate, povstate;
+
+ if (!joy_avail)
+ {
+ return;
+ }
+
+
+ // loop through the joystick buttons
+ // key a joystick event or auxillary event for higher number buttons for each state change
+ buttonstate = ji.dwButtons;
+ for (i=0 ; i < joy_numbuttons ; i++)
+ {
+ if ( (buttonstate & (1<<i)) && !(joy_oldbuttonstate & (1<<i)) )
+ {
+ key_index = (i < 4) ? K_JOY1 : K_AUX1;
+ Key_Event (key_index + i, true);
+ }
+
+ if ( !(buttonstate & (1<<i)) && (joy_oldbuttonstate & (1<<i)) )
+ {
+ key_index = (i < 4) ? K_JOY1 : K_AUX1;
+ Key_Event (key_index + i, false);
+ }
+ }
+ joy_oldbuttonstate = buttonstate;
+
+ if (joy_haspov)
+ {
+ // convert POV information into 4 bits of state information
+ // this avoids any potential problems related to moving from one
+ // direction to another without going through the center position
+ povstate = 0;
+ if(ji.dwPOV != JOY_POVCENTERED)
+ {
+ if (ji.dwPOV == JOY_POVFORWARD)
+ povstate |= 0x01;
+ if (ji.dwPOV == JOY_POVRIGHT)
+ povstate |= 0x02;
+ if (ji.dwPOV == JOY_POVBACKWARD)
+ povstate |= 0x04;
+ if (ji.dwPOV == JOY_POVLEFT)
+ povstate |= 0x08;
+ }
+ // determine which bits have changed and key an auxillary event for each change
+ for (i=0 ; i < 4 ; i++)
+ {
+ if ( (povstate & (1<<i)) && !(joy_oldpovstate & (1<<i)) )
+ {
+ Key_Event (K_AUX29 + i, true);
+ }
+
+ if ( !(povstate & (1<<i)) && (joy_oldpovstate & (1<<i)) )
+ {
+ Key_Event (K_AUX29 + i, false);
+ }
+ }
+ joy_oldpovstate = povstate;
+ }
+}
+
+
+/*
+===============
+IN_ReadJoystick
+===============
+*/
+qboolean IN_ReadJoystick (void)
+{
+
+ memset (&ji, 0, sizeof(ji));
+ ji.dwSize = sizeof(ji);
+ ji.dwFlags = joy_flags;
+
+ if (joyGetPosEx (joy_id, &ji) == JOYERR_NOERROR)
+ {
+ // this is a hack -- there is a bug in the Logitech WingMan Warrior DirectInput Driver
+ // rather than having 32768 be the zero point, they have the zero point at 32668
+ // go figure -- anyway, now we get the full resolution out of the device
+ if (joy_wwhack1.value != 0.0)
+ {
+ ji.dwUpos += 100;
+ }
+ return true;
+ }
+ else
+ {
+ // read error occurred
+ // turning off the joystick seems too harsh for 1 read error,\
+ // but what should be done?
+ // Con_Printf ("IN_ReadJoystick: no response\n");
+ // joy_avail = false;
+ return false;
+ }
+}
+
+
+/*
+===========
+IN_JoyMove
+===========
+*/
+void IN_JoyMove (usercmd_t *cmd)
+{
+ float speed, aspeed;
+ float fAxisValue, fTemp;
+ int i;
+
+ // complete initialization if first time in
+ // this is needed as cvars are not available at initialization time
+ if( joy_advancedinit != true )
+ {
+ Joy_AdvancedUpdate_f();
+ joy_advancedinit = true;
+ }
+
+ // verify joystick is available and that the user wants to use it
+ if (!joy_avail || !in_joystick.value)
+ {
+ return;
+ }
+
+ // collect the joystick data, if possible
+ if (IN_ReadJoystick () != true)
+ {
+ return;
+ }
+
+ if (in_speed.state & 1)
+ speed = cl_movespeedkey.value;
+ else
+ speed = 1;
+ aspeed = speed * host_frametime;
+
+ // loop through the axes
+ for (i = 0; i < JOY_MAX_AXES; i++)
+ {
+ // get the floating point zero-centered, potentially-inverted data for the current axis
+ fAxisValue = (float) *pdwRawValue[i];
+ // move centerpoint to zero
+ fAxisValue -= 32768.0;
+
+ if (joy_wwhack2.value != 0.0)
+ {
+ if (dwAxisMap[i] == AxisTurn)
+ {
+ // this is a special formula for the Logitech WingMan Warrior
+ // y=ax^b; where a = 300 and b = 1.3
+ // also x values are in increments of 800 (so this is factored out)
+ // then bounds check result to level out excessively high spin rates
+ fTemp = 300.0 * pow(abs(fAxisValue) / 800.0, 1.3);
+ if (fTemp > 14000.0)
+ fTemp = 14000.0;
+ // restore direction information
+ fAxisValue = (fAxisValue > 0.0) ? fTemp : -fTemp;
+ }
+ }
+
+ // convert range from -32768..32767 to -1..1
+ fAxisValue /= 32768.0;
+
+ switch (dwAxisMap[i])
+ {
+ case AxisForward:
+ if ((joy_advanced.value == 0.0) && (in_mlook.state & 1))
+ {
+ // user wants forward control to become look control
+ if (fabs(fAxisValue) > joy_pitchthreshold.value)
+ {
+ // if mouse invert is on, invert the joystick pitch value
+ // only absolute control support here (joy_advanced is false)
+ if (m_pitch.value < 0.0)
+ {
+ cl.viewangles[PITCH] -= (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value;
+ }
+ else
+ {
+ cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value;
+ }
+ V_StopPitchDrift();
+ }
+ else
+ {
+ // no pitch movement
+ // disable pitch return-to-center unless requested by user
+ // *** this code can be removed when the lookspring bug is fixed
+ // *** the bug always has the lookspring feature on
+ if(lookspring.value == 0.0)
+ V_StopPitchDrift();
+ }
+ }
+ else
+ {
+ // user wants forward control to be forward control
+ if (fabs(fAxisValue) > joy_forwardthreshold.value)
+ {
+ cmd->forwardmove += (fAxisValue * joy_forwardsensitivity.value) * speed * cl_forwardspeed.value;
+ }
+ }
+ break;
+
+ case AxisSide:
+ if (fabs(fAxisValue) > joy_sidethreshold.value)
+ {
+ cmd->sidemove += (fAxisValue * joy_sidesensitivity.value) * speed * cl_sidespeed.value;
+ }
+ break;
+
+ case AxisTurn:
+ if ((in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1)))
+ {
+ // user wants turn control to become side control
+ if (fabs(fAxisValue) > joy_sidethreshold.value)
+ {
+ cmd->sidemove -= (fAxisValue * joy_sidesensitivity.value) * speed * cl_sidespeed.value;
+ }
+ }
+ else
+ {
+ // user wants turn control to be turn control
+ if (fabs(fAxisValue) > joy_yawthreshold.value)
+ {
+ if(dwControlMap[i] == JOY_ABSOLUTE_AXIS)
+ {
+ cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity.value) * aspeed * cl_yawspeed.value;
+ }
+ else
+ {
+ cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity.value) * speed * 180.0;
+ }
+
+ }
+ }
+ break;
+
+ case AxisLook:
+ if (in_mlook.state & 1)
+ {
+ if (fabs(fAxisValue) > joy_pitchthreshold.value)
+ {
+ // pitch movement detected and pitch movement desired by user
+ if(dwControlMap[i] == JOY_ABSOLUTE_AXIS)
+ {
+ cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value;
+ }
+ else
+ {
+ cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * speed * 180.0;
+ }
+ V_StopPitchDrift();
+ }
+ else
+ {
+ // no pitch movement
+ // disable pitch return-to-center unless requested by user
+ // *** this code can be removed when the lookspring bug is fixed
+ // *** the bug always has the lookspring feature on
+ if(lookspring.value == 0.0)
+ V_StopPitchDrift();
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // bounds check pitch
+ if (cl.viewangles[PITCH] > 80.0)
+ cl.viewangles[PITCH] = 80.0;
+ if (cl.viewangles[PITCH] < -70.0)
+ cl.viewangles[PITCH] = -70.0;
+}
--- /dev/null
+++ b/WinQuake/keys.c
@@ -1,0 +1,759 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program 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.
+
+This program 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 this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+#include "quakedef.h"
+
+/*
+
+key up events are sent even if in console mode
+
+*/
+
+
+#define MAXCMDLINE 256
+char key_lines[32][MAXCMDLINE];
+int key_linepos;
+int shift_down=false;
+int key_lastpress;
+
+int edit_line=0;
+int history_line=0;
+
+keydest_t key_dest;
+
+int key_count; // incremented every key event
+
+char *keybindings[256];
+qboolean consolekeys[256]; // if true, can't be rebound while in console
+qboolean menubound[256]; // if true, can't be rebound while in menu
+int keyshift[256]; // key to map to if shift held down in console
+int key_repeats[256]; // if > 1, it is autorepeating
+qboolean keydown[256];
+
+typedef struct
+{
+ char *name;
+ int keynum;
+} keyname_t;
+
+keyname_t keynames[] =
+{
+ {"TAB", K_TAB},
+ {"ENTER", K_ENTER},
+ {"ESCAPE", K_ESCAPE},
+ {"SPACE", K_SPACE},
+ {"BACKSPACE", K_BACKSPACE},
+ {"UPARROW", K_UPARROW},
+ {"DOWNARROW", K_DOWNARROW},
+ {"LEFTARROW", K_LEFTARROW},
+ {"RIGHTARROW", K_RIGHTARROW},
+
+ {"ALT", K_ALT},
+ {"CTRL", K_CTRL},
+ {"SHIFT", K_SHIFT},
+
+ {"F1", K_F1},
+ {"F2", K_F2},
+ {"F3", K_F3},
+ {"F4", K_F4},
+ {"F5", K_F5},
+ {"F6", K_F6},
+ {"F7", K_F7},
+ {"F8", K_F8},
+ {"F9", K_F9},
+ {"F10", K_F10},
+ {"F11", K_F11},
+ {"F12", K_F12},
+
+ {"INS", K_INS},
+ {"DEL", K_DEL},
+ {"PGDN", K_PGDN},
+ {"PGUP", K_PGUP},
+ {"HOME", K_HOME},
+ {"END", K_END},
+
+ {"MOUSE1", K_MOUSE1},
+ {"MOUSE2", K_MOUSE2},
+ {"MOUSE3", K_MOUSE3},
+
+ {"JOY1", K_JOY1},
+ {"JOY2", K_JOY2},
+ {"JOY3", K_JOY3},
+ {"JOY4", K_JOY4},
+
+ {"AUX1", K_AUX1},
+ {"AUX2", K_AUX2},
+ {"AUX3", K_AUX3},
+ {"AUX4", K_AUX4},
+ {"AUX5", K_AUX5},
+ {"AUX6", K_AUX6},
+ {"AUX7", K_AUX7},
+ {"AUX8", K_AUX8},
+ {"AUX9", K_AUX9},
+ {"AUX10", K_AUX10},
+ {"AUX11", K_AUX11},
+ {"AUX12", K_AUX12},
+ {"AUX13", K_AUX13},
+ {"AUX14", K_AUX14},
+ {"AUX15", K_AUX15},
+ {"AUX16", K_AUX16},
+ {"AUX17", K_AUX17},
+ {"AUX18", K_AUX18},
+ {"AUX19", K_AUX19},
+ {"AUX20", K_AUX20},
+ {"AUX21", K_AUX21},
+ {"AUX22", K_AUX22},
+ {"AUX23", K_AUX23},
+ {"AUX24", K_AUX24},
+ {"AUX25", K_AUX25},
+ {"AUX26", K_AUX26},
+ {"AUX27", K_AUX27},
+ {"AUX28", K_AUX28},
+ {"AUX29", K_AUX29},
+ {"AUX30", K_AUX30},
+ {"AUX31", K_AUX31},
+ {"AUX32", K_AUX32},
+
+ {"PAUSE", K_PAUSE},
+
+ {"MWHEELUP", K_MWHEELUP},
+ {"MWHEELDOWN", K_MWHEELDOWN},
+
+ {"SEMICOLON", ';'}, // because a raw semicolon seperates commands
+
+ {NULL,0}
+};
+
+/*
+==============================================================================
+
+ LINE TYPING INTO THE CONSOLE
+
+==============================================================================
+*/
+
+
+/*
+====================
+Key_Console
+
+Interactive line editing and console scrollback
+====================
+*/
+void Key_Console (int key)
+{
+ char *cmd;
+
+ if (key == K_ENTER)
+ {
+ Cbuf_AddText (key_lines[edit_line]+1); // skip the >
+ Cbuf_AddText ("\n");
+ Con_Printf ("%s\n",key_lines[edit_line]);
+ edit_line = (edit_line + 1) & 31;
+ history_line = edit_line;
+ key_lines[edit_line][0] = ']';
+ key_linepos = 1;
+ if (cls.state == ca_disconnected)
+ SCR_UpdateScreen (); // force an update, because the command
+ // may take some time
+ return;
+ }
+
+ if (key == K_TAB)
+ { // command completion
+ cmd = Cmd_CompleteCommand (key_lines[edit_line]+1);
+ if (!cmd)
+ cmd = Cvar_CompleteVariable (key_lines[edit_line]+1);
+ if (cmd)
+ {
+ Q_strcpy (key_lines[edit_line]+1, cmd);
+ key_linepos = Q_strlen(cmd)+1;
+ key_lines[edit_line][key_linepos] = ' ';
+ key_linepos++;
+ key_lines[edit_line][key_linepos] = 0;
+ return;
+ }
+ }
+
+ if (key == K_BACKSPACE || key == K_LEFTARROW)
+ {
+ if (key_linepos > 1)
+ key_linepos--;
+ return;
+ }
+
+ if (key == K_UPARROW)
+ {
+ do
+ {
+ history_line = (history_line - 1) & 31;
+ } while (history_line != edit_line
+ && !key_lines[history_line][1]);
+ if (history_line == edit_line)
+ history_line = (edit_line+1)&31;
+ Q_strcpy(key_lines[edit_line], key_lines[history_line]);
+ key_linepos = Q_strlen(key_lines[edit_line]);
+ return;
+ }
+
+ if (key == K_DOWNARROW)
+ {
+ if (history_line == edit_line) return;
+ do
+ {
+ history_line = (history_line + 1) & 31;
+ }
+ while (history_line != edit_line
+ && !key_lines[history_line][1]);
+ if (history_line == edit_line)
+ {
+ key_lines[edit_line][0] = ']';
+ key_linepos = 1;
+ }
+ else
+ {
+ Q_strcpy(key_lines[edit_line], key_lines[history_line]);
+ key_linepos = Q_strlen(key_lines[edit_line]);
+ }
+ return;
+ }
+
+ if (key == K_PGUP || key==K_MWHEELUP)
+ {
+ con_backscroll += 2;
+ if (con_backscroll > con_totallines - (vid.height>>3) - 1)
+ con_backscroll = con_totallines - (vid.height>>3) - 1;
+ return;
+ }
+
+ if (key == K_PGDN || key==K_MWHEELDOWN)
+ {
+ con_backscroll -= 2;
+ if (con_backscroll < 0)
+ con_backscroll = 0;
+ return;
+ }
+
+ if (key == K_HOME)
+ {
+ con_backscroll = con_totallines - (vid.height>>3) - 1;
+ return;
+ }
+
+ if (key == K_END)
+ {
+ con_backscroll = 0;
+ return;
+ }
+
+ if (key < 32 || key > 127)
+ return; // non printable
+
+ if (key_linepos < MAXCMDLINE-1)
+ {
+ key_lines[edit_line][key_linepos] = key;
+ key_linepos++;
+ key_lines[edit_line][key_linepos] = 0;
+ }
+
+}
+
+//============================================================================
+
+char chat_buffer[32];
+qboolean team_message = false;
+
+void Key_Message (int key)
+{
+ static int chat_bufferlen = 0;
+
+ if (key == K_ENTER)
+ {
+ if (team_message)
+ Cbuf_AddText ("say_team \"");
+ else
+ Cbuf_AddText ("say \"");
+ Cbuf_AddText(chat_buffer);
+ Cbuf_AddText("\"\n");
+
+ key_dest = key_game;
+ chat_bufferlen = 0;
+ chat_buffer[0] = 0;
+ return;
+ }
+
+ if (key == K_ESCAPE)
+ {
+ key_dest = key_game;
+ chat_bufferlen = 0;
+ chat_buffer[0] = 0;
+ return;
+ }
+
+ if (key < 32 || key > 127)
+ return; // non printable
+
+ if (key == K_BACKSPACE)
+ {
+ if (chat_bufferlen)
+ {
+ chat_bufferlen--;
+ chat_buffer[chat_bufferlen] = 0;
+ }
+ return;
+ }
+
+ if (chat_bufferlen == 31)
+ return; // all full
+
+ chat_buffer[chat_bufferlen++] = key;
+ chat_buffer[chat_bufferlen] = 0;
+}
+
+//============================================================================
+
+
+/*
+===================
+Key_StringToKeynum
+
+Returns a key number to be used to index keybindings[] by looking at
+the given string. Single ascii characters return themselves, while
+the K_* names are matched up.
+===================
+*/
+int Key_StringToKeynum (char *str)
+{
+ keyname_t *kn;
+
+ if (!str || !str[0])
+ return -1;
+ if (!str[1])
+ return str[0];
+
+ for (kn=keynames ; kn->name ; kn++)
+ {
+ if (!Q_strcasecmp(str,kn->name))
+ return kn->keynum;
+ }
+ return -1;
+}
+
+/*
+===================
+Key_KeynumToString
+
+Returns a string (either a single ascii char, or a K_* name) for the
+given keynum.
+FIXME: handle quote special (general escape sequence?)
+===================
+*/
+char *Key_KeynumToString (int keynum)
+{
+ keyname_t *kn;
+ static char tinystr[2];
+
+ if (keynum == -1)
+ return "<KEY NOT FOUND>";
+ if (keynum > 32 && keynum < 127)
+ { // printable ascii
+ tinystr[0] = keynum;
+ tinystr[1] = 0;
+ return tinystr;
+ }
+
+ for (kn=keynames ; kn->name ; kn++)
+ if (keynum == kn->keynum)
+ return kn->name;
+
+ return "<UNKNOWN KEYNUM>";
+}
+
+
+/*
+===================
+Key_SetBinding
+===================
+*/
+void Key_SetBinding (int keynum, char *binding)
+{
+ char *new;
+ int l;
+
+ if (keynum == -1)
+ return;
+
+// free old bindings
+ if (keybindings[keynum])
+ {
+ Z_Free (keybindings[keynum]);
+ keybindings[keynum] = NULL;
+ }
+
+// allocate memory for new binding
+ l = Q_strlen (binding);
+ new = Z_Malloc (l+1);
+ Q_strcpy (new, binding);
+ new[l] = 0;
+ keybindings[keynum] = new;
+}
+
+/*
+===================
+Key_Unbind_f
+===================
+*/
+void Key_Unbind_f (void)
+{
+ int b;
+
+ if (Cmd_Argc() != 2)
+ {
+ Con_Printf ("unbind <key> : remove commands from a key\n");
+ return;
+ }
+
+ b = Key_StringToKeynum (Cmd_Argv(1));
+ if (b==-1)
+ {
+ Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1));
+ return;
+ }
+
+ Key_SetBinding (b, "");
+}
+
+void Key_Unbindall_f (void)
+{
+ int i;
+
+ for (i=0 ; i<256 ; i++)
+ if (keybindings[i])
+ Key_SetBinding (i, "");
+}
+
+
+/*
+===================
+Key_Bind_f
+===================
+*/
+void Key_Bind_f (void)
+{
+ int i, c, b;
+ char cmd[1024];
+
+ c = Cmd_Argc();
+
+ if (c != 2 && c != 3)
+ {
+ Con_Printf ("bind <key> [command] : attach a command to a key\n");
+ return;
+ }
+ b = Key_StringToKeynum (Cmd_Argv(1));
+ if (b==-1)
+ {
+ Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1));
+ return;
+ }
+
+ if (c == 2)
+ {
+ if (keybindings[b])
+ Con_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), keybindings[b] );
+ else
+ Con_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) );
+ return;
+ }
+
+// copy the rest of the command line
+ cmd[0] = 0; // start out with a null string
+ for (i=2 ; i< c ; i++)
+ {
+ if (i > 2)
+ strcat (cmd, " ");
+ strcat (cmd, Cmd_Argv(i));
+ }
+
+ Key_SetBinding (b, cmd);
+}
+
+/*
+============
+Key_WriteBindings
+
+Writes lines containing "bind key value"
+============
+*/
+void Key_WriteBindings (FILE *f)
+{
+ int i;
+
+ for (i=0 ; i<256 ; i++)
+ if (keybindings[i])
+ if (*keybindings[i])
+ fprintf (f, "bind \"%s\" \"%s\"\n", Key_KeynumToString(i), keybindings[i]);
+}
+
+
+/*
+===================
+Key_Init
+===================
+*/
+void Key_Init (void)
+{
+ int i;
+
+ for (i=0 ; i<32 ; i++)
+ {
+ key_lines[i][0] = ']';
+ key_lines[i][1] = 0;
+ }
+ key_linepos = 1;
+
+//
+// init ascii characters in console mode
+//
+ for (i=32 ; i<128 ; i++)
+ consolekeys[i] = true;
+ consolekeys[K_ENTER] = true;
+ consolekeys[K_TAB] = true;
+ consolekeys[K_LEFTARROW] = true;
+ consolekeys[K_RIGHTARROW] = true;
+ consolekeys[K_UPARROW] = true;
+ consolekeys[K_DOWNARROW] = true;
+ consolekeys[K_BACKSPACE] = true;
+ consolekeys[K_PGUP] = true;
+ consolekeys[K_PGDN] = true;
+ consolekeys[K_SHIFT] = true;
+ consolekeys[K_MWHEELUP] = true;
+ consolekeys[K_MWHEELDOWN] = true;
+ consolekeys['`'] = false;
+ consolekeys['~'] = false;
+
+ for (i=0 ; i<256 ; i++)
+ keyshift[i] = i;
+ for (i='a' ; i<='z' ; i++)
+ keyshift[i] = i - 'a' + 'A';
+ keyshift['1'] = '!';
+ keyshift['2'] = '@';
+ keyshift['3'] = '#';
+ keyshift['4'] = '$';
+ keyshift['5'] = '%';
+ keyshift['6'] = '^';
+ keyshift['7'] = '&';
+ keyshift['8'] = '*';
+ keyshift['9'] = '(';
+ keyshift['0'] = ')';
+ keyshift['-'] = '_';
+ keyshift['='] = '+';
+ keyshift[','] = '<';
+ keyshift['.'] = '>';
+ keyshift['/'] = '?';
+ keyshift[';'] = ':';
+ keyshift['\''] = '"';
+ keyshift['['] = '{';
+ keyshift[']'] = '}';
+ keyshift['`'] = '~';
+ keyshift['\\'] = '|';
+
+ menubound[K_ESCAPE] = true;
+ for (i=0 ; i<12 ; i++)
+ menubound[K_F1+i] = true;
+
+//
+// register our functions
+//
+ Cmd_AddCommand ("bind",Key_Bind_f);
+ Cmd_AddCommand ("unbind",Key_Unbind_f);
+ Cmd_AddCommand ("unbindall",Key_Unbindall_f);
+
+
+}
+
+/*
+===================
+Key_Event
+
+Called by the system between frames for both key up and key down events
+Should NOT be called during an interrupt!
+===================
+*/
+void Key_Event (int key, qboolean down)
+{
+ char *kb;
+ char cmd[1024];
+
+ keydown[key] = down;
+
+ if (!down)
+ key_repeats[key] = 0;
+
+ key_lastpress = key;
+ key_count++;
+ if (key_count <= 0)
+ {
+ return; // just catching keys for Con_NotifyBox
+ }
+
+// update auto-repeat status
+ if (down)
+ {
+ key_repeats[key]++;
+ if (key != K_BACKSPACE && key != K_PAUSE && key_repeats[key] > 1)
+ {
+ return; // ignore most autorepeats
+ }
+
+ if (key >= 200 && !keybindings[key])
+ Con_Printf ("%s is unbound, hit F4 to set.\n", Key_KeynumToString (key) );
+ }
+
+ if (key == K_SHIFT)
+ shift_down = down;
+
+//
+// handle escape specialy, so the user can never unbind it
+//
+ if (key == K_ESCAPE)
+ {
+ if (!down)
+ return;
+ switch (key_dest)
+ {
+ case key_message:
+ Key_Message (key);
+ break;
+ case key_menu:
+ M_Keydown (key);
+ break;
+ case key_game:
+ case key_console:
+ M_ToggleMenu_f ();
+ break;
+ default:
+ Sys_Error ("Bad key_dest");
+ }
+ return;
+ }
+
+//
+// key up events only generate commands if the game key binding is
+// a button command (leading + sign). These will occur even in console mode,
+// to keep the character from continuing an action started before a console
+// switch. Button commands include the kenum as a parameter, so multiple
+// downs can be matched with ups
+//
+ if (!down)
+ {
+ kb = keybindings[key];
+ if (kb && kb[0] == '+')
+ {
+ sprintf (cmd, "-%s %i\n", kb+1, key);
+ Cbuf_AddText (cmd);
+ }
+ if (keyshift[key] != key)
+ {
+ kb = keybindings[keyshift[key]];
+ if (kb && kb[0] == '+')
+ {
+ sprintf (cmd, "-%s %i\n", kb+1, key);
+ Cbuf_AddText (cmd);
+ }
+ }
+ return;
+ }
+
+//
+// during demo playback, most keys bring up the main menu
+//
+ if (cls.demoplayback && down && consolekeys[key] && key_dest == key_game)
+ {
+ M_ToggleMenu_f ();
+ return;
+ }
+
+//
+// if not a consolekey, send to the interpreter no matter what mode is
+//
+ if ( (key_dest == key_menu && menubound[key])
+ || (key_dest == key_console && !consolekeys[key])
+ || (key_dest == key_game && ( !con_forcedup || !consolekeys[key] ) ) )
+ {
+ kb = keybindings[key];
+ if (kb)
+ {
+ if (kb[0] == '+')
+ { // button commands add keynum as a parm
+ sprintf (cmd, "%s %i\n", kb, key);
+ Cbuf_AddText (cmd);
+ }
+ else
+ {
+ Cbuf_AddText (kb);
+ Cbuf_AddText ("\n");
+ }
+ }
+ return;
+ }
+
+ if (!down)
+ return; // other systems only care about key down events
+
+ if (shift_down)
+ {
+ key = keyshift[key];
+ }
+
+ switch (key_dest)
+ {
+ case key_message:
+ Key_Message (key);
+ break;
+ case key_menu:
+ M_Keydown (key);
+ break;
+
+ case key_game:
+ case key_console:
+ Key_Console (key);
+ break;
+ default:
+ Sys_Error ("Bad key_dest");
+ }
+}
+
+
+/*
+===================
+Key_ClearStates
+===================
+*/
+void Key_ClearStates (void)
+{
+ int i;
+
+ for (i=0 ; i<256 ; i++)
+ {
+ keydown[i] = false;
+ key_repeats[i] = 0;
+ }
+}
+
--- /dev/null
+++ b/WinQuake/kit/3DFX.TXT
@@ -1,0 +1,279 @@
+GLQuake Drivers for Voodoo Graphics Based 3D Accelerators
+Preliminary Release 2
+
+Copyright ( 1997 3Dfx Interactive, Inc. )
+All Rights Reserved
+
+3Dfx Interactive, Inc.
+www: www.3dfx.com
+news: news.3dfx.com
+
+-----------------------------------------------------------------------
+NOTE: GLQuake requires DirectX 2.0 or DirectX 3.0
+(Needed for DirectSound support)
+
+DirectX 2.0 or DirectX 3.0 can be installed from the media provided
+with your Voodoo Based 3D Accelerator.
+-----------------------------------------------------------------------
+
+
+
+Release Notes for GLQuake Preliminary Release 2 (Quake Version 1.07)
+
+-----------------------------------------------------------------------
+What's in the distribution?
+-----------------------------------------------------------------------
+This distribution contains GLQuake Drivers for Voodoo Graphics Based 3D
+Accelerators. These drivers were tested on the following boards:
+
+- Diamond Monster 3D
+- Orchid Righteous 3D
+- 3Dfx Interactive reference boards
+
+NOTE: The enclosed drivers are not meant to replace any Direct3D or
+Glide drivers provided by your Voodoo Graphics card manufacturer.
+Please obtain supported drivers from:
+
+- Diamond supported drivers can be obtained from www.diamondmm.com
+- Orchid supported drivers can be obtained from www.orchid.com
+
+
+Included Files
+--------------
+
+OPENGL32.DLL - QuakeGL Interface to Voodoo Graphics
+FXMEMMAP.VXD - Voodoo Graphics VXD
+GLQUAKE.EXE - ID Software provided GLQUAKE.EXE
+README.TXT - ID Software provided README.TXT
+READ3DFX.TXT - This File
+
+All enclosed files MUST reside in your Quake directory and not in the
+Windows\SYSTEM directory.
+
+OEMSR2 users: Do NOT replace OPENGL32.DLL located in your
+Windows\SYSTEM directory.
+
+
+
+-----------------------------------------------------------------------
+Installation
+-----------------------------------------------------------------------
+
+Requirements
+------------
+
+- Voodoo Graphics Based 3D Accelerator
+- Windows 95
+- A PC with a Pentium 90 or higher CPU
+- 16MB of RAM
+- 2D Video card set at 16 bit or higher color
+
+Installation
+------------
+
+Adding GLQuake Driver Support
+-----------------------------
+1) Install the FULL Version (Not the Shareware version!) of Quake.
+
+2) Copy the GLQUAKE.EXE and other associated files from the GLQuake
+ ZIP file to your Quake Directory. (Use Windows Explorer)
+
+3) Copy the enclosed OPENGL32.DLL file to your Quake Directory.
+ (Use Windows Explorer) NOTE: DO NOT COPY OPENGL32.DLL to your
+ Windows\SYSTEM directory
+
+4) Create a Desktop Shortcut for GLQuake: Using the right mouse button
+ drag the GLQUAKE.EXE file from Windows Explorer to the Desktop.
+ When prompted choose "Create Shortcut"
+
+5) Create an autoexec.cfg file in the ID1\ directory of your quake
+ installation if you do not already have one. Add the line
+
+ gl_playermip "2"
+
+ to the file.
+
+6) Start GLQuake by running the shortcut.
+
+
+Troubleshooting and Frequently Asked Questions
+----------------------------------------------
+
+1. Will GLQuake work with shareware Quake?
+
+No, the full registered version of Quake is required.
+
+
+
+2. Do I need any other drivers to run GLQuake on Voodoo Graphics?
+
+Just make sure that the FXMEMMAP.VXD file is in your Windows\SYSTEM
+directory and that DirectX 2.0, DirectX 3.0 or DirectX 3.0a are
+installed as GLQuake uses DirectSound. The latest version of DirectX
+can be obtained from:
+ http://www.microsoft.com/mediadev/download/isdk.htm
+
+
+
+3. I installed GLQuake and try to run the GLQUAKE.EXE file but I get a
+"no RGB fullscreen modes available" How do I get GLQuake to run?
+
+Make sure that your 2D video card is set to 16bit color (65K colors,
+not 16 colors). In addition, do not start GLQuake from a full screen
+MS-DOS prompt.
+
+
+
+4. GLQuake comes up for a little while then drops me back to the
+Windows 95 desktop, what's wrong?
+
+Your Virtual Memory settings on your system should be increased. Open
+Control Panel, System - click on the Performance tab and then click on
+Virtual Memory. Adjust the settings so that the minimum swap file size
+is 80MB. You may also want to delete all the mesh files - do this by
+deleting the quake\ID1\glquake directory.
+
+
+
+5. Why does GLQuake try to connect to my Internet connection whenever
+it starts?
+
+GLQuake uses Windows networking. Auto-Dial is likely enabled in your
+Internet Control Panel or in Internet Explorer Options. Single
+Player users: To disable Network use in GLQuake and prevent the
+network connection screen from coming up, add "-nolan" to the
+GLQUAKE.EXE command line, example:
+ GLQUAKE.EXE -nolan
+
+
+
+
+6. I have a three button mouse, but I can't use or set the middle
+button in GLQuake, what's wrong?
+
+To use a three button mouse with GLQuake, your Windows mouse driver
+must support three buttons. Use the Logitech PS/2, Serial or Bus
+driver instead of the Microsoft or Standard PS/2, Serial or Bus driver.
+Also, make certain that your Mouse control panel sets the middle button
+to "middle" and not "double click".
+
+
+
+7. Mouse input seems jumpy, how do I fix that?
+
+From the console (hit the ~ tilde key), enter m_filter 1 <enter>
+This option can be added to the AUTOEXEC.CFG file (in the \ID1
+directory). You may also add this option to the GLQUAKE.EXE command
+line, example:
+ GLQUAKE.EXE +m_filter 1
+
+
+
+8. While playing GLQuake the sound stutters, how do I fix it?
+
+If your sound card does not support DirectSound, you may encounter
+stuttering sound during game play. Try adding the following value to
+the CONFIG.CFG file (in the quake\ID1 directory):
+ _snd_mixahead ".14"
+
+
+
+9. When I hit ALT-TAB or the Windows start button to switch to another
+application why do I return to a 640x480 display?
+
+GLQuake by default needs to keep the 2D display at 640x480 while it is
+running. To return the display to your normal setting you must exit
+GLQuake. To prevent this, add the following to the GLQUAKE.EXE command
+line options "+_windowed_mouse 1" and "-window" example:
+ GLQUAKE.EXE +_windowed_mouse 1 -window
+
+
+
+10. GLQuake multiplayer can't find other games or won't connect.
+
+GLQuake uses Windows 95 Networking. Verify that the correct networking
+components are installed and that you can connect to the other machine
+using File and print sharing or TCP/IP ping. If you are using IPX also
+make certain that the frame type is the same on all the systems.
+
+
+
+11. GLQuake multiplayer slows down alot, how do I fix it?
+
+Add gl_playermip 2 to the AUTOEXEC.CFG file (in the \ID1 directory)
+You may however add "+gl_playermip 2" to the GLQUAKE.EXE command line,
+example:
+ GLQUAKE.EXE +gl_playermip 2
+
+
+
+12. Does the Activision(r) Scourge of Armagon add-on (Mission Pack 1)
+work with GLQuake?
+
+Yes, start GLQUAKE.EXE with a "-hipnotic" switch. Example:
+ GLQUAKE.EXE -hipnotic
+
+
+
+13. Do other 3rd party quake levels work with GLQuake?
+
+Not all 3rd party levels have been tested, some may not work properly
+or optimally.
+
+
+
+14. Will GLQuake use a Voodoo Graphics accelerator under Windows NT?
+
+The 3Dfx GLQuake drivers currently only work under Windows 95.
+
+
+
+15. After installing GLQuake the OpenGL screen savers in Windows 95
+(OEMSR2) don't work. What's wrong?
+
+The OpenGL Windows 95 screen savers in OEMSR2 will fail if you copied
+the OPENGL32.DLL file that comes with GLQuake to your Windows\SYSTEM
+directory. The 3Dfx OPENGL32.DLL file only works with Quake. It will
+not run with other OpenGL applications. If you copied the 3Dfx
+OPENGL32.DLL to your Windows\SYSTEM directory and need to restore the
+Microsoft provided OPENGL32.DLL, follow these steps:
+
+ OEMSR2 Users
+ ------------
+ 1) Insert your Windows 95 CD into your CD-ROM drive
+ 2) Open a MS-DOS prompt, (Click Start, Programs, MS-DOS Prompt)
+ 3) Switch to the Windows\SYSTEM directory, ie:
+ C: <enter>
+ CD\Windows\system <enter>
+ 4) At the command prompt, enter:
+ EXTRACT /A E:\WIN95 opengl32.dll <enter>
+ (Substitute E:\ for your CD-ROM drive letter)
+
+ Standard Windows 95 Users
+ -------------------------
+ 1) Download and reinstall OpenGL for Windows 95 from the source
+ you previously used.
+
+
+
+16. How do I get support for GLQuake
+
+GLQuake is currently unsupported. You may however find answers to
+questions on various Quake dedicated websites. 3Dfx provides a GLQuake
+newsgroup on news.3dfx.com (Newsgroup name is 3dfx.games.glquake ) to
+discuss GLQuake with other users. 3Dfx also provides a regularly
+updated GLQuake FAQ at: http://www.3dfx.com/game_dev/quake_faq.html
+
+
+
+16. How do I send a bug report?
+
+If your problem is not resolved in this document or our updated FAQ
+(please see #15) and your bug is related to visual quality, performance
+or stability send an email to [email protected] - Describe your
+system configuration (CPU Type, CPU Speed, 2D Video Card type, Amount
+of Memory, Virtual Memory Size..etc.) and how to recreate the bug.
+
+
+Voodoo Graphics is a trademark of 3Dfx Interactive, Inc. All other
+trademarks are the property of their respective owners.
\ No newline at end of file
--- /dev/null
+++ b/WinQuake/kit/JOYSTICK.TXT
@@ -1,0 +1,167 @@
+
+NEW NOTE FOR 1.08:
+Joysticks are disabled by defualt now, due to problems on some systems without joysticks installed. Type "joystick 1" at the console, and everything will behave as documented here. This will be saved in your config file, so you will only have to do it once.
+
+Description of Windows 95 Quake DirectInput support
+By: FPgaming, Inc. (www.fpgaming.com) -- Creators of the Assassin 3D
+File: JOYSTICK.TXT
+Created: 02/21/97
+(This may be more information than you ever wanted to know.)
+
+The joystick support with Windows 95 Quake has been significantly enhanced. Standard joysticks, digital joysticks and new advanced controllers like the FPgaming Assassin 3D, the Logitech WingMan Warrior and the SpaceTec IMC SpaceOrb are all supported.
+
+To make it work, just verify that your joystick or game controller is selected in the Joystick control panel applet and has been calibrated and tested, then launch Windows 95 Quake (WinQuake.exe or glquake.exe). For standard and new digital joysticks, WinQuake will detect the joystick and automatically configure it. For advanced controllers, you will additionally need to run a config file (or run it from your autoexec.cfg) prior to the device operating. This will set the advanced features for your particular device. This config file should be obtained from your game controller company. The config files for the comman game controllers are included below. If you don't want your joystick or game controller enabled, add '-nojoy' to your command-line.
+
+
+Standard Joystick Support
+The standard joystick support has been enhanced to provide the following:
+1. proportional movement (the farther you move the stick, the faster you move)
+2. support for up to 32 buttons (JOY1-JOY4 and AUX5-AUX32)
+3. sensitivity setting for each control (allows tuning and inverting the control direction)
+4. dead-zone setting for each control
+
+The default joystick setting is for joystick left/right movement to control turning and for joystick forward/backward movement to control moving forward/backward. For optional strafing, add the 'sidestep' feature to one of your buttons (via the Customize menu). For optional looking, add the 'mouse look' feature to one of your buttons (also via the Customize menu).
+
+Additionally, there are several features that you can set from the Options menu. 'Always Run' allows you change your maximum speed from walking to running. 'Invert Mouse' allows you to change the direction the joystick has to move to when looking up and down. 'Lookspring' enables automatic look-forward-when-moving. And, 'Lookstrafe' automatically enables strafing when the 'mouse look' button is pressed.
+
+The following variables control your sensititivity settings:
+ joyforwardsensitivity - controls the ramp-up speed for moving forward and backward
+ joysidesensitivity - controls the ramp-up speed for moving side to side
+ joypitchsensitivity - controls the speed that you look up and down
+ joyyawsensitivity - controls the speed that you look left to right
+You can set the sensitivity settings to negative numbers. This inverts the direction of movement for the control. The default sensitivity settings are 1 (or -1). There is no limit on the range; whatever feels good.
+
+The following variables control your threshold settings:
+ joyforwardthreshold - controls the dead-zone for moving forward and backward
+ joysidethreshold - controls the dead-zone for moving side to side
+ joypitchthreshold - controls the dead-zone for looking up and down
+ joyyawthreshold - controls the dead-zone for looking left and right
+The threshold settings allow you to control your dead-zone (or no-movement zone). The default threshold settings are .15 (meaning 15% of the full-range). The range of the threshold settings is from 0 to 1. Troublesome analog joysticks may need a larger number (like .2). Premium joysticks can use a smaller number (like .1).
+
+The joystick sensitivity settings and the threshold settings are not saved after you quit your game as inadvertant settings can really hose your control. If you want to keep any changes, add them into your autoexec.cfg file.
+
+If your joystick has a POV hat, the buttons are mapped to AUX29-AUX32. So, you get 8 buttons with the Logitech WingMan Extreme and 12 buttons with the Microsoft SideWinder 3D Pro, etc.
+
+
+Advanced Controller Support
+The following features have been added:
+1. support for all 6 axes (X, Y, Z, R, U, V)
+2. mapping of any axis to any control (Forward, Look, Side, Turn)
+3. proportional movement for all controls
+4. sensitivity setting for any control (allows tuning and inverting the control direction)
+5. threshold setting for any control (allows dead-zone setting)
+6. support for absolute controls (like joysticks) and relative controls (like trackballs and spinners)
+7. support for up to 32 buttons (JOY1-JOY4 and AUX5-AUX32)
+
+To make an advanced controller operate, you will need to get a config file from your game controller company. This file is typically placed in your quake\id1 directory and then it is called within your autoexec.cfg file. For example, if your config file is named gamectrl.cfg, place that file within your quake\id1 directory and add 'exec gamectrl.cfg' in your autoexec.cfg file. If you don't have an autoexec.cfg file, you can create one and just place this one line in it.
+
+******************************************************************************
+NOTE: The information below is for game controller companies to integrate their device and for anyone wanting to create a custom setup.
+
+In addition to the above new variables, there are six more for axis mapping. These are:
+ joyadvaxisx - controls mapping of DirectInput axis X (typically joystick left and right)
+ joyadvaxisy - controls mapping of DirectInput axis Y (typically joystick forward and backward)
+ joyadvaxisz - controls mapping of DirectInput axis Z (typically joystick throttle)
+ joyadvaxisr - controls mapping of DirectInput axis R (typically joystick rudder)
+ joyadvaxisu - controls mapping of DirectInput axis U (custom axis - Assassin 3D trackball left and right, WingMan Warrior SpinControl and SpaceOrb roll)
+ joyadvaxisv - controls mapping of DirectInput axis V (custom axis - Assassin 3D trackball forward and backward and SpaceOrb yaw)
+Each joyadvaxis variable can be set to the following controls:
+ 0 = Axis not used
+ 1 = Axis is for forward and backward movement
+ 2 = Axis is for looking up and down (pitch)
+ 3 = Axis is for side to side movement
+ 4 = Axis is for turning left and right (yaw)
+Additionally, each axis can be designated as an absolute axis (like a joystick) or a relative axis (like the FPgaming trackball or the WingMan Warrior SpinControl). Absolute axes are defined as having a stopping position whereas relative axes don't have a stopping position and just go around and around. To designate an axis as a relative axis, add 16 to the above control number. For example, to set the Assassin 3D's axis U to be looking left and right, type 'joyadvaxisu 20'. As another example, to make your rudder pedals contol turning left and right, type 'joyadvaxisr 4'. It's a bit complicated, but only needs to be done once.
+
+The advanced axes variables will not have any effect until joyadvanced is set to 1.0. Additionally, any changes to the to the axes will not take effect until the joyadvancedupdate command is executed. So, the procedure for creating an advanced mapping is:
+1. set 'joyadvanced 1'
+2. make any desired mapping changes
+3. make any desired sensitivity changes
+4. make any desired threshold changes
+3. call 'joyadvancedupdate'
+
+Here is the config file for the FPgaming Assassin 3D:
+// ADVA3D.CFG
+// Revision 1.0 -- refer to www.fpgaming.com for updates
+joyname "FPgaming Assassin 3D"
+joyadvanced 1
+joyadvaxisx 3
+joyadvaxisy 1
+joyadvaxisz 0
+joyadvaxisr 0
+joyadvaxisu 20
+joyadvaxisv 18
+joyforwardsensitivity -1.0
+joysidesensitivity 1.0
+joypitchsensitivity -0.25
+joyyawsensitivity -0.5
+joyforwardthreshold 0.15
+joysidethreshold 0.15
+joyyawthreshold 0.0
+joypitchthreshold 0.0
++mlook
+joyadvancedupdate
+
+Here is a config file for the Logitech WingMan Warrior:
+// ADVWW.CFG
+// Revision 0.1 -- refer to www.logitech.com for updates
+joyname "Logitech WingMan Warrior"
+joyadvanced 1.0
+joywwhack1 1.0
+joywwhack2 1.0
+joyadvaxisx 3
+joyadvaxisy 1
+joyadvaxisz 0
+joyadvaxisr 0
+joyadvaxisu 20
+joyadvaxisv 0
+joyforwardsensitivity -1.0
+joysidesensitivity 1.0
+joypitchsensitivity 0.0
+joyyawsensitivity -0.6
+joyforwardthreshold 0.15
+joysidethreshold 0.15
+joypitchthreshold 0.0
+joyyawthreshold 0.0
+joyadvancedupdate
+
+Here is a config file for the SpaceTec IMC SpaceOrb:
+// ADVSPORB.CFG
+// Revision 0.1 -- refer to www.spacetec.com for updates
+joyname "SpaceTec IMC SpaceOrb"
+joyadvanced 1.0
+joyadvaxisx 3
+joyadvaxisy 1
+joyadvaxisz 0
+joyadvaxisr 2
+joyadvaxisu 0
+joyadvaxisv 4
+joyforwardsensitivity -1.0
+joysidesensitivity 1.0
+joypitchsensitivity -0.5
+joyyawsensitivity 1
+joyforwardthreshold 0.1
+joysidethreshold 0.1
+joypitchthreshold 0.1
+joyyawthreshold 0.1
++mlook
+joyadvancedupdate
+
+Here is a config file for making your joystick operate looking around and strafing, your rudder pedals control turning left and right and throttle control moving forward and backward:
+joyname "Joystick, Rudder & Throttle"
+joyadvanced 1.0
+joyadvaxisx 3
+joyadvaxisy 2
+joyadvaxisz 1
+joyadvaxisr 4
+joyadvaxisu 0
+joyadvaxisv 0
+joyforwardsensitivity -1.0
+joysidesensitivity -1.0
+joypitchsensitivity 1.0
+joyyawsensitivity -1.0
+joyforwardthreshold 0.15
+joysidethreshold 0.15
+joyyawthreshold 0.15
+joypitchthreshold 0.15
+joyadvancedupdate
\ No newline at end of file
--- /dev/null
+++ b/WinQuake/kit/README.TXT
@@ -1,0 +1,171 @@
+Glquake v0.97, Quake v1.09 release notes
+
+3dfx owners -- read the 3dfx.txt file.
+
+On a standard OpenGL system, all you should need to do to run glquake is put
+glquake.exe in your quake directory, and run it from there. DO NOT install
+the opengl32.dll unless you have a 3dfx! Glquake should change the screen
+resolution to 640*480*32k colors and run full screen by default.
+
+If you are running win-95, your desktop must be set to 32k or 64k colors
+before running glquake. NT can switch automatically.
+
+Theoretically, glquake will run on any compliant OpenGL that supports the
+texture objects extensions, but unless it is very powerfull hardware that
+accelerates everything needed, the game play will not be acceptable. If it
+has to go through any software emulation paths, the performance will likely
+by well under one frame per second.
+
+3dfx has provided an opengl32.dll that implements everything glquake needs,
+but it is not a full opengl implementation. Other opengl applications are
+very unlikely to work with it, so consider it basically a "glquake driver".
+See the encluded 3dfx.txt for specific instalation notes. 3dfx can only run
+full screen, but you must still have your desktop set to a 16 bit color mode
+for glquake to start.
+
+resolution options
+------------------
+We had dynamic resolution changing in glquake for a while, but every single
+opengl driver I tried it on messed up in one way or another, so it is now
+limited to startup time only.
+
+glquake -window
+This will start glquake in a window on your desktop instead of switching the
+screen to lower resolution and covering everything.
+
+glquake -width 800 -height 600
+Tries to run glquake at the specified resolution. Combined with -window, it
+creates a desktop window that size, otherwise it tries to set a full screen
+resolution.
+
+You can also specify the resolution of the console independant of the screen
+resolution.
+
+glquake -conwidth 320
+This will specify a console resolution of 320 by 240 (the height is
+automatically determined by the default 4:3 aspect ratio, you can also
+specify the height directly with -conheight).
+
+In higher resolution modes such as 800x600 and 1024x768, glquake will default
+to a 640x480 console, since the font becomes small enough at higher
+resolutions to become unreadable. If do you wish to have a higher resolution
+console and status bar, specify it as well, such as:
+glquake -width 800 -height 600 -conwidth 800
+
+texture options
+---------------
+The amount of textures used in the game can have a large impact on performance.
+There are several options that let you trade off visual quality for better
+performance.
+
+There is no way to flush already loaded textures, so it is best to change
+these options on the command line, or they will only take effect on some of
+the textures when you change levels.
+
+OpenGL only allows textures to repeat on power of two boundaries (32, 64,
+128, etc), but software quake had a number of textures that repeated at 24
+or 96 pixel boundaries. These need to be either stretched out to the next
+higher size, or shrunk down to the next lower. By default, they are filtered
+down to the smaller size, but you can cause it to use the larger size if you
+really want by using:
+
+glquake +gl_round_down 0
+This will generally run well on a normal 4 MB 3dfx card, but for other cards
+that have either worse texture management or slower texture swapping speeds,
+there are some additional settings that can drastically lower the amount of
+textures to be managed.
+
+glquake +gl_picmip 1
+This causes all textures to have one half the dimensions they otherwise would.
+This makes them blurry, but very small. You can set this to 2 to make the
+textures one quarter the resolution on each axis for REALLY blurry textures.
+
+glquake +gl_playermip 1
+This is similar to picmip, but is only used for other players in deathmatch.
+Each player in a deathmatch requires an individual skin texture, so this can
+be a serious problem for texture management. It wouldn't be unreasonable to
+set this to 2 or even 3 if you are playing competatively (and don't care if
+the other guys have smudged skins). If you change this during the game, it
+will take effect as soon as a player changes their skin colors.
+
+GLQuake also supports the following extensions for faster texture operation:
+
+GL_SGIS_multitexture
+Multitextures support allows certain hardware to render the world in one
+pass instead of two. GLQuake uses two passes, one for the world textures
+and the second for the lightmaps that are blended on the textures. On some
+hardware, with a GL_SIGS_multitexture supported OpenGL implementation, this
+can be done in one pass. On hardware that supports this, you will get a
+60% to 100% increase in frame rate. Currently, only 3DFX dual TMU cards
+(such as the Obsidian 2220) support this extension, but other hardware will
+soon follow.
+
+This extension will be autodetected and used. If for some reason it is not
+working correctly, specify the command line option "-nomtex" to disable it.
+
+GL_EXT_shared_texture_palette
+GLQuake uses 16bit textures by default but on OpenGL implementations
+that support the GL_EXT_shared_texture_palette extension, GLQuake will use
+8bit textures instead. This results in using half the needed texture memory
+of 16bit texture and can improve performance. This is very little difference
+in visual quality due to the fact that the textures are 8bit sources to
+begin with.
+
+run time options
+----------------
+At the console, you can set these values to effect drawing.
+
+gl_texturemode GL_NEAREST
+Sets texture mapping to point sampled, which may be faster on some GL systems
+(not on 3dfx).
+
+gl_texturemode GL_LINEAR_MIPMAP
+This is the default texture mode.
+
+gl_texturemode GL_LINEAR_MIPMAP_LINEAR
+This is the highest quality texture mapping (trilinear), but only very high
+end hardware (intergraph intense 3D / realizm) supports it. Not that big of
+a deal, actually.
+
+gl_finish 0
+This causes the game to not issue a glFinish() call each frame, which may make
+some hardware run faster. If this is cleared, the 3dfx will back up a number
+of frames and not be very playable.
+
+gl_flashblend 0
+By default, glquake just draws a shaded ball around objects that are emiting
+light. Clearing this variable will cause it to properly relight the world
+like normal quake, but it can be a significant speed hit on some systems.
+
+gl_ztrick 0
+Glquake uses a buffering method that avoids clearing the Z buffer, but some
+hardware platforms don't like it. If the status bar and console are flashing
+every other frame, clear this variable.
+
+gl_keeptjunctions 0
+If you clear this, glquake will remove colinear vertexes when it reloads the
+level. This can give a few percent speedup, but it can leave a couple stray
+blinking pixels on the screen.
+
+novelty features
+----------------
+These are some rendering tricks that were easy to do in glquake. They aren't
+very robust, but they are pretty cool to look at.
+
+r_shadows 1
+This causes every object to cast a shadow.
+
+r_wateralpha 0.7
+This sets the opacity of water textures, so you can see through it in properly
+processed maps. 0.3 is very faint, almost like fog. 1 is completely solid
+(the default). Unfortunately, the standard quake maps don't contain any
+visibility information for seeing past water surfaces, so you can't just play
+quake with this turned on. If you just want to see what it looks like, you
+can set "r_novis 1", but that will make things go very slow. When I get a
+chance, I will probably release some maps that have been processed properly
+for this.
+
+r_mirroralpha 0.3
+This changes one particular texture (the stained glass texture in the EASY
+start hall) into a mirror. The value is the opacity of the mirror surface.
+
--- /dev/null
+++ b/WinQuake/mathlib.c
@@ -1,0 +1,585 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program 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.
+
+This program 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 this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// mathlib.c -- math primitives
+
+#include <math.h>
+#include "quakedef.h"
+
+void Sys_Error (char *error, ...);
+
+vec3_t vec3_origin = {0,0,0};
+int nanmask = 255<<23;
+
+/*-----------------------------------------------------------------*/
+
+#define DEG2RAD( a ) ( a * M_PI ) / 180.0F
+
+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 );
+}
+
+#ifdef _WIN32
+#pragma optimize( "", off )
+#endif
+
+
+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;
+
+ 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;
+
+ zrot[0][0] = cos( DEG2RAD( degrees ) );
+ zrot[0][1] = sin( DEG2RAD( degrees ) );
+ zrot[1][0] = -sin( DEG2RAD( degrees ) );
+ zrot[1][1] = cos( DEG2RAD( degrees ) );
+
+ R_ConcatRotations( m, zrot, tmpmat );
+ R_ConcatRotations( 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];
+ }
+}
+
+#ifdef _WIN32
+#pragma optimize( "", on )
+#endif
+
+/*-----------------------------------------------------------------*/
+
+
+float anglemod(float a)
+{
+#if 0
+ if (a >= 0)
+ a -= 360*(int)(a/360);
+ else
+ a += 360*( 1 + (int)(-a/360) );
+#endif
+ a = (360.0/65536) * ((int)(a*(65536/360.0)) & 65535);
+ return a;
+}
+
+/*
+==================
+BOPS_Error
+
+Split out like this for ASM to call.
+==================
+*/
+void BOPS_Error (void)
+{
+ Sys_Error ("BoxOnPlaneSide: Bad signbits");
+}
+
+
+#if !id386
+
+/*
+==================
+BoxOnPlaneSide
+
+Returns 1, 2, or 1 + 2
+==================
+*/
+int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, mplane_t *p)
+{
+ float dist1, dist2;
+ int sides;
+
+#if 0 // this is done by the BOX_ON_PLANE_SIDE macro before calling this
+ // function
+// fast axial cases
+ if (p->type < 3)
+ {
+ if (p->dist <= emins[p->type])
+ return 1;
+ if (p->dist >= emaxs[p->type])
+ return 2;
+ return 3;
+ }
+#endif
+
+// general case
+ switch (p->signbits)
+ {
+ case 0:
+dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+ break;
+ case 1:
+dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+ break;
+ case 2:
+dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+ break;
+ case 3:
+dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+ break;
+ case 4:
+dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+ break;
+ case 5:
+dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+ break;
+ case 6:
+dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+ break;
+ case 7:
+dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+ break;
+ default:
+ dist1 = dist2 = 0; // shut up compiler
+ BOPS_Error ();
+ break;
+ }
+
+#if 0
+ int i;
+ vec3_t corners[2];
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if (plane->normal[i] < 0)
+ {
+ corners[0][i] = emins[i];
+ corners[1][i] = emaxs[i];
+ }
+ else
+ {
+ corners[1][i] = emins[i];
+ corners[0][i] = emaxs[i];
+ }
+ }
+ dist = DotProduct (plane->normal, corners[0]) - plane->dist;
+ dist2 = DotProduct (plane->normal, corners[1]) - plane->dist;
+ sides = 0;
+ if (dist1 >= 0)
+ sides = 1;
+ if (dist2 < 0)
+ sides |= 2;
+
+#endif
+
+ sides = 0;
+ if (dist1 >= p->dist)
+ sides = 1;
+ if (dist2 < p->dist)
+ sides |= 2;
+
+#ifdef PARANOID
+if (sides == 0)
+ Sys_Error ("BoxOnPlaneSide: sides==0");
+#endif
+
+ return sides;
+}
+
+#endif
+
+
+void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
+{
+ float angle;
+ float sr, sp, sy, cr, cp, cy;
+
+ angle = angles[YAW] * (M_PI*2 / 360);
+ sy = sin(angle);
+ cy = cos(angle);
+ angle = angles[PITCH] * (M_PI*2 / 360);
+ sp = sin(angle);
+ cp = cos(angle);
+ angle = angles[ROLL] * (M_PI*2 / 360);
+ sr = sin(angle);
+ cr = cos(angle);
+
+ forward[0] = cp*cy;
+ forward[1] = cp*sy;
+ forward[2] = -sp;
+ right[0] = (-1*sr*sp*cy+-1*cr*-sy);
+ right[1] = (-1*sr*sp*sy+-1*cr*cy);
+ right[2] = -1*sr*cp;
+ up[0] = (cr*sp*cy+-sr*-sy);
+ up[1] = (cr*sp*sy+-sr*cy);
+ up[2] = cr*cp;
+}
+
+int VectorCompare (vec3_t v1, vec3_t v2)
+{
+ int i;
+
+ for (i=0 ; i<3 ; i++)
+ if (v1[i] != v2[i])
+ return 0;
+
+ return 1;
+}
+
+void VectorMA (vec3_t veca, float scale, vec3_t vecb, vec3_t vecc)
+{
+ vecc[0] = veca[0] + scale*vecb[0];
+ vecc[1] = veca[1] + scale*vecb[1];
+ vecc[2] = veca[2] + scale*vecb[2];
+}
+
+
+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 veca, vec3_t vecb, vec3_t out)
+{
+ out[0] = veca[0]-vecb[0];
+ out[1] = veca[1]-vecb[1];
+ out[2] = veca[2]-vecb[2];
+}
+
+void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out)
+{
+ out[0] = veca[0]+vecb[0];
+ out[1] = veca[1]+vecb[1];
+ out[2] = veca[2]+vecb[2];
+}
+
+void _VectorCopy (vec3_t in, vec3_t out)
+{
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+}
+
+void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross)
+{
+ cross[0] = v1[1]*v2[2] - v1[2]*v2[1];
+ cross[1] = v1[2]*v2[0] - v1[0]*v2[2];
+ cross[2] = v1[0]*v2[1] - v1[1]*v2[0];
+}
+
+double sqrt(double x);
+
+vec_t Length(vec3_t v)
+{
+ int i;
+ float length;
+
+ length = 0;
+ for (i=0 ; i< 3 ; i++)
+ length += v[i]*v[i];
+ length = sqrt (length); // FIXME
+
+ return length;
+}
+
+float VectorNormalize (vec3_t v)
+{
+ float length, ilength;
+
+ length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
+ length = sqrt (length); // FIXME
+
+ if (length)
+ {
+ ilength = 1/length;
+ v[0] *= ilength;
+ v[1] *= ilength;
+ v[2] *= ilength;
+ }
+
+ return length;
+
+}
+
+void VectorInverse (vec3_t v)
+{
+ v[0] = -v[0];
+ v[1] = -v[1];
+ v[2] = -v[2];
+}
+
+void VectorScale (vec3_t in, vec_t scale, vec3_t out)
+{
+ out[0] = in[0]*scale;
+ out[1] = in[1]*scale;
+ out[2] = in[2]*scale;
+}
+
+
+int Q_log2(int val)
+{
+ int answer=0;
+ while (val>>=1)
+ answer++;
+ return answer;
+}
+
+
+/*
+================
+R_ConcatRotations
+================
+*/
+void R_ConcatRotations (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];
+}
+
+
+/*
+================
+R_ConcatTransforms
+================
+*/
+void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4])
+{
+ 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[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] +
+ in1[0][2] * in2[2][3] + in1[0][3];
+ 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[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] +
+ in1[1][2] * in2[2][3] + in1[1][3];
+ 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];
+ out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] +
+ in1[2][2] * in2[2][3] + in1[2][3];
+}
+
+
+/*
+===================
+FloorDivMod
+
+Returns mathematically correct (floor-based) quotient and remainder for
+numer and denom, both of which should contain no fractional part. The
+quotient must fit in 32 bits.
+====================
+*/
+
+void FloorDivMod (double numer, double denom, int *quotient,
+ int *rem)
+{
+ int q, r;
+ double x;
+
+#ifndef PARANOID
+ if (denom <= 0.0)
+ Sys_Error ("FloorDivMod: bad denominator %d\n", denom);
+
+// if ((floor(numer) != numer) || (floor(denom) != denom))
+// Sys_Error ("FloorDivMod: non-integer numer or denom %f %f\n",
+// numer, denom);
+#endif
+
+ if (numer >= 0.0)
+ {
+
+ x = floor(numer / denom);
+ q = (int)x;
+ r = (int)floor(numer - (x * denom));
+ }
+ else
+ {
+ //
+ // perform operations with positive values, and fix mod to make floor-based
+ //
+ x = floor(-numer / denom);
+ q = -(int)x;
+ r = (int)floor(-numer - (x * denom));
+ if (r != 0)
+ {
+ q--;
+ r = (int)denom - r;
+ }
+ }
+
+ *quotient = q;
+ *rem = r;
+}
+
+
+/*
+===================
+GreatestCommonDivisor
+====================
+*/
+int GreatestCommonDivisor (int i1, int i2)
+{
+ if (i1 > i2)
+ {
+ if (i2 == 0)
+ return (i1);
+ return GreatestCommonDivisor (i2, i1 % i2);
+ }
+ else
+ {
+ if (i1 == 0)
+ return (i2);
+ return GreatestCommonDivisor (i1, i2 % i1);
+ }
+}
+
+
+#if !id386
+
+// TODO: move to nonintel.c
+
+/*
+===================
+Invert24To16
+
+Inverts an 8.24 value to a 16.16 value
+====================
+*/
+
+fixed16_t Invert24To16(fixed16_t val)
+{
+ if (val < 256)
+ return (0xFFFFFFFF);
+
+ return (fixed16_t)
+ (((double)0x10000 * (double)0x1000000 / (double)val) + 0.5);
+}
+
+#endif
--- /dev/null
+++ b/WinQuake/model.c
@@ -1,0 +1,1874 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program 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.
+
+This program 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 this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// models.c -- model loading and caching
+
+// models are the only shared resource between a client and server running
+// on the same machine.
+
+#include "quakedef.h"
+#include "r_local.h"
+
+model_t *loadmodel;
+char loadname[32]; // for hunk tags
+
+void Mod_LoadSpriteModel (model_t *mod, void *buffer);
+void Mod_LoadBrushModel (model_t *mod, void *buffer);
+void Mod_LoadAliasModel (model_t *mod, void *buffer);
+model_t *Mod_LoadModel (model_t *mod, qboolean crash);
+
+byte mod_novis[MAX_MAP_LEAFS/8];
+
+#define MAX_MOD_KNOWN 256
+model_t mod_known[MAX_MOD_KNOWN];
+int mod_numknown;
+
+// values for model_t's needload
+#define NL_PRESENT 0
+#define NL_NEEDS_LOADED 1
+#define NL_UNREFERENCED 2
+
+/*
+===============
+Mod_Init
+===============
+*/
+void Mod_Init (void)
+{
+ memset (mod_novis, 0xff, sizeof(mod_novis));
+}
+
+/*
+===============
+Mod_Extradata
+
+Caches the data if needed
+===============
+*/
+void *Mod_Extradata (model_t *mod)
+{
+ void *r;
+
+ r = Cache_Check (&mod->cache);
+ if (r)
+ return r;
+
+ Mod_LoadModel (mod, true);
+
+ if (!mod->cache.data)
+ Sys_Error ("Mod_Extradata: caching failed");
+ return mod->cache.data;
+}
+
+/*
+===============
+Mod_PointInLeaf
+===============
+*/
+mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model)
+{
+ mnode_t *node;
+ float d;
+ mplane_t *plane;
+
+ if (!model || !model->nodes)
+ Sys_Error ("Mod_PointInLeaf: bad model");
+
+ node = model->nodes;
+ while (1)
+ {
+ if (node->contents < 0)
+ return (mleaf_t *)node;
+ plane = node->plane;
+ d = DotProduct (p,plane->normal) - plane->dist;
+ if (d > 0)
+ node = node->children[0];
+ else
+ node = node->children[1];
+ }
+
+ return NULL; // never reached
+}
+
+
+/*
+===================
+Mod_DecompressVis
+===================
+*/
+byte *Mod_DecompressVis (byte *in, model_t *model)
+{
+ static byte decompressed[MAX_MAP_LEAFS/8];
+ int c;
+ byte *out;
+ int row;
+
+ row = (model->numleafs+7)>>3;
+ out = decompressed;
+
+ if (!in)
+ { // no vis info, so make all visible
+ while (row)
+ {
+ *out++ = 0xff;
+ row--;
+ }
+ return decompressed;
+ }
+
+ do
+ {
+ if (*in)
+ {
+ *out++ = *in++;
+ continue;
+ }
+
+ c = in[1];
+ in += 2;
+ while (c)
+ {
+ *out++ = 0;
+ c--;
+ }
+ } while (out - decompressed < row);
+
+ return decompressed;
+}
+
+byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model)
+{
+ if (leaf == model->leafs)
+ return mod_novis;
+ return Mod_DecompressVis (leaf->compressed_vis, model);
+}
+
+/*
+===================
+Mod_ClearAll
+===================
+*/
+void Mod_ClearAll (void)
+{
+ int i;
+ model_t *mod;
+
+
+ for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++) {
+ mod->needload = NL_UNREFERENCED;
+//FIX FOR CACHE_ALLOC ERRORS:
+ if (mod->type == mod_sprite) mod->cache.data = NULL;
+ }
+}
+
+/*
+==================
+Mod_FindName
+
+==================
+*/
+model_t *Mod_FindName (char *name)
+{
+ int i;
+ model_t *mod;
+ model_t *avail = NULL;
+
+ if (!name[0])
+ Sys_Error ("Mod_ForName: NULL name");
+
+//
+// search the currently loaded models
+//
+ for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)
+ {
+ if (!strcmp (mod->name, name) )
+ break;
+ if (mod->needload == NL_UNREFERENCED)
+ if (!avail || mod->type != mod_alias)
+ avail = mod;
+ }
+
+ if (i == mod_numknown)
+ {
+ if (mod_numknown == MAX_MOD_KNOWN)
+ {
+ if (avail)
+ {
+ mod = avail;
+ if (mod->type == mod_alias)
+ if (Cache_Check (&mod->cache))
+ Cache_Free (&mod->cache);
+ }
+ else
+ Sys_Error ("mod_numknown == MAX_MOD_KNOWN");
+ }
+ else
+ mod_numknown++;
+ strcpy (mod->name, name);
+ mod->needload = NL_NEEDS_LOADED;
+ }
+
+ return mod;
+}
+
+/*
+==================
+Mod_TouchModel
+
+==================
+*/
+void Mod_TouchModel (char *name)
+{
+ model_t *mod;
+
+ mod = Mod_FindName (name);
+
+ if (mod->needload == NL_PRESENT)
+ {
+ if (mod->type == mod_alias)
+ Cache_Check (&mod->cache);
+ }
+}
+
+/*
+==================
+Mod_LoadModel
+
+Loads a model into the cache
+==================
+*/
+model_t *Mod_LoadModel (model_t *mod, qboolean crash)
+{
+ unsigned *buf;
+ byte stackbuf[1024]; // avoid dirtying the cache heap
+
+ if (mod->type == mod_alias)
+ {
+ if (Cache_Check (&mod->cache))
+ {
+ mod->needload = NL_PRESENT;
+ return mod;
+ }
+ }
+ else
+ {
+ if (mod->needload == NL_PRESENT)
+ return mod;
+ }
+
+//
+// because the world is so huge, load it one piece at a time
+//
+
+//
+// load the file
+//
+ buf = (unsigned *)COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf));
+ if (!buf)
+ {
+ if (crash)
+ Sys_Error ("Mod_NumForName: %s not found", mod->name);
+ return NULL;
+ }
+
+//
+// allocate a new model
+//
+ COM_FileBase (mod->name, loadname);
+
+ loadmodel = mod;
+
+//
+// fill it in
+//
+
+// call the apropriate loader
+ mod->needload = NL_PRESENT;
+
+ switch (LittleLong(*(unsigned *)buf))
+ {
+ case IDPOLYHEADER:
+ Mod_LoadAliasModel (mod, buf);
+ break;
+
+ case IDSPRITEHEADER:
+ Mod_LoadSpriteModel (mod, buf);
+ break;
+
+ default:
+ Mod_LoadBrushModel (mod, buf);
+ break;
+ }
+
+ return mod;
+}
+
+/*
+==================
+Mod_ForName
+
+Loads in a model for the given name
+==================
+*/
+model_t *Mod_ForName (char *name, qboolean crash)
+{
+ model_t *mod;
+
+ mod = Mod_FindName (name);
+
+ return Mod_LoadModel (mod, crash);
+}
+
+
+/*
+===============================================================================
+
+ BRUSHMODEL LOADING
+
+===============================================================================
+*/
+
+byte *mod_base;
+
+
+/*
+=================
+Mod_LoadTextures
+=================
+*/
+void Mod_LoadTextures (lump_t *l)
+{
+ int i, j, pixels, num, max, altmax;
+ miptex_t *mt;
+ texture_t *tx, *tx2;
+ texture_t *anims[10];
+ texture_t *altanims[10];
+ dmiptexlump_t *m;
+
+ if (!l->filelen)
+ {
+ loadmodel->textures = NULL;
+ return;
+ }
+ m = (dmiptexlump_t *)(mod_base + l->fileofs);
+
+ m->nummiptex = LittleLong (m->nummiptex);
+
+ loadmodel->numtextures = m->nummiptex;
+ loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures) , loadname);
+
+ for (i=0 ; i<m->nummiptex ; i++)
+ {
+ m->dataofs[i] = LittleLong(m->dataofs[i]);
+ if (m->dataofs[i] == -1)
+ continue;
+ mt = (miptex_t *)((byte *)m + m->dataofs[i]);
+ mt->width = LittleLong (mt->width);
+ mt->height = LittleLong (mt->height);
+ for (j=0 ; j<MIPLEVELS ; j++)
+ mt->offsets[j] = LittleLong (mt->offsets[j]);
+
+ if ( (mt->width & 15) || (mt->height & 15) )
+ Sys_Error ("Texture %s is not 16 aligned", mt->name);
+ pixels = mt->width*mt->height/64*85;
+ tx = Hunk_AllocName (sizeof(texture_t) +pixels, loadname );
+ loadmodel->textures[i] = tx;
+
+ memcpy (tx->name, mt->name, sizeof(tx->name));
+ tx->width = mt->width;
+ tx->height = mt->height;
+ for (j=0 ; j<MIPLEVELS ; j++)
+ tx->offsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t);
+ // the pixels immediately follow the structures
+ memcpy ( tx+1, mt+1, pixels);
+
+ if (!Q_strncmp(mt->name,"sky",3))
+ R_InitSky (tx);
+ }
+
+//
+// sequence the animations
+//
+ for (i=0 ; i<m->nummiptex ; i++)
+ {
+ tx = loadmodel->textures[i];
+ if (!tx || tx->name[0] != '+')
+ continue;
+ if (tx->anim_next)
+ continue; // allready sequenced
+
+ // find the number of frames in the animation
+ memset (anims, 0, sizeof(anims));
+ memset (altanims, 0, sizeof(altanims));
+
+ max = tx->name[1];
+ altmax = 0;
+ if (max >= 'a' && max <= 'z')
+ max -= 'a' - 'A';
+ if (max >= '0' && max <= '9')
+ {
+ max -= '0';
+ altmax = 0;
+ anims[max] = tx;
+ max++;
+ }
+ else if (max >= 'A' && max <= 'J')
+ {
+ altmax = max - 'A';
+ max = 0;
+ altanims[altmax] = tx;
+ altmax++;
+ }
+ else
+ Sys_Error ("Bad animating texture %s", tx->name);
+
+ for (j=i+1 ; j<m->nummiptex ; j++)
+ {
+ tx2 = loadmodel->textures[j];
+ if (!tx2 || tx2->name[0] != '+')
+ continue;
+ if (strcmp (tx2->name+2, tx->name+2))
+ continue;
+
+ num = tx2->name[1];
+ if (num >= 'a' && num <= 'z')
+ num -= 'a' - 'A';
+ if (num >= '0' && num <= '9')
+ {
+ num -= '0';
+ anims[num] = tx2;
+ if (num+1 > max)
+ max = num + 1;
+ }
+ else if (num >= 'A' && num <= 'J')
+ {
+ num = num - 'A';
+ altanims[num] = tx2;
+ if (num+1 > altmax)
+ altmax = num+1;
+ }
+ else
+ Sys_Error ("Bad animating texture %s", tx->name);
+ }
+
+#define ANIM_CYCLE 2
+ // link them all together
+ for (j=0 ; j<max ; j++)
+ {
+ tx2 = anims[j];
+ if (!tx2)
+ Sys_Error ("Missing frame %i of %s",j, tx->name);
+ tx2->anim_total = max * ANIM_CYCLE;
+ tx2->anim_min = j * ANIM_CYCLE;
+ tx2->anim_max = (j+1) * ANIM_CYCLE;
+ tx2->anim_next = anims[ (j+1)%max ];
+ if (altmax)
+ tx2->alternate_anims = altanims[0];
+ }
+ for (j=0 ; j<altmax ; j++)
+ {
+ tx2 = altanims[j];
+ if (!tx2)
+ Sys_Error ("Missing frame %i of %s",j, tx->name);
+ tx2->anim_total = altmax * ANIM_CYCLE;
+ tx2->anim_min = j * ANIM_CYCLE;
+ tx2->anim_max = (j+1) * ANIM_CYCLE;
+ tx2->anim_next = altanims[ (j+1)%altmax ];
+ if (max)
+ tx2->alternate_anims = anims[0];
+ }
+ }
+}
+
+/*
+=================
+Mod_LoadLighting
+=================
+*/
+void Mod_LoadLighting (lump_t *l)
+{
+ if (!l->filelen)
+ {
+ loadmodel->lightdata = NULL;
+ return;
+ }
+ loadmodel->lightdata = Hunk_AllocName ( l->filelen, loadname);
+ memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
+}
+
+
+/*
+=================
+Mod_LoadVisibility
+=================
+*/
+void Mod_LoadVisibility (lump_t *l)
+{
+ if (!l->filelen)
+ {
+ loadmodel->visdata = NULL;
+ return;
+ }
+ loadmodel->visdata = Hunk_AllocName ( l->filelen, loadname);
+ memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
+}
+
+
+/*
+=================
+Mod_LoadEntities
+=================
+*/
+void Mod_LoadEntities (lump_t *l)
+{
+ if (!l->filelen)
+ {
+ loadmodel->entities = NULL;
+ return;
+ }
+ loadmodel->entities = Hunk_AllocName ( l->filelen, loadname);
+ memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
+}
+
+
+/*
+=================
+Mod_LoadVertexes
+=================
+*/
+void Mod_LoadVertexes (lump_t *l)
+{
+ dvertex_t *in;
+ mvertex_t *out;
+ int i, count;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Hunk_AllocName ( count*sizeof(*out), loadname);
+
+ loadmodel->vertexes = out;
+ loadmodel->numvertexes = count;
+
+ for ( i=0 ; i<count ; i++, in++, out++)
+ {
+ out->position[0] = LittleFloat (in->point[0]);
+ out->position[1] = LittleFloat (in->point[1]);
+ out->position[2] = LittleFloat (in->point[2]);
+ }
+}
+
+/*
+=================
+Mod_LoadSubmodels
+=================
+*/
+void Mod_LoadSubmodels (lump_t *l)
+{
+ dmodel_t *in;
+ dmodel_t *out;
+ int i, j, count;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Hunk_AllocName ( count*sizeof(*out), loadname);
+
+ loadmodel->submodels = out;
+ loadmodel->numsubmodels = count;
+
+ for ( i=0 ; i<count ; i++, in++, out++)
+ {
+ for (j=0 ; j<3 ; j++)
+ { // spread the mins / maxs by a pixel
+ out->mins[j] = LittleFloat (in->mins[j]) - 1;
+ out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
+ out->origin[j] = LittleFloat (in->origin[j]);
+ }
+ for (j=0 ; j<MAX_MAP_HULLS ; j++)
+ out->headnode[j] = LittleLong (in->headnode[j]);
+ out->visleafs = LittleLong (in->visleafs);
+ out->firstface = LittleLong (in->firstface);
+ out->numfaces = LittleLong (in->numfaces);
+ }
+}
+
+/*
+=================
+Mod_LoadEdges
+=================
+*/
+void Mod_LoadEdges (lump_t *l)
+{
+ dedge_t *in;
+ medge_t *out;
+ int i, count;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Hunk_AllocName ( (count + 1) * sizeof(*out), loadname);
+
+ loadmodel->edges = out;
+ loadmodel->numedges = count;
+
+ for ( i=0 ; i<count ; i++, in++, out++)
+ {
+ out->v[0] = (unsigned short)LittleShort(in->v[0]);
+ out->v[1] = (unsigned short)LittleShort(in->v[1]);
+ }
+}
+
+/*
+=================
+Mod_LoadTexinfo
+=================
+*/
+void Mod_LoadTexinfo (lump_t *l)
+{
+ texinfo_t *in;
+ mtexinfo_t *out;
+ int i, j, count;
+ int miptex;
+ float len1, len2;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Hunk_AllocName ( count*sizeof(*out), loadname);
+
+ loadmodel->texinfo = out;
+ loadmodel->numtexinfo = count;
+
+ for ( i=0 ; i<count ; i++, in++, out++)
+ {
+ for (j=0 ; j<8 ; j++)
+ out->vecs[0][j] = LittleFloat (in->vecs[0][j]);
+ len1 = Length (out->vecs[0]);
+ len2 = Length (out->vecs[1]);
+ len1 = (len1 + len2)/2;
+ if (len1 < 0.32)
+ out->mipadjust = 4;
+ else if (len1 < 0.49)
+ out->mipadjust = 3;
+ else if (len1 < 0.99)
+ out->mipadjust = 2;
+ else
+ out->mipadjust = 1;
+#if 0
+ if (len1 + len2 < 0.001)
+ out->mipadjust = 1; // don't crash
+ else
+ out->mipadjust = 1 / floor( (len1+len2)/2 + 0.1 );
+#endif
+
+ miptex = LittleLong (in->miptex);
+ out->flags = LittleLong (in->flags);
+
+ if (!loadmodel->textures)
+ {
+ out->texture = r_notexture_mip; // checkerboard texture
+ out->flags = 0;
+ }
+ else
+ {
+ if (miptex >= loadmodel->numtextures)
+ Sys_Error ("miptex >= loadmodel->numtextures");
+ out->texture = loadmodel->textures[miptex];
+ if (!out->texture)
+ {
+ out->texture = r_notexture_mip; // texture not found
+ out->flags = 0;
+ }
+ }
+ }
+}
+
+/*
+================
+CalcSurfaceExtents
+
+Fills in s->texturemins[] and s->extents[]
+================
+*/
+void CalcSurfaceExtents (msurface_t *s)
+{
+ float mins[2], maxs[2], val;
+ int i,j, e;
+ mvertex_t *v;
+ mtexinfo_t *tex;
+ int bmins[2], bmaxs[2];
+
+ mins[0] = mins[1] = 999999;
+ maxs[0] = maxs[1] = -99999;
+
+ tex = s->texinfo;
+
+ for (i=0 ; i<s->numedges ; i++)
+ {
+ e = loadmodel->surfedges[s->firstedge+i];
+ if (e >= 0)
+ v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
+ else
+ v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
+
+ for (j=0 ; j<2 ; j++)
+ {
+ val = v->position[0] * tex->vecs[j][0] +
+ v->position[1] * tex->vecs[j][1] +
+ v->position[2] * tex->vecs[j][2] +
+ tex->vecs[j][3];
+ if (val < mins[j])
+ mins[j] = val;
+ if (val > maxs[j])
+ maxs[j] = val;
+ }
+ }
+
+ for (i=0 ; i<2 ; i++)
+ {
+ bmins[i] = floor(mins[i]/16);
+ bmaxs[i] = ceil(maxs[i]/16);
+
+ s->texturemins[i] = bmins[i] * 16;
+ s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
+ if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 256)
+ Sys_Error ("Bad surface extents");
+ }
+}
+
+
+/*
+=================
+Mod_LoadFaces
+=================
+*/
+void Mod_LoadFaces (lump_t *l)
+{
+ dface_t *in;
+ msurface_t *out;
+ int i, count, surfnum;
+ int planenum, side;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Hunk_AllocName ( count*sizeof(*out), loadname);
+
+ loadmodel->surfaces = out;
+ loadmodel->numsurfaces = count;
+
+ for ( surfnum=0 ; surfnum<count ; surfnum++, in++, out++)
+ {
+ out->firstedge = LittleLong(in->firstedge);
+ out->numedges = LittleShort(in->numedges);
+ out->flags = 0;
+
+ planenum = LittleShort(in->planenum);
+ side = LittleShort(in->side);
+ if (side)
+ out->flags |= SURF_PLANEBACK;
+
+ out->plane = loadmodel->planes + planenum;
+
+ out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo);
+
+ CalcSurfaceExtents (out);
+
+ // lighting info
+
+ for (i=0 ; i<MAXLIGHTMAPS ; i++)
+ out->styles[i] = in->styles[i];
+ i = LittleLong(in->lightofs);
+ if (i == -1)
+ out->samples = NULL;
+ else
+ out->samples = loadmodel->lightdata + i;
+
+ // set the drawing flags flag
+
+ if (!Q_strncmp(out->texinfo->texture->name,"sky",3)) // sky
+ {
+ out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED);
+ continue;
+ }
+
+ if (!Q_strncmp(out->texinfo->texture->name,"*",1)) // turbulent
+ {
+ out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED);
+ for (i=0 ; i<2 ; i++)
+ {
+ out->extents[i] = 16384;
+ out->texturemins[i] = -8192;
+ }
+ continue;
+ }
+ }
+}
+
+
+/*
+=================
+Mod_SetParent
+=================
+*/
+void Mod_SetParent (mnode_t *node, mnode_t *parent)
+{
+ node->parent = parent;
+ if (node->contents < 0)
+ return;
+ Mod_SetParent (node->children[0], node);
+ Mod_SetParent (node->children[1], node);
+}
+
+/*
+=================
+Mod_LoadNodes
+=================
+*/
+void Mod_LoadNodes (lump_t *l)
+{
+ int i, j, count, p;
+ dnode_t *in;
+ mnode_t *out;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Hunk_AllocName ( count*sizeof(*out), loadname);
+
+ loadmodel->nodes = out;
+ loadmodel->numnodes = count;
+
+ for ( i=0 ; i<count ; i++, in++, out++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ out->minmaxs[j] = LittleShort (in->mins[j]);
+ out->minmaxs[3+j] = LittleShort (in->maxs[j]);
+ }
+
+ p = LittleLong(in->planenum);
+ out->plane = loadmodel->planes + p;
+
+ out->firstsurface = LittleShort (in->firstface);
+ out->numsurfaces = LittleShort (in->numfaces);
+
+ for (j=0 ; j<2 ; j++)
+ {
+ p = LittleShort (in->children[j]);
+ if (p >= 0)
+ out->children[j] = loadmodel->nodes + p;
+ else
+ out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
+ }
+ }
+
+ Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
+}
+
+/*
+=================
+Mod_LoadLeafs
+=================
+*/
+void Mod_LoadLeafs (lump_t *l)
+{
+ dleaf_t *in;
+ mleaf_t *out;
+ int i, j, count, p;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Hunk_AllocName ( count*sizeof(*out), loadname);
+
+ loadmodel->leafs = out;
+ loadmodel->numleafs = count;
+
+ for ( i=0 ; i<count ; i++, in++, out++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ out->minmaxs[j] = LittleShort (in->mins[j]);
+ out->minmaxs[3+j] = LittleShort (in->maxs[j]);
+ }
+
+ p = LittleLong(in->contents);
+ out->contents = p;
+
+ out->firstmarksurface = loadmodel->marksurfaces +
+ LittleShort(in->firstmarksurface);
+ out->nummarksurfaces = LittleShort(in->nummarksurfaces);
+
+ p = LittleLong(in->visofs);
+ if (p == -1)
+ out->compressed_vis = NULL;
+ else
+ out->compressed_vis = loadmodel->visdata + p;
+ out->efrags = NULL;
+
+ for (j=0 ; j<4 ; j++)
+ out->ambient_sound_level[j] = in->ambient_level[j];
+ }
+}
+
+/*
+=================
+Mod_LoadClipnodes
+=================
+*/
+void Mod_LoadClipnodes (lump_t *l)
+{
+ dclipnode_t *in, *out;
+ int i, count;
+ hull_t *hull;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Hunk_AllocName ( count*sizeof(*out), loadname);
+
+ loadmodel->clipnodes = out;
+ loadmodel->numclipnodes = count;
+
+ hull = &loadmodel->hulls[1];
+ hull->clipnodes = out;
+ hull->firstclipnode = 0;
+ hull->lastclipnode = count-1;
+ hull->planes = loadmodel->planes;
+ hull->clip_mins[0] = -16;
+ hull->clip_mins[1] = -16;
+ hull->clip_mins[2] = -24;
+ hull->clip_maxs[0] = 16;
+ hull->clip_maxs[1] = 16;
+ hull->clip_maxs[2] = 32;
+
+ hull = &loadmodel->hulls[2];
+ hull->clipnodes = out;
+ hull->firstclipnode = 0;
+ hull->lastclipnode = count-1;
+ hull->planes = loadmodel->planes;
+ hull->clip_mins[0] = -32;
+ hull->clip_mins[1] = -32;
+ hull->clip_mins[2] = -24;
+ hull->clip_maxs[0] = 32;
+ hull->clip_maxs[1] = 32;
+ hull->clip_maxs[2] = 64;
+
+ for (i=0 ; i<count ; i++, out++, in++)
+ {
+ out->planenum = LittleLong(in->planenum);
+ out->children[0] = LittleShort(in->children[0]);
+ out->children[1] = LittleShort(in->children[1]);
+ }
+}
+
+/*
+=================
+Mod_MakeHull0
+
+Deplicate the drawing hull structure as a clipping hull
+=================
+*/
+void Mod_MakeHull0 (void)
+{
+ mnode_t *in, *child;
+ dclipnode_t *out;
+ int i, j, count;
+ hull_t *hull;
+
+ hull = &loadmodel->hulls[0];
+
+ in = loadmodel->nodes;
+ count = loadmodel->numnodes;
+ out = Hunk_AllocName ( count*sizeof(*out), loadname);
+
+ hull->clipnodes = out;
+ hull->firstclipnode = 0;
+ hull->lastclipnode = count-1;
+ hull->planes = loadmodel->planes;
+
+ for (i=0 ; i<count ; i++, out++, in++)
+ {
+ out->planenum = in->plane - loadmodel->planes;
+ for (j=0 ; j<2 ; j++)
+ {
+ child = in->children[j];
+ if (child->contents < 0)
+ out->children[j] = child->contents;
+ else
+ out->children[j] = child - loadmodel->nodes;
+ }
+ }
+}
+
+/*
+=================
+Mod_LoadMarksurfaces
+=================
+*/
+void Mod_LoadMarksurfaces (lump_t *l)
+{
+ int i, j, count;
+ short *in;
+ msurface_t **out;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Hunk_AllocName ( count*sizeof(*out), loadname);
+
+ loadmodel->marksurfaces = out;
+ loadmodel->nummarksurfaces = count;
+
+ for ( i=0 ; i<count ; i++)
+ {
+ j = LittleShort(in[i]);
+ if (j >= loadmodel->numsurfaces)
+ Sys_Error ("Mod_ParseMarksurfaces: bad surface number");
+ out[i] = loadmodel->surfaces + j;
+ }
+}
+
+/*
+=================
+Mod_LoadSurfedges
+=================
+*/
+void Mod_LoadSurfedges (lump_t *l)
+{
+ int i, count;
+ int *in, *out;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Hunk_AllocName ( count*sizeof(*out), loadname);
+
+ loadmodel->surfedges = out;
+ loadmodel->numsurfedges = count;
+
+ for ( i=0 ; i<count ; i++)
+ out[i] = LittleLong (in[i]);
+}
+
+/*
+=================
+Mod_LoadPlanes
+=================
+*/
+void Mod_LoadPlanes (lump_t *l)
+{
+ int i, j;
+ mplane_t *out;
+ dplane_t *in;
+ int count;
+ int bits;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Hunk_AllocName ( count*2*sizeof(*out), loadname);
+
+ loadmodel->planes = out;
+ loadmodel->numplanes = count;
+
+ for ( i=0 ; i<count ; i++, in++, out++)
+ {
+ bits = 0;
+ for (j=0 ; j<3 ; j++)
+ {
+ out->normal[j] = LittleFloat (in->normal[j]);
+ if (out->normal[j] < 0)
+ bits |= 1<<j;
+ }
+
+ out->dist = LittleFloat (in->dist);
+ out->type = LittleLong (in->type);
+ out->signbits = bits;
+ }
+}
+
+/*
+=================
+RadiusFromBounds
+=================
+*/
+float RadiusFromBounds (vec3_t mins, vec3_t maxs)
+{
+ int i;
+ vec3_t corner;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]);
+ }
+
+ return Length (corner);
+}
+
+/*
+=================
+Mod_LoadBrushModel
+=================
+*/
+void Mod_LoadBrushModel (model_t *mod, void *buffer)
+{
+ int i, j;
+ dheader_t *header;
+ dmodel_t *bm;
+
+ loadmodel->type = mod_brush;
+
+ header = (dheader_t *)buffer;
+
+ i = LittleLong (header->version);
+ if (i != BSPVERSION)
+ Sys_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i)", mod->name, i, BSPVERSION);
+
+// swap all the lumps
+ mod_base = (byte *)header;
+
+ for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
+ ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
+
+// load into heap
+
+ Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
+ Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
+ Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
+ Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
+ Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
+ Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
+ Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
+ Mod_LoadFaces (&header->lumps[LUMP_FACES]);
+ Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
+ Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
+ Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
+ Mod_LoadNodes (&header->lumps[LUMP_NODES]);
+ Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
+ Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
+ Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
+
+ Mod_MakeHull0 ();
+
+ mod->numframes = 2; // regular and alternate animation
+ mod->flags = 0;
+
+//
+// set up the submodels (FIXME: this is confusing)
+//
+ for (i=0 ; i<mod->numsubmodels ; i++)
+ {
+ bm = &mod->submodels[i];
+
+ mod->hulls[0].firstclipnode = bm->headnode[0];
+ for (j=1 ; j<MAX_MAP_HULLS ; j++)
+ {
+ mod->hulls[j].firstclipnode = bm->headnode[j];
+ mod->hulls[j].lastclipnode = mod->numclipnodes-1;
+ }
+
+ mod->firstmodelsurface = bm->firstface;
+ mod->nummodelsurfaces = bm->numfaces;
+
+ VectorCopy (bm->maxs, mod->maxs);
+ VectorCopy (bm->mins, mod->mins);
+ mod->radius = RadiusFromBounds (mod->mins, mod->maxs);
+
+ mod->numleafs = bm->visleafs;
+
+ if (i < mod->numsubmodels-1)
+ { // duplicate the basic information
+ char name[10];
+
+ sprintf (name, "*%i", i+1);
+ loadmodel = Mod_FindName (name);
+ *loadmodel = *mod;
+ strcpy (loadmodel->name, name);
+ mod = loadmodel;
+ }
+ }
+}
+
+/*
+==============================================================================
+
+ALIAS MODELS
+
+==============================================================================
+*/
+
+/*
+=================
+Mod_LoadAliasFrame
+=================
+*/
+void * Mod_LoadAliasFrame (void * pin, int *pframeindex, int numv,
+ trivertx_t *pbboxmin, trivertx_t *pbboxmax, aliashdr_t *pheader, char *name)
+{
+ trivertx_t *pframe, *pinframe;
+ int i, j;
+ daliasframe_t *pdaliasframe;
+
+ pdaliasframe = (daliasframe_t *)pin;
+
+ strcpy (name, pdaliasframe->name);
+
+ for (i=0 ; i<3 ; i++)
+ {
+ // these are byte values, so we don't have to worry about
+ // endianness
+ pbboxmin->v[i] = pdaliasframe->bboxmin.v[i];
+ pbboxmax->v[i] = pdaliasframe->bboxmax.v[i];
+ }
+
+ pinframe = (trivertx_t *)(pdaliasframe + 1);
+ pframe = Hunk_AllocName (numv * sizeof(*pframe), loadname);
+
+ *pframeindex = (byte *)pframe - (byte *)pheader;
+
+ for (j=0 ; j<numv ; j++)
+ {
+ int k;
+
+ // these are all byte values, so no need to deal with endianness
+ pframe[j].lightnormalindex = pinframe[j].lightnormalindex;
+
+ for (k=0 ; k<3 ; k++)
+ {
+ pframe[j].v[k] = pinframe[j].v[k];
+ }
+ }
+
+ pinframe += numv;
+
+ return (void *)pinframe;
+}
+
+
+/*
+=================
+Mod_LoadAliasGroup
+=================
+*/
+void * Mod_LoadAliasGroup (void * pin, int *pframeindex, int numv,
+ trivertx_t *pbboxmin, trivertx_t *pbboxmax, aliashdr_t *pheader, char *name)
+{
+ daliasgroup_t *pingroup;
+ maliasgroup_t *paliasgroup;
+ int i, numframes;
+ daliasinterval_t *pin_intervals;
+ float *poutintervals;
+ void *ptemp;
+
+ pingroup = (daliasgroup_t *)pin;
+
+ numframes = LittleLong (pingroup->numframes);
+
+ paliasgroup = Hunk_AllocName (sizeof (maliasgroup_t) +
+ (numframes - 1) * sizeof (paliasgroup->frames[0]), loadname);
+
+ paliasgroup->numframes = numframes;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ // these are byte values, so we don't have to worry about endianness
+ pbboxmin->v[i] = pingroup->bboxmin.v[i];
+ pbboxmax->v[i] = pingroup->bboxmax.v[i];
+ }
+
+ *pframeindex = (byte *)paliasgroup - (byte *)pheader;
+
+ pin_intervals = (daliasinterval_t *)(pingroup + 1);
+
+ poutintervals = Hunk_AllocName (numframes * sizeof (float), loadname);
+
+ paliasgroup->intervals = (byte *)poutintervals - (byte *)pheader;
+
+ for (i=0 ; i<numframes ; i++)
+ {
+ *poutintervals = LittleFloat (pin_intervals->interval);
+ if (*poutintervals <= 0.0)
+ Sys_Error ("Mod_LoadAliasGroup: interval<=0");
+
+ poutintervals++;
+ pin_intervals++;
+ }
+
+ ptemp = (void *)pin_intervals;
+
+ for (i=0 ; i<numframes ; i++)
+ {
+ ptemp = Mod_LoadAliasFrame (ptemp,
+ &paliasgroup->frames[i].frame,
+ numv,
+ &paliasgroup->frames[i].bboxmin,
+ &paliasgroup->frames[i].bboxmax,
+ pheader, name);
+ }
+
+ return ptemp;
+}
+
+
+/*
+=================
+Mod_LoadAliasSkin
+=================
+*/
+void * Mod_LoadAliasSkin (void * pin, int *pskinindex, int skinsize,
+ aliashdr_t *pheader)
+{
+ int i;
+ byte *pskin, *pinskin;
+ unsigned short *pusskin;
+
+ pskin = Hunk_AllocName (skinsize * r_pixbytes, loadname);
+ pinskin = (byte *)pin;
+ *pskinindex = (byte *)pskin - (byte *)pheader;
+
+ if (r_pixbytes == 1)
+ {
+ Q_memcpy (pskin, pinskin, skinsize);
+ }
+ else if (r_pixbytes == 2)
+ {
+ pusskin = (unsigned short *)pskin;
+
+ for (i=0 ; i<skinsize ; i++)
+ pusskin[i] = d_8to16table[pinskin[i]];
+ }
+ else
+ {
+ Sys_Error ("Mod_LoadAliasSkin: driver set invalid r_pixbytes: %d\n",
+ r_pixbytes);
+ }
+
+ pinskin += skinsize;
+
+ return ((void *)pinskin);
+}
+
+
+/*
+=================
+Mod_LoadAliasSkinGroup
+=================
+*/
+void * Mod_LoadAliasSkinGroup (void * pin, int *pskinindex, int skinsize,
+ aliashdr_t *pheader)
+{
+ daliasskingroup_t *pinskingroup;
+ maliasskingroup_t *paliasskingroup;
+ int i, numskins;
+ daliasskininterval_t *pinskinintervals;
+ float *poutskinintervals;
+ void *ptemp;
+
+ pinskingroup = (daliasskingroup_t *)pin;
+
+ numskins = LittleLong (pinskingroup->numskins);
+
+ paliasskingroup = Hunk_AllocName (sizeof (maliasskingroup_t) +
+ (numskins - 1) * sizeof (paliasskingroup->skindescs[0]),
+ loadname);
+
+ paliasskingroup->numskins = numskins;
+
+ *pskinindex = (byte *)paliasskingroup - (byte *)pheader;
+
+ pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1);
+
+ poutskinintervals = Hunk_AllocName (numskins * sizeof (float),loadname);
+
+ paliasskingroup->intervals = (byte *)poutskinintervals - (byte *)pheader;
+
+ for (i=0 ; i<numskins ; i++)
+ {
+ *poutskinintervals = LittleFloat (pinskinintervals->interval);
+ if (*poutskinintervals <= 0)
+ Sys_Error ("Mod_LoadAliasSkinGroup: interval<=0");
+
+ poutskinintervals++;
+ pinskinintervals++;
+ }
+
+ ptemp = (void *)pinskinintervals;
+
+ for (i=0 ; i<numskins ; i++)
+ {
+ ptemp = Mod_LoadAliasSkin (ptemp,
+ &paliasskingroup->skindescs[i].skin, skinsize, pheader);
+ }
+
+ return ptemp;
+}
+
+
+/*
+=================
+Mod_LoadAliasModel
+=================
+*/
+void Mod_LoadAliasModel (model_t *mod, void *buffer)
+{
+ int i;
+ mdl_t *pmodel, *pinmodel;
+ stvert_t *pstverts, *pinstverts;
+ aliashdr_t *pheader;
+ mtriangle_t *ptri;
+ dtriangle_t *pintriangles;
+ int version, numframes, numskins;
+ int size;
+ daliasframetype_t *pframetype;
+ daliasskintype_t *pskintype;
+ maliasskindesc_t *pskindesc;
+ int skinsize;
+ int start, end, total;
+
+ start = Hunk_LowMark ();
+
+ pinmodel = (mdl_t *)buffer;
+
+ version = LittleLong (pinmodel->version);
+ if (version != ALIAS_VERSION)
+ Sys_Error ("%s has wrong version number (%i should be %i)",
+ mod->name, version, ALIAS_VERSION);
+
+//
+// allocate space for a working header, plus all the data except the frames,
+// skin and group info
+//
+ size = sizeof (aliashdr_t) + (LittleLong (pinmodel->numframes) - 1) *
+ sizeof (pheader->frames[0]) +
+ sizeof (mdl_t) +
+ LittleLong (pinmodel->numverts) * sizeof (stvert_t) +
+ LittleLong (pinmodel->numtris) * sizeof (mtriangle_t);
+
+ pheader = Hunk_AllocName (size, loadname);
+ pmodel = (mdl_t *) ((byte *)&pheader[1] +
+ (LittleLong (pinmodel->numframes) - 1) *
+ sizeof (pheader->frames[0]));
+
+// mod->cache.data = pheader;
+ mod->flags = LittleLong (pinmodel->flags);
+
+//
+// endian-adjust and copy the data, starting with the alias model header
+//
+ pmodel->boundingradius = LittleFloat (pinmodel->boundingradius);
+ pmodel->numskins = LittleLong (pinmodel->numskins);
+ pmodel->skinwidth = LittleLong (pinmodel->skinwidth);
+ pmodel->skinheight = LittleLong (pinmodel->skinheight);
+
+ if (pmodel->skinheight > MAX_LBM_HEIGHT)
+ Sys_Error ("model %s has a skin taller than %d", mod->name,
+ MAX_LBM_HEIGHT);
+
+ pmodel->numverts = LittleLong (pinmodel->numverts);
+
+ if (pmodel->numverts <= 0)
+ Sys_Error ("model %s has no vertices", mod->name);
+
+ if (pmodel->numverts > MAXALIASVERTS)
+ Sys_Error ("model %s has too many vertices", mod->name);
+
+ pmodel->numtris = LittleLong (pinmodel->numtris);
+
+ if (pmodel->numtris <= 0)
+ Sys_Error ("model %s has no triangles", mod->name);
+
+ pmodel->numframes = LittleLong (pinmodel->numframes);
+ pmodel->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO;
+ mod->synctype = LittleLong (pinmodel->synctype);
+ mod->numframes = pmodel->numframes;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ pmodel->scale[i] = LittleFloat (pinmodel->scale[i]);
+ pmodel->scale_origin[i] = LittleFloat (pinmodel->scale_origin[i]);
+ pmodel->eyeposition[i] = LittleFloat (pinmodel->eyeposition[i]);
+ }
+
+ numskins = pmodel->numskins;
+ numframes = pmodel->numframes;
+
+ if (pmodel->skinwidth & 0x03)
+ Sys_Error ("Mod_LoadAliasModel: skinwidth not multiple of 4");
+
+ pheader->model = (byte *)pmodel - (byte *)pheader;
+
+//
+// load the skins
+//
+ skinsize = pmodel->skinheight * pmodel->skinwidth;
+
+ if (numskins < 1)
+ Sys_Error ("Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins);
+
+ pskintype = (daliasskintype_t *)&pinmodel[1];
+
+ pskindesc = Hunk_AllocName (numskins * sizeof (maliasskindesc_t),
+ loadname);
+
+ pheader->skindesc = (byte *)pskindesc - (byte *)pheader;
+
+ for (i=0 ; i<numskins ; i++)
+ {
+ aliasskintype_t skintype;
+
+ skintype = LittleLong (pskintype->type);
+ pskindesc[i].type = skintype;
+
+ if (skintype == ALIAS_SKIN_SINGLE)
+ {
+ pskintype = (daliasskintype_t *)
+ Mod_LoadAliasSkin (pskintype + 1,
+ &pskindesc[i].skin,
+ skinsize, pheader);
+ }
+ else
+ {
+ pskintype = (daliasskintype_t *)
+ Mod_LoadAliasSkinGroup (pskintype + 1,
+ &pskindesc[i].skin,
+ skinsize, pheader);
+ }
+ }
+
+//
+// set base s and t vertices
+//
+ pstverts = (stvert_t *)&pmodel[1];
+ pinstverts = (stvert_t *)pskintype;
+
+ pheader->stverts = (byte *)pstverts - (byte *)pheader;
+
+ for (i=0 ; i<pmodel->numverts ; i++)
+ {
+ pstverts[i].onseam = LittleLong (pinstverts[i].onseam);
+ // put s and t in 16.16 format
+ pstverts[i].s = LittleLong (pinstverts[i].s) << 16;
+ pstverts[i].t = LittleLong (pinstverts[i].t) << 16;
+ }
+
+//
+// set up the triangles
+//
+ ptri = (mtriangle_t *)&pstverts[pmodel->numverts];
+ pintriangles = (dtriangle_t *)&pinstverts[pmodel->numverts];
+
+ pheader->triangles = (byte *)ptri - (byte *)pheader;
+
+ for (i=0 ; i<pmodel->numtris ; i++)
+ {
+ int j;
+
+ ptri[i].facesfront = LittleLong (pintriangles[i].facesfront);
+
+ for (j=0 ; j<3 ; j++)
+ {
+ ptri[i].vertindex[j] =
+ LittleLong (pintriangles[i].vertindex[j]);
+ }
+ }
+
+//
+// load the frames
+//
+ if (numframes < 1)
+ Sys_Error ("Mod_LoadAliasModel: Invalid # of frames: %d\n", numframes);
+
+ pframetype = (daliasframetype_t *)&pintriangles[pmodel->numtris];
+
+ for (i=0 ; i<numframes ; i++)
+ {
+ aliasframetype_t frametype;
+
+ frametype = LittleLong (pframetype->type);
+ pheader->frames[i].type = frametype;
+
+ if (frametype == ALIAS_SINGLE)
+ {
+ pframetype = (daliasframetype_t *)
+ Mod_LoadAliasFrame (pframetype + 1,
+ &pheader->frames[i].frame,
+ pmodel->numverts,
+ &pheader->frames[i].bboxmin,
+ &pheader->frames[i].bboxmax,
+ pheader, pheader->frames[i].name);
+ }
+ else
+ {
+ pframetype = (daliasframetype_t *)
+ Mod_LoadAliasGroup (pframetype + 1,
+ &pheader->frames[i].frame,
+ pmodel->numverts,
+ &pheader->frames[i].bboxmin,
+ &pheader->frames[i].bboxmax,
+ pheader, pheader->frames[i].name);
+ }
+ }
+
+ mod->type = mod_alias;
+
+// FIXME: do this right
+ mod->mins[0] = mod->mins[1] = mod->mins[2] = -16;
+ mod->maxs[0] = mod->maxs[1] = mod->maxs[2] = 16;
+
+//
+// move the complete, relocatable alias model to the cache
+//
+ end = Hunk_LowMark ();
+ total = end - start;
+
+ Cache_Alloc (&mod->cache, total, loadname);
+ if (!mod->cache.data)
+ return;
+ memcpy (mod->cache.data, pheader, total);
+
+ Hunk_FreeToLowMark (start);
+}
+
+//=============================================================================
+
+/*
+=================
+Mod_LoadSpriteFrame
+=================
+*/
+void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe)
+{
+ dspriteframe_t *pinframe;
+ mspriteframe_t *pspriteframe;
+ int i, width, height, size, origin[2];
+ unsigned short *ppixout;
+ byte *ppixin;
+
+ pinframe = (dspriteframe_t *)pin;
+
+ width = LittleLong (pinframe->width);
+ height = LittleLong (pinframe->height);
+ size = width * height;
+
+ pspriteframe = Hunk_AllocName (sizeof (mspriteframe_t) + size*r_pixbytes,
+ loadname);
+
+ Q_memset (pspriteframe, 0, sizeof (mspriteframe_t) + size);
+ *ppframe = pspriteframe;
+
+ pspriteframe->width = width;
+ pspriteframe->height = height;
+ origin[0] = LittleLong (pinframe->origin[0]);
+ origin[1] = LittleLong (pinframe->origin[1]);
+
+ pspriteframe->up = origin[1];
+ pspriteframe->down = origin[1] - height;
+ pspriteframe->left = origin[0];
+ pspriteframe->right = width + origin[0];
+
+ if (r_pixbytes == 1)
+ {
+ Q_memcpy (&pspriteframe->pixels[0], (byte *)(pinframe + 1), size);
+ }
+ else if (r_pixbytes == 2)
+ {
+ ppixin = (byte *)(pinframe + 1);
+ ppixout = (unsigned short *)&pspriteframe->pixels[0];
+
+ for (i=0 ; i<size ; i++)
+ ppixout[i] = d_8to16table[ppixin[i]];
+ }
+ else
+ {
+ Sys_Error ("Mod_LoadSpriteFrame: driver set invalid r_pixbytes: %d\n",
+ r_pixbytes);
+ }
+
+ return (void *)((byte *)pinframe + sizeof (dspriteframe_t) + size);
+}
+
+
+/*
+=================
+Mod_LoadSpriteGroup
+=================
+*/
+void * Mod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe)
+{
+ dspritegroup_t *pingroup;
+ mspritegroup_t *pspritegroup;
+ int i, numframes;
+ dspriteinterval_t *pin_intervals;
+ float *poutintervals;
+ void *ptemp;
+
+ pingroup = (dspritegroup_t *)pin;
+
+ numframes = LittleLong (pingroup->numframes);
+
+ pspritegroup = Hunk_AllocName (sizeof (mspritegroup_t) +
+ (numframes - 1) * sizeof (pspritegroup->frames[0]), loadname);
+
+ pspritegroup->numframes = numframes;
+
+ *ppframe = (mspriteframe_t *)pspritegroup;
+
+ pin_intervals = (dspriteinterval_t *)(pingroup + 1);
+
+ poutintervals = Hunk_AllocName (numframes * sizeof (float), loadname);
+
+ pspritegroup->intervals = poutintervals;
+
+ for (i=0 ; i<numframes ; i++)
+ {
+ *poutintervals = LittleFloat (pin_intervals->interval);
+ if (*poutintervals <= 0.0)
+ Sys_Error ("Mod_LoadSpriteGroup: interval<=0");
+
+ poutintervals++;
+ pin_intervals++;
+ }
+
+ ptemp = (void *)pin_intervals;
+
+ for (i=0 ; i<numframes ; i++)
+ {
+ ptemp = Mod_LoadSpriteFrame (ptemp, &pspritegroup->frames[i]);
+ }
+
+ return ptemp;
+}
+
+
+/*
+=================
+Mod_LoadSpriteModel
+=================
+*/
+void Mod_LoadSpriteModel (model_t *mod, void *buffer)
+{
+ int i;
+ int version;
+ dsprite_t *pin;
+ msprite_t *psprite;
+ int numframes;
+ int size;
+ dspriteframetype_t *pframetype;
+
+ pin = (dsprite_t *)buffer;
+
+ version = LittleLong (pin->version);
+ if (version != SPRITE_VERSION)
+ Sys_Error ("%s has wrong version number "
+ "(%i should be %i)", mod->name, version, SPRITE_VERSION);
+
+ numframes = LittleLong (pin->numframes);
+
+ size = sizeof (msprite_t) + (numframes - 1) * sizeof (psprite->frames);
+
+ psprite = Hunk_AllocName (size, loadname);
+
+ mod->cache.data = psprite;
+
+ psprite->type = LittleLong (pin->type);
+ psprite->maxwidth = LittleLong (pin->width);
+ psprite->maxheight = LittleLong (pin->height);
+ psprite->beamlength = LittleFloat (pin->beamlength);
+ mod->synctype = LittleLong (pin->synctype);
+ psprite->numframes = numframes;
+
+ mod->mins[0] = mod->mins[1] = -psprite->maxwidth/2;
+ mod->maxs[0] = mod->maxs[1] = psprite->maxwidth/2;
+ mod->mins[2] = -psprite->maxheight/2;
+ mod->maxs[2] = psprite->maxheight/2;
+
+//
+// load the frames
+//
+ if (numframes < 1)
+ Sys_Error ("Mod_LoadSpriteModel: Invalid # of frames: %d\n", numframes);
+
+ mod->numframes = numframes;
+ mod->flags = 0;
+
+ pframetype = (dspriteframetype_t *)(pin + 1);
+
+ for (i=0 ; i<numframes ; i++)
+ {
+ spriteframetype_t frametype;
+
+ frametype = LittleLong (pframetype->type);
+ psprite->frames[i].type = frametype;
+
+ if (frametype == SPR_SINGLE)
+ {
+ pframetype = (dspriteframetype_t *)
+ Mod_LoadSpriteFrame (pframetype + 1,
+ &psprite->frames[i].frameptr);
+ }
+ else
+ {
+ pframetype = (dspriteframetype_t *)
+ Mod_LoadSpriteGroup (pframetype + 1,
+ &psprite->frames[i].frameptr);
+ }
+ }
+
+ mod->type = mod_sprite;
+}
+
+//=============================================================================
+
+/*
+================
+Mod_Print
+================
+*/
+void Mod_Print (void)
+{
+ int i;
+ model_t *mod;
+
+ Con_Printf ("Cached models:\n");
+ for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++)
+ {
+ Con_Printf ("%8p : %s",mod->cache.data, mod->name);
+ if (mod->needload & NL_UNREFERENCED)
+ Con_Printf (" (!R)");
+ if (mod->needload & NL_NEEDS_LOADED)
+ Con_Printf (" (!P)");
+ Con_Printf ("\n");
+ }
+}
+
+