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

162
codemp/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

1490
codemp/qcommon/cm_draw.cpp Normal file

File diff suppressed because it is too large Load Diff

250
codemp/qcommon/cm_draw.h Normal file
View File

@@ -0,0 +1,250 @@
///////////////////////////////////////////////////////////////////////////////
// CDraw32 Class Interface
//
// Basic drawing routines for 32-bit per pixel buffer
///////////////////////////////////////////////////////////////////////////////
#if !defined(CM_DRAW_H_INC)
#define CM_DRAW_H_INC
#ifndef __linux__
//#include <windows.h>
#include "../qcommon/platform.h"
#endif
// 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

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

1184
codemp/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

310
codemp/qcommon/cm_local.h Normal file
View File

@@ -0,0 +1,310 @@
#pragma once
#if !defined(CM_LOCAL_H_INC)
#define CM_LOCAL_H_INC //rwwRMG - include guard
#include "cm_polylib.h"
#include "cm_landscape.h" //rwwRMG - include
#ifdef _XBOX
#include "sparc.h"
#endif
#define MAX_SUBMODELS 512
#define BOX_MODEL_HANDLE (MAX_SUBMODELS-1)
#define CAPSULE_MODEL_HANDLE (MAX_SUBMODELS-2)
#ifdef _XBOX
#pragma pack(push, 1)
typedef struct {
int planeNum;
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
int firstNode; // only for cmodel[0] (for the main and bsp instances)
} 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;
qboolean haswater;
} clipMap_t;
#else // _XBOX
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;
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
//rwwRMG - added:
// 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; //rwwRMG - changed from cm
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;
// 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 { //rwwRMG - modified
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 modelOrigin;// origin of the model tracing through
int contents; // ored contents of the model tracing through
qboolean isPoint; // optimized case
// trace_t trace; // returned from trace call
sphere_t sphere; // sphere for oriendted capsule collision
//rwwRMG - added:
vec3pair_t bounds; // enclosing box of start and end surrounding by size
vec3pair_t localBounds; // enclosing box of start and end surrounding by size for a segment
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;
} 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 );
//rwwRMG - changed to boxList to not conflict with list type
bool CM_CullWorldBox (const cplane_t *frustum, const vec3pair_t bounds); //rwwRMG - added
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 );
// cm_patch.c
struct patchCollide_s *CM_GeneratePatchCollide( int width, int height, vec3_t *points );
void CM_TraceThroughPatchCollide( traceWork_t *tw, trace_t &trace, const struct patchCollide_s *pc );
qboolean CM_PositionTestInPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc );
void CM_ClearLevelPatches( void );
//rwwRMG - added
//CCMLandScape *CM_RegisterTerrain(const char *config, bool server);
//void CM_ShutdownTerrain( thandle_t terrainId );
// 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_load.cpp
void CM_GetWorldBounds ( vec3_t mins, vec3_t maxs );
#endif

1809
codemp/qcommon/cm_patch.cpp Normal file

File diff suppressed because it is too large Load Diff

128
codemp/qcommon/cm_patch.h Normal file
View File

@@ -0,0 +1,128 @@
//#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;
#ifdef _XBOX
#define CM_MAX_GRID_SIZE 129
#else
#define MAX_GRID_SIZE 129
#endif
typedef struct {
int width;
int height;
qboolean wrapWidth;
qboolean wrapHeight;
#ifdef _XBOX
vec3_t points[CM_MAX_GRID_SIZE][CM_MAX_GRID_SIZE]; // [width][height]
#else
vec3_t points[MAX_GRID_SIZE][MAX_GRID_SIZE]; // [width][height]
#endif
} 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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,713 @@
//Anything above this #include will be ignored by the compiler
#include "../qcommon/exe_headers.h"
// 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);
// Com_Memset (w, 0, s); // qtrue param in Z_Malloc 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;
Com_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] = MAX_MAP_BOUNDS;
maxs[0] = maxs[1] = maxs[2] = -MAX_MAP_BOUNDS;
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];
Com_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: BUGUS_RANGE: %f",p1[j]);
j = i+1 == w->numpoints ? 0 : i+1;
// check the point is on the face plane
d = DotProduct (p1, facenormal) - facedist;
if (d < -ON_EPSILON || d > ON_EPSILON)
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;
Com_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;
Com_Memcpy( hullPoints, newHullPoints, numHullPoints * sizeof(vec3_t) );
}
FreeWinding( *hull );
w = AllocWinding( numHullPoints );
w->numpoints = numHullPoints;
*hull = w;
Com_Memcpy( w->p, hullPoints, numHullPoints * sizeof(vec3_t) );
}

View File

@@ -0,0 +1,47 @@
// this is only used for visualization tools in cm_ debug functions
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.1f
#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);

View File

@@ -0,0 +1,74 @@
#include "../game/q_shared.h"
#include "qfiles.h"
#ifdef _XBOX
void CM_LoadMap( const char *name, qboolean clientload, int *checksum);
#else
void CM_LoadMap( const char *name, qboolean clientload, int *checksum);
#endif
void CM_ClearMap( void );
clipHandle_t CM_InlineModel( int index ); // 0 = world, 1 + are bmodels
clipHandle_t CM_TempBoxModel( const vec3_t mins, const vec3_t maxs, int capsule );
void CM_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs );
int CM_NumClusters (void);
int CM_NumInlineModels( void );
char *CM_EntityString (void);
// 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, int capsule );
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, int capsule );
#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 );
//rwwRMG - changed to boxList to not conflict with list type
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 );
//rwwRMG - added:
bool CM_GenericBoxCollide(const vec3pair_t abounds, const vec3pair_t bbounds);
void CM_HandlePatchCollision(struct traceWork_s *tw, trace_t &trace, const vec3_t tStart, const vec3_t tEnd, class CCMPatch *patch, int checkcount);
void CM_CalcExtents(const vec3_t start, const vec3_t end, const struct traceWork_s *tw, vec3pair_t bounds);
// cm_tag.c
int CM_LerpTag( orientation_t *tag, clipHandle_t model, int startFrame, int endFrame,
float frac, const char *tagName );
// 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) );
// cm_shader.cpp
const char *CM_GetShaderText(const char *key);
void CM_FreeShaderText(void);
void CM_LoadShaderText(qboolean forceReload);

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

View File

@@ -0,0 +1,522 @@
//Anything above this #include will be ignored by the compiler
#include "../qcommon/exe_headers.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; // THIS IS THE ONLY COPY - IT IS NEVER FREED!
CHash<CCMShaderText> shaderTextTable;
CHash<CCMShader> cmShaderTable;
/*
====================
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 )
{
if( !shaderText )
{
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_FATAL, "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();
// We NEVER free the shadertext anymore!
// 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(qboolean forceReload)
{
if(forceReload)
{
CM_FreeShaderText();
}
// Above no longer blows away shader text, so we DO need to re-make the hash tables:
/*
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 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
// 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
{"water", ~CONTENTS_SOLID, 0, CONTENTS_WATER },
{"fog", ~CONTENTS_SOLID, 0, CONTENTS_FOG}, // carves surfaces entering
{"playerclip", ~CONTENTS_SOLID, 0, CONTENTS_PLAYERCLIP },
{"monsterclip", ~CONTENTS_SOLID, 0, CONTENTS_MONSTERCLIP },
{"botclip", ~CONTENTS_SOLID, 0, CONTENTS_BOTCLIP }, // for bots
{"shotclip", ~CONTENTS_SOLID, 0, CONTENTS_SHOTCLIP },
{"trigger", ~CONTENTS_SOLID, 0, CONTENTS_TRIGGER },
{"nodrop", ~CONTENTS_SOLID, 0, CONTENTS_NODROP }, // don't drop items or leave bodies (death fog, lava, etc)
{"terrain", ~CONTENTS_SOLID, 0, CONTENTS_TERRAIN }, // use special terrain collsion
{"ladder", ~CONTENTS_SOLID, 0, CONTENTS_LADDER }, // climb up in it like water
{"abseil", ~CONTENTS_SOLID, 0, CONTENTS_ABSEIL }, // can abseil down this brush
{"outside", ~CONTENTS_SOLID, 0, CONTENTS_OUTSIDE }, // volume is considered to be in the outside (i.e. not indoors)
{"inside", ~CONTENTS_SOLID, 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
};
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 |= i;
break;
}
}
}
/*
===============
ParseVector
===============
*/
static 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, "(" ) )
{
#ifndef FINAL_BUILD
Com_Printf( S_COLOR_YELLOW "WARNING: missing parenthesis in shader '%s'\n", shader->shader );
#endif
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, ")" ) )
{
#ifndef FINAL_BUILD
Com_Printf( S_COLOR_YELLOW "WARNING: missing parenthesis in shader '%s'\n", shader->shader );
#endif
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.
=================
*/
static 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 );
// 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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,497 @@
//Anything above this #include will be ignored by the compiler
#include "../qcommon/exe_headers.h"
#include "cm_local.h"
#include "cm_patch.h"
#include "cm_landscape.h"
#include "../qcommon/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;
// Hack. This shouldn't be here, but it's easier than including tr_local.h
typedef unsigned int GLenum;
#ifdef _XBOX
void R_LoadImage( const char *shortname, byte **pic, int *width, int *height, int *mipcount, GLenum *format );
#else
void R_LoadImage( const char *name, byte **pic, int *width, int *height, GLenum *format ) ;
#endif
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);
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,83 @@
#pragma once
#if !defined(CM_TERRAINMAP_H_INC)
#define CM_TERRAINMAP_H_INC
#ifdef _XBOX
#define TM_WIDTH 64
#define TM_HEIGHT 64
#define TM_BORDER 4
#else
#define TM_WIDTH 512
#define TM_HEIGHT 512
#define TM_BORDER 16
#endif
#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

575
codemp/qcommon/cm_test.cpp Normal file
View File

