Initial commit.

This commit is contained in:
Jim Gray
2013-04-04 14:32:05 -07:00
parent ba5c81da32
commit d71d53e8ec
2180 changed files with 1393544 additions and 1 deletions

67
code/qcommon/MiniHeap.h Normal file
View File

@@ -0,0 +1,67 @@
#if !defined(MINIHEAP_H_INC)
#define MINIHEAP_H_INC
class CMiniHeap
{
char *mHeap;
char *mCurrentHeap;
int mSize;
#if _DEBUG
int mMaxAlloc;
#endif
public:
// reset the heap back to the start
void ResetHeap()
{
#if _DEBUG
if ((int)mCurrentHeap - (int)mHeap>mMaxAlloc)
{
mMaxAlloc=(int)mCurrentHeap - (int)mHeap;
}
#endif
mCurrentHeap = mHeap;
}
// initialise the heap
CMiniHeap(int size)
{
mHeap = (char *)Z_Malloc(size, TAG_GHOUL2, qtrue);
mSize = size;
#if _DEBUG
mMaxAlloc=0;
#endif
if (mHeap)
{
ResetHeap();
}
}
// free up the heap
~CMiniHeap()
{
if (mHeap)
{
// the quake heap will be long gone, no need to free it Z_Free(mHeap);
}
}
// give me some space from the heap please
char *MiniHeapAlloc(int size)
{
if (size < (mSize - ((int)mCurrentHeap - (int)mHeap)))
{
char *tempAddress = mCurrentHeap;
mCurrentHeap += size;
return tempAddress;
}
return NULL;
}
};
extern CMiniHeap *G2VertSpaceServer;
#endif //MINIHEAP_H_INC

162
code/qcommon/chash.h Normal file
View File

@@ -0,0 +1,162 @@
// Notes
// Make sure extension is stripped if it needs to be
// Template class must have
// 1. A GetName() accessor - a null terminated string case insensitive
// 2. A Destroy() function - normally "delete this"
// 3. SetNext(T *) and T *GetNext() functions
#define HASH_SIZE 1024
template <class T, int TSize = HASH_SIZE, int (*TCompare)(const char *, const char *) = &strcmp>
class CHash
{
private:
T *mHashTable[TSize];
T *mNext;
int mCount;
T *mPrevious; // Internal work variable
long mHash; // Internal work variable
// Creates the hash value and sets the mHash member
void CreateHash(const char *key)
{
int i = 0;
char letter;
mHash = 0;
letter = *key++;
while (letter)
{
mHash += (long)(letter) * (i + 119);
i++;
letter = *key++;
}
mHash &= TSize - 1;
}
public:
// Constructor
CHash(void)
{
memset(mHashTable, NULL, sizeof(mHashTable));
mNext = NULL;
mCount = 0;
mPrevious = NULL;
mHash = 0;
}
// Destructor
~CHash(void)
{
#ifdef _DEBUG
// Com_OPrintf("Shutting down %s hash table .....", typeid(T).name());
#endif
clear();
#ifdef _DEBUG
// Com_OPrintf(" done\n");
#endif
}
// Returns the total number of entries in the hash table
int count(void) const { return(mCount); }
// Inserts an item into the hash table
void insert(T *item)
{
CreateHash(item->GetName());
item->SetNext(mHashTable[mHash]);
mHashTable[mHash] = item;
mCount++;
}
// Finds an item in the hash table (sets the mPrevious member)
T *find(const char *key)
{
CreateHash(key);
T *item = mHashTable[mHash];
mPrevious = NULL;
while(item)
{
mNext = item->GetNext();
if(!TCompare(item->GetName(), key))
{
return(item);
}
mPrevious = item;
item = mNext;
}
return(NULL);
}
// Remove item from the hash table referenced by key
bool remove(const char *key)
{
T *item = find(key);
if(item)
{
T *next = item->GetNext();
if(mPrevious)
{
mPrevious->SetNext(next);
}
else
{
mHashTable[mHash] = next;
}
item->Destroy();
mCount--;
return(true);
}
return(false);
}
// Remove item from hash referenced by item
bool remove(T *item)
{
return(remove(item->GetName()));
}
// Returns the first valid entry
T *head(void)
{
mHash = -1;
mNext = NULL;
return(next());
}
// Returns the next entry in the hash table
T *next(void)
{
T *item;
assert(mHash < TSize);
if(mNext)
{
item = mNext;
mNext = item->GetNext();
return(item);
}
mHash++;
for( ; mHash < TSize; mHash++)
{
item = mHashTable[mHash];
if(item)
{
mNext = item->GetNext();
return(item);
}
}
return(NULL);
}
// Destroy all entries in the hash table
void clear(void)
{
T *item = head();
while(item)
{
remove(item);
item = next();
}
}
// Override the [] operator
T *operator[](const char *key) { return(find(key)); }
};
// end

1488
code/qcommon/cm_draw.cpp Normal file

File diff suppressed because it is too large Load Diff

245
code/qcommon/cm_draw.h Normal file
View File

@@ -0,0 +1,245 @@
///////////////////////////////////////////////////////////////////////////////
// CDraw32 Class Interface
//
// Basic drawing routines for 32-bit per pixel buffer
///////////////////////////////////////////////////////////////////////////////
#if !defined(CM_DRAW_H_INC)
#define CM_DRAW_H_INC
// calc offset into image array for a pixel at (x,y)
#define PIXPOS(x,y,stride) (((y)*(stride))+(x))
#ifndef MIN
// handy macros
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define ABS(x) ((x)<0 ? -(x):(x))
#define SIGN(x) (((x) < 0) ? -1 : (((x) > 0) ? 1 : 0))
#endif
#ifndef CLAMP
#define SWAP(a,b) { a^=b; b^=a; a^=b; }
#define SQR(a) ((a)*(a))
#define CLAMP(v,l,h) ((v)<(l) ? (l) : (v) > (h) ? (h) : (v))
#define LERP(t, a, b) (((b)-(a))*(t) + (a))
// round a to nearest integer towards 0
#define FLOOR(a) ((a)>0 ? (int)(a) : -(int)(-a))
// round a to nearest integer away from 0
#define CEILING(a) \
((a)==(int)(a) ? (a) : (a)>0 ? 1+(int)(a) : -(1+(int)(-a)))
#include <stdlib.h>
#endif
class CPixel32
{
public:
byte r;
byte g;
byte b;
byte a;
CPixel32(byte R = 0, byte G = 0, byte B = 0, byte A = 255) : r(R), g(G), b(B), a(A) {}
CPixel32(long l) {r = (l >> 24) & 0xff; g = (l >> 16) & 0xff; b = (l >> 8) & 0xff; a = l & 0xff;};
~CPixel32()
{}
};
#define PIX32_SIZE sizeof(CPixel32)
// standard image operator macros
#define IMAGE_SIZE(width,height) ((width)*(height)*(PIX32_SIZE))
inline CPixel32 AVE_PIX (CPixel32 x, CPixel32 y)
{ CPixel32 t; t.r = (byte)(((int)x.r + (int)y.r)>>1);
t.g = (byte)(((int)x.g + (int)y.g)>>1);
t.b = (byte)(((int)x.b + (int)y.b)>>1);
t.a = (byte)(((int)x.a + (int)y.a)>>1); return t;}
inline CPixel32 ALPHA_PIX (CPixel32 x, CPixel32 y, long alpha, long inv_alpha)
{ CPixel32 t; t.r = (byte)((x.r*alpha + y.r*inv_alpha)>>8);
t.g = (byte)((x.g*alpha + y.g*inv_alpha)>>8);
t.b = (byte)((x.b*alpha + y.b*inv_alpha)>>8);
// t.a = (byte)((x.a*alpha + y.a*inv_alpha)>>8); return t;}
t.a = y.a; return t;}
inline CPixel32 LIGHT_PIX (CPixel32 p, long light)
{ CPixel32 t;
t.r = (byte)CLAMP(((p.r * light)>>10) + p.r, 0, 255);
t.g = (byte)CLAMP(((p.g * light)>>10) + p.g, 0, 255);
t.b = (byte)CLAMP(((p.b * light)>>10) + p.b, 0, 255);
t.a = p.a; return t;}
// Colors are 32-bit RGBA
// draw class
class CDraw32
{
public: // static drawing context - static so we set only ONCE for many draw calls
static CPixel32* buffer; // pointer to pixel buffer (one active)
static long buf_width; // size of buffer
static long buf_height; // size of buffer
static long stride; // stride of buffer in pixels
static long clip_min_x; // clip bounds
static long clip_min_y; // clip bounds
static long clip_max_x; // clip bounds
static long clip_max_y; // clip bounds
static long* row_off; // Table for quick Y calculations
private:
void BlitClip(long& dstX, long& dstY,
long& width, long& height,
long& srcX, long& srcY);
protected:
public:
CDraw32(); // constructor
~CDraw32(); // destructor
// set the rect to clip drawing functions to
static void SetClip(long min_x, long min_y,long max_x, long max_y)
{clip_min_x = MAX(min_x,0); clip_max_x = MIN(max_x,buf_width-1);
clip_min_y = MAX(min_y,0); clip_max_y = MIN(max_y,buf_height-1);}
static void GetClip(long& min_x, long& min_y,long& max_x, long& max_y)
{min_x = clip_min_x; min_y = clip_min_y;
max_x = clip_max_x; max_y = clip_max_y; }
// set the buffer to use for drawing off-screen
static void SetBuffer(CPixel32* buf) {buffer = buf;};
// set the dimensions of the off-screen buffer
static bool SetBufferSize(long width,long height,long stride_len);
// call this to free the table for quick y calcs before the program ends
static void CleanUp(void)
{if (row_off) delete [] row_off; row_off=NULL; buf_width=0; buf_height=0;}
// set a pixel at (x,y) to color (no clipping)
void PutPixNC(long x, long y, CPixel32 color)
{buffer[row_off[y] + x] = color;}
// set a pixel at (x,y) to color
void PutPix(long x, long y, CPixel32 color)
{ // clipping check
if (x < clip_min_x || x > clip_max_x ||
y < clip_min_y || y > clip_max_y)
return;
PutPixNC(x,y,color);
}
// get the color of a pixel at (x,y)
CPixel32 GetPix(long x, long y)
{return buffer[row_off[y] + x];}
// set a pixel at (x,y) with 50% translucency (no clip)
void PutPixAveNC(long x, long y, CPixel32 color)
{ PutPixNC(x,y,AVE_PIX(GetPix(x, y), color)); }
// set a pixel at (x,y) with 50% translucency
void PutPixAve(long x, long y, CPixel32 color)
{ // clipping check
if (x < clip_min_x || x > clip_max_x ||
y < clip_min_y || y > clip_max_y)
return;
PutPixNC(x,y,AVE_PIX(GetPix(x, y), color));
}
// set a pixel at (x,y) with translucency level (no clip)
void PutPixAlphaNC(long x, long y, CPixel32 color)
{ PutPixNC(x,y,ALPHA_PIX(color, GetPix(x, y), color.a, 256-color.a));}
// set a pixel at (x,y) with translucency level
void PutPixAlpha(long x, long y, CPixel32 color)
{ // clipping check
if (x < clip_min_x || x > clip_max_x ||
y < clip_min_y || y > clip_max_y)
return;
PutPixNC(x,y,ALPHA_PIX(color, GetPix(x, y), color.a, 256-color.a));}
// clear screen buffer to color from start to end line
void ClearLines(CPixel32 color,long start,long end);
// clear screen buffer to color provided
void ClearBuffer(CPixel32 color)
{ClearLines(color,0,buf_height-1);};
// fill buffer alpha from start to end line
void SetAlphaLines(byte alpha,long start,long end);
// clear screen buffer to color provided
void SetAlphaBuffer(byte alpha)
{SetAlphaLines(alpha,0,buf_height-1);};
// clip a line segment to the clip rect
bool ClipLine(long& x1, long& y1, long& x2, long& y2);
// draw a solid colored line, no clipping
void DrawLineNC(long x1, long y1, long x2, long y2, CPixel32 color);
// draw a solid color line
void DrawLine(long x1, long y1, long x2, long y2, CPixel32 color)
{ if (ClipLine(x1,y1,x2,y2)) DrawLineNC(x1,y1,x2,y2,color);}
void DrawLineAveNC(long x1, long y1, long x2, long y2, CPixel32 color);
// draw a translucent solid color line
void DrawLineAve(long x1, long y1, long x2, long y2, CPixel32 color)
{ if (ClipLine(x1,y1,x2,y2)) DrawLineAveNC(x1,y1,x2,y2,color);}
// draw an anti-aliased line, no clipping
void DrawLineAANC(long x0, long y0, long x1, long y1, CPixel32 color);
// draw an anti-aliased line
void DrawLineAA(long x1, long y1, long x2, long y2, CPixel32 color)
{ if (ClipLine(x1,y1,x2,y2)) DrawLineAANC(x1,y1,x2,y2,color);}
// draw a filled rectangle, no clipping
void DrawRectNC(long ulx, long uly, long width, long height,CPixel32 color);
// draw a filled rectangle
void DrawRect(long ulx, long uly, long width, long height, CPixel32 color);
// draw a filled rectangle
void DrawRectAve(long ulx, long uly, long width, long height,CPixel32 color);
// draw a box (unfilled rectangle) no clip
void DrawBoxNC(long ulx, long uly, long width, long height, CPixel32 color);
// draw a box (unfilled rectangle)
void DrawBox(long ulx, long uly, long width, long height, CPixel32 color);
// draw a box (unfilled rectangle)
void DrawBoxAve(long ulx, long uly, long width, long height, CPixel32 color);
// draw a circle with fill and edge colors
void DrawCircle(long xc, long yc, long r, CPixel32 edge, CPixel32 fill);
// draw a circle with fill and edge colors averaged with dest
void DrawCircleAve(long xc, long yc, long r, CPixel32 edge, CPixel32 fill);
// draw a polygon (complex) with fill and edge colors
void DrawPolygon(long nvert, POINT *point, CPixel32 edge, CPixel32 fill);
// simple blit function
void BlitNC(long dstX, long dstY, long dstWidth, long dstHeight,
CPixel32* srcImage, long srcX, long srcY, long srcStride);
void Blit(long dstX, long dstY, long dstWidth, long dstHeight,
CPixel32* srcImage, long srcX, long srcY, long srcStride);
// blit image times color
void BlitColor(long dstX, long dstY, long dstWidth, long dstHeight,
CPixel32* srcImage, long srcX, long srcY, long srcStride, CPixel32 color);
void Emboss(long dstX, long dstY, long width, long height,
CPixel32* clrImage, long clrX, long clrY, long clrStride);
};
///////////////////////////////////////////////////////////////////////////////
#endif

271
code/qcommon/cm_landscape.h Normal file
View File

@@ -0,0 +1,271 @@
#if !defined(CM_LANDSCAPE_H_INC)
#define CM_LANDSCAPE_H_INC
#pragma warning (push, 3) //go back down to 3 for the stl include
#include <list>
#pragma warning (pop)
using namespace std;
// These are the root classes using data shared in both the server and the renderer.
// This common data is also available to physics
#define HEIGHT_RESOLUTION 256
// Trying to make a guess at the optimal step through the patches
// This is the average of 1 side and the diagonal presuming a square patch
#define TERRAIN_STEP_MAGIC (1.0f / 1.2071f)
#define MIN_TERXELS 2
#define MAX_TERXELS 8
// Defined as 1 << (sqrt(MAX_TERXELS) + 1)
#define MAX_VARIANCE_SIZE 16
// Maximum number of instances to pick from an instance file
#define MAX_INSTANCE_TYPES 16
// Types of areas
typedef enum
{
AT_NONE,
AT_FLAT,
AT_BSP,
AT_NPC,
AT_GROUP,
AT_RIVER,
AT_OBJECTIVE,
AT_PLAYER,
} areaType_t;
class CArea
{
private:
vec3_t mPosition;
float mRadius;
float mAngle;
float mAngleDiff;
int mType;
int mVillageID;
public:
CArea(void) {}
~CArea(void) {}
void Init(vec3_t pos, float radius, float angle = 0.0f, int type = AT_NONE, float angleDiff = 0.0f, int villageID = 0)
{
VectorCopy(pos, mPosition);
mRadius = radius;
mAngle = angle;
mAngleDiff = angleDiff;
mType = type;
mVillageID = villageID;
}
float GetRadius(void) const { return(mRadius); }
float GetAngle(void) const { return(mAngle); }
float GetAngleDiff(void) const { return(mAngleDiff); }
vec3_t &GetPosition(void) { return(mPosition); }
int GetType(void) const { return(mType); }
int GetVillageID(void) const { return(mVillageID); }
};
typedef list<CArea*> areaList_t;
typedef list<CArea*>::iterator areaIter_t;
class CCMHeightDetails
{
private:
int mContents;
int mSurfaceFlags;
public:
CCMHeightDetails(void) {}
~CCMHeightDetails(void) {}
// Accessors
const int GetSurfaceFlags(void) const { return(mSurfaceFlags); }
const int GetContents(void) const { return(mContents); }
void SetFlags(const int con, const int sf) { mContents = con; mSurfaceFlags = sf; }
};
class CCMPatch
{
protected:
class CCMLandScape *owner; // Owning landscape
int mHx, mHy; // Terxel coords of patch
byte *mHeightMap; // Pointer to height map to use
byte mCornerHeights[4]; // Heights at the corners of the patch
vec3_t mWorldCoords; // World coordinate offset of this patch.
vec3pair_t mBounds; // mins and maxs of the patch for culling
int mNumBrushes; // number of brushes to collide with in the patch
struct cbrush_s *mPatchBrushData; // List of brushes that make up the patch
int mSurfaceFlags; // surfaceflag of the heightshader
int mContentFlags; // contents of the heightshader
public:
// Constructors
CCMPatch(void) {}
~CCMPatch(void);
// Accessors
const vec3_t &GetWorld(void) const { return(mWorldCoords); }
const vec3_t &GetMins(void) const { return(mBounds[0]); }
const vec3_t &GetMaxs(void) const { return(mBounds[1]); }
const vec3pair_t &GetBounds(void) const { return(mBounds); }
const int GetHeightMapX(void) const { return(mHx); }
const int GetHeightMapY(void) const { return(mHy); }
const int GetHeight(int corner) const { return(mCornerHeights[corner]); }
const int GetNumBrushes(void) const { return(mNumBrushes); }
struct cbrush_s *GetCollisionData(void) const { return(mPatchBrushData); }
void SetSurfaceFlags(const int in) { mSurfaceFlags = in; }
const int GetSurfaceFlags(void) const { return(mSurfaceFlags); }
void SetContents(const int in) { mContentFlags = in; }
const int GetContents(void) const { return(mContentFlags); }
// Prototypes
void Init(CCMLandScape *ls, int heightX, int heightY, vec3_t world, byte *hMap, byte *patchBrushData);
void InitPlane(struct cbrushside_s *side, cplane_t *plane, vec3_t p0, vec3_t p1, vec3_t p2);
void CreatePatchPlaneData(void);
void* GetAdjacentBrushX ( int x, int y );
void* GetAdjacentBrushY ( int x, int y );
};
class CRandomTerrain;
class CCMLandScape
{
private:
int mRefCount; // Number of times this class is referenced
thandle_t mTerrainHandle;
byte *mHeightMap; // Pointer to byte array of height samples
byte *mFlattenMap; // Pointer to byte array of flatten samples
int mWidth, mHeight; // Width and height of heightMap excluding the 1 pixel edge
int mTerxels; // Number of terxels per patch side
vec3_t mTerxelSize; // Vector to scale heightMap samples to real world coords
vec3pair_t mBounds; // Real world bounds of terrain brush
vec3_t mSize; // Size of terrain brush in real world coords excluding 1 patch edge
vec3_t mPatchSize; // Size of each patch in the x and y directions only
float mPatchScalarSize; // Horizontal size of the patch
int mBlockWidth, mBlockHeight; // Width and height of heightfield on blocks
CCMPatch *mPatches;
byte *mPatchBrushData; // Base memory from which the patch brush data is taken
bool mHasPhysics; // Set to true unless disabled
CRandomTerrain *mRandomTerrain;
int mBaseWaterHeight; // Base water height in terxels
float mWaterHeight; // Real world height of the water
int mWaterContents; // Contents of the water shader
int mWaterSurfaceFlags; // Surface flags of the water shader
unsigned long holdrand;
list<CArea *> mAreas; // List of flattened areas on this landscape
list<CArea *>::iterator mAreasIt;
CCMHeightDetails mHeightDetails[HEIGHT_RESOLUTION]; // Surfaceflags per height
vec3_t *mCoords; // Temp storage for real world coords
public:
CCMLandScape(const char *configstring, bool server);
~CCMLandScape(void);
CCMPatch *GetPatch(int x, int y);
// Prototypes
void PatchCollide(struct traceWork_s *tw, trace_t &trace, const vec3_t start, const vec3_t end, int checkcount);
void TerrainPatchIterate(void (*IterateFunc)( CCMPatch *, void * ), void *userdata) const;
float GetWorldHeight(vec3_t origin, const vec3pair_t bounds, bool aboveGround) const;
float WaterCollide(const vec3_t begin, const vec3_t end, float fraction) const;
void UpdatePatches(void);
void GetTerxelLocalCoords ( int x, int y, vec3_t coords[8] );
void LoadTerrainDef(const char *td);
void SetShaders(int height, class CCMShader *shader);
void FlattenArea(CArea *area, int height, bool save, bool forceHeight, bool smooth);
void CarveLine ( vec3_t start, vec3_t end, int depth, int width );
void CarveBezierCurve ( int numCtlPoints, vec3_t* ctlPoints, int steps, int depth, int size );
void SaveArea(CArea *area);
float FractionBelowLevel(CArea *area, int height);
bool AreaCollision(CArea *area, int *areaTypes, int areaTypeCount);
CArea *GetFirstArea(void);
CArea *GetFirstObjectiveArea(void);
CArea *GetPlayerArea(void);
CArea *GetNextArea(void);
CArea *GetNextObjectiveArea(void);
// Accessors
const int GetRefCount(void) const { return(mRefCount); }
void IncreaseRefCount(void) { mRefCount++; }
void DecreaseRefCount(void) { mRefCount--; }
const vec3pair_t &GetBounds(void) const { return(mBounds); }
const vec3_t &GetMins(void) const { return(mBounds[0]); }
const vec3_t &GetMaxs(void) const { return(mBounds[1]); }
const vec3_t &GetSize(void) const { return(mSize); }
const vec3_t &GetTerxelSize(void) const { return(mTerxelSize); }
const vec3_t &GetPatchSize(void) const { return(mPatchSize); }
const float GetPatchWidth(void) const { return(mPatchSize[0]); }
const float GetPatchHeight(void) const { return(mPatchSize[1]); }
const float GetPatchScalarSize(void) const { return(mPatchScalarSize); }
const int GetTerxels(void) const { return(mTerxels); }
const int GetRealWidth(void) const { return(mWidth + 1); }
const int GetRealHeight(void) const { return(mHeight + 1); }
const int GetRealArea(void) const { return((mWidth + 1) * (mHeight + 1)); }
const int GetWidth(void) const { return(mWidth); }
const int GetHeight(void) const { return(mHeight); }
const int GetArea(void) const { return(mWidth * mHeight); }
const int GetBlockWidth(void) const { return(mBlockWidth); }
const int GetBlockHeight(void) const { return(mBlockHeight); }
const int GetBlockCount(void) const { return(mBlockWidth * mBlockHeight); }
byte *GetHeightMap(void) const { return(mHeightMap); }
byte *GetFlattenMap(void) const { return(mFlattenMap); }
const thandle_t GetTerrainId(void) const { return(mTerrainHandle); }
void SetTerrainId(const thandle_t terrainId) { mTerrainHandle = terrainId; }
const float CalcWorldHeight(int height) const { return((height * mTerxelSize[2]) + mBounds[0][2]); }
const bool GetHasPhysics(void) const { return(mHasPhysics); }
const bool GetIsRandom(void) const { return(mRandomTerrain != 0); }
const int GetSurfaceFlags(int height) const { return(mHeightDetails[height].GetSurfaceFlags()); }
const int GetContentFlags(int height) const { return(mHeightDetails[height].GetContents()); }
void CalcRealCoords(void);
vec3_t *GetCoords(void) const { return(mCoords); }
int GetBaseWaterHeight(void) const { return(mBaseWaterHeight); }
void SetRealWaterHeight(int height) { mWaterHeight = height * mTerxelSize[2]; }
float GetWaterHeight(void) const { return(mWaterHeight); }
int GetWaterContents(void) const { return(mWaterContents); }
int GetWaterSurfaceFlags(void) const { return(mWaterSurfaceFlags); }
CRandomTerrain *GetRandomTerrain(void) { return mRandomTerrain; }
void rand_seed(int seed);
unsigned long get_rand_seed(void) { return holdrand; }
float flrand(float min, float max);
int irand(int min, int max);
};
void CM_TerrainPatchIterate(const class CCMLandScape *ls, void (*IterateFunc)( CCMPatch *, void * ), void *userdata);
class CCMLandScape *CM_InitTerrain(const char *configstring, thandle_t terrainId, bool server);
float CM_GetWorldHeight(const CCMLandScape *landscape, vec3_t origin, const vec3pair_t bounds, bool aboveGround);
void CM_FlattenArea(CCMLandScape *landscape, CArea *area, int height, bool save, bool forceHeight, bool smooth);
void CM_CarveBezierCurve (CCMLandScape *landscape, int numCtls, vec3_t* ctls, int steps, int depth, int size );
void CM_SaveArea(CCMLandScape *landscape, CArea *area);
float CM_FractionBelowLevel(CCMLandScape *landscape, CArea *area, int height);
bool CM_AreaCollision(class CCMLandScape *landscape, class CArea *area, int *areaTypes, int areaTypeCount);
CArea *CM_GetFirstArea(CCMLandScape *landscape);
CArea *CM_GetFirstObjectiveArea(CCMLandScape *landscape);
CArea *CM_GetPlayerArea(class CCMLandScape *common);
CArea *CM_GetNextArea(CCMLandScape *landscape);
CArea *CM_GetNextObjectiveArea(CCMLandScape *landscape);
void CM_CircularIterate(byte *data, int width, int height, int xo, int yo, int insideRadius, int outsideRadius, int *user, void (*callback)(byte *, float, int *));
CRandomTerrain *CreateRandomTerrain(const char *config, CCMLandScape *landscape, byte *heightmap, int width, int height);
void SV_LoadMissionDef(const char *configstring, class CCMLandScape *landscape);
void CL_CreateRandomTerrain(const char *config, class CCMLandScape *landscape, byte *image, int width, int height);
void CL_LoadInstanceDef(const char *configstring, class CCMLandScape *landscape);
void CL_LoadMissionDef(const char *configstring, class CCMLandScape *landscape);
extern cvar_t *com_terrainPhysics;
#endif
// end

1298
code/qcommon/cm_load.cpp Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

321
code/qcommon/cm_local.h Normal file
View File

@@ -0,0 +1,321 @@
#include "../game/q_shared.h"
#include "qcommon.h"
#include "cm_polylib.h"
#include "cm_landscape.h"
#ifdef _XBOX
#include "sparc.h"
#endif
#ifndef CM_LOCAL_H
#define CM_LOCAL_H
#define BOX_MODEL_HANDLE (MAX_SUBMODELS-1)
#ifdef _XBOX
#pragma pack(push, 1)
typedef struct {
short children[2]; // negative numbers are leafs
} cNode_t;
#pragma pack(pop)
#else // _XBOX
typedef struct {
cplane_t *plane;
int children[2]; // negative numbers are leafs
} cNode_t;
#endif // _XBOX
typedef struct {
int cluster;
int area;
int firstLeafBrush;
int numLeafBrushes;
int firstLeafSurface;
int numLeafSurfaces;
} cLeaf_t;
typedef struct cmodel_s {
vec3_t mins, maxs;
cLeaf_t leaf; // submodels don't reference the main tree
} cmodel_t;
#ifdef _XBOX
#pragma pack (push, 1)
typedef struct cbrushside_s {
NotSoShort planeNum;
unsigned char shaderNum;
} cbrushside_t;
#pragma pack(pop)
#else // _XBOX
typedef struct cbrushside_s {
cplane_t *plane;
int shaderNum;
} cbrushside_t;
#endif // _XBOX
typedef struct cbrush_s {
int shaderNum; // the shader that determined the contents
int contents;
vec3_t bounds[2];
cbrushside_t *sides;
unsigned short numsides;
unsigned short checkcount; // to avoid repeated testings
} cbrush_t;
class CCMShader
{
public:
char shader[MAX_QPATH];
class CCMShader *mNext;
int surfaceFlags;
int contentFlags;
const char *GetName(void) const { return(shader); }
class CCMShader *GetNext(void) const { return(mNext); }
void SetNext(class CCMShader *next) { mNext = next; }
void Destroy(void) { }
};
typedef struct {
int checkcount; // to avoid repeated testings
int surfaceFlags;
int contents;
struct patchCollide_s *pc;
} cPatch_t;
typedef struct {
int floodnum;
int floodvalid;
} cArea_t;
#ifdef _XBOX
template <class T>
class SPARC;
typedef struct {
char name[MAX_QPATH];
int numShaders;
CCMShader *shaders;
int numBrushSides;
cbrushside_t *brushsides;
int numPlanes;
cplane_t *planes;
int numNodes;
cNode_t *nodes;
int numLeafs;
cLeaf_t *leafs;
int numLeafBrushes;
int *leafbrushes;
int numLeafSurfaces;
int *leafsurfaces;
int numSubModels;
cmodel_t *cmodels;
int numBrushes;
cbrush_t *brushes;
int numClusters;
int clusterBytes;
SPARC<byte> *visibility;
qboolean vised; // if false, visibility is just a single cluster of ffs
int numEntityChars;
char *entityString;
int numAreas;
cArea_t *areas;
int *areaPortals; // [ numAreas*numAreas ] reference counts
int numSurfaces;
cPatch_t **surfaces; // non-patches will be NULL
int floodvalid;
int checkcount; // incremented on each trace
// CCMLandScape *landScape; // Removing terrain from Xbox
} clipMap_t;
#else // _XBOX
typedef struct {
char name[MAX_QPATH];
int numShaders;
//dshader_t *shaders;
CCMShader *shaders;
int numBrushSides;
cbrushside_t *brushsides;
int numPlanes;
cplane_t *planes;
int numNodes;
cNode_t *nodes;
int numLeafs;
cLeaf_t *leafs;
int numLeafBrushes;
int *leafbrushes;
int numLeafSurfaces;
int *leafsurfaces;
int numSubModels;
cmodel_t *cmodels;
int numBrushes;
cbrush_t *brushes;
int numClusters;
int clusterBytes;
byte *visibility;
qboolean vised; // if false, visibility is just a single cluster of ffs
int numEntityChars;
char *entityString;
int numAreas;
cArea_t *areas;
int *areaPortals; // [ numAreas*numAreas ] reference counts
int numSurfaces;
cPatch_t **surfaces; // non-patches will be NULL
int floodvalid;
int checkcount; // incremented on each trace
CCMLandScape *landScape;
} clipMap_t;
#endif // _XBOX
// keep 1/8 unit away to keep the position valid before network snapping
// and to avoid various numeric issues
#define SURFACE_CLIP_EPSILON (0.125)
extern clipMap_t cmg;
extern int c_pointcontents;
extern int c_traces, c_brush_traces, c_patch_traces;
extern cvar_t *cm_noAreas;
extern cvar_t *cm_noCurves;
extern cvar_t *cm_playerCurveClip;
extern clipMap_t SubBSP[MAX_SUB_BSP];
extern int NumSubBSP;
// cm_test.c
// Used for oriented capsule collision detection
typedef struct
{
qboolean use;
float radius;
float halfheight;
vec3_t offset;
} sphere_t;
typedef struct traceWork_s {
vec3_t start;
vec3_t end;
vec3_t size[2]; // size of the box being swept through the model
vec3_t offsets[8]; // [signbits][x] = either size[0][x] or size[1][x]
float maxOffset; // longest corner length from origin
vec3_t extents; // greatest of abs(size[0]) and abs(size[1])
vec3_t bounds[2]; // enclosing box of start and end surrounding by size
vec3pair_t localBounds; // enclosing box of start and end surrounding by size for a segment
vec3_t modelOrigin;// origin of the model tracing through
int contents; // ored contents of the model tracing through
qboolean isPoint; // optimized case
sphere_t sphere; // sphere for oriendted capsule collision
float baseEnterFrac; // global enter fraction (before processing subsections of the brush)
float baseLeaveFrac; // global leave fraction (before processing subsections of the brush)
float enterFrac; // fraction where the ray enters the brush
float leaveFrac; // fraction where the ray leaves the brush
cbrushside_t *leadside;
cplane_t *clipplane;
bool startout;
bool getout;
trace_t trace; // returned from trace call
// make sure nothing goes under here for Ghoul2 collision purposes
} traceWork_t;
typedef struct leafList_s {
int count;
int maxcount;
qboolean overflowed;
int *list;
vec3_t bounds[2];
int lastLeaf; // for overflows where each leaf can't be stored individually
void (*storeLeafs)( struct leafList_s *ll, int nodenum );
} leafList_t;
int CM_BoxBrushes( const vec3_t mins, const vec3_t maxs, cbrush_t **boxlist, int listsize );
void CM_StoreLeafs( leafList_t *ll, int nodenum );
void CM_StoreBrushes( leafList_t *ll, int nodenum );
void CM_BoxLeafnums_r( leafList_t *ll, int nodenum );
cmodel_t *CM_ClipHandleToModel( clipHandle_t handle, clipMap_t **clipMap = 0 );
void CM_CleanLeafCache(void);
// cm_load.c
void CM_ModelBounds( clipMap_t &cm, clipHandle_t model, vec3_t mins, vec3_t maxs );
// cm_patch.c
struct patchCollide_s *CM_GeneratePatchCollide( int width, int height, vec3_t *points );
void CM_TraceThroughPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc );
qboolean CM_PositionTestInPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc );
void CM_ClearLevelPatches( void );
// cm_shader.cpp
void CM_SetupShaderProperties( void );
void CM_ShutdownShaderProperties(void);
CCMShader *CM_GetShaderInfo( const char *name );
CCMShader *CM_GetShaderInfo( int shaderNum );
void CM_GetModelFormalName ( const char* model, const char* skin, char* name, int size );
//cm_trace.cpp
void CM_CalcExtents(const vec3_t start, const vec3_t end, const traceWork_t *tw, vec3pair_t bounds);
void CM_HandlePatchCollision(struct traceWork_s *tw, trace_t &trace, const vec3_t tStart, const vec3_t tEnd, CCMPatch *patch, int checkcount);
bool CM_GenericBoxCollide(const vec3pair_t abounds, const vec3pair_t bbounds);
//RM_Terrain.cpp
int Round(float value);
//random utils for cm_terrain (and others?)
#define VectorInc(v) ((v)[0] += 1.0f,(v)[1] += 1.0f,(v)[2] +=1.0f)
#define VectorDec(v) ((v)[0] -= 1.0f,(v)[1] -= 1.0f,(v)[2] -=1.0f)
#define VectorInverseScaleVector(a,b,c) ((c)[0]=(a)[0]/(b)[0],(c)[1]=(a)[1]/(b)[1],(c)[2]=(a)[2]/(b)[2])
#define VectorScaleVectorAdd(c,a,b,o) ((o)[0]=(c)[0]+((a)[0]*(b)[0]),(o)[1]=(c)[1]+((a)[1]*(b)[1]),(o)[2]=(c)[2]+((a)[2]*(b)[2]))
#define minimum(x,y) ((x)<(y)?(x):(y))
#define maximum(x,y) ((x)>(y)?(x):(y))
#endif

2930
code/qcommon/cm_patch.cpp Normal file

File diff suppressed because it is too large Load Diff

121
code/qcommon/cm_patch.h Normal file
View File

@@ -0,0 +1,121 @@
//#define CULL_BBOX
/*
This file does not reference any globals, and has these entry points:
void CM_ClearLevelPatches( void );
struct patchCollide_s *CM_GeneratePatchCollide( int width, int height, const vec3_t *points );
void CM_TraceThroughPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc );
qboolean CM_PositionTestInPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc );
void CM_DrawDebugSurface( void (*drawPoly)(int color, int numPoints, flaot *points) );
Issues for collision against curved surfaces:
Surface edges need to be handled differently than surface planes
Plane expansion causes raw surfaces to expand past expanded bounding box
Position test of a volume against a surface is tricky.
Position test of a point against a surface is not well defined, because the surface has no volume.
Tracing leading edge points instead of volumes?
Position test by tracing corner to corner? (8*7 traces -- ouch)
coplanar edges
triangulated patches
degenerate patches
endcaps
degenerate
WARNING: this may misbehave with meshes that have rows or columns that only
degenerate a few triangles. Completely degenerate rows and columns are handled
properly.
*/
#define MAX_FACETS 1024
#define MAX_PATCH_PLANES 2048
typedef struct {
float plane[4];
int signbits; // signx + (signy<<1) + (signz<<2), used as lookup during collision
} patchPlane_t;
#ifdef _XBOX
//Facets are now two structures - a maximum sized version that's used
//temporarily during load time, and smaller version that only allocates
//as much memory as needed. The load version is copied into the small
//version after it's been assembled.
#pragma pack(push, 1)
typedef struct {
int surfacePlane;
int numBorders; // 3 or four + 6 axial bevels + 4 or 3 * 4 edge bevels
short borderPlanes[4+6+16];
unsigned char borderInward[4+6+16];
unsigned char borderNoAdjust[4+6+16];
} facetLoad_t;
typedef struct {
int surfacePlane;
int numBorders; // 3 or four + 6 axial bevels + 4 or 3 * 4 edge bevels
char *data;
short *GetBorderPlanes(void) { return (short*)data; }
char *GetBorderInward(void) { return data + (numBorders * 2); }
char *GetBorderNoAdjust(void)
{ return data + (numBorders * 2) + numBorders; }
const short *GetBorderPlanes(void) const { return (short*)data; }
const char *GetBorderInward(void) const { return data + (numBorders * 2); }
const char *GetBorderNoAdjust(void) const
{ return data + (numBorders * 2) + numBorders; }
} facet_t;
#pragma pack(pop)
#else // _XBOX
typedef struct {
int surfacePlane;
int numBorders; // 3 or four + 6 axial bevels + 4 or 3 * 4 edge bevels
int borderPlanes[4+6+16];
int borderInward[4+6+16];
qboolean borderNoAdjust[4+6+16];
} facet_t;
#endif // _XBOX
typedef struct patchCollide_s {
vec3_t bounds[2];
int numPlanes; // surface planes plus edge planes
patchPlane_t *planes;
int numFacets;
facet_t *facets;
} patchCollide_t;
#define CM_MAX_GRID_SIZE 129
typedef struct {
int width;
int height;
qboolean wrapWidth;
qboolean wrapHeight;
vec3_t points[CM_MAX_GRID_SIZE][CM_MAX_GRID_SIZE]; // [width][height]
} cGrid_t;
#define SUBDIVIDE_DISTANCE 16 //4 // never more than this units away from curve
#define PLANE_TRI_EPSILON 0.1
#define WRAP_POINT_EPSILON 0.1
#ifdef _XBOX
struct patchCollide_s *CM_GeneratePatchCollide( int width, int height, vec3_t *points,
facetLoad_t *facetbuf, int *gridbuf );
#else
struct patchCollide_s *CM_GeneratePatchCollide( int width, int height, vec3_t *points );
#endif // _XBOX

711
code/qcommon/cm_polylib.cpp Normal file
View File

@@ -0,0 +1,711 @@
// this is only used for visualization tools in cm_ debug functions
#include "cm_local.h"
// counters are only bumped when running single threaded,
// because they are an awefull coherence problem
int c_active_windings;
int c_peak_windings;
int c_winding_allocs;
int c_winding_points;
void pw(winding_t *w)
{
int i;
for (i=0 ; i<w->numpoints ; i++)
printf ("(%5.1f, %5.1f, %5.1f)\n",w->p[i][0], w->p[i][1],w->p[i][2]);
}
/*
=============
AllocWinding
=============
*/
winding_t *AllocWinding (int points)
{
winding_t *w;
int s;
c_winding_allocs++;
c_winding_points += points;
c_active_windings++;
if (c_active_windings > c_peak_windings)
c_peak_windings = c_active_windings;
s = sizeof(vec_t)*3*points + sizeof(int);
w = (winding_t *) Z_Malloc (s,TAG_BSP, qtrue);//TAG_WINDING);
// memset (w, 0, s); // qtrue above does this
return w;
}
void FreeWinding (winding_t *w)
{
if (*(unsigned *)w == 0xdeaddead)
Com_Error (ERR_FATAL, "FreeWinding: freed a freed winding");
*(unsigned *)w = 0xdeaddead;
c_active_windings--;
Z_Free (w);
}
/*
============
RemoveColinearPoints
============
*/
int c_removed;
void RemoveColinearPoints (winding_t *w)
{
int i, j, k;
vec3_t v1, v2;
int nump;
vec3_t p[MAX_POINTS_ON_WINDING];
nump = 0;
for (i=0 ; i<w->numpoints ; i++)
{
j = (i+1)%w->numpoints;
k = (i+w->numpoints-1)%w->numpoints;
VectorSubtract (w->p[j], w->p[i], v1);
VectorSubtract (w->p[i], w->p[k], v2);
VectorNormalize2(v1,v1);
VectorNormalize2(v2,v2);
if (DotProduct(v1, v2) < 0.999)
{
VectorCopy (w->p[i], p[nump]);
nump++;
}
}
if (nump == w->numpoints)
return;
c_removed += w->numpoints - nump;
w->numpoints = nump;
memcpy (w->p, p, nump*sizeof(p[0]));
}
/*
============
WindingPlane
============
*/
void WindingPlane (winding_t *w, vec3_t normal, vec_t *dist)
{
vec3_t v1, v2;
VectorSubtract (w->p[1], w->p[0], v1);
VectorSubtract (w->p[2], w->p[0], v2);
CrossProduct (v2, v1, normal);
VectorNormalize2(normal, normal);
*dist = DotProduct (w->p[0], normal);
}
/*
=============
WindingArea
=============
*/
vec_t WindingArea (winding_t *w)
{
int i;
vec3_t d1, d2, cross;
vec_t total;
total = 0;
for (i=2 ; i<w->numpoints ; i++)
{
VectorSubtract (w->p[i-1], w->p[0], d1);
VectorSubtract (w->p[i], w->p[0], d2);
CrossProduct (d1, d2, cross);
total += 0.5 * VectorLength ( cross );
}
return total;
}
void WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs)
{
vec_t v;
int i,j;
mins[0] = mins[1] = mins[2] = WORLD_SIZE; // 99999; // WORLD_SIZE instead of MAX_WORLD_COORD so that...
maxs[0] = maxs[1] = maxs[2] = -WORLD_SIZE; //-99999; // ... it's guaranteed to be outide of legal
for (i=0 ; i<w->numpoints ; i++)
{
for (j=0 ; j<3 ; j++)
{
v = w->p[i][j];
if (v < mins[j])
mins[j] = v;
if (v > maxs[j])
maxs[j] = v;
}
}
}
/*
=============
WindingCenter
=============
*/
void WindingCenter (winding_t *w, vec3_t center)
{
int i;
float scale;
VectorCopy (vec3_origin, center);
for (i=0 ; i<w->numpoints ; i++)
VectorAdd (w->p[i], center, center);
scale = 1.0/w->numpoints;
VectorScale (center, scale, center);
}
/*
=================
BaseWindingForPlane
=================
*/
winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist)
{
int i, x;
vec_t max, v;
vec3_t org, vright, vup;
winding_t *w;
// find the major axis
max = -MAX_MAP_BOUNDS;
x = -1;
for (i=0 ; i<3; i++)
{
v = fabs(normal[i]);
if (v > max)
{
x = i;
max = v;
}
}
if (x==-1)
Com_Error (ERR_DROP, "BaseWindingForPlane: no axis found");
VectorCopy (vec3_origin, vup);
switch (x)
{
case 0:
case 1:
vup[2] = 1;
break;
case 2:
vup[0] = 1;
break;
}
v = DotProduct (vup, normal);
VectorMA (vup, -v, normal, vup);
VectorNormalize2(vup, vup);
VectorScale (normal, dist, org);
CrossProduct (vup, normal, vright);
VectorScale (vup, MAX_MAP_BOUNDS, vup);
VectorScale (vright, MAX_MAP_BOUNDS, vright);
// project a really big axis aligned box onto the plane
w = AllocWinding (4);
VectorSubtract (org, vright, w->p[0]);
VectorAdd (w->p[0], vup, w->p[0]);
VectorAdd (org, vright, w->p[1]);
VectorAdd (w->p[1], vup, w->p[1]);
VectorAdd (org, vright, w->p[2]);
VectorSubtract (w->p[2], vup, w->p[2]);
VectorSubtract (org, vright, w->p[3]);
VectorSubtract (w->p[3], vup, w->p[3]);
w->numpoints = 4;
return w;
}
/*
==================
CopyWinding
==================
*/
winding_t *CopyWinding (winding_t *w)
{
int size;
winding_t *c;
c = AllocWinding (w->numpoints);
size = (int)((winding_t *)0)->p[w->numpoints];
memcpy (c, w, size);
return c;
}
/*
==================
ReverseWinding
==================
*/
winding_t *ReverseWinding (winding_t *w)
{
int i;
winding_t *c;
c = AllocWinding (w->numpoints);
for (i=0 ; i<w->numpoints ; i++)
{
VectorCopy (w->p[w->numpoints-1-i], c->p[i]);
}
c->numpoints = w->numpoints;
return c;
}
/*
=============
ClipWindingEpsilon
=============
*/
void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist,
vec_t epsilon, winding_t **front, winding_t **back)
{
vec_t dists[MAX_POINTS_ON_WINDING+4];
int sides[MAX_POINTS_ON_WINDING+4];
int counts[3];
static vec_t dot; // VC 4.2 optimizer bug if not static
int i, j;
vec_t *p1, *p2;
vec3_t mid;
winding_t *f, *b;
int maxpts;
counts[0] = counts[1] = counts[2] = 0;
// determine sides for each point
for (i=0 ; i<in->numpoints ; i++)
{
dot = DotProduct (in->p[i], normal);
dot -= dist;
dists[i] = dot;
if (dot > epsilon)
sides[i] = SIDE_FRONT;
else if (dot < -epsilon)
sides[i] = SIDE_BACK;
else
{
sides[i] = SIDE_ON;
}
counts[sides[i]]++;
}
sides[i] = sides[0];
dists[i] = dists[0];
*front = *back = NULL;
if (!counts[0])
{
*back = CopyWinding (in);
return;
}
if (!counts[1])
{
*front = CopyWinding (in);
return;
}
maxpts = in->numpoints+4; // cant use counts[0]+2 because
// of fp grouping errors
*front = f = AllocWinding (maxpts);
*back = b = AllocWinding (maxpts);
for (i=0 ; i<in->numpoints ; i++)
{
p1 = in->p[i];
if (sides[i] == SIDE_ON)
{
VectorCopy (p1, f->p[f->numpoints]);
f->numpoints++;
VectorCopy (p1, b->p[b->numpoints]);
b->numpoints++;
continue;
}
if (sides[i] == SIDE_FRONT)
{
VectorCopy (p1, f->p[f->numpoints]);
f->numpoints++;
}
if (sides[i] == SIDE_BACK)
{
VectorCopy (p1, b->p[b->numpoints]);
b->numpoints++;
}
if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
continue;
// generate a split point
p2 = in->p[(i+1)%in->numpoints];
dot = dists[i] / (dists[i]-dists[i+1]);
for (j=0 ; j<3 ; j++)
{ // avoid round off error when possible
if (normal[j] == 1)
mid[j] = dist;
else if (normal[j] == -1)
mid[j] = -dist;
else
mid[j] = p1[j] + dot*(p2[j]-p1[j]);
}
VectorCopy (mid, f->p[f->numpoints]);
f->numpoints++;
VectorCopy (mid, b->p[b->numpoints]);
b->numpoints++;
}
if (f->numpoints > maxpts || b->numpoints > maxpts)
Com_Error (ERR_DROP, "ClipWinding: points exceeded estimate");
if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING)
Com_Error (ERR_DROP, "ClipWinding: MAX_POINTS_ON_WINDING");
}
/*
=============
ChopWindingInPlace
=============
*/
void ChopWindingInPlace (winding_t **inout, vec3_t normal, vec_t dist, vec_t epsilon)
{
winding_t *in;
vec_t dists[MAX_POINTS_ON_WINDING+4];
int sides[MAX_POINTS_ON_WINDING+4];
int counts[3];
static vec_t dot; // VC 4.2 optimizer bug if not static
int i, j;
vec_t *p1, *p2;
vec3_t mid;
winding_t *f;
int maxpts;
in = *inout;
counts[0] = counts[1] = counts[2] = 0;
// determine sides for each point
for (i=0 ; i<in->numpoints ; i++)
{
dot = DotProduct (in->p[i], normal);
dot -= dist;
dists[i] = dot;
if (dot > epsilon)
sides[i] = SIDE_FRONT;
else if (dot < -epsilon)
sides[i] = SIDE_BACK;
else
{
sides[i] = SIDE_ON;
}
counts[sides[i]]++;
}
sides[i] = sides[0];
dists[i] = dists[0];
if (!counts[0])
{
FreeWinding (in);
*inout = NULL;
return;
}
if (!counts[1])
return; // inout stays the same
maxpts = in->numpoints+4; // cant use counts[0]+2 because
// of fp grouping errors
f = AllocWinding (maxpts);
for (i=0 ; i<in->numpoints ; i++)
{
p1 = in->p[i];
if (sides[i] == SIDE_ON)
{
VectorCopy (p1, f->p[f->numpoints]);
f->numpoints++;
continue;
}
if (sides[i] == SIDE_FRONT)
{
VectorCopy (p1, f->p[f->numpoints]);
f->numpoints++;
}
if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
continue;
// generate a split point
p2 = in->p[(i+1)%in->numpoints];
dot = dists[i] / (dists[i]-dists[i+1]);
for (j=0 ; j<3 ; j++)
{ // avoid round off error when possible
if (normal[j] == 1)
mid[j] = dist;
else if (normal[j] == -1)
mid[j] = -dist;
else
mid[j] = p1[j] + dot*(p2[j]-p1[j]);
}
VectorCopy (mid, f->p[f->numpoints]);
f->numpoints++;
}
if (f->numpoints > maxpts)
Com_Error (ERR_DROP, "ClipWinding: points exceeded estimate");
if (f->numpoints > MAX_POINTS_ON_WINDING)
Com_Error (ERR_DROP, "ClipWinding: MAX_POINTS_ON_WINDING");
FreeWinding (in);
*inout = f;
}
/*
=================
ChopWinding
Returns the fragment of in that is on the front side
of the cliping plane. The original is freed.
=================
*/
winding_t *ChopWinding (winding_t *in, vec3_t normal, vec_t dist)
{
winding_t *f, *b;
ClipWindingEpsilon (in, normal, dist, ON_EPSILON, &f, &b);
FreeWinding (in);
if (b)
FreeWinding (b);
return f;
}
/*
=================
CheckWinding
=================
*/
void CheckWinding (winding_t *w)
{
int i, j;
vec_t *p1, *p2;
vec_t d, edgedist;
vec3_t dir, edgenormal, facenormal;
vec_t area;
vec_t facedist;
if (w->numpoints < 3)
Com_Error (ERR_DROP, "CheckWinding: %i points",w->numpoints);
area = WindingArea(w);
if (area < 1)
Com_Error (ERR_DROP, "CheckWinding: %f area", area);
WindingPlane (w, facenormal, &facedist);
for (i=0 ; i<w->numpoints ; i++)
{
p1 = w->p[i];
for (j=0 ; j<3 ; j++)
if (p1[j] > MAX_MAP_BOUNDS || p1[j] < -MAX_MAP_BOUNDS)
Com_Error (ERR_DROP, "CheckFace: BOGUS_RANGE: %f",p1[j]);
j = i+1 == w->numpoints ? 0 : i+1;
// check the point is on the face plane
d = DotProduct (p1, facenormal) - facedist;
if (d < -ON_EPSILON || d > ON_EPSILON)
Com_Error (ERR_DROP, "CheckWinding: point off plane");
// check the edge isnt degenerate
p2 = w->p[j];
VectorSubtract (p2, p1, dir);
if (VectorLength (dir) < ON_EPSILON)
Com_Error (ERR_DROP, "CheckWinding: degenerate edge");
CrossProduct (facenormal, dir, edgenormal);
VectorNormalize2 (edgenormal, edgenormal);
edgedist = DotProduct (p1, edgenormal);
edgedist += ON_EPSILON;
// all other points must be on front side
for (j=0 ; j<w->numpoints ; j++)
{
if (j == i)
continue;
d = DotProduct (w->p[j], edgenormal);
if (d > edgedist)
Com_Error (ERR_DROP, "CheckWinding: non-convex");
}
}
}
/*
============
WindingOnPlaneSide
============
*/
int WindingOnPlaneSide (winding_t *w, vec3_t normal, vec_t dist)
{
qboolean front, back;
int i;
vec_t d;
front = qfalse;
back = qfalse;
for (i=0 ; i<w->numpoints ; i++)
{
d = DotProduct (w->p[i], normal) - dist;
if (d < -ON_EPSILON)
{
if (front)
return SIDE_CROSS;
back = qtrue;
continue;
}
if (d > ON_EPSILON)
{
if (back)
return SIDE_CROSS;
front = qtrue;
continue;
}
}
if (back)
return SIDE_BACK;
if (front)
return SIDE_FRONT;
return SIDE_ON;
}
/*
=================
AddWindingToConvexHull
Both w and *hull are on the same plane
=================
*/
#define MAX_HULL_POINTS 128
void AddWindingToConvexHull( winding_t *w, winding_t **hull, vec3_t normal ) {
int i, j, k;
float *p, *copy;
vec3_t dir;
float d;
int numHullPoints, numNew;
vec3_t hullPoints[MAX_HULL_POINTS];
vec3_t newHullPoints[MAX_HULL_POINTS];
vec3_t hullDirs[MAX_HULL_POINTS];
qboolean hullSide[MAX_HULL_POINTS];
qboolean outside;
if ( !*hull ) {
*hull = CopyWinding( w );
return;
}
numHullPoints = (*hull)->numpoints;
memcpy( hullPoints, (*hull)->p, numHullPoints * sizeof(vec3_t) );
for ( i = 0 ; i < w->numpoints ; i++ ) {
p = w->p[i];
// calculate hull side vectors
for ( j = 0 ; j < numHullPoints ; j++ ) {
k = ( j + 1 ) % numHullPoints;
VectorSubtract( hullPoints[k], hullPoints[j], dir );
VectorNormalize2( dir, dir );
CrossProduct( normal, dir, hullDirs[j] );
}
outside = qfalse;
for ( j = 0 ; j < numHullPoints ; j++ ) {
VectorSubtract( p, hullPoints[j], dir );
d = DotProduct( dir, hullDirs[j] );
if ( d >= ON_EPSILON ) {
outside = qtrue;
}
if ( d >= -ON_EPSILON ) {
hullSide[j] = qtrue;
} else {
hullSide[j] = qfalse;
}
}
// if the point is effectively inside, do nothing
if ( !outside ) {
continue;
}
// find the back side to front side transition
for ( j = 0 ; j < numHullPoints ; j++ ) {
if ( !hullSide[ j % numHullPoints ] && hullSide[ (j + 1) % numHullPoints ] ) {
break;
}
}
if ( j == numHullPoints ) {
continue;
}
// insert the point here
VectorCopy( p, newHullPoints[0] );
numNew = 1;
// copy over all points that aren't double fronts
j = (j+1)%numHullPoints;
for ( k = 0 ; k < numHullPoints ; k++ ) {
if ( hullSide[ (j+k) % numHullPoints ] && hullSide[ (j+k+1) % numHullPoints ] ) {
continue;
}
copy = hullPoints[ (j+k+1) % numHullPoints ];
VectorCopy( copy, newHullPoints[numNew] );
numNew++;
}
numHullPoints = numNew;
memcpy( hullPoints, newHullPoints, numHullPoints * sizeof(vec3_t) );
}
FreeWinding( *hull );
w = AllocWinding( numHullPoints );
w->numpoints = numHullPoints;
*hull = w;
memcpy( w->p, hullPoints, numHullPoints * sizeof(vec3_t) );
}

51
code/qcommon/cm_polylib.h Normal file
View File

@@ -0,0 +1,51 @@
// this is only used for visualization tools in cm_ debug functions
#ifndef CM_POLYLIB_H
#define CM_POLYLIB_H
typedef struct
{
int numpoints;
vec3_t p[4]; // variable sized
} winding_t;
#define MAX_POINTS_ON_WINDING 64
#define SIDE_FRONT 0
#define SIDE_BACK 1
#define SIDE_ON 2
#define SIDE_CROSS 3
#define CLIP_EPSILON 0.1
#define MAX_MAP_BOUNDS 65535
// you can define on_epsilon in the makefile as tighter
#ifndef ON_EPSILON
#define ON_EPSILON 0.1f
#endif
winding_t *AllocWinding (int points);
vec_t WindingArea (winding_t *w);
void WindingCenter (winding_t *w, vec3_t center);
void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist,
vec_t epsilon, winding_t **front, winding_t **back);
winding_t *ChopWinding (winding_t *in, vec3_t normal, vec_t dist);
winding_t *CopyWinding (winding_t *w);
winding_t *ReverseWinding (winding_t *w);
winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist);
void CheckWinding (winding_t *w);
void WindingPlane (winding_t *w, vec3_t normal, vec_t *dist);
void RemoveColinearPoints (winding_t *w);
int WindingOnPlaneSide (winding_t *w, vec3_t normal, vec_t dist);
void FreeWinding (winding_t *w);
void WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs);
void AddWindingToConvexHull( winding_t *w, winding_t **hull, vec3_t normal );
void ChopWindingInPlace (winding_t **w, vec3_t normal, vec_t dist, vec_t epsilon);
// frees the original if clipped
void pw(winding_t *w);
#endif

72
code/qcommon/cm_public.h Normal file
View File

@@ -0,0 +1,72 @@
#ifndef __CM_PUBLIC_H__
#define __CM_PUBLIC_H__
#include "qfiles.h"
qboolean CM_DeleteCachedMap(qboolean bGuaranteedOkToDelete);
#ifdef _XBOX
void CM_LoadMap( const char *name, qboolean clientload, int *checksum);
#else
void CM_LoadMap( const char *name, qboolean clientload, int *checksum, qboolean subBSP);
#endif
void CM_ClearMap( void );
int CM_TotalMapContents();
clipHandle_t CM_InlineModel( int index ); // 0 = world, 1 + are bmodels
clipHandle_t CM_TempBoxModel( const vec3_t mins, const vec3_t maxs );//, const int contents );
int CM_ModelContents( clipHandle_t model, int subBSPIndex );
int CM_NumClusters (void);
int CM_NumInlineModels( void );
char *CM_EntityString (void);
char *CM_SubBSPEntityString (int index);
int CM_LoadSubBSP(const char *name, qboolean clientload);
int CM_FindSubBSP(int modelIndex);
// returns an ORed contents mask
int CM_PointContents( const vec3_t p, clipHandle_t model );
int CM_TransformedPointContents( const vec3_t p, clipHandle_t model, const vec3_t origin, const vec3_t angles );
void CM_BoxTrace ( trace_t *results, const vec3_t start, const vec3_t end,
const vec3_t mins, const vec3_t maxs,
clipHandle_t model, int brushmask);
void CM_TransformedBoxTrace( trace_t *results, const vec3_t start, const vec3_t end,
const vec3_t mins, const vec3_t maxs,
clipHandle_t model, int brushmask,
const vec3_t origin, const vec3_t angles);
#ifdef _XBOX
const byte *CM_ClusterPVS (int cluster);
#else
byte *CM_ClusterPVS (int cluster);
#endif
int CM_PointLeafnum( const vec3_t p );
// only returns non-solid leafs
// overflow if return listsize and if *lastLeaf != list[listsize-1]
int CM_BoxLeafnums( const vec3_t mins, const vec3_t maxs, int *boxList,
int listsize, int *lastLeaf );
int CM_LeafCluster (int leafnum);
int CM_LeafArea (int leafnum);
void CM_AdjustAreaPortalState( int area1, int area2, qboolean open );
qboolean CM_AreasConnected( int area1, int area2 );
int CM_WriteAreaBits( byte *buffer, int area );
//for savegames
void CM_WritePortalState ();
void CM_ReadPortalState ();
// cm_marks.c
int CM_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection,
int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer );
// cm_patch.c
void CM_DrawDebugSurface( void (*drawPoly)(int color, int numPoints, float *points) );
#endif //__CM_PUBLIC_H__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,89 @@
#pragma once
#if !defined(CM_RANDOMTERRAIN_H_INC)
#define CM_RANDOMTERRAIN_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including cm_randomterrain.h")
#endif
//class CPathInfo;
#define SPLINE_MERGE_SIZE 3
#define CIRCLE_STAMP_SIZE 128
class CPathInfo
{
private:
vec4_t *mPoints, *mWork;
vec_t *mWeights;
int mNumPoints;
float mMinWidth, mMaxWidth;
float mInc;
float mDepth, mBreadth;
float mDeviation;
byte mCircleStamp[CIRCLE_STAMP_SIZE][CIRCLE_STAMP_SIZE];
void CreateCircle(void);
void Stamp(int x, int y, int size, int depth, unsigned char *Data, int DataWidth, int DataHeight);
public:
CPathInfo(CCMLandScape *landscape, int numPoints, float bx, float by, float ex, float ey,
float minWidth, float maxWidth, float depth, float deviation, float breadth,
CPathInfo *Connected, unsigned CreationFlags);
~CPathInfo(void);
int GetNumPoints(void) { return mNumPoints; }
float *GetPoint(int index) { return mPoints[index]; }
float GetWidth(int index) { return mPoints[index][3]; }
void GetInfo(float PercentInto, vec4_t Coord, vec4_t Vector);
void DrawPath(unsigned char *Data, int DataWidth, int DataHeight );
};
const int MAX_RANDOM_PATHS = 30;
// Path Creation Flags
#define PATH_CREATION_CONNECT_FRONT 0x00000001
class CRandomTerrain
{
private:
class CCMLandScape *mLandScape;
int mWidth;
int mHeight;
int mArea;
int mBorder;
byte *mGrid;
CPathInfo *mPaths[MAX_RANDOM_PATHS];
public:
CRandomTerrain(void);
~CRandomTerrain(void);
CCMLandScape *GetLandScape(void) { return mLandScape; }
const vec3pair_t &GetBounds(void) const { return mLandScape->GetBounds(); }
void rand_seed(int seed) { mLandScape->rand_seed(seed); }
int get_rand_seed(void) { return mLandScape->get_rand_seed();}
float flrand(float min, float max) { return mLandScape->flrand(min, max); }
int irand(int min, int max) { return mLandScape->irand(min, max); }
void Init(class CCMLandScape *landscape, byte *data, int width, int height);
void Shutdown(void);
bool CreatePath(int PathID, int ConnectedID, unsigned CreationFlags, int numPoints,
float bx, float by, float ex, float ey,
float minWidth, float maxWidth, float depth, float deviation, float breadth );
bool GetPathInfo(int PathNum, float PercentInto, vec2_t Coord, vec2_t Vector);
void ParseGenerate(const char *GenerateFile);
void Smooth ( void );
void Generate(int symmetric);
void ClearPaths(void);
};
unsigned RMG_CreateSeed(char *TextSeed);
#endif // CM_RANDOM_H_INC

529
code/qcommon/cm_shader.cpp Normal file
View File

@@ -0,0 +1,529 @@
#include "../server/exe_headers.h"
#include "../game/q_shared.h"
#include "cm_local.h"
#include "memory.h"
#include "chash.h"
class CCMShaderText
{
private:
char mName[MAX_QPATH];
class CCMShaderText *mNext;
const char *mData;
public:
// Constructors
CCMShaderText(const char *name, const char *data) { Q_strncpyz(mName, name, MAX_QPATH); mNext = NULL; mData = data; }
~CCMShaderText(void) {}
// Accessors
const char *GetName(void) const { return(mName); }
class CCMShaderText *GetNext(void) const { return(mNext); }
void SetNext(class CCMShaderText *next) { mNext = next; }
void Destroy(void) { delete this; }
const char *GetData(void) const { return(mData); }
};
char *shaderText = NULL;
CHash<CCMShaderText> shaderTextTable;
CHash<CCMShader> cmShaderTable;
const char *SkipWhitespace( const char *data, qboolean *hasNewLines );
//rwwFIXMEFIXME: Called at RE_BeginRegistration because Hunk_Clear
//destroys the memory cmShaderTable is on. This is a temp solution
//I guess.
void ShaderTableCleanup()
{
cmShaderTable.clear();
}
/*
====================
CM_CreateShaderTextHash
=====================
*/
void CM_CreateShaderTextHash(void)
{
const char *p;
qboolean hasNewLines;
char *token;
CCMShaderText *shader;
p = shaderText;
// look for label
while (p)
{
p = SkipWhitespace(p, &hasNewLines);
token = COM_ParseExt( &p, qtrue );
if ( !token[0] )
{
break;
}
shader = new CCMShaderText(token, p);
shaderTextTable.insert(shader);
SkipBracedSection(&p);
}
}
/*
====================
CM_LoadShaderFiles
Finds and loads all .shader files, combining them into
a single large text block that can be scanned for shader names
=====================
*/
#define MAX_SHADER_FILES 1024
void CM_LoadShaderFiles( void )
{
char **shaderFiles1;
int numShaders1;
char *buffers[MAX_SHADER_FILES];
int numShaders;
int i;
int sum = 0;
// scan for shader files
shaderFiles1 = FS_ListFiles( "shaders", ".shader", &numShaders1 );
if ( !shaderFiles1 || !numShaders1 )
{
Com_Printf( S_COLOR_YELLOW "WARNING: no shader files found\n" );
return;
}
numShaders = numShaders1;
if ( numShaders > MAX_SHADER_FILES )
{
numShaders = MAX_SHADER_FILES;
}
// load and parse shader files
for ( i = 0; i < numShaders1; i++ )
{
char filename[MAX_QPATH];
Com_sprintf( filename, sizeof( filename ), "shaders/%s", shaderFiles1[i] );
Com_DPrintf( "...loading '%s'\n", filename );
FS_ReadFile( filename, (void **)&buffers[i] );
if ( !buffers[i] )
{
Com_Error( ERR_DROP, "Couldn't load %s", filename );
}
sum += COM_Compress( buffers[i] );
}
// build single large buffer
shaderText = (char *)Z_Malloc( sum + numShaders * 2, TAG_SHADERTEXT, qtrue);
// free in reverse order, so the temp files are all dumped
for ( i = numShaders - 1; i >= 0 ; i-- )
{
strcat( shaderText, "\n" );
strcat( shaderText, buffers[i] );
FS_FreeFile( buffers[i] );
}
// free up memory
FS_FreeFileList( shaderFiles1 );
}
/*
==================
CM_GetShaderText
==================
*/
const char *CM_GetShaderText(const char *key)
{
CCMShaderText *st;
st = shaderTextTable[key];
if(st)
{
return(st->GetData());
}
return(NULL);
}
/*
==================
CM_FreeShaderText
==================
*/
void CM_FreeShaderText(void)
{
shaderTextTable.clear();
if(shaderText)
{
Z_Free(shaderText);
shaderText = NULL;
}
}
/*
==================
CM_LoadShaderText
Loads in all the .shader files so it can be accessed by the server and the renderer
Creates a hash table to quickly access the shader text
==================
*/
void CM_LoadShaderText(bool forceReload)
{
if(forceReload)
{
CM_FreeShaderText();
}
if(shaderText)
{
return;
}
Com_Printf("Loading shader text .....\n");
CM_LoadShaderFiles();
CM_CreateShaderTextHash();
Com_Printf("..... %d shader definitions loaded\n", shaderTextTable.count());
}
/*
===============
ParseSurfaceParm
surfaceparm <name>
===============
*/
typedef struct
{
char *name;
int clearSolid, surfaceFlags, contents;
} infoParm_t;
infoParm_t svInfoParms[] =
{
// Game content Flags
{"nonsolid", ~CONTENTS_SOLID, 0, 0 }, // special hack to clear solid flag
{"nonopaque", ~CONTENTS_OPAQUE, 0, 0 }, // special hack to clear opaque flag
{"lava", ~CONTENTS_SOLID, 0, CONTENTS_LAVA }, // very damaging
{"slime", ~CONTENTS_SOLID, 0, CONTENTS_SLIME }, // mildly damaging
{"water", ~CONTENTS_SOLID, 0, CONTENTS_WATER },
{"fog", ~CONTENTS_SOLID, 0, CONTENTS_FOG}, // carves surfaces entering
{"shotclip", ~CONTENTS_SOLID, 0, CONTENTS_SHOTCLIP }, /* block shots, but not people */
{"playerclip", ~(CONTENTS_SOLID|CONTENTS_OPAQUE),0,CONTENTS_PLAYERCLIP }, /* block only the player */
{"monsterclip", ~(CONTENTS_SOLID|CONTENTS_OPAQUE),0,CONTENTS_MONSTERCLIP },
{"botclip", ~(CONTENTS_SOLID|CONTENTS_OPAQUE),0,CONTENTS_BOTCLIP }, /* NPC do not enter */
{"trigger", ~(CONTENTS_SOLID|CONTENTS_OPAQUE),0,CONTENTS_TRIGGER },
{"nodrop", ~(CONTENTS_SOLID|CONTENTS_OPAQUE),0,CONTENTS_NODROP }, // don't drop items or leave bodies (death fog, lava, etc)
{"terrain", ~(CONTENTS_SOLID|CONTENTS_OPAQUE),0,CONTENTS_TERRAIN }, /* use special terrain collsion */
{"ladder", ~(CONTENTS_SOLID|CONTENTS_OPAQUE),0,CONTENTS_LADDER }, // climb up in it like water
{"abseil", ~(CONTENTS_SOLID|CONTENTS_OPAQUE),0,CONTENTS_ABSEIL }, // can abseil down this brush
{"outside", ~(CONTENTS_SOLID|CONTENTS_OPAQUE),0,CONTENTS_OUTSIDE }, // volume is considered to be in the outside (i.e. not indoors)
{"inside", ~(CONTENTS_SOLID|CONTENTS_OPAQUE),0,CONTENTS_INSIDE }, // volume is considered to be inside (i.e. indoors)
{"detail", -1, 0, CONTENTS_DETAIL }, // don't include in structural bsp
{"trans", -1, 0, CONTENTS_TRANSLUCENT }, // surface has an alpha component
/* Game surface flags */
{"sky", -1, SURF_SKY, 0 }, /* emit light from an environment map */
{"slick", -1, SURF_SLICK, 0 },
{"nodamage", -1, SURF_NODAMAGE, 0 },
{"noimpact", -1, SURF_NOIMPACT, 0 }, /* don't make impact explosions or marks */
{"nomarks", -1, SURF_NOMARKS, 0 }, /* don't make impact marks, but still explode */
{"nodraw", -1, SURF_NODRAW, 0 }, /* don't generate a drawsurface (or a lightmap) */
{"nosteps", -1, SURF_NOSTEPS, 0 },
{"nodlight", -1, SURF_NODLIGHT, 0 }, /* don't ever add dynamic lights */
{"metalsteps", -1, SURF_METALSTEPS,0 },
{"nomiscents", -1, SURF_NOMISCENTS,0 }, /* No misc ents on this surface */
{"forcefield", -1, SURF_FORCEFIELD,0 },
{"forcesight", -1, SURF_FORCESIGHT,0 }, // only visible with force sight
};
void SV_ParseSurfaceParm( CCMShader * shader, const char **text )
{
char *token;
int numsvInfoParms = sizeof(svInfoParms) / sizeof(svInfoParms[0]);
int i;
token = COM_ParseExt( text, qfalse );
for ( i = 0 ; i < numsvInfoParms ; i++ )
{
if ( !Q_stricmp( token, svInfoParms[i].name ) )
{
shader->surfaceFlags |= svInfoParms[i].surfaceFlags;
shader->contentFlags |= svInfoParms[i].contents;
shader->contentFlags &= svInfoParms[i].clearSolid;
break;
}
}
}
/*
=================
ParseMaterial
=================
*/
const char *svMaterialNames[MATERIAL_LAST] =
{
MATERIALS
};
void SV_ParseMaterial( CCMShader *shader, const char **text )
{
char *token;
int i;
token = COM_ParseExt( text, qfalse );
if ( !token[0] )
{
Com_Printf( S_COLOR_YELLOW "WARNING: missing material in shader '%s'\n", shader->shader );
return;
}
for(i = 0; i < MATERIAL_LAST; i++)
{
if ( !Q_stricmp( token, svMaterialNames[i] ) )
{
shader->surfaceFlags &= ~MATERIAL_MASK;//safety, clear it first
shader->surfaceFlags |= i;
break;
}
}
}
/*
===============
ParseVector
===============
*/
qboolean CM_ParseVector( CCMShader *shader, const char **text, int count, float *v )
{
char *token;
int i;
// FIXME: spaces are currently required after parens, should change parseext...
token = COM_ParseExt( text, qfalse );
if ( strcmp( token, "(" ) )
{
Com_Printf( S_COLOR_YELLOW "WARNING: missing parenthesis in shader '%s'\n", shader->shader );
return qfalse;
}
for ( i = 0 ; i < count ; i++ )
{
token = COM_ParseExt( text, qfalse );
if ( !token[0] )
{
Com_Printf( S_COLOR_YELLOW "WARNING: missing vector element in shader '%s'\n", shader->shader );
return qfalse;
}
v[i] = atof( token );
}
token = COM_ParseExt( text, qfalse );
if ( strcmp( token, ")" ) )
{
Com_Printf( S_COLOR_YELLOW "WARNING: missing parenthesis in shader '%s'\n", shader->shader );
return qfalse;
}
return qtrue;
}
/*
=================
CM_ParseShader
The current text pointer is at the explicit text definition of the
shader. Parse it into the global shader variable.
This extracts all the info from the shader required for physics and collision
It is designed to *NOT* load any image files and not require any of the renderer to
be initialised.
=================
*/
void CM_ParseShader( CCMShader *shader, const char **text )
{
char *token;
token = COM_ParseExt( text, qtrue );
if ( token[0] != '{' )
{
Com_Printf( S_COLOR_YELLOW "WARNING: expecting '{', found '%s' instead in shader '%s'\n", token, shader->shader );
return;
}
while ( true )
{
token = COM_ParseExt( text, qtrue );
if ( !token[0] )
{
Com_Printf( S_COLOR_YELLOW "WARNING: no concluding '}' in shader %s\n", shader->shader );
return;
}
// end of shader definition
if ( token[0] == '}' )
{
break;
}
// stage definition
else if ( token[0] == '{' )
{
SkipBracedSection( text );
continue;
}
// material deprecated as of 11 Jan 01
// material undeprecated as of 7 May 01 - q3map_material deprecated
else if ( !Q_stricmp( token, "material" ) || !Q_stricmp( token, "q3map_material" ) )
{
SV_ParseMaterial( shader, text );
}
// sun parms
// q3map_sun deprecated as of 11 Jan 01
else if ( !Q_stricmp( token, "sun" ) || !Q_stricmp( token, "q3map_sun" ) )
{
// float a, b;
token = COM_ParseExt( text, qfalse );
// shader->sunLight[0] = atof( token );
token = COM_ParseExt( text, qfalse );
// shader->sunLight[1] = atof( token );
token = COM_ParseExt( text, qfalse );
// shader->sunLight[2] = atof( token );
// VectorNormalize( shader->sunLight );
token = COM_ParseExt( text, qfalse );
// a = atof( token );
// VectorScale( shader->sunLight, a, shader->sunLight);
token = COM_ParseExt( text, qfalse );
// a = DEG2RAD(atof( token ));
token = COM_ParseExt( text, qfalse );
// b = DEG2RAD(atof( token ));
// shader->sunDirection[0] = cos( a ) * cos( b );
// shader->sunDirection[1] = sin( a ) * cos( b );
// shader->sunDirection[2] = sin( b );
}
else if ( !Q_stricmp( token, "surfaceParm" ) )
{
SV_ParseSurfaceParm( shader, text );
continue;
}
else if ( !Q_stricmp( token, "fogParms" ) )
{
vec3_t fogColor;
if ( !CM_ParseVector( shader, text, 3, fogColor ) )
{
return;
}
token = COM_ParseExt( text, qfalse );
if ( !token[0] )
{
Com_Printf( S_COLOR_YELLOW "WARNING: missing parm for 'fogParms' keyword in shader '%s'\n", shader->shader );
continue;
}
// shader->depthForOpaque = atof( token );
// skip any old gradient directions
SkipRestOfLine( (const char **)text );
continue;
}
}
return;
}
/*
=================
CM_SetupShaderProperties
Scans thru the shaders loaded for the map, parses the text of that shader and
extracts the interesting info *WITHOUT* loading up any images or requiring
the renderer to be active.
=================
*/
void CM_SetupShaderProperties(void)
{
int i;
const char *def;
CCMShader *shader;
// Add all basic shaders to the cmShaderTable
for(i = 0; i < cmg.numShaders; i++)
{
cmShaderTable.insert(CM_GetShaderInfo(i));
}
// Go through and parse evaluate shader names to shadernums
for(i = 0; i < cmg.numShaders; i++)
{
shader = CM_GetShaderInfo(i);
def = CM_GetShaderText(shader->shader);
if(def)
{
CM_ParseShader(shader, &def);
}
}
}
void CM_ShutdownShaderProperties(void)
{
if(cmShaderTable.count())
{
Com_Printf("Shutting down cmShaderTable .....\n");
cmShaderTable.clear();
}
}
CCMShader *CM_GetShaderInfo( const char *name )
{
CCMShader *out;
const char *def;
out = cmShaderTable[name];
if(out)
{
return(out);
}
// Create a new CCMShader class
//out = (CCMShader *)Hunk_Alloc( sizeof( CCMShader ), h_high );
out = (CCMShader *)Hunk_Alloc( sizeof( CCMShader ), qtrue );
// Set defaults
Q_strncpyz(out->shader, name, MAX_QPATH);
out->contentFlags = CONTENTS_SOLID | CONTENTS_OPAQUE;
// Parse in any text if it exists
def = CM_GetShaderText(name);
if(def)
{
CM_ParseShader(out, &def);
}
cmShaderTable.insert(out);
return(out);
}
CCMShader *CM_GetShaderInfo( int shaderNum )
{
CCMShader *out;
if((shaderNum < 0) || (shaderNum >= cmg.numShaders))
{
return(NULL);
}
out = cmg.shaders + shaderNum;
return(out);
}
// end

1714
code/qcommon/cm_terrain.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,489 @@
#include "../server/exe_headers.h"
#include "cm_local.h"
#include "cm_patch.h"
#include "cm_landscape.h"
#include "../game/genericparser2.h"
//#include "image.h"
//#include "../qcommon/q_imath.h"
#include "cm_terrainmap.h"
#include "cm_draw.h"
#include "../png/png.h"
static CTerrainMap *TerrainMap = 0;
void R_CreateAutomapImage( const char *name, const byte *pic, int width, int height,
qboolean mipmap, qboolean allowPicmip, qboolean allowTC, int glWrapClampMode );
// simple function for getting a proper color for a side
inline CPixel32 SideColor(int side)
{
CPixel32 col(255,255,255);
switch (side)
{
default:
break;
case SIDE_BLUE:
col = CPixel32(0,0,192);
break;
case SIDE_RED:
col = CPixel32(192,0,0);
break;
}
return col;
}
CTerrainMap::CTerrainMap(CCMLandScape *landscape) :
mLandscape(landscape)
{
ApplyBackground();
ApplyHeightmap();
CDraw32 draw;
draw.SetBuffer((CPixel32*) mImage);
draw.SetBufferSize(TM_WIDTH,TM_HEIGHT,TM_WIDTH);
// create version with paths and water shown
int x,y;
int water;
int land;
for (y=0; y<TM_HEIGHT; y++)
for (x=0; x<TM_WIDTH; x++)
{
CPixel32 cp = ((CPixel32*)mBufImage)[PIXPOS(x,y,TM_WIDTH)];
land = CLAMP(((255 - cp.a)*2)/3,0,255);
water = CLAMP((landscape->GetBaseWaterHeight() - cp.a)*4, 0, 255);
cp.a = 255;
if (x > TM_BORDER && x < (TM_WIDTH-TM_BORDER) &&
y > TM_BORDER && y < (TM_WIDTH-TM_BORDER))
{
cp = ALPHA_PIX (CPixel32(0,0,0), cp, land, 256-land);
if (water > 0)
cp = ALPHA_PIX (CPixel32(0,0,255), cp, water, 256-water);
}
draw.PutPix(x, y, cp);
}
// Load icons for symbols on map
GLenum format;
#ifdef _XBOX
int mipcount;
R_LoadImage("gfx/menus/rmg/start", (byte**)&mSymStart, &mSymStartWidth, &mSymStartHeight, &mipcount, &format);
R_LoadImage("gfx/menus/rmg/end", (byte**)&mSymEnd, &mSymEndWidth, &mSymEndHeight, &mipcount, &format);
R_LoadImage("gfx/menus/rmg/objective", (byte**)&mSymObjective, &mSymObjectiveWidth, &mSymObjectiveHeight, &mipcount, &format);
R_LoadImage("gfx/menus/rmg/building", (byte**)&mSymBld, &mSymBldWidth, &mSymBldHeight, &mipcount, &format);
#else
R_LoadImage("gfx/menus/rmg/start", (byte**)&mSymStart, &mSymStartWidth, &mSymStartHeight, &format);
R_LoadImage("gfx/menus/rmg/end", (byte**)&mSymEnd, &mSymEndWidth, &mSymEndHeight, &format);
R_LoadImage("gfx/menus/rmg/objective", (byte**)&mSymObjective, &mSymObjectiveWidth, &mSymObjectiveHeight, &format);
R_LoadImage("gfx/menus/rmg/building", (byte**)&mSymBld, &mSymBldWidth, &mSymBldHeight, &format);
#endif
}
CTerrainMap::~CTerrainMap()
{
if (mSymStart)
{
Z_Free(mSymStart);
mSymStart = NULL;
}
if (mSymEnd)
{
Z_Free(mSymEnd);
mSymEnd = NULL;
}
if (mSymBld)
{
Z_Free(mSymBld);
mSymBld = NULL;
}
if (mSymObjective)
{
Z_Free(mSymObjective);
mSymObjective = NULL;
}
CDraw32::CleanUp();
}
void CTerrainMap::ApplyBackground(void)
{
int x, y;
byte *outPos;
float xRel, yRel, xInc, yInc;
byte *backgroundImage;
int backgroundWidth, backgroundHeight, backgroundDepth;
int pos;
GLenum format;
memset(mImage, 255, sizeof(mBufImage));
// R_LoadImage("textures\\kamchatka\\ice", &backgroundImage, &backgroundWidth, &backgroundHeight, &format);0
backgroundDepth = 4;
#ifdef _XBOX
int mipcount;
R_LoadImage("gfx\\menus\\rmg\\01_bg", &backgroundImage, &backgroundWidth, &backgroundHeight, &mipcount, &format);
#else
R_LoadImage("gfx\\menus\\rmg\\01_bg", &backgroundImage, &backgroundWidth, &backgroundHeight, &format);
#endif
if (backgroundImage)
{
outPos = (byte *)mBufImage;
xInc = (float)backgroundWidth / (float)TM_WIDTH;
yInc = (float)backgroundHeight / (float)TM_HEIGHT;
yRel = 0.0;
for(y=0;y<TM_HEIGHT;y++)
{
xRel = 0.0;
for(x=0;x<TM_WIDTH;x++)
{
pos = ((((int)yRel)*backgroundWidth) + ((int)xRel)) * 4;
*outPos = backgroundImage[pos++];
outPos++;
*outPos = backgroundImage[pos++];
outPos++;
*outPos = backgroundImage[pos];
outPos+=2;
xRel += xInc;
}
yRel += yInc;
}
Z_Free(backgroundImage);
}
}
void CTerrainMap::ApplyHeightmap(void)
{
int x, y;
byte *inPos = mLandscape->GetHeightMap();
int width = mLandscape->GetRealWidth();
int height = mLandscape->GetRealHeight();
byte *outPos;
unsigned tempColor;
float xRel, yRel, xInc, yInc;
int count;
outPos = (byte *)mBufImage;
outPos += (((TM_BORDER * TM_WIDTH) + TM_BORDER) * 4);
xInc = (float)width / (float)(TM_REAL_WIDTH);
yInc = (float)height / (float)(TM_REAL_HEIGHT);
// add in height map as alpha
yRel = 0.0;
for(y=0;y<TM_REAL_HEIGHT;y++)
{
// x is flipped!
xRel = width;
for(x=0;x<TM_REAL_WIDTH;x++)
{
count = 1;
tempColor = inPos[(((int)yRel)*width) + ((int)xRel)];
if (yRel >= 1.0)
{
tempColor += inPos[(((int)(yRel-0.5))*width) + ((int)xRel)];
count++;
}
if (yRel <= height-2)
{
tempColor += inPos[(((int)(yRel+0.5))*width) + ((int)xRel)];
count++;
}
if (xRel >= 1.0)
{
tempColor += inPos[(((int)(yRel))*width) + ((int)(xRel-0.5))];
count++;
}
if (xRel <= width-2)
{
tempColor += inPos[(((int)(yRel))*width) + ((int)(xRel+0.5))];
count++;
}
tempColor /= count;
outPos[3] = tempColor;
outPos += 4;
// x is flipped!
xRel -= xInc;
}
outPos += TM_BORDER * 4 * 2;
yRel += yInc;
}
}
// Convert position in game coords to automap coords
void CTerrainMap::ConvertPos(int& x, int& y)
{
x = ((x - mLandscape->GetMins()[0]) / mLandscape->GetSize()[0]) * TM_REAL_WIDTH;
y = ((y - mLandscape->GetMins()[1]) / mLandscape->GetSize()[1]) * TM_REAL_HEIGHT;
// x is flipped!
x = TM_REAL_WIDTH - x - 1;
// border
x += TM_BORDER;
y += TM_BORDER;
}
void CTerrainMap::AddStart(int x, int y, int side)
{
ConvertPos(x, y);
CDraw32 draw;
draw.BlitColor(x-mSymStartWidth/2, y-mSymStartHeight/2, mSymStartWidth, mSymStartHeight,
(CPixel32*)mSymStart, 0, 0, mSymStartWidth, SideColor(side));
}
void CTerrainMap::AddEnd(int x, int y, int side)
{
ConvertPos(x, y);
CDraw32 draw;
draw.BlitColor(x-mSymEndWidth/2, y-mSymEndHeight/2, mSymEndWidth, mSymEndHeight,
(CPixel32*)mSymEnd, 0, 0, mSymEndWidth, SideColor(side));
}
void CTerrainMap::AddObjective(int x, int y, int side)
{
ConvertPos(x, y);
CDraw32 draw;
draw.BlitColor(x-mSymObjectiveWidth/2, y-mSymObjectiveHeight/2, mSymObjectiveWidth, mSymObjectiveHeight,
(CPixel32*)mSymObjective, 0, 0, mSymObjectiveWidth, SideColor(side));
}
void CTerrainMap::AddBuilding(int x, int y, int side)
{
ConvertPos(x, y);
CDraw32 draw;
draw.BlitColor(x-mSymBldWidth/2, y-mSymBldHeight/2, mSymBldWidth, mSymBldHeight,
(CPixel32*)mSymBld, 0, 0, mSymBldWidth, SideColor(side));
}
void CTerrainMap::AddNPC(int x, int y, bool friendly)
{
ConvertPos(x, y);
CDraw32 draw;
if (friendly)
draw.DrawCircle(x,y,3, CPixel32(0,192,0), CPixel32(0,0,0,0));
else
draw.DrawCircle(x,y,3, CPixel32(192,0,0), CPixel32(0,0,0,0));
}
void CTerrainMap::AddNode(int x, int y)
{
ConvertPos(x, y);
CDraw32 draw;
draw.DrawCircle(x,y,20, CPixel32(255,255,255), CPixel32(0,0,0,0));
}
void CTerrainMap::AddWallRect(int x, int y, int side)
{
ConvertPos(x, y);
CDraw32 draw;
switch (side)
{
default:
draw.DrawBox(x-1,y-1,3,3,CPixel32(192,192,192,128));
break;
case SIDE_BLUE:
draw.DrawBox(x-1,y-1,3,3,CPixel32(0,0,192,128));
break;
case SIDE_RED:
draw.DrawBox(x-1,y-1,3,3,CPixel32(192,0,0,128));
break;
}
}
void CTerrainMap::AddPlayer(vec3_t origin, vec3_t angles)
{
// draw player start on automap
CDraw32 draw;
vec3_t up;
vec3_t pt[4] = {{0,0,0},{-5,-5,0},{10,0,0},{-5,5,0}};
vec3_t p;
int x,y,i;
float facing;
POINT poly[4];
facing = angles[1];
up[0] = 0;
up[1] = 0;
up[2] = 1;
x = (int)origin[0];
y = (int)origin[1];
ConvertPos(x, y);
x++; y++;
for (i=0; i<4; i++)
{
RotatePointAroundVector( p, up, pt[i], facing );
poly[i].x = (int)(-p[0] + x);
poly[i].y = (int)(p[1] + y);
}
// draw arrowhead shadow
draw.DrawPolygon(4, poly, CPixel32(0,0,0,128), CPixel32(0,0,0,128));
// draw arrowhead
for (i=0; i<4; i++)
{
poly[i].x--;
poly[i].y--;
}
draw.DrawPolygon(4, poly, CPixel32(255,255,255), CPixel32(255,255,255));
}
void CTerrainMap::Upload(vec3_t player_origin, vec3_t player_angles)
{
CDraw32 draw;
// copy completed map to mBufImage
draw.SetBuffer((CPixel32*) mBufImage);
draw.SetBufferSize(TM_WIDTH,TM_HEIGHT,TM_WIDTH);
draw.Blit(0, 0, TM_WIDTH, TM_HEIGHT,
(CPixel32*)mImage, 0, 0, TM_WIDTH);
// now draw player's location on map
if (player_origin)
{
AddPlayer(player_origin, player_angles);
}
draw.SetAlphaBuffer(255);
R_CreateAutomapImage("*automap", (unsigned char *)draw.buffer, TM_WIDTH, TM_HEIGHT, qfalse, qfalse, qtrue, qfalse);
draw.SetBuffer((CPixel32*) mImage);
}
void CTerrainMap::SaveImageToDisk(const char * terrainName, const char * missionName, const char * seed)
{
//ri.COM_SavePNG(va("save/%s_%s_%s.png", terrainName, missionName, seed),
// (unsigned char *)mImage, TM_WIDTH, TM_HEIGHT, 4);
//rww - Use JPG here? This function seems to be only for debugging anyway.
// PNG_Save(va("save/%s_%s_%s.png", terrainName, missionName, seed),
// (unsigned char *)mImage, TM_WIDTH, TM_HEIGHT, 4);
}
void CM_TM_Create(CCMLandScape *landscape)
{
if (TerrainMap)
{
CM_TM_Free();
}
TerrainMap = new CTerrainMap(landscape);
}
void CM_TM_Free(void)
{
if (TerrainMap)
{
delete TerrainMap;
TerrainMap = 0;
}
}
void CM_TM_AddStart(int x, int y, int side)
{
if (TerrainMap)
{
TerrainMap->AddStart(x, y, side);
}
}
void CM_TM_AddEnd(int x, int y, int side)
{
if (TerrainMap)
{
TerrainMap->AddEnd(x, y, side);
}
}
void CM_TM_AddObjective(int x, int y, int side)
{
if (TerrainMap)
{
TerrainMap->AddObjective(x, y, side);
}
}
void CM_TM_AddNPC(int x, int y, bool friendly)
{
if (TerrainMap)
{
TerrainMap->AddNPC(x, y, friendly);
}
}
void CM_TM_AddNode(int x, int y)
{
if (TerrainMap)
{
TerrainMap->AddNode(x, y);
}
}
void CM_TM_AddBuilding(int x, int y, int side)
{
if (TerrainMap)
{
TerrainMap->AddBuilding(x, y, side);
}
}
void CM_TM_AddWallRect(int x, int y, int side)
{
if (TerrainMap)
{
TerrainMap->AddWallRect(x, y, side);
}
}
void CM_TM_Upload(vec3_t player_origin, vec3_t player_angles)
{
if (TerrainMap)
{
TerrainMap->Upload(player_origin, player_angles);
}
}
void CM_TM_SaveImageToDisk(const char * terrainName, const char * missionName, const char * seed)
{
if (TerrainMap)
{ // write out automap
TerrainMap->SaveImageToDisk(terrainName, missionName, seed);
}
}
void CM_TM_ConvertPosition(int &x, int &y, int Width, int Height)
{
if (TerrainMap)
{
TerrainMap->ConvertPos(x, y);
x = x * Width / TM_WIDTH;
y = y * Height / TM_HEIGHT;
}
}

View File

@@ -0,0 +1,77 @@
#pragma once
#if !defined(CM_TERRAINMAP_H_INC)
#define CM_TERRAINMAP_H_INC
#define TM_WIDTH 512
#define TM_HEIGHT 512
#define TM_BORDER 16
#define TM_REAL_WIDTH (TM_WIDTH-TM_BORDER-TM_BORDER)
#define TM_REAL_HEIGHT (TM_HEIGHT-TM_BORDER-TM_BORDER)
class CTerrainMap
{
private:
byte mImage[TM_HEIGHT][TM_WIDTH][4]; // image to output
byte mBufImage[TM_HEIGHT][TM_WIDTH][4]; // src data for image, color and bump
byte* mSymBld;
int mSymBldWidth;
int mSymBldHeight;
byte* mSymStart;
int mSymStartWidth;
int mSymStartHeight;
byte* mSymEnd;
int mSymEndWidth;
int mSymEndHeight;
byte* mSymObjective;
int mSymObjectiveWidth;
int mSymObjectiveHeight;
CCMLandScape *mLandscape;
void ApplyBackground(void);
void ApplyHeightmap(void);
public:
CTerrainMap(CCMLandScape *landscape);
~CTerrainMap();
void ConvertPos(int& x, int& y);
void AddBuilding(int x, int y, int side);
void AddStart(int x, int y, int side);
void AddEnd(int x, int y, int side);
void AddObjective(int x, int y, int side);
void AddNPC(int x, int y, bool friendly);
void AddWallRect(int x, int y, int side);
void AddNode(int x, int y);
void AddPlayer(vec3_t origin, vec3_t angles);
void Upload(vec3_t player_origin, vec3_t player_angles);
void SaveImageToDisk(const char * terrainName, const char * missionName, const char * seed);
};
enum
{
SIDE_NONE =0,
SIDE_BLUE =1,
SIDE_RED = 2
};
void CM_TM_Create(CCMLandScape *landscape);
void CM_TM_Free(void);
void CM_TM_AddStart(int x, int y, int side = SIDE_NONE);
void CM_TM_AddEnd(int x, int y, int side = SIDE_NONE);
void CM_TM_AddObjective(int x, int y, int side = SIDE_NONE);
void CM_TM_AddNPC(int x, int y, bool friendly);
void CM_TM_AddWallRect(int x, int y, int side = SIDE_NONE);
void CM_TM_AddNode(int x, int y);
void CM_TM_AddBuilding(int x, int y, int side = SIDE_NONE);
void CM_TM_Upload(vec3_t player_origin, vec3_t player_angles);
void CM_TM_SaveImageToDisk(const char * terrainName, const char * missionName, const char * seed);
void CM_TM_ConvertPosition(int &x, int &y, int Width, int Height);
#endif CM_TERRAINMAP_H_INC

793
code/qcommon/cm_test.cpp Normal file
View File

@@ -0,0 +1,793 @@
#include "cm_local.h"
#pragma warning (push, 3) //go back down to 3 for the stl include
#pragma warning (pop)
using namespace std;
#ifdef _XBOX
#include "../renderer/tr_local.h"
#endif
class CPoint
{
public:
float x,y,z;
CPoint(float _x,float _y,float _z):
x(_x),
y(_y),
z(_z)
{
}
bool operator== (const CPoint& _P) const {return((x==_P.x)&&(y==_P.y)&&(z==_P.z));}
};
/*
class CPointComparator
{
public:
bool operator()(const CPoint& _A,const CPoint& _B) const {return((_A.x==_B.x)&&(_A.y==_B.y)&&(_A.z==_B.z));}
};
*/
// Fixed memory version of pointToLeaf that doesn't use STL
// cuts down on memory fragmentation
struct PointAndLeaf
{
// Default constructor for array construction below
PointAndLeaf() : point(0, 0, 0), leaf(0) { }
CPoint point;
int leaf;
};
// I think it is a patholoically bad idea to do a 64 item linear search for a cache,
// so I reduced this to something more manageable.
// hopefully getting rid of water checks on maps with no water will leave us less
// reliant on this cache. -gwg
#define MAX_POINTS_TO_LEAVES 16
static PointAndLeaf pointToLeaf[MAX_POINTS_TO_LEAVES];
static int oldestPointToLeaf = 0, sizePointToLeaf = 0;
//static hlist<pair<CPoint,int> > pointToLeaf;
//static hlist<pair<CPoint,int> > pointToContents;
void CM_CleanLeafCache(void)
{
oldestPointToLeaf = sizePointToLeaf = 0;
// pointToLeaf.clear();
#if 0 // VVFIXME
hlist<pair<CPoint,int> >::iterator l;
for(l=pointToLeaf.begin();l!=pointToLeaf.end();l++)
{
pointToLeaf.erase(l);
}
#endif
/*
for(l=pointToContents.begin();l!=pointToContents.end();l++)
{
pointToContents.erase(l);
}
*/
}
/*
==================
CM_PointLeafnum_r
==================
*/
int CM_PointLeafnum_r( const vec3_t p, int num, clipMap_t *local ) {
float d;
cNode_t *node;
cplane_t *plane;
#ifdef _XBOX
if(!tr.world) {
return 0;
}
#endif
while (num >= 0)
{
node = local->nodes + num;
#ifdef _XBOX
plane = cmg.planes + tr.world->nodes[num].planeNum;
#else
plane = node->plane;
#endif
if (plane->type < 3)
d = p[plane->type] - plane->dist;
else
d = DotProduct (plane->normal, p) - plane->dist;
if (d < 0)
num = node->children[1];
else
num = node->children[0];
}
c_pointcontents++; // optimize counter
return -1 - num;
}
int CM_PointLeafnum( const vec3_t p ) {
if ( !cmg.numNodes ) { // map not loaded
return 0;
}
return CM_PointLeafnum_r (p, 0, &cmg);
}
/*
======================================================================
LEAF LISTING
======================================================================
*/
void CM_StoreLeafs( leafList_t *ll, int nodenum ) {
int leafNum;
leafNum = -1 - nodenum;
// store the lastLeaf even if the list is overflowed
if ( cmg.leafs[ leafNum ].cluster != -1 ) {
ll->lastLeaf = leafNum;
}
if ( ll->count >= ll->maxcount) {
ll->overflowed = qtrue;
return;
}
ll->list[ ll->count++ ] = leafNum;
}
void CM_StoreBrushes( leafList_t *ll, int nodenum ) {
int i, k;
int leafnum;
int brushnum;
cLeaf_t *leaf;
cbrush_t *b;
leafnum = -1 - nodenum;
leaf = &cmg.leafs[leafnum];
for ( k = 0 ; k < leaf->numLeafBrushes ; k++ ) {
brushnum = cmg.leafbrushes[leaf->firstLeafBrush+k];
b = &cmg.brushes[brushnum];
if ( b->checkcount == cmg.checkcount ) {
continue; // already checked this brush in another leaf
}
b->checkcount = cmg.checkcount;
for ( i = 0 ; i < 3 ; i++ ) {
if ( b->bounds[0][i] >= ll->bounds[1][i] || b->bounds[1][i] <= ll->bounds[0][i] ) {
break;
}
}
if ( i != 3 ) {
continue;
}
if ( ll->count >= ll->maxcount) {
ll->overflowed = qtrue;
return;
}
((cbrush_t **)ll->list)[ ll->count++ ] = b;
}
#if 0
// store patches?
for ( k = 0 ; k < leaf->numLeafSurfaces ; k++ ) {
patch = cmg.surfaces[ cmg.leafsurfaces[ leaf->firstleafsurface + k ] ];
if ( !patch ) {
continue;
}
}
#endif
}
/*
=============
CM_BoxLeafnums
Fills in a list of all the leafs touched
=============
*/
void CM_BoxLeafnums_r( leafList_t *ll, int nodenum ) {
cplane_t *plane;
cNode_t *node;
int s;
#ifdef _XBOX
if(!tr.world) {
return;
}
#endif
while (1) {
if (nodenum < 0) {
ll->storeLeafs( ll, nodenum );
return;
}
node = &cmg.nodes[nodenum];
#ifdef _XBOX
plane = cmg.planes + tr.world->nodes[nodenum].planeNum;
#else
plane = node->plane;
#endif
s = BoxOnPlaneSide( ll->bounds[0], ll->bounds[1], plane );
if (s == 1) {
nodenum = node->children[0];
} else if (s == 2) {
nodenum = node->children[1];
} else {
// go down both
CM_BoxLeafnums_r( ll, node->children[0] );
nodenum = node->children[1];
}
}
}
/*
==================
CM_BoxLeafnums
==================
*/
int CM_BoxLeafnums( const vec3_t mins, const vec3_t maxs, int *boxList, int listsize, int *lastLeaf) {
leafList_t ll;
cmg.checkcount++;
VectorCopy( mins, ll.bounds[0] );
VectorCopy( maxs, ll.bounds[1] );
ll.count = 0;
ll.maxcount = listsize;
ll.list = boxList;
ll.storeLeafs = CM_StoreLeafs;
ll.lastLeaf = 0;
ll.overflowed = qfalse;
CM_BoxLeafnums_r( &ll, 0 );
*lastLeaf = ll.lastLeaf;
return ll.count;
}
/*
==================
CM_BoxBrushes
==================
*/
int CM_BoxBrushes( const vec3_t mins, const vec3_t maxs, cbrush_t **boxlist, int listsize ) {
leafList_t ll;
cmg.checkcount++;
VectorCopy( mins, ll.bounds[0] );
VectorCopy( maxs, ll.bounds[1] );
ll.count = 0;
ll.maxcount = listsize;
ll.list = (int *)boxlist;
ll.storeLeafs = CM_StoreBrushes;
ll.lastLeaf = 0;
ll.overflowed = qfalse;
CM_BoxLeafnums_r( &ll, 0 );
return ll.count;
}
//====================================================================
/*
==================
CM_PointContents
==================
*/
#if 1
int CM_PointContents( const vec3_t p, clipHandle_t model ) {
int leafnum=0;
int i, k;
int brushnum;
cLeaf_t *leaf;
cbrush_t *b;
int contents;
float d;
cmodel_t *clipm;
clipMap_t *local;
if (!cmg.numNodes) { // map not loaded
return 0;
}
if ( model ) {
clipm = CM_ClipHandleToModel( model, &local );
leaf = &clipm->leaf;
}
else
{
local = &cmg;
CPoint pt(p[0],p[1],p[2]);
/* map<CPoint,int,CPointComparator>::iterator l=pointToLeaf.find(pt);
if(l!=pointToLeaf.end())
{
leafnum=(*l).second;
}
else
{
if(pointToLeaf.size()>=64)
{
pointToLeaf.clear();
Com_Printf("Cleared cache\n");
}
leafnum=CM_PointLeafnum_r(p, 0);
pointToLeaf[pt]=leafnum;
}*/
int l = 0;
for ( ; l < sizePointToLeaf; ++l)
{
if (pointToLeaf[l].point == pt)
{
leafnum = pointToLeaf[l].leaf;
break;
}
}
if (l == sizePointToLeaf)
{ // Didn't find it
if (sizePointToLeaf < MAX_POINTS_TO_LEAVES)
{ // We're adding a new one, rather than replacing
sizePointToLeaf++;
}
else
{ // Put it in the "oldest" slot
l = oldestPointToLeaf++;
oldestPointToLeaf &= (MAX_POINTS_TO_LEAVES-1);
}
leafnum = CM_PointLeafnum_r(p, 0, local);
pointToLeaf[l].leaf = leafnum;
pointToLeaf[l].point = pt;
}
/*
hlist<pair<CPoint,int> >::iterator l;
for(l=pointToLeaf.begin();l!=pointToLeaf.end();l++)
{
if((*l).first==pt)
{
leafnum=(*l).second;
break;
}
}
if(l==pointToLeaf.end())
{
if(pointToLeaf.size()>=64)
{
pointToLeaf.pop_back();
}
leafnum=CM_PointLeafnum_r(p, 0, local);
pointToLeaf.push_front(pair<CPoint,int>(pt,leafnum));
}
*/
leaf = &local->leafs[leafnum];
}
contents = 0;
for (k=0 ; k<leaf->numLeafBrushes ; k++) {
brushnum = local->leafbrushes[leaf->firstLeafBrush+k];
b = &local->brushes[brushnum];
// see if the point is in the brush
for ( i = 0 ; i < b->numsides ; i++ ) {
#ifdef _XBOX
d = DotProduct( p,
cmg.planes[b->sides[i].planeNum.GetValue()].normal );
#else
d = DotProduct( p, b->sides[i].plane->normal );
#endif
// FIXME test for Cash
// if ( d >= b->sides[i].plane->dist ) {
#ifdef _XBOX
if ( d > cmg.planes[b->sides[i].planeNum.GetValue()].dist ) {
#else
if ( d > b->sides[i].plane->dist ) {
#endif
break;
}
}
if ( i == b->numsides ) {
contents |= b->contents;
#ifndef _XBOX // Removing terrain from Xbox
if(cmg.landScape && (contents & CONTENTS_TERRAIN) )
{
if(p[2] < cmg.landScape->GetWaterHeight())
{
contents |= cmg.landScape->GetWaterContents();
}
}
#endif
}
}
return contents;
}
#else
int CM_PointContents( const vec3_t p, clipHandle_t model ) {
int leafnum=0;
int i, k;
int brushnum;
cLeaf_t *leaf;
cbrush_t *b;
int contents;
float d;
cmodel_t *clipm;
if (!cmg.numNodes) { // map not loaded
return 0;
}
CPoint pt(p[0],p[1],p[2]);
if ( model )
{
clipm = CM_ClipHandleToModel( model );
leaf = &clipm->leaf;
}
else
{
hlist<pair<CPoint,int> >::iterator l;
for(l=pointToContents.begin();l!=pointToContents.end();l++)
{
if((*l).first==pt)
{
// Breakout early.
return((*l).second);
}
}
leafnum=CM_PointLeafnum_r(p, 0);
leaf = &cmg.leafs[leafnum];
}
contents = 0;
for (k=0 ; k<leaf->numLeafBrushes ; k++)
{
brushnum = cmg.leafbrushes[leaf->firstLeafBrush+k];
b = &cmg.brushes[brushnum];
// see if the point is in the brush
for ( i = 0 ; i < b->numsides ; i++ )
{
d = DotProduct( p, b->sides[i].plane->normal );
// FIXME test for Cash
// if ( d >= b->sides[i].plane->dist ) {
if ( d > b->sides[i].plane->dist )
{
break;
}
}
if ( i == b->numsides )
{
contents |= b->contents;
}
}
// Cache the result for next time.
if(!model)
{
if(pointToContents.size()>=64)
{
pointToContents.pop_back();
}
pointToContents.push_front(pair<CPoint,int>(pt,contents));
}
return contents;
}
#endif
/*
==================
CM_TransformedPointContents
Handles offseting and rotation of the end points for moving and
rotating entities
==================
*/
int CM_TransformedPointContents( const vec3_t p, clipHandle_t model, const vec3_t origin, const vec3_t angles) {
vec3_t p_l;
vec3_t temp;
vec3_t forward, right, up;
// subtract origin offset
VectorSubtract (p, origin, p_l);
// rotate start and end into the models frame of reference
if ( model != BOX_MODEL_HANDLE &&
(angles[0] || angles[1] || angles[2]) )
{
AngleVectors (angles, forward, right, up);
VectorCopy (p_l, temp);
p_l[0] = DotProduct (temp, forward);
p_l[1] = -DotProduct (temp, right);
p_l[2] = DotProduct (temp, up);
}
return CM_PointContents( p_l, model );
}
/*
===============================================================================
PVS
===============================================================================
*/
#ifdef _XBOX
extern trGlobals_t tr;
const byte *CM_ClusterPVS (int cluster) {
if (cluster < 0 || cluster >= cmg.numClusters || !cmg.vised ) {
return NULL;
}
return cmg.visibility->Decompress(cluster * cmg.clusterBytes,
cmg.numClusters);
}
#else
byte *CM_ClusterPVS (int cluster) {
if (cluster < 0 || cluster >= cmg.numClusters || !cmg.vised ) {
return cmg.visibility;
}
return cmg.visibility + cluster * cmg.clusterBytes;
}
#endif
/*
===============================================================================
AREAPORTALS
===============================================================================
*/
#ifdef _XBOX
void CM_FloodArea_r( int areaNum, int floodnum) {
int i;
cArea_t *area;
int *con;
area = &cmg.areas[ areaNum ];
if ( area->floodvalid == cmg.floodvalid ) {
if (area->floodnum == floodnum)
return;
Com_Error (ERR_DROP, "FloodArea_r: reflooded");
}
area->floodnum = floodnum;
area->floodvalid = cmg.floodvalid;
con = cmg.areaPortals + areaNum * cmg.numAreas;
for ( i=0 ; i < cmg.numAreas ; i++ ) {
if ( con[i] > 0 ) {
CM_FloodArea_r( i, floodnum );
}
}
}
#else // _XBOX
void CM_FloodArea_r( int areaNum, int floodnum, clipMap_t &cm) {
int i;
cArea_t *area;
int *con;
area = &cmg.areas[ areaNum ];
if ( area->floodvalid == cmg.floodvalid ) {
if (area->floodnum == floodnum)
return;
Com_Error (ERR_DROP, "FloodArea_r: reflooded");
}
area->floodnum = floodnum;
area->floodvalid = cmg.floodvalid;
con = cmg.areaPortals + areaNum * cmg.numAreas;
for ( i=0 ; i < cmg.numAreas ; i++ ) {
if ( con[i] > 0 ) {
CM_FloodArea_r( i, floodnum, cm );
}
}
}
#endif // XBOX
/*
====================
CM_FloodAreaConnections
====================
*/
#ifdef _XBOX
void CM_FloodAreaConnections( void ) {
int i;
cArea_t *area;
int floodnum;
// all current floods are now invalid
cmg.floodvalid++;
floodnum = 0;
for (i = 0 ; i < cmg.numAreas ; i++) {
area = &cmg.areas[i];
if (area->floodvalid == cmg.floodvalid) {
continue; // already flooded into
}
floodnum++;
CM_FloodArea_r (i, floodnum);
}
}
#else // _XBOX
void CM_FloodAreaConnections( clipMap_t &cm ) {
int i;
cArea_t *area;
int floodnum;
// all current floods are now invalid
cmg.floodvalid++;
floodnum = 0;
for (i = 0 ; i < cmg.numAreas ; i++) {
area = &cmg.areas[i];
if (area->floodvalid == cmg.floodvalid) {
continue; // already flooded into
}
floodnum++;
CM_FloodArea_r (i, floodnum, cm);
}
}
#endif // _XBOX
/*
====================
CM_AdjustAreaPortalState
====================
*/
void CM_AdjustAreaPortalState( int area1, int area2, qboolean open ) {
if ( area1 < 0 || area2 < 0 ) {
return;
}
if ( area1 >= cmg.numAreas || area2 >= cmg.numAreas ) {
Com_Error (ERR_DROP, "CM_ChangeAreaPortalState: bad area number");
}
if ( open ) {
cmg.areaPortals[ area1 * cmg.numAreas + area2 ]++;
cmg.areaPortals[ area2 * cmg.numAreas + area1 ]++;
} else {
cmg.areaPortals[ area1 * cmg.numAreas + area2 ]--;
cmg.areaPortals[ area2 * cmg.numAreas + area1 ]--;
if ( cmg.areaPortals[ area2 * cmg.numAreas + area1 ] < 0 ) {
Com_Error (ERR_DROP, "CM_AdjustAreaPortalState: negative reference count");
}
}
#ifdef _XBOX
CM_FloodAreaConnections ();
#else
CM_FloodAreaConnections (cmg);
#endif
}
/*
====================
CM_AreasConnected
====================
*/
qboolean CM_AreasConnected( int area1, int area2 ) {
#ifndef BSPC
if ( cm_noAreas->integer ) {
return qtrue;
}
#endif
if ( area1 < 0 || area2 < 0 ) {
return qfalse;
}
if (area1 >= cmg.numAreas || area2 >= cmg.numAreas) {
Com_Error (ERR_DROP, "area >= cmg.numAreas");
}
if (cmg.areas[area1].floodnum == cmg.areas[area2].floodnum) {
return qtrue;
}
return qfalse;
}
/*
=================
CM_WriteAreaBits
Writes a bit vector of all the areas
that are in the same flood as the area parameter
Returns the number of bytes needed to hold all the bits.
The bits are OR'd in, so you can CM_WriteAreaBits from multiple
viewpoints and get the union of all visible areas.
This is used to cull non-visible entities from snapshots
=================
*/
int CM_WriteAreaBits (byte *buffer, int area)
{
int i;
int floodnum;
int bytes;
bytes = (cmg.numAreas+7)>>3;
#ifndef BSPC
if (cm_noAreas->integer || area == -1)
#else
if ( area == -1)
#endif
{ // for debugging, send everything
memset (buffer, 255, bytes);
}
else
{
floodnum = cmg.areas[area].floodnum;
for (i=0 ; i<cmg.numAreas ; i++)
{
if (cmg.areas[i].floodnum == floodnum || area == -1)
buffer[i>>3] |= 1<<(i&7);
}
}
return bytes;
}
void CM_SnapPVS(vec3_t origin,byte *buffer)
{
int clientarea;
int leafnum;
int i;
leafnum = CM_PointLeafnum (origin);
clientarea = CM_LeafArea (leafnum);
// calculate the visible areas
memset(buffer,0,MAX_MAP_AREA_BYTES);
CM_WriteAreaBits(buffer,clientarea);
for ( i = 0 ; i < MAX_MAP_AREA_BYTES/4 ; i++ ) {
((int *)buffer)[i] = ((int *)buffer)[i] ^ -1;
}
}

1223
code/qcommon/cm_trace.cpp Normal file

File diff suppressed because it is too large Load Diff

722
code/qcommon/cmd.cpp Normal file
View File

@@ -0,0 +1,722 @@
// cmd.c -- Quake script command processing module
#include "../game/q_shared.h"
#include "qcommon.h"
#define MAX_CMD_BUFFER 8192
int cmd_wait;
msg_t cmd_text;
byte cmd_text_buf[MAX_CMD_BUFFER];
char cmd_defer_text_buf[MAX_CMD_BUFFER];
//=============================================================================
/*
============
Cmd_Wait_f
Causes execution of the remainder of the command buffer to be delayed until
next frame. This allows commands like:
bind g "cmd use rocket ; +attack ; wait ; -attack ; cmd use blaster"
============
*/
void Cmd_Wait_f( void ) {
if ( Cmd_Argc() == 2 ) {
cmd_wait = atoi( Cmd_Argv( 1 ) );
} else {
cmd_wait = 1;
}
}
/*
=============================================================================
COMMAND BUFFER
=============================================================================
*/
/*
============
Cbuf_Init
============
*/
void Cbuf_Init (void)
{
MSG_Init (&cmd_text, cmd_text_buf, sizeof(cmd_text_buf));
}
/*
============
Cbuf_AddText
Adds command text at the end of the buffer, does NOT add a final \n
============
*/
void Cbuf_AddText( const char *text ) {
int l;
l = strlen (text);
if (cmd_text.cursize + l >= cmd_text.maxsize)
{
Com_Printf ("Cbuf_AddText: overflow\n");
return;
}
MSG_WriteData (&cmd_text, text, strlen (text));
}
/*
============
Cbuf_InsertText
Adds command text immediately after the current command
Adds a \n to the text
============
*/
void Cbuf_InsertText( const char *text ) {
int len;
int i;
len = strlen( text ) + 1;
if ( len + cmd_text.cursize > cmd_text.maxsize ) {
Com_Printf( "Cbuf_InsertText overflowed\n" );
return;
}
// move the existing command text
for ( i = cmd_text.cursize - 1 ; i >= 0 ; i-- ) {
cmd_text.data[ i + len ] = cmd_text.data[ i ];
}
// copy the new text in
memcpy( cmd_text.data, text, len - 1 );
// add a \n
cmd_text.data[ len - 1 ] = '\n';
cmd_text.cursize += len;
}
/*
============
Cbuf_ExecuteText
============
*/
void Cbuf_ExecuteText (int exec_when, const char *text)
{
switch (exec_when)
{
case EXEC_NOW:
Cmd_ExecuteString (text);
break;
case EXEC_INSERT:
Cbuf_InsertText (text);
break;
case EXEC_APPEND:
Cbuf_AddText (text);
break;
default:
Com_Error (ERR_FATAL, "Cbuf_ExecuteText: bad exec_when");
}
}
/*
============
Cbuf_Execute
============
*/
void Cbuf_Execute (void)
{
int i;
char *text;
char line[MAX_CMD_BUFFER];
int quotes;
while (cmd_text.cursize)
{
if ( cmd_wait ) {
// skip out while text still remains in buffer, leaving it
// for next frame
cmd_wait--;
break;
}
// find a \n or ; line break
text = (char *)cmd_text.data;
quotes = 0;
for (i=0 ; i< cmd_text.cursize ; i++)
{
if (text[i] == '"')
quotes++;
if ( !(quotes&1) && text[i] == ';')
break; // don't break if inside a quoted string
if (text[i] == '\n' || text[i] == '\r' )
break;
}
memcpy (line, text, i);
line[i] = 0;
// delete the text from the command buffer and move remaining commands down
// this is necessary because commands (exec) can insert data at the
// beginning of the text buffer
if (i == cmd_text.cursize)
cmd_text.cursize = 0;
else
{
i++;
cmd_text.cursize -= i;
memmove (text, text+i, cmd_text.cursize);
}
// execute the command line
Cmd_ExecuteString (line);
}
}
/*
==============================================================================
SCRIPT COMMANDS
==============================================================================
*/
/*
===============
Cmd_Exec_f
===============
*/
void Cmd_Exec_f( void ) {
char *f;
int len;
char filename[MAX_QPATH];
if (Cmd_Argc () != 2) {
Com_Printf ("exec <filename> : execute a script file\n");
return;
}
Q_strncpyz( filename, Cmd_Argv(1), sizeof( filename ) );
COM_DefaultExtension( filename, sizeof( filename ), ".cfg" );
len = FS_ReadFile( filename, (void **)&f);
if (!f) {
Com_Printf ("couldn't exec %s\n",Cmd_Argv(1));
return;
}
Com_Printf ("execing %s\n",Cmd_Argv(1));
Cbuf_InsertText (f);
FS_FreeFile (f);
}
/*
===============
Cmd_Vstr_f
Inserts the current value of a variable as command text
===============
*/
void Cmd_Vstr_f( void ) {
char *v;
if (Cmd_Argc () != 2) {
Com_Printf ("vstr <variablename> : execute a variable command\n");
return;
}
v = Cvar_VariableString( Cmd_Argv( 1 ) );
Cbuf_InsertText( va("%s\n", v ) );
}
/*
===============
Cmd_Echo_f
Just prints the rest of the line to the console
===============
*/
void Cmd_Echo_f (void)
{
int i;
for (i=1 ; i<Cmd_Argc() ; i++)
Com_Printf ("%s ",Cmd_Argv(i));
Com_Printf ("\n");
}
/*
=============================================================================
COMMAND EXECUTION
=============================================================================
*/
#define CMD_MAX_NUM 256
#define CMD_MAX_NAME 32
typedef struct cmd_function_s
{
char name[CMD_MAX_NAME];
xcommand_t function;
} cmd_function_t;
static int cmd_argc;
static char *cmd_argv[MAX_STRING_TOKENS]; // points into cmd_tokenized
static char cmd_tokenized[MAX_STRING_CHARS+MAX_STRING_TOKENS]; // will have 0 bytes inserted
static cmd_function_t cmd_functions[CMD_MAX_NUM] = {0}; // possible commands to execute
/*
============
Cmd_Argc
============
*/
int Cmd_Argc( void ) {
return cmd_argc;
}
/*
============
Cmd_Argv
============
*/
char *Cmd_Argv( int arg ) {
if ( (unsigned)arg >= cmd_argc ) {
return "";
}
return cmd_argv[arg];
}
/*
============
Cmd_ArgvBuffer
The interpreted versions use this because
they can't have pointers returned to them
============
*/
void Cmd_ArgvBuffer( int arg, char *buffer, int bufferLength ) {
Q_strncpyz( buffer, Cmd_Argv( arg ), bufferLength );
}
/*
============
Cmd_Args
Returns a single string containing argv(1) to argv(argc()-1)
============
*/
char *Cmd_Args( void ) {
static char cmd_args[MAX_STRING_CHARS];
int i;
cmd_args[0] = 0;
for ( i = 1 ; i < cmd_argc ; i++ ) {
strcat( cmd_args, cmd_argv[i] );
if ( i != cmd_argc ) {
strcat( cmd_args, " " );
}
}
return cmd_args;
}
/*
============
Cmd_ArgsBuffer
The interpreted versions use this because
they can't have pointers returned to them
============
*/
void Cmd_ArgsBuffer( char *buffer, int bufferLength ) {
Q_strncpyz( buffer, Cmd_Args(), bufferLength );
}
/*
============
Cmd_TokenizeString
Parses the given string into command line tokens.
The text is copied to a seperate buffer and 0 characters
are inserted in the apropriate place, The argv array
will point into this temporary buffer.
============
*/
void Cmd_TokenizeString( const char *text_in ) {
const unsigned char *text;
char *textOut;
// clear previous args
cmd_argc = 0;
if ( !text_in ) {
return;
}
text = (const unsigned char *)text_in;
textOut = cmd_tokenized;
while ( 1 ) {
if ( cmd_argc == MAX_STRING_TOKENS ) {
return; // this is usually something malicious
}
while ( 1 ) {
// skip whitespace
while ( *text && *text <= ' ' ) {
text++;
}
if ( !*text ) {
return; // all tokens parsed
}
// skip // comments
if ( text[0] == '/' && text[1] == '/' ) {
return; // all tokens parsed
}
// skip /* */ comments
if ( text[0] == '/' && text[1] =='*' ) {
while ( *text && ( text[0] != '*' || text[1] != '/' ) ) {
text++;
}
if ( !*text ) {
return; // all tokens parsed
}
text += 2;
} else {
break; // we are ready to parse a token
}
}
// handle quoted strings
if ( *text == '"' ) {
cmd_argv[cmd_argc] = textOut;
cmd_argc++;
text++;
while ( *text && *text != '"' ) {
*textOut++ = *text++;
}
*textOut++ = 0;
if ( !*text ) {
return; // all tokens parsed
}
text++;
continue;
}
// regular token
cmd_argv[cmd_argc] = textOut;
cmd_argc++;
// skip until whitespace, quote, or command
while ( *text > ' ' ) {
if ( text[0] == '"' ) {
break;
}
if ( text[0] == '/' && text[1] == '/' ) {
break;
}
// skip /* */ comments
if ( text[0] == '/' && text[1] =='*' ) {
break;
}
*textOut++ = *text++;
}
*textOut++ = 0;
if ( !*text ) {
return; // all tokens parsed
}
}
}
/*
============
Cmd_AddCommand
============
*/
void Cmd_AddCommand( const char *cmd_name, xcommand_t function ) {
cmd_function_t *cmd;
cmd_function_t *add = NULL;
int c;
// fail if the command already exists
for ( c = 0; c < CMD_MAX_NUM; ++c )
{
cmd = cmd_functions + c;
if ( !strcmp( cmd_name, cmd->name ) ) {
// allow completion-only commands to be silently doubled
if ( function != NULL ) {
Com_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
}
return;
}
if ( add == NULL && cmd->name[0] == '\0')
{
add = cmd;
}
}
if ( add == NULL )
{
Com_Printf ("Cmd_AddCommand: Too many commands registered\n", cmd_name);
return;
}
Q_strncpyz(add->name, cmd_name, CMD_MAX_NAME, qtrue);
add->function = function;
}
/*
============
Cmd_RemoveCommand
============
*/
void Cmd_RemoveCommand( const char *cmd_name ) {
cmd_function_t *cmd;
for ( int c = 0; c < CMD_MAX_NUM; ++c )
{
cmd = cmd_functions + c;
if ( !strcmp( cmd_name, cmd->name ) ) {
cmd->name[0] = '\0';
return;
}
}
}
char *Cmd_CompleteCommandNext (char *partial, char *last)
{
cmd_function_t *cmd, *base;
int len, c;
len = strlen(partial);
if (!len)
return NULL;
// start past last match
base = NULL;
if(last)
{
for (c = 0; c < CMD_MAX_NUM; ++c)
{
cmd = cmd_functions + c;
if(!strcmp(last, cmd->name))
{
base = cmd + 1;
break;
}
}
if(base == NULL)
{ //not found, either error or at end of list
return NULL;
}
}
else
{
base = cmd_functions;
}
for (c = base - cmd_functions; c < CMD_MAX_NUM; ++c)
{
cmd = cmd_functions + c;
if (!strcmp (partial,cmd->name))
return cmd->name;
}
// check for partial match
for (c = base - cmd_functions; c < CMD_MAX_NUM; ++c)
{
cmd = cmd_functions + c;
if (!strncmp (partial,cmd->name, len))
return cmd->name;
}
return NULL;
}
/*
============
Cmd_CompleteCommand
============
*/
char *Cmd_CompleteCommand( const char *partial ) {
cmd_function_t *cmd;
int len;
len = strlen(partial);
if (!len)
return NULL;
// check for exact match
for (int c = 0; c < CMD_MAX_NUM; ++c)
{
cmd = cmd_functions + c;
if (!Q_stricmp( partial, cmd->name))
return cmd->name;
}
// check for partial match
for (int c = 0; c < CMD_MAX_NUM; ++c)
{
cmd = cmd_functions + c;
if (!Q_stricmpn (partial,cmd->name, len))
return cmd->name;
}
return NULL;
}
/*
============
Cmd_ExecuteString
A complete command line has been parsed, so try to execute it
============
*/
extern void Key_SetCatcher( int catcher );
extern void Menus_CloseAll(void);
void Cmd_ExecuteString( const char *text ) {
// execute the command line
Cmd_TokenizeString( text );
if ( !Cmd_Argc() ) {
return; // no tokens
}
cvar_t* levelSelectCheat = Cvar_Get("levelSelectCheat", "-1", CVAR_SAVEGAME);
if( (!strcmp(text,"use end_level") || strstr(text, "maptransition") || !strcmp(text, "uimenu ingameMissionSelect") || !strcmp(text, "uimenu ingameGotoTier")) && // level end
levelSelectCheat->integer != -1 ) // was cheating
{
Cbuf_ExecuteText( EXEC_APPEND, "disconnect\n" ); // disconnect the player
Key_SetCatcher( KEYCATCH_UI ); // set them in the ui
Menus_CloseAll(); // close all the menus
Cvar_Set("levelSelectCheat", "-1");
return;
}
// check registered command functions
for ( int c = 0; c < CMD_MAX_NUM; ++c )
{
if ( !Q_stricmp( cmd_argv[0],cmd_functions[c].name ) ) {
// rearrange the links so that the command will be
// near the head of the list next time it is used
cmd_function_t temp = cmd_functions[c];
cmd_functions[c] = cmd_functions[0];
cmd_functions[0] = temp;
// perform the action
if ( !temp.function ) {
// let the cgame or game handle it
break;
} else {
temp.function ();
}
return;
}
}
// check cvars
if ( Cvar_Command() ) {
return;
}
// check client game commands
if ( com_cl_running && com_cl_running->integer && CL_GameCommand() ) {
return;
}
// check server game commands
if ( com_sv_running && com_sv_running->integer && SV_GameCommand() ) {
return;
}
// check ui commands
if ( com_cl_running && com_cl_running->integer && UI_GameCommand() ) {
return;
}
// send it as a server command if we are connected
// this will usually result in a chat message
CL_ForwardCommandToServer ();
}
/*
============
Cmd_List_f
============
*/
void Cmd_List_f (void)
{
cmd_function_t *cmd;
int i;
char *match;
if ( Cmd_Argc() > 1 ) {
match = Cmd_Argv( 1 );
} else {
match = NULL;
}
i = 0;
for ( int c = 0; c < CMD_MAX_NUM; ++c )
{
cmd = cmd_functions + c;
if (match && !Com_Filter(match, cmd->name, qfalse)) continue;
Com_Printf ("%s\n", cmd->name);
i++;
}
Com_Printf ("%i commands\n", i);
}
/*
============
Cmd_Init
============
*/
void Cmd_Init (void)
{
//
// register our commands
//
Cmd_AddCommand ("cmdlist",Cmd_List_f);
Cmd_AddCommand ("exec",Cmd_Exec_f);
Cmd_AddCommand ("vstr",Cmd_Vstr_f);
Cmd_AddCommand ("echo",Cmd_Echo_f);
Cmd_AddCommand ("wait", Cmd_Wait_f);
}

1662
code/qcommon/common.cpp Normal file

File diff suppressed because it is too large Load Diff

951
code/qcommon/cvar.cpp Normal file
View File

@@ -0,0 +1,951 @@
// cvar.c -- dynamic variable tracking
#include "../game/q_shared.h"
#include "qcommon.h"
cvar_t *cvar_vars;
cvar_t *cvar_cheats;
int cvar_modifiedFlags;
#define MAX_CVARS 1024
cvar_t cvar_indexes[MAX_CVARS];
int cvar_numIndexes;
cvar_t *Cvar_Set2( const char *var_name, const char *value, qboolean force);
static char *lastMemPool = NULL;
static int memPoolSize;
//If the string came from the memory pool, don't really free it. The entire
//memory pool will be wiped during the next level load.
static void Cvar_FreeString(char *string)
{
if(!lastMemPool || string < lastMemPool ||
string >= lastMemPool + memPoolSize) {
Z_Free(string);
}
}
/*
============
Cvar_ValidateString
============
*/
static qboolean Cvar_ValidateString( const char *s ) {
if ( !s ) {
return qfalse;
}
if ( strchr( s, '\\' ) ) {
return qfalse;
}
if ( strchr( s, '\"' ) ) {
return qfalse;
}
if ( strchr( s, ';' ) ) {
return qfalse;
}
return qtrue;
}
/*
============
Cvar_FindVar
============
*/
static cvar_t *Cvar_FindVar( const char *var_name ) {
cvar_t *var;
for (var=cvar_vars ; var ; var=var->next) {
if (!Q_stricmp(var_name, var->name)) {
return var;
}
}
return NULL;
}
/*
============
Cvar_VariableValue
============
*/
float Cvar_VariableValue( const char *var_name ) {
cvar_t *var;
var = Cvar_FindVar (var_name);
if (!var)
return 0;
return var->value;
}
/*
============
Cvar_VariableIntegerValue
============
*/
int Cvar_VariableIntegerValue( const char *var_name ) {
cvar_t *var;
var = Cvar_FindVar (var_name);
if (!var)
return 0;
return var->integer;
}
/*
============
Cvar_VariableString
============
*/
char *Cvar_VariableString( const char *var_name ) {
cvar_t *var;
var = Cvar_FindVar (var_name);
if (!var)
return "";
return var->string;
}
/*
============
Cvar_VariableStringBuffer
============
*/
void Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ) {
cvar_t *var;
var = Cvar_FindVar (var_name);
if (!var) {
*buffer = 0;
}
else {
Q_strncpyz( buffer, var->string, bufsize );
}
}
/*
============
Cvar_CompleteVariable
============
*/
char *Cvar_CompleteVariable( const char *partial ) {
cvar_t *cvar;
int len;
len = strlen(partial);
if ( !len ) {
return NULL;
}
// check partial match
for ( cvar=cvar_vars ; cvar ; cvar=cvar->next ) {
if ( !Q_stricmpn (partial,cvar->name, len) ) {
if ( (cvar->flags & CVAR_CHEAT) && !cvar_cheats->integer ) {
continue;
}
else {
return cvar->name;
}
}
}
return NULL;
}
/*
============
Cvar_CompleteVariableNext - get the next cvar in alphabetical order.
============
*/
char *Cvar_CompleteVariableNext (char *partial, char *last)
{
cvar_t *cvar, *base;
int len;
len = strlen(partial);
if (!len)
return NULL;
// this check needed since cvars may be resetting from cmd searches
base = NULL;
if(last)
{
for (cvar=cvar_vars; cvar; cvar = cvar->next)
{
if(!Q_stricmp(last, cvar->name))
{
base = cvar->next;
break;
}
}
if(base == NULL)
{ //not found, either error or at end of list
return NULL;
}
}
else
{
base = cvar_vars;
}
// check partial match
for (cvar=base ; cvar ; cvar=cvar->next)
{
if (!Q_stricmpn (partial,cvar->name, len)) {
if ( (cvar->flags & CVAR_CHEAT) && !cvar_cheats->integer ) {
continue;
}
else {
return cvar->name;
}
}
}
return NULL;
}
/*
============
Cvar_Get
If the variable already exists, the value will not be set unless CVAR_ROM
The flags will be or'ed in if the variable exists.
============
*/
cvar_t *Cvar_Get( const char *var_name, const char *var_value, int flags ) {
cvar_t *var;
if ( !var_name || ! var_value ) {
Com_Error( ERR_FATAL, "Cvar_Get: NULL parameter" );
}
if ( !Cvar_ValidateString( var_name ) ) {
Com_Printf("invalid cvar name string: %s\n", var_name );
var_name = "BADNAME";
}
#if 0 // FIXME: values with backslash happen
if ( !Cvar_ValidateString( var_value ) ) {
Com_Printf("invalid cvar value string: %s\n", var_value );
var_value = "BADVALUE";
}
#endif
var = Cvar_FindVar (var_name);
if ( var ) {
// if the C code is now specifying a variable that the user already
// set a value for, take the new value as the reset value
if ( ( var->flags & CVAR_USER_CREATED ) && !( flags & CVAR_USER_CREATED )
&& var_value[0] ) {
var->flags &= ~CVAR_USER_CREATED;
Cvar_FreeString( var->resetString );
var->resetString = CopyString( var_value );
// ZOID--needs to be set so that cvars the game sets as
// SERVERINFO get sent to clients
cvar_modifiedFlags |= flags;
}
var->flags |= flags;
// only allow one non-empty reset string without a warning
if ( !var->resetString[0] ) {
// we don't have a reset string yet
Cvar_FreeString( var->resetString );
var->resetString = CopyString( var_value );
} else if ( var_value[0] && strcmp( var->resetString, var_value ) ) {
Com_Printf( "Warning: cvar \"%s\" given initial values: \"%s\" and \"%s\"\n",
var_name, var->resetString, var_value );
}
// if we have a latched string, take that value now
if ( var->latchedString ) {
char *s;
s = var->latchedString;
var->latchedString = NULL; // otherwise cvar_set2 would free it
Cvar_Set2( var_name, s, qtrue );
Cvar_FreeString( s );
}
// use a CVAR_SET for rom sets, get won't override
#if 0
// CVAR_ROM always overrides
if ( flags & CVAR_ROM ) {
Cvar_Set2( var_name, var_value, qtrue );
}
#endif
return var;
}
//
// allocate a new cvar
//
if ( cvar_numIndexes == MAX_CVARS ) {
Com_Error( ERR_FATAL, "MAX_CVARS" );
}
var = &cvar_indexes[cvar_numIndexes];
cvar_numIndexes++;
var->name = CopyString (var_name);
var->string = CopyString (var_value);
var->modified = qtrue;
var->modificationCount = 1;
var->value = atof (var->string);
var->integer = atoi(var->string);
var->resetString = CopyString( var_value );
// link the variable in
var->next = cvar_vars;
cvar_vars = var;
var->flags = flags;
return var;
}
/*
============
Cvar_Set2
============
*/
cvar_t *Cvar_Set2( const char *var_name, const char *value, qboolean force ) {
cvar_t *var;
Com_DPrintf( "Cvar_Set2: %s %s\n", var_name, value );
if ( !Cvar_ValidateString( var_name ) ) {
Com_Printf("invalid cvar name string: %s\n", var_name );
var_name = "BADNAME";
}
#if 0 // FIXME
if ( value && !Cvar_ValidateString( value ) ) {
Com_Printf("invalid cvar value string: %s\n", value );
var_value = "BADVALUE";
}
#endif
var = Cvar_FindVar (var_name);
if (!var) {
if ( !value ) {
return NULL;
}
// create it
if ( !force ) {
return Cvar_Get( var_name, value, CVAR_USER_CREATED );
} else {
return Cvar_Get (var_name, value, 0);
}
}
if (!value ) {
value = var->resetString;
}
// note what types of cvars have been modified (userinfo, archive, serverinfo, systeminfo)
cvar_modifiedFlags |= var->flags;
if (!force)
{
if (var->flags & CVAR_ROM)
{
Com_Printf ("%s is read only.\n", var_name);
return var;
}
if (var->flags & CVAR_INIT)
{
Com_Printf ("%s is write protected.\n", var_name);
return var;
}
if (var->flags & CVAR_LATCH)
{
if (var->latchedString)
{
if (strcmp(value, var->latchedString) == 0)
return var;
Cvar_FreeString (var->latchedString);
}
else
{
if (strcmp(value, var->string) == 0)
return var;
}
Com_Printf ("%s will be changed upon restarting.\n", var_name);
var->latchedString = CopyString(value);
var->modified = qtrue;
var->modificationCount++;
return var;
}
if ( (var->flags & CVAR_CHEAT) && !cvar_cheats->integer )
{
Com_Printf ("%s is cheat protected.\n", var_name);
return var;
}
}
else
{
if (var->latchedString)
{
Cvar_FreeString (var->latchedString);
var->latchedString = NULL;
}
}
if (!strcmp(value, var->string))
return var; // not changed
var->modified = qtrue;
var->modificationCount++;
Cvar_FreeString (var->string); // free the old value string
var->string = CopyString(value);
var->value = atof (var->string);
var->integer = atoi (var->string);
return var;
}
/*
============
Cvar_Set
============
*/
void Cvar_Set( const char *var_name, const char *value) {
Cvar_Set2 (var_name, value, qtrue);
}
/*
============
Cvar_SetValue
============
*/
void Cvar_SetValue( const char *var_name, float value) {
char val[32];
if ( value == (int)value ) {
Com_sprintf (val, sizeof(val), "%i",(int)value);
} else {
Com_sprintf (val, sizeof(val), "%f",value);
}
Cvar_Set (var_name, val);
}
/*
============
Cvar_Reset
============
*/
void Cvar_Reset( const char *var_name ) {
Cvar_Set2( var_name, NULL, qfalse );
}
/*
============
Cvar_SetCheatState
Any testing variables will be reset to the safe values
============
*/
void Cvar_SetCheatState( void ) {
cvar_t *var;
// set all default vars to the safe value
for ( var = cvar_vars ; var ; var = var->next ) {
if ( var->flags & CVAR_CHEAT) {
Cvar_Set( var->name, var->resetString );
}
}
}
/*
============
Cvar_Command
Handles variable inspection and changing from the console
============
*/
qboolean Cvar_Command( void ) {
cvar_t *v;
// check variables
v = Cvar_FindVar (Cmd_Argv(0));
if (!v) {
return qfalse;
}
// perform a variable print or set
if ( Cmd_Argc() == 1 ) {
Com_Printf ("\"%s\" is:\"%s" S_COLOR_WHITE "\" default:\"%s" S_COLOR_WHITE "\"\n", v->name, v->string, v->resetString );
if ( v->latchedString ) {
Com_Printf( "latched: \"%s\"\n", v->latchedString );
}
return qtrue;
}
//JFM toggle test
char *value;
value = Cmd_Argv(1);
if (value[0] =='!') //toggle
{
char buff[5];
sprintf(buff,"%i",!v->value);
Cvar_Set2 (v->name, buff, qfalse);// toggle the value
}
else
Cvar_Set2 (v->name, value, qfalse);// set the value if forcing isn't required
return qtrue;
}
/*
============
Cvar_Toggle_f
Toggles a cvar for easy single key binding
============
*/
void Cvar_Toggle_f( void ) {
int v;
if ( Cmd_Argc() != 2 ) {
Com_Printf ("usage: toggle <variable>\n");
return;
}
v = Cvar_VariableIntegerValue( Cmd_Argv( 1 ) );
v = !v;
Cvar_Set2 (Cmd_Argv(1), va("%i", v), qfalse);
}
/*
============
Cvar_Set_f
Allows setting and defining of arbitrary cvars from console, even if they
weren't declared in C code.
============
*/
void Cvar_Set_f( void ) {
int i, c, l, len;
char combined[MAX_STRING_TOKENS];
c = Cmd_Argc();
if ( c < 3 ) {
Com_Printf ("usage: set <variable> <value>\n");
return;
}
combined[0] = 0;
l = 0;
for ( i = 2 ; i < c ; i++ ) {
len = strlen ( Cmd_Argv( i ) + 1 );
if ( l + len >= MAX_STRING_TOKENS - 2 ) {
break;
}
strcat( combined, Cmd_Argv( i ) );
if ( i != c-1 ) {
strcat( combined, " " );
}
l += len;
}
Cvar_Set2 (Cmd_Argv(1), combined, qfalse);
}
/*
============
Cvar_SetU_f
As Cvar_Set, but also flags it as userinfo
============
*/
void Cvar_SetU_f( void ) {
cvar_t *v;
if ( Cmd_Argc() != 3 ) {
Com_Printf ("usage: setu <variable> <value>\n");
return;
}
Cvar_Set_f();
v = Cvar_FindVar( Cmd_Argv( 1 ) );
if ( !v ) {
return;
}
v->flags |= CVAR_USERINFO;
}
/*
============
Cvar_SetS_f
As Cvar_Set, but also flags it as userinfo
============
*/
void Cvar_SetS_f( void ) {
cvar_t *v;
if ( Cmd_Argc() != 3 ) {
Com_Printf ("usage: sets <variable> <value>\n");
return;
}
Cvar_Set_f();
v = Cvar_FindVar( Cmd_Argv( 1 ) );
if ( !v ) {
return;
}
v->flags |= CVAR_SERVERINFO;
}
/*
============
Cvar_SetA_f
As Cvar_Set, but also flags it as archived
============
*/
void Cvar_SetA_f( void ) {
cvar_t *v;
if ( Cmd_Argc() != 3 ) {
Com_Printf ("usage: seta <variable> <value>\n");
return;
}
Cvar_Set_f();
v = Cvar_FindVar( Cmd_Argv( 1 ) );
if ( !v ) {
return;
}
v->flags |= CVAR_ARCHIVE;
}
/*
============
Cvar_Reset_f
============
*/
void Cvar_Reset_f( void ) {
if ( Cmd_Argc() != 2 ) {
Com_Printf ("usage: reset <variable>\n");
return;
}
Cvar_Reset( Cmd_Argv( 1 ) );
}
/*
============
Cvar_WriteVariables
Appends lines containing "set variable value" for all variables
with the archive flag set to qtrue.
============
*/
void Cvar_WriteVariables( fileHandle_t f ) {
#ifndef _XBOX
cvar_t *var;
char buffer[1024];
for (var = cvar_vars ; var ; var = var->next) {
if (var->flags & CVAR_ARCHIVE ) {
// write the latched value, even if it hasn't taken effect yet
if ( var->latchedString ) {
Com_sprintf (buffer, sizeof(buffer), "seta %s \"%s\"\n", var->name, var->latchedString);
} else {
Com_sprintf (buffer, sizeof(buffer), "seta %s \"%s\"\n", var->name, var->string);
}
FS_Printf (f, "%s", buffer);
}
}
#endif
}
/*
============
Cvar_List_f
============
*/
void Cvar_List_f( void ) {
cvar_t *var;
int i;
char *match;
if ( Cmd_Argc() > 1 ) {
match = Cmd_Argv( 1 );
} else {
match = NULL;
}
i = 0;
for (var = cvar_vars ; var ; var = var->next, i++)
{
if (match && !Com_Filter(match, var->name, qfalse)) continue;
if (var->flags & CVAR_SERVERINFO) {
Com_Printf("S");
} else {
Com_Printf(" ");
}
if (var->flags & CVAR_USERINFO) {
Com_Printf("U");
} else {
Com_Printf(" ");
}
if (var->flags & CVAR_ROM) {
Com_Printf("R");
} else {
Com_Printf(" ");
}
if (var->flags & CVAR_INIT) {
Com_Printf("I");
} else {
Com_Printf(" ");
}
if (var->flags & CVAR_ARCHIVE) {
Com_Printf("A");
} else {
Com_Printf(" ");
}
if (var->flags & CVAR_LATCH) {
Com_Printf("L");
} else {
Com_Printf(" ");
}
if (var->flags & CVAR_CHEAT) {
if (!cvar_cheats->integer)
{
i--;
continue;
}
Com_Printf("C");
} else {
Com_Printf(" ");
}
Com_Printf (" %s \"%s\"\n", var->name, var->string);
}
Com_Printf ("\n%i total cvars\n", i);
}
/*
============
Cvar_Restart_f
Resets all cvars to their hardcoded values
============
*/
void Cvar_Restart_f( void ) {
cvar_t *var;
cvar_t **prev;
prev = &cvar_vars;
while ( 1 ) {
var = *prev;
if ( !var ) {
break;
}
// don't mess with rom values, or some inter-module
// communication will get broken (com_cl_running, etc)
if ( var->flags & ( CVAR_ROM | CVAR_INIT | CVAR_NORESTART ) ) {
prev = &var->next;
continue;
}
// throw out any variables the user created
if ( var->flags & CVAR_USER_CREATED ) {
*prev = var->next;
if ( var->name ) {
Cvar_FreeString( var->name );
}
if ( var->string ) {
Cvar_FreeString( var->string );
}
if ( var->latchedString ) {
Cvar_FreeString( var->latchedString );
}
if ( var->resetString ) {
Cvar_FreeString( var->resetString );
}
// clear the var completely, since we
// can't remove the index from the list
memset( var, 0, sizeof( var ) );
continue;
}
Cvar_Set( var->name, var->resetString );
prev = &var->next;
}
}
/*
=====================
Cvar_InfoString
=====================
*/
char *Cvar_InfoString( int bit ) {
static char info[MAX_INFO_STRING];
cvar_t *var;
info[0] = 0;
for (var = cvar_vars ; var ; var = var->next) {
if (var->flags & bit) {
Info_SetValueForKey (info, var->name, var->string);
}
}
return info;
}
/*
=====================
Cvar_InfoStringBuffer
=====================
*/
void Cvar_InfoStringBuffer( int bit, char* buff, int buffsize ) {
Q_strncpyz(buff,Cvar_InfoString(bit),buffsize);
}
/*
=====================
Cvar_Register
basically a slightly modified Cvar_Get for the interpreted modules
=====================
*/
void Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags ) {
cvar_t *cv;
cv = Cvar_Get( varName, defaultValue, flags );
if ( !vmCvar ) {
return;
}
vmCvar->handle = cv - cvar_indexes;
vmCvar->modificationCount = -1;
Cvar_Update( vmCvar );
}
/*
=====================
Cvar_Register
updates an interpreted modules' version of a cvar
=====================
*/
void Cvar_Update( vmCvar_t *vmCvar ) {
cvar_t *cv;
if ( (unsigned)vmCvar->handle >= cvar_numIndexes ) {
Com_Error( ERR_DROP, "Cvar_Update: handle out of range" );
}
cv = cvar_indexes + vmCvar->handle;
if ( cv->modificationCount == vmCvar->modificationCount ) {
return;
}
if ( !cv->string ) {
return; // variable might have been cleared by a cvar_restart
}
vmCvar->modificationCount = cv->modificationCount;
Q_strncpyz( vmCvar->string, cv->string, sizeof( vmCvar->string ) );
vmCvar->value = cv->value;
vmCvar->integer = cv->integer;
}
/*
============
Cvar_Init
Reads in all archived cvars
============
*/
void Cvar_Init (void) {
cvar_cheats = Cvar_Get("helpUsObi", "0", CVAR_SYSTEMINFO );
Cmd_AddCommand ("toggle", Cvar_Toggle_f);
Cmd_AddCommand ("set", Cvar_Set_f);
Cmd_AddCommand ("sets", Cvar_SetS_f);
Cmd_AddCommand ("setu", Cvar_SetU_f);
Cmd_AddCommand ("seta", Cvar_SetA_f);
Cmd_AddCommand ("reset", Cvar_Reset_f);
Cmd_AddCommand ("cvarlist", Cvar_List_f);
Cmd_AddCommand ("cvar_restart", Cvar_Restart_f);
}
static void Cvar_Realloc(char **string, char *memPool, int &memPoolUsed)
{
if(string && *string)
{
char *temp = memPool + memPoolUsed;
strcpy(temp, *string);
memPoolUsed += strlen(*string) + 1;
Cvar_FreeString(*string);
*string = temp;
}
}
//Turns many small allocation blocks into one big one.
void Cvar_Defrag(void)
{
cvar_t *var;
int totalMem = 0;
int nextMemPoolSize;
for (var = cvar_vars; var; var = var->next)
{
if (var->name) {
totalMem += strlen(var->name) + 1;
}
if (var->string) {
totalMem += strlen(var->string) + 1;
}
if (var->resetString) {
totalMem += strlen(var->resetString) + 1;
}
if (var->latchedString) {
totalMem += strlen(var->latchedString) + 1;
}
}
char *mem = (char*)Z_Malloc(totalMem, TAG_SMALL, qfalse);
nextMemPoolSize = totalMem;
totalMem = 0;
for (var = cvar_vars; var; var = var->next)
{
Cvar_Realloc(&var->name, mem, totalMem);
Cvar_Realloc(&var->string, mem, totalMem);
Cvar_Realloc(&var->resetString, mem, totalMem);
Cvar_Realloc(&var->latchedString, mem, totalMem);
}
if(lastMemPool) {
Z_Free(lastMemPool);
}
lastMemPool = mem;
memPoolSize = nextMemPoolSize;
}

119
code/qcommon/files.h Normal file
View File

@@ -0,0 +1,119 @@
#ifndef __FILES_H
#define __FILES_H
/*
Structures local to the files_* modules.
*/
#ifdef _XBOX
#include "../goblib/goblib.h"
typedef int wfhandle_t;
#else
#include "../zlib32/zip.h"
#include "unzip.h"
#endif
#define MAX_ZPATH 256
#define BASEGAME "base"
typedef struct fileInPack_s {
char *name; // name of the file
unsigned long pos; // file info position in zip
struct fileInPack_s* next; // next file in the hash
} fileInPack_t;
typedef struct {
char pakFilename[MAX_OSPATH]; // c:\quake3\base\asset0.pk3
#ifndef _XBOX
unzFile handle;
#endif
int checksum;
int numfiles;
int hashSize; // hash table size (power of 2)
fileInPack_t* *hashTable; // hash table
fileInPack_t* buildBuffer; // buffer with the filenames etc.
} pack_t;
typedef struct {
char path[MAX_OSPATH]; // c:\stvoy
char gamedir[MAX_OSPATH]; // base
} directory_t;
typedef struct searchpath_s {
struct searchpath_s *next;
pack_t *pack; // only one of pack / dir will be non NULL
directory_t *dir;
} searchpath_t;
#define MAX_FILE_HANDLES 16
typedef union qfile_gus {
FILE* o;
#ifndef _XBOX
unzFile z;
#endif
} qfile_gut;
typedef struct qfile_us {
qfile_gut file;
qboolean unique;
} qfile_ut;
typedef struct {
qfile_ut handleFiles;
qboolean handleSync;
int baseOffset;
int fileSize;
int zipFilePos;
qboolean zipFile;
char name[MAX_QPATH];
#ifdef _XBOX
GOBHandle ghandle;
qboolean gob;
qboolean used;
wfhandle_t whandle;
#endif
} fileHandleData_t;
extern fileHandleData_t fsh[MAX_FILE_HANDLES];
extern searchpath_t *fs_searchpaths;
extern char fs_gamedir[MAX_OSPATH]; // this will be a single file name with no separators
extern cvar_t *fs_debug;
extern cvar_t *fs_basepath;
extern cvar_t *fs_cdpath;
extern cvar_t *fs_copyfiles;
extern cvar_t *fs_gamedirvar;
extern cvar_t *fs_restrict;
extern int fs_readCount; // total bytes read
extern int fs_loadCount; // total files read
extern int fs_packFiles; // total number of files in packs
void FS_Startup( const char *gameName );
void FS_CreatePath(char *OSPath);
char *FS_BuildOSPath( const char *base, const char *game, const char *qpath );
char *FS_BuildOSPath( const char *qpath );
char *FS_BuildOSPathUnMapped( const char *qpath );
fileHandle_t FS_HandleForFile(void);
qboolean FS_FilenameCompare( const char *s1, const char *s2 );
int FS_SV_FOpenFileRead( const char *filename, fileHandle_t *fp );
void FS_Shutdown( void );
void FS_SetRestrictions(void);
void FS_CheckInit(void);
void FS_ReplaceSeparators( char *path );
#endif

View File

@@ -0,0 +1,605 @@
/*****************************************************************************
* name: files.c
*
* desc: handle based filesystem for Quake III Arena
*
*
*****************************************************************************/
#include "../game/q_shared.h"
#include "qcommon.h"
#include "files.h"
/*
=============================================================================
QUAKE3 FILESYSTEM
All of Quake's data access is through a hierarchical file system, but the contents of
the file system can be transparently merged from several sources.
A "qpath" is a reference to game file data. MAX_QPATH is 64 characters, which must include
a terminating zero. "..", "\\", and ":" are explicitly illegal in qpaths to prevent any
references outside the quake directory system.
The "base path" is the path to the directory holding all the game directories and usually
the executable. It defaults to ".", but can be overridden with a "+set fs_basepath c:\quake3"
command line to allow code debugging in a different directory. Basepath cannot
be modified at all after startup. Any files that are created (demos, screenshots,
etc) will be created relative to the base path, so base path should usually be writable.
The "cd path" is the path to an alternate hierarchy that will be searched if a file
is not located in the base path. A user can do a partial install that copies some
data to a base path created on their hard drive and leave the rest on the cd. Files
are never writen to the cd path. It defaults to a value set by the installer, like
"e:\quake3", but it can be overridden with "+set ds_cdpath g:\quake3".
If a user runs the game directly from a CD, the base path would be on the CD. This
should still function correctly, but all file writes will fail (harmlessly).
The "base game" is the directory under the paths where data comes from by default, and
can be either "base" or "demo".
The "current game" may be the same as the base game, or it may be the name of another
directory under the paths that should be searched for files before looking in the base game.
This is the basis for addons.
Clients automatically set the game directory after receiving a gamestate from a server,
so only servers need to worry about +set fs_game.
No other directories outside of the base game and current game will ever be referenced by
filesystem functions.
To save disk space and speed loading, directory trees can be collapsed into zip files.
The files use a ".pk3" extension to prevent users from unzipping them accidentally, but
otherwise the are simply normal uncompressed zip files. A game directory can have multiple
zip files of the form "asset0.pk3", "pak1.pk3", etc. Zip files are searched in decending order
from the highest number to the lowest, and will always take precedence over the filesystem.
This allows a pk3 distributed as a patch to override all existing data.
Because we will have updated executables freely available online, there is no point to
trying to restrict demo / oem versions of the game with code changes. Demo / oem versions
should be exactly the same executables as release versions, but with different data that
automatically restricts where game media can come from to prevent add-ons from working.
After the paths are initialized, quake will look for the product.txt file. If not
found and verified, the game will run in restricted mode. In restricted mode, only
files contained in demo/asset0.pk3 will be available for loading, and only if the zip header is
verified to not have been modified. A single exception is made for jaconfig.cfg. Files
can still be written out in restricted mode, so screenshots and demos are allowed.
Restricted mode can be tested by setting "+set fs_restrict 1" on the command line, even
if there is a valid product.txt under the basepath or cdpath.
If not running in restricted mode, and a file is not found in any local filesystem,
an attempt will be made to download it and save it under the base path.
If the "fs_copyfiles" cvar is set to 1, then every time a file is sourced from the cd
path, it will be copied over to the base path. This is a development aid to help build
test releases and to copy working sets over slow network links.
(If set to 2, copying will only take place if the two filetimes are NOT EQUAL)
The qpath "sound/newstuff/test.wav" would be searched for in the following places:
base path + current game's zip files
base path + current game's directory
cd path + current game's zip files
cd path + current game's directory
base path + base game's zip files
base path + base game's directory
cd path + base game's zip files
cd path + base game's directory
server download, to be written to base path + current game's directory
The filesystem can be safely shutdown and reinitialized with different
basedir / cddir / game combinations, but all other subsystems that rely on it
(sound, video) must also be forced to restart.
Because the same files are loaded by both the clip model (CM_) and renderer (TR_)
subsystems, a simple single-file caching scheme is used. The CM_ subsystems will
load the file with a request to cache. Only one file will be kept cached at a time,
so any models that are going to be referenced by both subsystems should alternate
between the CM_ load function and the ref load function.
TODO: A qpath that starts with a leading slash will always refer to the base game, even if another
game is currently active. This allows character models, skins, and sounds to be downloaded
to a common directory no matter which game is active.
How to prevent downloading zip files?
Pass pk3 file names in systeminfo, and download before FS_Restart()?
Aborting a download disconnects the client from the server.
How to mark files as downloadable? Commercial add-ons won't be downloadable.
Non-commercial downloads will want to download the entire zip file.
the game would have to be reset to actually read the zip in
Auto-update information
Path separators
Casing
separate server gamedir and client gamedir, so if the user starts
a local game after having connected to a network game, it won't stick
with the network game.
allow menu options for game selection?
Read / write config to floppy option.
Different version coexistance?
When building a pak file, make sure a jaconfig.cfg isn't present in it,
or configs will never get loaded from disk!
todo:
downloading (outside fs?)
game directory passing and restarting
=============================================================================
*/
// if this is defined, the executable positively won't work with any paks other
// than the demo pak, even if productid is present. This is only used for our
// last demo release to prevent the mac and linux users from using the demo
// executable with the production windows pak before the mac/linux products
// hit the shelves a little later
//#define PRE_RELEASE_DEMO
char fs_gamedir[MAX_OSPATH]; // this will be a single file name with no separators
cvar_t *fs_debug;
cvar_t *fs_basepath;
cvar_t *fs_cdpath;
cvar_t *fs_copyfiles;
cvar_t *fs_gamedirvar;
cvar_t *fs_restrict;
searchpath_t *fs_searchpaths;
int fs_readCount; // total bytes read
int fs_loadCount; // total files read
int fs_packFiles; // total number of files in packs
qboolean initialized = qfalse;
fileHandleData_t fsh[MAX_FILE_HANDLES];
void FS_CheckInit(void)
{
if (!initialized)
{
Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" );
}
}
/*
==============
FS_Initialized
==============
*/
qboolean FS_Initialized() {
return (qboolean)(fs_searchpaths != NULL);
}
fileHandle_t FS_HandleForFile(void) {
int i;
for ( i = 1 ; i < MAX_FILE_HANDLES ; i++ ) {
#ifdef _XBOX
if ( !fsh[i].used ) {
#else
if ( fsh[i].handleFiles.file.o == NULL ) {
#endif
return i;
}
}
Com_Printf( "FS_HandleForFile: all handles taken:\n" );
for ( i = 1 ; i < MAX_FILE_HANDLES ; i++ ) {
Com_Printf( "%d. %s\n", i, fsh[i].name);
}
Com_Error( ERR_DROP, "FS_HandleForFile: none free" );
return 0;
}
/*
====================
FS_ReplaceSeparators
Fix things up differently for win/unix/mac
====================
*/
void FS_ReplaceSeparators( char *path ) {
char *s;
for ( s = path ; *s ; s++ ) {
if ( *s == '/' || *s == '\\' ) {
*s = PATH_SEP;
}
}
}
/*
===================
FS_BuildOSPath
Qpath may have either forward or backwards slashes
===================
*/
char *FS_BuildOSPath( const char *qpath )
{
char temp[MAX_OSPATH];
static char ospath[2][MAX_OSPATH];
static int toggle;
toggle ^= 1; // flip-flop to allow two returns without clash
Com_sprintf( temp, sizeof(temp), "/%s/%s", fs_gamedirvar->string, qpath );
FS_ReplaceSeparators( temp );
Com_sprintf( ospath[toggle], sizeof( ospath[0] ), "%s%s",
fs_basepath->string, temp );
return ospath[toggle];
}
char *FS_BuildOSPathUnMapped( const char *qpath )
{
char temp[MAX_OSPATH];
static char ospath[2][MAX_OSPATH];
static int toggle;
toggle ^= 1; // flip-flop to allow two returns without clash
Com_sprintf( temp, sizeof(temp), "/%s/%s", fs_gamedirvar->string, qpath );
FS_ReplaceSeparators( temp );
Com_sprintf( ospath[toggle], sizeof( ospath[0] ), "%s%s",
"d:", temp );
return ospath[toggle];
}
#ifndef _XBOX
char *FS_BuildOSPath( const char *base, const char *game, const char *qpath ) {
char temp[MAX_OSPATH];
static char ospath[4][MAX_OSPATH];
static int toggle;
toggle = (++toggle)&3; // allows four returns without clash (increased from 2 during fs_copyfiles 2 enhancement)
Com_sprintf( temp, sizeof(temp), "/%s/%s", game, qpath );
FS_ReplaceSeparators( temp );
Com_sprintf( ospath[toggle], sizeof( ospath[0] ), "%s%s", base, temp );
return ospath[toggle];
}
#endif
/*
============
FS_CreatePath
Creates any directories needed to store the given filename
============
*/
void FS_CreatePath (char *OSPath) {
char *ofs;
// make absolutely sure that it can't back up the path
// FIXME: is c: allowed???
if ( strstr( OSPath, ".." ) || strstr( OSPath, "::" ) ) {
Com_Printf( "WARNING: refusing to create relative path \"%s\"\n", OSPath );
return;
}
strlwr(OSPath);
for (ofs = OSPath+1 ; *ofs ; ofs++) {
if (*ofs == PATH_SEP) {
// create the directory
*ofs = 0;
Sys_Mkdir (OSPath);
*ofs = PATH_SEP;
}
}
}
/*
===========
FS_SV_FOpenFileRead
===========
*/
int FS_SV_FOpenFileRead( const char *filename, fileHandle_t *fp ) {
char *ospath;
fileHandle_t f;
if ( !fs_searchpaths ) {
Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" );
}
f = FS_HandleForFile();
fsh[f].zipFile = qfalse;
Q_strncpyz( fsh[f].name, filename, sizeof( fsh[f].name ) );
// don't let sound stutter
S_ClearSoundBuffer();
#ifdef _XBOX
ospath = FS_BuildOSPath( filename );
#else
ospath = FS_BuildOSPath( fs_basepath->string, filename, "" );
#endif
// remove trailing slash
ospath[strlen(ospath)-1] = '\0';
if ( fs_debug->integer ) {
Com_Printf( "FS_SV_FOpenFileRead: %s\n", ospath );
}
fsh[f].handleFiles.file.o = fopen( ospath, "rb" );
fsh[f].handleSync = qfalse;
if (!fsh[f].handleFiles.file.o) {
f = 0;
}
*fp = f;
if (f) {
return FS_filelength(f);
}
return 0;
}
/*
===========
FS_FOpenFileAppend
===========
*/
fileHandle_t FS_FOpenFileAppend( const char *filename ) {
char *ospath;
fileHandle_t f;
if ( !fs_searchpaths ) {
Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" );
}
f = FS_HandleForFile();
fsh[f].zipFile = qfalse;
Q_strncpyz( fsh[f].name, filename, sizeof( fsh[f].name ) );
// don't let sound stutter
S_ClearSoundBuffer();
#ifdef _XBOX
ospath = FS_BuildOSPath( filename );
#else
ospath = FS_BuildOSPath( fs_basepath->string, fs_gamedir, filename );
#endif
if ( fs_debug->integer ) {
Com_Printf( "FS_FOpenFileAppend: %s\n", ospath );
}
FS_CreatePath( ospath );
fsh[f].handleFiles.file.o = fopen( ospath, "ab" );
fsh[f].handleSync = qfalse;
if (!fsh[f].handleFiles.file.o) {
f = 0;
}
return f;
}
/*
===========
FS_FilenameCompare
Ignore case and seprator char distinctions
===========
*/
qboolean FS_FilenameCompare( const char *s1, const char *s2 ) {
int c1, c2;
do {
c1 = *s1++;
c2 = *s2++;
if ( Q_islower(c1) ) {
c1 -= ('a' - 'A');
}
if ( Q_islower(c2) ) {
c2 -= ('a' - 'A');
}
if ( c1 == '\\' || c1 == ':' ) {
c1 = '/';
}
if ( c2 == '\\' || c2 == ':' ) {
c2 = '/';
}
if (c1 != c2) {
return -1; // strings not equal
}
} while (c1);
return 0; // strings are equal
}
#define MAXPRINTMSG 4096
void QDECL FS_Printf( fileHandle_t h, const char *fmt, ... ) {
va_list argptr;
char msg[MAXPRINTMSG];
va_start (argptr,fmt);
vsprintf (msg,fmt,argptr);
va_end (argptr);
FS_Write(msg, strlen(msg), h);
}
/*
============
FS_WriteFile
Filename are relative to the quake search path
============
*/
void FS_WriteFile( const char *qpath, const void *buffer, int size ) {
fileHandle_t f;
if ( !fs_searchpaths ) {
Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" );
}
if ( !qpath || !buffer ) {
Com_Error( ERR_FATAL, "FS_WriteFile: NULL parameter" );
}
f = FS_FOpenFileWrite( qpath );
if ( !f ) {
Com_Printf( "Failed to open %s\n", qpath );
return;
}
FS_Write( buffer, size, f );
FS_FCloseFile( f );
}
/*
================
FS_Shutdown
Frees all resources and closes all files
================
*/
void FS_Shutdown( void ) {
searchpath_t *p, *next;
int i;
for(i = 0; i < MAX_FILE_HANDLES; i++) {
if (fsh[i].fileSize) {
FS_FCloseFile(i);
}
}
// free everything
for ( p = fs_searchpaths ; p ; p = next ) {
next = p->next;
if ( p->pack ) {
#ifndef _XBOX
unzClose(p->pack->handle);
#endif
Z_Free( p->pack->buildBuffer );
Z_Free( p->pack );
}
if ( p->dir ) {
Z_Free( p->dir );
}
Z_Free( p );
}
// any FS_ calls will now be an error until reinitialized
fs_searchpaths = NULL;
Cmd_RemoveCommand( "path" );
Cmd_RemoveCommand( "dir" );
Cmd_RemoveCommand( "touchFile" );
initialized = qfalse;
}
/*
================
FS_InitFilesystem
Called only at inital startup, not when the filesystem
is resetting due to a game change
================
*/
void FS_InitFilesystem( void ) {
// allow command line parms to override our defaults
// we don't have to specially handle this, because normal command
// line variable sets happen before the filesystem
// has been initialized
//
// UPDATE: BTO (VV)
// we have to specially handle this, because normal command
// line variable sets don't happen until after the filesystem
// has already been initialized
Com_StartupVariable( "fs_cdpath" );
Com_StartupVariable( "fs_basepath" );
Com_StartupVariable( "fs_game" );
Com_StartupVariable( "fs_copyfiles" );
Com_StartupVariable( "fs_restrict" );
// try to start up normally
FS_Startup( BASEGAME );
initialized = qtrue;
// see if we are going to allow add-ons
FS_SetRestrictions();
// if we can't find default.cfg, assume that the paths are
// busted and error out now, rather than getting an unreadable
// graphics screen when the font fails to load
if ( FS_ReadFile( "default.cfg", NULL ) <= 0 ) {
Com_Error( ERR_FATAL, "Couldn't load default.cfg" );
}
}
void FS_Flush( fileHandle_t f ) {
fflush(fsh[f].handleFiles.file.o);
}

File diff suppressed because it is too large Load Diff

1741
code/qcommon/files_pc.cpp Normal file

File diff suppressed because it is too large Load Diff

169
code/qcommon/fixedmap.h Normal file
View File

@@ -0,0 +1,169 @@
#ifndef __FIXEDMAP_H
#define __FIXEDMAP_H
/*
An STL map-like container. Quickly thrown together to replace STL maps
in specific instances. Many gotchas. Use with caution.
*/
#include <stdlib.h>
template < class T, class U >
class VVFixedMap
{
private:
struct Data {
T data;
U key;
};
Data *items;
unsigned int numItems;
unsigned int maxItems;
VVFixedMap(void) {}
public:
VVFixedMap(unsigned int maxItems)
{
items = new Data[maxItems];
numItems = 0;
this->maxItems = maxItems;
}
~VVFixedMap(void)
{
items -= ( maxItems - numItems );
delete [] items;
numItems = 0;
}
bool Insert(const T &newItem, const U &key)
{
Data *storage = NULL;
//Check for fullness.
if(numItems >= maxItems) {
assert( 0 );
return false;
}
//Check for reuse.
if(!FindUnsorted(key, storage)) {
storage = items + numItems;
numItems++;
}
else
assert( 0 );
storage->data = newItem;
storage->key = key;
return true;
}
// Faster version of Insert(), but it doesn't check for dupes. Used
// by the filecode cache (when we know the data we're inserting is good).
bool InsertUnsafe(const T &newItem, const U &key)
{
//Check for fullness.
if(numItems >= maxItems) {
return false;
}
Data *storage = items + numItems;
numItems++;
storage->data = newItem;
storage->key = key;
return true;
}
void Sort(void)
{
qsort(items, numItems, sizeof(Data),
VVFixedMap< T, U >::FixedMapSorter);
}
//Binary search, items must have been sorted!
T *Find(const U &key)
{
int i;
int high;
int low;
for(low = -1, high = numItems; high - low > 1; ) {
i = (high + low) / 2;
if(key < items[i].key) {
high = i;
} else if(key > items[i].key) {
low = i;
} else {
return &items[i].data;
}
}
if(items[i+1].key == key) {
return &items[i+1].data;
} else if(items[i-1].key == key) {
return &items[i-1].data;
}
return NULL;
}
//Slower, but don't need to call sort first.
T *FindUnsorted(const U &key, Data *&storage)
{
int i;
for(i=0; i<numItems; i++) {
if(items[i].key == key) {
storage = items + i;
return &items[i].data;
}
}
return NULL;
}
// returns the top item's data
// and removes the item from the map
T *Pop(void)
{
T* top = NULL;
if(numItems)
{
top = &items[0].data;
items++;
numItems--;
}
return top;
}
static int FixedMapSorter(const void *a, const void *b)
{
if(((Data*)a)->key > ((Data*)b)->key) {
return 1;
} else if(((Data*)a)->key == ((Data*)b)->key) {
return 0;
} else {
return -1;
}
}
};
#endif

525
code/qcommon/hstring.cpp Normal file
View File

@@ -0,0 +1,525 @@
#include "cm_local.h"
#include "hstring.h"
#if defined (_DEBUG) && defined (_WIN32)
#define WIN32_LEAN_AND_MEAN 1
//#include <windows.h> // for Sleep for Z_Malloc recovery attempy
#include "platform.h"
#endif
// mapPoolBlockCount is defined differently in the executable (sv_main.cpp) and the game dll (g_main.cpp) cuz
//we likely don't need as many blocks in the executable as we do in the game
extern int mapPoolBlockCount;
// Used to fool optimizer during compilation of mem touch routines.
int HaHaOptimizer2=0;
#ifndef _XBOX
CMapPoolLow &GetMapPool()
{
// this may need to be ifdefed to be different for different modules
static CMapPoolLow thePool;
return thePool;
}
#endif
#define MAPBLOCK_SIZE_NODES (1024)
#define MAPNODE_FREE (0xa1)
#define MAPNODE_INUSE (0x94)
struct SMapNode
{
unsigned char mData[MAP_NODE_SIZE-2];
unsigned char mMapBlockNum;
unsigned char mTag;
};
class CMapBlock
{
int mId;
char mRaw[(MAPBLOCK_SIZE_NODES+1)*MAP_NODE_SIZE];
SMapNode *mNodes;
int mLastNode;
public:
CMapBlock(int id,vector <void *> &freeList) :
mLastNode(0)
{
// Alloc node storage for MAPBLOCK_SIZE_NODES worth of nodes.
mNodes=(SMapNode *)((((unsigned long)mRaw)+MAP_NODE_SIZE)&~(unsigned long)0x1f);
// Set all nodes to initially be free.
int i;
for(i=0;i<MAPBLOCK_SIZE_NODES;i++)
{
mNodes[i].mMapBlockNum=id;
mNodes[i].mTag=MAPNODE_FREE;
freeList.push_back((void *)&mNodes[i]);
}
}
bool bOwnsNode(void *node)
{
return((((SMapNode *)node)>=&mNodes[0])&&(((SMapNode *)node)<&mNodes[MAPBLOCK_SIZE_NODES]));
}
};
#ifndef _XBOX
CMapPoolLow::CMapPoolLow()
{
mLastBlockNum=-1;
}
CMapPoolLow::~CMapPoolLow()
{
#if _DEBUG
char mess[1000];
#if _GAME
if(mFreeList.size()<mMapBlocks.size()*MAPBLOCK_SIZE_NODES)
{
sprintf(mess,"[MEM][GAME] !!!! Map Pool Leaked %d nodes\n",(MAPBLOCK_SIZE_NODES*mMapBlocks.size())-mFreeList.size());
OutputDebugString(mess);
}
sprintf(mess, "[MEM][GAME] Map Pool max. mem used = %d\n",mMapBlocks.size()*MAPBLOCK_SIZE_NODES*MAP_NODE_SIZE);
OutputDebugString(mess);
#elif _CGAME
if (mFreeList.size()<mMapBlocks.size()*MAPBLOCK_SIZE_NODES)
{
sprintf(mess, "[MEM][CGAME] !!!! Map Pool Leaked %d nodes\n",(MAPBLOCK_SIZE_NODES*mMapBlocks.size())-mFreeList.size());
OutputDebugString(mess);
}
sprintf(mess, "[MEM][CGAME] Map Pool max. mem used = %d\n",mMapBlocks.size()*MAPBLOCK_SIZE_NODES*MAP_NODE_SIZE);
OutputDebugString(mess);
#else
if (mFreeList.size()<mMapBlocks.size()*MAPBLOCK_SIZE_NODES)
{
sprintf(mess, "[MEM][EXE] !!!! Map Pool Leaked %d nodes\n",(MAPBLOCK_SIZE_NODES*mMapBlocks.size())-mFreeList.size());
OutputDebugString(mess);
}
sprintf(mess, "[MEM][EXE] Map Pool max. mem used = %d\n",mMapBlocks.size()*MAPBLOCK_SIZE_NODES*MAP_NODE_SIZE);
OutputDebugString(mess);
#endif
#endif
int i;
for (i=0;i<mMapBlocks.size();i++)
{
delete mMapBlocks[i];
}
}
void *CMapPoolLow::Alloc()
{
// Try to request a node. First we look in the free-list, but if that
// happens to be empty, we allocate more storage in the current CMapBlock.
void *node=0;
if(mFreeList.size())
{
// Retrieve the node to be recycled.
node=(void *)mFreeList[mFreeList.size()-1];
mFreeList.pop_back();
}
else
{
// None free, so alloc another block.
CMapBlock *block=new CMapBlock(mLastBlockNum+1,mFreeList);
assert(block);
mMapBlocks.push_back(block);
mLastBlockNum++;
node=(void *)mFreeList[mFreeList.size()-1];
mFreeList.pop_back();
}
// Validate we aren't somehow grabbing something that is already in use
// and also that the end marker is intact.
assert(((SMapNode *)node)->mTag==MAPNODE_FREE);
assert((((SMapNode *)node)->mMapBlockNum)>=0);
assert((((SMapNode *)node)->mMapBlockNum)<256);
assert((((SMapNode *)node)->mMapBlockNum)<=mLastBlockNum);
assert(mMapBlocks[((SMapNode *)node)->mMapBlockNum]->bOwnsNode(node));
// Ok, mark the node as in use.
((SMapNode *)node)->mTag=MAPNODE_INUSE;
return(node);
}
void CMapPoolLow::Free(void *p)
{
// Validate that someone isn't trying to double free this node and also
// that the end marker is intact.
assert(((SMapNode *)p)->mTag==MAPNODE_INUSE);
assert((((SMapNode *)p)->mMapBlockNum)>=0);
assert((((SMapNode *)p)->mMapBlockNum)<256);
assert((((SMapNode *)p)->mMapBlockNum)<=mLastBlockNum);
assert(mMapBlocks[((SMapNode *)p)->mMapBlockNum]->bOwnsNode(p));
// Ok, mark the the node as free.
((SMapNode *)p)->mTag=MAPNODE_FREE;
// Add a new freelist entry to point at this node.
mFreeList.push_back(p);
}
void CMapPoolLow::TouchMem()
{
int i,j;
unsigned char *memory;
int totSize=0;
for(i=0;i<mMapBlocks.size();i++)
{
memory=(unsigned char *)mMapBlocks[i];
totSize+=sizeof(CMapBlock);
for(j=0;j<sizeof(CMapBlock);j+=256)
{
HaHaOptimizer2+=memory[j];
}
}
#ifdef _DEBUG
Com_Printf("MapPool: Bytes touched %i\n",totSize);
#endif
}
#endif // _XBOX
////////////
// hString stuff
////////////
#define MAX_HASH (65536*2)
// Max number of strings we can ever deal with.
#define MAX_HSTRINGS 100000
// Size of a string storage block in bytes.
#define BLOCK_SIZE 65536
int HashFunction(const char *key)
{
long hash=0;
int i=0;
unsigned char letter;
letter = (unsigned char)*key++;
while (letter)
{
hash += (long)(letter) * (i + 119);
i++;
letter = (unsigned char)*key++;
}
hash &= MAX_HASH - 1;
return (int)hash;
}
class CHashHelper
{
int mHashes[MAX_HASH];
int mFindPtr;
int mFindPtrStart;
public:
CHashHelper()
{
int i;
for (i=0;i<MAX_HASH;i++)
{
mHashes[i]=0;
}
}
void Add(int hash,int value)
{
assert(hash>=0&&hash<MAX_HASH);
assert(value); // 0 is the empty marker
int i=hash;
while (mHashes[i])
{
assert(mHashes[i]!=value); //please don't insert things twice
i=(i+1)&(MAX_HASH-1);
assert(i!=hash); //hash table is full?
}
mHashes[i]=value;
}
int FindFirst(int hash)
{
mFindPtr=hash;
mFindPtrStart=hash;
return FindNext();
}
int FindNext()
{
assert(mFindPtr>=0&&mFindPtr<MAX_HASH);
int val=mHashes[mFindPtr];
mFindPtr=(mFindPtr+1)&(MAX_HASH-1);
assert(mFindPtr!=mFindPtrStart); //hash table full?
return val;
}
void TouchMem()
{
int i;
for(i=0;i<sizeof(mHashes);i+=256)
{
HaHaOptimizer2+=((unsigned char *)mHashes)[i];
}
#ifdef _DEBUG
Com_Printf("Hash helper: Bytes touched %i\n",sizeof(mHashes));
#endif
}
};
CHashHelper &HashHelper()
{
static CHashHelper It;
return It;
}
static char *gCharPtrs[MAX_HSTRINGS];
class CHSBlock
{
int mBytesUsed;
char mRaw[BLOCK_SIZE];
public:
CHSBlock(void) :
mBytesUsed(0)
{
// So we can do a comparison of blocks for debug purposes.
memset(mRaw,0,BLOCK_SIZE);
};
char *Alloc(int sizeBytes)
{
// Remember to include 0 termintor in size.
sizeBytes++;
// Is it WAAAY to big? If so we complain loudly.
assert(sizeBytes<=BLOCK_SIZE);
// If we don't have space in the current block, return failure.
if(sizeBytes>(BLOCK_SIZE-mBytesUsed))
{
return(0);
}
// Return the pointer to the start of allocated space.
char *ret=&mRaw[mBytesUsed];
mBytesUsed+=sizeBytes;
return ret;
}
bool operator== (const CHSBlock &block) const
{
if(!memcmp(mRaw,block.mRaw,BLOCK_SIZE))
{
return(true);
}
return(false);
}
};
class CPool
{
vector<CHSBlock *> mBlockVec;
public:
int mNextStringId;
int mLastBlockNum;
CPool(void) :
mNextStringId(1),
mLastBlockNum(-1)
{
memset(gCharPtrs,0,MAX_HSTRINGS*4);
}
~CPool(void)
{
int i;
for (i=0;i<mBlockVec.size();i++)
{
delete mBlockVec[i];
}
}
char *Alloc(int sizeBytes,int &id)
{
// Can't alloc more than MAX_HSTRINGS.
assert(mNextStringId<MAX_HSTRINGS);
char *raw=0;
if (mLastBlockNum>=0)
{
// Get the pointer to the start of allocated space in the current block.
raw=mBlockVec[mLastBlockNum]->Alloc(sizeBytes);
}
if(!raw)
{
// Ok, make a new empty block and append it.
CHSBlock *block=new(CHSBlock);
mBlockVec.push_back(block);
mLastBlockNum++;
raw=mBlockVec[mLastBlockNum]->Alloc(sizeBytes);
}
// Should never really happen!!
assert(raw);
id=mNextStringId;
gCharPtrs[mNextStringId]=raw;
mNextStringId++;
return(raw);
}
bool operator== (const CPool &pool) const
{
int i;
for(i=0;i<mBlockVec.size();i++)
{
if(!(*mBlockVec[i]==*pool.mBlockVec[i]))
{
return(false);
}
}
return(true);
}
void TouchMem()
{
int i,j;
unsigned char *memory;
int totSize=0;
for(i=0;i<mBlockVec.size();i++)
{
memory=(unsigned char *)mBlockVec[i];
totSize+=sizeof(CHSBlock);
for(j=0;j<sizeof(CHSBlock);j+=256)
{
HaHaOptimizer2+=memory[j];
}
}
#ifdef _DEBUG
Com_Printf("String Pool: Bytes touched %i\n",totSize);
#endif
}
};
#ifdef _DEBUG
CPool &TheDebugPool(void);
CPool &ThePool(void);
class CPoolChecker
{
public:
CPoolChecker()
{
TheDebugPool();
ThePool();
}
~CPoolChecker()
{
#if 0
int i;
for (i=1;i<ThePool().mNextStringId;i++)
{
OutputDebugString(gCharPtrs[i]);
OutputDebugString("\n");
}
#endif
#if _DEBUG
char mess[1000];
#if _GAME
sprintf(mess,"[MEM][GAME] String Pool %d unique strings, %dK\n",ThePool().mNextStringId,(ThePool().mLastBlockNum+1)*BLOCK_SIZE/1024);
#elif _CGAME
sprintf(mess,"[MEM][CGAME] String Pool %d unique strings, %dK\n",ThePool().mNextStringId,(ThePool().mLastBlockNum+1)*BLOCK_SIZE/1024);
#else
sprintf(mess,"[MEM][EXE] String Pool %d unique strings, %dK\n",ThePool().mNextStringId,(ThePool().mLastBlockNum+1)*BLOCK_SIZE/1024);
#endif
OutputDebugString(mess);
#endif
// if this fails it means the string storage is CORRUPTED, let someone know
assert(TheDebugPool()==ThePool());
}
};
static CPoolChecker TheCPoolChecker;
CPool &TheDebugPool(void)
{
static CPool theDebugPool;
return(theDebugPool);
}
#endif
CPool &ThePool(void)
{
static CPool thePool;
return(thePool);
}
void TouchStringPool(void)
{
ThePool().TouchMem();
HashHelper().TouchMem();
}
//
// Now the rest of the hString class.
//
#ifdef _XBOX
namespace exeNamespace
{
#endif
void hstring::Init(const char *str)
{
if(!str)
{
mId=0;
return;
}
int hash=HashFunction(str);
int id=HashHelper().FindFirst(hash);
while (id)
{
assert(id>0&&id<ThePool().mNextStringId);
if (!strcmp(str,gCharPtrs[id]))
{
mId=id;
return;
}
id=HashHelper().FindNext();
}
char *raw=ThePool().Alloc(strlen(str),mId);
strcpy(raw,str);
HashHelper().Add(hash,mId);
#ifdef _DEBUG
int test;
raw=TheDebugPool().Alloc(strlen(str),test);
assert(test==mId);
strcpy(raw,str);
#endif
}
const char *hstring::c_str(void) const
{
if(!mId)
{
return("");
}
assert(mId>0&&mId<ThePool().mNextStringId);
return(gCharPtrs[mId]);
}
string hstring::str(void) const
{
if(!mId)
{
return(string());
}
assert(mId>0&&mId<ThePool().mNextStringId);
return string(gCharPtrs[mId]);
}
#ifdef _XBOX
} // exeNamespace
#endif

219
code/qcommon/hstring.h Normal file
View File

@@ -0,0 +1,219 @@
#if !defined hString_H
#define hString_H
#pragma warning (push, 3) //go back down to 3 for the stl include
#include <string>
#include <set>
#include <list>
#include <map>
#pragma warning (pop)
using namespace std;
class hstring
{
int mId;
void Init(const char *str);
public:
hstring()
{
mId=0;
}
hstring(const char *str)
{
Init(str);
}
hstring(const string &str)
{
Init(str.c_str());
}
hstring(const hstring &str)
{
mId=str.mId;
}
operator string () const
{
return str();
}
const char *c_str(void) const;
string str(void) const;
hstring& operator= (const char *str)
{
Init(str);
return *this;
}
hstring& operator= (const string &str)
{
Init(str.c_str());
return *this;
}
hstring& operator= (const hstring &str)
{
mId=str.mId;
return *this;
}
bool operator== (const hstring &str) const
{
return((mId==str.mId)?true:false);
}
int compare(const hstring &str) const
{
return strcmp(c_str(),str.c_str());
}
bool operator< (const hstring &str) const
{
return((mId<str.mId)?true:false);
}
int length() const
{
return strlen(c_str());
}
};
void TouchStringPool(void);
////////////
// MapPool
////////////
#define MAP_NODE_SIZE (32)
class CMapBlock;
class CMapPoolLow
{
vector <CMapBlock *> mMapBlocks;
vector <void *> mFreeList;
int mLastBlockNum;
public:
CMapPoolLow();
~CMapPoolLow();
void *Alloc();
void Free(void *p);
void TouchMem();
};
CMapPoolLow &GetMapPool();
template<class T>
class CMapPool
{
CMapPoolLow &mPool;
public:
CMapPool() : mPool(GetMapPool())
{
}
template <class U>
CMapPool(const U&) : mPool(GetMapPool())
{
}
~CMapPool()
{
}
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
template <class U>
struct rebind
{
typedef CMapPool<U> other;
};
// return address of values
pointer address (reference value) const
{
return &value;
}
const_pointer address (const_reference value) const
{
return &value;
}
// return maximum number of elements that can be allocated
size_type max_size () const
{
// return mMaxSize;
return 0xfffffff; //uh, take a guess
}
// allocate but don't initialize num elements of type T
pointer allocate (size_type num, const void* = 0)
{
assert(sizeof(T)<=(MAP_NODE_SIZE-2)); // to big for this pool
assert(num==1); //allocator not design for this
return (T*)mPool.Alloc();
}
void *_Charalloc(size_type size)
{
assert(size<=(MAP_NODE_SIZE-2)); // to big for this pool
return mPool.Alloc();
}
// initialize elements of allocated storage p with value value
void construct (pointer p, const T& value)
{
// initialize memory with placement new
new((void*)p)T(value);
}
// destroy elements of initialized storage p
void destroy (pointer p)
{
// destroy objects by calling their destructor
p->~T();
}
// deallocate storage p of deleted elements
template<class U>
void deallocate (U *p, size_type num)
{
assert(num==1); //allocator not design for this
mPool.Free(p);
}
};
template <class T1,class T2>
bool operator== (const CMapPool<T1>&,
const CMapPool<T2>&)
{
return false;
}
template <class T1,class T2>
bool operator!= (const CMapPool<T1>&,
const CMapPool<T2>&)
{
return true;
}
template <class K,class V,class Compare = less<K> >
class hmap : public map<K,V,Compare,CMapPool<V> >{};
template <class K,class V,class Compare = less<K> >
class hmultimap : public multimap<K,V,Compare,CMapPool<V> >{};
template <class K,class Compare = less<K> >
class hset : public set<K,Compare,CMapPool<K> >{};
template <class K,class Compare = less<K> >
class hmultiset : public multiset<K,Compare,CMapPool<K> >{};
template <class K>
class hlist : public list<K,CMapPool<K> >{};
#endif // hString_H

274
code/qcommon/md4.cpp Normal file
View File

@@ -0,0 +1,274 @@
/* GLOBAL.H - RSAREF types and constants */
#include <string.h>
/* POINTER defines a generic pointer type */
typedef unsigned char *POINTER;
/* UINT2 defines a two byte word */
typedef unsigned short int UINT2;
/* UINT4 defines a four byte word */
typedef unsigned long int UINT4;
/* MD4.H - header file for MD4C.C */
/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991.
All rights reserved.
License to copy and use this software is granted provided that it is identified as the “RSA Data Security, Inc. MD4 Message-Digest Algorithm” in all material mentioning or referencing this software or this function.
License is also granted to make and use derivative works provided that such works are identified as “derived from the RSA Data Security, Inc. MD4 Message-Digest Algorithm” in all material mentioning or referencing the derived work.
RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided “as is” without express or implied warranty of any kind.
These notices must be retained in any copies of any part of this documentation and/or software. */
/* MD4 context. */
typedef struct {
UINT4 state[4]; /* state (ABCD) */
UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
unsigned char buffer[64]; /* input buffer */
} MD4_CTX;
void MD4Init (MD4_CTX *);
void MD4Update (MD4_CTX *, const unsigned char *, unsigned int);
void MD4Final (unsigned char [16], MD4_CTX *);
/* MD4C.C - RSA Data Security, Inc., MD4 message-digest algorithm */
/* Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved.
License to copy and use this software is granted provided that it is identified as the
RSA Data Security, Inc. MD4 Message-Digest Algorithm
in all material mentioning or referencing this software or this function.
License is also granted to make and use derivative works provided that such works are identified as
derived from the RSA Data Security, Inc. MD4 Message-Digest Algorithm
in all material mentioning or referencing the derived work.
RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided
as is without express or implied warranty of any kind.
These notices must be retained in any copies of any part of this documentation and/or software. */
/* Constants for MD4Transform routine. */
#define S11 3
#define S12 7
#define S13 11
#define S14 19
#define S21 3
#define S22 5
#define S23 9
#define S24 13
#define S31 3
#define S32 9
#define S33 11
#define S34 15
static void MD4Transform (UINT4 [4], const unsigned char [64]);
static void Encode (unsigned char *, UINT4 *, unsigned int);
static void Decode (UINT4 *, const unsigned char *, unsigned int);
static unsigned char PADDING[64] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/* F, G and H are basic MD4 functions. */
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
/* ROTATE_LEFT rotates x left n bits. */
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
/* FF, GG and HH are transformations for rounds 1, 2 and 3 */
/* Rotation is separate from addition to prevent recomputation */
#define FF(a, b, c, d, x, s) {(a) += F ((b), (c), (d)) + (x); (a) = ROTATE_LEFT ((a), (s));}
#define GG(a, b, c, d, x, s) {(a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; (a) = ROTATE_LEFT ((a), (s));}
#define HH(a, b, c, d, x, s) {(a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; (a) = ROTATE_LEFT ((a), (s));}
/* MD4 initialization. Begins an MD4 operation, writing a new context. */
void MD4Init (MD4_CTX *context)
{
context->count[0] = context->count[1] = 0;
/* Load magic initialization constants.*/
context->state[0] = 0x67452301;
context->state[1] = 0xefcdab89;
context->state[2] = 0x98badcfe;
context->state[3] = 0x10325476;
}
/* MD4 block update operation. Continues an MD4 message-digest operation, processing another message block, and updating the context. */
void MD4Update (MD4_CTX *context, const unsigned char *input, unsigned int inputLen)
{
unsigned int i, index, partLen;
/* Compute number of bytes mod 64 */
index = (unsigned int)((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
if ((context->count[0] += ((UINT4)inputLen << 3))< ((UINT4)inputLen << 3))
context->count[1]++;
context->count[1] += ((UINT4)inputLen >> 29);
partLen = 64 - index;
/* Transform as many times as possible.*/
if (inputLen >= partLen)
{
memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen);
MD4Transform (context->state, context->buffer);
for (i = partLen; i + 63 < inputLen; i += 64)
MD4Transform (context->state, &input[i]);
index = 0;
}
else
i = 0;
/* Buffer remaining input */
memcpy ((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i);
}
/* MD4 finalization. Ends an MD4 message-digest operation, writing the the message digest and zeroizing the context. */
void MD4Final (unsigned char digest[16], MD4_CTX *context)
{
unsigned char bits[8];
unsigned int index, padLen;
/* Save number of bits */
Encode (bits, context->count, 8);
/* Pad out to 56 mod 64.*/
index = (unsigned int)((context->count[0] >> 3) & 0x3f);
padLen = (index < 56) ? (56 - index) : (120 - index);
MD4Update (context, PADDING, padLen);
/* Append length (before padding) */
MD4Update (context, bits, 8);
/* Store state in digest */
Encode (digest, context->state, 16);
/* Zeroize sensitive information.*/
memset ((POINTER)context, 0, sizeof (*context));
}
/* MD4 basic transformation. Transforms state based on block. */
static void MD4Transform (UINT4 state[4], const unsigned char block[64])
{
UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
Decode (x, block, 64);
/* Round 1 */
FF (a, b, c, d, x[ 0], S11); /* 1 */
FF (d, a, b, c, x[ 1], S12); /* 2 */
FF (c, d, a, b, x[ 2], S13); /* 3 */
FF (b, c, d, a, x[ 3], S14); /* 4 */
FF (a, b, c, d, x[ 4], S11); /* 5 */
FF (d, a, b, c, x[ 5], S12); /* 6 */
FF (c, d, a, b, x[ 6], S13); /* 7 */
FF (b, c, d, a, x[ 7], S14); /* 8 */
FF (a, b, c, d, x[ 8], S11); /* 9 */
FF (d, a, b, c, x[ 9], S12); /* 10 */
FF (c, d, a, b, x[10], S13); /* 11 */
FF (b, c, d, a, x[11], S14); /* 12 */
FF (a, b, c, d, x[12], S11); /* 13 */
FF (d, a, b, c, x[13], S12); /* 14 */
FF (c, d, a, b, x[14], S13); /* 15 */
FF (b, c, d, a, x[15], S14); /* 16 */
/* Round 2 */
GG (a, b, c, d, x[ 0], S21); /* 17 */
GG (d, a, b, c, x[ 4], S22); /* 18 */
GG (c, d, a, b, x[ 8], S23); /* 19 */
GG (b, c, d, a, x[12], S24); /* 20 */
GG (a, b, c, d, x[ 1], S21); /* 21 */
GG (d, a, b, c, x[ 5], S22); /* 22 */
GG (c, d, a, b, x[ 9], S23); /* 23 */
GG (b, c, d, a, x[13], S24); /* 24 */
GG (a, b, c, d, x[ 2], S21); /* 25 */
GG (d, a, b, c, x[ 6], S22); /* 26 */
GG (c, d, a, b, x[10], S23); /* 27 */
GG (b, c, d, a, x[14], S24); /* 28 */
GG (a, b, c, d, x[ 3], S21); /* 29 */
GG (d, a, b, c, x[ 7], S22); /* 30 */
GG (c, d, a, b, x[11], S23); /* 31 */
GG (b, c, d, a, x[15], S24); /* 32 */
/* Round 3 */
HH (a, b, c, d, x[ 0], S31); /* 33 */
HH (d, a, b, c, x[ 8], S32); /* 34 */
HH (c, d, a, b, x[ 4], S33); /* 35 */
HH (b, c, d, a, x[12], S34); /* 36 */
HH (a, b, c, d, x[ 2], S31); /* 37 */
HH (d, a, b, c, x[10], S32); /* 38 */
HH (c, d, a, b, x[ 6], S33); /* 39 */
HH (b, c, d, a, x[14], S34); /* 40 */
HH (a, b, c, d, x[ 1], S31); /* 41 */
HH (d, a, b, c, x[ 9], S32); /* 42 */
HH (c, d, a, b, x[ 5], S33); /* 43 */
HH (b, c, d, a, x[13], S34); /* 44 */
HH (a, b, c, d, x[ 3], S31); /* 45 */
HH (d, a, b, c, x[11], S32); /* 46 */
HH (c, d, a, b, x[ 7], S33); /* 47 */
HH (b, c, d, a, x[15], S34); /* 48 */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
/* Zeroize sensitive information.*/
memset ((POINTER)x, 0, sizeof (x));
}
/* Encodes input (UINT4) into output (unsigned char). Assumes len is a multiple of 4. */
static void Encode (unsigned char *output, UINT4 *input, unsigned int len)
{
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4) {
output[j] = (unsigned char)(input[i] & 0xff);
output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
}
}
/* Decodes input (unsigned char) into output (UINT4). Assumes len is a multiple of 4. */
static void Decode (UINT4 *output, const unsigned char *input, unsigned int len)
{
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4)
output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
}
//===================================================================
unsigned Com_BlockChecksum (void const *buffer, int length)
{
int digest[4];
unsigned val;
MD4_CTX ctx;
MD4Init (&ctx);
MD4Update (&ctx, (unsigned char *)buffer, length);
MD4Final ( (unsigned char *)digest, &ctx);
val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3];
return val;
}

1248
code/qcommon/msg.cpp Normal file

File diff suppressed because it is too large Load Diff

566
code/qcommon/net_chan.cpp Normal file
View File

@@ -0,0 +1,566 @@
#include "../game/q_shared.h"
#include "qcommon.h"
/*
packet header
-------------
4 outgoing sequence. high bit will be set if this is a fragmented message
4 acknowledge sequence
[2 qport (only for client to server)]
[2 fragment start byte]
[2 fragment length. if < FRAGMENT_SIZE, this is the last fragment]
if the sequence number is -1, the packet should be handled as an out-of-band
message instead of as part of a netcon.
All fragments will have the same sequence numbers.
The qport field is a workaround for bad address translating routers that
sometimes remap the client's source port on a packet during gameplay.
If the base part of the net address matches and the qport matches, then the
channel matches even if the IP port differs. The IP port should be updated
to the new value before sending out any replies.
*/
#define MAX_PACKETLEN (MAX_MSGLEN) //(1400) // max size of a network packet
#define MAX_LOOPDATA 16 * 1024
#if (MAX_PACKETLEN > MAX_MSGLEN)
#error MAX_PACKETLEN must be <= MAX_MSGLEN
#endif
#if (MAX_LOOPDATA > MAX_MSGLEN)
#error MAX_LOOPDATA must be <= MAX_MSGLEN
#endif
#define FRAGMENT_SIZE (MAX_PACKETLEN - 100)
#define PACKET_HEADER 10 // two ints and a short
#define FRAGMENT_BIT (1<<31)
cvar_t *showpackets;
cvar_t *showdrop;
cvar_t *qport;
static char *netsrcString[2] = {
"client",
"server"
};
typedef struct {
char loopData[MAX_LOOPDATA];
int get, send;
} loopback_t;
static loopback_t *loopbacks = NULL;
/*
===============
Netchan_Init
===============
*/
void Netchan_Init( int port ) {
if (!loopbacks)
{
loopbacks = (loopback_t*) Z_Malloc(sizeof(loopback_t) * 2, TAG_NEWDEL, qtrue);
}
port &= 0xffff;
showpackets = Cvar_Get ("showpackets", "0", CVAR_TEMP );
showdrop = Cvar_Get ("showdrop", "0", CVAR_TEMP );
qport = Cvar_Get ("qport", va("%i", port), CVAR_INIT );
}
void Netchan_Shutdown()
{
if (loopbacks)
{
Z_Free(loopbacks);
loopbacks = 0;
}
}
/*
==============
Netchan_Setup
called to open a channel to a remote system
==============
*/
void Netchan_Setup( netsrc_t sock, netchan_t *chan, netadr_t adr, int qport ) {
memset (chan, 0, sizeof(*chan));
chan->sock = sock;
chan->remoteAddress = adr;
chan->qport = qport;
chan->incomingSequence = 0;
chan->outgoingSequence = 1;
}
/*
===============
Netchan_Transmit
Sends a message to a connection, fragmenting if necessary
A 0 length will still generate a packet.
================
*/
void Netchan_Transmit( netchan_t *chan, int length, const byte *data ) {
msg_t send;
byte send_buf[MAX_PACKETLEN];
int fragmentStart, fragmentLength;
fragmentStart = 0; // stop warning message
fragmentLength = 0;
// fragment large reliable messages
if ( length >= FRAGMENT_SIZE ) {
fragmentStart = 0;
do {
// write the packet header
MSG_Init (&send, send_buf, sizeof(send_buf));
MSG_WriteLong( &send, chan->outgoingSequence | FRAGMENT_BIT );
MSG_WriteLong( &send, chan->incomingSequence );
// send the qport if we are a client
if ( chan->sock == NS_CLIENT ) {
MSG_WriteShort( &send, qport->integer );
}
// copy the reliable message to the packet first
fragmentLength = FRAGMENT_SIZE;
if ( fragmentStart + fragmentLength > length ) {
fragmentLength = length - fragmentStart;
}
MSG_WriteShort( &send, fragmentStart );
MSG_WriteShort( &send, fragmentLength );
MSG_WriteData( &send, data + fragmentStart, fragmentLength );
// send the datagram
NET_SendPacket( chan->sock, send.cursize, send.data, chan->remoteAddress );
if ( showpackets->integer ) {
Com_Printf ("%s send %4i : s=%i ack=%i fragment=%i,%i\n"
, netsrcString[ chan->sock ]
, send.cursize
, chan->outgoingSequence - 1
, chan->incomingSequence
, fragmentStart, fragmentLength);
}
fragmentStart += fragmentLength;
// this exit condition is a little tricky, because a packet
// that is exactly the fragment length still needs to send
// a second packet of zero length so that the other side
// can tell there aren't more to follow
} while ( fragmentStart != length || fragmentLength == FRAGMENT_SIZE );
chan->outgoingSequence++;
return;
}
// write the packet header
MSG_Init (&send, send_buf, sizeof(send_buf));
MSG_WriteLong( &send, chan->outgoingSequence );
MSG_WriteLong( &send, chan->incomingSequence );
chan->outgoingSequence++;
// send the qport if we are a client
if ( chan->sock == NS_CLIENT ) {
MSG_WriteShort( &send, qport->integer );
}
MSG_WriteData( &send, data, length );
// send the datagram
NET_SendPacket( chan->sock, send.cursize, send.data, chan->remoteAddress );
if ( showpackets->integer ) {
Com_Printf( "%s send %4i : s=%i ack=%i\n"
, netsrcString[ chan->sock ]
, send.cursize
, chan->outgoingSequence - 1
, chan->incomingSequence );
}
}
/*
=================
Netchan_Process
Returns qfalse if the message should not be processed due to being
out of order or a fragment.
Msg must be large enough to hold MAX_MSGLEN, because if this is the
final fragment of a multi-part message, the entire thing will be
copied out.
=================
*/
qboolean Netchan_Process( netchan_t *chan, msg_t *msg ) {
int sequence, sequence_ack;
int qport;
int fragmentStart, fragmentLength;
qboolean fragmented;
// get sequence numbers
MSG_BeginReading( msg );
sequence = MSG_ReadLong( msg );
sequence_ack = MSG_ReadLong( msg );
// check for fragment information
if ( sequence & FRAGMENT_BIT ) {
sequence &= ~FRAGMENT_BIT;
fragmented = qtrue;
} else {
fragmented = qfalse;
}
// read the qport if we are a server
if ( chan->sock == NS_SERVER ) {
qport = MSG_ReadShort( msg );
}
// read the fragment information
if ( fragmented ) {
fragmentStart = MSG_ReadShort( msg );
fragmentLength = MSG_ReadShort( msg );
} else {
fragmentStart = 0; // stop warning message
fragmentLength = 0;
}
if ( showpackets->integer ) {
if ( fragmented ) {
Com_Printf( "%s recv %4i : s=%i ack=%i fragment=%i,%i\n"
, netsrcString[ chan->sock ]
, msg->cursize
, sequence
, sequence_ack
, fragmentStart, fragmentLength );
} else {
Com_Printf( "%s recv %4i : s=%i ack=%i\n"
, netsrcString[ chan->sock ]
, msg->cursize
, sequence
, sequence_ack );
}
}
//
// discard out of order or duplicated packets
//
if ( sequence <= chan->incomingSequence ) {
if ( showdrop->integer || showpackets->integer ) {
Com_Printf( "%s:Out of order packet %i at %i\n"
, NET_AdrToString( chan->remoteAddress )
, sequence
, chan->incomingSequence );
}
return qfalse;
}
//
// dropped packets don't keep the message from being used
//
chan->dropped = sequence - (chan->incomingSequence+1);
if ( chan->dropped > 0 ) {
if ( showdrop->integer || showpackets->integer ) {
Com_Printf( "%s:Dropped %i packets at %i\n"
, NET_AdrToString( chan->remoteAddress )
, chan->dropped
, sequence );
}
}
//
// if this is the final framgent of a reliable message,
// bump incoming_reliable_sequence
//
if ( fragmented ) {
// make sure we
if ( sequence != chan->fragmentSequence ) {
chan->fragmentSequence = sequence;
chan->fragmentLength = 0;
}
// if we missed a fragment, dump the message
if ( fragmentStart != chan->fragmentLength ) {
if ( showdrop->integer || showpackets->integer ) {
Com_Printf( "%s:Dropped a message fragment\n"
, NET_AdrToString( chan->remoteAddress )
, sequence);
}
// we can still keep the part that we have so far,
// so we don't need to clear chan->fragmentLength
return qfalse;
}
// copy the fragment to the fragment buffer
if ( fragmentLength < 0 || msg->readcount + fragmentLength > msg->cursize ||
chan->fragmentLength + fragmentLength > sizeof( chan->fragmentBuffer ) ) {
if ( showdrop->integer || showpackets->integer ) {
Com_Printf ("%s:illegal fragment length\n"
, NET_AdrToString (chan->remoteAddress ) );
}
return qfalse;
}
memcpy( chan->fragmentBuffer + chan->fragmentLength,
msg->data + msg->readcount, fragmentLength );
chan->fragmentLength += fragmentLength;
// if this wasn't the last fragment, don't process anything
if ( fragmentLength == FRAGMENT_SIZE ) {
return qfalse;
}
if ( chan->fragmentLength > msg->maxsize ) {
Com_Printf( "%s:fragmentLength %i > msg->maxsize\n"
, NET_AdrToString (chan->remoteAddress ),
chan->fragmentLength );
return qfalse;
}
// copy the full message over the partial fragment
// make sure the sequence number is still there
*(int *)msg->data = LittleLong( sequence );
memcpy( msg->data + 4, chan->fragmentBuffer, chan->fragmentLength );
msg->cursize = chan->fragmentLength + 4;
chan->fragmentLength = 0;
msg->readcount = 4; // past the sequence number
return qtrue;
}
//
// the message can now be read from the current message pointer
//
chan->incomingSequence = sequence;
chan->incomingAcknowledged = sequence_ack;
return qtrue;
}
//==============================================================================
/*
===================
NET_CompareBaseAdr
Compares without the port
===================
*/
qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b)
{
if (a.type != b.type)
return qfalse;
if (a.type == NA_LOOPBACK)
return qtrue;
Com_Printf ("NET_CompareBaseAdr: bad address type\n");
return qfalse;
}
const char *NET_AdrToString (netadr_t a)
{
static char s[64];
if (a.type == NA_LOOPBACK) {
Com_sprintf (s, sizeof(s), "loopback");
}
return s;
}
qboolean NET_CompareAdr (netadr_t a, netadr_t b)
{
if (a.type != b.type)
return qfalse;
if (a.type == NA_LOOPBACK)
return qtrue;
Com_Printf ("NET_CompareAdr: bad address type\n");
return qfalse;
}
qboolean NET_IsLocalAddress( netadr_t adr ) {
return adr.type == NA_LOOPBACK;
}
/*
=============================================================================
LOOPBACK BUFFERS FOR LOCAL PLAYER
=============================================================================
*/
qboolean NET_GetLoopPacket (netsrc_t sock, netadr_t *net_from, msg_t *net_message)
{
int i;
loopback_t *loop;
loop = &loopbacks[sock];
//If read and write positions are the same, nothing left to read.
if (loop->get == loop->send)
return qfalse;
//Get read position. Wrap if too close to end.
i = loop->get;
if(i > MAX_LOOPDATA - 4) {
i = 0;
}
//Get length of packet.
int length = *(int*)(loop->loopData + i);
i += 4;
//See if entire packet is at end of buffer or part is at the beginning.
if(i + length <= MAX_LOOPDATA) {
//Everything fits, full copy.
memcpy (net_message->data, loop->loopData + i, length);
net_message->cursize = length;
i += length;
loop->get = i;
} else {
//Doesn't all fit, partial copy
const int copyToEnd = MAX_LOOPDATA - i;
memcpy (net_message->data, loop->loopData + i, copyToEnd);
memcpy ((char*)net_message->data + copyToEnd,
loop->loopData, length - copyToEnd);
net_message->cursize = length;
loop->get = length - copyToEnd;
}
memset (net_from, 0, sizeof(*net_from));
net_from->type = NA_LOOPBACK;
return qtrue;
}
void NET_SendLoopPacket (netsrc_t sock, int length, const void *data, netadr_t to)
{
int i;
loopback_t *loop;
loop = &loopbacks[sock^1];
//Make sure there is enough free space in the buffer.
int freeSpace;
if(loop->send >= loop->get) {
freeSpace = MAX_LOOPDATA - (loop->send - loop->get);
} else {
freeSpace = loop->get - loop->send;
}
assert(freeSpace > length);
//Get write position. Wrap around if too close to end.
i = loop->send;
if(i > MAX_LOOPDATA - 4) {
i = 0;
}
//Write length of packet.
*(int*)(loop->loopData + i) = length;
i += 4;
//See if the whole packet will fit on the end of the buffer or if we
//need to write part of it back at the beginning.
if(i + length <= MAX_LOOPDATA) {
//Everything fits, full copy.
memcpy (loop->loopData + i, data, length);
i += length;
loop->send = i;
} else {
//Doesn't all fit, partial copy
int copyToEnd = MAX_LOOPDATA - i;
memcpy(loop->loopData + i, data, copyToEnd);
memcpy(loop->loopData, (char*)data + copyToEnd, length - copyToEnd);
loop->send = length - copyToEnd;
}
}
//=============================================================================
void NET_SendPacket( netsrc_t sock, int length, const void *data, netadr_t to ) {
// sequenced packets are shown in netchan, so just show oob
if ( showpackets->integer && *(int *)data == -1 ) {
Com_Printf ("send packet %4i\n", length);
}
if ( to.type == NA_LOOPBACK ) {
NET_SendLoopPacket (sock, length, data, to);
return;
}
}
/*
===============
NET_OutOfBandPrint
Sends a text message in an out-of-band datagram
================
*/
void QDECL NET_OutOfBandPrint( netsrc_t sock, netadr_t adr, const char *format, ... ) {
va_list argptr;
char string[MAX_MSGLEN];
// set the header
string[0] = (char) 0xff;
string[1] = (char) 0xff;
string[2] = (char) 0xff;
string[3] = (char) 0xff;
va_start( argptr, format );
vsprintf( string+4, format, argptr );
va_end( argptr );
// send the datagram
NET_SendPacket( sock, strlen( string ), string, adr );
}
/*
=============
NET_StringToAdr
Traps "localhost" for loopback, passes everything else to system
=============
*/
qboolean NET_StringToAdr( const char *s, netadr_t *a ) {
if (!strcmp (s, "localhost")) {
memset (a, 0, sizeof(*a));
a->type = NA_LOOPBACK;
return qtrue;
}
a->type = NA_BAD;
return qfalse;
}

17
code/qcommon/platform.h Normal file
View File

@@ -0,0 +1,17 @@
// Simple header file to dispatch to the relevant platform API headers
#ifndef _PLATFORM_H
#define _PLATFORM_H
#if defined(_XBOX)
#include <xtl.h>
#endif
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN 1
#endif
#if defined(_WINDOWS)
#include <windows.h>
#endif
#endif

855
code/qcommon/qcommon.h Normal file
View File

@@ -0,0 +1,855 @@
// qcommon.h -- definitions common between client and server, but not game.or ref modules
#ifndef __QCOMMON_H__
#define __QCOMMON_H__
#include "stringed_ingame.h"
#include "../qcommon/cm_public.h"
// some zone mem debugging stuff
#ifndef FINAL_BUILD
#ifdef _DEBUG
//
// both of these should be REM'd unless you specifically need them...
//
//#define DEBUG_ZONE_ALLOCS // adds __FILE__ and __LINE__ info to zone blocks, to see who's leaking
//#define DETAILED_ZONE_DEBUG_CODE // this slows things down a LOT, and is only for tracking nasty double-freeing Z_Malloc bugs
#endif
#endif
//============================================================================
//
// msg.c
//
typedef struct {
qboolean allowoverflow; // if false, do a Com_Error
qboolean overflowed; // set to true if the buffer size failed (with allowoverflow set)
byte *data;
int maxsize;
int cursize;
int readcount;
int bit; // for bitwise reads and writes
} msg_t;
void MSG_Init (msg_t *buf, byte *data, int length);
void MSG_Clear (msg_t *buf);
void *MSG_GetSpace (msg_t *buf, int length);
void MSG_WriteData (msg_t *buf, const void *data, int length);
struct usercmd_s;
struct entityState_s;
struct playerState_s;
void MSG_WriteBits( msg_t *msg, int value, int bits );
void MSG_WriteByte (msg_t *sb, int c);
void MSG_WriteShort (msg_t *sb, int c);
void MSG_WriteLong (msg_t *sb, int c);
void MSG_WriteString (msg_t *sb, const char *s);
void MSG_BeginReading (msg_t *sb);
int MSG_ReadBits( msg_t *msg, int bits );
int MSG_ReadByte (msg_t *sb);
int MSG_ReadShort (msg_t *sb);
int MSG_ReadLong (msg_t *sb);
char *MSG_ReadString (msg_t *sb);
char *MSG_ReadStringLine (msg_t *sb);
void MSG_ReadData (msg_t *sb, void *buffer, int size);
void MSG_WriteDeltaUsercmd( msg_t *msg, struct usercmd_s *from, struct usercmd_s *to );
void MSG_ReadDeltaUsercmd( msg_t *msg, struct usercmd_s *from, struct usercmd_s *to );
void MSG_WriteDeltaEntity( msg_t *msg, struct entityState_s *from, struct entityState_s *to
, qboolean force );
void MSG_ReadDeltaEntity( msg_t *msg, entityState_t *from, entityState_t *to,
int number );
void MSG_ReadEntity( msg_t *msg, entityState_t *to);
void MSG_WriteEntity( msg_t *msg, entityState_t *to, int removeNum);
void MSG_WriteDeltaPlayerstate( msg_t *msg, struct playerState_s *from, struct playerState_s *to );
void MSG_ReadDeltaPlayerstate( msg_t *msg, struct playerState_s *from, struct playerState_s *to );
//============================================================================
#ifdef _M_IX86
//
// optimised stuff for Intel, since most of our data is in that format anyway...
//
extern short BigShort (short l);
extern int BigLong (int l);
extern float BigFloat (float l);
#define LittleShort(l) l
#define LittleLong(l) l
#define LittleFloat(l) l
//
#else
//
// standard smart-swap code...
//
extern short BigShort (short l);
extern short LittleShort (short l);
extern int BigLong (int l);
extern int LittleLong (int l);
extern float BigFloat (float l);
extern float LittleFloat (float l);
//
#endif
/*
==============================================================
NET
==============================================================
*/
#ifdef _XBOX
#define PACKET_BACKUP 2
#else
#define PACKET_BACKUP 16 // number of old messages that must be kept on client and
#endif // server for delta comrpession and ping estimation
#define PACKET_MASK (PACKET_BACKUP-1)
#define MAX_PACKET_USERCMDS 32 // max number of usercmd_t in a packet
#define PORT_ANY -1
#define MAX_RELIABLE_COMMANDS 64 // max string commands buffered for restransmit
typedef enum {
NA_BAD, // an address lookup failed
NA_LOOPBACK,
} netadrtype_t;
typedef enum {
NS_CLIENT,
NS_SERVER
} netsrc_t;
typedef struct {
netadrtype_t type;
unsigned short port;
} netadr_t;
void NET_SendPacket (netsrc_t sock, int length, const void *data, netadr_t to);
void NET_OutOfBandPrint( netsrc_t net_socket, netadr_t adr, const char *format, ...);
qboolean NET_CompareAdr (netadr_t a, netadr_t b);
qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b);
qboolean NET_IsLocalAddress (netadr_t adr);
qboolean NET_IsLANAddress (netadr_t adr);
const char *NET_AdrToString (netadr_t a);
qboolean NET_StringToAdr ( const char *s, netadr_t *a);
qboolean NET_GetLoopPacket (netsrc_t sock, netadr_t *net_from, msg_t *net_message);
#define MAX_MSGLEN (1*17408) // max length of a message, which may
//#define MAX_MSGLEN (3*16384) // max length of a message, which may
// be fragmented into multiple packets
/*
Netchan handles packet fragmentation and out of order / duplicate suppression
*/
typedef struct {
netsrc_t sock;
int dropped; // between last packet and previous
netadr_t remoteAddress;
int qport; // qport value to write when transmitting
// sequencing variables
int incomingSequence;
int incomingAcknowledged;
int outgoingSequence;
// incoming fragment assembly buffer
int fragmentSequence;
int fragmentLength;
byte fragmentBuffer[MAX_MSGLEN];
} netchan_t;
void Netchan_Init( int qport );
void Netchan_Setup( netsrc_t sock, netchan_t *chan, netadr_t adr, int qport );
void Netchan_Transmit( netchan_t *chan, int length, const byte *data );
qboolean Netchan_Process( netchan_t *chan, msg_t *msg );
/*
==============================================================
PROTOCOL
==============================================================
*/
#define PROTOCOL_VERSION 40
#define PORT_SERVER 27960
// the svc_strings[] array in cl_parse.c should mirror this
//
// server to client
//
enum svc_ops_e {
svc_bad,
svc_nop,
svc_gamestate,
svc_configstring, // [short] [string] only in gamestate messages
svc_baseline, // only in gamestate messages
svc_serverCommand, // [string] to be executed by client game module
svc_download, // [short] size [size bytes]
svc_snapshot
};
//
// client to server
//
enum clc_ops_e {
clc_bad,
clc_nop,
clc_move, // [[usercmd_t]
clc_clientCommand // [string] message
};
/*
==============================================================
CMD
Command text buffering and command execution
==============================================================
*/
/*
Any number of commands can be added in a frame, from several different sources.
Most commands come from either keybindings or console line input, but entire text
files can be execed.
*/
void Cbuf_Init (void);
// allocates an initial text buffer that will grow as needed
void Cbuf_AddText( const char *text );
// Adds command text at the end of the buffer, does NOT add a final \n
void Cbuf_ExecuteText( int exec_when, const char *text );
// this can be used in place of either Cbuf_AddText or Cbuf_InsertText
void Cbuf_Execute (void);
// Pulls off \n terminated lines of text from the command buffer and sends
// them through Cmd_ExecuteString. Stops when the buffer is empty.
// Normally called once per frame, but may be explicitly invoked.
// Do not call inside a command function, or current args will be destroyed.
//===========================================================================
/*
Command execution takes a null terminated string, breaks it into tokens,
then searches for a command or variable that matches the first token.
*/
typedef void (*xcommand_t) (void);
void Cmd_Init (void);
void Cmd_AddCommand( const char *cmd_name, xcommand_t function );
// called by the init functions of other parts of the program to
// register commands and functions to call for them.
// The cmd_name is referenced later, so it should not be in temp memory
// if function is NULL, the command will be forwarded to the server
// as a clc_clientCommand instead of executed locally
void Cmd_RemoveCommand( const char *cmd_name );
char *Cmd_CompleteCommand( const char *partial );
// attempts to match a partial command for automatic command line completion
// returns NULL if nothing fits
int Cmd_Argc (void);
char *Cmd_Argv (int arg);
void Cmd_ArgvBuffer( int arg, char *buffer, int bufferLength );
char *Cmd_Args (void);
void Cmd_ArgsBuffer( char *buffer, int bufferLength );
// The functions that execute commands get their parameters with these
// functions. Cmd_Argv () will return an empty string, not a NULL
// if arg > argc, so string operations are allways safe.
void Cmd_TokenizeString( const char *text );
// Takes a null terminated string. Does not need to be /n terminated.
// breaks the string up into arg tokens.
void Cmd_ExecuteString( const char *text );
// Parses a single line of text into arguments and tries to execute it
// as if it was typed at the console
/*
==============================================================
CVAR
==============================================================
*/
/*
cvar_t variables are used to hold scalar or string variables that can be changed
or displayed at the console or prog code as well as accessed directly
in C code.
The user can access cvars from the console in three ways:
r_draworder prints the current value
r_draworder 0 sets the current value to 0
set r_draworder 0 as above, but creates the cvar if not present
Cvars are restricted from having the same names as commands to keep this
interface from being ambiguous.
The are also occasionally used to communicated information between different
modules of the program.
*/
cvar_t *Cvar_Get( const char *var_name, const char *value, int flags );
// creates the variable if it doesn't exist, or returns the existing one
// if it exists, the value will not be changed, but flags will be ORed in
// that allows variables to be unarchived without needing bitflags
// if value is "", the value will not override a previously set value.
void Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags );
// basically a slightly modified Cvar_Get for the interpreted modules
void Cvar_Update( vmCvar_t *vmCvar );
// updates an interpreted modules' version of a cvar
void Cvar_Set( const char *var_name, const char *value );
// will create the variable with no flags if it doesn't exist
void Cvar_SetValue( const char *var_name, float value );
// expands value to a string and calls Cvar_Set
float Cvar_VariableValue( const char *var_name );
int Cvar_VariableIntegerValue( const char *var_name );
// returns 0 if not defined or non numeric
char *Cvar_VariableString( const char *var_name );
void Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize );
// returns an empty string if not defined
char *Cvar_CompleteVariable( const char *partial );
// attempts to match a partial variable name for command line completion
// returns NULL if nothing fits
void Cvar_Reset( const char *var_name );
void Cvar_SetCheatState( void );
// reset all testing vars to a safe value
qboolean Cvar_Command( void );
// called by Cmd_ExecuteString when Cmd_Argv(0) doesn't match a known
// command. Returns true if the command was a variable reference that
// was handled. (print or change)
void Cvar_WriteVariables( fileHandle_t f );
// writes lines containing "set variable value" for all variables
// with the archive flag set to true.
void Cvar_Init( void );
char *Cvar_InfoString( int bit );
// returns an info string containing all the cvars that have the given bit set
// in their flags ( CVAR_USERINFO, CVAR_SERVERINFO, CVAR_SYSTEMINFO, etc )
void Cvar_InfoStringBuffer( int bit, char *buff, int buffsize );
void Cvar_Restart_f( void );
extern int cvar_modifiedFlags;
// whenever a cvar is modifed, its flags will be OR'd into this, so
// a single check can determine if any CVAR_USERINFO, CVAR_SERVERINFO,
// etc, variables have been modified since the last check. The bit
// can then be cleared to allow another change detection.
/*
==============================================================
FILESYSTEM
No stdio calls should be used by any part of the game, because
we need to deal with all sorts of directory and seperator char
issues.
==============================================================
*/
qboolean FS_Initialized();
void FS_InitFilesystem (void);
char **FS_ListFiles( const char *directory, const char *extension, int *numfiles );
// directory should not have either a leading or trailing /
// if extension is "/", only subdirectories will be returned
// the returned files will not include any directories or /
void FS_FreeFileList( char **filelist );
int FS_GetFileList( const char *path, const char *extension, char *listbuf, int bufsize );
fileHandle_t FS_FOpenFileWrite( const char *qpath );
// will properly create any needed paths and deal with seperater character issues
fileHandle_t FS_FOpenFileAppend( const char *filename ); // this was present already, but no public proto
qboolean FS_GetExtendedInfo_FOpenFileRead(const char *filename, char **ppsFilename, int *piOffset);
//return value is success of opening file, then ppsFilename and piOffset are valid
int FS_FOpenFileRead( const char *qpath, fileHandle_t *file, qboolean uniqueFILE );
// if uniqueFILE is true, then a new FILE will be fopened even if the file
// is found in an already open pak file. If uniqueFILE is false, you must call
// FS_FCloseFile instead of fclose, otherwise the pak FILE would be improperly closed
// It is generally safe to always set uniqueFILE to true, because the majority of
// file IO goes through FS_ReadFile, which Does The Right Thing already.
int FS_FileIsInPAK(const char *filename );
// returns 1 if a file is in the PAK file, otherwise -1
int FS_Write( const void *buffer, int len, fileHandle_t f );
int FS_Read( void *buffer, int len, fileHandle_t f );
// properly handles partial reads and reads from other dlls
void FS_FCloseFile( fileHandle_t f );
// note: you can't just fclose from another DLL, due to MS libc issues
int FS_ReadFile( const char *qpath, void **buffer );
// returns the length of the file
// a null buffer will just return the file length without loading
// as a quick check for existance. -1 length == not present
// A 0 byte will always be appended at the end, so string ops are safe.
// the buffer should be considered read-only, because it may be cached
// for other uses.
void FS_ForceFlush( fileHandle_t f );
// forces flush on files we're writing to.
void FS_FreeFile( void *buffer );
// frees the memory returned by FS_ReadFile
void FS_WriteFile( const char *qpath, const void *buffer, int size );
// writes a complete file, creating any subdirectories needed
int FS_filelength( fileHandle_t f );
// doesn't work for files that are opened from a pack file
int FS_FTell( fileHandle_t f );
// where are we?
void FS_Flush( fileHandle_t f );
void QDECL FS_Printf( fileHandle_t f, const char *fmt, ... );
// like fprintf
int FS_FOpenFileByMode( const char *qpath, fileHandle_t *f, fsMode_t mode );
// opens a file for reading, writing, or appending depending on the value of mode
int FS_Seek( fileHandle_t f, long offset, int origin );
// seek on a file (doesn't work for zip files!!!!!!!!)
// These 2 are generally only used by the save games, filenames are local (eg "saves/blah.sav")
//
void FS_DeleteUserGenFile( const char *filename );
qboolean FS_MoveUserGenFile ( const char *filename_src, const char *filename_dst );
/*
==============================================================
MISC
==============================================================
*/
//==========================================================
//
// NOTE NOTE NOTE!!!!!!!!!!!!!
//
// Any CPUID_XXXX defined as higher than CPUID_INTEL_MMX *must* have MMX support (eg like CPUID_AMD_3DNOW (0x30) has),
// this allows convenient MMX capability checking. If you for some reason want to support some new processor that does
// *NOT* have MMX (yeah, right), then define it as a lower number. -slc
//
// ( These values are returned by Sys_GetProcessorId )
//
#define CPUID_GENERIC 0 // any unrecognized processor
#define CPUID_AXP 0x10
#define CPUID_INTEL_UNSUPPORTED 0x20 // Intel 386/486
#define CPUID_INTEL_PENTIUM 0x21 // Intel Pentium or PPro
#define CPUID_INTEL_MMX 0x22 // Intel Pentium/MMX or P2/MMX
#define CPUID_INTEL_KATMAI 0x23 // Intel Katmai
#define CPUID_INTEL_WILLIAMETTE 0x24 // Intel Williamette
#define CPUID_AMD_3DNOW 0x30 // AMD K6 3DNOW!
//
//==========================================================
#define RoundUp(N, M) ((N) + ((unsigned int)(M)) - (((unsigned int)(N)) % ((unsigned int)(M))))
#define RoundDown(N, M) ((N) - (((unsigned int)(N)) % ((unsigned int)(M))))
char *CopyString( const char *in );
void Info_Print( const char *s );
void Com_BeginRedirect (char *buffer, int buffersize, void (*flush)(char *));
void Com_EndRedirect( void );
void QDECL Com_Printf( const char *fmt, ... );
void QDECL Com_PrintfAlways( const char *fmt, ... );
void QDECL Com_DPrintf( const char *fmt, ... );
void QDECL Com_Error( int code, const char *fmt, ... );
void Com_Quit_f( void );
int Com_EventLoop( void );
int Com_Milliseconds( void ); // will be journaled properly
unsigned Com_BlockChecksum( const void *buffer, int length );
int Com_Filter(char *filter, char *name, int casesensitive);
void Com_StartupVariable( const char *match );
// checks for and removes command line "+set var arg" constructs
// if match is NULL, all set commands will be executed, otherwise
// only a set with the exact name. Only used during startup.
extern cvar_t *com_developer;
extern cvar_t *com_speeds;
extern cvar_t *com_timescale;
extern cvar_t *com_sv_running;
extern cvar_t *com_cl_running;
extern cvar_t *com_viewlog; // 0 = hidden, 1 = visible, 2 = minimized
extern cvar_t *com_version;
// both client and server must agree to pause
extern cvar_t *cl_paused;
extern cvar_t *sv_paused;
// com_speeds times
extern int time_game;
extern int time_frontend;
extern int time_backend; // renderer backend time
extern int timeInTrace;
extern int timeInPVSCheck;
extern int numTraces;
extern int com_frameTime;
extern int com_frameMsec;
extern qboolean com_errorEntered;
#ifndef _XBOX
extern fileHandle_t com_journalFile;
extern fileHandle_t com_journalDataFile;
#endif
/*
--- low memory ----
server vm
server clipmap
---mark---
renderer initialization (shaders, etc)
UI vm
cgame vm
renderer map
renderer models
---free---
temp file loading
--- high memory ---
*/
int Z_Validate( void ); // also used to insure all of these are paged in
int Z_MemSize ( memtag_t eTag );
void Z_TagFree ( memtag_t eTag );
int Z_Free ( void *ptr ); //returns bytes freed
int Z_Size ( void *pvAddress);
void Z_MorphMallocTag( void *pvAddress, memtag_t eDesiredTag );
qboolean Z_IsFromZone(void *pvAddress, memtag_t eTag);
qboolean Z_IsFromTempPool(void *pvAddress);
#ifdef DEBUG_ZONE_ALLOCS
void *_D_Z_Malloc ( int iSize, memtag_t eTag, qboolean bZeroit, const char *psFile, int iLine );
void *_D_S_Malloc ( int iSize, const char *psFile, int iLine );
void _D_Z_Label ( const void *pvAddress, const char *pslabel );
#define Z_Malloc(_iSize, _eTag, _bZeroit) _D_Z_Malloc (_iSize, _eTag, _bZeroit, __FILE__, __LINE__)
#define S_Malloc(_iSize) _D_S_Malloc (_iSize, __FILE__, __LINE__) // NOT 0 filled memory only for small allocations
#define Z_Label(_ptr, _label) _D_Z_Label (_ptr, _label)
#else
void *Z_Malloc ( int iSize, memtag_t eTag, qboolean bZeroit, int iAlign = 4); // return memory NOT zero-filled by default
void *S_Malloc ( int iSize ); // NOT 0 filled memory only for small allocations
#define Z_Label(_ptr, _label) /* */
#endif
void Hunk_Clear( void );
void Hunk_ClearToMark( void );
void Hunk_SetMark( void );
// note the opposite default for 'bZeroIt' in Hunk_Alloc to Z_Malloc, since Hunk_Alloc always used to memset(0)...
//
inline void *Hunk_Alloc( int size, qboolean bZeroIt = qtrue)
{
return Z_Malloc(size, TAG_HUNKALLOC, bZeroIt);
}
// Used to re-tag new/delete allocations:
void Z_PushNewDeleteTag( memtag_t eTag );
void Z_PopNewDeleteTag( void );
void Com_TouchMemory( void );
// commandLine should not include the executable name (argv[0])
void Com_SetOrgAngles(vec3_t org,vec3_t angles);
void Com_Init( char *commandLine );
void Com_Frame( void );
void Com_Shutdown( void );
void Com_ShutdownZoneMemory(void);
void Com_ShutdownHunkMemory(void);
bool Com_ParseTextFile(const char *file, class CGenericParser2 &parser, bool cleanFirst = true);
CGenericParser2 *Com_ParseTextFile(const char *file, bool cleanFirst, bool writeable);
void Com_ParseTextFileDestroy(class CGenericParser2 &parser);
/*
==============================================================
CLIENT / SERVER SYSTEMS
==============================================================
*/
//
// client interface
//
void CL_InitKeyCommands( void );
// the keyboard binding interface must be setup before execing
// config files, but the rest of client startup will happen later
void CL_Init( void );
void CL_Disconnect( void );
void CL_Shutdown( void );
void CL_Frame( int msec,float fractionMsec );
qboolean CL_GameCommand( void );
void CL_KeyEvent (int key, qboolean down, unsigned time);
void CL_CharEvent( int key );
// char events are for field typing, not game control
void CL_MouseEvent( int dx, int dy, int time );
void CL_JoystickEvent( int axis, int value, int time );
void CL_PacketEvent( netadr_t from, msg_t *msg );
void CL_ConsolePrint( char *text );
void CL_MapLoading( void );
// do a screen update before starting to load a map
// when the server is going to load a new map, the entire hunk
// will be cleared, so the client must shutdown cgame, ui, and
// the renderer
void CL_ForwardCommandToServer( void );
// adds the current command line as a clc_clientCommand to the client message.
// things like godmode, noclip, etc, are commands directed to the server,
// so when they are typed in at the console, they will need to be forwarded.
void CL_FlushMemory( void );
// dump all memory on an error
void CL_StartHunkUsers( void );
void Key_WriteBindings( fileHandle_t f );
// for writing the config files
void S_ClearSoundBuffer( void );
// call before filesystem access
void SCR_DebugGraph (float value, int color); // FIXME: move logging to common?
//
// server interface
//
void SV_Init( void );
void SV_Shutdown( char *finalmsg );
void SV_Frame( int msec,float fractionMsec);
void SV_PacketEvent( netadr_t from, msg_t *msg );
qboolean SV_GameCommand( void );
//
// UI interface
//
qboolean UI_GameCommand( void );
/*
==============================================================
NON-PORTABLE SYSTEM SERVICES
==============================================================
*/
typedef enum {
AXIS_SIDE,
AXIS_FORWARD,
AXIS_UP,
AXIS_ROLL,
AXIS_YAW,
AXIS_PITCH,
MAX_JOYSTICK_AXIS
} joystickAxis_t;
typedef enum {
SE_NONE, // evTime is still valid
SE_KEY, // evValue is a key code, evValue2 is the down flag
SE_CHAR, // evValue is an ascii char
SE_MOUSE, // evValue and evValue2 are reletive signed x / y moves
SE_JOYSTICK_AXIS, // evValue is an axis number and evValue2 is the current state (-127 to 127)
SE_CONSOLE, // evPtr is a char*
SE_PACKET // evPtr is a netadr_t followed by data bytes to evPtrLength
} sysEventType_t;
typedef struct {
int evTime;
sysEventType_t evType;
int evValue, evValue2;
int evPtrLength; // bytes of data pointed to by evPtr, for journaling
void *evPtr; // this must be manually freed if not NULL
} sysEvent_t;
sysEvent_t Sys_GetEvent( void );
void Sys_Init (void);
char *Sys_GetCurrentUser( void );
void QDECL Sys_Error( const char *error, ...);
void Sys_Quit (void);
char *Sys_GetClipboardData( void ); // note that this isn't journaled...
void Sys_Print( const char *msg );
#ifdef _XBOX
void Sys_Log( const char *file, const char *msg );
void Sys_Log( const char *file, const void *buffer, int size, bool flush );
#endif
// Sys_Milliseconds should only be used for profiling purposes,
// any game related timing information should come from event timestamps
int Sys_Milliseconds (void);
// the system console is shown when a dedicated server is running
void Sys_DisplaySystemConsole( qboolean show );
int Sys_GetProcessorId( void );
void Sys_BeginStreamedFile( fileHandle_t f, int readahead );
void Sys_EndStreamedFile( fileHandle_t f );
int Sys_StreamedRead( void *buffer, int size, int count, fileHandle_t f );
void Sys_StreamSeek( fileHandle_t f, int offset, int origin );
void Sys_ShowConsole( int level, qboolean quitOnClose );
void Sys_SetErrorText( const char *text );
qboolean Sys_CheckCD( void );
void Sys_Mkdir( const char *path );
char *Sys_Cwd( void );
char *Sys_DefaultCDPath(void);
char *Sys_DefaultBasePath(void);
char **Sys_ListFiles( const char *directory, const char *extension, int *numfiles, qboolean wantsubs );
void Sys_FreeFileList( char **filelist );
void Sys_BeginProfiling( void );
void Sys_EndProfiling( void );
qboolean Sys_LowPhysicalMemory();
qboolean Sys_FileOutOfDate( LPCSTR psFinalFileName /* dest */, LPCSTR psDataFileName /* src */ );
qboolean Sys_CopyFile(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, qboolean bOverwrite);
//byte* SCR_GetScreenshot(qboolean *qValid);
//void SCR_SetScreenshot(const byte *pbData, int w, int h);
//byte* SCR_TempRawImage_ReadFromFile(const char *psLocalFilename, int *piWidth, int *piHeight, byte *pbReSampleBuffer, qboolean qbVertFlip);
//void SCR_TempRawImage_CleanUp();
inline int Round(float value)
{
return((int)floorf(value + 0.5f));
}
#ifdef _XBOX
//////////////////////////////
//
// Map Lump Loader
//
struct Lump
{
void* data;
int len;
Lump() : data(NULL), len(0) {}
~Lump() { clear(); }
void load(const char* map, const char* lump)
{
clear();
char path[MAX_QPATH];
Com_sprintf(path, MAX_QPATH, "%s/%s.mle", map, lump);
len = FS_ReadFile(path, &data);
if (len < 0) len = 0;
}
void clear(void)
{
if (data)
{
FS_FreeFile(data);
data = NULL;
}
}
};
#endif _XBOX
#endif //__QCOMMON_H__

634
code/qcommon/qfiles.h Normal file
View File

@@ -0,0 +1,634 @@
#ifndef __QFILES_H__
#define __QFILES_H__
//
// qfiles.h: quake file formats
// This file must be identical in the quake and utils directories
//
// surface geometry should not exceed these limits
#define SHADER_MAX_VERTEXES 1000
#define SHADER_MAX_INDEXES (6*SHADER_MAX_VERTEXES)
// the maximum size of game reletive pathnames
#define MAX_QPATH 64
/*
========================================================================
QVM files
========================================================================
*/
#define VM_MAGIC 0x12721444
typedef struct {
int vmMagic;
int instructionCount;
int codeOffset;
int codeLength;
int dataOffset;
int dataLength;
int litLength; // ( dataLength - litLength ) should be byteswapped on load
int bssLength; // zero filled memory appended to datalength
} vmHeader_t;
/*
========================================================================
PCX files are used for 8 bit images
========================================================================
*/
typedef struct {
char manufacturer;
char version;
char encoding;
char bits_per_pixel;
unsigned short xmin,ymin,xmax,ymax;
unsigned short hres,vres;
unsigned char palette[48];
char reserved;
char color_planes;
unsigned short bytes_per_line;
unsigned short palette_type;
char filler[58];
unsigned char data; // unbounded
} pcx_t;
/*
========================================================================
TGA files are used for 24/32 bit images
========================================================================
*/
typedef struct _TargaHeader {
unsigned char id_length, colormap_type, image_type;
unsigned short colormap_index, colormap_length;
unsigned char colormap_size;
unsigned short x_origin, y_origin, width, height;
unsigned char pixel_size, attributes;
} TargaHeader;
/*
========================================================================
.MD3 triangle model file format
========================================================================
*/
#define MD3_IDENT (('3'<<24)+('P'<<16)+('D'<<8)+'I')
#define MD3_VERSION 15
// limits
#define MD3_MAX_LODS 3
#define MD3_MAX_TRIANGLES 8192 // per surface
#define MD3_MAX_VERTS 4096 // per surface
#define MD3_MAX_SHADERS 256 // per surface
#define MD3_MAX_FRAMES 1024 // per model
#define MD3_MAX_SURFACES 32 + 32 // per model
#define MD3_MAX_TAGS 16 // per frame
// vertex scales
#define MD3_XYZ_SCALE (1.0/64)
typedef struct md3Frame_s {
vec3_t bounds[2];
vec3_t localOrigin;
float radius;
char name[16];
} md3Frame_t;
typedef struct md3Tag_s {
char name[MAX_QPATH]; // tag name
vec3_t origin;
vec3_t axis[3];
} md3Tag_t;
/*
** md3Surface_t
**
** CHUNK SIZE
** header sizeof( md3Surface_t )
** shaders sizeof( md3Shader_t ) * numShaders
** triangles[0] sizeof( md3Triangle_t ) * numTriangles
** st sizeof( md3St_t ) * numVerts
** XyzNormals sizeof( md3XyzNormal_t ) * numVerts * numFrames
*/
typedef struct {
int ident; //
char name[MAX_QPATH]; // polyset name
int flags;
int numFrames; // all surfaces in a model should have the same
int numShaders; // all surfaces in a model should have the same
int numVerts;
int numTriangles;
int ofsTriangles;
int ofsShaders; // offset from start of md3Surface_t
int ofsSt; // texture coords are common for all frames
int ofsXyzNormals; // numVerts * numFrames
int ofsEnd; // next surface follows
} md3Surface_t;
typedef struct {
char name[MAX_QPATH];
int shaderIndex; // for in-game use
} md3Shader_t;
typedef struct {
int indexes[3];
} md3Triangle_t;
typedef struct {
float st[2];
} md3St_t;
typedef struct {
short xyz[3];
short normal;
} md3XyzNormal_t;
typedef struct {
int ident;
int version;
char name[MAX_QPATH]; // model name
int flags;
int numFrames;
int numTags;
int numSurfaces;
int numSkins;
int ofsFrames; // offset for first frame
int ofsTags; // numFrames * numTags
int ofsSurfaces; // first surface, others follow
int ofsEnd; // end of file
} md3Header_t;
/*
==============================================================================
.BSP file format
==============================================================================
*/
#define BSP_IDENT (('P'<<24)+('S'<<16)+('B'<<8)+'R')
// little-endian "IBSP"
#define BSP_VERSION 1
// there shouldn't be any problem with increasing these values at the
// expense of more memory allocation in the utilities
#define MAX_MAP_MODELS 0x400
#define MAX_MAP_BRUSHES 0x8000
#define MAX_MAP_ENTITIES 0x800
#define MAX_MAP_ENTSTRING 0x40000
#define MAX_MAP_SHADERS 0x400
#define MAX_MAP_AREAS 0x100 // MAX_MAP_AREA_BYTES in q_shared must match!
#define MAX_MAP_FOGS 0x100
#define MAX_MAP_PLANES 0x20000
#define MAX_MAP_NODES 0x20000
#define MAX_MAP_BRUSHSIDES 0x20000
#define MAX_MAP_LEAFS 0x20000
#define MAX_MAP_LEAFFACES 0x20000
#define MAX_MAP_LEAFBRUSHES 0x40000
#define MAX_MAP_PORTALS 0x20000
#define MAX_MAP_LIGHTING 0x800000
#define MAX_MAP_LIGHTGRID 65535
#define MAX_MAP_LIGHTGRID_ARRAY 0x100000
#define MAX_MAP_VISIBILITY 0x400000
#define MAX_MAP_DRAW_SURFS 0x20000
#define MAX_MAP_DRAW_VERTS 0x80000
#define MAX_MAP_DRAW_INDEXES 0x80000
// key / value pair sizes in the entities lump
#define MAX_KEY 32
#define MAX_VALUE 1024
// the editor uses these predefined yaw angles to orient entities up or down
#define ANGLE_UP -1
#define ANGLE_DOWN -2
#define LIGHTMAP_WIDTH 128
#define LIGHTMAP_HEIGHT 128
//=============================================================================
#ifdef _XBOX
#pragma pack(push, 1)
typedef struct {
float mins[3], maxs[3];
int firstSurface;
unsigned short numSurfaces;
int firstBrush;
unsigned short numBrushes;
} dmodel_t;
typedef struct {
char shader[MAX_QPATH];
int surfaceFlags;
int contentFlags;
} dshader_t;
// planes x^1 is allways the opposite of plane x
typedef struct {
float normal[3];
float dist;
} dplane_t;
typedef struct {
int planeNum;
short children[2]; // negative numbers are -(leafs+1), not nodes
short mins[3]; // for frustom culling
short maxs[3];
} dnode_t;
typedef struct {
short cluster; // -1 = opaque cluster (do I still store these?)
signed char area;
short mins[3]; // for frustum culling
short maxs[3];
unsigned short firstLeafSurface;
unsigned short numLeafSurfaces;
unsigned short firstLeafBrush;
unsigned short numLeafBrushes;
} dleaf_t;
typedef struct {
int planeNum; // positive plane side faces out of the leaf
byte shaderNum;
} dbrushside_t;
typedef struct {
int firstSide;
byte numSides;
unsigned short shaderNum; // the shader that determines the contents flags
} dbrush_t;
typedef struct {
char shader[MAX_QPATH];
int brushNum;
int visibleSide; // the brush side that ray tests need to clip against (-1 == none)
} dfog_t;
// Light Style Constants
#define MAXLIGHTMAPS 4
#define LS_NORMAL 0x00
#define LS_UNUSED 0xfe
#define LS_NONE 0xff
#define MAX_LIGHT_STYLES 64
typedef struct {
float lightmap[MAXLIGHTMAPS][2];
float st[2];
short xyz[3];
short normal[3];
byte color[MAXLIGHTMAPS][4];
} mapVert_t;
#define DRAWVERT_LIGHTMAP_SCALE 32768.0f
// Change texture coordinates for TriSurfs to be even more fine grain.
// See below for note about keeping MIN_ST and MAX_ST up to date with
// ST_SCALE. These are in 4.12. Okay, how about 5.11? Gah. 9.7!
//#define DRAWVERT_ST_SCALE 4096.0f
//#define DRAWVERT_ST_SCALE 2048.0f
#define DRAWVERT_ST_SCALE 128.0f
// We use a slightly different format for the fixed point texture
// coords in Grid/Mesh drawverts: 10.6 rather than 12.4
// To be sure that this is ok, keep the max and min values equal to
// the largest and smallest whole numbers that can be stored using the
// format. (ie: Don't change GRID_DRAWVERT_ST_SCALE without changing
// the other two!) (And don't forget that we're using a bit for sign.)
#define GRID_DRAWVERT_ST_SCALE 64.0f
// This master switch controls whether we use compressed (4-bit per channel)
// vertex colors in draw and surface verts. It saves memory, but I'm switching
// it off, because we end up with that nasty green/purple streaking effect.
// If we ever figure out how to do it better... (1555? 565?)
//#define COMPRESS_VERTEX_COLORS
typedef struct {
short xyz[3];
short dvst[2];
short dvlightmap[MAXLIGHTMAPS][2];
short normal[3];
#ifdef _XBOX
vec3_t tangent;
#endif
#ifdef COMPRESS_VERTEX_COLORS
byte dvcolor[MAXLIGHTMAPS][1];
#else
byte dvcolor[MAXLIGHTMAPS][4];
#endif
} drawVert_t;
typedef struct {
byte flags;
byte latLong[2];
} dgrid_t;
typedef struct {
int code;
byte shaderNum;
signed char fogNum;
unsigned int verts; // high 20 bits are first vert, low 12 are num verts
unsigned int indexes; // high 20 bits are first index, low 12 are num indices
byte lightmapStyles[MAXLIGHTMAPS];
byte lightmapNum[MAXLIGHTMAPS];
short lightmapVecs[3];
} dface_t;
typedef struct {
int code;
byte shaderNum;
signed char fogNum;
unsigned int verts; // high 20 bits are first vert, low 12 are num verts
byte lightmapStyles[MAXLIGHTMAPS];
byte lightmapNum[MAXLIGHTMAPS];
short lightmapVecs[2][3]; // for patches, [0] and [1] are lodbounds
byte patchWidth;
byte patchHeight;
} dpatch_t;
typedef struct {
int code;
byte shaderNum;
signed char fogNum;
unsigned int verts; // high 20 bits are first vert, low 12 are num verts
unsigned int indexes; // high 20 bits are first index, low 12 are num indices
byte lightmapStyles[MAXLIGHTMAPS];
} dtrisurf_t;
typedef struct {
int code;
byte shaderNum;
signed char fogNum;
short origin[3];
short normal[3];
byte color[3];
} dflare_t;
#pragma pack(pop)
#else // _XBOX
typedef struct {
int fileofs, filelen;
} lump_t;
#define LUMP_ENTITIES 0
#define LUMP_SHADERS 1
#define LUMP_PLANES 2
#define LUMP_NODES 3
#define LUMP_LEAFS 4
#define LUMP_LEAFSURFACES 5
#define LUMP_LEAFBRUSHES 6
#define LUMP_MODELS 7
#define LUMP_BRUSHES 8
#define LUMP_BRUSHSIDES 9
#define LUMP_DRAWVERTS 10
#define LUMP_DRAWINDEXES 11
#define LUMP_FOGS 12
#define LUMP_SURFACES 13
#define LUMP_LIGHTMAPS 14
#define LUMP_LIGHTGRID 15
#define LUMP_VISIBILITY 16
#define LUMP_LIGHTARRAY 17
#define HEADER_LUMPS 18
typedef struct {
int ident;
int version;
lump_t lumps[HEADER_LUMPS];
} dheader_t;
typedef struct {
float mins[3], maxs[3];
int firstSurface, numSurfaces;
int firstBrush, numBrushes;
} dmodel_t;
typedef struct dshader_s {
char shader[MAX_QPATH];
int surfaceFlags;
int contentFlags;
} dshader_t;
// planes x^1 is allways the opposite of plane x
typedef struct {
float normal[3];
float dist;
} dplane_t;
typedef struct {
int planeNum;
int children[2]; // negative numbers are -(leafs+1), not nodes
int mins[3]; // for frustom culling
int maxs[3];
} dnode_t;
typedef struct {
int cluster; // -1 = opaque cluster (do I still store these?)
int area;
int mins[3]; // for frustum culling
int maxs[3];
int firstLeafSurface;
int numLeafSurfaces;
int firstLeafBrush;
int numLeafBrushes;
} dleaf_t;
typedef struct {
int planeNum; // positive plane side faces out of the leaf
int shaderNum;
int drawSurfNum;
} dbrushside_t;
typedef struct {
int firstSide;
int numSides;
int shaderNum; // the shader that determines the contents flags
} dbrush_t;
typedef struct {
char shader[MAX_QPATH];
int brushNum;
int visibleSide; // the brush side that ray tests need to clip against (-1 == none)
} dfog_t;
// Light Style Constants
#define MAXLIGHTMAPS 4
#define LS_NORMAL 0x00
#define LS_UNUSED 0xfe
#define LS_NONE 0xff
#define MAX_LIGHT_STYLES 64
typedef struct {
vec3_t xyz;
float st[2];
float lightmap[MAXLIGHTMAPS][2];
vec3_t normal;
byte color[MAXLIGHTMAPS][4];
} mapVert_t;
typedef struct {
vec3_t xyz;
float st[2];
float lightmap[MAXLIGHTMAPS][2];
vec3_t normal;
byte color[MAXLIGHTMAPS][4];
} drawVert_t;
typedef struct
{
byte ambientLight[MAXLIGHTMAPS][3];
byte directLight[MAXLIGHTMAPS][3];
byte styles[MAXLIGHTMAPS];
byte latLong[2];
} dgrid_t;
typedef enum {
MST_BAD,
MST_PLANAR,
MST_PATCH,
MST_TRIANGLE_SOUP,
MST_FLARE
} mapSurfaceType_t;
typedef struct {
int shaderNum;
int fogNum;
int surfaceType;
int firstVert;
int numVerts;
int firstIndex;
int numIndexes;
byte lightmapStyles[MAXLIGHTMAPS], vertexStyles[MAXLIGHTMAPS];
int lightmapNum[MAXLIGHTMAPS];
int lightmapX[MAXLIGHTMAPS], lightmapY[MAXLIGHTMAPS];
int lightmapWidth, lightmapHeight;
vec3_t lightmapOrigin;
vec3_t lightmapVecs[3]; // for patches, [0] and [1] are lodbounds
int patchWidth;
int patchHeight;
} dsurface_t;
#endif _XBOX
typedef enum //# hunkAllocType_e
{
HA_MISC,
HA_MAP,
HA_SHADERS,
HA_LIGHTING,
HA_FOG,
HA_PATCHES,
HA_VIS,
HA_SUBMODELS,
HA_MODELS,
MAX_HA_TYPES
} hunkAllocType_t;
/////////////////////////////////////////////////////////////
//
// Defines and structures required for fonts
#define GLYPH_COUNT 256
// Must match define in stmparse.h
#define STYLE_DROPSHADOW 0x80000000
#define STYLE_BLINK 0x40000000
#define SET_MASK 0x00ffffff
typedef struct
{
short width; // number of pixels wide
short height; // number of scan lines
short horizAdvance; // number of pixels to advance to the next char
short horizOffset; // x offset into space to render glyph
int baseline; // y offset
float s; // x start tex coord
float t; // y start tex coord
float s2; // x end tex coord
float t2; // y end tex coord
} glyphInfo_t;
// this file corresponds 1:1 with the "*.fontdat" files, so don't change it unless you're going to
// recompile the fontgen util and regenerate all the fonts!
//
typedef struct dfontdat_s
{
glyphInfo_t mGlyphs[GLYPH_COUNT];
short mPointSize;
short mHeight; // max height of font
short mAscender;
short mDescender;
short mKoreanHack; // unused field, written out by John's fontgen program but we have to leave it there for disk structs <sigh>
} dfontdat_t;
/////////////////// fonts end ////////////////////////////////////
#endif

725
code/qcommon/sparc.h Normal file
View File

@@ -0,0 +1,725 @@
/*
* UNPUBLISHED -- Rights reserved under the copyright laws of the
* United States. Use of a copyright notice is precautionary only and
* does not imply publication or disclosure.
*
* THIS DOCUMENTATION CONTAINS CONFIDENTIAL AND PROPRIETARY INFORMATION
* OF VICARIOUS VISIONS, INC. ANY DUPLICATION, MODIFICATION,
* DISTRIBUTION, OR DISCLOSURE IS STRICTLY PROHIBITED WITHOUT THE PRIOR
* EXPRESS WRITTEN PERMISSION OF VICARIOUS VISIONS, INC.
*/
/*
AUTHOR: Dave Calvin
CREATED: 2002-05-07
SParse ARray Compressor. Given an array, this class reduces the memory
needed to store the array by eliminating the most-frequently used element.
The remaining elements are increased in size by one integer.
If the compressed data would be larger than the original data, the
original data is stored as is.
Compression is O(2N) where N is the number of elements to compress.
Decompression is O(log M + N) where M is the number of elements after
compression (CompressedLength()) and N is the number of elements to decompress.
Decompression is O(1) when the same or smaller amount of data is requested as
the last decompression.
The pointer returned by Decompress() is valid until the class is destroyed
or a new call is made to Compress() or Decompress().
Elements must define operator==, operator!=, and sizeof.
*/
#ifndef __SPARC_H
#define __SPARC_H
#ifdef _GAMECUBE
#define SPARC_BIG_ENDIAN
#endif
//Bigger than a short, smaller than an int.
#pragma pack(push, 1)
struct NotSoShort
{
unsigned char bytes[3];
NotSoShort(void) {}
NotSoShort(unsigned int source) {
#ifdef SPARC_BIG_ENDIAN
bytes[2] = source & 0xFF;
bytes[1] = (source >> 8) & 0xFF;
bytes[0] = (source >> 16) & 0xFF;
#else
bytes[0] = source & 0xFF;
bytes[1] = (source >> 8) & 0xFF;
bytes[2] = (source >> 16) & 0xFF;
#endif
}
inline unsigned int GetValue(void) {
#ifdef SPARC_BIG_ENDIAN
return (bytes[0] << 16) | (bytes[1] << 8) | bytes[2];
#else
return (bytes[2] << 16) | (bytes[1] << 8) | bytes[0];
#endif
}
inline bool operator==(unsigned int cmp) {
#ifdef SPARC_BIG_ENDIAN
return cmp == ((*(unsigned int*)bytes) >> 8);
#else
return cmp == ((*(unsigned int*)bytes) & 0x00FFFFFF);
#endif
}
bool operator<(unsigned int cmp) {
unsigned int tmp = *(unsigned int*)bytes;
#ifdef SPARC_BIG_ENDIAN
tmp >>= 8;
#else
tmp &= 0x00FFFFFF;
#endif
return tmp < cmp;
}
bool operator<=(unsigned int cmp) {
unsigned int tmp = *(unsigned int*)bytes;
#ifdef SPARC_BIG_ENDIAN
tmp >>= 8;
#else
tmp &= 0x00FFFFFF;
#endif
return tmp <= cmp;
}
bool operator>(unsigned int cmp) {
unsigned int tmp = *(unsigned int*)bytes;
#ifdef SPARC_BIG_ENDIAN
tmp >>= 8;
#else
tmp &= 0x00FFFFFF;
#endif
return tmp > cmp;
}
};
//Compressed data is made up of these elements.
template <class T, class U>
struct SPARCElement
{
T data;
U offset;
};
#pragma pack(pop)
inline unsigned int SPARC_SWAP32(unsigned int x, bool doSwap) {
if (doSwap) {
return ((unsigned int)( ( (x & 0xff000000) >> 24)
+ ( (x & 0x00ff0000) >> 8 )
+ ( (x & 0x0000ff00) << 8 )
+ ( (x & 0x000000ff) << 24 ) ));
}
return x;
}
inline NotSoShort SPARC_SWAP24(NotSoShort x, bool doSwap) {
if (doSwap) {
x.bytes[0] ^= x.bytes[2];
x.bytes[2] ^= x.bytes[0];
x.bytes[0] ^= x.bytes[2];
}
return x;
}
inline unsigned short SPARC_SWAP16(unsigned short x, bool doSwap) {
if (doSwap) {
return ((unsigned short)( ( (x & 0xff00) >> 8)
+ ( (x & 0x00ff) << 8 ) ));
}
return x;
}
//The core of the SPARC system. T is the data type to be compressed.
//U is the data type needed to store offsets information in the compressed
//data. Smaller U makes for better compression but bigger data requires
//larger U.
template <class T, class U>
class SPARCCore
{
private:
//Using compression or just storing clear data?
bool compressionUsed;
//Compressed data and its length.
SPARCElement<T, U> *compressedData;
unsigned int compressedLength;
//Decompression cache.
T *decompressedData;
unsigned int decompressedOffset;
unsigned int decompressedLength;
//Element which was removed to compress.
T removedElement;
//Length of original data before compression.
unsigned int originalLength;
//Memory allocators.
void* (*Allocator)(unsigned int size);
void (*Deallocator)(void *ptr);
//Destroy all allocated memory.
void Cleanup(void) {
if(compressedData) {
if(Deallocator) {
Deallocator(compressedData);
} else {
delete [] compressedData;
}
compressedData = NULL;
}
if(decompressedData) {
if(Deallocator) {
Deallocator(decompressedData);
} else {
delete [] decompressedData;
}
decompressedData = NULL;
}
}
void Init(void) {
compressionUsed = false;
compressedData = NULL;
originalLength = 0;
compressedLength = 0;
decompressedData = NULL;
decompressedOffset = 0;
decompressedLength = 0;
}
//Binary search for the compressed element most closely matching 'offset'.
SPARCElement<T, U> *FindDecompStart(unsigned int offset)
{
unsigned int startPoint = compressedLength / 2;
unsigned int divisor = 4;
unsigned int leap;
while(1) {
if(compressedData[startPoint].offset <= offset &&
compressedData[startPoint+1].offset > offset) {
if(compressedData[startPoint].offset == offset) {
return &compressedData[startPoint];
} else {
return &compressedData[startPoint+1];
}
}
leap = compressedLength / divisor;
if(leap < 1) {
leap = 1;
} else {
divisor *= 2;
}
if(compressedData[startPoint].offset > offset) {
startPoint -= leap;
} else {
startPoint += leap;
}
}
}
public:
SPARCCore(void) {
Init();
Allocator = NULL;
Deallocator = NULL;
}
~SPARCCore(void) {
Cleanup();
}
void SetAllocator(void* (*alloc)(unsigned int size),
void (*dealloc)(void *ptr)) {
Allocator = alloc;
Deallocator = dealloc;
}
//Just store the array without compression.
unsigned int Store(const T *array, unsigned int length) {
//Destroy old data.
Cleanup();
Init();
//Allocate memory and copy array.
if(Allocator) {
decompressedData = (T*)Allocator(length * sizeof(T));
} else {
decompressedData = new T[length];
}
compressedLength = length;
memcpy(decompressedData, array, sizeof(T) * length);
//Set length.
originalLength = length;
return CompressedSize();
}
//Load compressed data directly.
unsigned int Load(const char *array, unsigned int length) {
//Destroy old data.
Cleanup();
Init();
//Restore some attributes.
compressionUsed = (bool)*array++;
assert(sizeof(T) == 1); //For now only support characters.
removedElement = *(T*)array;
array += sizeof(T);
originalLength = *(unsigned int*)array;
array += sizeof(unsigned int);
compressedLength = *(unsigned int*)array;
array += sizeof(unsigned int);
//Allocate memory and copy array.
if (compressionUsed) {
if(Allocator) {
compressedData = (SPARCElement<T, U>*)
Allocator(compressedLength * sizeof(SPARCElement<T, U>));
} else {
compressedData = new SPARCElement<T, U>[compressedLength];
}
memcpy(compressedData, array,
compressedLength * sizeof(SPARCElement<T, U>));
}
else {
if(Allocator) {
decompressedData = (T*)Allocator(
compressedLength * sizeof(T));
} else {
decompressedData = new T[compressedLength];
}
memcpy(decompressedData, array, compressedLength * sizeof(T));
}
return CompressedSize();
}
//Save state for later restoration.
unsigned int Save(char *array, unsigned int length, bool doSwap) {
//Figure out how much space is needed.
unsigned int size = sizeof(char) + sizeof(T) +
sizeof(unsigned int) + sizeof(unsigned int);
if (compressionUsed) {
size += compressedLength * sizeof(SPARCElement<T, U>);
}
else {
size += compressedLength * sizeof(T);
}
assert(length >= size);
//Save some attributes.
*array++ = (char)compressionUsed;
assert(sizeof(T) == 1); //For now only support characters.
*(T*)array = removedElement;
array += sizeof(T);
*(unsigned int*)array = SPARC_SWAP32(originalLength, doSwap);
array += sizeof(unsigned int);
*(unsigned int*)array = SPARC_SWAP32(compressedLength, doSwap);
array += sizeof(unsigned int);
//Store compressed data (or uncompressed data if none exists)
if (compressionUsed) {
for (unsigned int i = 0; i < compressedLength; ++i) {
//Copy the data element. For now only support characters.
((SPARCElement<T, U> *)array)[i].data = compressedData[i].data;
//Copy the offset to the next unique element.
if (sizeof(U) == 1) {
((SPARCElement<T, U> *)array)[i].offset =
compressedData[i].offset;
}
else if (sizeof(U) == 2) {
((SPARCElement<T, unsigned short> *)array)[i].offset =
SPARC_SWAP16(*(unsigned short*)&compressedData[i].offset,
doSwap);
}
else if (sizeof(U) == 3) {
((SPARCElement<T, NotSoShort> *)array)[i].offset =
SPARC_SWAP24(*(NotSoShort*)&compressedData[i].offset,
doSwap);
}
else if (sizeof(U) == 4) {
((SPARCElement<T, unsigned int> *)array)[i].offset =
SPARC_SWAP32(*(unsigned int*)&compressedData[i].offset,
doSwap);
}
}
}
else {
memcpy(array, decompressedData, compressedLength * sizeof(T));
}
return size;
}
//Compresses this array, returns the compressed size. Compresses
//by eliminating the given element.
unsigned int Compress(const T *array, unsigned int length, T removal) {
unsigned int i;
unsigned int numRemove = 0;
SPARCElement<T, U> *compress;
//Destroy old data.
Cleanup();
Init();
//Count number of elements to remove. Can't remove first or
//last element (prevents boundary conditions).
for(i=1; i<length-1; i++) {
if(array[i] == removal) {
numRemove++;
}
}
compressedLength = length - numRemove;
originalLength = length;
//If we're going to allocate more memory than was originally used,
//just store the data.
if(sizeof(SPARCElement<T, U>) * compressedLength >=
sizeof(T) * length) {
Store(array, length);
return CompressedSize();
}
//Allocate memory for compressed elements.
if(Allocator) {
compressedData = (SPARCElement<T, U>*)
Allocator(compressedLength * sizeof(SPARCElement<T, U>));
} else {
compressedData = new SPARCElement<T, U>[compressedLength];
}
compressionUsed = true;
//Fill compressed array. First and last elements go in no matter
//what.
compressedData[0].data = array[0];
compressedData[0].offset = 0;
compress = &compressedData[1];
for(i=1; i<length-1; i++) {
if(array[i] != removal) {
compress->data = array[i];
compress->offset = i;
compress++;
}
}
compress->data = array[i];
compress->offset = i;
//Store removal value for decompression purposes.
removedElement = removal;
//Store original length for bounds checking.
originalLength = length;
//Return the compressed size.
return CompressedSize();
}
//Get the compressed data size in bytes, or 0 if nothing stored.
unsigned int CompressedSize(void) {
return compressedLength * sizeof(SPARCElement<T, U>);
}
//Get the decompressed data starting at offset and ending at
//offset + length. Returns NULL on error.
const T *Decompress(unsigned int offset, unsigned int length) {
SPARCElement<T, U> *decomp = NULL;
unsigned int i;
//If data isn't compressed, just return a pointers.
if(!compressionUsed) {
return decompressedData + offset;
}
//If last decompression falls within offset and length, just return
//a pointer.
if(decompressedData && decompressedOffset <= offset &&
decompressedOffset + decompressedLength >= offset + length) {
return decompressedData + offset - decompressedOffset;
}
//Allocate new space for decompression if length has changed.
if(length != decompressedLength) {
//Destroy old data first.
if(decompressedData) {
if(Deallocator) {
Deallocator(decompressedData);
} else {
delete [] decompressedData;
}
}
if(Allocator) {
decompressedData = (T*)Allocator(length * sizeof(T));
} else {
decompressedData = new T[length];
}
}
decompressedOffset = offset;
decompressedLength = length;
//Find position to start decompressing from.
decomp = FindDecompStart(offset);
if(!decomp) { //should never happen
assert(0);
return NULL;
}
//Decompress the data.
for(i=0; i < length; i++) {
if(decomp->offset == i + offset) {
decompressedData[i] = decomp->data;
decomp++;
} else {
decompressedData[i] = removedElement;
}
}
return decompressedData;
}
};
//The user-interface to SPARC. Automatically selects the best core based
//on data size.
template <class T>
class SPARC
{
private:
void *core;
unsigned char offsetBytes;
//Memory allocators.
void* (*Allocator)(unsigned int size);
void (*Deallocator)(void *ptr);
public:
SPARC(void) {
core = NULL;
offsetBytes = 0;
Allocator = NULL;
Deallocator = NULL;
}
~SPARC(void) {
Release();
};
void SetAllocator(void* (*alloc)(unsigned int size),
void (*dealloc)(void *ptr)) {
Allocator = alloc;
Deallocator = dealloc;
}
//Select a core, cast it to the right type and return the size.
unsigned int CompressedSize(void) {
if(!core) {
return 0;
}
switch(offsetBytes) {
case 1:
return ((SPARCCore<T, unsigned char>*)core)->CompressedSize();
case 2:
return ((SPARCCore<T, unsigned short>*)core)->CompressedSize();
case 3:
return ((SPARCCore<T, NotSoShort>*)core)->CompressedSize();
case 4:
return ((SPARCCore<T, unsigned int>*)core)->CompressedSize();
}
return 0;
}
//Always use the same core type since we won't be compressing.
unsigned int Store(const T *array, unsigned int length)
{
Release();
offsetBytes = 1;
core = new SPARCCore<T, unsigned char>;
((SPARCCore<T, unsigned char>*)core)->
SetAllocator(Allocator, Deallocator);
return ((SPARCCore<T, unsigned char>*)core)-> Store(array, length);
}
//Load compressed data directly.
unsigned int Load(const char *array, unsigned int length) {
Release();
offsetBytes = *array++;
switch (offsetBytes) {
case 1:
core = new SPARCCore<T, unsigned char>;
((SPARCCore<T, unsigned char>*)core)->
SetAllocator(Allocator, Deallocator);
return ((SPARCCore<T, unsigned char>*)core)->
Load(array, length-1);
case 2:
core = new SPARCCore<T, unsigned short>;
((SPARCCore<T, unsigned short>*)core)->
SetAllocator(Allocator, Deallocator);
return ((SPARCCore<T, unsigned short>*)core)->
Load(array, length-1);
case 3:
core = new SPARCCore<T, NotSoShort>;
((SPARCCore<T, NotSoShort>*)core)->
SetAllocator(Allocator, Deallocator);
return ((SPARCCore<T, NotSoShort>*)core)->
Load(array, length-1);
case 4:
core = new SPARCCore<T, unsigned int>;
((SPARCCore<T, unsigned int>*)core)->
SetAllocator(Allocator, Deallocator);
return ((SPARCCore<T, unsigned int>*)core)->
Load(array, length-1);
default:
assert(false);
return 0;
}
}
//Save compressed data into array.
unsigned int Save(char *array, unsigned int length, bool doSwap) {
*array++ = offsetBytes;
switch (offsetBytes) {
case 1:
return ((SPARCCore<T, unsigned char>*)core)->
Save(array, length-1, doSwap);
case 2:
return ((SPARCCore<T, unsigned short>*)core)->
Save(array, length-1, doSwap);
case 3:
return ((SPARCCore<T, NotSoShort>*)core)->
Save(array, length-1, doSwap);
case 4:
return ((SPARCCore<T, unsigned int>*)core)->
Save(array, length-1, doSwap);
default:
assert(false);
return 0;
}
}
//Create the smallest core possible for the given data.
unsigned int Compress(const T *array, unsigned int length, T removal) {
Release();
if(length < 256) {
offsetBytes = 1;
core = new SPARCCore<T, unsigned char>;
((SPARCCore<T, unsigned char>*)core)->
SetAllocator(Allocator, Deallocator);
return ((SPARCCore<T, unsigned char>*)core)->
Compress(array, length, removal);
} else if(length < 65536) {
offsetBytes = 2;
core = new SPARCCore<T, unsigned short>;
((SPARCCore<T, unsigned short>*)core)->
SetAllocator(Allocator, Deallocator);
return ((SPARCCore<T, unsigned short>*)core)->
Compress(array, length, removal);
} else if(length < 16777216) {
offsetBytes = 3;
core = new SPARCCore<T, NotSoShort>;
((SPARCCore<T, NotSoShort>*)core)->
SetAllocator(Allocator, Deallocator);
return ((SPARCCore<T, NotSoShort>*)core)->
Compress(array, length, removal);
} else {
offsetBytes = 4;
core = new SPARCCore<T, unsigned int>;
((SPARCCore<T, unsigned int>*)core)->
SetAllocator(Allocator, Deallocator);
return ((SPARCCore<T, unsigned int>*)core)->
Compress(array, length, removal);
}
}
//Cast to the correct core type and decompress.
const T *Decompress(unsigned int offset, unsigned int length) {
if(!core) {
return NULL;
}
switch(offsetBytes) {
case 1:
return ((SPARCCore<T, unsigned char>*)core)->
Decompress(offset, length);
case 2:
return ((SPARCCore<T, unsigned short>*)core)->
Decompress(offset, length);
case 3:
return ((SPARCCore<T, NotSoShort>*)core)->
Decompress(offset, length);
case 4:
return ((SPARCCore<T, unsigned int>*)core)->
Decompress(offset, length);
}
return NULL;
}
//Destroy all compressed data and the current decompressed buffer.
void Release(void) {
if(core) {
switch(offsetBytes) {
case 1:
delete (SPARCCore<T, unsigned char>*)core;
break;
case 2:
delete (SPARCCore<T, unsigned short>*)core;
break;
case 3:
delete (SPARCCore<T, NotSoShort>*)core;
break;
case 4:
delete (SPARCCore<T, unsigned int>*)core;
break;
}
core = NULL;
}
}
};
#endif

120
code/qcommon/sstring.h Normal file
View File

@@ -0,0 +1,120 @@
// Filename:- sstring.h
//
// Gil's string template, used to replace Microsoft's <string> vrsion which doesn't compile under certain stl map<>
// conditions...
#ifndef SSTRING_H
#define SSTRING_H
template<int MaxSize>
class sstring
{
struct SStorage
{
char data[MaxSize];
};
SStorage mStorage;
public:
/* don't figure we need this
template<int oMaxSize>
sstring(const sstring<oMaxSize> &o)
{
assert(strlen(o.mStorage.data)<MaxSize);
strcpy(mStorage.data,o.mStorage.data);
}
*/
sstring(const sstring<MaxSize> &o)
{
//strcpy(mStorage.data,o.mStorage.data);
Q_strncpyz(mStorage.data,o.mStorage.data,sizeof(mStorage.data),qtrue);
}
sstring(const char *s)
{
//assert(strlen(s)<MaxSize);
//strcpy(mStorage.data,s);
Q_strncpyz(mStorage.data,s,sizeof(mStorage.data),qtrue);
}
sstring()
{
mStorage.data[0]=0;
}
/* don't figure we need this
template<int oMaxSize>
sstring<oMaxSize> & operator =(const sstring<oMaxSize> &o)
{
assert(strlen(o.mStorage.data)<MaxSize);
strcpy(mStorage.data,o.mStorage.data);
return *this;
}
*/
sstring<MaxSize> & operator=(const sstring<MaxSize> &o)
{
//strcpy(mStorage.data,o.mStorage.data);
Q_strncpyz(mStorage.data,o.mStorage.data,sizeof(mStorage.data),qtrue);
return *this;
}
sstring<MaxSize> & operator=(const char *s)
{
assert(strlen(s)<MaxSize);
//strcpy(mStorage.data,s);
Q_strncpyz(mStorage.data,s,sizeof(mStorage.data),qtrue);
return *this;
}
char *c_str()
{
return mStorage.data;
}
const char *c_str() const
{
return mStorage.data;
}
int capacity() const
{
return MaxSize; // not sure if this should be MaxSize-1? depends if talking bytes or strlen space I guess
}
int length() const
{
return strlen(mStorage.data);
}
bool operator==(const sstring<MaxSize> &o) const
{
if (!strcmpi(mStorage.data,o.mStorage.data))
{
return true;
}
return false;
}
bool operator!=(const sstring<MaxSize> &o) const
{
if (strcmpi(mStorage.data,o.mStorage.data)!=0)
{
return true;
}
return false;
}
bool operator<(const sstring<MaxSize> &o) const
{
if (strcmpi(mStorage.data,o.mStorage.data)<0)
{
return true;
}
return false;
}
bool operator>(const sstring<MaxSize> &o) const
{
if (strcmpi(mStorage.data,o.mStorage.data)>0)
{
return true;
}
return false;
}
};
typedef sstring<MAX_QPATH> sstring_t;
#endif // #ifndef SSTRING_H
/////////////////// eof ////////////////////

View File

@@ -0,0 +1,985 @@
// Filename:- stringed_ingame.cpp
//
// This file is designed to be pasted into each game project that uses the StringEd package's files.
// You can alter the way it does things by (eg) replacing STL with RATL, BUT LEAVE THE OVERALL
// FUNCTIONALITY THE SAME, or if I ever make any funadamental changes to the way the package works
// then you're going to be SOOL (shit out of luck ;-)...
//
//////////////////////////////////////////////////
//
// stuff common to all qcommon files...
#include "../server/server.h"
#include "../game/q_shared.h"
#include "qcommon.h"
#include "../qcommon/fixedmap.h"
#include "../zlib/zlib.h"
//
//////////////////////////////////////////////////
#pragma warning ( disable : 4511 ) // copy constructor could not be generated
#pragma warning ( disable : 4512 ) // assignment operator could not be generated
#pragma warning ( disable : 4663 ) // C++ language change: blah blah template crap blah blah
#include "stringed_ingame.h"
#include "stringed_interface.h"
// Needed for DWORD and XC_LANGUAGE defines:
#include <xtl.h>
///////////////////////////////////////////////
// some STL stuff...
#include <string>
using namespace std;
///////////////////////////////////////////////
cvar_t *se_language = NULL;
// Yeah, it's hardcoded. I don't give a shit.
#define MAX_STRING_ENTRIES 4096
typedef struct SE_Entry_s
{
string m_strString;
} SE_Entry_t;
//typedef map <string, SE_Entry_t> mapStringEntries_t;
class CStringEdPackage
{
private:
SE_BOOL m_bEndMarkerFound_ParseOnly;
string m_strCurrentEntryRef_ParseOnly;
string m_strCurrentEntryEnglish_ParseOnly;
string m_strCurrentFileRef_ParseOnly;
string m_strLoadingLanguage_ParseOnly; // eg "german"
SE_BOOL m_bLoadingEnglish_ParseOnly;
public:
CStringEdPackage()
{
Z_PushNewDeleteTag( TAG_STRINGED );
m_Strings = new VVFixedMap< char *, unsigned long >( MAX_STRING_ENTRIES );
Z_PopNewDeleteTag();
Clear( SE_FALSE );
}
~CStringEdPackage()
{
Clear( SE_FALSE );
}
// Text entries, indexed by crc32 of reference:
VVFixedMap< char *, unsigned long > *m_Strings;
// mapStringEntries_t m_StringEntries; // needs to be in public space now
void Clear( SE_BOOL bChangingLanguages );
void SetupNewFileParse( LPCSTR psFileName );
SE_BOOL ReadLine( LPCSTR &psParsePos, char *psDest );
LPCSTR ParseLine( LPCSTR psLine );
LPCSTR ExtractLanguageFromPath( LPCSTR psFileName );
SE_BOOL EndMarkerFoundDuringParse( void )
{
return m_bEndMarkerFound_ParseOnly;
}
private:
void AddEntry( LPCSTR psLocalReference );
int GetNumStrings(void);
void SetString( LPCSTR psLocalReference, LPCSTR psNewString, SE_BOOL bEnglishDebug );
SE_BOOL SetReference( int iIndex, LPCSTR psNewString );
LPCSTR GetCurrentFileName(void);
LPCSTR GetCurrentReference_ParseOnly( void );
SE_BOOL CheckLineForKeyword( LPCSTR psKeyword, LPCSTR &psLine);
LPCSTR InsideQuotes( LPCSTR psLine );
LPCSTR ConvertCRLiterals_Read( LPCSTR psString );
void REMKill( char *psBuffer );
char *Filename_PathOnly( LPCSTR psFilename );
char *Filename_WithoutPath(LPCSTR psFilename);
char *Filename_WithoutExt(LPCSTR psFilename);
};
CStringEdPackage TheStringPackage;
void CStringEdPackage::Clear( SE_BOOL bChangingLanguages )
{
// m_StringEntries.clear();
m_bEndMarkerFound_ParseOnly = SE_FALSE;
m_strCurrentEntryRef_ParseOnly = "";
m_strCurrentEntryEnglish_ParseOnly = "";
//
// the other vars are cleared in SetupNewFileParse(), and are ok to not do here.
//
}
// loses anything after the path (if any), (eg) "dir/name.bmp" becomes "dir"
// (copes with either slash-scheme for names)
//
// (normally I'd call another function for this, but this is supposed to be engine-independant,
// so a certain amount of re-invention of the wheel is to be expected...)
//
char *CStringEdPackage::Filename_PathOnly(LPCSTR psFilename)
{
static char sString[ iSE_MAX_FILENAME_LENGTH ];
strcpy(sString,psFilename);
char *p1= strrchr(sString,'\\');
char *p2= strrchr(sString,'/');
char *p = (p1>p2)?p1:p2;
if (p)
*p=0;
return sString;
}
// returns (eg) "dir/name" for "dir/name.bmp"
// (copes with either slash-scheme for names)
//
// (normally I'd call another function for this, but this is supposed to be engine-independant,
// so a certain amount of re-invention of the wheel is to be expected...)
//
char *CStringEdPackage::Filename_WithoutExt(LPCSTR psFilename)
{
static char sString[ iSE_MAX_FILENAME_LENGTH ];
strcpy(sString,psFilename);
char *p = strrchr(sString,'.');
char *p2= strrchr(sString,'\\');
char *p3= strrchr(sString,'/');
// special check, make sure the first suffix we found from the end wasn't just a directory suffix (eg on a path'd filename with no extension anyway)
//
if (p &&
(p2==0 || (p2 && p>p2)) &&
(p3==0 || (p3 && p>p3))
)
*p=0;
return sString;
}
// returns actual filename only, no path
// (copes with either slash-scheme for names)
//
// (normally I'd call another function for this, but this is supposed to be engine-independant,
// so a certain amount of re-invention of the wheel is to be expected...)
//
char *CStringEdPackage::Filename_WithoutPath(LPCSTR psFilename)
{
static char sString[ iSE_MAX_FILENAME_LENGTH ];
LPCSTR psCopyPos = psFilename;
while (*psFilename)
{
if (*psFilename == '/' || *psFilename == '\\')
psCopyPos = psFilename+1;
psFilename++;
}
strcpy(sString,psCopyPos);
return sString;
}
LPCSTR CStringEdPackage::ExtractLanguageFromPath( LPCSTR psFileName )
{
return Filename_WithoutPath( Filename_PathOnly( psFileName ) );
}
void CStringEdPackage::SetupNewFileParse( LPCSTR psFileName )
{
char sString[ iSE_MAX_FILENAME_LENGTH ];
strcpy(sString, Filename_WithoutPath( Filename_WithoutExt( psFileName ) ));
Q_strupr(sString);
m_strCurrentFileRef_ParseOnly = sString; // eg "OBJECTIVES"
m_strLoadingLanguage_ParseOnly = ExtractLanguageFromPath( psFileName );
m_bLoadingEnglish_ParseOnly = (!stricmp( m_strLoadingLanguage_ParseOnly.c_str(), "english" )) ? SE_TRUE : SE_FALSE;
}
// returns SE_TRUE if supplied keyword found at line start (and advances supplied ptr past any whitespace to next arg (or line end if none),
//
// else returns SE_FALSE...
//
SE_BOOL CStringEdPackage::CheckLineForKeyword( LPCSTR psKeyword, LPCSTR &psLine)
{
if (!Q_stricmpn(psKeyword, psLine, strlen(psKeyword)) )
{
psLine += strlen(psKeyword);
// skip whitespace to arrive at next item...
//
while ( *psLine == '\t' || *psLine == ' ' )
{
psLine++;
}
return SE_TRUE;
}
return SE_FALSE;
}
// change "\n" to '\n' (i.e. 2-byte char-string to 1-byte ctrl-code)...
// (or "\r\n" in editor)
//
LPCSTR CStringEdPackage::ConvertCRLiterals_Read( LPCSTR psString )
{
static string str;
str = psString;
int iLoc;
while ( (iLoc = str.find("\\n")) != -1 )
{
str[iLoc ] = '\n';
str.erase( iLoc+1,1 );
}
return str.c_str();
}
// kill off any "//" onwards part in the line, but NOT if it's inside a quoted string...
//
void CStringEdPackage::REMKill( char *psBuffer )
{
char *psScanPos = psBuffer;
char *p;
int iDoubleQuotesSoFar = 0;
// scan forwards in case there are more than one (and the first is inside quotes)...
//
while ( (p=strstr(psScanPos,"//")) != NULL)
{
// count the number of double quotes before this point, if odd number, then we're inside quotes...
//
int iDoubleQuoteCount = iDoubleQuotesSoFar;
for (int i=0; i<p-psScanPos; i++)
{
if (psScanPos[i] == '"')
{
iDoubleQuoteCount++;
}
}
if (!(iDoubleQuoteCount&1))
{
// not inside quotes, so kill line here...
//
*p='\0';
//
// and remove any trailing whitespace...
//
if (psScanPos[0]) // any strlen? (else access violation with -1 below)
{
int iWhiteSpaceScanPos = strlen(psScanPos)-1;
while (iWhiteSpaceScanPos>=0 && isspace(psScanPos[iWhiteSpaceScanPos]))
{
psScanPos[iWhiteSpaceScanPos--] = '\0';
}
}
return;
}
else
{
// inside quotes (blast), oh well, skip past and keep scanning...
//
psScanPos = p+1;
iDoubleQuotesSoFar = iDoubleQuoteCount;
}
}
}
// returns true while new lines available to be read...
//
SE_BOOL CStringEdPackage::ReadLine( LPCSTR &psParsePos, char *psDest )
{
if (psParsePos[0])
{
LPCSTR psLineEnd = strchr(psParsePos, '\n');
if (psLineEnd)
{
int iCharsToCopy = (psLineEnd - psParsePos);
strncpy(psDest, psParsePos, iCharsToCopy);
psDest[iCharsToCopy] = '\0';
psParsePos += iCharsToCopy;
while (*psParsePos && strchr("\r\n",*psParsePos))
{
psParsePos++; // skip over CR or CR/LF pairs
}
}
else
{
// last line...
//
strcpy(psDest, psParsePos);
psParsePos += strlen(psParsePos);
}
// clean up the line...
//
if (psDest[0])
{
int iWhiteSpaceScanPos = strlen(psDest)-1;
while (iWhiteSpaceScanPos>=0 && isspace(psDest[iWhiteSpaceScanPos]))
{
psDest[iWhiteSpaceScanPos--] = '\0';
}
REMKill( psDest );
}
return SE_TRUE;
}
return SE_FALSE;
}
// remove any outside quotes from this supplied line, plus any leading or trailing whitespace...
//
LPCSTR CStringEdPackage::InsideQuotes( LPCSTR psLine )
{
// I *could* replace this string object with a declared array, but wasn't sure how big to leave it, and it'd have to
// be static as well, hence permanent. (problem on consoles?)
//
static string str;
str = ""; // do NOT join to above line
// skip any leading whitespace...
//
while (*psLine == ' ' || *psLine == '\t')
{
psLine++;
}
// skip any leading quote...
//
if (*psLine == '"')
{
psLine++;
}
// assign it...
//
str = psLine;
if (psLine[0])
{
// lose any trailing whitespace...
//
while ( str.c_str()[ strlen(str.c_str()) -1 ] == ' ' ||
str.c_str()[ strlen(str.c_str()) -1 ] == '\t'
)
{
str.erase( strlen(str.c_str()) -1, 1);
}
// lose any trailing quote...
//
if (str.c_str()[ strlen(str.c_str()) -1 ] == '"')
{
str.erase( strlen(str.c_str()) -1, 1);
}
}
// and return it...
//
return str.c_str();
}
// this copes with both foreigners using hi-char values (eg the french using 0x92 instead of 0x27
// for a "'" char), as well as the fact that our buggy fontgen program writes out zeroed glyph info for
// some fonts anyway (though not all, just as a gotcha).
//
// New bit, instead of static buffer (since XBox guys are desperately short of mem) I return a malloc'd buffer now,
// so remember to free it!
//
static char *CopeWithDumbStringData( LPCSTR psSentence, LPCSTR psThisLanguage )
{
const int iBufferSize = strlen(psSentence)*3; // *3 to allow for expansion of anything even stupid string consisting entirely of elipsis chars
char *psNewString = (char *) Z_Malloc(iBufferSize, TAG_TEMP_WORKSPACE, qfalse);
Q_strncpyz(psNewString, psSentence, iBufferSize);
// this is annoying, I have to just guess at which languages to do it for (ie NOT ASIAN/MBCS!!!) since the
// string system was deliberately (and correctly) designed to not know or care whether it was doing SBCS
// or MBCS languages, because it was never envisioned that I'd have to clean up other people's mess.
//
// Ok, bollocks to it, this will have to do. Any other languages that come later and have bugs in their text can
// get fixed by them typing it in properly in the first place...
//
if (!stricmp(psThisLanguage,"ENGLISH") ||
!stricmp(psThisLanguage,"FRENCH") ||
!stricmp(psThisLanguage,"GERMAN") ||
!stricmp(psThisLanguage,"ITALIAN") ||
!stricmp(psThisLanguage,"SPANISH") ||
!stricmp(psThisLanguage,"POLISH") ||
!stricmp(psThisLanguage,"RUSSIAN")
)
{
char *p;
// strXLS_Speech.Replace(va("%c",0x92),va("%c",0x27)); // "'"
while ((p=strchr(psNewString,0x92))!=NULL) // "rich" (and illegal) apostrophe
{
*p = 0x27;
}
// strXLS_Speech.Replace(va("%c",0x93),"\""); // smart quotes -> '"'
while ((p=strchr(psNewString,0x93))!=NULL)
{
*p = '"';
}
// strXLS_Speech.Replace(va("%c",0x94),"\""); // smart quotes -> '"'
while ((p=strchr(psNewString,0x94))!=NULL)
{
*p = '"';
}
// strXLS_Speech.Replace(va("%c",0x0B),"."); // full stop
while ((p=strchr(psNewString,0x0B))!=NULL)
{
*p = '.';
}
// strXLS_Speech.Replace(va("%c",0x85),"..."); // "..."-char -> 3-char "..."
while ((p=strchr(psNewString,0x85))!=NULL) // "rich" (and illegal) apostrophe
{
memmove(p+2,p,strlen(p));
*p++ = '.';
*p++ = '.';
*p = '.';
}
// strXLS_Speech.Replace(va("%c",0x91),va("%c",0x27)); // "'"
while ((p=strchr(psNewString,0x91))!=NULL)
{
*p = 0x27;
}
// strXLS_Speech.Replace(va("%c",0x96),va("%c",0x2D)); // "-"
while ((p=strchr(psNewString,0x96))!=NULL)
{
*p = 0x2D;
}
// strXLS_Speech.Replace(va("%c",0x97),va("%c",0x2D)); // "-"
while ((p=strchr(psNewString,0x97))!=NULL)
{
*p = 0x2D;
}
// bug fix for picky grammatical errors, replace "?." with "? "
//
while ((p=strstr(psNewString,"?."))!=NULL)
{
p[1] = ' ';
}
// StripEd and our print code don't support tabs...
//
while ((p=strchr(psNewString,0x09))!=NULL)
{
*p = ' ';
}
}
return psNewString;
}
// return is either NULL for good else error message to display...
//
LPCSTR CStringEdPackage::ParseLine( LPCSTR psLine )
{
LPCSTR psErrorMessage = NULL;
if (psLine)
{
if (CheckLineForKeyword( sSE_KEYWORD_VERSION, psLine ))
{
// VERSION "1"
//
LPCSTR psVersionNumber = InsideQuotes( psLine );
int iVersionNumber = atoi( psVersionNumber );
if (iVersionNumber != iSE_VERSION)
{
psErrorMessage = va("Unexpected version number %d, expecting %d!\n", iVersionNumber, iSE_VERSION);
}
}
else
if ( CheckLineForKeyword(sSE_KEYWORD_CONFIG, psLine)
|| CheckLineForKeyword(sSE_KEYWORD_FILENOTES, psLine)
|| CheckLineForKeyword(sSE_KEYWORD_NOTES, psLine)
)
{
// not used ingame, but need to absorb the token
}
else
if (CheckLineForKeyword(sSE_KEYWORD_REFERENCE, psLine))
{
// REFERENCE GUARD_GOOD_TO_SEE_YOU
//
LPCSTR psLocalReference = InsideQuotes( psLine );
AddEntry( psLocalReference );
}
else
if (CheckLineForKeyword(sSE_KEYWORD_ENDMARKER, psLine))
{
// ENDMARKER
//
m_bEndMarkerFound_ParseOnly = SE_TRUE; // the only major error checking I bother to do (for file truncation)
}
else
if (!Q_stricmpn(sSE_KEYWORD_LANG, psLine, strlen(sSE_KEYWORD_LANG)))
{
// LANG_ENGLISH "GUARD: Good to see you, sir. Taylor is waiting for you in the clean tent. We need to get you suited up. "
//
LPCSTR psReference = GetCurrentReference_ParseOnly();
if ( psReference[0] )
{
psLine += strlen(sSE_KEYWORD_LANG);
// what language is this?...
//
LPCSTR psWordEnd = psLine;
while (*psWordEnd && *psWordEnd != ' ' && *psWordEnd != '\t')
{
psWordEnd++;
}
char sThisLanguage[1024]={0};
int iCharsToCopy = psWordEnd - psLine;
if (iCharsToCopy > sizeof(sThisLanguage)-1)
{
iCharsToCopy = sizeof(sThisLanguage)-1;
}
strncpy(sThisLanguage, psLine, iCharsToCopy); // already declared as {0} so no need to zero-cap dest buffer
psLine += strlen(sThisLanguage);
LPCSTR _psSentence = ConvertCRLiterals_Read( InsideQuotes( psLine ) );
// Dammit, I hate having to do crap like this just because other people mess up and put
// stupid data in their text, so I have to cope with it.
//
// note hackery with _psSentence and psSentence because of const-ness. bleurgh. Just don't ask.
//
char *psSentence = CopeWithDumbStringData( _psSentence, sThisLanguage );
if ( m_bLoadingEnglish_ParseOnly )
{
// if loading just "english", then go ahead and store it...
//
SetString( psReference, psSentence, SE_FALSE );
}
else
{
// if loading a foreign language...
//
SE_BOOL bSentenceIsEnglish = (!stricmp(sThisLanguage,"english")) ? SE_TRUE: SE_FALSE; // see whether this is the english master or not
// this check can be omitted, I'm just being extra careful here...
//
if ( !bSentenceIsEnglish )
{
// basically this is just checking that an .STE file override is the same language as the .STR...
//
if (stricmp( m_strLoadingLanguage_ParseOnly.c_str(), sThisLanguage ))
{
psErrorMessage = va("Language \"%s\" found when expecting \"%s\"!\n", sThisLanguage, m_strLoadingLanguage_ParseOnly.c_str());
}
}
if (!psErrorMessage)
{
SetString( psReference, psSentence, bSentenceIsEnglish );
}
}
Z_Free( psSentence );
}
else
{
psErrorMessage = "Error parsing file: Unexpected \"" sSE_KEYWORD_LANG "\"\n";
}
}
else
{
psErrorMessage = va("Unknown keyword at linestart: \"%s\"\n", psLine);
}
}
return psErrorMessage;
}
// returns reference of string being parsed, else "" for none.
//
LPCSTR CStringEdPackage::GetCurrentReference_ParseOnly( void )
{
return m_strCurrentEntryRef_ParseOnly.c_str();
}
// add new string entry (during parse)
//
void CStringEdPackage::AddEntry( LPCSTR psLocalReference )
{
// the reason I don't just assign it anyway is because the optional .STE override files don't contain flags,
// and therefore would wipe out the parsed flags of the .STR file...
//
/*
mapStringEntries_t::iterator itEntry = m_StringEntries.find( va("%s_%s",m_strCurrentFileRef_ParseOnly.c_str(), psLocalReference) );
if (itEntry == m_StringEntries.end())
{
SE_Entry_t SE_Entry;
m_StringEntries[ va("%s_%s", m_strCurrentFileRef_ParseOnly.c_str(), psLocalReference) ] = SE_Entry;
}
*/
m_strCurrentEntryRef_ParseOnly = psLocalReference;
}
void CStringEdPackage::SetString( LPCSTR psLocalReference, LPCSTR psNewString, SE_BOOL bEnglishDebug )
{
const char *ref = va("%s_%s", m_strCurrentFileRef_ParseOnly.c_str(), psLocalReference);
unsigned long refCrc = crc32( 0, (const Bytef *)ref, strlen(ref) );
if ( bEnglishDebug )
{
// This is the leading english text of a foreign sentence pair (so it's the debug-key text):
// don't store, just make a note in-case #same shows up:
m_strCurrentEntryEnglish_ParseOnly = psNewString;
}
else if ( m_bLoadingEnglish_ParseOnly )
{
// It's the english text, and we're loading english. Add it!
int len = strlen( psNewString );
char *strData = (char *) Z_Malloc( len + 1, TAG_STRINGED, qfalse );
strcpy( strData, psNewString );
m_Strings->Insert( strData, refCrc );
}
else
{
// It's foreign text, we're going to add it, but we need to check for #same
if (!stricmp(psNewString, sSE_EXPORT_SAME))
{
// If it's #same, then copy the stored english version:
int len = m_strCurrentEntryEnglish_ParseOnly.length();
char *strData = (char *) Z_Malloc( len + 1, TAG_STRINGED, qfalse );
strcpy( strData, m_strCurrentEntryEnglish_ParseOnly.c_str() );
m_Strings->Insert( strData, refCrc );
}
else
{
// Explicit foreign text. Add it!
int len = strlen( psNewString );
char *strData = (char *) Z_Malloc( len + 1, TAG_STRINGED, qfalse );
strcpy( strData, psNewString );
m_Strings->Insert( strData, refCrc );
}
}
}
// filename is local here, eg: "strings/german/obj.str"
//
// return is either NULL for good else error message to display...
//
static LPCSTR SE_Load_Actual( LPCSTR psFileName, SE_BOOL bSpeculativeLoad )
{
LPCSTR psErrorMessage = NULL;
unsigned char *psLoadedData = SE_LoadFileData( psFileName );
if ( psLoadedData )
{
// now parse the data...
//
char *psParsePos = (char *) psLoadedData;
TheStringPackage.SetupNewFileParse( psFileName );
char sLineBuffer[16384]; // should be enough for one line of text (some of them can be BIG though)
while ( !psErrorMessage && TheStringPackage.ReadLine((LPCSTR &) psParsePos, sLineBuffer ) )
{
if (strlen(sLineBuffer))
{
psErrorMessage = TheStringPackage.ParseLine( sLineBuffer );
}
}
SE_FreeFileDataAfterLoad( psLoadedData);
if (!psErrorMessage && !TheStringPackage.EndMarkerFoundDuringParse())
{
psErrorMessage = va("Truncated file, failed to find \"%s\" at file end!", sSE_KEYWORD_ENDMARKER);
}
}
else
{
if ( bSpeculativeLoad )
{
// then it's ok to not find the file, so do nothing...
}
else
{
psErrorMessage = va("Unable to load \"%s\"!", psFileName);
}
}
return psErrorMessage;
}
static LPCSTR SE_GetFoundFile( string &strResult )
{
static char sTemp[1024/*MAX_PATH*/];
if (!strlen(strResult.c_str()))
return NULL;
strncpy(sTemp,strResult.c_str(),sizeof(sTemp)-1);
sTemp[sizeof(sTemp)-1]='\0';
char *psSemiColon = strchr(sTemp,';');
if ( psSemiColon)
{
*psSemiColon = '\0';
strResult.erase(0,(psSemiColon-sTemp)+1);
}
else
{
// no semicolon found, probably last entry? (though i think even those have them on, oh well)
//
strResult.erase();
}
// strlwr(sTemp); // just for consistancy and set<> -> set<> erasure checking etc
return sTemp;
}
//////////// API entry points from rest of game.... //////////////////////////////
// filename is local here, eg: "strings/german/obj.str"
//
// return is either NULL for good else error message to display...
//
LPCSTR SE_Load( LPCSTR psFileName, SE_BOOL bFailIsCritical = SE_TRUE )
{
////////////////////////////////////////////////////
//
// ingame here tends to pass in names without paths, but I expect them when doing a language load, so...
//
char sTemp[1000]={0};
if (!strchr(psFileName,'/'))
{
strcpy(sTemp,sSE_STRINGS_DIR);
strcat(sTemp,"/");
if (se_language)
{
strcat(sTemp,se_language->string);
strcat(sTemp,"/");
}
}
strcat(sTemp,psFileName);
COM_DefaultExtension( sTemp, sizeof(sTemp), sSE_INGAME_FILE_EXTENSION);
psFileName = &sTemp[0];
//
////////////////////////////////////////////////////
LPCSTR psErrorMessage = SE_Load_Actual( psFileName, SE_FALSE );
// check for any corresponding / overriding .STE files and load them afterwards...
//
if ( !psErrorMessage )
{
char sFileName[ iSE_MAX_FILENAME_LENGTH ];
strncpy( sFileName, psFileName, sizeof(sFileName)-1 );
sFileName[ sizeof(sFileName)-1 ] = '\0';
char *p = strrchr( sFileName, '.' );
if (p && strlen(p) == strlen(sSE_EXPORT_FILE_EXTENSION))
{
strcpy( p, sSE_EXPORT_FILE_EXTENSION );
psErrorMessage = SE_Load_Actual( sFileName, SE_TRUE );
}
}
if (psErrorMessage)
{
if (bFailIsCritical)
{
// TheStringPackage.Clear(TRUE); // Will we want to do this? Any errors that arise should be fixed immediately
Com_Error( ERR_DROP, "SE_Load(): Couldn't load \"%s\"!\n\nError: \"%s\"\n", psFileName, psErrorMessage );
}
else
{
Com_DPrintf(S_COLOR_YELLOW "SE_Load(): Couldn't load \"%s\"!\n", psFileName );
}
}
return psErrorMessage;
}
// convenience-function for the main GetString call...
//
LPCSTR SE_GetString( LPCSTR psPackageReference, LPCSTR psStringReference)
{
char sReference[256]; // will always be enough, I've never seen one more than about 30 chars long
sprintf(sReference,"%s_%s", psPackageReference, psStringReference);
return SE_GetString( Q_strupr(sReference) );
}
LPCSTR SE_GetString( LPCSTR psPackageAndStringReference )
{
int len = strlen( psPackageAndStringReference );
char sReference[256]; // will always be enough, I've never seen one more than about 30 chars long
assert( len < sizeof(sReference) );
Q_strncpyz( sReference, psPackageAndStringReference, sizeof(sReference) );
Q_strupr( sReference );
unsigned long refCrc = crc32( 0, (const Bytef *)sReference, len );
char **strData = TheStringPackage.m_Strings->Find( refCrc );
if( !strData )
return "";
else
return *strData;
}
void SE_NewLanguage(void)
{
TheStringPackage.Clear( SE_TRUE );
}
// these two functions aren't needed other than to make Quake-type games happy and/or stop memory managers
// complaining about leaks if they report them before the global StringEd package object calls it's own dtor.
//
// but here they are for completeness's sake I guess...
//
void SE_Init(void)
{
Z_PushNewDeleteTag( TAG_STRINGED );
TheStringPackage.Clear( SE_FALSE );
// se_language = Cvar_Get("se_language", "english", CVAR_ARCHIVE | CVAR_NORESTART);
extern DWORD g_dwLanguage;
switch( g_dwLanguage )
{
case XC_LANGUAGE_FRENCH:
se_language = Cvar_Get("se_language", "french", CVAR_NORESTART);
break;
case XC_LANGUAGE_GERMAN:
se_language = Cvar_Get("se_language", "german", CVAR_NORESTART);
break;
case XC_LANGUAGE_ENGLISH:
default:
se_language = Cvar_Get("se_language", "english", CVAR_NORESTART);
break;
}
// Rather than calling SE_LoadLanguage directly, do this. Otherwise,
// se_langauge->modified doesn't get cleared, and we parse the string files
// twice. Gah.
SE_CheckForLanguageUpdates();
Z_PopNewDeleteTag();
}
// returns error message else NULL for ok.
//
// Any errors that result from this should probably be treated as game-fatal, since an asset file is fuxored.
//
LPCSTR SE_LoadLanguage( LPCSTR psLanguage )
{
LPCSTR psErrorMessage = NULL;
if (psLanguage && psLanguage[0])
{
SE_NewLanguage();
string strResults;
/*int iFilesFound = */SE_BuildFileList(
#ifdef _STRINGED
va("C:\\Source\\Tools\\StringEd\\test_data\\%s",sSE_STRINGS_DIR)
#else
sSE_STRINGS_DIR
#endif
, strResults
);
LPCSTR p;
while ( (p=SE_GetFoundFile (strResults)) != NULL && !psErrorMessage )
{
LPCSTR psThisLang = TheStringPackage.ExtractLanguageFromPath( p );
if ( !stricmp( psLanguage, psThisLang ) )
{
psErrorMessage = SE_Load( p );
}
}
}
else
{
assert( 0 && "SE_LoadLanguage(): Bad language name!" );
}
return psErrorMessage;
}
// called in Com_Frame, so don't take up any time! (can also be called during dedicated)
//
// instead of re-loading just the files we've already loaded I'm going to load the whole language (simpler)
//
void SE_CheckForLanguageUpdates(void)
{
if (se_language && se_language->modified)
{
LPCSTR psErrorMessage = SE_LoadLanguage( se_language->string );
if ( psErrorMessage )
{
Com_Error( ERR_DROP, psErrorMessage );
}
TheStringPackage.m_Strings->Sort();
se_language->modified = SE_FALSE;
}
}
///////////////////////// eof //////////////////////////

View File

@@ -0,0 +1,53 @@
// Filename:- stringed_ingame.h
//
#ifndef STRINGED_INGAME_H
#define STRINGED_INGAME_H
// alter these to suit your own game...
//
#define SE_BOOL qboolean
#define SE_TRUE qtrue
#define SE_FALSE qfalse
#define iSE_MAX_FILENAME_LENGTH MAX_QPATH
#define sSE_STRINGS_DIR "strings"
extern cvar_t *se_language;
// some needed text-equates, do not alter these under any circumstances !!!! (unless you're me. Which you're not)
//
#define iSE_VERSION 1
#define sSE_KEYWORD_VERSION "VERSION"
#define sSE_KEYWORD_CONFIG "CONFIG"
#define sSE_KEYWORD_FILENOTES "FILENOTES"
#define sSE_KEYWORD_REFERENCE "REFERENCE"
#define sSE_KEYWORD_NOTES "NOTES"
#define sSE_KEYWORD_LANG "LANG_"
#define sSE_KEYWORD_ENDMARKER "ENDMARKER"
#define sSE_FILE_EXTENSION ".st" // editor-only NEVER used ingame, but I wanted all extensions together
#define sSE_EXPORT_FILE_EXTENSION ".ste"
#define sSE_INGAME_FILE_EXTENSION ".str"
#define sSE_EXPORT_SAME "#same"
// available API calls...
//
typedef const char *LPCSTR;
void SE_Init ( void );
void SE_CheckForLanguageUpdates(void);
LPCSTR SE_LoadLanguage ( LPCSTR psLanguage );
void SE_NewLanguage ( void );
//
// for convenience, two ways of getting at the same data...
//
LPCSTR SE_GetString ( LPCSTR psPackageReference, LPCSTR psStringReference);
LPCSTR SE_GetString ( LPCSTR psPackageAndStringReference);
#endif // #ifndef STRINGED_INGAME_H
/////////////////// eof ////////////////

View File

@@ -0,0 +1,215 @@
// Filename:- stringed_interface.cpp
//
// This file contains functions that StringEd wants to call to do things like load/save, they can be modified
// for use ingame, but must remain functionally the same...
//
// Please try and put modifications for whichever games this is used for inside #defines, so I can copy the same file
// into each project.
//
//////////////////////////////////////////////////
//
// stuff common to all qcommon files...
#include "../server/server.h"
#include "../game/q_shared.h"
#include "qcommon.h"
//
//////////////////////////////////////////////////
#pragma warning ( disable : 4511 ) // copy constructor could not be generated
#pragma warning ( disable : 4512 ) // assignment operator could not be generated
#pragma warning ( disable : 4663 ) // C++ language change: blah blah template crap blah blah
#include "stringed_interface.h"
#include "stringed_ingame.h"
#include <string>
using namespace std;
#ifdef _STRINGED
#include <stdlib.h>
#include <memory.h>
#include "generic.h"
#endif
// this just gets the binary of the file into memory, so I can parse it. Called by main SGE loader
//
// returns either char * of loaded file, else NULL for failed-to-open...
//
unsigned char *SE_LoadFileData( const char *psFileName, int *piLoadedLength /* = 0 */)
{
unsigned char *psReturn = NULL;
if ( piLoadedLength )
{
*piLoadedLength = 0;
}
#ifdef _STRINGED
if (psFileName[1] == ':')
{
// full-path filename...
//
FILE *fh = fopen( psFileName, "rb" );
if (fh)
{
long lLength = filesize(fh);
if (lLength > 0)
{
psReturn = (unsigned char *) malloc( lLength + 1);
if (psReturn)
{
int iBytesRead = fread( psReturn, 1, lLength, fh );
if (iBytesRead != lLength)
{
// error reading file!!!...
//
free(psReturn);
psReturn = NULL;
}
else
{
psReturn[ lLength ] = '\0';
if ( piLoadedLength )
{
*piLoadedLength = iBytesRead;
}
}
fclose(fh);
}
}
}
}
else
#endif
{
// local filename, so prepend the base dir etc according to game and load it however (from PAK?)
//
unsigned char *pvLoadedData;
int iLen = FS_ReadFile( psFileName, (void **)&pvLoadedData );
if (iLen>0)
{
psReturn = pvLoadedData;
if ( piLoadedLength )
{
*piLoadedLength = iLen;
}
}
}
return psReturn;
}
// called by main SGE code after loaded data has been parsedinto internal structures...
//
void SE_FreeFileDataAfterLoad( unsigned char *psLoadedFile )
{
#ifdef _STRINGED
if ( psLoadedFile )
{
free( psLoadedFile );
}
#else
if ( psLoadedFile )
{
FS_FreeFile( psLoadedFile );
}
#endif
}
#ifndef _STRINGED
// quake-style method of doing things since their file-list code doesn't have a 'recursive' flag...
//
int giFilesFound;
static void SE_R_ListFiles( const char *psExtension, const char *psDir, string &strResults )
{
// Com_Printf(va("Scanning Dir: %s\n",psDir));
char **sysFiles, **dirFiles;
int numSysFiles, i, numdirs;
dirFiles = FS_ListFiles( psDir, "/", &numdirs);
for (i=0;i<numdirs;i++)
{
if (dirFiles[i][0] && dirFiles[i][0] != '.') // skip blanks, plus ".", ".." etc
{
char sDirName[MAX_QPATH];
sprintf(sDirName, "%s/%s", psDir, dirFiles[i]);
//
// for some reason the quake filesystem in this game now returns an extra slash on the end,
// didn't used to. Sigh...
//
if (sDirName[strlen(sDirName)-1] == '/')
{
sDirName[strlen(sDirName)-1] = '\0';
}
SE_R_ListFiles( psExtension, sDirName, strResults );
}
}
sysFiles = FS_ListFiles( psDir, psExtension, &numSysFiles );
for(i=0; i<numSysFiles; i++)
{
char sFilename[MAX_QPATH];
sprintf(sFilename,"%s/%s", psDir, sysFiles[i]);
// Com_Printf("%sFound file: %s",!i?"\n":"",sFilename);
strResults += sFilename;
strResults += ';';
giFilesFound++;
// read it in...
//
/* byte *pbData = NULL;
int iSize = FS_ReadFile( sFilename, (void **)&pbData);
if (pbData)
{
FS_FreeFile( pbData );
}
*/
}
FS_FreeFileList( sysFiles );
FS_FreeFileList( dirFiles );
}
#endif
// replace this with a call to whatever your own code equivalent is.
//
// expected result is a ';'-delineated string (including last one) containing file-list search results
//
int SE_BuildFileList( const char *psStartDir, string &strResults )
{
#ifndef _STRINGED
giFilesFound = 0;
strResults = "";
SE_R_ListFiles( sSE_INGAME_FILE_EXTENSION, psStartDir, strResults );
return giFilesFound;
#else
// .ST files...
//
int iFilesFound = BuildFileList( va("%s\\*%s",psStartDir, sSE_INGAME_FILE_EXTENSION), // LPCSTR psPathAndFilter,
true // bool bRecurseSubDirs
);
extern string strResult;
strResults = strResult;
return iFilesFound;
#endif
}
/////////////////////// eof ///////////////////////

View File

@@ -0,0 +1,21 @@
// Filename:- stringed_interface.h
//
// These are the functions that get replaced by game-specific ones (or StringEd code) so SGE can access files etc
//
#ifndef STRINGED_INTERFACE_H
#define STRINGED_INTERFACE_H
#pragma warning ( disable : 4786 ) // disable the usual stupid and pointless STL warning
#include <string>
using namespace std;
unsigned char * SE_LoadFileData ( const char *psFileName, int *piLoadedLength = 0);
void SE_FreeFileDataAfterLoad( unsigned char *psLoadedFile );
int SE_BuildFileList ( const char *psStartDir, string &strResults );
#endif // #ifndef STRINGED_INTERFACE_H
////////////////// eof ///////////////////

View File

@@ -0,0 +1,13 @@
// Current version of the single player game
#include "../win32/autoversion.h"
#ifdef _DEBUG
#define Q3_VERSION "(debug)JA: v"VERSION_STRING_DOTTED
#elif defined FINAL_BUILD
#define Q3_VERSION "JA: v"VERSION_STRING_DOTTED
#else
#define Q3_VERSION "(internal)JA: v"VERSION_STRING_DOTTED
#endif
// end

42
code/qcommon/tags.h Normal file
View File

@@ -0,0 +1,42 @@
// Filename:- tags.h
// do NOT include-protect this file, or add any fields or labels, because it's included within enums and tables
//
// these macro args get "TAG_" prepended on them for enum purposes, and appear as literal strings for "meminfo" command
TAGDEF(ALL),
TAGDEF(HUNKALLOC), // mem that was formerly from the hunk AFTER the SetMark (ie discarded during vid_reset)
TAGDEF(HUNKMISCMODELS), // sub-hunk alloc to track misc models
TAGDEF(FILESYS), // general filesystem usage
TAGDEF(LISTFILES), // for "*.blah" lists
TAGDEF(AMBIENTSET),
TAGDEF(G_ALLOC), // used by G_Alloc()
TAGDEF(CLIENTS), // Memory used for client info
TAGDEF(STATIC), // special usage for 1-byte allocations from 0..9 to avoid CopyString() slowdowns during cvar value copies
TAGDEF(SMALL), // used by S_Malloc, but probably more of a hint now. Will be dumped later
TAGDEF(MODEL_MD3), // specific model types' disk images
TAGDEF(MODEL_GLM), // "
TAGDEF(MODEL_GLA), // "
TAGDEF(ICARUS), // Memory used internally by the Icarus scripting system
TAGDEF(IMAGE_T), // an image_t struct (no longer on the hunk because of cached texture stuff)
TAGDEF(TEMP_WORKSPACE), // anything like file loading or image workspace that's only temporary
TAGDEF(SND_RAWDATA), // raw sound data, either MP3 or WAV
TAGDEF(GHOUL2), // Ghoul2 stuff
TAGDEF(BSP), // guess.
TAGDEF(GP2), // generic parser 2
TAGDEF(ANIMATION_CFG), // may as well keep this seperate / readable
TAGDEF(SAVEGAME), // used for allocating chunks during savegame file read
// TAGDEF(INFLATE), // Temp memory used by zlib32
// TAGDEF(DEFLATE), // Temp memory used by zlib32
TAGDEF(POINTCACHE), // weather effects
TAGDEF(NEWDEL),
TAGDEF(UI_ALLOC),
TAGDEF(LIPSYNC),
TAGDEF(FILELIST),
TAGDEF(BINK),
TAGDEF(STRINGED),
TAGDEF(COUNT)
//////////////// eof //////////////

62
code/qcommon/timing.h Normal file
View File

@@ -0,0 +1,62 @@
class timing_c
{
private:
__int64 start;
__int64 end;
int reset;
public:
timing_c(void)
{
}
void Start()
{
const __int64 *s = &start;
__asm
{
push eax
push ebx
push edx
rdtsc
mov ebx, s
mov [ebx], eax
mov [ebx + 4], edx
pop edx
pop ebx
pop eax
}
}
int End()
{
const __int64 *e = &end;
__int64 time;
__asm
{
push eax
push ebx
push edx
rdtsc
mov ebx, e
mov [ebx], eax
mov [ebx + 4], edx
pop edx
pop ebx
pop eax
}
time = end - start;
if (time < 0)
{
time = 0;
}
return((int)time);
}
};
// end

View File

@@ -0,0 +1,506 @@
/* Triangle/triangle intersection test routine,
* by Tomas Moller, 1997.
* See article "A Fast Triangle-Triangle Intersection Test",
* Journal of Graphics Tools, 2(2), 1997
*
* int tri_tri_intersect(float V0[3],float V1[3],float V2[3],
* float U0[3],float U1[3],float U2[3])
*
* parameters: vertices of triangle 1: V0,V1,V2
* vertices of triangle 2: U0,U1,U2
* result : returns 1 if the triangles intersect, otherwise 0
*
*/
// leave this at the top for PCH reasons...
#include "common_headers.h"
#include <math.h>
#include "../game/q_shared.h"
#include "../game/g_local.h"
/* if USE_EPSILON_TEST is true then we do a check:
if |dv|<EPSILON then dv=0.0;
else no check is done (which is less robust)
*/
#define USE_EPSILON_TEST 1
#define EPSILON 0.000001
/* some macros */
#define CROSS(dest,v1,v2) \
dest[0]=v1[1]*v2[2]-v1[2]*v2[1]; \
dest[1]=v1[2]*v2[0]-v1[0]*v2[2]; \
dest[2]=v1[0]*v2[1]-v1[1]*v2[0];
#define DOT(v1,v2) (v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2])
#define SUB(dest,v1,v2) \
dest[0]=v1[0]-v2[0]; \
dest[1]=v1[1]-v2[1]; \
dest[2]=v1[2]-v2[2];
/* sort so that a<=b */
#define SORT(a,b) \
if(a>b) \
{ \
float c; \
c=a; \
a=b; \
b=c; \
}
#define ISECT(VV0,VV1,VV2,D0,D1,D2,isect0,isect1) \
isect0=VV0+(VV1-VV0)*D0/(D0-D1); \
isect1=VV0+(VV2-VV0)*D0/(D0-D2);
#define COMPUTE_INTERVALS(VV0,VV1,VV2,D0,D1,D2,D0D1,D0D2,isect0,isect1) \
if(D0D1>0.0f) \
{ \
/* here we know that D0D2<=0.0 */ \
/* that is D0, D1 are on the same side, D2 on the other or on the plane */ \
ISECT(VV2,VV0,VV1,D2,D0,D1,isect0,isect1); \
} \
else if(D0D2>0.0f) \
{ \
/* here we know that d0d1<=0.0 */ \
ISECT(VV1,VV0,VV2,D1,D0,D2,isect0,isect1); \
} \
else if(D1*D2>0.0f || D0!=0.0f) \
{ \
/* here we know that d0d1<=0.0 or that D0!=0.0 */ \
ISECT(VV0,VV1,VV2,D0,D1,D2,isect0,isect1); \
} \
else if(D1!=0.0f) \
{ \
ISECT(VV1,VV0,VV2,D1,D0,D2,isect0,isect1); \
} \
else if(D2!=0.0f) \
{ \
ISECT(VV2,VV0,VV1,D2,D0,D1,isect0,isect1); \
} \
else \
{ \
/* triangles are coplanar */ \
return coplanar_tri_tri(N1,V0,V1,V2,U0,U1,U2); \
}
/* this edge to edge test is based on Franlin Antonio's gem:
"Faster Line Segment Intersection", in Graphics Gems III,
pp. 199-202 */
#define EDGE_EDGE_TEST(V0,U0,U1) \
Bx=U0[i0]-U1[i0]; \
By=U0[i1]-U1[i1]; \
Cx=V0[i0]-U0[i0]; \
Cy=V0[i1]-U0[i1]; \
f=Ay*Bx-Ax*By; \
d=By*Cx-Bx*Cy; \
if((f>0 && d>=0 && d<=f) || (f<0 && d<=0 && d>=f)) \
{ \
e=Ax*Cy-Ay*Cx; \
if(f>0) \
{ \
if(e>=0 && e<=f) return 1; \
} \
else \
{ \
if(e<=0 && e>=f) return 1; \
} \
}
#define EDGE_AGAINST_TRI_EDGES(V0,V1,U0,U1,U2) \
{ \
float Ax,Ay,Bx,By,Cx,Cy,e,d,f; \
Ax=V1[i0]-V0[i0]; \
Ay=V1[i1]-V0[i1]; \
/* test edge U0,U1 against V0,V1 */ \
EDGE_EDGE_TEST(V0,U0,U1); \
/* test edge U1,U2 against V0,V1 */ \
EDGE_EDGE_TEST(V0,U1,U2); \
/* test edge U2,U1 against V0,V1 */ \
EDGE_EDGE_TEST(V0,U2,U0); \
}
#define POINT_IN_TRI(V0,U0,U1,U2) \
{ \
float a,b,c,d0,d1,d2; \
/* is T1 completly inside T2? */ \
/* check if V0 is inside tri(U0,U1,U2) */ \
a=U1[i1]-U0[i1]; \
b=-(U1[i0]-U0[i0]); \
c=-a*U0[i0]-b*U0[i1]; \
d0=a*V0[i0]+b*V0[i1]+c; \
\
a=U2[i1]-U1[i1]; \
b=-(U2[i0]-U1[i0]); \
c=-a*U1[i0]-b*U1[i1]; \
d1=a*V0[i0]+b*V0[i1]+c; \
\
a=U0[i1]-U2[i1]; \
b=-(U0[i0]-U2[i0]); \
c=-a*U2[i0]-b*U2[i1]; \
d2=a*V0[i0]+b*V0[i1]+c; \
if(d0*d1>0.0) \
{ \
if(d0*d2>0.0) return 1; \
} \
}
qboolean coplanar_tri_tri(vec3_t N,vec3_t V0,vec3_t V1,vec3_t V2,
vec3_t U0,vec3_t U1,vec3_t U2)
{
vec3_t A;
short i0,i1;
/* first project onto an axis-aligned plane, that maximizes the area */
/* of the triangles, compute indices: i0,i1. */
A[0]=fabs(N[0]);
A[1]=fabs(N[1]);
A[2]=fabs(N[2]);
if(A[0]>A[1])
{
if(A[0]>A[2])
{
i0=1; /* A[0] is greatest */
i1=2;
}
else
{
i0=0; /* A[2] is greatest */
i1=1;
}
}
else /* A[0]<=A[1] */
{
if(A[2]>A[1])
{
i0=0; /* A[2] is greatest */
i1=1;
}
else
{
i0=0; /* A[1] is greatest */
i1=2;
}
}
/* test all edges of triangle 1 against the edges of triangle 2 */
EDGE_AGAINST_TRI_EDGES(V0,V1,U0,U1,U2);
EDGE_AGAINST_TRI_EDGES(V1,V2,U0,U1,U2);
EDGE_AGAINST_TRI_EDGES(V2,V0,U0,U1,U2);
/* finally, test if tri1 is totally contained in tri2 or vice versa */
POINT_IN_TRI(V0,U0,U1,U2);
POINT_IN_TRI(U0,V0,V1,V2);
return qfalse;
}
qboolean tri_tri_intersect(vec3_t V0,vec3_t V1,vec3_t V2,
vec3_t U0,vec3_t U1,vec3_t U2)
{
vec3_t E1,E2;
vec3_t N1,N2;
float d1,d2;
float du0,du1,du2,dv0,dv1,dv2;
vec3_t D;
float isect1[2], isect2[2];
float du0du1,du0du2,dv0dv1,dv0dv2;
short index;
float vp0,vp1,vp2;
float up0,up1,up2;
float b,c,max;
/* compute plane equation of triangle(V0,V1,V2) */
SUB(E1,V1,V0);
SUB(E2,V2,V0);
CROSS(N1,E1,E2);
d1=-DOT(N1,V0);
/* plane equation 1: N1.X+d1=0 */
/* put U0,U1,U2 into plane equation 1 to compute signed distances to the plane*/
du0=DOT(N1,U0)+d1;
du1=DOT(N1,U1)+d1;
du2=DOT(N1,U2)+d1;
/* coplanarity robustness check */
#if USE_EPSILON_TEST
if(fabs(du0)<EPSILON) du0=0.0;
if(fabs(du1)<EPSILON) du1=0.0;
if(fabs(du2)<EPSILON) du2=0.0;
#endif
du0du1=du0*du1;
du0du2=du0*du2;
if(du0du1>0.0f && du0du2>0.0f) /* same sign on all of them + not equal 0 ? */
return 0; /* no intersection occurs */
/* compute plane of triangle (U0,U1,U2) */
SUB(E1,U1,U0);
SUB(E2,U2,U0);
CROSS(N2,E1,E2);
d2=-DOT(N2,U0);
/* plane equation 2: N2.X+d2=0 */
/* put V0,V1,V2 into plane equation 2 */
dv0=DOT(N2,V0)+d2;
dv1=DOT(N2,V1)+d2;
dv2=DOT(N2,V2)+d2;
#if USE_EPSILON_TEST
if(fabs(dv0)<EPSILON) dv0=0.0;
if(fabs(dv1)<EPSILON) dv1=0.0;
if(fabs(dv2)<EPSILON) dv2=0.0;
#endif
dv0dv1=dv0*dv1;
dv0dv2=dv0*dv2;
if(dv0dv1>0.0f && dv0dv2>0.0f) /* same sign on all of them + not equal 0 ? */
return 0; /* no intersection occurs */
/* compute direction of intersection line */
CROSS(D,N1,N2);
/* compute and index to the largest component of D */
max=fabs(D[0]);
index=0;
b=fabs(D[1]);
c=fabs(D[2]);
if(b>max) max=b,index=1;
if(c>max) max=c,index=2;
/* this is the simplified projection onto L*/
vp0=V0[index];
vp1=V1[index];
vp2=V2[index];
up0=U0[index];
up1=U1[index];
up2=U2[index];
/* compute interval for triangle 1 */
COMPUTE_INTERVALS(vp0,vp1,vp2,dv0,dv1,dv2,dv0dv1,dv0dv2,isect1[0],isect1[1]);
/* compute interval for triangle 2 */
COMPUTE_INTERVALS(up0,up1,up2,du0,du1,du2,du0du1,du0du2,isect2[0],isect2[1]);
SORT(isect1[0],isect1[1]);
SORT(isect2[0],isect2[1]);
if(isect1[1]<isect2[0] || isect2[1]<isect1[0]) return qtrue;
return qfalse;
}
float LineSegmentDistance( vec3_t a, vec3_t b, vec3_t c, vec3_t d )
{
vec3_t v1, v2, v3, cross;
//FIXME: what if parallel or intersect?
//FIXME: this doesn't take into account the endpoints...
//get the two lines
VectorSubtract( b, a, v1 );
VectorSubtract( c, d, v2 );
//get their normalized cross product
CrossProduct( v1, v2, cross );
/*
float crossLength = VectorLength( cross );
if ( crossLength == 0 )
{//intersect! Or... parallel?
return 0;
}
VectorScale( cross, 1/crossLength, cross );
*/
VectorNormalize( cross );
//now get a vector from v1 to v2
VectorSubtract( d, a, v3 );
//distance is dot product of that new vector and the normalized cross product
float dist = fabs( DotProduct( v3, cross ) );
return dist;
}
extern qboolean G_FindClosestPointOnLineSegment( const vec3_t start, const vec3_t end, const vec3_t from, vec3_t result );
float ShortestLineSegBewteen2LineSegs( vec3_t start1, vec3_t end1, vec3_t start2, vec3_t end2, vec3_t close_pnt1, vec3_t close_pnt2 )
{
float current_dist, new_dist;
vec3_t new_pnt;
//start1, end1 : the first segment
//start2, end2 : the second segment
//output, one point on each segment, the closest two points on the segments.
//compute some temporaries:
//vec start_dif = start2 - start1
vec3_t start_dif;
VectorSubtract( start2, start1, start_dif );
//vec v1 = end1 - start1
vec3_t v1;
VectorSubtract( end1, start1, v1 );
//vec v2 = end2 - start2
vec3_t v2;
VectorSubtract( end2, start2, v2 );
//
float v1v1 = DotProduct( v1, v1 );
float v2v2 = DotProduct( v2, v2 );
float v1v2 = DotProduct( v1, v2 );
//the main computation
float denom = (v1v2 * v1v2) - (v1v1 * v2v2);
//if denom is small, then skip all this and jump to the section marked below
if ( fabs(denom) > 0.001f )
{
float s = -( (v2v2*DotProduct( v1, start_dif )) - (v1v2*DotProduct( v2, start_dif )) ) / denom;
float t = ( (v1v1*DotProduct( v2, start_dif )) - (v1v2*DotProduct( v1, start_dif )) ) / denom;
qboolean done = qtrue;
if ( s < 0 )
{
done = qfalse;
s = 0;// and see note below
}
if ( s > 1 )
{
done = qfalse;
s = 1;// and see note below
}
if ( t < 0 )
{
done = qfalse;
t = 0;// and see note below
}
if ( t > 1 )
{
done = qfalse;
t = 1;// and see note below
}
//vec close_pnt1 = start1 + s * v1
VectorMA( start1, s, v1, close_pnt1 );
//vec close_pnt2 = start2 + t * v2
VectorMA( start2, t, v2, close_pnt2 );
current_dist = Distance( close_pnt1, close_pnt2 );
//now, if none of those if's fired, you are done.
if ( done )
{
return current_dist;
}
//If they did fire, then we need to do some additional tests.
//What we are gonna do is see if we can find a shorter distance than the above
//involving the endpoints.
}
else
{
//******start here for paralell lines with current_dist = infinity****
current_dist = Q3_INFINITE;
}
//test 2 close_pnts first
/*
G_FindClosestPointOnLineSegment( start1, end1, close_pnt2, new_pnt );
new_dist = Distance( close_pnt2, new_pnt );
if ( new_dist < current_dist )
{//then update close_pnt1 close_pnt2 and current_dist
VectorCopy( new_pnt, close_pnt1 );
VectorCopy( close_pnt2, close_pnt2 );
current_dist = new_dist;
}
G_FindClosestPointOnLineSegment( start2, end2, close_pnt1, new_pnt );
new_dist = Distance( close_pnt1, new_pnt );
if ( new_dist < current_dist )
{//then update close_pnt1 close_pnt2 and current_dist
VectorCopy( close_pnt1, close_pnt1 );
VectorCopy( new_pnt, close_pnt2 );
current_dist = new_dist;
}
*/
//test all the endpoints
new_dist = Distance( start1, start2 );
if ( new_dist < current_dist )
{//then update close_pnt1 close_pnt2 and current_dist
VectorCopy( start1, close_pnt1 );
VectorCopy( start2, close_pnt2 );
current_dist = new_dist;
}
new_dist = Distance( start1, end2 );
if ( new_dist < current_dist )
{//then update close_pnt1 close_pnt2 and current_dist
VectorCopy( start1, close_pnt1 );
VectorCopy( end2, close_pnt2 );
current_dist = new_dist;
}
new_dist = Distance( end1, start2 );
if ( new_dist < current_dist )
{//then update close_pnt1 close_pnt2 and current_dist
VectorCopy( end1, close_pnt1 );
VectorCopy( start2, close_pnt2 );
current_dist = new_dist;
}
new_dist = Distance( end1, end2 );
if ( new_dist < current_dist )
{//then update close_pnt1 close_pnt2 and current_dist
VectorCopy( end1, close_pnt1 );
VectorCopy( end2, close_pnt2 );
current_dist = new_dist;
}
//Then we have 4 more point / segment tests
G_FindClosestPointOnLineSegment( start2, end2, start1, new_pnt );
new_dist = Distance( start1, new_pnt );
if ( new_dist < current_dist )
{//then update close_pnt1 close_pnt2 and current_dist
VectorCopy( start1, close_pnt1 );
VectorCopy( new_pnt, close_pnt2 );
current_dist = new_dist;
}
G_FindClosestPointOnLineSegment( start2, end2, end1, new_pnt );
new_dist = Distance( end1, new_pnt );
if ( new_dist < current_dist )
{//then update close_pnt1 close_pnt2 and current_dist
VectorCopy( end1, close_pnt1 );
VectorCopy( new_pnt, close_pnt2 );
current_dist = new_dist;
}
G_FindClosestPointOnLineSegment( start1, end1, start2, new_pnt );
new_dist = Distance( start2, new_pnt );
if ( new_dist < current_dist )
{//then update close_pnt1 close_pnt2 and current_dist
VectorCopy( new_pnt, close_pnt1 );
VectorCopy( start2, close_pnt2 );
current_dist = new_dist;
}
G_FindClosestPointOnLineSegment( start1, end1, end2, new_pnt );
new_dist = Distance( end2, new_pnt );
if ( new_dist < current_dist )
{//then update close_pnt1 close_pnt2 and current_dist
VectorCopy( new_pnt, close_pnt1 );
VectorCopy( end2, close_pnt2 );
current_dist = new_dist;
}
return current_dist;
}

1348
code/qcommon/unzip.cpp Normal file

File diff suppressed because it is too large Load Diff

286
code/qcommon/unzip.h Normal file
View File

@@ -0,0 +1,286 @@
#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP)
/* like the STRICT of WIN32, we define a pointer that cannot be converted
from (void*) without cast */
typedef struct TagunzFile__ { int unused; } unzFile__;
typedef unzFile__ *unzFile;
#else
typedef void* unzFile;
#endif
#define ZIP_FILE FILE
/* tm_unz contain date/time info */
typedef struct tm_unz_s
{
unsigned int tm_sec; /* seconds after the minute - [0,59] */
unsigned int tm_min; /* minutes after the hour - [0,59] */
unsigned int tm_hour; /* hours since midnight - [0,23] */
unsigned int tm_mday; /* day of the month - [1,31] */
unsigned int tm_mon; /* months since January - [0,11] */
unsigned int tm_year; /* years - [1980..2044] */
} tm_unz;
/* unz_global_info structure contain global data about the ZIPfile
These data comes from the end of central dir */
typedef struct unz_global_info_s
{
unsigned long number_entry; /* total number of entries in the central dir on this disk */
unsigned long size_comment; /* size of the global comment of the zipfile */
} unz_global_info;
/* unz_file_info contain information about a file in the zipfile */
typedef struct unz_file_info_s
{
unsigned long version; /* version made by 2 unsigned chars */
unsigned long version_needed; /* version needed to extract 2 unsigned chars */
unsigned long flag; /* general purpose bit flag 2 unsigned chars */
unsigned long compression_method; /* compression method 2 unsigned chars */
unsigned long dosDate; /* last mod file date in Dos fmt 4 unsigned chars */
unsigned long crc; /* crc-32 4 unsigned chars */
unsigned long compressed_size; /* compressed size 4 unsigned chars */
unsigned long uncompressed_size; /* uncompressed size 4 unsigned chars */
unsigned long size_filename; /* filename length 2 unsigned chars */
unsigned long size_file_extra; /* extra field length 2 unsigned chars */
unsigned long size_file_comment; /* file comment length 2 unsigned chars */
unsigned long disk_num_start; /* disk number start 2 unsigned chars */
unsigned long internal_fa; /* internal file attributes 2 unsigned chars */
unsigned long external_fa; /* external file attributes 4 unsigned chars */
tm_unz tmu_date;
} unz_file_info;
/* unz_file_info_interntal contain internal info about a file in zipfile*/
typedef struct unz_file_info_internal_s
{
unsigned long offset_curfile;/* relative offset of static header 4 unsigned chars */
} unz_file_info_internal;
/* file_in_zip_read_info_s contain internal information about a file in zipfile,
when reading and decompress it */
typedef struct
{
char *read_buffer; /* internal buffer for compressed data */
z_stream stream; /* zLib stream structure for inflate */
unsigned long pos_in_zipfile; /* position in unsigned char on the zipfile, for fseek*/
unsigned long stream_initialised; /* flag set if stream structure is initialised*/
unsigned long offset_local_extrafield;/* offset of the static extra field */
unsigned int size_local_extrafield;/* size of the static extra field */
unsigned long pos_local_extrafield; /* position in the static extra field in read*/
unsigned long crc32; /* crc32 of all data uncompressed */
unsigned long crc32_wait; /* crc32 we must obtain after decompress all */
unsigned long rest_read_compressed; /* number of unsigned char to be decompressed */
unsigned long rest_read_uncompressed;/*number of unsigned char to be obtained after decomp*/
ZIP_FILE *file; /* io structore of the zipfile */
unsigned long compression_method; /* compression method (0==store) */
unsigned long byte_before_the_zipfile;/* unsigned char before the zipfile, (>0 for sfx)*/
} file_in_zip_read_info_s;
/* unz_s contain internal information about the zipfile
*/
typedef struct
{
ZIP_FILE* file; /* io structore of the zipfile */
unz_global_info gi; /* public global information */
unsigned long byte_before_the_zipfile;/* unsigned char before the zipfile, (>0 for sfx)*/
unsigned long num_file; /* number of the current file in the zipfile*/
unsigned long pos_in_central_dir; /* pos of the current file in the central dir*/
unsigned long current_file_ok; /* flag about the usability of the current file*/
unsigned long central_pos; /* position of the beginning of the central dir*/
unsigned long size_central_dir; /* size of the central directory */
unsigned long offset_central_dir; /* offset of start of central directory with
respect to the starting disk number */
unz_file_info cur_file_info; /* public info about the current file in zip*/
unz_file_info_internal cur_file_info_internal; /* private info about it*/
file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current
file if we are decompressing it */
} unz_s;
#define UNZ_OK (0)
#define UNZ_END_OF_LIST_OF_FILE (-100)
#define UNZ_ERRNO (Z_DATA_ERROR)
#define UNZ_EOF (0)
#define UNZ_PARAMERROR (-102)
#define UNZ_BADZIPFILE (-103)
#define UNZ_INTERNALERROR (-104)
#define UNZ_CRCERROR (-105)
#define UNZ_CASESENSITIVE 1
#define UNZ_NOTCASESENSITIVE 2
#define UNZ_OSDEFAULTCASE 0
extern int unzStringFileNameCompare (const char* fileName1, const char* fileName2, int iCaseSensitivity);
/*
Compare two filename (fileName1,fileName2).
If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
or strcasecmp)
If iCaseSenisivity = 0, case sensitivity is defaut of your operating system
(like 1 on Unix, 2 on Windows)
*/
extern unzFile unzOpen (const char *path);
extern unzFile unzReOpen (const char* path, unzFile file);
/*
Open a Zip file. path contain the full pathname (by example,
on a Windows NT computer "c:\\zlib\\zlib111.zip" or on an Unix computer
"zlib/zlib111.zip".
If the zipfile cannot be opened (file don't exist or in not valid), the
return value is NULL.
Else, the return value is a unzFile Handle, usable with other function
of this unzip package.
*/
extern int unzClose (unzFile file);
/*
Close a ZipFile opened with unzipOpen.
If there is files inside the .Zip opened with unzOpenCurrentFile (see later),
these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
return UNZ_OK if there is no problem. */
extern int unzGetGlobalInfo (unzFile file, unz_global_info *pglobal_info);
/*
Write info about the ZipFile in the *pglobal_info structure.
No preparation of the structure is needed
return UNZ_OK if there is no problem. */
extern int unzGetGlobalComment (unzFile file, char *szComment, unsigned long uSizeBuf);
/*
Get the global comment string of the ZipFile, in the szComment buffer.
uSizeBuf is the size of the szComment buffer.
return the number of unsigned char copied or an error code <0
*/
/***************************************************************************/
/* Unzip package allow you browse the directory of the zipfile */
extern int unzGoToFirstFile (unzFile file);
/*
Set the current file of the zipfile to the first file.
return UNZ_OK if there is no problem
*/
extern int unzGoToNextFile (unzFile file);
/*
Set the current file of the zipfile to the next file.
return UNZ_OK if there is no problem
return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
*/
extern int unzGetCurrentFileInfoPosition (unzFile file, unsigned long *pos );
/*
Get the position of the info of the current file in the zip.
return UNZ_OK if there is no problem
*/
extern int unzSetCurrentFileInfoPosition (unzFile file, unsigned long pos );
/*
Set the position of the info of the current file in the zip.
return UNZ_OK if there is no problem
*/
extern int unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity);
/*
Try locate the file szFileName in the zipfile.
For the iCaseSensitivity signification, see unzStringFileNameCompare
return value :
UNZ_OK if the file is found. It becomes the current file.
UNZ_END_OF_LIST_OF_FILE if the file is not found
*/
extern int unzGetCurrentFileInfo (unzFile file, unz_file_info *pfile_info, char *szFileName, unsigned long fileNameBufferSize, void *extraField, unsigned long extraFieldBufferSize, char *szComment, unsigned long commentBufferSize);
/*
Get Info about the current file
if pfile_info!=NULL, the *pfile_info structure will contain somes info about
the current file
if szFileName!=NULL, the filemane string will be copied in szFileName
(fileNameBufferSize is the size of the buffer)
if extraField!=NULL, the extra field information will be copied in extraField
(extraFieldBufferSize is the size of the buffer).
This is the Central-header version of the extra field
if szComment!=NULL, the comment string of the file will be copied in szComment
(commentBufferSize is the size of the buffer)
*/
/***************************************************************************/
/* for reading the content of the current zipfile, you can open it, read data
from it, and close it (you can close it before reading all the file)
*/
extern int unzOpenCurrentFile (unzFile file);
/*
Open for reading data the current file in the zipfile.
If there is no error, the return value is UNZ_OK.
*/
extern int unzCloseCurrentFile (unzFile file);
/*
Close the file in zip opened with unzOpenCurrentFile
Return UNZ_CRCERROR if all the file was read but the CRC is not good
*/
extern int unzReadCurrentFile (unzFile file, void* buf, unsigned len);
/*
Read unsigned chars from the current file (opened by unzOpenCurrentFile)
buf contain buffer where data must be copied
len the size of buf.
return the number of unsigned char copied if somes unsigned chars are copied
return 0 if the end of file was reached
return <0 with error code if there is an error
(UNZ_ERRNO for IO error, or zLib error for uncompress error)
*/
extern long unztell(unzFile file);
/*
Give the current position in uncompressed data
*/
extern int unzeof (unzFile file);
/*
return 1 if the end of file was reached, 0 elsewhere
*/
extern int unzGetLocalExtrafield (unzFile file, void* buf, unsigned len);
/*
Read extra field from the current file (opened by unzOpenCurrentFile)
This is the local-header version of the extra field (sometimes, there is
more info in the local-header version than in the central-header)
if buf==NULL, it return the size of the local extra field
if buf!=NULL, len is the size of the buffer, the extra header is copied in
buf.
the return value is the number of unsigned chars copied in buf, or (if <0)
the error code
*/

View File

@@ -0,0 +1,382 @@
#include "xb_settings.h"
#include <xtl.h>
#include "../game/q_shared.h"
#include "qcommon.h"
#define SETTINGS_VERSION 0x00082877
#define SETTINGS_DIRNAME "Settings"
#define SETTINGS_FILENAME "settings.dat"
#define SETTINGS_IMAGE "saveimage.xbx"
#define SETTINGS_IMAGE_SRC "d:\\base\\media\\settings.xbx"
// The one copy of Settings:
XBSettings Settings;
const DWORD settingsSize = sizeof(Settings);
const DWORD sigSize = sizeof(XCALCSIG_SIGNATURE);
// This isn't user data, don't put it in XBSettings!
enum XBSettingsStatus
{
SETTINGS_OK, // Everything is ok
SETTINGS_MISSING, // File is not on disk
SETTINGS_CORRUPT, // File on disk is corrupt
SETTINGS_FAILED, // General error
};
XBSettingsStatus SettingsStatus;
bool settingsDisabled = false;
const char *buttonConfigStrings[3] = {
"weaponsbias",
"forcebias",
"southpaw",
};
const char *triggerConfigStrings[2] = {
"default",
"southpaw",
};
XBSettings::XBSettings( void )
{
version = SETTINGS_VERSION;
// Defaults:
invertAim[0] = invertAim[1] = false;
thumbstickMode[0] = thumbstickMode[1] = 0;
buttonMode[0] = buttonMode[1] = 0;
triggerMode[0] = triggerMode[1] = 0;
rumble[0] = rumble[1] = 1;
autolevel[0] = autolevel[0] = 0;
autoswitch[0] = autoswitch[1] = 1;
sensitivityX[0] = sensitivityX[1] = 2.0f;
sensitivityY[0] = sensitivityY[1] = 2.0f;
hotswapSP[0] = hotswapSP[1] = hotswapSP[2] = -1;
hotswapMP[0] = hotswapMP[1] = -1;
hotswapMP[2] = hotswapMP[3] = -1;
effectsVolume = 1.0f;
musicVolume = 0.25f;
voiceVolume = 1.0f;
brightness = 6.0f;
subtitles = 0;
#ifdef XBOX_DEMO
// Demo has no foreign audio, so we turn subtitles on if Dash language is FR/GE
DWORD dwLang = XGetLanguage();
if( dwLang == XC_LANGUAGE_FRENCH || dwLang == XC_LANGUAGE_GERMAN )
subtitles = 1;
#endif
voiceMode = 2;
voiceMask = 0;
appearOffline = 0;
#ifdef XBOX_DEMO
Disable(); // Ensure that we never try to load/save settings in the demo
#endif
}
// Write the current stored settings to the HD:
bool XBSettings::Save( void )
{
// Do nothing if user chose "Continue Without Saving"
if( settingsDisabled )
return true;
char settingsPath[128];
char *pathEnd;
DWORD dwWritten;
// Build the settings directory:
unsigned short wideName[128];
mbstowcs( wideName, SETTINGS_DIRNAME, sizeof(wideName) );
// Open/create the settings directory:
if (XCreateSaveGame( "U:\\", wideName, OPEN_ALWAYS, 0, settingsPath, sizeof(settingsPath) ) != ERROR_SUCCESS )
{
SettingsStatus = SETTINGS_FAILED;
return false;
}
// Build path to settings file:
pathEnd = settingsPath + strlen( settingsPath );
strcpy( pathEnd, SETTINGS_FILENAME );
// Open/create the settings file:
HANDLE hFile = CreateFile( settingsPath, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
if( hFile == INVALID_HANDLE_VALUE )
{
SettingsStatus = SETTINGS_FAILED;
return false;
}
// Write the data:
if( !WriteFile( hFile, this, settingsSize, &dwWritten, NULL ) || (dwWritten != settingsSize) )
{
SettingsStatus = SETTINGS_FAILED;
CloseHandle( hFile );
return false;
}
// Sign the data:
XCALCSIG_SIGNATURE xsig;
if( !Sign( &xsig ) )
{
SettingsStatus = SETTINGS_FAILED;
CloseHandle( hFile );
return false;
}
// Write signature:
if( !WriteFile( hFile, &xsig, sigSize, &dwWritten, NULL ) || (dwWritten != sigSize) )
{
SettingsStatus = SETTINGS_FAILED;
CloseHandle( hFile );
return false;
}
// Truncate and close file:
SetEndOfFile( hFile );
CloseHandle( hFile );
// Copy the save image over:
strcpy( pathEnd, SETTINGS_IMAGE );
CopyFile( SETTINGS_IMAGE_SRC, settingsPath, FALSE );
return true;
}
// Read saved settings from the HD:
bool XBSettings::Load( void )
{
// Do nothing if user chose "Continue Without Saving"
if( settingsDisabled )
return true;
char settingsPath[128];
char *pathEnd;
DWORD dwRead;
// Build the settings directory:
unsigned short wideName[128];
mbstowcs( wideName, SETTINGS_DIRNAME, sizeof(wideName) );
// Open the settings directory:
if( XCreateSaveGame( "U:\\", wideName, OPEN_EXISTING, 0, settingsPath, sizeof(settingsPath) ) != ERROR_SUCCESS )
{
SettingsStatus = SETTINGS_MISSING;
return false;
}
// Build path to settings file:
pathEnd = settingsPath + strlen( settingsPath );
strcpy( pathEnd, SETTINGS_FILENAME );
HANDLE hFile = CreateFile( settingsPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if( hFile == INVALID_HANDLE_VALUE )
{
SettingsStatus = SETTINGS_CORRUPT;
return false;
}
// Verify file size:
if( GetFileSize( hFile, NULL ) != (settingsSize + sigSize) )
{
SettingsStatus = SETTINGS_CORRUPT;
CloseHandle( hFile );
return false;
}
// Temp struct to read data into:
XBSettings temp;
if( !ReadFile( hFile, &temp, settingsSize, &dwRead, NULL ) || (dwRead != settingsSize) )
{
SettingsStatus = SETTINGS_CORRUPT;
CloseHandle( hFile );
return false;
}
// Calculate signature over the read-in data:
XCALCSIG_SIGNATURE xsig;
if( !temp.Sign( &xsig ) )
{
SettingsStatus = SETTINGS_CORRUPT;
CloseHandle( hFile );
return false;
}
// Read in stored signature:
XCALCSIG_SIGNATURE storedSig;
if( !ReadFile( hFile, &storedSig, sigSize, &dwRead, NULL ) || (dwRead != sigSize) )
{
SettingsStatus = SETTINGS_CORRUPT;
CloseHandle( hFile );
return false;
}
// We're done with the file:
CloseHandle( hFile );
// Compare signatures:
if( memcmp( &xsig, &storedSig, sigSize ) != 0 )
{
SettingsStatus = SETTINGS_CORRUPT;
return false;
}
// Lastly, verify that the version number is right:
if( temp.version != SETTINGS_VERSION )
{
SettingsStatus = SETTINGS_CORRUPT;
return false;
}
// OK. The data checks out!
*this = temp;
// TODO: Range-check all the values?
return true;
}
void XBSettings::Delete( void )
{
// Build the settings directory:
unsigned short wideName[128];
mbstowcs( wideName, SETTINGS_DIRNAME, sizeof(wideName) );
// Delete the game:
XDeleteSaveGame( "U:\\", wideName );
}
bool XBSettings::Corrupt( void )
{
return (SettingsStatus == SETTINGS_CORRUPT);
}
bool XBSettings::Missing( void )
{
return (SettingsStatus == SETTINGS_MISSING);
}
// Copy all stored settings into cvars
void XBSettings::SetAll( void )
{
Cvar_SetValue( "m_pitch", invertAim[0] ? 0.022f : -0.022f );
Cvar_SetValue( "ui_thumbStickMode", thumbstickMode[0] );
Cvar_Set( "ui_buttonconfig", buttonConfigStrings[buttonMode[0]] );
Cbuf_ExecuteText( EXEC_APPEND, va("exec cfg/spbuttonConfig%d.cfg\n", buttonMode[0]) );
Cvar_Set( "ui_triggerconfig", triggerConfigStrings[triggerMode[0]] );
Cbuf_ExecuteText( EXEC_APPEND, va("exec cfg/triggersConfig%d.cfg\n", triggerMode[0]) );
Cvar_SetValue( "in_useRumble", rumble[0] );
Cvar_SetValue( "cl_autolevel", autolevel[0] );
Cvar_SetValue( "cg_autoswitch", autoswitch[0] );
Cvar_SetValue( "sensitivity", sensitivityX[0] );
Cvar_SetValue( "sensitivityY", sensitivityY[0] );
if( hotswapSP[0] >= 0 )
Cvar_SetValue( "hotswap0", hotswapSP[0] );
else
Cvar_Set( "hotswap0", "" );
if( hotswapSP[1] >= 0 )
Cvar_SetValue( "hotswap1", hotswapSP[1] );
else
Cvar_Set( "hotswap1", "" );
if( hotswapSP[2] >= 0 )
Cvar_SetValue( "hotswap2", hotswapSP[2] );
else
Cvar_Set( "hotswap2", "" );
Cvar_SetValue( "s_effects_volume", effectsVolume );
Cvar_SetValue( "s_music_volume", musicVolume );
Cvar_SetValue( "s_voice_volume", voiceVolume );
Cvar_SetValue( "s_brightness_volume", brightness );
extern void GLimp_SetGamma(float);
GLimp_SetGamma(Cvar_VariableValue( "s_brightness_volume" ) / 5.0f);
Cvar_SetValue( "g_subtitles", subtitles );
}
#ifdef XBOX_DEMO
void XBSettings::RestoreDefaults( void )
{
version = SETTINGS_VERSION;
// Defaults:
invertAim[0] = invertAim[1] = false;
thumbstickMode[0] = thumbstickMode[1] = 0;
buttonMode[0] = buttonMode[1] = 0;
triggerMode[0] = triggerMode[1] = 0;
rumble[0] = rumble[1] = 1;
autolevel[0] = autolevel[0] = 0;
autoswitch[0] = autoswitch[1] = 1;
sensitivityX[0] = sensitivityX[1] = 2.0f;
sensitivityY[0] = sensitivityY[1] = 2.0f;
hotswapSP[0] = hotswapSP[1] = hotswapSP[2] = -1;
hotswapMP[0] = hotswapMP[1] = -1;
hotswapMP[2] = hotswapMP[3] = -1;
effectsVolume = 1.0f;
musicVolume = 0.25f;
voiceVolume = 1.0f;
brightness = 6.0f;
subtitles = 0;
// Demo has no foreign audio, so we turn subtitles on if Dash language is FR/GE
DWORD dwLang = XGetLanguage();
if( dwLang == XC_LANGUAGE_FRENCH || dwLang == XC_LANGUAGE_GERMAN )
subtitles = 1;
voiceMode = 2;
voiceMask = 0;
appearOffline = 0;
}
#endif
// Utility - signs the current contents of this XBSettings into the supplied struct:
bool XBSettings::Sign( XCALCSIG_SIGNATURE *pSig )
{
// Start the signature:
HANDLE hSig = XCalculateSignatureBegin( 0 );
if( hSig == INVALID_HANDLE_VALUE )
return false;
// Build the signature
if( XCalculateSignatureUpdate( hSig, (BYTE *) this, sizeof(*this) ) != ERROR_SUCCESS )
return false;
// Finish the signature:
if( XCalculateSignatureEnd( hSig, pSig ) != ERROR_SUCCESS )
return false;
// Done!
return true;
}
// Master switch for turning off settings when user picks
// "Continue Without Saving"
void XBSettings::Disable( void )
{
settingsDisabled = true;
}
bool XBSettings::IsDisabled( void )
{
return settingsDisabled;
}

View File

@@ -0,0 +1,87 @@
#ifndef __XB_SETTINGS_H
#define __XB_SETTINGS_H
#include <xtl.h>
enum XBStartupState
{
STARTUP_LOAD_SETTINGS,
STARTUP_COMBINED_SPACE_CHECK,
STARTUP_GAME_SPACE_CHECK,
STARTUP_INVITE_CHECK,
STARTUP_FINISH,
};
// Minimum save size on Xbox. Bleh:
#define SETTINGS_NUM_BLOCKS 4
struct XBSettings
{
// Magic number/revision stamp:
unsigned long version;
// Controls, etc... One for SP/P1 in MP, other for P2 in MP:
bool invertAim[2];
int thumbstickMode[2];
int buttonMode[2];
int triggerMode[2];
int rumble[2];
int autolevel[2];
int autoswitch[2];
float sensitivityX[2];
float sensitivityY[2];
// Black/White/X assignments, SP:
int hotswapSP[3];
// Black/White for players one & two, MP:
int hotswapMP[4];
// A/V settings, Global:
float effectsVolume;
float musicVolume;
float voiceVolume;
float brightness;
// Subtitles, only used in SP:
int subtitles;
// Voice/Live options, only used in MP:
int voiceMode;
int voiceMask;
int appearOffline;
// INTERFACE:
XBSettings( void );
bool Save( void );
bool Load( void );
void Delete( void );
// For determining why a Save/Load failed:
bool Missing( void );
bool Corrupt( void );
// This copies all settings from the Settings struct to their various cvars
void SetAll( void );
// Turn off the settings file completely:
void Disable( void );
// Has the user turned off saving (by choosing "Continue Without Saving")?
bool IsDisabled( void );
#ifdef XBOX_DEMO
void RestoreDefaults( void );
#endif
private:
bool Sign( XCALCSIG_SIGNATURE *pSig );
};
// One global copy (declared in xb_settings.cpp)
extern XBSettings Settings;
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,958 @@
// Created 2/3/03 by Brian Osman - split Zone code from common.cpp
#include "../game/q_shared.h"
#include "qcommon.h"
#include "../qcommon/sstring.h"
#include "platform.h"
#ifdef DEBUG_ZONE_ALLOCS
int giZoneSnaphotNum=0;
#define DEBUG_ZONE_ALLOC_OPTIONAL_LABEL_SIZE 256
typedef sstring<DEBUG_ZONE_ALLOC_OPTIONAL_LABEL_SIZE> sDebugString_t;
#endif
static void Z_Details_f(void);
// define a string table of all mem tags...
//
#ifdef TAGDEF // itu?
#undef TAGDEF
#endif
#define TAGDEF(blah) #blah
static const char *psTagStrings[TAG_COUNT+1]= // +1 because TAG_COUNT will itself become a string here. Oh well.
{
#include "../qcommon/tags.h"
};
// This handles zone memory allocation.
// It is a wrapper around malloc with a tag id and a magic number at the start
#define ZONE_MAGIC 0x21436587
// if you change ANYTHING in this structure, be sure to update the tables below using DEF_STATIC...
//
typedef struct zoneHeader_s
{
int iMagic;
memtag_t eTag;
int iSize;
struct zoneHeader_s *pNext;
struct zoneHeader_s *pPrev;
#ifdef DEBUG_ZONE_ALLOCS
char sSrcFileBaseName[MAX_QPATH];
int iSrcFileLineNum;
char sOptionalLabel[DEBUG_ZONE_ALLOC_OPTIONAL_LABEL_SIZE];
int iSnapshotNumber;
#endif
} zoneHeader_t;
typedef struct
{
int iMagic;
} zoneTail_t;
static inline zoneTail_t *ZoneTailFromHeader(zoneHeader_t *pHeader)
{
return (zoneTail_t*) ( (char*)pHeader + sizeof(*pHeader) + pHeader->iSize );
}
#ifdef DETAILED_ZONE_DEBUG_CODE
map <void*,int> mapAllocatedZones;
#endif
typedef struct zoneStats_s
{
int iCount;
int iCurrent;
int iPeak;
// I'm keeping these updated on the fly, since it's quicker for cache-pool
// purposes rather than recalculating each time...
//
int iSizesPerTag [TAG_COUNT];
int iCountsPerTag[TAG_COUNT];
} zoneStats_t;
typedef struct zone_s
{
zoneStats_t Stats;
zoneHeader_t Header;
} zone_t;
cvar_t *com_validateZone;
zone_t TheZone = {0};
// Scans through the linked list of mallocs and makes sure no data has been overwritten
int Z_Validate(void)
{
int ret=0;
if(!com_validateZone || !com_validateZone->integer)
{
return ret;
}
zoneHeader_t *pMemory = TheZone.Header.pNext;
while (pMemory)
{
#ifdef DETAILED_ZONE_DEBUG_CODE
// this won't happen here, but wtf?
int& iAllocCount = mapAllocatedZones[pMemory];
if (iAllocCount <= 0)
{
Com_Error(ERR_FATAL, "Z_Validate(): Bad block allocation count!");
return ret;
}
#endif
if(pMemory->iMagic != ZONE_MAGIC)
{
Com_Error(ERR_FATAL, "Z_Validate(): Corrupt zone header!");
return ret;
}
// this block of code is intended to make sure all of the data is paged in
if (pMemory->eTag != TAG_IMAGE_T
&& pMemory->eTag != TAG_MODEL_MD3
&& pMemory->eTag != TAG_MODEL_GLM
&& pMemory->eTag != TAG_MODEL_GLA ) //don't bother with disk caches as they've already been hit or will be thrown out next
{
unsigned char *memstart = (unsigned char *)pMemory;
int totalSize = pMemory->iSize;
while (totalSize > 4096)
{
memstart += 4096;
ret += (int)(*memstart); // this fools the optimizer
totalSize -= 4096;
}
}
if (ZoneTailFromHeader(pMemory)->iMagic != ZONE_MAGIC)
{
Com_Error(ERR_FATAL, "Z_Validate(): Corrupt zone tail!");
return ret;
}
pMemory = pMemory->pNext;
}
return ret;
}
// static mem blocks to reduce a lot of small zone overhead
//
#pragma pack(push)
#pragma pack(1)
typedef struct
{
zoneHeader_t Header;
// byte mem[0];
zoneTail_t Tail;
} StaticZeroMem_t;
typedef struct
{
zoneHeader_t Header;
byte mem[2];
zoneTail_t Tail;
} StaticMem_t;
#pragma pack(pop)
const static StaticZeroMem_t gZeroMalloc =
{ {ZONE_MAGIC, TAG_STATIC,0,NULL,NULL},{ZONE_MAGIC}};
#ifdef DEBUG_ZONE_ALLOCS
#define DEF_STATIC(_char) {ZONE_MAGIC, TAG_STATIC,2,NULL,NULL, "<static>",0,"",0},_char,'\0',{ZONE_MAGIC}
#else
#define DEF_STATIC(_char) {ZONE_MAGIC, TAG_STATIC,2,NULL,NULL },_char,'\0',{ZONE_MAGIC}
#endif
const static StaticMem_t gEmptyString =
{ DEF_STATIC('\0') };
const static StaticMem_t gNumberString[] = {
{ DEF_STATIC('0') },
{ DEF_STATIC('1') },
{ DEF_STATIC('2') },
{ DEF_STATIC('3') },
{ DEF_STATIC('4') },
{ DEF_STATIC('5') },
{ DEF_STATIC('6') },
{ DEF_STATIC('7') },
{ DEF_STATIC('8') },
{ DEF_STATIC('9') },
};
qboolean gbMemFreeupOccured = qfalse;
#ifdef DEBUG_ZONE_ALLOCS
void *_D_Z_Malloc ( int iSize, memtag_t eTag, qboolean bZeroit, const char *psFile, int iLine)
#else
void *Z_Malloc(int iSize, memtag_t eTag, qboolean bZeroit, int unusedAlign)
#endif
{
gbMemFreeupOccured = qfalse;
if (iSize == 0)
{
zoneHeader_t *pMemory = (zoneHeader_t *) &gZeroMalloc;
return &pMemory[1];
}
// Add in tracking info and round to a longword... (ignore longword aligning now we're not using contiguous blocks)
//
// int iRealSize = (iSize + sizeof(zoneHeader_t) + sizeof(zoneTail_t) + 3) & 0xfffffffc;
int iRealSize = (iSize + sizeof(zoneHeader_t) + sizeof(zoneTail_t));
// Allocate a chunk...
//
zoneHeader_t *pMemory = NULL;
while (pMemory == NULL)
{
#ifdef _WIN32
if (gbMemFreeupOccured)
{
Sleep(1000); // sleep for a second, so Windows has a chance to shuffle mem to de-swiss-cheese it
}
#endif
if (bZeroit) {
pMemory = (zoneHeader_t *) calloc ( iRealSize, 1 );
} else {
pMemory = (zoneHeader_t *) malloc ( iRealSize );
}
if (!pMemory)
{
// new bit, if we fail to malloc memory, try dumping some of the cached stuff that's non-vital and try again...
//
// ditch the BSP cache...
//
if (CM_DeleteCachedMap(qfalse))
{
gbMemFreeupOccured = qtrue;
continue; // we've just ditched a whole load of memory, so try again with the malloc
}
// ditch any sounds not used on this level...
//
extern qboolean SND_RegisterAudio_LevelLoadEnd(qboolean bDeleteEverythingNotUsedThisLevel);
if (SND_RegisterAudio_LevelLoadEnd(qtrue))
{
gbMemFreeupOccured = qtrue;
continue; // we've dropped at least one sound, so try again with the malloc
}
// ditch any image_t's (and associated GL texture mem) not used on this level...
//
extern qboolean RE_RegisterImages_LevelLoadEnd(void);
if (RE_RegisterImages_LevelLoadEnd())
{
gbMemFreeupOccured = qtrue;
continue; // we've dropped at least one image, so try again with the malloc
}
// ditch the model-binaries cache... (must be getting desperate here!)
//
extern qboolean RE_RegisterModels_LevelLoadEnd(qboolean bDeleteEverythingNotUsedThisLevel);
if (RE_RegisterModels_LevelLoadEnd(qtrue))
{
gbMemFreeupOccured = qtrue;
continue;
}
// as a last panic measure, dump all the audio memory, but not if we're in the audio loader
// (which is annoying, but I'm not sure how to ensure we're not dumping any memory needed by the sound
// currently being loaded if that was the case)...
//
// note that this keeps querying until it's freed up as many bytes as the requested size, but freeing
// several small blocks might not mean that one larger one is satisfiable after freeup, however that'll
// just make it go round again and try for freeing up another bunch of blocks until the total is satisfied
// again (though this will have freed twice the requested amount in that case), so it'll either work
// eventually or not free up enough and drop through to the final ERR_DROP. No worries...
//
extern qboolean gbInsideLoadSound;
extern int SND_FreeOldestSound(void); // I had to add a void-arg version of this because of link issues, sigh
if (!gbInsideLoadSound)
{
int iBytesFreed = SND_FreeOldestSound();
if (iBytesFreed)
{
int iTheseBytesFreed = 0;
while ( (iTheseBytesFreed = SND_FreeOldestSound()) != 0)
{
iBytesFreed += iTheseBytesFreed;
if (iBytesFreed >= iRealSize)
break; // early opt-out since we've managed to recover enough (mem-contiguity issues aside)
}
gbMemFreeupOccured = qtrue;
continue;
}
}
// sigh, dunno what else to try, I guess we'll have to give up and report this as an out-of-mem error...
//
// findlabel: "recovermem"
Com_Printf(S_COLOR_RED"Z_Malloc(): Failed to alloc %d bytes (TAG_%s) !!!!!\n", iSize, psTagStrings[eTag]);
Z_Details_f();
Com_Error(ERR_FATAL,"(Repeat): Z_Malloc(): Failed to alloc %d bytes (TAG_%s) !!!!!\n", iSize, psTagStrings[eTag]);
return NULL;
}
}
#ifdef DEBUG_ZONE_ALLOCS
extern char *Filename_WithoutPath(const char *psFilename);
Q_strncpyz(pMemory->sSrcFileBaseName, Filename_WithoutPath(psFile), sizeof(pMemory->sSrcFileBaseName));
pMemory->iSrcFileLineNum = iLine;
pMemory->sOptionalLabel[0] = '\0';
pMemory->iSnapshotNumber = giZoneSnaphotNum;
#endif
// Link in
pMemory->iMagic = ZONE_MAGIC;
pMemory->eTag = eTag;
pMemory->iSize = iSize;
pMemory->pNext = TheZone.Header.pNext;
TheZone.Header.pNext = pMemory;
if (pMemory->pNext)
{
pMemory->pNext->pPrev = pMemory;
}
pMemory->pPrev = &TheZone.Header;
//
// add tail...
//
ZoneTailFromHeader(pMemory)->iMagic = ZONE_MAGIC;
// Update stats...
//
TheZone.Stats.iCurrent += iSize;
TheZone.Stats.iCount++;
TheZone.Stats.iSizesPerTag [eTag] += iSize;
TheZone.Stats.iCountsPerTag [eTag]++;
if (TheZone.Stats.iCurrent > TheZone.Stats.iPeak)
{
TheZone.Stats.iPeak = TheZone.Stats.iCurrent;
}
#ifdef DETAILED_ZONE_DEBUG_CODE
mapAllocatedZones[pMemory]++;
#endif
Z_Validate(); // check for corruption
void *pvReturnMem = &pMemory[1];
return pvReturnMem;
}
// used during model cacheing to save an extra malloc, lets us morph the disk-load buffer then
// just not fs_freefile() it afterwards.
//
void Z_MorphMallocTag( void *pvAddress, memtag_t eDesiredTag )
{
zoneHeader_t *pMemory = ((zoneHeader_t *)pvAddress) - 1;
if (pMemory->iMagic != ZONE_MAGIC)
{
Com_Error(ERR_FATAL, "Z_MorphMallocTag(): Not a valid zone header!");
return; // won't get here
}
// DEC existing tag stats...
//
// TheZone.Stats.iCurrent - unchanged
// TheZone.Stats.iCount - unchanged
TheZone.Stats.iSizesPerTag [pMemory->eTag] -= pMemory->iSize;
TheZone.Stats.iCountsPerTag [pMemory->eTag]--;
// morph...
//
pMemory->eTag = eDesiredTag;
// INC new tag stats...
//
// TheZone.Stats.iCurrent - unchanged
// TheZone.Stats.iCount - unchanged
TheZone.Stats.iSizesPerTag [pMemory->eTag] += pMemory->iSize;
TheZone.Stats.iCountsPerTag [pMemory->eTag]++;
}
static int Zone_FreeBlock(zoneHeader_t *pMemory)
{
const int iSize = pMemory->iSize;
if (pMemory->eTag != TAG_STATIC) // belt and braces, should never hit this though
{
// Update stats...
//
TheZone.Stats.iCount--;
TheZone.Stats.iCurrent -= pMemory->iSize;
TheZone.Stats.iSizesPerTag [pMemory->eTag] -= pMemory->iSize;
TheZone.Stats.iCountsPerTag [pMemory->eTag]--;
// Sanity checks...
//
assert(pMemory->pPrev->pNext == pMemory);
assert(!pMemory->pNext || (pMemory->pNext->pPrev == pMemory));
// Unlink and free...
//
pMemory->pPrev->pNext = pMemory->pNext;
if(pMemory->pNext)
{
pMemory->pNext->pPrev = pMemory->pPrev;
}
//debugging double frees
pMemory->iMagic = 'FREE';
free (pMemory);
#ifdef DETAILED_ZONE_DEBUG_CODE
// this has already been checked for in execution order, but wtf?
int& iAllocCount = mapAllocatedZones[pMemory];
if (iAllocCount == 0)
{
Com_Error(ERR_FATAL, "Zone_FreeBlock(): Double-freeing block!");
return -1;
}
iAllocCount--;
#endif
}
return iSize;
}
// stats-query function to to see if it's our malloc
// returns block size if so
qboolean Z_IsFromZone(void *pvAddress, memtag_t eTag)
{
zoneHeader_t *pMemory = ((zoneHeader_t *)pvAddress) - 1;
#if 1 //debugging double free
if (pMemory->iMagic == 'FREE')
{
Com_Printf("Z_IsFromZone(%x): Ptr has been freed already!(%9s)\n",pvAddress,pvAddress);
return qfalse;
}
#endif
if (pMemory->iMagic != ZONE_MAGIC)
{
return qfalse;
}
//looks like it is from our zone, let's double check the tag
if (pMemory->eTag != eTag)
{
return qfalse;
}
return pMemory->iSize;
}
// stats-query function to ask how big a malloc is...
//
int Z_Size(void *pvAddress)
{
zoneHeader_t *pMemory = ((zoneHeader_t *)pvAddress) - 1;
if (pMemory->eTag == TAG_STATIC)
{
return 0; // kind of
}
if (pMemory->iMagic != ZONE_MAGIC)
{
Com_Error(ERR_FATAL, "Z_Size(): Not a valid zone header!");
return 0; // won't get here
}
return pMemory->iSize;
}
#ifdef DEBUG_ZONE_ALLOCS
void _D_Z_Label(const void *pvAddress, const char *psLabel)
{
zoneHeader_t *pMemory = ((zoneHeader_t *)pvAddress) - 1;
if (pMemory->eTag == TAG_STATIC)
{
return;
}
if (pMemory->iMagic != ZONE_MAGIC)
{
Com_Error(ERR_FATAL, "_D_Z_Label(): Not a valid zone header!");
}
Q_strncpyz( pMemory->sOptionalLabel, psLabel, sizeof(pMemory->sOptionalLabel));
pMemory->sOptionalLabel[ sizeof(pMemory->sOptionalLabel)-1 ] = '\0';
}
#endif
// Frees a block of memory...
//
int Z_Free(void *pvAddress)
{
if (!TheZone.Stats.iCount)
{
//Com_Error(ERR_FATAL, "Z_Free(): Zone has been cleard already!");
Com_Printf("Z_Free(%x): Zone has been cleard already!\n",pvAddress);
return -1;
}
zoneHeader_t *pMemory = ((zoneHeader_t *)pvAddress) - 1;
#if 1 //debugging double free
if (pMemory->iMagic == 'FREE')
{
Com_Error(ERR_FATAL, "Z_Free(%s): Block already-freed, or not allocated through Z_Malloc!",pvAddress);
return -1;
}
#endif
if (pMemory->eTag == TAG_STATIC)
{
return 0;
}
#ifdef DETAILED_ZONE_DEBUG_CODE
//
// check this error *before* barfing on bad magics...
//
int& iAllocCount = mapAllocatedZones[pMemory];
if (iAllocCount <= 0)
{
Com_Error(ERR_FATAL, "Z_Free(): Block already-freed, or not allocated through Z_Malloc!");
return -1;
}
#endif
if (pMemory->iMagic != ZONE_MAGIC)
{
Com_Error(ERR_FATAL, "Z_Free(): Corrupt zone header!");
return -1;
}
if (ZoneTailFromHeader(pMemory)->iMagic != ZONE_MAGIC)
{
Com_Error(ERR_FATAL, "Z_Free(): Corrupt zone tail!");
return -1;
}
return Zone_FreeBlock(pMemory);
}
int Z_MemSize(memtag_t eTag)
{
return TheZone.Stats.iSizesPerTag[eTag];
}
// Frees all blocks with the specified tag...
//
void Z_TagFree(memtag_t eTag)
{
//#ifdef _DEBUG
// int iZoneBlocks = TheZone.Stats.iCount;
//#endif
zoneHeader_t *pMemory = TheZone.Header.pNext;
while (pMemory)
{
zoneHeader_t *pNext = pMemory->pNext;
if ( (eTag == TAG_ALL) || (pMemory->eTag == eTag))
{
Zone_FreeBlock(pMemory);
}
pMemory = pNext;
}
// these stupid pragmas don't work here???!?!?!
//
//#ifdef _DEBUG
//#pragma warning( disable : 4189)
// int iBlocksFreed = iZoneBlocks - TheZone.Stats.iCount;
//#pragma warning( default : 4189)
//#endif
}
#ifdef DEBUG_ZONE_ALLOCS
void *_D_S_Malloc ( int iSize, const char *psFile, int iLine)
{
return _D_Z_Malloc( iSize, TAG_SMALL, qfalse, psFile, iLine );
}
#else
void *S_Malloc( int iSize )
{
return Z_Malloc( iSize, TAG_SMALL, qfalse);
}
#endif
#ifdef _DEBUG
static void Z_MemRecoverTest_f(void)
{
// needs to be in _DEBUG only, not good for final game!
//
if ( Cmd_Argc() != 2 ) {
Com_Printf( "Usage: zone_memrecovertest max2alloc\n" );
return;
}
int iMaxAlloc = 1024*1024*atoi( Cmd_Argv(1) );
int iTotalMalloc = 0;
while (1)
{
const int iThisMalloc = 5* (1024 * 1024);
Z_Malloc(iThisMalloc, TAG_SPECIAL_MEM_TEST, qfalse); // and lose, just to consume memory
iTotalMalloc += iThisMalloc;
if (gbMemFreeupOccured || (iTotalMalloc >= iMaxAlloc) )
break;
}
Z_TagFree(TAG_SPECIAL_MEM_TEST);
}
#endif
// Gives a summary of the zone memory usage
static void Z_Stats_f(void)
{
Com_Printf("\nThe zone is using %d bytes (%.2fMB) in %d memory blocks\n",
TheZone.Stats.iCurrent,
(float)TheZone.Stats.iCurrent / 1024.0f / 1024.0f,
TheZone.Stats.iCount
);
Com_Printf("The zone peaked at %d bytes (%.2fMB)\n",
TheZone.Stats.iPeak,
(float)TheZone.Stats.iPeak / 1024.0f / 1024.0f
);
}
// Gives a detailed breakdown of the memory blocks in the zone
//
static void Z_Details_f(void)
{
Com_Printf("---------------------------------------------------------------------------\n");
Com_Printf("%20s %9s\n","Zone Tag","Bytes");
Com_Printf("%20s %9s\n","--------","-----");
for (int i=0; i<TAG_COUNT; i++)
{
int iThisCount = TheZone.Stats.iCountsPerTag[i];
int iThisSize = TheZone.Stats.iSizesPerTag [i];
if (iThisCount)
{
// can you believe that using %2.2f as a format specifier doesn't bloody work?
// It ignores the left-hand specifier. Sigh, now I've got to do shit like this...
//
float fSize = (float)(iThisSize) / 1024.0f / 1024.0f;
int iSize = fSize;
int iRemainder = 100.0f * (fSize - floor(fSize));
Com_Printf("%20s %9d (%2d.%02dMB) in %6d blocks (%9d Bytes/block)\n",
psTagStrings[i],
iThisSize,
iSize,iRemainder,
iThisCount, iThisSize / iThisCount
);
}
}
Com_Printf("---------------------------------------------------------------------------\n");
Z_Stats_f();
}
#ifdef DEBUG_ZONE_ALLOCS
#pragma warning (disable:4503) // decorated name length xceeded, name was truncated
typedef map <sDebugString_t,int> LabelRefCount_t; // yet another place where Gil's tring class works and MS's doesn't
typedef map <sDebugString_t,LabelRefCount_t> TagBlockLabels_t;
TagBlockLabels_t AllTagBlockLabels;
#pragma warning (disable:4503) // decorated name length xceeded, name was truncated
static void Z_Snapshot_f(void)
{
AllTagBlockLabels.clear();
zoneHeader_t *pMemory = TheZone.Header.pNext;
while (pMemory)
{
AllTagBlockLabels[psTagStrings[pMemory->eTag]][pMemory->sOptionalLabel]++;
pMemory = pMemory->pNext;
}
giZoneSnaphotNum++;
Com_Printf("Ok. ( Current snapshot num is now %d )\n",giZoneSnaphotNum);
}
static void Z_TagDebug_f(void)
{
TagBlockLabels_t AllTagBlockLabels_Local;
qboolean bSnapShotTestActive = qfalse;
memtag_t eTag = TAG_ALL;
const char *psTAGName = Cmd_Argv(1);
if (psTAGName[0])
{
// check optional arg...
//
if (!Q_stricmp(psTAGName,"#snap"))
{
bSnapShotTestActive = qtrue;
AllTagBlockLabels_Local = AllTagBlockLabels; // horrible great STL copy
psTAGName = Cmd_Argv(2);
}
if (psTAGName[0])
{
// skip over "tag_" if user supplied it...
//
if (!Q_stricmpn(psTAGName,"TAG_",4))
{
psTAGName += 4;
}
// see if the user specified a valid tag...
//
for (int i=0; i<TAG_COUNT; i++)
{
if (!Q_stricmp(psTAGName,psTagStrings[i]))
{
eTag = (memtag_t) i;
break;
}
}
}
}
else
{
Com_Printf("Usage: 'zone_tagdebug [#snap] <tag>', e.g. TAG_GHOUL2, TAG_ALL (careful!)\n");
return;
}
Com_Printf("Dumping debug data for tag \"%s\"...%s\n\n",psTagStrings[eTag], bSnapShotTestActive?"( since snapshot only )":"");
Com_Printf("%8s"," "); // to compensate for code further down: Com_Printf("(%5d) ",iBlocksListed);
if (eTag == TAG_ALL)
{
Com_Printf("%20s ","Zone Tag");
}
Com_Printf("%9s\n","Bytes");
Com_Printf("%8s"," ");
if (eTag == TAG_ALL)
{
Com_Printf("%20s ","--------");
}
Com_Printf("%9s\n","-----");
if (bSnapShotTestActive)
{
// dec ref counts in last snapshot for all current blocks (which will make new stuff go negative)
//
zoneHeader_t *pMemory = TheZone.Header.pNext;
while (pMemory)
{
if (pMemory->eTag == eTag || eTag == TAG_ALL)
{
AllTagBlockLabels_Local[psTagStrings[pMemory->eTag]][pMemory->sOptionalLabel]--;
}
pMemory = pMemory->pNext;
}
}
// now dump them out...
//
int iBlocksListed = 0;
int iTotalSize = 0;
zoneHeader_t *pMemory = TheZone.Header.pNext;
while (pMemory)
{
if ( (pMemory->eTag == eTag || eTag == TAG_ALL)
&& (!bSnapShotTestActive || (pMemory->iSnapshotNumber == giZoneSnaphotNum && AllTagBlockLabels_Local[psTagStrings[pMemory->eTag]][pMemory->sOptionalLabel] <0) )
)
{
float fSize = (float)(pMemory->iSize) / 1024.0f / 1024.0f;
int iSize = fSize;
int iRemainder = 100.0f * (fSize - floor(fSize));
Com_Printf("(%5d) ",iBlocksListed);
if (eTag == TAG_ALL)
{
Com_Printf("%20s",psTagStrings[pMemory->eTag]);
}
Com_Printf(" %9d (%2d.%02dMB) File: \"%s\", Line: %d\n",
pMemory->iSize,
iSize,iRemainder,
pMemory->sSrcFileBaseName,
pMemory->iSrcFileLineNum
);
if (pMemory->sOptionalLabel[0])
{
Com_Printf("( Label: \"%s\" )\n",pMemory->sOptionalLabel);
}
iBlocksListed++;
iTotalSize += pMemory->iSize;
if (bSnapShotTestActive)
{
// bump ref count so we only 1 warning per new string, not for every one sharing that label...
//
AllTagBlockLabels_Local[psTagStrings[pMemory->eTag]][pMemory->sOptionalLabel]++;
}
}
pMemory = pMemory->pNext;
}
Com_Printf("( %d blocks listed, %d bytes (%.2fMB) total )\n",iBlocksListed, iTotalSize, (float)iTotalSize / 1024.0f / 1024.0f);
}
#endif
// Shuts down the zone memory system and frees up all memory
void Com_ShutdownZoneMemory(void)
{
Cmd_RemoveCommand("zone_stats");
Cmd_RemoveCommand("zone_details");
#ifdef _DEBUG
Cmd_RemoveCommand("zone_memrecovertest");
#endif
#ifdef DEBUG_ZONE_ALLOCS
Cmd_RemoveCommand("zone_tagdebug");
Cmd_RemoveCommand("zone_snapshot");
#endif
if(TheZone.Stats.iCount)
{
//Com_Printf("Automatically freeing %d blocks making up %d bytes\n", TheZone.Stats.iCount, TheZone.Stats.iCurrent);
Z_TagFree(TAG_ALL);
assert(!TheZone.Stats.iCount);
assert(!TheZone.Stats.iCurrent);
}
}
// Initialises the zone memory system
void Com_InitZoneMemory( void )
{
Com_Printf("Initialising zone memory .....\n");
memset(&TheZone, 0, sizeof(TheZone));
TheZone.Header.iMagic = ZONE_MAGIC;
com_validateZone = Cvar_Get("com_validateZone", "0", 0);
Cmd_AddCommand("zone_stats", Z_Stats_f);
Cmd_AddCommand("zone_details", Z_Details_f);
#ifdef _DEBUG
Cmd_AddCommand("zone_memrecovertest", Z_MemRecoverTest_f);
#endif
#ifdef DEBUG_ZONE_ALLOCS
Cmd_AddCommand("zone_tagdebug", Z_TagDebug_f);
Cmd_AddCommand("zone_snapshot", Z_Snapshot_f);
#endif
}
/*
========================
CopyString
NOTE: never write over the memory CopyString returns because
memory from a memstatic_t might be returned
========================
*/
char *CopyString( const char *in ) {
char *out;
if (!in[0]) {
return ((char *)&gEmptyString) + sizeof(zoneHeader_t);
}
else if (!in[1]) {
if (in[0] >= '0' && in[0] <= '9') {
return ((char *)&gNumberString[in[0]-'0']) + sizeof(zoneHeader_t);
}
}
out = (char *) S_Malloc (strlen(in)+1);
strcpy (out, in);
Z_Label(out,in);
return out;
}
/*
===============
Com_TouchMemory
Touch all known used data to make sure it is paged in
===============
*/
void Com_TouchMemory( void ) {
int start, end;
int i, j;
int sum;
int totalTouched;
Z_Validate();
start = Sys_Milliseconds();
sum = 0;
totalTouched=0;
zoneHeader_t *pMemory = TheZone.Header.pNext;
while (pMemory)
{
byte *pMem = (byte *) &pMemory[1];
j = pMemory->iSize >> 2;
for (i=0; i<j; i+=64){
sum += ((int*)pMem)[i];
}
totalTouched+=pMemory->iSize;
pMemory = pMemory->pNext;
}
end = Sys_Milliseconds();
//Com_Printf( "Com_TouchMemory: %i bytes, %i msec\n", totalTouched, end - start );
}