Initial commit.
This commit is contained in:
162
codemp/qcommon/chash.h
Normal file
162
codemp/qcommon/chash.h
Normal 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
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
250
codemp/qcommon/cm_draw.h
Normal 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
|
||||
271
codemp/qcommon/cm_landscape.h
Normal file
271
codemp/qcommon/cm_landscape.h
Normal 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
1184
codemp/qcommon/cm_load.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1155
codemp/qcommon/cm_load_xbox.cpp
Normal file
1155
codemp/qcommon/cm_load_xbox.cpp
Normal file
File diff suppressed because it is too large
Load Diff
310
codemp/qcommon/cm_local.h
Normal file
310
codemp/qcommon/cm_local.h
Normal 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
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
128
codemp/qcommon/cm_patch.h
Normal 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
|
||||
1760
codemp/qcommon/cm_patch_xbox.cpp
Normal file
1760
codemp/qcommon/cm_patch_xbox.cpp
Normal file
File diff suppressed because it is too large
Load Diff
713
codemp/qcommon/cm_polylib.cpp
Normal file
713
codemp/qcommon/cm_polylib.cpp
Normal 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) );
|
||||
}
|
||||
|
||||
|
||||
47
codemp/qcommon/cm_polylib.h
Normal file
47
codemp/qcommon/cm_polylib.h
Normal 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);
|
||||
74
codemp/qcommon/cm_public.h
Normal file
74
codemp/qcommon/cm_public.h
Normal 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);
|
||||
1091
codemp/qcommon/cm_randomterrain.cpp
Normal file
1091
codemp/qcommon/cm_randomterrain.cpp
Normal file
File diff suppressed because it is too large
Load Diff
89
codemp/qcommon/cm_randomterrain.h
Normal file
89
codemp/qcommon/cm_randomterrain.h
Normal 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
|
||||
522
codemp/qcommon/cm_shader.cpp
Normal file
522
codemp/qcommon/cm_shader.cpp
Normal 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
|
||||
1720
codemp/qcommon/cm_terrain.cpp
Normal file
1720
codemp/qcommon/cm_terrain.cpp
Normal file
File diff suppressed because it is too large
Load Diff
497
codemp/qcommon/cm_terrainmap.cpp
Normal file
497
codemp/qcommon/cm_terrainmap.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
83
codemp/qcommon/cm_terrainmap.h
Normal file
83
codemp/qcommon/cm_terrainmap.h
Normal 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
575
codemp/qcommon/cm_test.cpp
Normal 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
2002
codemp/qcommon/cm_trace.cpp
Normal file
File diff suppressed because it is too large
Load Diff
651
codemp/qcommon/cmd_common.cpp
Normal file
651
codemp/qcommon/cmd_common.cpp
Normal 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);
|
||||
}
|
||||
|
||||
165
codemp/qcommon/cmd_console.cpp
Normal file
165
codemp/qcommon/cmd_console.cpp
Normal 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
174
codemp/qcommon/cmd_pc.cpp
Normal 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);
|
||||
}
|
||||
|
||||
97
codemp/qcommon/cnetprofile.cpp
Normal file
97
codemp/qcommon/cnetprofile.cpp
Normal 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
2400
codemp/qcommon/common.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1031
codemp/qcommon/cvar.cpp
Normal file
1031
codemp/qcommon/cvar.cpp
Normal file
File diff suppressed because it is too large
Load Diff
38
codemp/qcommon/disablewarnings.h
Normal file
38
codemp/qcommon/disablewarnings.h
Normal 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
|
||||
3
codemp/qcommon/exe_headers.cpp
Normal file
3
codemp/qcommon/exe_headers.cpp
Normal file
@@ -0,0 +1,3 @@
|
||||
//This file creates the PCH for the rest of the project to use
|
||||
|
||||
#include "../qcommon/exe_headers.h"
|
||||
5
codemp/qcommon/exe_headers.h
Normal file
5
codemp/qcommon/exe_headers.h
Normal 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
158
codemp/qcommon/files.h
Normal 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
|
||||
512
codemp/qcommon/files_common.cpp
Normal file
512
codemp/qcommon/files_common.cpp
Normal 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?
|
||||
}
|
||||
|
||||
1068
codemp/qcommon/files_console.cpp
Normal file
1068
codemp/qcommon/files_console.cpp
Normal file
File diff suppressed because it is too large
Load Diff
3125
codemp/qcommon/files_pc.cpp
Normal file
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
167
codemp/qcommon/fixedmap.h
Normal 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
|
||||
14
codemp/qcommon/game_version.h
Normal file
14
codemp/qcommon/game_version.h
Normal 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
|
||||
1203
codemp/qcommon/genericparser2.cpp
Normal file
1203
codemp/qcommon/genericparser2.cpp
Normal file
File diff suppressed because it is too large
Load Diff
204
codemp/qcommon/genericparser2.h
Normal file
204
codemp/qcommon/genericparser2.h
Normal 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
501
codemp/qcommon/hstring.cpp
Normal 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
229
codemp/qcommon/hstring.h
Normal 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
541
codemp/qcommon/huffman.cpp
Normal 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
|
||||
20
codemp/qcommon/inetprofile.h
Normal file
20
codemp/qcommon/inetprofile.h
Normal 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
296
codemp/qcommon/md4.cpp
Normal 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
57
codemp/qcommon/miniheap.h
Normal 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
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
703
codemp/qcommon/net_chan.cpp
Normal 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
22
codemp/qcommon/platform.h
Normal 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
|
||||
4
codemp/qcommon/q_math.cpp
Normal file
4
codemp/qcommon/q_math.cpp
Normal 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"
|
||||
4
codemp/qcommon/q_shared.cpp
Normal file
4
codemp/qcommon/q_shared.cpp
Normal 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
1158
codemp/qcommon/qcommon.h
Normal file
File diff suppressed because it is too large
Load Diff
607
codemp/qcommon/qfiles.h
Normal file
607
codemp/qcommon/qfiles.h
Normal 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
|
||||
1040
codemp/qcommon/roffsystem.cpp
Normal file
1040
codemp/qcommon/roffsystem.cpp
Normal file
File diff suppressed because it is too large
Load Diff
185
codemp/qcommon/roffsystem.h
Normal file
185
codemp/qcommon/roffsystem.h
Normal 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
725
codemp/qcommon/sparc.h
Normal 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
120
codemp/qcommon/sstring.h
Normal 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 ////////////////////
|
||||
|
||||
985
codemp/qcommon/stringed_ingame.cpp
Normal file
985
codemp/qcommon/stringed_ingame.cpp
Normal 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 //////////////////////////
|
||||
53
codemp/qcommon/stringed_ingame.h
Normal file
53
codemp/qcommon/stringed_ingame.h
Normal 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 ////////////////
|
||||
|
||||
215
codemp/qcommon/stringed_interface.cpp
Normal file
215
codemp/qcommon/stringed_interface.cpp
Normal 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 ///////////////////////
|
||||
|
||||
21
codemp/qcommon/stringed_interface.h
Normal file
21
codemp/qcommon/stringed_interface.h
Normal 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
1776
codemp/qcommon/strip.cpp
Normal file
File diff suppressed because it is too large
Load Diff
312
codemp/qcommon/strip.h
Normal file
312
codemp/qcommon/strip.h
Normal 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
57
codemp/qcommon/tags.h
Normal 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
61
codemp/qcommon/timing.h
Normal 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
1337
codemp/qcommon/unzip.cpp
Normal file
File diff suppressed because it is too large
Load Diff
289
codemp/qcommon/unzip.h
Normal file
289
codemp/qcommon/unzip.h
Normal 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
954
codemp/qcommon/vm.cpp
Normal 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
|
||||
229
codemp/qcommon/vm_console.cpp
Normal file
229
codemp/qcommon/vm_console.cpp
Normal 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; }
|
||||
905
codemp/qcommon/vm_interpreted.cpp
Normal file
905
codemp/qcommon/vm_interpreted.cpp
Normal 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
182
codemp/qcommon/vm_local.h
Normal 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
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
1166
codemp/qcommon/vm_x86.cpp
Normal file
File diff suppressed because it is too large
Load Diff
342
codemp/qcommon/xb_settings.cpp
Normal file
342
codemp/qcommon/xb_settings.cpp
Normal 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;
|
||||
}
|
||||
83
codemp/qcommon/xb_settings.h
Normal file
83
codemp/qcommon/xb_settings.h
Normal 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
|
||||
1899
codemp/qcommon/z_memman_console.cpp
Normal file
1899
codemp/qcommon/z_memman_console.cpp
Normal file
File diff suppressed because it is too large
Load Diff
832
codemp/qcommon/z_memman_pc.cpp
Normal file
832
codemp/qcommon/z_memman_pc.cpp
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user