@@ -0,0 +1,575 @@
//Anything above this #include will be ignored by the compiler
#include "../qcommon/exe_headers.h"
#include "cm_local.h"
#ifdef _XBOX
#include "../renderer/tr_local.h"
#endif
/*
==================
CM_PointLeafnum_r
==================
*/
int CM_PointLeafnum_r( const vec3_t p, int num, clipMap_t *local ) {
float d;
cNode_t *node;
cplane_t *plane;
while (num >= 0)
{
node = local->nodes + num;
#ifdef _XBOX
plane = cmg.planes + node->planeNum;//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 = cm.surfaces[ cm.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;
while (1) {
if (nodenum < 0) {
ll->storeLeafs( ll, nodenum );
return;
}
node = &cmg.nodes[nodenum];
#ifdef _XBOX
plane = cmg.planes + node->planeNum;//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) {
//rwwRMG - changed to boxList to not conflict with list type
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 ) {
//rwwRMG - changed to boxList to not conflict with list type
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
==================
*/
int CM_PointContents( const vec3_t p, clipHandle_t model ) {
int leafnum;
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 );
if (clipm->firstNode != -1)
{
leafnum = CM_PointLeafnum_r (p, 0, local);
leaf = &local->leafs[leafnum];
}
else
{
leaf = &clipm->leaf;
}
}
else
{
local = &cmg;
leafnum = CM_PointLeafnum_r (p, 0, &cmg);
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;
/*
if(cmg.landScape && (contents & CONTENTS_TERRAIN))
{
if(p[2] < cmg.landScape->GetWaterHeight())
{
contents |= cmg.landScape->GetWaterContents();
}
}
*/
}
}
return contents;
}
/*
==================
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 // _XBOX
/*
===============================================================================
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 = &cm.areas[ areaNum ];
if ( area->floodvalid == cm.floodvalid ) {
if (area->floodnum == floodnum)
return;
Com_Error (ERR_DROP, "FloodArea_r: reflooded");
}
area->floodnum = floodnum;
area->floodvalid = cm.floodvalid;
con = cm.areaPortals + areaNum * cm.numAreas;
for ( i=0 ; i < cm.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
cm.floodvalid++;
floodnum = 0;
for (i = 0 ; i < cm.numAreas ; i++) {
area = &cm.areas[i];
if (area->floodvalid == cm.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
Com_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;
}

2002
codemp/qcommon/cm_trace.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,651 @@
// cmd.c -- Quake script command processing module
//Anything above this #include will be ignored by the compiler
#include "../qcommon/exe_headers.h"
#ifdef _XBOX
#include "../cgame/cg_local.h"
#include "../client/cl_data.h"
#endif
#define MAX_CMD_BUFFER 16384
#define MAX_CMD_LINE 1024
typedef struct {
byte *data;
int maxsize;
int cursize;
} cmd_t;
int cmd_wait;
cmd_t cmd_text;
byte cmd_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 ) {
#ifdef _XBOX
if(ClientManager::splitScreenMode == qtrue)
{
if ( Cmd_Argc() == 2 ) {
ClientManager::ActiveClient().cmd_wait = atoi( Cmd_Argv( 1 ) );
} else {
ClientManager::ActiveClient().cmd_wait = 1;
}
}
else
{
#endif
if ( Cmd_Argc() == 2 ) {
cmd_wait = atoi( Cmd_Argv( 1 ) );
} else {
cmd_wait = 1;
}
#ifdef _XBOX
}
#endif
}
/*
=============================================================================
COMMAND BUFFER
=============================================================================
*/
/*
============
Cbuf_Init
============
*/
void Cbuf_Init (void)
{
#ifdef _XBOX
if(ClientManager::splitScreenMode == qtrue)
{
CM_START_LOOP();
MSG_Init (&ClientManager::ActiveClient().cmd_text, ClientManager::ActiveClient().cmd_text_buf,
sizeof(ClientManager::ActiveClient().cmd_text_buf));
CM_END_LOOP();
}
else
{
#endif
cmd_text.data = cmd_text_buf;
cmd_text.maxsize = MAX_CMD_BUFFER;
cmd_text.cursize = 0;
#ifdef _XBOX
}
#endif
}
/*
============
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);
#ifdef _XBOX
if(ClientManager::splitScreenMode == qtrue)
{
if (ClientManager::ActiveClient().cmd_text.cursize + l >= ClientManager::ActiveClient().cmd_text.maxsize)
{
Com_Printf ("Cbuf_AddText: overflow\n");
return;
}
Com_Memcpy (&ClientManager::ActiveClient().cmd_text.data[ClientManager::ActiveClient().cmd_text.cursize], text, strlen (text));
ClientManager::ActiveClient().cmd_text.cursize += l;
}
else
{
#endif
if (cmd_text.cursize + l >= cmd_text.maxsize)
{
Com_Printf ("Cbuf_AddText: overflow\n");
return;
}
Com_Memcpy(&cmd_text.data[cmd_text.cursize], text, l);
cmd_text.cursize += l;
#ifdef _XBOX
}
#endif
}
/*
============
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;
#ifdef _XBOX
if(ClientManager::splitScreenMode == qtrue)
{
if ( len + ClientManager::ActiveClient().cmd_text.cursize > ClientManager::ActiveClient().cmd_text.maxsize ) {
Com_Printf( "Cbuf_InsertText overflowed\n" );
return;
}
// move the existing command text
for ( i = ClientManager::ActiveClient().cmd_text.cursize - 1 ; i >= 0 ; i-- ) {
ClientManager::ActiveClient().cmd_text.data[ i + len ] = ClientManager::ActiveClient().cmd_text.data[ i ];
}
// copy the new text in
memcpy( ClientManager::ActiveClient().cmd_text.data, text, len - 1 );
// add a \n
ClientManager::ActiveClient().cmd_text.data[ len - 1 ] = '\n';
ClientManager::ActiveClient().cmd_text.cursize += len;
}
else
{
#endif
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
Com_Memcpy( cmd_text.data, text, len - 1 );
// add a \n
cmd_text.data[ len - 1 ] = '\n';
cmd_text.cursize += len;
#ifdef _XBOX
}
#endif
}
/*
============
Cbuf_ExecuteText
============
*/
void Cbuf_ExecuteText (int exec_when, const char *text)
{
switch (exec_when)
{
case EXEC_NOW:
if (text && strlen(text) > 0) {
Cmd_ExecuteString (text);
} else {
Cbuf_Execute();
}
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_LINE];
int quotes;
#ifdef _XBOX
if(ClientManager::splitScreenMode == qtrue)
{
CM_START_LOOP();
while (ClientManager::ActiveClient().cmd_text.cursize)
{
if ( ClientManager::ActiveClient().cmd_wait ) {
// skip out while text still remains in buffer, leaving it
// for next frame
ClientManager::ActiveClient().cmd_wait--;
break;
}
// find a \n or ; line break
text = (char *)ClientManager::ActiveClient().cmd_text.data;
quotes = 0;
for (i=0 ; i< ClientManager::ActiveClient().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 == ClientManager::ActiveClient().cmd_text.cursize)
ClientManager::ActiveClient().cmd_text.cursize = 0;
else
{
i++;
ClientManager::ActiveClient().cmd_text.cursize -= i;
memmove (text, text+i, ClientManager::ActiveClient().cmd_text.cursize);
}
// execute the command line
Cmd_ExecuteString (line);
}
CM_END_LOOP();
}
else
{
#endif
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;
}
if( i >= (MAX_CMD_LINE - 1)) {
i = MAX_CMD_LINE - 1;
}
Com_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);
}
#ifdef _XBOX
}
#endif
}
/*
==============================================================================
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;
}
#ifndef FINAL_BUILD
Com_Printf ("execing %s\n",Cmd_Argv(1));
#endif
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
=============================================================================
*/
static int cmd_argc;
static char *cmd_argv[MAX_STRING_TOKENS]; // points into cmd_tokenized
static char cmd_tokenized[BIG_INFO_STRING+MAX_STRING_TOKENS]; // will have 0 bytes inserted
/*
============
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-1 ) {
strcat( cmd_args, " " );
}
}
return cmd_args;
}
/*
============
Cmd_Args
Returns a single string containing argv(arg) to argv(argc()-1)
============
*/
char *Cmd_ArgsFrom( int arg ) {
static char cmd_args[BIG_INFO_STRING];
int i;
cmd_args[0] = 0;
if (arg < 0)
arg = 0;
for ( i = arg ; i < cmd_argc ; i++ ) {
strcat( cmd_args, cmd_argv[i] );
if ( i != cmd_argc-1 ) {
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 char *text;
char *textOut;
// clear previous args
cmd_argc = 0;
if ( !text_in ) {
return;
}
text = 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 ( *(const unsigned char* /*eurofix*/)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_Init
============
*/
extern void Cmd_List_f(void);
void Cmd_Init (void) {
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);
}

View File

@@ -0,0 +1,165 @@
#include "../qcommon/exe_headers.h"
#define CMD_MAX_NUM 512
#define CMD_MAX_NAME 32
typedef struct cmd_function_s
{
char name[CMD_MAX_NAME];
xcommand_t function;
} cmd_function_t;
static cmd_function_t cmd_functions[CMD_MAX_NUM] = {0}; // possible commands to execute
/*
============
Cmd_AddCommand
============
*/
void Cmd_AddCommand( const char *cmd_name, xcommand_t function ) {
cmd_function_t *cmd;
cmd_function_t *add = NULL;
int i;
// fail if the command already exists
for (i=0; i<CMD_MAX_NUM; i++) {
cmd = cmd_functions + i;
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) {
Com_Printf("Cmd_AddCommand: Too many commands registered\n");
return;
}
if(strlen(cmd_name) >= CMD_MAX_NAME - 1) {
Com_Printf("Cmd_AddCommand: Excessively long command name\n");
} else {
Q_strncpyz(add->name, cmd_name, CMD_MAX_NAME);
add->function = function;
}
}
/*
============
Cmd_RemoveCommand
============
*/
void Cmd_RemoveCommand( const char *cmd_name ) {
cmd_function_t *cmd;
int i;
for(i=0; i<CMD_MAX_NUM; i++) {
cmd = cmd_functions + i;
if ( !strcmp( cmd_name, cmd->name ) ) {
cmd->name[0] = 0;
return;
}
}
}
/*
============
Cmd_ExecuteString
A complete command line has been parsed, so try to execute it
============
*/
void Cmd_ExecuteString( const char *text ) {
int i;
// execute the command line
Cmd_TokenizeString( text );
if ( !Cmd_Argc() ) {
return; // no tokens
}
// check registered command functions
for(i=0; i<CMD_MAX_NUM; i++) {
if ( !Q_stricmp( Cmd_Argv(0), cmd_functions[i].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[i];
cmd_functions[i] = 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 ( text );
CL_ForwardCommandToServer ( text );
}
/*
============
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);
}

174
codemp/qcommon/cmd_pc.cpp Normal file
View File

@@ -0,0 +1,174 @@
#include "../qcommon/exe_headers.h"
typedef struct cmd_function_s
{
struct cmd_function_s *next;
char *name;
xcommand_t function;
} cmd_function_t;
static cmd_function_t *cmd_functions; // possible commands to execute
/*
============
Cmd_AddCommand
============
*/
void Cmd_AddCommand( const char *cmd_name, xcommand_t function ) {
cmd_function_t *cmd;
// fail if the command already exists
for ( cmd = cmd_functions ; cmd ; cmd=cmd->next ) {
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;
}
}
// use a small malloc to avoid zone fragmentation
cmd = (struct cmd_function_s *)S_Malloc (sizeof(cmd_function_t));
cmd->name = CopyString( cmd_name );
cmd->function = function;
cmd->next = cmd_functions;
cmd_functions = cmd;
}
/*
============
Cmd_RemoveCommand
============
*/
void Cmd_RemoveCommand( const char *cmd_name ) {
cmd_function_t *cmd, **back;
back = &cmd_functions;
while( 1 ) {
cmd = *back;
if ( !cmd ) {
// command wasn't active
return;
}
if ( !strcmp( cmd_name, cmd->name ) ) {
*back = cmd->next;
if (cmd->name) {
Z_Free(cmd->name);
}
Z_Free (cmd);
return;
}
back = &cmd->next;
}
}
/*
============
Cmd_CommandCompletion
============
*/
void Cmd_CommandCompletion( void(*callback)(const char *s) ) {
cmd_function_t *cmd;
for (cmd=cmd_functions ; cmd ; cmd=cmd->next) {
callback( cmd->name );
}
}
/*
============
Cmd_ExecuteString
A complete command line has been parsed, so try to execute it
============
*/
void Cmd_ExecuteString( const char *text ) {
cmd_function_t *cmd, **prev;
// execute the command line
Cmd_TokenizeString( text );
if ( !Cmd_Argc() ) {
return; // no tokens
}
// check registered command functions
for ( prev = &cmd_functions ; *prev ; prev = &cmd->next ) {
cmd = *prev;
if ( !Q_stricmp( Cmd_Argv(0), cmd->name ) ) {
// rearrange the links so that the command will be
// near the head of the list next time it is used
*prev = cmd->next;
cmd->next = cmd_functions;
cmd_functions = cmd;
// perform the action
if ( !cmd->function ) {
// let the cgame or game handle it
break;
} else {
cmd->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 ( text );
CL_ForwardCommandToServer ( text );
}
/*
============
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 (cmd=cmd_functions ; cmd ; cmd=cmd->next) {
if (match && !Com_Filter(match, cmd->name, qfalse)) continue;
Com_Printf ("%s\n", cmd->name);
i++;
}
Com_Printf ("%i commands\n", i);
}

View File

@@ -0,0 +1,97 @@
//Anything above this #include will be ignored by the compiler
#include "../qcommon/exe_headers.h"
#ifdef _DONETPROFILE_
#pragma warning( disable : 4786)
#pragma warning( disable : 4100)
#pragma warning( disable : 4663)
#include <windows.h>
#include <stdio.h>
#include <assert.h>
#include "hstring.h"
#include "INetProfile.h"
using namespace std;
class CNetProfile : public INetProfile
{
float mElapsedTime;
map <hstring,unsigned int> mFieldCounts;
float mFrameCount;
public:
void Reset(void)
{
mFieldCounts.clear();
mFrameCount=0;
}
void AddField(char *fieldName,int sizeBytes)
{
assert(sizeBytes>=0);
if(sizeBytes==0)
{
return;
}
map<hstring,unsigned int>::iterator f=mFieldCounts.find(fieldName);
if(f==mFieldCounts.end())
{
mFieldCounts[fieldName]=(unsigned int)sizeBytes;
}
else
{
mFieldCounts[fieldName]+=(unsigned int)sizeBytes;
}
}
void IncTime(int msec)
{
mElapsedTime+=msec;
}
void ShowTotals(void)
{
float totalBytes=0;
multimap<unsigned int,hstring> sort;
map<hstring,unsigned int>::iterator f;
for(f=mFieldCounts.begin();f!=mFieldCounts.end();f++)
{
sort.insert(pair<unsigned int,hstring> ((*f).second,(*f).first));
totalBytes+=(*f).second;
}
multimap<unsigned int,hstring>::iterator j;
char msg[1024];
float percent;
sprintf(msg,
"******** Totals: bytes %d : bytes per sec %d ********\n",
(unsigned int)totalBytes,
(unsigned int)((totalBytes/mElapsedTime)*1000));
Sleep(10);
OutputDebugString(msg);
for(j=sort.begin();j!=sort.end();j++)
{
percent=(((float)(*j).first)/totalBytes)*100.0f;
assert(strlen((*j).second.c_str())<1024);
sprintf(msg,"%36s : %3.4f percent : %d bytes \n",(*j).second.c_str(),percent,(*j).first);
Sleep(10);
OutputDebugString(msg);
}
}
};
INetProfile &ClReadProf(void)
{
static CNetProfile theClReadProf;
return(theClReadProf);
}
INetProfile &ClSendProf(void)
{
static CNetProfile theClSendProf;
return(theClSendProf);
}
#endif // _DONETPROFILE_

2400
codemp/qcommon/common.cpp Normal file

File diff suppressed because it is too large Load Diff

1031
codemp/qcommon/cvar.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
// hide these nasty warnings
#ifdef _WIN32
#pragma warning(disable : 4018) // signed/unsigned mismatch
#pragma warning(disable : 4032)
#pragma warning(disable : 4051)
#pragma warning(disable : 4057) // slightly different base types
#pragma warning(disable : 4100) // unreferenced formal parameter
#pragma warning(disable : 4115)
#pragma warning(disable : 4125) // decimal digit terminates octal escape sequence
#pragma warning(disable : 4127) // conditional expression is constant
#pragma warning(disable : 4136)
#pragma warning(disable : 4152) // nonstandard extension, function/data pointer conversion in expression
#pragma warning(disable : 4201)
#pragma warning(disable : 4214)
#pragma warning(disable : 4244) // conversion from double to float
#pragma warning(disable : 4284) // return type not UDT
#pragma warning(disable : 4305) // truncation from const double to float
#pragma warning(disable : 4310) // cast truncates constant value
#pragma warning(disable : 4389) // signed/unsigned mismatch
#pragma warning(disable : 4503) // decorated name length truncated
//#pragma warning(disable: 4505)!!!remove these to reduce vm size!! // unreferenced local function has been removed
#pragma warning(disable : 4511) //copy ctor could not be genned
#pragma warning(disable : 4512) //assignment op could not be genned
#pragma warning(disable : 4514) // unreffed inline removed
#pragma warning(disable : 4663) // c++ lang change
#pragma warning(disable : 4702) // unreachable code
#pragma warning(disable : 4710) // not inlined
#pragma warning(disable : 4711) // selected for automatic inline expansion
#pragma warning(disable : 4220) // varargs matches remaining parameters
#pragma warning(disable : 4786) //identifier was truncated
//rww (for vc.net, warning numbers changed apparently):
#pragma warning(disable : 4213) //nonstandard extension used : cast on l-value
#pragma warning(disable : 4245) //signed/unsigned mismatch
#endif

View File

@@ -0,0 +1,3 @@
//This file creates the PCH for the rest of the project to use
#include "../qcommon/exe_headers.h"

View File

@@ -0,0 +1,5 @@
//This is shared by client and server so it's best to keep that in mind
//for the sake of ds builds.
#include "../game/q_shared.h"
#include "../qcommon/qcommon.h"

158
codemp/qcommon/files.h Normal file
View File

@@ -0,0 +1,158 @@
#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 BASEGAME "base"
#define DEMOGAME "demo"
// every time a new demo pk3 file is built, this checksum must be updated.
// the easiest way to get it is to just run the game and see what it spits out
#define DEMO_PAK_CHECKSUM 437558517u
// 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
// NOW defined in build files
//#define PRE_RELEASE_TADEMO
#define MAX_ZPATH 256
#define MAX_SEARCH_PATHS 4096
#define MAX_FILEHASH_SIZE 1024
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\pak0.pk3
char pakBasename[MAX_OSPATH]; // pak0
char pakGamename[MAX_OSPATH]; // base
#ifndef _XBOX
unzFile handle; // handle to zip file
#endif
int checksum; // regular checksum
int pure_checksum; // checksum for pure
int numfiles; // number of files in pk3
int referenced; // referenced file flags
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:\jk2
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;
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;
qboolean streamed;
char name[MAX_ZPATH];
#ifdef _XBOX
GOBHandle ghandle;
qboolean gob;
qboolean used;
wfhandle_t whandle;
#endif
} fileHandleData_t;
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_homepath;
extern cvar_t *fs_basepath;
extern cvar_t *fs_basegame;
extern cvar_t *fs_cdpath;
extern cvar_t *fs_copyfiles;
extern cvar_t *fs_gamedirvar;
extern cvar_t *fs_restrict;
extern cvar_t *fs_dirbeforepak; //rww - when building search path, keep directories at top and insert pk3's under them
extern searchpath_t *fs_searchpaths;
extern int fs_readCount; // total bytes read
extern int fs_loadCount; // total files read
extern int fs_loadStack; // total files in memory
extern int fs_packFiles; // total number of files in packs
extern int fs_fakeChkSum;
extern int fs_checksumFeed;
extern fileHandleData_t fsh[MAX_FILE_HANDLES];
extern qboolean initialized;
// never load anything from pk3 files that are not present at the server when pure
extern int fs_numServerPaks;
extern int fs_serverPaks[MAX_SEARCH_PATHS]; // checksums
extern char *fs_serverPakNames[MAX_SEARCH_PATHS]; // pk3 names
// only used for autodownload, to make sure the client has at least
// all the pk3 files that are referenced at the server side
extern int fs_numServerReferencedPaks;
extern int fs_serverReferencedPaks[MAX_SEARCH_PATHS]; // checksums
extern char *fs_serverReferencedPakNames[MAX_SEARCH_PATHS]; // pk3 names
// last valid game folder used
extern char lastValidBase[MAX_OSPATH];
extern char lastValidGame[MAX_OSPATH];
#ifdef FS_MISSING
extern FILE* missingFiles;
#endif
void FS_Startup( const char *gameName );
qboolean FS_CreatePath(char *OSPath);
char *FS_BuildOSPath( const char *base, const char *game, const char *qpath );
char *FS_BuildOSPath( 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,512 @@
/*****************************************************************************
* name: files.c
*
* desc: handle based filesystem for Quake III Arena
*
*****************************************************************************/
//Anything above this #include will be ignored by the compiler
#include "../qcommon/exe_headers.h"
#include "../client/client.h"
//#include "../zlib32/zip.h"
//#include "unzip.h"
#include "files.h"
//#include <windows.h> //rww - included to make fs_copyfiles 2 related functions happy.
#include "platform.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_ZPATH is 256 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 reletive 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 "home path" is the path used for all write access. On win32 systems we have "base path"
== "home path", but on *nix systems the base installation is usually readonly, and
"home path" points to ~/.q3a or similar
The user can also install custom mods and content in "home path", so it should be searched
along with "home path" and "cd path" for game content.
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 "pak0.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 demoq3/pak0.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 jampconfig.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)
File search order: when FS_FOpenFileRead gets called it will go through the fs_searchpaths
structure and stop on the first successful hit. fs_searchpaths is built with successive
calls to FS_AddGameDirectory
Additionaly, we search in several subdirectories:
current game is the current mode
base game is a variable to allow mods based on other mods
(such as base + missionpack content combination in a mod for instance)
BASEGAME is the hardcoded base game ("base")
e.g. the qpath "sound/newstuff/test.wav" would be searched for in the following places:
home path + current game's zip files
home path + current game's directory
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
home path + base game's zip file
home path + base game's directory
base path + base game's zip file
base path + base game's directory
cd path + base game's zip file
cd path + base game's directory
home path + BASEGAME's zip file
home path + BASEGAME's directory
base path + BASEGAME's zip file
base path + BASEGAME's directory
cd path + BASEGAME's zip file
cd path + BASEGAME's directory
server download, to be written to home 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 jampconfig.cfg isn't present in it,
or configs will never get loaded from disk!
todo:
downloading (outside fs?)
game directory passing and restarting
=============================================================================
*/
char fs_gamedir[MAX_OSPATH]; // this will be a single file name with no separators
cvar_t *fs_debug;
cvar_t *fs_homepath;
cvar_t *fs_basepath;
cvar_t *fs_basegame;
cvar_t *fs_cdpath;
cvar_t *fs_copyfiles;
cvar_t *fs_gamedirvar;
cvar_t *fs_restrict;
cvar_t *fs_dirbeforepak; //rww - when building search path, keep directories at top and insert pk3's under them
searchpath_t *fs_searchpaths;
int fs_readCount; // total bytes read
int fs_loadCount; // total files read
int fs_loadStack; // total files in memory
int fs_packFiles; // total number of files in packs
int fs_fakeChkSum;
int fs_checksumFeed;
fileHandleData_t fsh[MAX_FILE_HANDLES];
// never load anything from pk3 files that are not present at the server when pure
int fs_numServerPaks;
int fs_serverPaks[MAX_SEARCH_PATHS]; // checksums
char *fs_serverPakNames[MAX_SEARCH_PATHS]; // pk3 names
// only used for autodownload, to make sure the client has at least
// all the pk3 files that are referenced at the server side
int fs_numServerReferencedPaks;
int fs_serverReferencedPaks[MAX_SEARCH_PATHS]; // checksums
char *fs_serverReferencedPakNames[MAX_SEARCH_PATHS]; // pk3 names
// last valid game folder used
char lastValidBase[MAX_OSPATH];
char lastValidGame[MAX_OSPATH];
#ifdef FS_MISSING
FILE* missingFiles = NULL;
#endif
qboolean initialized = qfalse;
/*
Extra utility for checking that FS is up and running
*/
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);
}
/*
=================
FS_LoadStack
return load stack
=================
*/
int FS_LoadStack()
{
return fs_loadStack;
}
fileHandle_t FS_HandleForFile(void) {
int i;
for ( i = 1 ; i < MAX_FILE_HANDLES ; i++ ) {
if ( fsh[i].handleFiles.file.o == NULL ) {
return i;
}
}
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
// Fix for filenames that are given to FS with a leading "/" (/botfiles/Foo)
if (qpath[0] == '\\' || qpath[0] == '/')
qpath++;
// FIXME VVFIXME Holy crap this is wrong.
// Com_sprintf( temp, sizeof(temp), "/%s/%s", fs_gamedirvar->string, qpath );
Com_sprintf( temp, sizeof(temp), "/%s/%s", "base", qpath );
FS_ReplaceSeparators( temp );
Com_sprintf( ospath[toggle], sizeof( ospath[0] ), "%s%s",
fs_basepath->string, temp );
return ospath[toggle];
}
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;
//pre-fs_cf2
//toggle ^= 1; // flip-flop to allow two returns without clash
//post-fs_cf2
toggle = (++toggle)&3; // allows four returns without clash (increased from 2 during fs_copyfiles 2 enhancement)
if( !game || !game[0] ) {
game = fs_gamedir;
}
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];
}
/*
===========
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 (c1 >= 'a' && c1 <= 'z') {
c1 -= ('a' - 'A');
}
if (c2 >= 'a' && c2 <= 'z') {
c2 -= ('a' - 'A');
}
if ( c1 == '\\' || c1 == ':' ) {
c1 = '/';
}
if ( c2 == '\\' || c2 == ':' ) {
c2 = '/';
}
if (c1 != c2) {
return (qboolean)-1; // strings not equal
}
} while (c1);
return (qboolean)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);
}
/*
======================================================================================
CONVENIENCE FUNCTIONS FOR ENTIRE FILES
======================================================================================
*/
/*
============
FS_WriteFile
Filename are reletive 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( qboolean closemfp ) {
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( "fdir" );
Cmd_RemoveCommand( "touchFile" );
#ifdef FS_MISSING
if (closemfp) {
fclose(missingFiles);
}
#endif
}
/*
================
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 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_homepath" );
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( "mpdefault.cfg", NULL ) <= 0 ) {
Com_Error( ERR_FATAL, "Couldn't load mpdefault.cfg" );
// bk001208 - SafeMode see below, FIXME?
}
Q_strncpyz(lastValidBase, fs_basepath->string, sizeof(lastValidBase));
Q_strncpyz(lastValidGame, fs_gamedirvar->string, sizeof(lastValidGame));
// bk001208 - SafeMode see below, FIXME?
}

File diff suppressed because it is too large Load Diff

3125
codemp/qcommon/files_pc.cpp Normal file

File diff suppressed because it is too large Load Diff

167
codemp/qcommon/fixedmap.h Normal file
View File

@@ -0,0 +1,167 @@
#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++;
}
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

View File

@@ -0,0 +1,14 @@
// Copyright (C) 2000-2002 Raven Software, Inc.
//
#include "../win32/AutoVersion.h"
// Current version of the multi player game
#ifdef _DEBUG
#define Q3_VERSION "(debug)JAmp: v"VERSION_STRING_DOTTED
#elif defined FINAL_BUILD
#define Q3_VERSION "JAmp: v"VERSION_STRING_DOTTED
#else
#define Q3_VERSION "(internal)JAmp: v"VERSION_STRING_DOTTED
#endif
//end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,204 @@
#pragma once
#if !defined(GENERICPARSER2_H_INC)
#define GENERICPARSER2_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including GenericParser2.h")
#endif
#include "disablewarnings.h"
#ifdef USE_LOCAL_GENERICPARSER
#include <memory.h>
#include <malloc.h>
#include <string.h>
#define trap_Z_Malloc(x, y) malloc(x)
#define trap_Z_Free(x) free(x)
#endif
class CTextPool;
class CGPObject;
class CTextPool
{
private:
char *mPool;
CTextPool *mNext;
int mSize, mUsed;
public:
CTextPool(int initSize = 10240);
~CTextPool(void);
CTextPool *GetNext(void) { return mNext; }
void SetNext(CTextPool *which) { mNext = which; }
char *GetPool(void) { return mPool; }
int GetUsed(void) { return mUsed; }
char *AllocText(char *text, bool addNULL = true, CTextPool **poolPtr = 0);
};
void CleanTextPool(CTextPool *pool);
class CGPObject
{
protected:
const char *mName;
CGPObject *mNext, *mInOrderNext, *mInOrderPrevious;
public:
CGPObject(const char *initName);
const char *GetName(void) { return mName; }
CGPObject *GetNext(void) { return mNext; }
void SetNext(CGPObject *which) { mNext = which; }
CGPObject *GetInOrderNext(void) { return mInOrderNext; }
void SetInOrderNext(CGPObject *which) { mInOrderNext = which; }
CGPObject *GetInOrderPrevious(void) { return mInOrderPrevious; }
void SetInOrderPrevious(CGPObject *which) { mInOrderPrevious = which; }
bool WriteText(CTextPool **textPool, const char *text);
};
class CGPValue : public CGPObject
{
private:
CGPObject *mList;
public:
CGPValue(const char *initName, const char *initValue = 0);
~CGPValue(void);
CGPValue *GetNext(void) { return (CGPValue *)mNext; }
CGPValue *Duplicate(CTextPool **textPool = 0);
bool IsList(void);
const char *GetTopValue(void);
CGPObject *GetList(void) { return mList; }
void AddValue(const char *newValue, CTextPool **textPool = 0);
bool Parse(char **dataPtr, CTextPool **textPool);
bool Write(CTextPool **textPool, int depth);
};
class CGPGroup : public CGPObject
{
private:
CGPValue *mPairs, *mInOrderPairs;
CGPValue *mCurrentPair;
CGPGroup *mSubGroups, *mInOrderSubGroups;
CGPGroup *mCurrentSubGroup;
CGPGroup *mParent;
bool mWriteable;
void SortObject(CGPObject *object, CGPObject **unsortedList, CGPObject **sortedList,
CGPObject **lastObject);
public:
CGPGroup(const char *initName = "Top Level", CGPGroup *initParent = 0);
~CGPGroup(void);
CGPGroup *GetParent(void) { return mParent; }
CGPGroup *GetNext(void) { return (CGPGroup *)mNext; }
int GetNumSubGroups(void);
int GetNumPairs(void);
void Clean(void);
CGPGroup *Duplicate(CTextPool **textPool = 0, CGPGroup *initParent = 0);
void SetWriteable(const bool writeable) { mWriteable = writeable; }
CGPValue *GetPairs(void) { return mPairs; }
CGPValue *GetInOrderPairs(void) { return mInOrderPairs; }
CGPGroup *GetSubGroups(void) { return mSubGroups; }
CGPGroup *GetInOrderSubGroups(void) { return mInOrderSubGroups; }
CGPValue *AddPair(const char *name, const char *value, CTextPool **textPool = 0);
void AddPair(CGPValue *NewPair);
CGPGroup *AddGroup(const char *name, CTextPool **textPool = 0);
void AddGroup(CGPGroup *NewGroup);
CGPGroup *FindSubGroup(const char *name);
bool Parse(char **dataPtr, CTextPool **textPool);
bool Write(CTextPool **textPool, int depth);
CGPValue *FindPair(const char *key);
const char *FindPairValue(const char *key, const char *defaultVal = 0);
};
class CGenericParser2
{
private:
CGPGroup mTopLevel;
CTextPool *mTextPool;
bool mWriteable;
public:
CGenericParser2(void);
~CGenericParser2(void);
void SetWriteable(const bool writeable) { mWriteable = writeable; }
CGPGroup *GetBaseParseGroup(void) { return &mTopLevel; }
bool Parse(char **dataPtr, bool cleanFirst = true, bool writeable = false);
bool Parse(char *dataPtr, bool cleanFirst = true, bool writeable = false)
{
return Parse(&dataPtr, cleanFirst, writeable);
}
void Clean(void);
bool Write(CTextPool *textPool);
};
// The following groups of routines are used for a C interface into GP2.
// C++ users should just use the objects as normally and not call these routines below
//
typedef void *TGenericParser2;
typedef void *TGPGroup;
typedef void *TGPValue;
// CGenericParser2 (void *) routines
TGenericParser2 GP_Parse(char **dataPtr, bool cleanFirst, bool writeable);
void GP_Clean(TGenericParser2 GP2);
void GP_Delete(TGenericParser2 *GP2);
TGPGroup GP_GetBaseParseGroup(TGenericParser2 GP2);
// CGPGroup (void *) routines
const char *GPG_GetName(TGPGroup GPG);
bool GPG_GetName(TGPGroup GPG, char *Value);
TGPGroup GPG_GetNext(TGPGroup GPG);
TGPGroup GPG_GetInOrderNext(TGPGroup GPG);
TGPGroup GPG_GetInOrderPrevious(TGPGroup GPG);
TGPGroup GPG_GetPairs(TGPGroup GPG);
TGPGroup GPG_GetInOrderPairs(TGPGroup GPG);
TGPGroup GPG_GetSubGroups(TGPGroup GPG);
TGPGroup GPG_GetInOrderSubGroups(TGPGroup GPG);
TGPGroup GPG_FindSubGroup(TGPGroup GPG, const char *name);
TGPValue GPG_FindPair(TGPGroup GPG, const char *key);
const char *GPG_FindPairValue(TGPGroup GPG, const char *key, const char *defaultVal);
bool GPG_FindPairValue(TGPGroup GPG, const char *key, const char *defaultVal, char *Value);
// CGPValue (void *) routines
const char *GPV_GetName(TGPValue GPV);
bool GPV_GetName(TGPValue GPV, char *Value);
TGPValue GPV_GetNext(TGPValue GPV);
TGPValue GPV_GetInOrderNext(TGPValue GPV);
TGPValue GPV_GetInOrderPrevious(TGPValue GPV);
bool GPV_IsList(TGPValue GPV);
const char *GPV_GetTopValue(TGPValue GPV);
bool GPV_GetTopValue(TGPValue GPV, char *Value);
TGPValue GPV_GetList(TGPValue GPV);
#endif // GENERICPARSER2_H_INC

501
codemp/qcommon/hstring.cpp Normal file
View File

@@ -0,0 +1,501 @@
//Anything above this #include will be ignored by the compiler
#include "../qcommon/exe_headers.h"
#ifdef _DONETPROFILE_
#include <assert.h>
#include "hstring.h"
using namespace std;
// 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;
CMapPoolLow &GetMapPool()
{
// this may need to be ifdefed to be different for different modules
static CMapPoolLow thePool;
return thePool;
}
#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]));
}
};
CMapPoolLow::CMapPoolLow()
{
mLastBlockNum=-1;
}
CMapPoolLow::~CMapPoolLow()
{
#if _DEBUG
#if _GAME
if(mFreeList.size()<mMapBlocks.size()*MAPBLOCK_SIZE_NODES)
{
// Com_Printf("[MEM][GAME] !!!! Map Pool Leaked %d nodes\n",(MAPBLOCK_SIZE_NODES*mMapBlocks.size())-mFreeList.size());
}
// Com_Printf("[MEM][GAME] Map Pool max. mem used = %d\n",mMapBlocks.size()*MAPBLOCK_SIZE_NODES*MAP_NODE_SIZE);
#elif _CGAME
if (mFreeList.size()<mMapBlocks.size()*MAPBLOCK_SIZE_NODES)
{
// Com_Printf("[MEM][CGAME] !!!! Map Pool Leaked %d nodes\n",(MAPBLOCK_SIZE_NODES*mMapBlocks.size())-mFreeList.size());
}
// Com_Printf("[MEM][CGAME] Map Pool max. mem used = %d\n",mMapBlocks.size()*MAPBLOCK_SIZE_NODES*MAP_NODE_SIZE);
#else
if (mFreeList.size()<mMapBlocks.size()*MAPBLOCK_SIZE_NODES)
{
// Com_Printf("[MEM][EXE] !!!! Map Pool Leaked %d nodes\n",(MAPBLOCK_SIZE_NODES*mMapBlocks.size())-mFreeList.size());
}
// Com_Printf("[MEM][EXE] Map Pool max. mem used = %d\n",mMapBlocks.size()*MAPBLOCK_SIZE_NODES*MAP_NODE_SIZE);
#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
}
////////////
// hString stuff
////////////
#define MAX_HASH (65536*2)
// Max number of strings we can ever deal with.
#define MAX_STRINGS 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_STRINGS];
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_STRINGS*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_STRINGS.
assert(mNextStringId<MAX_STRINGS);
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()
{
#if 0
int i;
for (i=1;i<ThePool().mNextStringId;i++)
{
OutputDebugString(gCharPtrs[i]);
OutputDebugString("\n");
}
#endif
#if _DEBUG
#if _GAME
// Com_Printf("[MEM][GAME] String Pool %d unique strings, %dK\n",ThePool().mNextStringId,(ThePool().mLastBlockNum+1)*BLOCK_SIZE/1024);
#elif _CGAME
// Com_Printf("[MEM][CGAME] String Pool %d unique strings, %dK\n",ThePool().mNextStringId,(ThePool().mLastBlockNum+1)*BLOCK_SIZE/1024);
#else
// Com_Printf("[MEM][EXE] String Pool %d unique strings, %dK\n",ThePool().mNextStringId,(ThePool().mLastBlockNum+1)*BLOCK_SIZE/1024);
#endif
#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.
//
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]);
}
#endif // _DONETPROFILE_

229
codemp/qcommon/hstring.h Normal file
View File

@@ -0,0 +1,229 @@
#ifdef _DONETPROFILE_
#if !defined hString_H
#define hString_H
#include <map>
#include <list>
#include <set>
#include <vector>
#include <string>
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 0xfffffff;
}
// 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> >{};
// General purpose allocator/deallocator.
template <class X> X *XAlloc(void)
{
assert(sizeof(X)<=(MAP_NODE_SIZE-2));
return((X *)GetMapPool().Alloc());
}
template<class X> void XFree(X *x)
{
GetMapPool().Free((void *)x);
}
#endif // hString_H
#endif // _DONETPROFILE_

541
codemp/qcommon/huffman.cpp Normal file
View File

@@ -0,0 +1,541 @@
/* This is based on the Adaptive Huffman algorithm described in Sayood's Data
* Compression book. The ranks are not actually stored, but implicitly defined
* by the location of a node within a doubly-linked list */
//Anything above this #include will be ignored by the compiler
#include "../qcommon/exe_headers.h"
static int bloc = 0;
void Huff_putBit( int bit, byte *fout, int *offset) {
bloc = *offset;
if ((bloc&7) == 0) {
fout[(bloc>>3)] = 0;
}
fout[(bloc>>3)] |= bit << (bloc&7);
bloc++;
*offset = bloc;
}
int Huff_getBit( byte *fin, int *offset) {
int t;
bloc = *offset;
t = (fin[(bloc>>3)] >> (bloc&7)) & 0x1;
bloc++;
*offset = bloc;
return t;
}
/* Add a bit to the output file (buffered) */
static void add_bit (char bit, byte *fout) {
if ((bloc&7) == 0) {
fout[(bloc>>3)] = 0;
}
fout[(bloc>>3)] |= bit << (bloc&7);
bloc++;
}
/* Receive one bit from the input file (buffered) */
static int get_bit (byte *fin) {
int t;
t = (fin[(bloc>>3)] >> (bloc&7)) & 0x1;
bloc++;
return t;
}
static node_t **get_ppnode(huff_t* huff) {
node_t **tppnode;
if (!huff->freelist) {
return &(huff->nodePtrs[huff->blocPtrs++]);
} else {
tppnode = huff->freelist;
huff->freelist = (node_t **)*tppnode;
return tppnode;
}
}
static void free_ppnode(huff_t* huff, node_t **ppnode) {
*ppnode = (node_t *)huff->freelist;
huff->freelist = ppnode;
}
/* Swap the location of these two nodes in the tree */
static void swap (huff_t* huff, node_t *node1, node_t *node2) {
node_t *par1, *par2;
par1 = node1->parent;
par2 = node2->parent;
if (par1) {
if (par1->left == node1) {
par1->left = node2;
} else {
par1->right = node2;
}
} else {
huff->tree = node2;
}
if (par2) {
if (par2->left == node2) {
par2->left = node1;
} else {
par2->right = node1;
}
} else {
huff->tree = node1;
}
node1->parent = par2;
node2->parent = par1;
}
/* Swap these two nodes in the linked list (update ranks) */
static void swaplist(node_t *node1, node_t *node2) {
node_t *par1;
par1 = node1->next;
node1->next = node2->next;
node2->next = par1;
par1 = node1->prev;
node1->prev = node2->prev;
node2->prev = par1;
if (node1->next == node1) {
node1->next = node2;
}
if (node2->next == node2) {
node2->next = node1;
}
if (node1->next) {
node1->next->prev = node1;
}
if (node2->next) {
node2->next->prev = node2;
}
if (node1->prev) {
node1->prev->next = node1;
}
if (node2->prev) {
node2->prev->next = node2;
}
}
/* Do the increments */
static void increment(huff_t* huff, node_t *node) {
node_t *lnode;
if (!node) {
return;
}
if (node->next != NULL && node->next->weight == node->weight) {
lnode = *node->head;
if (lnode != node->parent) {
swap(huff, lnode, node);
}
swaplist(lnode, node);
}
if (node->prev && node->prev->weight == node->weight) {
*node->head = node->prev;
} else {
*node->head = NULL;
free_ppnode(huff, node->head);
}
node->weight++;
if (node->next && node->next->weight == node->weight) {
node->head = node->next->head;
} else {
node->head = get_ppnode(huff);
*node->head = node;
}
if (node->parent) {
increment(huff, node->parent);
if (node->prev == node->parent) {
swaplist(node, node->parent);
if (*node->head == node) {
*node->head = node->parent;
}
}
}
}
void Huff_addRef(huff_t* huff, byte ch) {
node_t *tnode, *tnode2;
if (huff->loc[ch] == NULL) { /* if this is the first transmission of this node */
tnode = &(huff->nodeList[huff->blocNode++]);
tnode2 = &(huff->nodeList[huff->blocNode++]);
tnode2->symbol = INTERNAL_NODE;
tnode2->weight = 1;
tnode2->next = huff->lhead->next;
if (huff->lhead->next) {
huff->lhead->next->prev = tnode2;
if (huff->lhead->next->weight == 1) {
tnode2->head = huff->lhead->next->head;
} else {
tnode2->head = get_ppnode(huff);
*tnode2->head = tnode2;
}
} else {
tnode2->head = get_ppnode(huff);
*tnode2->head = tnode2;
}
huff->lhead->next = tnode2;
tnode2->prev = huff->lhead;
tnode->symbol = ch;
tnode->weight = 1;
tnode->next = huff->lhead->next;
if (huff->lhead->next) {
huff->lhead->next->prev = tnode;
if (huff->lhead->next->weight == 1) {
tnode->head = huff->lhead->next->head;
} else {
/* this should never happen */
tnode->head = get_ppnode(huff);
*tnode->head = tnode2;
}
} else {
/* this should never happen */
tnode->head = get_ppnode(huff);
*tnode->head = tnode;
}
huff->lhead->next = tnode;
tnode->prev = huff->lhead;
tnode->left = tnode->right = NULL;
if (huff->lhead->parent) {
if (huff->lhead->parent->left == huff->lhead) { /* lhead is guaranteed to by the NYT */
huff->lhead->parent->left = tnode2;
} else {
huff->lhead->parent->right = tnode2;
}
} else {
huff->tree = tnode2;
}
tnode2->right = tnode;
tnode2->left = huff->lhead;
tnode2->parent = huff->lhead->parent;
huff->lhead->parent = tnode->parent = tnode2;
huff->loc[ch] = tnode;
increment(huff, tnode2->parent);
} else {
increment(huff, huff->loc[ch]);
}
}
/* Get a symbol */
int Huff_Receive (node_t *node, int *ch, byte *fin) {
while (node && node->symbol == INTERNAL_NODE) {
if (get_bit(fin)) {
node = node->right;
} else {
node = node->left;
}
}
if (!node) {
return 0;
// Com_Error(ERR_DROP, "Illegal tree!\n");
}
return (*ch = node->symbol);
}
/* Get a symbol */
void Huff_offsetReceive (node_t *node, int *ch, byte *fin, int *offset) {
bloc = *offset;
while (node && node->symbol == INTERNAL_NODE) {
if (get_bit(fin)) {
node = node->right;
} else {
node = node->left;
}
}
if (!node) {
*ch = 0;
return;
// Com_Error(ERR_DROP, "Illegal tree!\n");
}
*ch = node->symbol;
*offset = bloc;
}
/* Send the prefix code for this node */
static void send(node_t *node, node_t *child, byte *fout) {
if (node->parent) {
send(node->parent, node, fout);
}
if (child) {
if (node->right == child) {
add_bit(1, fout);
} else {
add_bit(0, fout);
}
}
}
/* Send a symbol */
void Huff_transmit (huff_t *huff, int ch, byte *fout) {
int i;
if (huff->loc[ch] == NULL) {
/* node_t hasn't been transmitted, send a NYT, then the symbol */
Huff_transmit(huff, NYT, fout);
for (i = 7; i >= 0; i--) {
add_bit((char)((ch >> i) & 0x1), fout);
}
} else {
send(huff->loc[ch], NULL, fout);
}
}
void Huff_offsetTransmit (huff_t *huff, int ch, byte *fout, int *offset) {
bloc = *offset;
send(huff->loc[ch], NULL, fout);
*offset = bloc;
}
void Huff_Decompress(msg_t *mbuf, int offset) {
int ch, cch, i, j, size;
byte seq[65536];
byte* buffer;
huff_t huff;
size = mbuf->cursize - offset;
buffer = mbuf->data + offset;
if ( size <= 0 ) {
return;
}
Com_Memset(&huff, 0, sizeof(huff_t));
// Initialize the tree & list with the NYT node
huff.tree = huff.lhead = huff.ltail = huff.loc[NYT] = &(huff.nodeList[huff.blocNode++]);
huff.tree->symbol = NYT;
huff.tree->weight = 0;
huff.lhead->next = huff.lhead->prev = NULL;
huff.tree->parent = huff.tree->left = huff.tree->right = NULL;
cch = buffer[0]*256 + buffer[1];
// don't overflow with bad messages
if ( cch > mbuf->maxsize - offset ) {
cch = mbuf->maxsize - offset;
}
bloc = 16;
for ( j = 0; j < cch; j++ ) {
ch = 0;
// don't overflow reading from the messages
// FIXME: would it be better to have a overflow check in get_bit ?
if ( (bloc >> 3) > size ) {
seq[j] = 0;
break;
}
Huff_Receive(huff.tree, &ch, buffer); /* Get a character */
if ( ch == NYT ) { /* We got a NYT, get the symbol associated with it */
ch = 0;
for ( i = 0; i < 8; i++ ) {
ch = (ch<<1) + get_bit(buffer);
}
}
seq[j] = ch; /* Write symbol */
Huff_addRef(&huff, (byte)ch); /* Increment node */
}
mbuf->cursize = cch + offset;
Com_Memcpy(mbuf->data + offset, seq, cch);
}
extern int oldsize;
void Huff_Compress(msg_t *mbuf, int offset) {
int i, ch, size;
byte seq[65536];
byte* buffer;
huff_t huff;
size = mbuf->cursize - offset;
buffer = mbuf->data+ + offset;
if (size<=0) {
return;
}
Com_Memset(&huff, 0, sizeof(huff_t));
// Add the NYT (not yet transmitted) node into the tree/list */
huff.tree = huff.lhead = huff.loc[NYT] = &(huff.nodeList[huff.blocNode++]);
huff.tree->symbol = NYT;
huff.tree->weight = 0;
huff.lhead->next = huff.lhead->prev = NULL;
huff.tree->parent = huff.tree->left = huff.tree->right = NULL;
huff.loc[NYT] = huff.tree;
seq[0] = (size>>8);
seq[1] = size&0xff;
bloc = 16;
for (i=0; i<size; i++ ) {
ch = buffer[i];
Huff_transmit(&huff, ch, seq); /* Transmit symbol */
Huff_addRef(&huff, (byte)ch); /* Do update */
}
bloc += 8; // next byte
mbuf->cursize = (bloc>>3) + offset;
Com_Memcpy(mbuf->data+offset, seq, (bloc>>3));
}
void Huff_Init(huffman_t *huff) {
Com_Memset(&huff->compressor, 0, sizeof(huff_t));
Com_Memset(&huff->decompressor, 0, sizeof(huff_t));
// Initialize the tree & list with the NYT node
huff->decompressor.tree = huff->decompressor.lhead = huff->decompressor.ltail = huff->decompressor.loc[NYT] = &(huff->decompressor.nodeList[huff->decompressor.blocNode++]);
huff->decompressor.tree->symbol = NYT;
huff->decompressor.tree->weight = 0;
huff->decompressor.lhead->next = huff->decompressor.lhead->prev = NULL;
huff->decompressor.tree->parent = huff->decompressor.tree->left = huff->decompressor.tree->right = NULL;
// Add the NYT (not yet transmitted) node into the tree/list */
huff->compressor.tree = huff->compressor.lhead = huff->compressor.loc[NYT] = &(huff->compressor.nodeList[huff->compressor.blocNode++]);
huff->compressor.tree->symbol = NYT;
huff->compressor.tree->weight = 0;
huff->compressor.lhead->next = huff->compressor.lhead->prev = NULL;
huff->compressor.tree->parent = huff->compressor.tree->left = huff->compressor.tree->right = NULL;
huff->compressor.loc[NYT] = huff->compressor.tree;
}
#ifdef _XBOX
// This huffman code is horrendously slow.
// Let's see if we can make it suck slightly less.
void Huff_LoadPointer( huff_t *huff, void **ptr )
{
*ptr = (*ptr) ? (void *)(((unsigned long)(*ptr) + (char *)huff) - 1) : 0;
}
void Huff_DecodeHuff( huff_t *huff )
{
// This just does pointer fixup for all pointers within the structure:
// Do all the nodes:
for( int i = 0; i < 768; ++i )
{
node_t *node = &huff->nodeList[i];
Huff_LoadPointer( huff, (void **)&node->left );
Huff_LoadPointer( huff, (void **)&node->right );
Huff_LoadPointer( huff, (void **)&node->parent );
Huff_LoadPointer( huff, (void **)&node->next );
Huff_LoadPointer( huff, (void **)&node->prev );
Huff_LoadPointer( huff, (void **)&node->head );
}
// Do all the pointers in the top level structure:
Huff_LoadPointer( huff, (void **)&huff->tree );
Huff_LoadPointer( huff, (void **)&huff->lhead );
Huff_LoadPointer( huff, (void **)&huff->ltail );
Huff_LoadPointer( huff, (void **)&huff->freelist );
// The node pointers in the loc array:
for( i = 0; i < HMAX+1; ++i )
Huff_LoadPointer( huff, (void **)&huff->loc[i] );
// The node pointers in the nodePtrs array
for( i = 0; i < 768; ++i )
Huff_LoadPointer( huff, (void **)&huff->nodePtrs[i] );
}
bool Huff_Load( huffman_t *huff )
{
FILE *in = fopen( "d:\\huff_table", "rb" );
if( !in )
return false;
if( !fread( &huff->compressor, sizeof(huff->compressor), 1, in ) ||
!fread( &huff->decompressor, sizeof(huff->decompressor), 1, in ) )
{
fclose( in );
return false;
}
Huff_DecodeHuff( &huff->compressor );
Huff_DecodeHuff( &huff->decompressor );
fclose( in );
return true;
}
#ifndef FINAL_BUILD // Saving of encoded tables not supported once we're on DVD
void Huff_SavePointer( huff_t *huff, void **ptr )
{
*ptr = (*ptr) ? (void *)(((char *)(*ptr) - (char *)huff) + 1) : 0;
}
void Huff_EncodeHuff( huff_t *huff )
{
// This just does pointer encoding for all pointers within the structure:
// Fix all pointers inside nodes:
for( int i = 0; i < 768; ++i )
{
node_t *node = &huff->nodeList[i];
Huff_SavePointer( huff, (void **)&node->left );
Huff_SavePointer( huff, (void **)&node->right );
Huff_SavePointer( huff, (void **)&node->parent );
Huff_SavePointer( huff, (void **)&node->next );
Huff_SavePointer( huff, (void **)&node->prev );
Huff_SavePointer( huff, (void **)&node->head );
}
// Do all the pointers in the top level structure:
Huff_SavePointer( huff, (void **)&huff->tree );
Huff_SavePointer( huff, (void **)&huff->lhead );
Huff_SavePointer( huff, (void **)&huff->ltail );
Huff_SavePointer( huff, (void **)&huff->freelist );
// The node pointers in the loc array:
for( int i = 0; i < HMAX+1; ++i )
Huff_SavePointer( huff, (void **)&huff->loc[i] );
// The node pointers in the nodePtrs array
for( int i = 0; i < 768; ++i )
Huff_SavePointer( huff, (void **)&huff->nodePtrs[i] );
}
void Huff_Save( huffman_t *huff )
{
FILE *out = fopen( "d:\\huff_table", "wb" );
if( !out )
return;
// Convert all pointers
Huff_EncodeHuff( &huff->compressor );
Huff_EncodeHuff( &huff->decompressor );
// Write out the structures
fwrite( &huff->compressor, sizeof(huff->compressor), 1, out );
fwrite( &huff->decompressor, sizeof(huff->decompressor), 1, out );
// Undo pointer conversion
Huff_DecodeHuff( &huff->compressor );
Huff_DecodeHuff( &huff->decompressor );
fclose( out );
return;
}
#endif // FINAL_BUILD
#endif // _XBOX

View File

@@ -0,0 +1,20 @@
#ifdef _DONETPROFILE_
#define _INETPROFILE_H_
#ifdef _INETPROFILE_H_
class INetProfile
{
public:
virtual void Reset(void)=0;
virtual void AddField(char *fieldName,int sizeBytes)=0;
virtual void IncTime(int msec)=0;
virtual void ShowTotals(void)=0;
};
INetProfile &ClReadProf(void);
INetProfile &ClSendProf(void);
#endif // _INETPROFILE_H_
#endif // _DONETPROFILE_

296
codemp/qcommon/md4.cpp Normal file
View File

@@ -0,0 +1,296 @@
/* GLOBAL.H - RSAREF types and constants */
//Anything above this #include will be ignored by the compiler
#include "../qcommon/exe_headers.h"
#include <string.h>
#if defined(_WIN32)
#pragma warning(disable : 4711) // selected for automatic inline expansion
#endif
/* 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 *);
void Com_Memset (void* dest, const int val, const size_t count);
void Com_Memcpy (void* dest, const void* src, const size_t count);
/* 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)
{
Com_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 */
Com_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.*/
Com_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.*/
Com_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 (const void *buffer, int length)
{
int digest[4];
unsigned val;
MD4_CTX ctx;
MD4Init (&ctx);
MD4Update (&ctx, (unsigned char *)buffer, length);
MD4Final ( (unsigned char *)digest, &ctx);
val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3];
return val;
}
unsigned Com_BlockChecksumKey (void *buffer, int length, int key)
{
int digest[4];
unsigned val;
MD4_CTX ctx;
MD4Init (&ctx);
MD4Update (&ctx, (unsigned char *)&key, 4);
MD4Update (&ctx, (unsigned char *)buffer, length);
MD4Final ( (unsigned char *)digest, &ctx);
val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3];
return val;
}

57
codemp/qcommon/miniheap.h Normal file
View File

@@ -0,0 +1,57 @@
#if !defined(MINIHEAP_H_INC)
#define MINIHEAP_H_INC
class CMiniHeap
{
private:
char *mHeap;
char *mCurrentHeap;
int mSize;
public:
// reset the heap back to the start
void ResetHeap()
{
mCurrentHeap = mHeap;
}
// initialise the heap
CMiniHeap(int size)
{
mHeap = (char *)malloc(size);
mSize = size;
if (mHeap)
{
ResetHeap();
}
}
// free up the heap
~CMiniHeap()
{
if (mHeap)
{
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;
extern CMiniHeap *G2VertSpaceClient;
#endif //MINIHEAP_H_INC

3097
codemp/qcommon/msg.cpp Normal file

File diff suppressed because it is too large Load Diff

703
codemp/qcommon/net_chan.cpp Normal file
View File

@@ -0,0 +1,703 @@
//Anything above this #include will be ignored by the compiler
#include "../qcommon/exe_headers.h"
#ifdef _XBOX
#include "../cgame/cg_local.h"
#include "../client/cl_data.h"
#endif
/*
packet header
-------------
4 outgoing sequence. high bit will be set if this is a fragmented message
[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.
*/
#ifdef _XBOX
#define MAX_PACKETLEN 1359 // UDP total packet size
#define FRAGMENT_SIZE (MAX_PACKETLEN - 55 - 10) // 55 is packet overhead ||| 10 is fudge factor - needed due to huffman?
#else
#define MAX_PACKETLEN 1400 // max size of a network packet
#define FRAGMENT_SIZE (MAX_PACKETLEN - 100)
#define PACKET_HEADER 10 // two ints and a short
#endif
#define FRAGMENT_BIT (1<<31)
cvar_t *showpackets;
cvar_t *showdrop;
cvar_t *qport;
cvar_t *net_killdroppedfragments;
static char *netsrcString[2] = {
"client",
"server"
};
/*
===============
Netchan_Init
===============
*/
void Netchan_Init( int port ) {
#ifdef _XBOX
CM_START_LOOP();
if (!ClientManager::ActiveClient().loopbacks)
{
Z_PushNewDeleteTag( TAG_CLIENT_MANAGER );
ClientManager::ActiveClient().loopbacks = new loopback_t[2];
memset(ClientManager::ActiveClient().loopbacks, 0, sizeof(loopback_t) * 2);
Z_PopNewDeleteTag();
}
CM_END_LOOP();
#endif
port &= 0xffff;
showpackets = Cvar_Get ("showpackets", "0", CVAR_TEMP );
showdrop = Cvar_Get ("showdrop", "0", CVAR_TEMP );
qport = Cvar_Get ("net_qport", va("%i", port), CVAR_INIT );
net_killdroppedfragments = Cvar_Get ("net_killdroppedfragments", "0", CVAR_TEMP);
}
/*
==============
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 ) {
Com_Memset (chan, 0, sizeof(*chan));
chan->sock = sock;
chan->remoteAddress = adr;
chan->qport = qport;
chan->incomingSequence = 0;
chan->outgoingSequence = 1;
}
/*
=================
Netchan_TransmitNextFragment
Send one fragment of the current message
=================
*/
bool Netchan_TransmitNextFragment( netchan_t *chan ) {
msg_t send;
byte send_buf[MAX_PACKETLEN];
int fragmentLength;
// write the packet header
MSG_InitOOB (&send, send_buf, sizeof(send_buf)); // <-- only do the oob here
MSG_WriteLong( &send, chan->outgoingSequence | FRAGMENT_BIT );
// send the qport if we are a client
if ( chan->sock == NS_CLIENT ) {
if(ClientManager::splitScreenMode == qtrue)
MSG_WriteShort( &send, ClientManager::ActivePort() );
else
MSG_WriteShort( &send, qport->integer );
}
// copy the reliable message to the packet first
fragmentLength = FRAGMENT_SIZE;
if ( chan->unsentFragmentStart + fragmentLength > chan->unsentLength ) {
fragmentLength = chan->unsentLength - chan->unsentFragmentStart;
}
MSG_WriteShort( &send, chan->unsentFragmentStart );
MSG_WriteShort( &send, fragmentLength );
MSG_WriteData( &send, chan->unsentBuffer + chan->unsentFragmentStart, fragmentLength );
// send the datagram
bool retVal = NET_SendPacket( chan->sock, send.cursize, send.data, chan->remoteAddress );
chan->unsentFragmentStart += 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
if ( chan->unsentFragmentStart == chan->unsentLength && fragmentLength != FRAGMENT_SIZE ) {
chan->outgoingSequence++;
chan->unsentFragments = qfalse;
}
return retVal;
}
/*
===============
Netchan_Transmit
Sends a message to a connection, fragmenting if necessary
A 0 length will still generate a packet.
================
*/
bool Netchan_Transmit( netchan_t *chan, int length, const byte *data ) {
msg_t send;
byte send_buf[MAX_PACKETLEN];
if ( length > MAX_MSGLEN ) {
Com_Error( ERR_DROP, "Netchan_Transmit: length = %i", length );
}
chan->unsentFragmentStart = 0;
if (chan->unsentFragments)
{
Com_Printf("[ISM] Stomping Unsent Fragments %s\n",netsrcString[ chan->sock ]);
}
// fragment large reliable messages
if ( length >= FRAGMENT_SIZE )
{
chan->unsentFragments = qtrue;
chan->unsentLength = length;
Com_Memcpy( chan->unsentBuffer, data, length );
// only send the first fragment now
return Netchan_TransmitNextFragment( chan );
}
// write the packet header
MSG_InitOOB (&send, send_buf, sizeof(send_buf));
MSG_WriteLong( &send, chan->outgoingSequence );
chan->outgoingSequence++;
// send the qport if we are a client
if ( chan->sock == NS_CLIENT ) {
#ifdef _XBOX
if(ClientManager::splitScreenMode == qtrue)
{
MSG_WriteShort( &send, ClientManager::ActivePort() );
}
else
#endif
MSG_WriteShort( &send, qport->integer );
}
MSG_WriteData( &send, data, length );
// send the datagram
return 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;
int qport;
int fragmentStart, fragmentLength;
qboolean fragmented;
// get sequence numbers
MSG_BeginReadingOOB( msg );
sequence = 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 = (unsigned short)MSG_ReadShort( msg );
fragmentLength = (unsigned short)MSG_ReadShort( msg );
} else {
fragmentStart = 0; // stop warning message
fragmentLength = 0;
}
if ( showpackets->integer ) {
if ( fragmented ) {
Com_Printf( "%s recv %4i : s=%i fragment=%i,%i\n"
, netsrcString[ chan->sock ]
, msg->cursize
, sequence
, fragmentStart, fragmentLength );
} else {
Com_Printf( "%s recv %4i : s=%i\n"
, netsrcString[ chan->sock ]
, msg->cursize
, sequence );
}
}
//
// 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;
}
Com_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+4 > msg->maxsize ) {
Com_Printf( "%s:fragmentLength %i > msg->maxsize\n"
, NET_AdrToString (chan->remoteAddress ),
chan->fragmentLength+4 );
return qfalse;
}
// copy the full message over the partial fragment
// make sure the sequence number is still there
*(int *)msg->data = LittleLong( sequence );
Com_Memcpy( msg->data + 4, chan->fragmentBuffer, chan->fragmentLength );
msg->cursize = chan->fragmentLength + 4;
chan->fragmentLength = 0;
msg->readcount = 4; // past the sequence number
msg->bit = 32; // past the sequence number
// but I am a wuss -mw
// chan->incomingSequence = sequence; // lets not accept any more with this sequence number -gil
return qtrue;
}
//
// the message can now be read from the current message pointer
//
chan->incomingSequence = sequence;
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;
if (a.type == NA_IP)
{
if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3])
return qtrue;
return qfalse;
}
#ifndef _XBOX // No IPX
if (a.type == NA_IPX)
{
if ((memcmp(a.ipx, b.ipx, 10) == 0))
return qtrue;
return qfalse;
}
#endif
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");
} else if (a.type == NA_BOT) {
Com_sprintf (s, sizeof(s), "bot");
} else if (a.type == NA_IP) {
Com_sprintf (s, sizeof(s), "%i.%i.%i.%i:%i",
a.ip[0], a.ip[1], a.ip[2], a.ip[3], BigShort(a.port));
} else if (a.type == NA_BAD) {
Com_sprintf (s, sizeof(s), "BAD");
} else {
Com_sprintf (s, sizeof(s), "%02x%02x%02x%02x.%02x%02x%02x%02x%02x%02x:%i",
a.ipx[0], a.ipx[1], a.ipx[2], a.ipx[3], a.ipx[4], a.ipx[5], a.ipx[6], a.ipx[7], a.ipx[8], a.ipx[9],
BigShort(a.port));
}
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;
if (a.type == NA_IP)
{
if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3] && a.port == b.port)
return qtrue;
return qfalse;
}
#ifndef _XBOX // No IPX
if (a.type == NA_IPX)
{
if ((memcmp(a.ipx, b.ipx, 10) == 0) && a.port == b.port)
return qtrue;
return qfalse;
}
#endif
Com_Printf ("NET_CompareAdr: bad address type\n");
return qfalse;
}
qboolean NET_IsLocalAddress( netadr_t adr ) {
return (qboolean)(adr.type == NA_LOOPBACK);
}
/*
=============================================================================
LOOPBACK BUFFERS FOR LOCAL PLAYER
=============================================================================
*/
// there needs to be enough loopback messages to hold a complete
// gamestate of maximum size
//#define MAX_LOOPBACK 16
//typedef struct {
// byte data[MAX_PACKETLEN];
// int datalen;
//} loopmsg_t;
//typedef struct {
// loopmsg_t msgs[MAX_LOOPBACK];
// int get, send;
//} loopback_t;
//loopback_t loopbacks[2];
qboolean NET_GetLoopPacket (netsrc_t sock, netadr_t *net_from, msg_t *net_message)
{
int i;
loopback_t *loop;
loop = (loopback_t*)&ClientManager::ActiveClient().loopbacks[sock];
if (loop->send - loop->get > MAX_LOOPBACK)
loop->get = loop->send - MAX_LOOPBACK;
if (loop->get >= loop->send)
return qfalse;
i = loop->get & (MAX_LOOPBACK-1);
loop->get++;
Com_Memcpy (net_message->data, loop->msgs[i].data, loop->msgs[i].datalen);
net_message->cursize = loop->msgs[i].datalen;
Com_Memset (net_from, 0, sizeof(*net_from));
net_from->type = NA_LOOPBACK;
#ifdef _XBOX
if(ClientManager::splitScreenMode == qtrue)
{
if (sock == NS_CLIENT)
net_from->port = 0;// // server is on port 0;
else if (sock == NS_SERVER)
net_from->port = ClientManager::ActiveClientNum();
else
assert(0 && "what happened????");
}
#endif
return qtrue;
}
void NET_SendLoopPacket (netsrc_t sock, int length, const void *data, netadr_t to)
{
int i;
loopback_t *loop;
loop = (loopback_t*)&ClientManager::ActiveClient().loopbacks[sock^1];
i = loop->send & (MAX_LOOPBACK-1);
loop->send++;
Com_Memcpy (loop->msgs[i].data, data, length);
loop->msgs[i].datalen = length;
}
//=============================================================================
bool 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 ) {
#ifdef _XBOX
if(ClientManager::splitScreenMode == qtrue)
to.port = ClientManager::ActiveClientNum();
#endif
NET_SendLoopPacket (sock, length, data, to);
return true;
}
if ( to.type == NA_BOT ) {
return true;
}
if ( to.type == NA_BAD ) {
return false;
}
return Sys_SendPacket( length, data, to );
}
/*
===============
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] = -1;
string[1] = -1;
string[2] = -1;
string[3] = -1;
va_start( argptr, format );
vsprintf( string+4, format, argptr );
va_end( argptr );
// send the datagram
NET_SendPacket( sock, strlen( string ), string, adr );
}
// Used for packets that need to be broadcast. The only such
// packets are "infoResponse"
void QDECL NET_BroadcastPrint( netsrc_t sock, const char *format, ... )
{
va_list argptr;
char string[MAX_MSGLEN];
// set the header
string[0] = -1;
string[1] = -1;
string[2] = -1;
string[3] = -1;
va_start( argptr, format );
vsprintf( string+4, format, argptr );
va_end( argptr );
// send the datagram
Sys_SendBroadcastPacket( strlen( string ), string );
}
/*
===============
NET_OutOfBandPrint
Sends a data message in an out-of-band datagram (only used for "connect")
================
*/
bool QDECL NET_OutOfBandData( netsrc_t sock, netadr_t adr, byte *format, int len ) {
byte string[MAX_MSGLEN+4];
int i;
msg_t mbuf;
// set the header
string[0] = 0xff;
string[1] = 0xff;
string[2] = 0xff;
string[3] = 0xff;
for(i=0;i<len;i++) {
string[i+4] = format[i];
}
mbuf.data = string;
mbuf.cursize = len+4;
Huff_Compress( &mbuf, 12);
// send the datagram
return NET_SendPacket( sock, mbuf.cursize, mbuf.data, adr );
}
/*
=============
NET_StringToAdr
Traps "localhost" for loopback, passes everything else to system
=============
*/
qboolean NET_StringToAdr( const char *s, netadr_t *a ) {
qboolean r;
char base[MAX_STRING_CHARS];
char *port;
if (!strcmp (s, "localhost")) {
Com_Memset (a, 0, sizeof(*a));
a->type = NA_LOOPBACK;
return qtrue;
}
// look for a port number
Q_strncpyz( base, s, sizeof( base ) );
port = strstr( base, ":" );
if ( port ) {
*port = 0;
port++;
}
r = Sys_StringToAdr( base, a );
if ( !r ) {
a->type = NA_BAD;
return qfalse;
}
// inet_addr returns this if out of range
if ( a->ip[0] == 255 && a->ip[1] == 255 && a->ip[2] == 255 && a->ip[3] == 255 ) {
a->type = NA_BAD;
return qfalse;
}
if ( port ) {
a->port = BigShort( (short)atoi( port ) );
} else {
a->port = BigShort( PORT_SERVER );
}
return qtrue;
}

22
codemp/qcommon/platform.h Normal file
View File

@@ -0,0 +1,22 @@
// Simple header file to dispatch to the relevant platform API headers
#ifndef _PLATFORM_H
#define _PLATFORM_H
#if defined(_XBOX)
#include <xtl.h>
#endif
#if defined(_WINDOWS)
#include <windows.h>
#endif
#if defined (__linux__)
typedef const char *LPCTSTR;
typedef const char *LPCSTR;
typedef unsigned long DWORD;
typedef unsigned int UINT;
typedef void* HANDLE;
typedef DWORD COLORREF;
typedef unsigned char BYTE;
#endif
#endif

View File

@@ -0,0 +1,4 @@
//Anything above this #include will be ignored by the compiler
#include "../qcommon/exe_headers.h"
#include "../game/q_math.c"

View File

@@ -0,0 +1,4 @@
//Anything above this #include will be ignored by the compiler
#include "../qcommon/exe_headers.h"
#include "../game/q_shared.c"

1158
codemp/qcommon/qcommon.h Normal file

File diff suppressed because it is too large Load Diff

607
codemp/qcommon/qfiles.h Normal file
View File

@@ -0,0 +1,607 @@
#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 relative 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
==============================================================================
*/
// little-endian "RBSP"
#define BSP_IDENT (('P'<<24)+('S'<<16)+('B'<<8)+'R')
#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 0x600000
#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_LSNONE 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. OK, how about 5.11? Ick, 7.9
//#define DRAWVERT_ST_SCALE 4096.0f
//#define DRAWVERT_ST_SCALE 2048.0f
#define DRAWVERT_ST_SCALE 512.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
typedef struct {
vec3_t xyz;
short dvst[2];
short dvlightmap[MAXLIGHTMAPS][2];
vec3_t normal;
#ifdef _XBOX
vec3_t tangent;
#endif
byte dvcolor[MAXLIGHTMAPS][2];
} 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 {
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_LSNONE 0xff //rww - changed name because it unhappily conflicts with a lightsaber state name and changing this is just easier
#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
/////////////////////////////////////////////////////////////
//
// 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;
} dfontdat_t;
/////////////////// fonts end ////////////////////////////////////
#endif

File diff suppressed because it is too large Load Diff

185
codemp/qcommon/roffsystem.h Normal file
View File

@@ -0,0 +1,185 @@
#if defined (_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif
#if !defined(CROFFSYSTEM_H_INC)
#define CROFFSYSTEM_H_INC
#ifndef __Q_SHARED_H
#include "../game/q_shared.h" //needs to be in here for entityState_t
#endif
#if !defined(SERVER_H_INC)
#include "../server/server.h"
#endif
#pragma warning (push, 3) //go back down to 3 for the stl include
#include <vector>
#include <map>
#pragma warning (pop)
using namespace std;
// ROFF Defines
//-------------------
#define ROFF_VERSION 1
#define ROFF_NEW_VERSION 2
#define ROFF_STRING "ROFF"
#define ROFF_SAMPLE_RATE 10 // 10hz
#define ROFF_AUTO_FIX_BAD_ANGLES // exporter can mess up angles,
// defining this attempts to detect and fix these problems
// The CROFFSystem object provides all of the functionality of ROFF
// caching, playback, and clean-up, plus some useful debug features.
//--------------------------------------
class CROFFSystem
//--------------------------------------
{
private:
//------
// forward declarations
class CROFF;
struct SROFFEntity;
typedef map <int, CROFF *> TROFFList;
typedef vector <SROFFEntity *> TROFFEntList;
TROFFList mROFFList; // List of cached roffs
int mID; // unique ID generator for new roff objects
TROFFEntList mROFFEntList; // List of roffing entities
// ROFF Header file definition, nothing else needs to see this
typedef struct tROFFHeader
//-------------------------------
{
char mHeader[4]; // should match roff_string defined above
long mVersion; // version num, supported version defined above
float mCount; // I think this is a float because of a limitation of the roff exporter
} TROFFHeader;
// ROFF Entry, nothing else needs to see this
typedef struct tROFFEntry
//-------------------------------
{
float mOriginOffset[3];
float mRotateOffset[3];
} TROFFEntry;
typedef struct tROFF2Header
//-------------------------------
{
char mHeader[4]; // should match roff_string defined above
long mVersion; // version num, supported version defined above
int mCount; // I think this is a float because of a limitation of the roff exporter
int mFrameRate; // Frame rate the roff should be played at
int mNumNotes; // number of notes (null terminated strings) after the roff data
} TROFF2Header;
// ROFF Entry, nothing else needs to see this
typedef struct tROFF2Entry
//-------------------------------
{
float mOriginOffset[3];
float mRotateOffset[3];
int mStartNote, mNumNotes; // note track info
} TROFF2Entry;
// An individual ROFF object,
// contains actual rotation/offset information
//--------------------------------------
class CROFF
//--------------------------------------
{
public:
//------
int mID; // id for this roff file
char mROFFFilePath[MAX_QPATH]; // roff file path
int mROFFEntries; // count of move/rotate commands
int mFrameTime; // frame rate
int mLerp; // Lerp rate (FPS)
TROFF2Entry *mMoveRotateList; // move rotate/command list
int mNumNoteTracks;
char **mNoteTrackIndexes;
qboolean mUsedByClient;
qboolean mUsedByServer;
CROFF()
{
mUsedByClient = mUsedByServer = qfalse;
}
CROFF( const char *file, int id );
~CROFF();
}; // class CROFF
// The roff system tracks entities that are
// roffing, so this is the internal structure
// that represents these objects.
//--------------------------------------
struct SROFFEntity
//--------------------------------------
{
int mEntID; // the entity that is currently roffing
int mROFFID; // the roff to be applied to that entity
int mNextROFFTime; // next time we should roff
int mROFFFrame; // current roff frame we are applying
qboolean mKill; // flag to kill a roffing ent
qboolean mSignal; // TODO: Need to implement some sort of signal to Icarus when roff is done.
qboolean mTranslated; // should this roff be "rotated" to fit the entity's initial position?
qboolean mIsClient;
vec3_t mStartAngles; // initial angle of the entity
}; // struct SROFFEntity
qboolean IsROFF( byte *file ); // Makes sure the file is a valid roff file
qboolean InitROFF( byte *file, CROFF *obj ); // Handles stashing raw roff data into the roff object
qboolean InitROFF2( byte *file, CROFF *obj ); // Handles stashing raw roff data into the roff object
void FixBadAngles(CROFF *obj);
int NewID() { return ++mID; } // Increment before return so we can use zero as failed return val
qboolean ApplyROFF( SROFFEntity *roff_ent,
CROFFSystem::CROFF *roff ); // True = success; False = roff complete
void ProcessNote(SROFFEntity *roff_ent, char *note);
void SetLerp( trajectory_t *tr,
trType_t, vec3_t origin,
vec3_t delta, int time, int rate );
qboolean ClearLerp( SROFFEntity *roff_ent ); // Clears out the angular and position lerp fields
public:
//------
CROFFSystem() { mID = 0; mROFFEntList.clear(); }
~CROFFSystem() { Restart(); }
qboolean Restart(); // Free up all system resources and reset the ID counter
int Cache( const char *file, qboolean isClient ); // roffs should be precached at the start of each level
int GetID( const char *file ); // find the roff id by filename
qboolean Unload( int id ); // when a roff is done, it can be removed to free up resources
qboolean Clean(qboolean isClient); // should be called when level is done, frees all roff resources
void List(void); // dumps a list of all cached roff files to the console
qboolean List( int id ); // dumps the contents of the specified roff to the console
qboolean Play( int entID, int roffID, qboolean doTranslation, qboolean isClient); // TODO: implement signal on playback completion.
void ListEnts(); // List the entities that are currently roffing
qboolean PurgeEnt( int entID, qboolean isClient ); // Purge the specified entity from the entity list by id
qboolean PurgeEnt( char *file ); // Purge the specified entity from the entity list by name
void UpdateEntities(qboolean isClient); // applys roff data to roffing entities.
}; // class CROFFSystem
extern CROFFSystem theROFFSystem;
#endif // CROFFSYSTEM_H_INC

725
codemp/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
codemp/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));
}
sstring(const char *s)
{
//assert(strlen(s)<MaxSize);
//strcpy(mStorage.data,s);
Q_strncpyz(mStorage.data,s,sizeof(mStorage.data));
}
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));
return *this;
}
sstring<MaxSize> & operator=(const char *s)
{
assert(strlen(s)<MaxSize);
//strcpy(mStorage.data,s);
Q_strncpyz(mStorage.data,s,sizeof(mStorage.data));
return *this;
}
char *c_str()
{
return mStorage.data;
}
const char *c_str() const
{
return mStorage.data;
}
int capacity() const
{
return MaxSize;
}
int length() const
{
return strlen(mStorage.data);
}
bool operator==(const sstring<MaxSize> &o) const
{
if (!Q_stricmp(mStorage.data,o.mStorage.data))
{
return true;
}
return false;
}
bool operator!=(const sstring<MaxSize> &o) const
{
if (Q_stricmp(mStorage.data,o.mStorage.data)!=0)
{
return true;
}
return false;
}
bool operator<(const sstring<MaxSize> &o) const
{
if (Q_stricmp(mStorage.data,o.mStorage.data)<0)
{
return true;
}
return false;
}
bool operator>(const sstring<MaxSize> &o) const
{
if (Q_stricmp(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 ///////////////////

1776
codemp/qcommon/strip.cpp Normal file

File diff suppressed because it is too large Load Diff

312
codemp/qcommon/strip.h Normal file
View File

@@ -0,0 +1,312 @@
#ifndef __STRIP_H
#define __STRIP_H
/*
#ifndef _SOF_
#pragma warning(disable:4786) // Or STL will generate ugly warnings.
#include <string>
using namespace std;
#endif
*/
#pragma warning (push, 3) //go back down to 3 for the stl include
#include <string>
#include <map>
#include <list>
#pragma warning (pop)
using namespace std;
#define STRIP_VERSION 1
#define MAX_LANGUAGES 10
#define MAX_STRINGS 256
#define MAX_ID 255
enum
{
SP_LANGUAGE_ENGLISH = 0,
SP_LANGUAGE_FRENCH,
SP_LANGUAGE_GERMAN,
SP_LANGUAGE_BRITISH,
SP_LANGUAGE_KOREAN,
SP_LANGUAGE_TAIWANESE,
SP_LANGUAGE_ITALIAN,
SP_LANGUAGE_SPANISH,
SP_LANGUAGE_JAPANESE,
SP_LANGUAGE_10,
SP_LANGUGAGE_MAX,
SP_LANGUAGE_ALL = 255
};
#define SP_PACKAGE 0xff00
#define SP_STRING 0x00ff
#define SP_GET_PACKAGE(x) ( (x & SP_PACKAGE) >> 8 )
// Flags
#define SP_FLAG1 0x00000001 // CENTERED
#define SP_FLAG2 0x00000002
#define SP_FLAG3 0x00000004
#define SP_FLAG4 0x00000008
#define SP_FLAG5 0x00000010
#define SP_FLAG6 0x00000020
#define SP_FLAG7 0x00000040
#define SP_FLAG8 0x00000080
#define SP_FLAG9 0x00000100
#define SP_FLAG_ORIGINAL 0x00000200
// Registration
#define SP_REGISTER_CLIENT (0x01)
#define SP_REGISTER_SERVER (0x02)
#define SP_REGISTER_MENU (0x04)
#define SP_REGISTER_REQUIRED (0x08)
class cCriteria
{
public:
int WhichLanguage;
cCriteria(int initWhichLanguage = SP_LANGUAGE_ALL);
};
#ifdef _STRIPED_
class cStringPackageED;
class cCriteriaED : public cCriteria
{
public:
cStringPackageED *Merge;
cCriteriaED(int initWhichLanguage = SP_LANGUAGE_ALL, cStringPackageED *initMerge = NULL);
};
#endif
class cStrings
{
private:
unsigned int Flags;
char *Reference;
public:
cStrings(unsigned int initFlags = 0, char *initReference = NULL);
virtual ~cStrings(void);
virtual void Clear(void);
void SetFlags(unsigned int newFlags);
void SetReference(char *newReference);
unsigned int GetFlags(void) { return Flags; }
char *GetReference(void) { return Reference; }
virtual bool UnderstandToken(int token, char *data );
virtual bool Load(char *&Data, int &Size);
virtual bool SubSave(FILE *FH);
bool Save(FILE *FH );
};
#ifdef _STRIPED_
class cStringsED : public cStrings
{
private:
char *Reference;
char *Text[MAX_LANGUAGES];
char *Notes;
bool Used;
public:
cStringsED(unsigned int initFlags = 0, char *initReference = NULL, char *initNotes = NULL);
virtual ~cStringsED();
virtual void Clear(void);
void SetUsed(bool newUsed = true) { Used = newUsed; }
void SetText(int index, char *newText);
void SetNotes(char *newNotes);
bool GetUsed(void) { return Used; }
char *GetText(int index) { return Text[index]; }
char *GetNotes(void) { return Notes; }
virtual bool UnderstandToken(int token, char *data, cCriteria &Criteria);
virtual bool SubSave(FILE *FH, cCriteria &Criteria);
virtual bool Load(char *&Data, int &Size, cCriteria &Criteria);
};
#endif
#ifndef _STRIPED_
class cStringsSingle : public cStrings
{
private:
char *Text;
virtual void Clear(void);
void SetText(const char *newText);
public:
cStringsSingle(unsigned int initFlags = 0, char *initReference = NULL);
virtual ~cStringsSingle();
char *GetText(void) { return Text; }
virtual bool UnderstandToken(int token, char *data);
};
//======================================================================
class cStringPackageID
{
private:
string name;
byte reg;
public:
cStringPackageID(const char *in_name, byte in_reg) { name = in_name; reg = in_reg; }
const char *GetName(void) const { return(name.c_str()); }
byte GetReg(void) const { return(reg); }
};
#endif
class cStringPackage
{
protected:
unsigned char ID;
unsigned char Registration;
string name;
char *Reference;
public:
cStringPackage(const char *in, unsigned char initID = 0, char *initDescription = NULL, char *initReference = NULL);
~cStringPackage(void);
void Register(unsigned char newRegistration) { Registration |= newRegistration; }
bool UnRegister(unsigned char oldRegistration) { Registration &= ~oldRegistration; return (Registration == 0); }
bool RegisteredOnServer(void) const { return(!!(Registration & SP_REGISTER_SERVER)); }
byte GetRegistration(void) const { return(Registration); }
void SetID(unsigned char newID) { ID = newID; }
void SetReference(char *newReference);
unsigned char GetID(void) { return ID; }
char *GetReference(void) { return Reference; }
const char *GetName(void) const { return(name.c_str()); }
virtual bool UnderstandToken(char *&Data, int &Size, int token, char *data);
virtual bool SubSave(FILE *FH);
bool Save(char *FileName);
virtual bool Load(char *FileName);
virtual bool Load(char *Data, int &Size);
};
#ifdef _STRIPED_
class cStringPackageED : public cStringPackage
{
private:
cStringsED Strings[MAX_STRINGS];
char *Description;
public:
cStringPackageED(unsigned char initID = 0, char *initDescription = NULL, char *initReference = NULL);
~cStringPackageED(void);
void SetDescription(char *newDescription);
char *GetDescription(void) { return Description; }
cStringsED *FindString(int &index);
void ClearString(int index);
virtual bool UnderstandToken(char *&Data, int &Size, int token, char *data, cCriteria &Criteria);
virtual bool SubSave(FILE *FH, cCriteria &Criteria);
bool GenerateCHeader(char *FileName);
bool GenerateDSHeader(char *FileName);
};
#endif
#ifndef _STRIPED_
class cStringPackageSingle : public cStringPackage
{
private:
cStringsSingle Strings[MAX_STRINGS];
map<string, int> ReferenceTable;
public:
cStringPackageSingle(const char *in, unsigned char initID = 0, char *initReference = NULL);
~cStringPackageSingle(void);
cStringsSingle *FindString(int index) { return &Strings[index]; }
cStringsSingle *FindString(char *ReferenceLookup);
int FindStringID(const char *ReferenceLookup);
virtual bool UnderstandToken(char *&Data, int &Size, int token, char *data );
};
#endif
typedef struct sFlagPair
{
int Name;
unsigned long Value;
} tFlagPair;
extern sFlagPair FlagPairs[];
extern sFlagPair LanguagePairs[];
#ifdef _STRIPED_
#define LANGUAGELIST_MAX 16
#define LANGUAGE_LENGTH 64
#define FLAGLIST_MAX 16
#define FLAG_LENGTH 32
extern cStringPackageED *StringPackage;
extern char LanguageList[LANGUAGELIST_MAX][LANGUAGE_LENGTH];
extern char FlagList[FLAGLIST_MAX][FLAG_LENGTH];
#endif
#ifndef _STRIPED_
// Registration
cStringPackageSingle *SP_Register(const char *Package, unsigned char Registration);
qboolean SP_RegisterServer(const char *Package);
void SP_Unload(unsigned char Registration);
// Direct string functions
int SP_GetStringID(const char *Reference);
cStringsSingle *SP_GetString(unsigned short ID);
cStringsSingle *SP_GetString(const char *Reference);
const char *SP_GetStringText(unsigned short ID);
const char *SP_GetStringTextString(const char *Reference);
// Initialization
void SP_Init(void);
#endif
extern int Language_GetIntegerValue(void);
#endif // __STRIP_H

57
codemp/qcommon/tags.h Normal file
View File

@@ -0,0 +1,57 @@
// 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(BOTLIB),
TAGDEF(CLIENTS), // Memory used for client info
TAGDEF(HUNK_MARK1), //hunk allocations before the mark is set
TAGDEF(HUNK_MARK2), //hunk allocations after the mark is set
TAGDEF(EVENT),
TAGDEF(FILESYS), // general filesystem usage
TAGDEF(GHOUL2), // Ghoul2 stuff
TAGDEF(LISTFILES), // for "*.blah" lists
TAGDEF(AMBIENTSET),
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
//sorry, I don't want to have to keep adding these and recompiling, so there may be more than I need
TAGDEF(ICARUS2), //for debugging mem leaks in icarus -rww
TAGDEF(ICARUS3), //for debugging mem leaks in icarus -rww
TAGDEF(ICARUS4), //for debugging mem leaks in icarus -rww
TAGDEF(ICARUS5), //for debugging mem leaks in icarus -rww
TAGDEF(SHADERTEXT),
TAGDEF(SND_RAWDATA), // raw sound data, either MP3 or WAV
TAGDEF(TEMP_WORKSPACE), // anything like file loading or image workspace that's only temporary
TAGDEF(TEMP_PNG), // image workspace that's only temporary
TAGDEF(TEXTPOOL), // for some special text-pool class thingy
TAGDEF(IMAGE_T), // an image_t struct (no longer on the hunk because of cached texture stuff)
TAGDEF(BSP), // guess.
TAGDEF(GRIDMESH), // some specific temp workspace that only seems to be in the MP codebase
TAGDEF(POINTCACHE), // weather system
TAGDEF(VM_ALLOCATED), // allocated by game or cgame via memory shifting
TAGDEF(TEMP_HUNKALLOC),
TAGDEF(NEWDEL), // new / delete -> Z_Malloc on Xbox
TAGDEF(UI_ALLOC), // UI DLL calls to UI_Alloc
TAGDEF(CG_UI_ALLOC), // Cgame DLL calls to UI_Alloc
TAGDEF(BG_ALLOC),
TAGDEF(BINK),
TAGDEF(XBL_FRIENDS), // friends list
TAGDEF(STRINGED),
TAGDEF(CLIENT_MANAGER),
TAGDEF(CLIENT_MANAGER_SPECIAL), // Special: Use HeapAlloc() for second client data to re-use spare model memory
TAGDEF(COUNT)
//////////////// eof //////////////

61
codemp/qcommon/timing.h Normal file
View File

@@ -0,0 +1,61 @@
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;
#ifndef __linux__
__asm
{
push eax
push ebx
push edx
rdtsc
mov ebx, e
mov [ebx], eax
mov [ebx + 4], edx
pop edx
pop ebx
pop eax
}
#endif
time = end - start;
if (time < 0)
{
time = 0;
}
return((int)time);
}
};
// end

1337
codemp/qcommon/unzip.cpp Normal file

File diff suppressed because it is too large Load Diff

289
codemp/qcommon/unzip.h Normal file
View File

@@ -0,0 +1,289 @@
//unzip.h
#define ZIP_FILE FILE
#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
/* 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 */
unsigned char* tmpFile;
int tmpPos,tmpSize;
} 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
*/

954
codemp/qcommon/vm.cpp Normal file
View File

@@ -0,0 +1,954 @@
// vm.c -- virtual machine
/*
intermix code and data
symbol table
a dll has one imported function: VM_SystemCall
and one exported function: Perform
*/
//Anything above this #include will be ignored by the compiler
#include "../qcommon/exe_headers.h"
#include "vm_local.h"
#ifdef CRAZY_SYMBOL_MAP
symbolVMMap_t g_vmMap;
symbolMap_t *g_symbolMap;
#endif
vm_t *currentVM = NULL; // bk001212
vm_t *lastVM = NULL; // bk001212
int vm_debugLevel;
#define MAX_VM 3
vm_t vmTable[MAX_VM];
void VM_VmInfo_f( void );
void VM_VmProfile_f( void );
// converts a VM pointer to a C pointer and
// checks to make sure that the range is acceptable
void *VM_VM2C( vmptr_t p, int length ) {
return (void *)p;
}
void VM_Debug( int level ) {
vm_debugLevel = level;
}
/*
==============
VM_Init
==============
*/
void VM_Init( void ) {
Cvar_Get( "vm_cgame", "0", CVAR_SYSTEMINFO|CVAR_ARCHIVE ); // default to DLLs now instead. Our VMs are getting too HUGE.
Cvar_Get( "vm_game", "0", CVAR_SYSTEMINFO|CVAR_ARCHIVE ); //
Cvar_Get( "vm_ui", "0", CVAR_SYSTEMINFO|CVAR_ARCHIVE ); //
//client wants to know if the server is using vm's for certain modules,
//so if pure we can force the same method (be it vm or dll) -rww
Cmd_AddCommand ("vmprofile", VM_VmProfile_f );
Cmd_AddCommand ("vminfo", VM_VmInfo_f );
Com_Memset( vmTable, 0, sizeof( vmTable ) );
}
/*
===============
VM_ValueToSymbol
Assumes a program counter value
===============
*/
const char *VM_ValueToSymbol( vm_t *vm, int value ) {
vmSymbol_t *sym;
static char text[MAX_TOKEN_CHARS];
#ifdef CRAZY_SYMBOL_MAP
sym = (*g_symbolMap)[value];
if (!sym)
{
#endif
sym = vm->symbols;
if ( !sym ) {
return "NO SYMBOLS";
}
// find the symbol
while ( sym->next && sym->next->symValue <= value ) {
sym = sym->next;
}
#ifdef CRAZY_SYMBOL_MAP
if (sym)
{ //for instant recollection next time
(*g_symbolMap)[value] = sym;
}
}
#endif
if ( value == sym->symValue ) {
return sym->symName;
}
Com_sprintf( text, sizeof( text ), "%s+%i", sym->symName, value - sym->symValue );
return text;
}
/*
===============
VM_ValueToFunctionSymbol
For profiling, find the symbol behind this value
===============
*/
vmSymbol_t *VM_ValueToFunctionSymbol( vm_t *vm, int value ) {
vmSymbol_t *sym;
static vmSymbol_t nullSym;
#ifdef CRAZY_SYMBOL_MAP
sym = (*g_symbolMap)[value];
if ( !sym )
{
#endif
sym = vm->symbols;
if ( !sym ) {
return &nullSym;
}
while ( sym->next && sym->next->symValue <= value ) {
sym = sym->next;
}
#ifdef CRAZY_SYMBOL_MAP
if (sym)
{ //for instant recollection next time
(*g_symbolMap)[value] = sym;
}
}
#endif
return sym;
}
/*
===============
VM_SymbolToValue
===============
*/
int VM_SymbolToValue( vm_t *vm, const char *symbol ) {
vmSymbol_t *sym;
for ( sym = vm->symbols ; sym ; sym = sym->next ) {
if ( !strcmp( symbol, sym->symName ) ) {
return sym->symValue;
}
}
return 0;
}
/*
=====================
VM_SymbolForCompiledPointer
=====================
*/
const char *VM_SymbolForCompiledPointer( vm_t *vm, void *code ) {
int i;
if ( code < (void *)vm->codeBase ) {
return "Before code block";
}
if ( code >= (void *)(vm->codeBase + vm->codeLength) ) {
return "After code block";
}
// find which original instruction it is after
for ( i = 0 ; i < vm->codeLength ; i++ ) {
if ( (void *)vm->instructionPointers[i] > code ) {
break;
}
}
i--;
// now look up the bytecode instruction pointer
#ifdef CRAZY_SYMBOL_MAP
VM_SetSymbolMap(vm);
#endif
return VM_ValueToSymbol( vm, i );
}
/*
===============
ParseHex
===============
*/
int ParseHex( const char *text ) {
int value;
int c;
value = 0;
while ( ( c = *text++ ) != 0 ) {
if ( c >= '0' && c <= '9' ) {
value = value * 16 + c - '0';
continue;
}
if ( c >= 'a' && c <= 'f' ) {
value = value * 16 + 10 + c - 'a';
continue;
}
if ( c >= 'A' && c <= 'F' ) {
value = value * 16 + 10 + c - 'A';
continue;
}
}
return value;
}
/*
===============
VM_Alloc
Convenience function for changing the way VMs are allocated.
===============
*/
void *VM_Alloc( int size )
{
return Hunk_Alloc(size, h_high);
//return Z_Malloc(size, TAG_ALL, qtrue);
}
/*
===============
VM_LoadSymbols
===============
*/
void VM_LoadSymbols( vm_t *vm ) {
int len;
char *mapfile, *token;
const char *text_p;
char name[MAX_QPATH];
char symbols[MAX_QPATH];
vmSymbol_t **prev, *sym;
int count;
int value;
int chars;
int segment;
int numInstructions;
// don't load symbols if not developer
if ( !com_developer->integer ) {
return;
}
COM_StripExtension( vm->name, name );
Com_sprintf( symbols, sizeof( symbols ), "vm/%s.map", name );
len = FS_ReadFile( symbols, (void **)&mapfile );
if ( !mapfile ) {
Com_Printf( "Couldn't load symbol file: %s\n", symbols );
return;
}
numInstructions = vm->instructionPointersLength >> 2;
// parse the symbols
text_p = mapfile;
prev = &vm->symbols;
count = 0;
#ifdef CRAZY_SYMBOL_MAP
VM_SetSymbolMap(vm);
#endif
while ( 1 ) {
token = COM_Parse( &text_p );
if ( !token[0] ) {
break;
}
segment = ParseHex( token );
if ( segment ) {
COM_Parse( &text_p );
COM_Parse( &text_p );
continue; // only load code segment values
}
token = COM_Parse( &text_p );
if ( !token[0] ) {
Com_Printf( "WARNING: incomplete line at end of file\n" );
break;
}
value = ParseHex( token );
token = COM_Parse( &text_p );
if ( !token[0] ) {
Com_Printf( "WARNING: incomplete line at end of file\n" );
break;
}
chars = strlen( token );
sym = (struct vmSymbol_s *)VM_Alloc( sizeof( *sym ) + chars );
*prev = sym;
prev = &sym->next;
sym->next = NULL;
// convert value from an instruction number to a code offset
if ( value >= 0 && value < numInstructions ) {
value = vm->instructionPointers[value];
}
sym->symValue = value;
Q_strncpyz( sym->symName, token, chars + 1 );
#ifdef CRAZY_SYMBOL_MAP
(*g_symbolMap)[value] = sym;
#endif
count++;
}
vm->numSymbols = count;
Com_Printf( "%i symbols parsed from %s\n", count, symbols );
FS_FreeFile( mapfile );
}
/*
============
VM_DllSyscall
Dlls will call this directly
rcg010206 The horror; the horror.
The syscall mechanism relies on stack manipulation to get it's args.
This is likely due to C's inability to pass "..." parameters to
a function in one clean chunk. On PowerPC Linux, these parameters
are not necessarily passed on the stack, so while (&arg[0] == arg)
is true, (&arg[1] == 2nd function parameter) is not necessarily
accurate, as arg's value might have been stored to the stack or
other piece of scratch memory to give it a valid address, but the
next parameter might still be sitting in a register.
Quake's syscall system also assumes that the stack grows downward,
and that any needed types can be squeezed, safely, into a signed int.
This hack below copies all needed values for an argument to a
array in memory, so that Quake can get the correct values. This can
also be used on systems where the stack grows upwards, as the
presumably standard and safe stdargs.h macros are used.
As for having enough space in a signed int for your datatypes, well,
it might be better to wait for DOOM 3 before you start porting. :)
The original code, while probably still inherently dangerous, seems
to work well enough for the platforms it already works on. Rather
than add the performance hit for those platforms, the original code
is still in use there.
For speed, we just grab 15 arguments, and don't worry about exactly
how many the syscall actually needs; the extra is thrown away.
============
*/
int QDECL VM_DllSyscall( int arg, ... ) {
#if ((defined __linux__) && (defined __powerpc__))
// rcg010206 - see commentary above
int args[16];
int i;
va_list ap;
args[0] = arg;
va_start(ap, arg);
for (i = 1; i < sizeof (args) / sizeof (args[i]); i++)
args[i] = va_arg(ap, int);
va_end(ap);
return currentVM->systemCall( args );
#else // original id code
return currentVM->systemCall( &arg );
#endif
}
/*
=================
VM_Restart
Reload the data, but leave everything else in place
This allows a server to do a map_restart without changing memory allocation
=================
*/
vm_t *VM_Restart( vm_t *vm ) {
vmHeader_t *header;
int length;
int dataLength;
int i;
char filename[MAX_QPATH];
// DLL's can't be restarted in place
if ( vm->dllHandle ) {
char name[MAX_QPATH];
int (*systemCall)( int *parms );
systemCall = vm->systemCall;
Q_strncpyz( name, vm->name, sizeof( name ) );
VM_Free( vm );
vm = VM_Create( name, systemCall, VMI_NATIVE );
return vm;
}
// load the image
Com_Printf( "VM_Restart()\n", filename );
Com_sprintf( filename, sizeof(filename), "vm/%s.qvm", vm->name );
Com_Printf( "Loading vm file %s.\n", filename );
length = FS_ReadFile( filename, (void **)&header );
if ( !header ) {
Com_Error( ERR_DROP, "VM_Restart failed.\n" );
}
// byte swap the header
for ( i = 0 ; i < sizeof( *header ) / 4 ; i++ ) {
((int *)header)[i] = LittleLong( ((int *)header)[i] );
}
// validate
if ( header->vmMagic != VM_MAGIC
|| header->bssLength < 0
|| header->dataLength < 0
|| header->litLength < 0
|| header->codeLength <= 0 ) {
VM_Free( vm );
Com_Error( ERR_FATAL, "%s has bad header", filename );
}
// round up to next power of 2 so all data operations can
// be mask protected
dataLength = header->dataLength + header->litLength + header->bssLength;
for ( i = 0 ; dataLength > ( 1 << i ) ; i++ ) {
}
dataLength = 1 << i;
// clear the data
Com_Memset( vm->dataBase, 0, dataLength );
// copy the intialized data
Com_Memcpy( vm->dataBase, (byte *)header + header->dataOffset, header->dataLength + header->litLength );
// byte swap the longs
for ( i = 0 ; i < header->dataLength ; i += 4 ) {
*(int *)(vm->dataBase + i) = LittleLong( *(int *)(vm->dataBase + i ) );
}
// free the original file
FS_FreeFile( header );
return vm;
}
/*
================
VM_Create
If image ends in .qvm it will be interpreted, otherwise
it will attempt to load as a system dll
================
*/
#define STACK_SIZE 0x20000
vm_t *VM_Create( const char *module, int (*systemCalls)(int *),
vmInterpret_t interpret ) {
vm_t *vm;
vmHeader_t *header;
int length;
int dataLength;
int i;
char filename[MAX_QPATH];
if ( !module || !module[0] || !systemCalls ) {
Com_Error( ERR_FATAL, "VM_Create: bad parms" );
}
// see if we already have the VM
for ( i = 0 ; i < MAX_VM ; i++ ) {
if (!Q_stricmp(vmTable[i].name, module)) {
vm = &vmTable[i];
return vm;
}
}
// find a free vm
for ( i = 0 ; i < MAX_VM ; i++ ) {
if ( !vmTable[i].name[0] ) {
break;
}
}
if ( i == MAX_VM ) {
Com_Error( ERR_FATAL, "VM_Create: no free vm_t" );
}
vm = &vmTable[i];
Q_strncpyz( vm->name, module, sizeof( vm->name ) );
vm->systemCall = systemCalls;
// never allow dll loading with a demo
if ( interpret == VMI_NATIVE ) {
if ( Cvar_VariableValue( "fs_restrict" ) ) {
interpret = VMI_COMPILED;
}
}
if ( interpret == VMI_NATIVE ) {
// try to load as a system dll
Com_Printf( "Loading dll file %s.\n", vm->name );
vm->dllHandle = Sys_LoadDll( module, &vm->entryPoint, VM_DllSyscall );
if ( vm->dllHandle ) {
return vm;
}
Com_Printf( "Failed to load dll, looking for qvm.\n" );
interpret = VMI_COMPILED;
}
// load the image
Com_sprintf( filename, sizeof(filename), "vm/%s.qvm", vm->name );
Com_Printf( "Loading vm file %s.\n", filename );
length = FS_ReadFile( filename, (void **)&header );
if ( !header ) {
Com_Printf( "Failed.\n" );
VM_Free( vm );
return NULL;
}
// byte swap the header
for ( i = 0 ; i < sizeof( *header ) / 4 ; i++ ) {
((int *)header)[i] = LittleLong( ((int *)header)[i] );
}
// validate
if ( header->vmMagic != VM_MAGIC
|| header->bssLength < 0
|| header->dataLength < 0
|| header->litLength < 0
|| header->codeLength <= 0 ) {
VM_Free( vm );
Com_Error( ERR_FATAL, "%s has bad header", filename );
}
// round up to next power of 2 so all data operations can
// be mask protected
dataLength = header->dataLength + header->litLength + header->bssLength;
for ( i = 0 ; dataLength > ( 1 << i ) ; i++ ) {
}
dataLength = 1 << i;
// allocate zero filled space for initialized and uninitialized data
vm->dataBase = (unsigned char *)VM_Alloc( dataLength );
vm->dataMask = dataLength - 1;
// copy the intialized data
Com_Memcpy( vm->dataBase, (byte *)header + header->dataOffset, header->dataLength + header->litLength );
// byte swap the longs
for ( i = 0 ; i < header->dataLength ; i += 4 ) {
*(int *)(vm->dataBase + i) = LittleLong( *(int *)(vm->dataBase + i ) );
}
// allocate space for the jump targets, which will be filled in by the compile/prep functions
vm->instructionPointersLength = header->instructionCount * 4;
vm->instructionPointers = (int *)VM_Alloc( vm->instructionPointersLength );
// copy or compile the instructions
vm->codeLength = header->codeLength;
if ( interpret >= VMI_COMPILED ) {
vm->compiled = qtrue;
VM_Compile( vm, header );
} else {
vm->compiled = qfalse;
VM_PrepareInterpreter( vm, header );
}
// free the original file
FS_FreeFile( header );
// load the map file
VM_LoadSymbols( vm );
// the stack is implicitly at the end of the image
vm->programStack = vm->dataMask + 1;
vm->stackBottom = vm->programStack - STACK_SIZE;
return vm;
}
/*
==============
VM_Free
==============
*/
void VM_Free( vm_t *vm ) {
if ( vm->dllHandle ) {
Sys_UnloadDll( vm->dllHandle );
Com_Memset( vm, 0, sizeof( *vm ) );
}
#if 0 // now automatically freed by hunk
if ( vm->codeBase ) {
Z_Free( vm->codeBase );
}
if ( vm->dataBase ) {
Z_Free( vm->dataBase );
}
if ( vm->instructionPointers ) {
Z_Free( vm->instructionPointers );
}
#endif
Com_Memset( vm, 0, sizeof( *vm ) );
currentVM = NULL;
lastVM = NULL;
}
void VM_Clear(void) {
int i;
for (i=0;i<MAX_VM; i++) {
if ( vmTable[i].dllHandle ) {
Sys_UnloadDll( vmTable[i].dllHandle );
}
Com_Memset( &vmTable[i], 0, sizeof( vm_t ) );
}
currentVM = NULL;
lastVM = NULL;
}
void *VM_ArgPtr( int intValue ) {
if ( !intValue ) {
return NULL;
}
// bk001220 - currentVM is missing on reconnect
if ( currentVM==NULL )
return NULL;
if ( currentVM->entryPoint ) {
return (void *)(currentVM->dataBase + intValue);
}
else {
return (void *)(currentVM->dataBase + (intValue & currentVM->dataMask));
}
}
extern vm_t *gvm;
void *BotVMShift( int ptr )
{
if ( !ptr )
{
return NULL;
}
if (!gvm)
{ //always using the game vm here.
return NULL;
}
if ( gvm->entryPoint )
{
return (void *)(gvm->dataBase + ptr);
}
else
{
return (void *)(gvm->dataBase + (ptr & gvm->dataMask));
}
}
void VM_Shifted_Alloc(void **ptr, int size)
{
void *mem;
if (!currentVM)
{
assert(0);
*ptr = NULL;
return;
}
//first allocate our desired memory, up front
mem = Z_Malloc(size+1, TAG_VM_ALLOCATED, qfalse);
if (!mem)
{
assert(0);
*ptr = NULL;
return;
}
memset(mem, 0, size+1);
//This can happen.. if a free chunk of memory is found before the vm alloc pointer, commonly happens
//when allocating like 4 bytes or whatever. However it seems to actually be handled which I didn't
//think it would be.. so hey.
#if 0
if ((int)mem < (int)currentVM->dataBase)
{
assert(!"Unspeakably bad thing has occured (mem ptr < vm base ptr)");
*ptr = NULL;
return;
}
#endif
//Alright, subtract the database from the memory pointer to get a memory address relative to the VM.
//When the VM modifies it it should be modifying the same chunk of memory we have allocated in the engine.
*ptr = (void *)((int)mem - (int)currentVM->dataBase);
}
void VM_Shifted_Free(void **ptr)
{
void *mem;
if (!currentVM)
{
assert(0);
return;
}
//Shift the VM memory pointer back to get the same pointer we initially allocated in real memory space.
mem = (void *)((int)currentVM->dataBase + (int)*ptr);
if (!mem)
{
assert(0);
return;
}
Z_Free(mem);
*ptr = NULL; //go ahead and clear the pointer for the game.
}
void *VM_ExplicitArgPtr( vm_t *vm, int intValue ) {
if ( !intValue ) {
return NULL;
}
// bk010124 - currentVM is missing on reconnect here as well?
if ( currentVM==NULL )
return NULL;
//
if ( vm->entryPoint ) {
return (void *)(vm->dataBase + intValue);
}
else {
return (void *)(vm->dataBase + (intValue & vm->dataMask));
}
}
/*
==============
VM_Call
Upon a system call, the stack will look like:
sp+32 parm1
sp+28 parm0
sp+24 return value
sp+20 return address
sp+16 local1
sp+14 local0
sp+12 arg1
sp+8 arg0
sp+4 return stack
sp return address
An interpreted function will immediately execute
an OP_ENTER instruction, which will subtract space for
locals from sp
==============
*/
#define MAX_STACK 256
#define STACK_MASK (MAX_STACK-1)
int QDECL VM_Call( vm_t *vm, int callnum, ... ) {
vm_t *oldVM;
int r;
int i;
int args[16];
va_list ap;
if ( !vm ) {
Com_Error( ERR_FATAL, "VM_Call with NULL vm" );
}
oldVM = currentVM;
currentVM = vm;
lastVM = vm;
if ( vm_debugLevel ) {
Com_Printf( "VM_Call( %i )\n", callnum );
}
// if we have a dll loaded, call it directly
if ( vm->entryPoint ) {
//rcg010207 - see dissertation at top of VM_DllSyscall() in this file.
va_start(ap, callnum);
for (i = 0; i < sizeof (args) / sizeof (args[i]); i++) {
args[i] = va_arg(ap, int);
}
va_end(ap);
r = vm->entryPoint( callnum, args[0], args[1], args[2], args[3],
args[4], args[5], args[6], args[7],
args[8], args[9], args[10], args[11],
args[12], args[13], args[14], args[15]);
} else if ( vm->compiled ) {
r = VM_CallCompiled( vm, &callnum );
} else {
r = VM_CallInterpreted( vm, &callnum );
}
if ( oldVM != NULL ) // bk001220 - assert(currentVM!=NULL) for oldVM==NULL
currentVM = oldVM;
return r;
}
//=================================================================
static int QDECL VM_ProfileSort( const void *a, const void *b ) {
vmSymbol_t *sa, *sb;
sa = *(vmSymbol_t **)a;
sb = *(vmSymbol_t **)b;
if ( sa->profileCount < sb->profileCount ) {
return -1;
}
if ( sa->profileCount > sb->profileCount ) {
return 1;
}
return 0;
}
/*
==============
VM_VmProfile_f
==============
*/
void VM_VmProfile_f( void ) {
vm_t *vm;
vmSymbol_t **sorted, *sym;
int i;
double total;
if ( !lastVM ) {
return;
}
vm = lastVM;
if ( !vm->numSymbols ) {
return;
}
sorted = (struct vmSymbol_s **)Z_Malloc( vm->numSymbols * sizeof( *sorted ), TAG_VM, qtrue );
sorted[0] = vm->symbols;
total = sorted[0]->profileCount;
for ( i = 1 ; i < vm->numSymbols ; i++ ) {
sorted[i] = sorted[i-1]->next;
total += sorted[i]->profileCount;
}
qsort( sorted, vm->numSymbols, sizeof( *sorted ), VM_ProfileSort );
for ( i = 0 ; i < vm->numSymbols ; i++ ) {
int perc;
sym = sorted[i];
perc = 100 * (float) sym->profileCount / total;
Com_Printf( "%2i%% %9i %s\n", perc, sym->profileCount, sym->symName );
sym->profileCount = 0;
}
Com_Printf(" %9.0f total\n", total );
Z_Free( sorted );
}
/*
==============
VM_VmInfo_f
==============
*/
void VM_VmInfo_f( void ) {
vm_t *vm;
int i;
Com_Printf( "Registered virtual machines:\n" );
for ( i = 0 ; i < MAX_VM ; i++ ) {
vm = &vmTable[i];
if ( !vm->name[0] ) {
break;
}
Com_Printf( "%s : ", vm->name );
if ( vm->dllHandle ) {
Com_Printf( "native\n" );
continue;
}
if ( vm->compiled ) {
Com_Printf( "compiled on load\n" );
} else {
Com_Printf( "interpreted\n" );
}
Com_Printf( " code length : %7i\n", vm->codeLength );
Com_Printf( " table length: %7i\n", vm->instructionPointersLength );
Com_Printf( " data length : %7i\n", vm->dataMask + 1 );
}
}
/*
===============
VM_LogSyscalls
Insert calls to this while debugging the vm compiler
===============
*/
void VM_LogSyscalls( int *args ) {
static int callnum;
static FILE *f;
if ( !f ) {
f = fopen("syscalls.log", "w" );
}
callnum++;
fprintf(f, "%i: %i (%i) = %i %i %i %i\n", callnum, args - (int *)currentVM->dataBase,
args[0], args[1], args[2], args[3], args[4] );
}
#ifdef oDLL_ONLY // bk010215 - for DLL_ONLY dedicated servers/builds w/o VM
int VM_CallCompiled( vm_t *vm, int *args ) {
return(0);
}
void VM_Compile( vm_t *vm, vmHeader_t *header ) {}
#endif // DLL_ONLY

View File

@@ -0,0 +1,229 @@
#include "../qcommon/exe_headers.h"
#include "vm_local.h"
vm_t *currentVM = NULL;
vm_t *lastVM = NULL;
#define MAX_VM 3
vm_t vmTable[MAX_VM];
/*
============
VM_DllSyscall
Dlls will call this directly
rcg010206 The horror; the horror.
The syscall mechanism relies on stack manipulation to get it's args.
This is likely due to C's inability to pass "..." parameters to
a function in one clean chunk. On PowerPC Linux, these parameters
are not necessarily passed on the stack, so while (&arg[0] == arg)
is true, (&arg[1] == 2nd function parameter) is not necessarily
accurate, as arg's value might have been stored to the stack or
other piece of scratch memory to give it a valid address, but the
next parameter might still be sitting in a register.
Quake's syscall system also assumes that the stack grows downward,
and that any needed types can be squeezed, safely, into a signed int.
This hack below copies all needed values for an argument to a
array in memory, so that Quake can get the correct values. This can
also be used on systems where the stack grows upwards, as the
presumably standard and safe stdargs.h macros are used.
As for having enough space in a signed int for your datatypes, well,
it might be better to wait for DOOM 3 before you start porting. :)
The original code, while probably still inherently dangerous, seems
to work well enough for the platforms it already works on. Rather
than add the performance hit for those platforms, the original code
is still in use there.
For speed, we just grab 15 arguments, and don't worry about exactly
how many the syscall actually needs; the extra is thrown away.
============
*/
int QDECL VM_DllSyscall( int arg, ... )
{
return currentVM->systemCall( &arg );
}
/*
================
VM_Create
If image ends in .qvm it will be interpreted, otherwise
it will attempt to load as a system dll
================
*/
//#define STACK_SIZE 0x20000
#define UI_VM_INDEX 0
#define CG_VM_INDEX 1
#define G_VM_INDEX 2
namespace cgame
{
extern int vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11 );
void dllEntry( int (QDECL *syscallptr)( int arg,... ) );
};
namespace game
{
extern int vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11 );
void dllEntry( int (QDECL *syscallptr)( int arg,... ) );
};
namespace ui
{
extern int vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11 );
void dllEntry( int (QDECL *syscallptr)( int arg,... ) );
};
vm_t *VM_Create( const char *module, int (*systemCalls)(int *),
vmInterpret_t interpret ) {
if (!Q_stricmp("ui", module))
{
// UI VM
vmTable[UI_VM_INDEX].entryPoint = (int (*)(int,...)) ui::vmMain;
vmTable[UI_VM_INDEX].systemCall = systemCalls;
ui::dllEntry(VM_DllSyscall);
return &vmTable[UI_VM_INDEX];
}
else if (!Q_stricmp("cgame", module))
{
// CG VM
vmTable[CG_VM_INDEX].entryPoint = (int (*)(int,...)) cgame::vmMain;
vmTable[CG_VM_INDEX].systemCall = systemCalls;
cgame::dllEntry(VM_DllSyscall);
return &vmTable[CG_VM_INDEX];
}
else if (!Q_stricmp("jampgame", module))
{
// G VM
vmTable[G_VM_INDEX].entryPoint = (int (*)(int,...)) game::vmMain;
vmTable[G_VM_INDEX].systemCall = systemCalls;
game::dllEntry(VM_DllSyscall);
return &vmTable[G_VM_INDEX];
}
else
return NULL;
}
/*
==============
VM_Call
Upon a system call, the stack will look like:
sp+32 parm1
sp+28 parm0
sp+24 return value
sp+20 return address
sp+16 local1
sp+14 local0
sp+12 arg1
sp+8 arg0
sp+4 return stack
sp return address
An interpreted function will immediately execute
an OP_ENTER instruction, which will subtract space for
locals from sp
==============
*/
#define MAX_STACK 256
#define STACK_MASK (MAX_STACK-1)
int QDECL VM_Call( vm_t *vm, int callnum, ... )
{
// Remember what the current VM was when we started.
vm_t *oldVM = currentVM;
// Change current VM so that VMA() crap works
currentVM = vm;
// Forward the call to the vm's vmMain function, passing through more data than
// we should. I'm going to be sick.
#if defined(_GAMECUBE)
int i;
int args[16];
va_list ap;
va_start(ap, callnum);
for (i = 0; i < sizeof (args) / sizeof (args[i]); i++)
args[i] = va_arg(ap, int);
va_end(ap);
int r = vm->entryPoint( callnum, args[0], args[1], args[2], args[3],
args[4], args[5], args[6], args[7],
args[8], args[9], args[10], args[11],
args[12], args[13], args[14], args[15]);
#else
int r = vm->entryPoint( (&callnum)[0], (&callnum)[1], (&callnum)[2], (&callnum)[3],
(&callnum)[4], (&callnum)[5], (&callnum)[6], (&callnum)[7],
(&callnum)[8], (&callnum)[9], (&callnum)[10], (&callnum)[11], (&callnum)[12] );
#endif
// Restore VM pointer XXX: Why does the below code check for non-NULL?
currentVM = oldVM;
return r;
}
// This function seems really suspect. Let's cross our fingers...
void *BotVMShift( int ptr )
{
return (void *)ptr;
}
// Functions to support dynamic memory allocation by VMs.
// I don't really trust these. Oh well.
void VM_Shifted_Alloc(void **ptr, int size)
{
if (!currentVM)
{
assert(0);
*ptr = NULL;
return;
}
//first allocate our desired memory, up front
*ptr = Z_Malloc(size, TAG_VM_ALLOCATED, qtrue);
}
void VM_Shifted_Free(void **ptr)
{
if (!currentVM)
{
assert(0);
return;
}
Z_Free(*ptr);
*ptr = NULL; //go ahead and clear the pointer for the game.
}
// Stupid casting function. We can't do this in the macros, because sv_game calls this
// directly now.
void *VM_ArgPtr( int intValue )
{
return (void *)intValue;
}
void VM_Free(vm_t *) {}
void VM_Debug(int) {}
void VM_Clear(void) {}
void VM_Init(void) {}
void *VM_ExplicitArgPtr(vm_t *, int) { return NULL; }
vm_t *VM_Restart(vm_t *vm) { return vm; }

View File

@@ -0,0 +1,905 @@
//Anything above this #include will be ignored by the compiler
#include "../qcommon/exe_headers.h"
#include "vm_local.h"
#define DEBUG_VM
#ifdef DEBUG_VM // bk001204
static char *opnames[256] = {
"OP_UNDEF",
"OP_IGNORE",
"OP_BREAK",
"OP_ENTER",
"OP_LEAVE",
"OP_CALL",
"OP_PUSH",
"OP_POP",
"OP_CONST",
"OP_LOCAL",
"OP_JUMP",
//-------------------
"OP_EQ",
"OP_NE",
"OP_LTI",
"OP_LEI",
"OP_GTI",
"OP_GEI",
"OP_LTU",
"OP_LEU",
"OP_GTU",
"OP_GEU",
"OP_EQF",
"OP_NEF",
"OP_LTF",
"OP_LEF",
"OP_GTF",
"OP_GEF",
//-------------------
"OP_LOAD1",
"OP_LOAD2",
"OP_LOAD4",
"OP_STORE1",
"OP_STORE2",
"OP_STORE4",
"OP_ARG",
"OP_BLOCK_COPY",
//-------------------
"OP_SEX8",
"OP_SEX16",
"OP_NEGI",
"OP_ADD",
"OP_SUB",
"OP_DIVI",
"OP_DIVU",
"OP_MODI",
"OP_MODU",
"OP_MULI",
"OP_MULU",
"OP_BAND",
"OP_BOR",
"OP_BXOR",
"OP_BCOM",
"OP_LSH",
"OP_RSHI",
"OP_RSHU",
"OP_NEGF",
"OP_ADDF",
"OP_SUBF",
"OP_DIVF",
"OP_MULF",
"OP_CVIF",
"OP_CVFI"
};
#endif
#if idppc
#if defined(__GNUC__)
static inline unsigned int loadWord(void *addr) {
unsigned int word;
asm("lwbrx %0,0,%1" : "=r" (word) : "r" (addr));
return word;
}
#else
#define loadWord(addr) __lwbrx(addr,0)
#endif
#else
#define loadWord(addr) *((int *)addr)
#endif
char *VM_Indent( vm_t *vm ) {
static char *string = " ";
if ( vm->callLevel > 20 ) {
return string;
}
return string + 2 * ( 20 - vm->callLevel );
}
void VM_StackTrace( vm_t *vm, int programCounter, int programStack ) {
int count;
count = 0;
do {
Com_Printf( "%s\n", VM_ValueToSymbol( vm, programCounter ) );
programStack = *(int *)&vm->dataBase[programStack+4];
programCounter = *(int *)&vm->dataBase[programStack];
} while ( programCounter != -1 && ++count < 32 );
}
/*
====================
VM_PrepareInterpreter
====================
*/
void VM_PrepareInterpreter( vm_t *vm, vmHeader_t *header ) {
int op;
int pc;
byte *code;
int instruction;
int *codeBase;
vm->codeBase = (unsigned char *)Hunk_Alloc( vm->codeLength*4, h_high ); // we're now int aligned
// memcpy( vm->codeBase, (byte *)header + header->codeOffset, vm->codeLength );
// we don't need to translate the instructions, but we still need
// to find each instructions starting point for jumps
pc = 0;
instruction = 0;
code = (byte *)header + header->codeOffset;
codeBase = (int *)vm->codeBase;
while ( instruction < header->instructionCount ) {
vm->instructionPointers[ instruction ] = pc;
instruction++;
op = code[ pc ];
codeBase[pc] = op;
if ( pc > header->codeLength ) {
Com_Error( ERR_FATAL, "VM_PrepareInterpreter: pc > header->codeLength" );
}
pc++;
// these are the only opcodes that aren't a single byte
switch ( op ) {
case OP_ENTER:
case OP_CONST:
case OP_LOCAL:
case OP_LEAVE:
case OP_EQ:
case OP_NE:
case OP_LTI:
case OP_LEI:
case OP_GTI:
case OP_GEI:
case OP_LTU:
case OP_LEU:
case OP_GTU:
case OP_GEU:
case OP_EQF:
case OP_NEF:
case OP_LTF:
case OP_LEF:
case OP_GTF:
case OP_GEF:
case OP_BLOCK_COPY:
codeBase[pc+0] = loadWord(&code[pc]);
pc += 4;
break;
case OP_ARG:
codeBase[pc+0] = code[pc];
pc += 1;
break;
default:
break;
}
}
pc = 0;
instruction = 0;
code = (byte *)header + header->codeOffset;
codeBase = (int *)vm->codeBase;
while ( instruction < header->instructionCount ) {
op = code[ pc ];
instruction++;
pc++;
switch ( op ) {
case OP_ENTER:
case OP_CONST:
case OP_LOCAL:
case OP_LEAVE:
case OP_EQ:
case OP_NE:
case OP_LTI:
case OP_LEI:
case OP_GTI:
case OP_GEI:
case OP_LTU:
case OP_LEU:
case OP_GTU:
case OP_GEU:
case OP_EQF:
case OP_NEF:
case OP_LTF:
case OP_LEF:
case OP_GTF:
case OP_GEF:
case OP_BLOCK_COPY:
switch(op) {
case OP_EQ:
case OP_NE:
case OP_LTI:
case OP_LEI:
case OP_GTI:
case OP_GEI:
case OP_LTU:
case OP_LEU:
case OP_GTU:
case OP_GEU:
case OP_EQF:
case OP_NEF:
case OP_LTF:
case OP_LEF:
case OP_GTF:
case OP_GEF:
codeBase[pc] = vm->instructionPointers[codeBase[pc]];
break;
default:
break;
}
pc += 4;
break;
case OP_ARG:
pc += 1;
break;
default:
break;
}
}
}
/*
==============
VM_Call
Upon a system call, the stack will look like:
sp+32 parm1
sp+28 parm0
sp+24 return stack
sp+20 return address
sp+16 local1
sp+14 local0
sp+12 arg1
sp+8 arg0
sp+4 return stack
sp return address
An interpreted function will immediately execute
an OP_ENTER instruction, which will subtract space for
locals from sp
==============
*/
#define MAX_STACK 256
#define STACK_MASK (MAX_STACK-1)
#define DEBUGSTR va("%s%i", VM_Indent(vm), opStack-stack )
int VM_CallInterpreted( vm_t *vm, int *args ) {
int stack[MAX_STACK];
int *opStack;
int programCounter;
int programStack;
int stackOnEntry;
byte *image;
int *codeImage;
int v1;
int dataMask;
#ifdef DEBUG_VM
vmSymbol_t *profileSymbol = NULL;
#endif
// interpret the code
vm->currentlyInterpreting = qtrue;
#ifdef CRAZY_SYMBOL_MAP
VM_SetSymbolMap(vm);
#endif
// we might be called recursively, so this might not be the very top
programStack = stackOnEntry = vm->programStack;
#ifdef DEBUG_VM
if (com_vmdebug->integer)
{
profileSymbol = VM_ValueToFunctionSymbol( vm, 0 );
}
// uncomment this for debugging breakpoints
vm->breakFunction = 0;
#endif
// set up the stack frame
image = vm->dataBase;
codeImage = (int *)vm->codeBase;
dataMask = vm->dataMask;
// leave a free spot at start of stack so
// that as long as opStack is valid, opStack-1 will
// not corrupt anything
opStack = stack;
programCounter = 0;
programStack -= 48;
*(int *)&image[ programStack + 44] = args[9];
*(int *)&image[ programStack + 40] = args[8];
*(int *)&image[ programStack + 36] = args[7];
*(int *)&image[ programStack + 32] = args[6];
*(int *)&image[ programStack + 28] = args[5];
*(int *)&image[ programStack + 24] = args[4];
*(int *)&image[ programStack + 20] = args[3];
*(int *)&image[ programStack + 16] = args[2];
*(int *)&image[ programStack + 12] = args[1];
*(int *)&image[ programStack + 8 ] = args[0];
*(int *)&image[ programStack + 4 ] = 0; // return stack
*(int *)&image[ programStack ] = -1; // will terminate the loop on return
vm->callLevel = 0;
VM_Debug(0);
// vm_debugLevel=2;
// main interpreter loop, will exit when a LEAVE instruction
// grabs the -1 program counter
#define r2 codeImage[programCounter]
while ( 1 ) {
int opcode, r0, r1;
// unsigned int r2;
nextInstruction:
r0 = ((int *)opStack)[0];
r1 = ((int *)opStack)[-1];
nextInstruction2:
opcode = codeImage[ programCounter++ ];
#ifdef DEBUG_VM
if (com_vmdebug->integer > 1)
{
if ( (unsigned)programCounter > vm->codeLength ) {
Com_Error( ERR_DROP, "VM pc out of range" );
}
if ( opStack < stack ) {
Com_Error( ERR_DROP, "VM opStack underflow" );
}
if ( opStack >= stack+MAX_STACK ) {
Com_Error( ERR_DROP, "VM opStack overflow" );
}
if ( programStack <= vm->stackBottom ) {
Com_Error( ERR_DROP, "VM stack overflow" );
}
if ( programStack & 3 ) {
Com_Error( ERR_DROP, "VM program stack misaligned" );
}
if ( vm_debugLevel > 1 ) {
Com_Printf( "%s %s\n", DEBUGSTR, opnames[opcode] );
}
profileSymbol->profileCount++;
}
#endif
switch ( opcode ) {
#ifdef DEBUG_VM
default:
Com_Error( ERR_DROP, "Bad VM instruction" ); // this should be scanned on load!
#endif
case OP_BREAK:
vm->breakCount++;
goto nextInstruction2;
case OP_CONST:
opStack++;
r1 = r0;
r0 = *opStack = r2;
programCounter += 4;
goto nextInstruction2;
case OP_LOCAL:
opStack++;
r1 = r0;
r0 = *opStack = r2+programStack;
programCounter += 4;
goto nextInstruction2;
case OP_LOAD4:
#ifdef DEBUG_VM
if (com_vmdebug->integer > 1)
{
if ( *opStack & 3 ) {
Com_Error( ERR_DROP, "OP_LOAD4 misaligned" );
}
}
#endif
r0 = *opStack = *(int *)&image[ r0&dataMask ];
goto nextInstruction2;
case OP_LOAD2:
r0 = *opStack = *(unsigned short *)&image[ r0&dataMask ];
goto nextInstruction2;
case OP_LOAD1:
r0 = *opStack = image[ r0&dataMask ];
goto nextInstruction2;
case OP_STORE4:
*(int *)&image[ r1&(dataMask & ~3) ] = r0;
opStack -= 2;
goto nextInstruction;
case OP_STORE2:
*(short *)&image[ r1&(dataMask & ~1) ] = r0;
opStack -= 2;
goto nextInstruction;
case OP_STORE1:
image[ r1&dataMask ] = r0;
opStack -= 2;
goto nextInstruction;
case OP_ARG:
// single byte offset from programStack
*(int *)&image[ codeImage[programCounter] + programStack ] = r0;
opStack--;
programCounter += 1;
goto nextInstruction;
case OP_BLOCK_COPY:
{
int *src, *dest;
int i, count, srci, desti;
count = r2;
// MrE: copy range check
srci = r0 & dataMask;
desti = r1 & dataMask;
count = ((srci + count) & dataMask) - srci;
count = ((desti + count) & dataMask) - desti;
src = (int *)&image[ r0&dataMask ];
dest = (int *)&image[ r1&dataMask ];
if ( ( (int)src | (int)dest | count ) & 3 ) {
Com_Error( ERR_DROP, "OP_BLOCK_COPY not dword aligned" );
}
count >>= 2;
for ( i = count-1 ; i>= 0 ; i-- ) {
dest[i] = src[i];
}
programCounter += 4;
opStack -= 2;
}
goto nextInstruction;
case OP_CALL:
// save current program counter
*(int *)&image[ programStack ] = programCounter;
// jump to the location on the stack
programCounter = r0;
opStack--;
if ( programCounter < 0 ) {
// system call
int r;
int temp;
#ifdef DEBUG_VM
int stomped = 0;
if (com_vmdebug->integer > 1)
{
if ( vm_debugLevel ) {
Com_Printf( "%s---> systemcall(%i)\n", DEBUGSTR, -1 - programCounter );
}
}
#endif
// save the stack to allow recursive VM entry
temp = vm->callLevel;
vm->programStack = programStack - 4;
#ifdef DEBUG_VM
if (com_vmdebug->integer > 1)
{
stomped = *(int *)&image[ programStack + 4 ];
}
#endif
*(int *)&image[ programStack + 4 ] = -1 - programCounter;
//VM_LogSyscalls( (int *)&image[ programStack + 4 ] );
r = vm->systemCall( (int *)&image[ programStack + 4 ] );
#ifdef DEBUG_VM
if (com_vmdebug->integer > 1)
{
// this is just our stack frame pointer, only needed
// for debugging
*(int *)&image[ programStack + 4 ] = stomped;
}
#endif
// save return value
opStack++;
*opStack = r;
programCounter = *(int *)&image[ programStack ];
vm->callLevel = temp;
#ifdef DEBUG_VM
if (com_vmdebug->integer > 1)
{
if ( vm_debugLevel ) {
Com_Printf( "%s<--- %s\n", DEBUGSTR, VM_ValueToSymbol( vm, programCounter ) );
}
}
#endif
} else {
programCounter = vm->instructionPointers[ programCounter ];
}
goto nextInstruction;
// push and pop are only needed for discarded or bad function return values
case OP_PUSH:
opStack++;
goto nextInstruction;
case OP_POP:
opStack--;
goto nextInstruction;
case OP_ENTER:
#ifdef DEBUG_VM
if (com_vmdebug->integer)
{
profileSymbol = VM_ValueToFunctionSymbol( vm, programCounter );
}
#endif
// get size of stack frame
v1 = r2;
programCounter += 4;
programStack -= v1;
#ifdef DEBUG_VM
if (com_vmdebug->integer > 1)
{
// save old stack frame for debugging traces
*(int *)&image[programStack+4] = programStack + v1;
if ( vm_debugLevel ) {
Com_Printf( "%s---> %s\n", DEBUGSTR, VM_ValueToSymbol( vm, programCounter - 5 ) );
if ( vm->breakFunction && programCounter - 5 == vm->breakFunction ) {
// this is to allow setting breakpoints here in the debugger
vm->breakCount++;
// vm_debugLevel = 2;
// VM_StackTrace( vm, programCounter, programStack );
}
vm->callLevel++;
}
}
#endif
goto nextInstruction;
case OP_LEAVE:
// remove our stack frame
v1 = r2;
programStack += v1;
// grab the saved program counter
programCounter = *(int *)&image[ programStack ];
#ifdef DEBUG_VM
if (com_vmdebug->integer)
{
profileSymbol = VM_ValueToFunctionSymbol( vm, programCounter );
if ( vm_debugLevel ) {
vm->callLevel--;
Com_Printf( "%s<--- %s\n", DEBUGSTR, VM_ValueToSymbol( vm, programCounter ) );
}
}
#endif
// check for leaving the VM
if ( programCounter == -1 ) {
goto done;
}
goto nextInstruction;
/*
===================================================================
BRANCHES
===================================================================
*/
case OP_JUMP:
programCounter = r0;
programCounter = vm->instructionPointers[ programCounter ];
opStack--;
goto nextInstruction;
case OP_EQ:
opStack -= 2;
if ( r1 == r0 ) {
programCounter = r2; //vm->instructionPointers[r2];
goto nextInstruction;
} else {
programCounter += 4;
goto nextInstruction;
}
case OP_NE:
opStack -= 2;
if ( r1 != r0 ) {
programCounter = r2; //vm->instructionPointers[r2];
goto nextInstruction;
} else {
programCounter += 4;
goto nextInstruction;
}
case OP_LTI:
opStack -= 2;
if ( r1 < r0 ) {
programCounter = r2; //vm->instructionPointers[r2];
goto nextInstruction;
} else {
programCounter += 4;
goto nextInstruction;
}
case OP_LEI:
opStack -= 2;
if ( r1 <= r0 ) {
programCounter = r2; //vm->instructionPointers[r2];
goto nextInstruction;
} else {
programCounter += 4;
goto nextInstruction;
}
case OP_GTI:
opStack -= 2;
if ( r1 > r0 ) {
programCounter = r2; //vm->instructionPointers[r2];
goto nextInstruction;
} else {
programCounter += 4;
goto nextInstruction;
}
case OP_GEI:
opStack -= 2;
if ( r1 >= r0 ) {
programCounter = r2; //vm->instructionPointers[r2];
goto nextInstruction;
} else {
programCounter += 4;
goto nextInstruction;
}
case OP_LTU:
opStack -= 2;
if ( ((unsigned)r1) < ((unsigned)r0) ) {
programCounter = r2; //vm->instructionPointers[r2];
goto nextInstruction;
} else {
programCounter += 4;
goto nextInstruction;
}
case OP_LEU:
opStack -= 2;
if ( ((unsigned)r1) <= ((unsigned)r0) ) {
programCounter = r2; //vm->instructionPointers[r2];
goto nextInstruction;
} else {
programCounter += 4;
goto nextInstruction;
}
case OP_GTU:
opStack -= 2;
if ( ((unsigned)r1) > ((unsigned)r0) ) {
programCounter = r2; //vm->instructionPointers[r2];
goto nextInstruction;
} else {
programCounter += 4;
goto nextInstruction;
}
case OP_GEU:
opStack -= 2;
if ( ((unsigned)r1) >= ((unsigned)r0) ) {
programCounter = r2; //vm->instructionPointers[r2];
goto nextInstruction;
} else {
programCounter += 4;
goto nextInstruction;
}
case OP_EQF:
if ( ((float *)opStack)[-1] == *(float *)opStack ) {
programCounter = r2; //vm->instructionPointers[r2];
opStack -= 2;
goto nextInstruction;
} else {
programCounter += 4;
opStack -= 2;
goto nextInstruction;
}
case OP_NEF:
if ( ((float *)opStack)[-1] != *(float *)opStack ) {
programCounter = r2; //vm->instructionPointers[r2];
opStack -= 2;
goto nextInstruction;
} else {
programCounter += 4;
opStack -= 2;
goto nextInstruction;
}
case OP_LTF:
if ( ((float *)opStack)[-1] < *(float *)opStack ) {
programCounter = r2; //vm->instructionPointers[r2];
opStack -= 2;
goto nextInstruction;
} else {
programCounter += 4;
opStack -= 2;
goto nextInstruction;
}
case OP_LEF:
if ( ((float *)opStack)[-1] <= *(float *)opStack ) {
programCounter = r2; //vm->instructionPointers[r2];
opStack -= 2;
goto nextInstruction;
} else {
programCounter += 4;
opStack -= 2;
goto nextInstruction;
}
case OP_GTF:
if ( ((float *)opStack)[-1] > *(float *)opStack ) {
programCounter = r2; //vm->instructionPointers[r2];
opStack -= 2;
goto nextInstruction;
} else {
programCounter += 4;
opStack -= 2;
goto nextInstruction;
}
case OP_GEF:
if ( ((float *)opStack)[-1] >= *(float *)opStack ) {
programCounter = r2; //vm->instructionPointers[r2];
opStack -= 2;
goto nextInstruction;
} else {
programCounter += 4;
opStack -= 2;
goto nextInstruction;
}
//===================================================================
case OP_NEGI:
*opStack = -r0;
goto nextInstruction;
case OP_ADD:
opStack[-1] = r1 + r0;
opStack--;
goto nextInstruction;
case OP_SUB:
opStack[-1] = r1 - r0;
opStack--;
goto nextInstruction;
case OP_DIVI:
opStack[-1] = r1 / r0;
opStack--;
goto nextInstruction;
case OP_DIVU:
opStack[-1] = ((unsigned)r1) / ((unsigned)r0);
opStack--;
goto nextInstruction;
case OP_MODI:
opStack[-1] = r1 % r0;
opStack--;
goto nextInstruction;
case OP_MODU:
opStack[-1] = ((unsigned)r1) % (unsigned)r0;
opStack--;
goto nextInstruction;
case OP_MULI:
opStack[-1] = r1 * r0;
opStack--;
goto nextInstruction;
case OP_MULU:
opStack[-1] = ((unsigned)r1) * ((unsigned)r0);
opStack--;
goto nextInstruction;
case OP_BAND:
opStack[-1] = ((unsigned)r1) & ((unsigned)r0);
opStack--;
goto nextInstruction;
case OP_BOR:
opStack[-1] = ((unsigned)r1) | ((unsigned)r0);
opStack--;
goto nextInstruction;
case OP_BXOR:
opStack[-1] = ((unsigned)r1) ^ ((unsigned)r0);
opStack--;
goto nextInstruction;
case OP_BCOM:
opStack[-1] = ~ ((unsigned)r0);
goto nextInstruction;
case OP_LSH:
opStack[-1] = r1 << r0;
opStack--;
goto nextInstruction;
case OP_RSHI:
opStack[-1] = r1 >> r0;
opStack--;
goto nextInstruction;
case OP_RSHU:
opStack[-1] = ((unsigned)r1) >> r0;
opStack--;
goto nextInstruction;
case OP_NEGF:
*(float *)opStack = -*(float *)opStack;
goto nextInstruction;
case OP_ADDF:
*(float *)(opStack-1) = *(float *)(opStack-1) + *(float *)opStack;
opStack--;
goto nextInstruction;
case OP_SUBF:
*(float *)(opStack-1) = *(float *)(opStack-1) - *(float *)opStack;
opStack--;
goto nextInstruction;
case OP_DIVF:
*(float *)(opStack-1) = *(float *)(opStack-1) / *(float *)opStack;
opStack--;
goto nextInstruction;
case OP_MULF:
*(float *)(opStack-1) = *(float *)(opStack-1) * *(float *)opStack;
opStack--;
goto nextInstruction;
case OP_CVIF:
*(float *)opStack = (float)*opStack;
goto nextInstruction;
case OP_CVFI:
*opStack = (int) *(float *)opStack;
goto nextInstruction;
case OP_SEX8:
*opStack = (signed char)*opStack;
goto nextInstruction;
case OP_SEX16:
*opStack = (short)*opStack;
goto nextInstruction;
}
}
done:
vm->currentlyInterpreting = qfalse;
if ( opStack != &stack[1] ) {
Com_Error( ERR_DROP, "Interpreter error: opStack = %i", opStack - stack );
}
vm->programStack = stackOnEntry;
// return the result
return *opStack;
}

182
codemp/qcommon/vm_local.h Normal file
View File

@@ -0,0 +1,182 @@
//rww - so that I may utilize vm debugging features WITHOUT DROPPING TO 0.1FPS
#ifndef _XBOX
#define CRAZY_SYMBOL_MAP
#endif
#ifdef CRAZY_SYMBOL_MAP
#include <map>
#endif
typedef enum {
OP_UNDEF,
OP_IGNORE,
OP_BREAK,
OP_ENTER,
OP_LEAVE,
OP_CALL,
OP_PUSH,
OP_POP,
OP_CONST,
OP_LOCAL,
OP_JUMP,
//-------------------
OP_EQ,
OP_NE,
OP_LTI,
OP_LEI,
OP_GTI,
OP_GEI,
OP_LTU,
OP_LEU,
OP_GTU,
OP_GEU,
OP_EQF,
OP_NEF,
OP_LTF,
OP_LEF,
OP_GTF,
OP_GEF,
//-------------------
OP_LOAD1,
OP_LOAD2,
OP_LOAD4,
OP_STORE1,
OP_STORE2,
OP_STORE4, // *(stack[top-1]) = stack[top]
OP_ARG,
OP_BLOCK_COPY,
//-------------------
OP_SEX8,
OP_SEX16,
OP_NEGI,
OP_ADD,
OP_SUB,
OP_DIVI,
OP_DIVU,
OP_MODI,
OP_MODU,
OP_MULI,
OP_MULU,
OP_BAND,
OP_BOR,
OP_BXOR,
OP_BCOM,
OP_LSH,
OP_RSHI,
OP_RSHU,
OP_NEGF,
OP_ADDF,
OP_SUBF,
OP_DIVF,
OP_MULF,
OP_CVIF,
OP_CVFI
} opcode_t;
typedef int vmptr_t;
typedef struct vmSymbol_s {
struct vmSymbol_s *next;
int symValue;
int profileCount;
char symName[1]; // variable sized
} vmSymbol_t;
#define VM_OFFSET_PROGRAM_STACK 0
#define VM_OFFSET_SYSTEM_CALL 4
struct vm_s {
// DO NOT MOVE OR CHANGE THESE WITHOUT CHANGING THE VM_OFFSET_* DEFINES
// USED BY THE ASM CODE
int programStack; // the vm may be recursively entered
int (*systemCall)( int *parms );
//------------------------------------
char name[MAX_QPATH];
// for dynamic linked modules
void *dllHandle;
int (QDECL *entryPoint)( int callNum, ... );
// for interpreted modules
qboolean currentlyInterpreting;
qboolean compiled;
byte *codeBase;
int codeLength;
int *instructionPointers;
int instructionPointersLength;
byte *dataBase;
int dataMask;
int stackBottom; // if programStack < stackBottom, error
int numSymbols;
struct vmSymbol_s *symbols;
int callLevel; // for debug indenting
int breakFunction; // increment breakCount on function entry to this
int breakCount;
};
#ifdef CRAZY_SYMBOL_MAP
typedef std::map<int, vmSymbol_s*> symbolMap_t;
typedef std::map<vm_t*, symbolMap_t> symbolVMMap_t;
extern symbolVMMap_t g_vmMap;
extern symbolMap_t *g_symbolMap;
/*
Set the symbol map based on the VM currently
being in interpreted. This is done so that we
do not have to do a map lookup for the VM with
each symbol request.
-rww
*/
inline void VM_SetSymbolMap(vm_t *vm)
{
g_symbolMap = &g_vmMap[vm];
}
#endif
extern vm_t *currentVM;
extern int vm_debugLevel;
void VM_Compile( vm_t *vm, vmHeader_t *header );
int VM_CallCompiled( vm_t *vm, int *args );
void VM_PrepareInterpreter( vm_t *vm, vmHeader_t *header );
int VM_CallInterpreted( vm_t *vm, int *args );
vmSymbol_t *VM_ValueToFunctionSymbol( vm_t *vm, int value );
int VM_SymbolToValue( vm_t *vm, const char *symbol );
const char *VM_ValueToSymbol( vm_t *vm, int value );
void VM_LogSyscalls( int *args );

1274
codemp/qcommon/vm_ppc.cpp Normal file

File diff suppressed because it is too large Load Diff

1166
codemp/qcommon/vm_x86.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,342 @@
#include "xb_settings.h"
#include <xtl.h>
#include "../game/q_shared.h"
#include "qcommon.h"
#include "../cgame/cg_local.h"
#include "../client/cl_data.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;
subtitles = 0;
voiceMode = 2;
voiceMask = 0;
appearOffline = 0;
brightness = 6.0f;
}
// 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 )
{
int clNum = ClientManager::ActiveClientNum();
ClientManager::ActiveClient().cg_pitch = invertAim[clNum] ? 0.022f : -0.022f;
Cbuf_ExecuteText( EXEC_APPEND, va("exec cfg/uibuttonConfig%d.cfg\n", buttonMode[clNum]) );
Cbuf_ExecuteText( EXEC_APPEND, va("exec cfg/triggersConfig%d.cfg\n", triggerMode[clNum]) );
// Do both of these, easier than checking:
Cvar_SetValue( "in_useRumble", rumble[0] );
Cvar_SetValue( "in_useRumble2", rumble[1] );
ClientManager::ActiveClient().cg_autolevel = autolevel[clNum];
ClientManager::ActiveClient().cg_autoswitch = autoswitch[clNum];
ClientManager::ActiveClient().cg_sensitivity = sensitivityX[clNum];
ClientManager::ActiveClient().cg_sensitivityY = sensitivityY[clNum];
if( hotswapMP[0] >= 0 )
Cvar_SetValue( "hotswap0", hotswapMP[0] );
else
Cvar_Set( "hotswap0", "" );
if( hotswapMP[1] >= 0 )
Cvar_SetValue( "hotswap1", hotswapMP[1] );
else
Cvar_Set( "hotswap1", "" );
if( hotswapMP[2] >= 0 )
Cvar_SetValue( "hotswap2", hotswapMP[2] );
else
Cvar_Set( "hotswap2", "" );
if( hotswapMP[3] >= 0 )
Cvar_SetValue( "hotswap3", hotswapMP[3] );
else
Cvar_Set( "hotswap3", "" );
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);
// Online options stuff is grabbed when it's needed
}
// 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,83 @@
#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 );
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,832 @@
// Created 3/13/03 by Brian Osman (VV) - Split Zone/Hunk from common
//Anything above this #include will be ignored by the compiler
#include "../qcommon/exe_headers.h"
#include "platform.h"
////////////////////////////////////////////////
//
#ifdef TAGDEF // itu?
#undef TAGDEF
#endif
#define TAGDEF(blah) #blah
const static char *psTagStrings[TAG_COUNT+1]= // +1 because TAG_COUNT will itself become a string here. Oh well.
{
#include "../qcommon/tags.h"
};
//
////////////////////////////////////////////////
static void Z_Details_f(void);
void CIN_CloseAllVideos();
// 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
typedef struct zoneHeader_s
{
int iMagic;
memtag_t eTag;
int iSize;
struct zoneHeader_s *pNext;
struct zoneHeader_s *pPrev;
} 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
void Z_Validate(void)
{
if(!com_validateZone || !com_validateZone->integer)
{
return;
}
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;
}
#endif
if(pMemory->iMagic != ZONE_MAGIC)
{
Com_Error(ERR_FATAL, "Z_Validate(): Corrupt zone header!");
return;
}
if (ZoneTailFromHeader(pMemory)->iMagic != ZONE_MAGIC)
{
Com_Error(ERR_FATAL, "Z_Validate(): Corrupt zone tail!");
return;
}
pMemory = pMemory->pNext;
}
}
// 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)
StaticZeroMem_t gZeroMalloc =
{ {ZONE_MAGIC, TAG_STATIC,0,NULL,NULL},{ZONE_MAGIC}};
StaticMem_t gEmptyString =
{ {ZONE_MAGIC, TAG_STATIC,2,NULL,NULL},'\0','\0',{ZONE_MAGIC}};
StaticMem_t gNumberString[] = {
{ {ZONE_MAGIC, TAG_STATIC,2,NULL,NULL},'0','\0',{ZONE_MAGIC}},
{ {ZONE_MAGIC, TAG_STATIC,2,NULL,NULL},'1','\0',{ZONE_MAGIC}},
{ {ZONE_MAGIC, TAG_STATIC,2,NULL,NULL},'2','\0',{ZONE_MAGIC}},
{ {ZONE_MAGIC, TAG_STATIC,2,NULL,NULL},'3','\0',{ZONE_MAGIC}},
{ {ZONE_MAGIC, TAG_STATIC,2,NULL,NULL},'4','\0',{ZONE_MAGIC}},
{ {ZONE_MAGIC, TAG_STATIC,2,NULL,NULL},'5','\0',{ZONE_MAGIC}},
{ {ZONE_MAGIC, TAG_STATIC,2,NULL,NULL},'6','\0',{ZONE_MAGIC}},
{ {ZONE_MAGIC, TAG_STATIC,2,NULL,NULL},'7','\0',{ZONE_MAGIC}},
{ {ZONE_MAGIC, TAG_STATIC,2,NULL,NULL},'8','\0',{ZONE_MAGIC}},
{ {ZONE_MAGIC, TAG_STATIC,2,NULL,NULL},'9','\0',{ZONE_MAGIC}},
};
qboolean gbMemFreeupOccured = qfalse;
void *Z_Malloc(int iSize, memtag_t eTag, qboolean bZeroit /* = qfalse */, int iUnusedAlign /* = 4 */)
{
gbMemFreeupOccured = qfalse;
if (iSize == 0)
{
zoneHeader_t *pMemory = (zoneHeader_t *) &gZeroMalloc;
return &pMemory[1];
}
// Add in tracking info
//
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...
//
extern qboolean CM_DeleteCachedMap(qboolean bGuaranteedOkToDelete);
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
}
#ifndef DEDICATED
// ditch any image_t's (and associated GL memory) 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
}
#endif
// 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();
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;
}
}
// 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 void Zone_FreeBlock(zoneHeader_t *pMemory)
{
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;
}
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;
}
iAllocCount--;
#endif
}
}
// 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;
}
// Frees a block of memory...
//
void Z_Free(void *pvAddress)
{
if (pvAddress == NULL) // I've put this in as a safety measure because of some bits of #ifdef BSPC stuff -Ste.
{
//Com_Error(ERR_FATAL, "Z_Free(): NULL arg");
return;
}
zoneHeader_t *pMemory = ((zoneHeader_t *)pvAddress) - 1;
if (pMemory->eTag == TAG_STATIC)
{
return;
}
#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;
}
#endif
if (pMemory->iMagic != ZONE_MAGIC)
{
Com_Error(ERR_FATAL, "Z_Free(): Corrupt zone header!");
return;
}
if (ZoneTailFromHeader(pMemory)->iMagic != ZONE_MAGIC)
{
Com_Error(ERR_FATAL, "Z_Free(): Corrupt zone tail!");
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
}
void *S_Malloc( int iSize ) {
return Z_Malloc( iSize, TAG_SMALL );
}
#ifdef _DEBUG
static void Z_MemRecoverTest_f(void)
{
// needs to be in _DEBUG only, not good for final game!
// fixme: findmeste: Remove this sometime
//
int iTotalMalloc = 0;
while (1)
{
int iThisMalloc = 5* (1024 * 1024);
Z_Malloc(iThisMalloc, TAG_SPECIAL_MEM_TEST, qfalse); // and lose, just to consume memory
iTotalMalloc += iThisMalloc;
if (gbMemFreeupOccured)
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 average)\n",
psTagStrings[i],
iThisSize,
iSize,iRemainder,
iThisCount, iThisSize / iThisCount
);
}
}
Com_Printf("---------------------------------------------------------------------------\n");
Z_Stats_f();
}
// Shuts down the zone memory system and frees up all memory
void Com_ShutdownZoneMemory(void)
{
// Com_Printf("Shutting down zone memory .....\n");
Cmd_RemoveCommand("zone_stats");
Cmd_RemoveCommand("zone_details");
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 )
{
memset(&TheZone, 0, sizeof(TheZone));
TheZone.Header.iMagic = ZONE_MAGIC;
//#ifdef _DEBUG
// com_validateZone = Cvar_Get("com_validateZone", "1", 0);
//#else
com_validateZone = Cvar_Get("com_validateZone", "0", 0);
//#endif
Cmd_AddCommand("zone_stats", Z_Stats_f);
Cmd_AddCommand("zone_details", Z_Details_f);
#ifdef _DEBUG
Cmd_AddCommand("zone_memrecovertest", Z_MemRecoverTest_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);
return out;
}
static memtag_t hunk_tag;
/*
===============
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;
// start = Sys_Milliseconds();
Z_Validate();
sum = 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];
}
pMemory = pMemory->pNext;
}
// end = Sys_Milliseconds();
// Com_Printf( "Com_TouchMemory: %i msec\n", end - start );
}
qboolean Com_TheHunkMarkHasBeenMade(void)
{
if (hunk_tag == TAG_HUNK_MARK2)
{
return qtrue;
}
return qfalse;
}
/*
=================
Com_InitHunkMemory
=================
*/
void Com_InitHunkMemory( void ) {
hunk_tag = TAG_HUNK_MARK1;
Hunk_Clear();
}
void Com_ShutdownHunkMemory(void)
{
//Er, ok. Clear it then I guess.
Z_TagFree(TAG_HUNK_MARK1);
Z_TagFree(TAG_HUNK_MARK2);
}
/*
====================
Hunk_MemoryRemaining
====================
*/
int Hunk_MemoryRemaining( void ) {
return (64*1024*1024) - (Z_MemSize(TAG_HUNK_MARK1)+Z_MemSize(TAG_HUNK_MARK2)); //Yeah. Whatever. We've got no size now.
}
/*
===================
Hunk_SetMark
The server calls this after the level and game VM have been loaded
===================
*/
void Hunk_SetMark( void ) {
hunk_tag = TAG_HUNK_MARK2;
}
/*
=================
Hunk_ClearToMark
The client calls this before starting a vid_restart or snd_restart
=================
*/
void Hunk_ClearToMark( void ) {
assert(hunk_tag == TAG_HUNK_MARK2); //if this is not true then no mark has been made
Z_TagFree(TAG_HUNK_MARK2);
}
/*
=================
Hunk_CheckMark
=================
*/
qboolean Hunk_CheckMark( void ) {
//if( hunk_low.mark || hunk_high.mark ) {
if (hunk_tag != TAG_HUNK_MARK1)
{
return qtrue;
}
return qfalse;
}
void CL_ShutdownCGame( void );
void CL_ShutdownUI( void );
void SV_ShutdownGameProgs( void );
/*
=================
Hunk_Clear
The server calls this before shutting down or loading a new map
=================
*/
void R_HunkClearCrap(void);
#ifdef _FULL_G2_LEAK_CHECKING
void G2_DEBUG_ReportLeaks(void);
#endif
void Hunk_Clear( void ) {
#ifndef DEDICATED
CL_ShutdownCGame();
CL_ShutdownUI();
#endif
SV_ShutdownGameProgs();
#ifndef DEDICATED
CIN_CloseAllVideos();
#endif
hunk_tag = TAG_HUNK_MARK1;
Z_TagFree(TAG_HUNK_MARK1);
Z_TagFree(TAG_HUNK_MARK2);
R_HunkClearCrap();
// Com_Printf( "Hunk_Clear: reset the hunk ok\n" );
VM_Clear();
//See if any ghoul2 stuff was leaked, at this point it should be all cleaned up.
#ifdef _FULL_G2_LEAK_CHECKING
assert(g_Ghoul2Allocations == 0 && g_G2ClientAlloc == 0 && g_G2ServerAlloc == 0);
if (g_Ghoul2Allocations)
{
Com_Printf("%i bytes leaked by ghoul2 routines (%i client, %i server)\n", g_Ghoul2Allocations, g_G2ClientAlloc, g_G2ServerAlloc);
G2_DEBUG_ReportLeaks();
}
#endif
}
/*
=================
Hunk_Alloc
Allocate permanent (until the hunk is cleared) memory
=================
*/
void *Hunk_Alloc( int size, ha_pref preference ) {
return Z_Malloc(size, hunk_tag, qtrue);
}
/*
=================
Hunk_AllocateTempMemory
This is used by the file loading system.
Multiple files can be loaded in temporary memory.
When the files-in-use count reaches zero, all temp memory will be deleted
=================
*/
void *Hunk_AllocateTempMemory( int size ) {
// don't bother clearing, because we are going to load a file over it
return Z_Malloc(size, TAG_TEMP_HUNKALLOC, qfalse);
}
/*
==================
Hunk_FreeTempMemory
==================
*/
void Hunk_FreeTempMemory( void *buf )
{
Z_Free(buf);
}
/*
=================
Hunk_ClearTempMemory
The temp space is no longer needed. If we have left more
touched but unused memory on this side, have future
permanent allocs use this side.
=================
*/
void Hunk_ClearTempMemory( void ) {
Z_TagFree(TAG_TEMP_HUNKALLOC);
}