mirror of
https://github.com/tribes2/engine.git
synced 2026-03-04 13:00:26 +00:00
t2 engine svn checkout
This commit is contained in:
commit
ff569bd2ae
988 changed files with 394180 additions and 0 deletions
137
core/bitMatrix.h
Normal file
137
core/bitMatrix.h
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _BITMATRIX_H_
|
||||
#define _BITMATRIX_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "Platform/platform.h"
|
||||
#endif
|
||||
#ifndef _BITVECTOR_H_
|
||||
#include "Core/bitVector.h"
|
||||
#endif
|
||||
|
||||
class BitMatrix
|
||||
{
|
||||
U32 mWidth;
|
||||
U32 mHeight;
|
||||
U32 mRowByteWidth;
|
||||
|
||||
U8* mBits;
|
||||
U32 mSize;
|
||||
|
||||
BitVector mColFlags;
|
||||
BitVector mRowFlags;
|
||||
|
||||
public:
|
||||
BitMatrix(const U32 width, const U32 height);
|
||||
~BitMatrix();
|
||||
|
||||
void clearAllBits();
|
||||
void setAllBits();
|
||||
|
||||
void setBit(const U32 x, const U32 y);
|
||||
void clearBit(const U32 x, const U32 y);
|
||||
|
||||
// Queries
|
||||
bool isSet(const U32 x, const U32 y) const;
|
||||
|
||||
bool isAnySetCol(const U32 x);
|
||||
bool isAnySetRow(const U32 y);
|
||||
};
|
||||
|
||||
inline BitMatrix::BitMatrix(const U32 width, const U32 height)
|
||||
: mColFlags(width),
|
||||
mRowFlags(height)
|
||||
{
|
||||
AssertFatal(width != 0 && height != 0, "Error, w/h must be non-zero");
|
||||
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
mRowByteWidth = (width + 7) >> 3;
|
||||
|
||||
mSize = mRowByteWidth * mHeight;
|
||||
mBits = new U8[mSize];
|
||||
}
|
||||
|
||||
inline BitMatrix::~BitMatrix()
|
||||
{
|
||||
mWidth = 0;
|
||||
mHeight = 0;
|
||||
mRowByteWidth = 0;
|
||||
mSize = 0;
|
||||
|
||||
delete [] mBits;
|
||||
mBits = NULL;
|
||||
}
|
||||
|
||||
inline void BitMatrix::clearAllBits()
|
||||
{
|
||||
AssertFatal(mBits != NULL, "Error, clearing after deletion");
|
||||
|
||||
dMemset(mBits, 0x00, mSize);
|
||||
mColFlags.clear();
|
||||
mRowFlags.clear();
|
||||
}
|
||||
|
||||
inline void BitMatrix::setAllBits()
|
||||
{
|
||||
AssertFatal(mBits != NULL, "Error, setting after deletion");
|
||||
|
||||
dMemset(mBits, 0xFF, mSize);
|
||||
mColFlags.set();
|
||||
mRowFlags.set();
|
||||
}
|
||||
|
||||
inline void BitMatrix::setBit(const U32 x, const U32 y)
|
||||
{
|
||||
AssertFatal(x < mWidth && y < mHeight, "Error, out of bounds bit!");
|
||||
|
||||
U8* pRow = &mBits[y * mRowByteWidth];
|
||||
|
||||
U8* pByte = &pRow[x >> 3];
|
||||
*pByte |= 1 << (x & 0x7);
|
||||
|
||||
mColFlags.set(x);
|
||||
mRowFlags.set(y);
|
||||
}
|
||||
|
||||
inline void BitMatrix::clearBit(const U32 x, const U32 y)
|
||||
{
|
||||
AssertFatal(x < mWidth && y < mHeight, "Error, out of bounds bit!");
|
||||
|
||||
U8* pRow = &mBits[y * mRowByteWidth];
|
||||
|
||||
U8* pByte = &pRow[x >> 3];
|
||||
*pByte &= ~(1 << (x & 0x7));
|
||||
}
|
||||
|
||||
inline bool BitMatrix::isSet(const U32 x, const U32 y) const
|
||||
{
|
||||
AssertFatal(x < mWidth && y < mHeight, "Error, out of bounds bit!");
|
||||
|
||||
U8* pRow = &mBits[y * mRowByteWidth];
|
||||
|
||||
U8* pByte = &pRow[x >> 3];
|
||||
return (*pByte & (1 << (x & 0x7))) != 0;
|
||||
}
|
||||
|
||||
inline bool BitMatrix::isAnySetCol(const U32 x)
|
||||
{
|
||||
AssertFatal(x < mWidth, "Error, out of bounds column!");
|
||||
|
||||
return mColFlags.test(x);
|
||||
}
|
||||
|
||||
inline bool BitMatrix::isAnySetRow(const U32 y)
|
||||
{
|
||||
AssertFatal(y < mHeight, "Error, out of bounds row!");
|
||||
|
||||
return mRowFlags.test(y);
|
||||
}
|
||||
|
||||
#endif // _H_BITMATRIX_
|
||||
834
core/bitRender.cc
Normal file
834
core/bitRender.cc
Normal file
|
|
@ -0,0 +1,834 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "Core/bitRender.h"
|
||||
|
||||
U32 openTable[32] = { 0xFFFFFFFF,0xFFFFFFFE,0xFFFFFFFC,0xFFFFFFF8,
|
||||
0xFFFFFFF0,0xFFFFFFE0,0xFFFFFFC0,0xFFFFFF80,
|
||||
0xFFFFFF00,0xFFFFFE00,0xFFFFFC00,0xFFFFF800,
|
||||
0xFFFFF000,0xFFFFE000,0xFFFFC000,0xFFFF8000,
|
||||
0xFFFF0000,0xFFFE0000,0xFFFC0000,0xFFF80000,
|
||||
0xFFF00000,0xFFE00000,0xFFC00000,0xFF800000,
|
||||
0xFF000000,0xFE000000,0xFC000000,0xF8000000,
|
||||
0xF0000000,0xE0000000,0xC0000000,0x80000000 };
|
||||
|
||||
U32 closeTable[32] = { 0x00000001,0x00000003,0x00000007,0x0000000F,
|
||||
0x0000001F,0x0000003F,0x0000007F,0x000000FF,
|
||||
0x000001FF,0x000003FF,0x000007FF,0x00000FFF,
|
||||
0x00001FFF,0x00003FFF,0x00007FFF,0x0000FFFF,
|
||||
0x0001FFFF,0x0003FFFF,0x0007FFFF,0x000FFFFF,
|
||||
0x001FFFFF,0x003FFFFF,0x007FFFFF,0x00FFFFFF,
|
||||
0x01FFFFFF,0x03FFFFFF,0x07FFFFFF,0x0FFFFFFF,
|
||||
0x1FFFFFFF,0x3FFFFFFF,0x7FFFFFFF,0xFFFFFFFF };
|
||||
|
||||
struct DrawStruct
|
||||
{
|
||||
S16 start;
|
||||
S16 num;
|
||||
};
|
||||
|
||||
void BitRender::render_strips(const U8 * draw, S32 numDraw, S32 szDraw, const U16 * indices, const Point2I * points, S32 dim, U32 * bits)
|
||||
{
|
||||
const U8 * drawEnd = draw + numDraw*szDraw;
|
||||
|
||||
// loop through strips...
|
||||
for (; draw<drawEnd; draw += szDraw)
|
||||
{
|
||||
const DrawStruct * drawStruct = (DrawStruct*)draw;
|
||||
const U16 * icurrent = indices + drawStruct->start;
|
||||
const U16 * iend = icurrent + drawStruct->num;
|
||||
|
||||
const Point2I * vv0 = points + *(icurrent++);
|
||||
const Point2I * vv1;
|
||||
const Point2I * vv2 = points + *(icurrent++);
|
||||
const Point2I **nextPt = &vv1;
|
||||
|
||||
while (icurrent<iend)
|
||||
{
|
||||
*nextPt = vv2;
|
||||
nextPt = (const Point2I**)( (S32)nextPt ^ (S32)&vv0 ^ (S32)&vv1 );
|
||||
vv2 = points + *(icurrent++);
|
||||
|
||||
// skip degenerate triangles...
|
||||
if (vv0==vv1 || vv1==vv2 || vv2==vv0)
|
||||
continue;
|
||||
// following copied from BitRender::render...indented to highlight this fact, no function to indentation
|
||||
{
|
||||
const Point2I * v0 = vv0;
|
||||
const Point2I * v1 = vv1;
|
||||
const Point2I * v2 = vv2;
|
||||
|
||||
AssertFatal( v0->x>=0 && v0->x<dim && v0->y>=0 && v0->y<dim,"BitRender::render: v0 out of range");
|
||||
AssertFatal( v1->x>=0 && v1->x<dim && v1->y>=0 && v1->y<dim,"BitRender::render: v1 out of range");
|
||||
AssertFatal( v2->x>=0 && v2->x<dim && v2->y>=0 && v2->y<dim,"BitRender::render: v2 out of range");
|
||||
|
||||
// back-face cull
|
||||
if ((v0->x-v1->x)*(v2->y-v1->y) > (v0->y-v1->y)*(v2->x-v1->x))
|
||||
continue;
|
||||
|
||||
// rotate so that v0 holds topmost y coord
|
||||
// Note: The particular inequalities used ( <=, <, then <=) are important.
|
||||
// They guarantee that if there are two verts on the top row, that
|
||||
// they will be vert v0 and v1 (also could be three).
|
||||
if (v1->y <= v2->y)
|
||||
{
|
||||
if (v1->y < v0->y)
|
||||
{
|
||||
const Point2I * tmp = v0;
|
||||
v0 = v1;
|
||||
v1 = v2;
|
||||
v2 = tmp;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (v2->y <= v0->y)
|
||||
{
|
||||
const Point2I * tmp = v0;
|
||||
v0 = v2;
|
||||
v2 = v1;
|
||||
v1 = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
// all the control variables we have to set up...
|
||||
S32 leftDeltaY, xLeftInc, xLeftErrInc, xLeftDir, xLeft, xLeftErr = 0;
|
||||
S32 rightDeltaY, xRightInc, xRightErrInc, xRightDir, xRight, xRightErr = 0;
|
||||
S32 * changeThisDelta;
|
||||
S32 * changeThisInc;
|
||||
S32 * changeThisErrInc;
|
||||
S32 * changeThisDir;
|
||||
S32 toThisDelta;
|
||||
S32 toThisInc;
|
||||
S32 toThisErrInc;
|
||||
S32 toThisDir;
|
||||
S32 ySwitch;
|
||||
S32 yBottom;
|
||||
|
||||
// check for special case where top row holds two verts
|
||||
if (v0->y==v1->y)
|
||||
{
|
||||
leftDeltaY = v2->y-v0->y;
|
||||
rightDeltaY = v2->y-v1->y;
|
||||
ySwitch = v2->y;
|
||||
yBottom = v2->y;
|
||||
|
||||
if (v1->y==v2->y)
|
||||
{
|
||||
// special-special case where top row holds all three verts
|
||||
xLeft = getMin(getMin(v0->x,v1->x),v2->x);
|
||||
xRight = getMax(getMax(v0->x,v1->x),v2->x);
|
||||
xLeftErrInc = xRightErrInc = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// standard-special case...v2 on different row from v0 and v1
|
||||
xLeft = v0->x;
|
||||
xLeftInc = (v2->x-v0->x) / leftDeltaY;
|
||||
xLeftErrInc = (v2->x-v0->x) - leftDeltaY*xLeftInc;
|
||||
xLeftDir = v2->x-v0->x > 0 ? 1 : -1;
|
||||
|
||||
xRight = v1->x;
|
||||
xRightInc = (v2->x-v1->x) / rightDeltaY;
|
||||
xRightErrInc = (v2->x-v1->x) - rightDeltaY*xRightInc;
|
||||
xRightDir = v2->x-v1->x > 0 ? 1 : -1;
|
||||
}
|
||||
|
||||
// set these variables to avoid a crash...
|
||||
changeThisDelta = &toThisDelta;
|
||||
changeThisInc = &toThisInc;
|
||||
changeThisErrInc = &toThisErrInc;
|
||||
changeThisDir = &toThisDir;
|
||||
}
|
||||
else
|
||||
{
|
||||
leftDeltaY = v2->y-v0->y;
|
||||
xLeftInc = (v2->x-v0->x) / leftDeltaY;
|
||||
xLeftErrInc = (v2->x-v0->x) - leftDeltaY*xLeftInc;
|
||||
xLeftDir = v2->x-v0->x > 0 ? 1 : -1;
|
||||
|
||||
rightDeltaY = v1->y-v0->y;
|
||||
xRightInc = (v1->x-v0->x) / rightDeltaY;
|
||||
xRightErrInc = (v1->x-v0->x) - rightDeltaY*xRightInc;
|
||||
xRightDir = v1->x-v0->x > 0 ? 1 : -1;
|
||||
|
||||
xLeft = xRight = v0->x;
|
||||
|
||||
if (v1->y<v2->y)
|
||||
{
|
||||
// right edge bends
|
||||
changeThisDelta = &rightDeltaY;
|
||||
changeThisInc = &xRightInc;
|
||||
changeThisErrInc = &xRightErrInc;
|
||||
changeThisDir = &xRightDir;
|
||||
toThisDelta = v2->y-v1->y;
|
||||
toThisInc = (v2->x-v1->x) / toThisDelta;
|
||||
toThisErrInc = (v2->x-v1->x) - toThisDelta * toThisInc;
|
||||
toThisDir = v2->x-v1->x > 0 ? 1 : -1;
|
||||
ySwitch = v1->y-1;
|
||||
yBottom = v2->y;
|
||||
}
|
||||
else
|
||||
{
|
||||
// left edge bends
|
||||
changeThisDelta = &leftDeltaY;
|
||||
changeThisInc = &xLeftInc;
|
||||
changeThisErrInc = &xLeftErrInc;
|
||||
changeThisDir = &xLeftDir;
|
||||
toThisDelta = v1->y-v2->y;
|
||||
toThisInc = toThisDelta ? (v1->x-v2->x) / toThisDelta : 0;
|
||||
toThisErrInc = toThisDelta ? (v1->x-v2->x) - toThisDelta * toThisInc : 0;
|
||||
toThisDir = v1->x-v2->x > 0 ? 1 : -1;
|
||||
ySwitch = v2->y-1;
|
||||
yBottom = v1->y;
|
||||
}
|
||||
}
|
||||
xLeftErrInc *= xLeftDir;
|
||||
xRightErrInc *= xRightDir;
|
||||
toThisErrInc *= toThisDir;
|
||||
|
||||
U32 * rowStart = bits + v0->y * (dim>>5);
|
||||
for (S32 y=v0->y; y<=yBottom;)
|
||||
{
|
||||
do
|
||||
{
|
||||
AssertFatal(xLeft<=xRight,"BitRender::render");
|
||||
|
||||
U32 open = openTable[xLeft&31];
|
||||
U32 close = closeTable[xRight&31];
|
||||
if ( (xLeft^xRight) & ~31)
|
||||
{
|
||||
U32 * x = rowStart+(xLeft>>5);
|
||||
*x |= open;
|
||||
U32 * xEnd = rowStart+(xRight>>5);
|
||||
while (++x<xEnd)
|
||||
*x |= 0xFFFFFFFF;
|
||||
*x |= close;
|
||||
}
|
||||
else
|
||||
rowStart[xLeft>>5] |= open & close;
|
||||
|
||||
xLeft += xLeftInc;
|
||||
xLeftErr += xLeftErrInc;
|
||||
if (xLeftErr >= leftDeltaY)
|
||||
{
|
||||
xLeft += xLeftDir;
|
||||
xLeftErr -= leftDeltaY;
|
||||
}
|
||||
|
||||
xRight += xRightInc;
|
||||
xRightErr += xRightErrInc;
|
||||
if (xRightErr >= rightDeltaY)
|
||||
{
|
||||
xRight += xRightDir;
|
||||
xRightErr -= rightDeltaY;
|
||||
}
|
||||
|
||||
rowStart += dim>>5;
|
||||
|
||||
} while (y++<ySwitch);
|
||||
|
||||
ySwitch=yBottom; // get here at most twice
|
||||
*changeThisDelta = toThisDelta;
|
||||
*changeThisInc = toThisInc;
|
||||
*changeThisErrInc = toThisErrInc;
|
||||
*changeThisDir = toThisDir;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BitRender::render_tris(const U8 * draw, S32 numDraw, S32 szDraw, const U16 * indices, const Point2I * points, S32 dim, U32 * bits)
|
||||
{
|
||||
const U8 * drawEnd = draw + numDraw*szDraw;
|
||||
|
||||
// loop through strips...
|
||||
for (; draw<drawEnd; draw += szDraw)
|
||||
{
|
||||
const DrawStruct * drawStruct = (DrawStruct*)draw;
|
||||
const U16 * icurrent = indices + drawStruct->start;
|
||||
const U16 * iend = icurrent + drawStruct->num;
|
||||
|
||||
while (icurrent<iend)
|
||||
{
|
||||
const Point2I * v0 = points + *(icurrent++);
|
||||
const Point2I * v1 = points + *(icurrent++);
|
||||
const Point2I * v2 = points + *(icurrent++);
|
||||
|
||||
// following copied from BitRender::render...indented to highlight this fact, no function to indentation
|
||||
{
|
||||
AssertFatal( v0->x>=0 && v0->x<dim && v0->y>=0 && v0->y<dim,"BitRender::render: v0 out of range");
|
||||
AssertFatal( v1->x>=0 && v1->x<dim && v1->y>=0 && v1->y<dim,"BitRender::render: v1 out of range");
|
||||
AssertFatal( v2->x>=0 && v2->x<dim && v2->y>=0 && v2->y<dim,"BitRender::render: v2 out of range");
|
||||
|
||||
// back-face cull
|
||||
if ((v0->x-v1->x)*(v2->y-v1->y) > (v0->y-v1->y)*(v2->x-v1->x))
|
||||
return;
|
||||
|
||||
// rotate so that v0 holds topmost y coord
|
||||
// Note: The particular inequalities used ( <=, <, then <=) are important.
|
||||
// They guarantee that if there are two verts on the top row, that
|
||||
// they will be vert v0 and v1 (also could be three).
|
||||
if (v1->y <= v2->y)
|
||||
{
|
||||
if (v1->y < v0->y)
|
||||
{
|
||||
const Point2I * tmp = v0;
|
||||
v0 = v1;
|
||||
v1 = v2;
|
||||
v2 = tmp;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (v2->y <= v0->y)
|
||||
{
|
||||
const Point2I * tmp = v0;
|
||||
v0 = v2;
|
||||
v2 = v1;
|
||||
v1 = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
// all the control variables we have to set up...
|
||||
S32 leftDeltaY, xLeftInc, xLeftErrInc, xLeftDir, xLeft, xLeftErr = 0;
|
||||
S32 rightDeltaY, xRightInc, xRightErrInc, xRightDir, xRight, xRightErr = 0;
|
||||
S32 * changeThisDelta;
|
||||
S32 * changeThisInc;
|
||||
S32 * changeThisErrInc;
|
||||
S32 * changeThisDir;
|
||||
S32 toThisDelta;
|
||||
S32 toThisInc;
|
||||
S32 toThisErrInc;
|
||||
S32 toThisDir;
|
||||
S32 ySwitch;
|
||||
S32 yBottom;
|
||||
|
||||
// check for special case where top row holds two verts
|
||||
if (v0->y==v1->y)
|
||||
{
|
||||
leftDeltaY = v2->y-v0->y;
|
||||
rightDeltaY = v2->y-v1->y;
|
||||
ySwitch = v2->y;
|
||||
yBottom = v2->y;
|
||||
|
||||
if (v1->y==v2->y)
|
||||
{
|
||||
// special-special case where top row holds all three verts
|
||||
xLeft = getMin(getMin(v0->x,v1->x),v2->x);
|
||||
xRight = getMax(getMax(v0->x,v1->x),v2->x);
|
||||
xLeftErrInc = xRightErrInc = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// standard-special case...v2 on different row from v0 and v1
|
||||
xLeft = v0->x;
|
||||
xLeftInc = (v2->x-v0->x) / leftDeltaY;
|
||||
xLeftErrInc = (v2->x-v0->x) - leftDeltaY*xLeftInc;
|
||||
xLeftDir = v2->x-v0->x > 0 ? 1 : -1;
|
||||
|
||||
xRight = v1->x;
|
||||
xRightInc = (v2->x-v1->x) / rightDeltaY;
|
||||
xRightErrInc = (v2->x-v1->x) - rightDeltaY*xRightInc;
|
||||
xRightDir = v2->x-v1->x > 0 ? 1 : -1;
|
||||
}
|
||||
|
||||
// set these variables to avoid a crash...
|
||||
changeThisDelta = &toThisDelta;
|
||||
changeThisInc = &toThisInc;
|
||||
changeThisErrInc = &toThisErrInc;
|
||||
changeThisDir = &toThisDir;
|
||||
}
|
||||
else
|
||||
{
|
||||
leftDeltaY = v2->y-v0->y;
|
||||
xLeftInc = (v2->x-v0->x) / leftDeltaY;
|
||||
xLeftErrInc = (v2->x-v0->x) - leftDeltaY*xLeftInc;
|
||||
xLeftDir = v2->x-v0->x > 0 ? 1 : -1;
|
||||
|
||||
rightDeltaY = v1->y-v0->y;
|
||||
xRightInc = (v1->x-v0->x) / rightDeltaY;
|
||||
xRightErrInc = (v1->x-v0->x) - rightDeltaY*xRightInc;
|
||||
xRightDir = v1->x-v0->x > 0 ? 1 : -1;
|
||||
|
||||
xLeft = xRight = v0->x;
|
||||
|
||||
if (v1->y<v2->y)
|
||||
{
|
||||
// right edge bends
|
||||
changeThisDelta = &rightDeltaY;
|
||||
changeThisInc = &xRightInc;
|
||||
changeThisErrInc = &xRightErrInc;
|
||||
changeThisDir = &xRightDir;
|
||||
toThisDelta = v2->y-v1->y;
|
||||
toThisInc = (v2->x-v1->x) / toThisDelta;
|
||||
toThisErrInc = (v2->x-v1->x) - toThisDelta * toThisInc;
|
||||
toThisDir = v2->x-v1->x > 0 ? 1 : -1;
|
||||
ySwitch = v1->y-1;
|
||||
yBottom = v2->y;
|
||||
}
|
||||
else
|
||||
{
|
||||
// left edge bends
|
||||
changeThisDelta = &leftDeltaY;
|
||||
changeThisInc = &xLeftInc;
|
||||
changeThisErrInc = &xLeftErrInc;
|
||||
changeThisDir = &xLeftDir;
|
||||
toThisDelta = v1->y-v2->y;
|
||||
toThisInc = toThisDelta ? (v1->x-v2->x) / toThisDelta : 0;
|
||||
toThisErrInc = toThisDelta ? (v1->x-v2->x) - toThisDelta * toThisInc : 0;
|
||||
toThisDir = v1->x-v2->x > 0 ? 1 : -1;
|
||||
ySwitch = v2->y-1;
|
||||
yBottom = v1->y;
|
||||
}
|
||||
}
|
||||
xLeftErrInc *= xLeftDir;
|
||||
xRightErrInc *= xRightDir;
|
||||
toThisErrInc *= toThisDir;
|
||||
|
||||
U32 * rowStart = bits + v0->y * (dim>>5);
|
||||
for (S32 y=v0->y; y<=yBottom;)
|
||||
{
|
||||
do
|
||||
{
|
||||
AssertFatal(xLeft<=xRight,"BitRender::render");
|
||||
|
||||
U32 open = openTable[xLeft&31];
|
||||
U32 close = closeTable[xRight&31];
|
||||
if ( (xLeft^xRight) & ~31)
|
||||
{
|
||||
U32 * x = rowStart+(xLeft>>5);
|
||||
*x |= open;
|
||||
U32 * xEnd = rowStart+(xRight>>5);
|
||||
while (++x<xEnd)
|
||||
*x |= 0xFFFFFFFF;
|
||||
*x |= close;
|
||||
}
|
||||
else
|
||||
rowStart[xLeft>>5] |= open & close;
|
||||
|
||||
xLeft += xLeftInc;
|
||||
xLeftErr += xLeftErrInc;
|
||||
if (xLeftErr >= leftDeltaY)
|
||||
{
|
||||
xLeft += xLeftDir;
|
||||
xLeftErr -= leftDeltaY;
|
||||
}
|
||||
|
||||
xRight += xRightInc;
|
||||
xRightErr += xRightErrInc;
|
||||
if (xRightErr >= rightDeltaY)
|
||||
{
|
||||
xRight += xRightDir;
|
||||
xRightErr -= rightDeltaY;
|
||||
}
|
||||
|
||||
rowStart += dim>>5;
|
||||
|
||||
} while (y++<ySwitch);
|
||||
|
||||
ySwitch=yBottom; // get here at most twice
|
||||
*changeThisDelta = toThisDelta;
|
||||
*changeThisInc = toThisInc;
|
||||
*changeThisErrInc = toThisErrInc;
|
||||
*changeThisDir = toThisDir;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// assumes coords are in range
|
||||
void BitRender::render(const Point2I * v0, const Point2I * v1, const Point2I * v2, S32 dim, U32 * bits)
|
||||
{
|
||||
AssertFatal( v0->x>=0 && v0->x<dim && v0->y>=0 && v0->y<dim,"BitRender::render: v0 out of range");
|
||||
AssertFatal( v1->x>=0 && v1->x<dim && v1->y>=0 && v1->y<dim,"BitRender::render: v1 out of range");
|
||||
AssertFatal( v2->x>=0 && v2->x<dim && v2->y>=0 && v2->y<dim,"BitRender::render: v2 out of range");
|
||||
|
||||
// back-face cull
|
||||
if ((v0->x-v1->x)*(v2->y-v1->y) > (v0->y-v1->y)*(v2->x-v1->x))
|
||||
return;
|
||||
|
||||
// rotate so that v0 holds topmost y coord
|
||||
// Note: The particular inequalities used ( <=, <, then <=) are important.
|
||||
// They guarantee that if there are two verts on the top row, that
|
||||
// they will be vert v0 and v1 (also could be three).
|
||||
if (v1->y <= v2->y)
|
||||
{
|
||||
if (v1->y < v0->y)
|
||||
{
|
||||
const Point2I * tmp = v0;
|
||||
v0 = v1;
|
||||
v1 = v2;
|
||||
v2 = tmp;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (v2->y <= v0->y)
|
||||
{
|
||||
const Point2I * tmp = v0;
|
||||
v0 = v2;
|
||||
v2 = v1;
|
||||
v1 = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
// all the control variables we have to set up...
|
||||
S32 leftDeltaY, xLeftInc, xLeftErrInc, xLeftDir, xLeft, xLeftErr = 0;
|
||||
S32 rightDeltaY, xRightInc, xRightErrInc, xRightDir, xRight, xRightErr = 0;
|
||||
S32 * changeThisDelta;
|
||||
S32 * changeThisInc;
|
||||
S32 * changeThisErrInc;
|
||||
S32 * changeThisDir;
|
||||
S32 toThisDelta;
|
||||
S32 toThisInc;
|
||||
S32 toThisErrInc;
|
||||
S32 toThisDir;
|
||||
S32 ySwitch;
|
||||
S32 yBottom;
|
||||
|
||||
// check for special case where top row holds two verts
|
||||
if (v0->y==v1->y)
|
||||
{
|
||||
leftDeltaY = v2->y-v0->y;
|
||||
rightDeltaY = v2->y-v1->y;
|
||||
ySwitch = v2->y;
|
||||
yBottom = v2->y;
|
||||
|
||||
if (v1->y==v2->y)
|
||||
{
|
||||
// special-special case where top row holds all three verts
|
||||
xLeft = getMin(getMin(v0->x,v1->x),v2->x);
|
||||
xRight = getMax(getMax(v0->x,v1->x),v2->x);
|
||||
xLeftErrInc = xRightErrInc = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// standard-special case...v2 on different row from v0 and v1
|
||||
xLeft = v0->x;
|
||||
xLeftInc = (v2->x-v0->x) / leftDeltaY;
|
||||
xLeftErrInc = (v2->x-v0->x) - leftDeltaY*xLeftInc;
|
||||
xLeftDir = v2->x-v0->x > 0 ? 1 : -1;
|
||||
|
||||
xRight = v1->x;
|
||||
xRightInc = (v2->x-v1->x) / rightDeltaY;
|
||||
xRightErrInc = (v2->x-v1->x) - rightDeltaY*xRightInc;
|
||||
xRightDir = v2->x-v1->x > 0 ? 1 : -1;
|
||||
}
|
||||
|
||||
// set these variables to avoid a crash...
|
||||
changeThisDelta = &toThisDelta;
|
||||
changeThisInc = &toThisInc;
|
||||
changeThisErrInc = &toThisErrInc;
|
||||
changeThisDir = &toThisDir;
|
||||
}
|
||||
else
|
||||
{
|
||||
leftDeltaY = v2->y-v0->y;
|
||||
xLeftInc = (v2->x-v0->x) / leftDeltaY;
|
||||
xLeftErrInc = (v2->x-v0->x) - leftDeltaY*xLeftInc;
|
||||
xLeftDir = v2->x-v0->x > 0 ? 1 : -1;
|
||||
|
||||
rightDeltaY = v1->y-v0->y;
|
||||
xRightInc = (v1->x-v0->x) / rightDeltaY;
|
||||
xRightErrInc = (v1->x-v0->x) - rightDeltaY*xRightInc;
|
||||
xRightDir = v1->x-v0->x > 0 ? 1 : -1;
|
||||
|
||||
xLeft = xRight = v0->x;
|
||||
|
||||
if (v1->y<v2->y)
|
||||
{
|
||||
// right edge bends
|
||||
changeThisDelta = &rightDeltaY;
|
||||
changeThisInc = &xRightInc;
|
||||
changeThisErrInc = &xRightErrInc;
|
||||
changeThisDir = &xRightDir;
|
||||
toThisDelta = v2->y-v1->y;
|
||||
toThisInc = (v2->x-v1->x) / toThisDelta;
|
||||
toThisErrInc = (v2->x-v1->x) - toThisDelta * toThisInc;
|
||||
toThisDir = v2->x-v1->x > 0 ? 1 : -1;
|
||||
ySwitch = v1->y-1;
|
||||
yBottom = v2->y;
|
||||
}
|
||||
else
|
||||
{
|
||||
// left edge bends
|
||||
changeThisDelta = &leftDeltaY;
|
||||
changeThisInc = &xLeftInc;
|
||||
changeThisErrInc = &xLeftErrInc;
|
||||
changeThisDir = &xLeftDir;
|
||||
toThisDelta = v1->y-v2->y;
|
||||
toThisInc = toThisDelta ? (v1->x-v2->x) / toThisDelta : 0;
|
||||
toThisErrInc = toThisDelta ? (v1->x-v2->x) - toThisDelta * toThisInc : 0;
|
||||
toThisDir = v1->x-v2->x > 0 ? 1 : -1;
|
||||
ySwitch = v2->y-1;
|
||||
yBottom = v1->y;
|
||||
}
|
||||
}
|
||||
xLeftErrInc *= xLeftDir;
|
||||
xRightErrInc *= xRightDir;
|
||||
toThisErrInc *= toThisDir;
|
||||
|
||||
U32 * rowStart = bits + v0->y * (dim>>5);
|
||||
for (S32 y=v0->y; y<=yBottom;)
|
||||
{
|
||||
do
|
||||
{
|
||||
AssertFatal(xLeft<=xRight,"BitRender::render");
|
||||
|
||||
U32 open = openTable[xLeft&31];
|
||||
U32 close = closeTable[xRight&31];
|
||||
if ( (xLeft^xRight) & ~31)
|
||||
{
|
||||
U32 * x = rowStart+(xLeft>>5);
|
||||
*x |= open;
|
||||
U32 * xEnd = rowStart+(xRight>>5);
|
||||
while (++x<xEnd)
|
||||
*x |= 0xFFFFFFFF;
|
||||
*x |= close;
|
||||
}
|
||||
else
|
||||
rowStart[xLeft>>5] |= open & close;
|
||||
|
||||
xLeft += xLeftInc;
|
||||
xLeftErr += xLeftErrInc;
|
||||
if (xLeftErr >= leftDeltaY)
|
||||
{
|
||||
xLeft += xLeftDir;
|
||||
xLeftErr -= leftDeltaY;
|
||||
}
|
||||
|
||||
xRight += xRightInc;
|
||||
xRightErr += xRightErrInc;
|
||||
if (xRightErr >= rightDeltaY)
|
||||
{
|
||||
xRight += xRightDir;
|
||||
xRightErr -= rightDeltaY;
|
||||
}
|
||||
|
||||
rowStart += dim>>5;
|
||||
|
||||
} while (y++<ySwitch);
|
||||
|
||||
ySwitch=yBottom; // get here at most twice
|
||||
*changeThisDelta = toThisDelta;
|
||||
*changeThisInc = toThisInc;
|
||||
*changeThisErrInc = toThisErrInc;
|
||||
*changeThisDir = toThisDir;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
U8 bitTable[16][4] =
|
||||
{
|
||||
{ 0, 0, 0, 0}, // 0
|
||||
{255, 0, 0, 0}, // 1
|
||||
{ 0,255, 0, 0}, // 2
|
||||
{255,255, 0, 0}, // 3
|
||||
{ 0, 0,255, 0}, // 4
|
||||
{255, 0,255, 0}, // 5
|
||||
{ 0,255,255, 0}, // 6
|
||||
{255,255,255, 0}, // 7
|
||||
{ 0, 0, 0,255}, // 8
|
||||
{255, 0, 0,255}, // 9
|
||||
{ 0,255, 0,255}, // 10
|
||||
{255,255, 0,255}, // 11
|
||||
{ 0, 0,255,255}, // 12
|
||||
{255, 0,255,255}, // 13
|
||||
{ 0,255,255,255}, // 14
|
||||
{255,255,255,255}, // 15
|
||||
};
|
||||
|
||||
void BitRender::bitTo8Bit(U32 * bits, U32 * eightBits, S32 dim)
|
||||
{
|
||||
dim *= dim>>5;
|
||||
for (S32 i=0; i<dim; i++)
|
||||
{
|
||||
U32 val = *bits++;
|
||||
|
||||
*eightBits++ = *(U32*)&bitTable[val&0xF];
|
||||
val >>= 4;
|
||||
|
||||
*eightBits++ = *(U32*)&bitTable[val&0xF];
|
||||
val >>= 4;
|
||||
|
||||
*eightBits++ = *(U32*)&bitTable[val&0xF];
|
||||
val >>= 4;
|
||||
|
||||
*eightBits++ = *(U32*)&bitTable[val&0xF];
|
||||
val >>= 4;
|
||||
|
||||
*eightBits++ = *(U32*)&bitTable[val&0xF];
|
||||
val >>= 4;
|
||||
|
||||
*eightBits++ = *(U32*)&bitTable[val&0xF];
|
||||
val >>= 4;
|
||||
|
||||
*eightBits++ = *(U32*)&bitTable[val&0xF];
|
||||
val >>= 4;
|
||||
|
||||
*eightBits++ = *(U32*)&bitTable[val&0xF];
|
||||
val >>= 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
U8 bitTableA[16][4] =
|
||||
{
|
||||
{ 0, 0, 0, 0}, // 0
|
||||
{ 0, 0, 0, 0}, // 1
|
||||
{ 0, 0, 0, 0}, // 2
|
||||
{ 0, 0, 0, 0}, // 3
|
||||
{ 0, 0, 0, 0}, // 4
|
||||
{ 0, 0, 0, 0}, // 5
|
||||
{ 0, 0, 0, 0}, // 6
|
||||
{ 0, 0, 0, 0}, // 7
|
||||
{ 17, 0, 0, 0}, // 8
|
||||
{ 17, 0, 0, 0}, // 9
|
||||
{ 17, 0, 0, 0}, // 10
|
||||
{ 17, 0, 0, 0}, // 11
|
||||
{ 17, 0, 0, 0}, // 12
|
||||
{ 17, 0, 0, 0}, // 13
|
||||
{ 17, 0, 0, 0}, // 14
|
||||
{ 17, 0, 0, 0}, // 15
|
||||
};
|
||||
|
||||
U8 bitTableB[16][4] =
|
||||
{
|
||||
{ 0, 0, 0, 0}, // 0
|
||||
{ 34, 17, 0, 0}, // 1
|
||||
{ 17, 34, 17, 0}, // 2
|
||||
{ 51, 51, 17, 0}, // 3
|
||||
{ 0, 17, 34, 17}, // 4
|
||||
{ 34, 34, 34, 17}, // 5
|
||||
{ 17, 51, 51, 17}, // 6
|
||||
{ 51, 68, 51, 17}, // 7
|
||||
{ 0, 0, 17, 34}, // 8
|
||||
{ 34, 17, 17, 34}, // 9
|
||||
{ 17, 34, 34, 34}, // 10
|
||||
{ 51, 51, 34, 34}, // 11
|
||||
{ 0, 17, 51, 51}, // 12
|
||||
{ 34, 34, 51, 51}, // 13
|
||||
{ 17, 51, 68, 51}, // 14
|
||||
{ 51, 68, 68, 51}, // 15
|
||||
};
|
||||
|
||||
|
||||
U8 bitTableC[16][4] =
|
||||
{
|
||||
{ 0, 0, 0, 0}, // 0
|
||||
{ 0, 0, 0, 17}, // 1
|
||||
{ 0, 0, 0, 0}, // 2
|
||||
{ 0, 0, 0, 17}, // 3
|
||||
{ 0, 0, 0, 0}, // 4
|
||||
{ 0, 0, 0, 17}, // 5
|
||||
{ 0, 0, 0, 0}, // 6
|
||||
{ 0, 0, 0, 17}, // 7
|
||||
{ 0, 0, 0, 0}, // 8
|
||||
{ 0, 0, 0, 17}, // 9
|
||||
{ 0, 0, 0, 0}, // 10
|
||||
{ 0, 0, 0, 17}, // 11
|
||||
{ 0, 0, 0, 0}, // 12
|
||||
{ 0, 0, 0, 17}, // 13
|
||||
{ 0, 0, 0, 0}, // 14
|
||||
{ 0, 0, 0, 17}, // 15
|
||||
};
|
||||
|
||||
U8 bitTableE[16][4] =
|
||||
{
|
||||
{ 0, 0, 0, 0}, // 0
|
||||
{ 51, 34, 0, 0}, // 1
|
||||
{ 34, 51, 34, 0}, // 2
|
||||
{ 85, 85, 34, 0}, // 3
|
||||
{ 0, 34, 51, 34}, // 4
|
||||
{ 51, 68, 51, 34}, // 5
|
||||
{ 34, 85, 85, 34}, // 6
|
||||
{ 85,119, 85, 34}, // 7
|
||||
{ 0, 0, 34, 51}, // 8
|
||||
{ 51, 34, 34, 51}, // 9
|
||||
{ 34, 51, 68, 51}, // 10
|
||||
{ 85, 85, 68, 51}, // 11
|
||||
{ 0, 34, 85, 85}, // 12
|
||||
{ 51, 68, 85, 85}, // 13
|
||||
{ 34, 85,119, 85}, // 14
|
||||
{ 85,119,119, 85}, // 15
|
||||
};
|
||||
|
||||
void BitRender::bitTo8Bit_3(U32 * bits, U32 * eightBits, S32 dim)
|
||||
{
|
||||
// clear out first row of dest
|
||||
U32 * end32 = eightBits + (dim>>2);
|
||||
do { *eightBits++=0; } while (eightBits<end32);
|
||||
end32 += (dim>>2)*(dim-1);
|
||||
|
||||
U8 * p0 = (U8*)bits;
|
||||
U8 bitLo10 = 0x0F & *p0;
|
||||
U8 bitHi10 = (*p0) >> 4;
|
||||
p0++;
|
||||
|
||||
U8 * p1 = (U8*)bits + (dim>>3);
|
||||
U8 bitLo11 = 0x0F & *p1;
|
||||
U8 bitHi11 = (*p1) >> 4;
|
||||
p1++;
|
||||
|
||||
U8 * p2 = (U8*)bits + (dim>>2);
|
||||
U8 bitLo12 = 0x0F & *p2;
|
||||
U8 bitHi12 = (*p2) >> 4;
|
||||
p2++;
|
||||
|
||||
U8 bitLo20, bitHi20;
|
||||
U8 bitLo21, bitHi21;
|
||||
U8 bitLo22, bitHi22;
|
||||
U8 bitHi00 = 0;
|
||||
U8 bitHi01 = 0;
|
||||
U8 bitHi02 = 0;
|
||||
|
||||
// go thru penultimate row (but stop before last entry in that row)
|
||||
U8 * end = (U8*)bits + dim*(dim>>3) - 1;
|
||||
do
|
||||
{
|
||||
bitLo20 = 0x0F & *p0;
|
||||
bitHi20 = (*p0) >> 4;
|
||||
bitLo21 = 0x0F & *p1;
|
||||
bitHi21 = (*p1) >> 4;
|
||||
bitLo22 = 0x0F & *p2;
|
||||
bitHi22 = (*p2) >> 4;
|
||||
|
||||
*eightBits++ = *(U32*)&bitTableA[bitHi00] + *(U32*)&bitTableB[bitLo10] + *(U32*)&bitTableC[bitHi10] +
|
||||
*(U32*)&bitTableA[bitHi01]*2 + *(U32*)&bitTableE[bitLo11] + *(U32*)&bitTableC[bitHi11]*2 +
|
||||
*(U32*)&bitTableA[bitHi02] + *(U32*)&bitTableB[bitLo12] + *(U32*)&bitTableC[bitHi12];
|
||||
*eightBits++ = *(U32*)&bitTableA[bitLo10] + *(U32*)&bitTableB[bitHi10] + *(U32*)&bitTableC[bitLo20] +
|
||||
*(U32*)&bitTableA[bitLo11]*2 + *(U32*)&bitTableE[bitHi11] + *(U32*)&bitTableC[bitLo21]*2 +
|
||||
*(U32*)&bitTableA[bitLo12] + *(U32*)&bitTableB[bitHi12] + *(U32*)&bitTableC[bitLo22];
|
||||
|
||||
bitHi00 = bitHi10;
|
||||
bitLo10 = bitLo20;
|
||||
bitHi10 = bitHi20;
|
||||
bitHi01 = bitHi11;
|
||||
bitLo11 = bitLo21;
|
||||
bitHi11 = bitHi21;
|
||||
bitHi02 = bitHi12;
|
||||
bitLo12 = bitLo22;
|
||||
bitHi12 = bitHi22;
|
||||
|
||||
p0++;
|
||||
p1++;
|
||||
p2++;
|
||||
}
|
||||
while (p2<end);
|
||||
|
||||
// clear out last row of dest
|
||||
do { *eightBits++=0; } while (eightBits<end32);
|
||||
}
|
||||
|
||||
|
||||
|
||||
29
core/bitRender.h
Normal file
29
core/bitRender.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _BITRENDER_H_
|
||||
#define _BITRENDER_H_
|
||||
|
||||
#ifndef _MMATH_H_
|
||||
#include "Math/mMath.h"
|
||||
#endif
|
||||
|
||||
struct BitRender
|
||||
{
|
||||
// render a triangle to a bitmap of 1-bit per pixel and size dim X dim
|
||||
static void render(const Point2I *, const Point2I *, const Point2I *, S32 dim, U32 * bits);
|
||||
// render a number of triangle strips to 1-bit per pixel bmp of size dim by dim
|
||||
static void render_strips(const U8 * draw, S32 numDraw, S32 szDraw, const U16 * indices, const Point2I * points, S32 dim, U32 * bits);
|
||||
// render a number of triangles to 1-bit per pixel bmp of size dim by dim
|
||||
static void render_tris(const U8 * draw, S32 numDraw, S32 szDraw, const U16 * indices, const Point2I * points, S32 dim, U32 * bits);
|
||||
|
||||
static void bitTo8Bit(U32 * bits, U32 * eightBits, S32 dim);
|
||||
static void bitTo8Bit_3(U32 * bits, U32 * eightBits, S32 dim);
|
||||
};
|
||||
|
||||
#endif // _BIT_RENDER_H_
|
||||
|
||||
52
core/bitSet.h
Normal file
52
core/bitSet.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _BITSET_H_
|
||||
#define _BITSET_H_
|
||||
|
||||
//Includes
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "Platform/platform.h"
|
||||
#endif
|
||||
|
||||
class BitSet32
|
||||
{
|
||||
private:
|
||||
U32 mbits;
|
||||
|
||||
public:
|
||||
BitSet32() { mbits = 0; }
|
||||
BitSet32(const BitSet32& in_rCopy) { mbits = in_rCopy.mbits; }
|
||||
BitSet32(const U32 in_mask) { mbits = in_mask; }
|
||||
|
||||
operator U32() const { return mbits; }
|
||||
U32 getMask() const { return mbits; }
|
||||
|
||||
void set() { mbits = 0xFFFFFFFFUL; }
|
||||
void set(const U32 m) { mbits |= m; }
|
||||
void set(BitSet32 s, bool b) { mbits = (mbits&~(s.mbits))|(b?s.mbits:0); }
|
||||
|
||||
void clear() { mbits = 0; }
|
||||
void clear(const U32 m) { mbits &= ~m; }
|
||||
|
||||
void toggle(const U32 m) { mbits ^= m; }
|
||||
|
||||
bool test(const U32 m) const { return (mbits & m) != 0; }
|
||||
bool testStrict(const U32 m) const { return (mbits & m) == m; }
|
||||
|
||||
BitSet32& operator =(const U32 m) { mbits = m; return *this; }
|
||||
BitSet32& operator|=(const U32 m) { mbits |= m; return *this; }
|
||||
BitSet32& operator&=(const U32 m) { mbits &= m; return *this; }
|
||||
BitSet32& operator^=(const U32 m) { mbits ^= m; return *this; }
|
||||
|
||||
BitSet32 operator|(const U32 m) const { return BitSet32(mbits | m); }
|
||||
BitSet32 operator&(const U32 m) const { return BitSet32(mbits & m); }
|
||||
BitSet32 operator^(const U32 m) const { return BitSet32(mbits ^ m); }
|
||||
};
|
||||
|
||||
|
||||
#endif //_NBITSET_H_
|
||||
916
core/bitStream.cc
Normal file
916
core/bitStream.cc
Normal file
|
|
@ -0,0 +1,916 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "core/bitStream.h"
|
||||
#include "core/tVector.h"
|
||||
#include "math/mPoint.h"
|
||||
#include "math/mMatrix.h"
|
||||
#include "math/mQuat.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "platform/event.h"
|
||||
|
||||
static BitStream gPacketStream(NULL, 0);
|
||||
static U8 gPacketBuffer[MaxPacketDataSize];
|
||||
|
||||
// bitstream utility functions
|
||||
|
||||
void BitStream::setStringBuffer(char buffer[256])
|
||||
{
|
||||
stringBuffer = buffer;
|
||||
}
|
||||
|
||||
BitStream *BitStream::getPacketStream(U32 writeSize)
|
||||
{
|
||||
if(!writeSize)
|
||||
writeSize = MaxPacketDataSize;
|
||||
|
||||
gPacketStream.setBuffer(gPacketBuffer, writeSize, MaxPacketDataSize);
|
||||
gPacketStream.setPosition(0);
|
||||
|
||||
return &gPacketStream;
|
||||
}
|
||||
|
||||
void BitStream::sendPacketStream(const NetAddress *addr)
|
||||
{
|
||||
Net::sendto(addr, gPacketStream.getBuffer(), gPacketStream.getPosition());
|
||||
}
|
||||
|
||||
// FIXMEFIXMEFIXME MATH
|
||||
|
||||
inline bool IsEqual(F32 a, F32 b) { return a == b; }
|
||||
|
||||
ResizeBitStream::ResizeBitStream(U32 minSpace, U32 initialSize) : BitStream(NULL, 0, 0)
|
||||
{
|
||||
mMinSpace = minSpace;
|
||||
if(!initialSize)
|
||||
initialSize = minSpace * 2;
|
||||
U8 *buf = (U8 *) dMalloc(initialSize);
|
||||
setBuffer(buf, initialSize, initialSize);
|
||||
}
|
||||
|
||||
ResizeBitStream::~ResizeBitStream()
|
||||
{
|
||||
dFree(dataPtr);
|
||||
}
|
||||
|
||||
void ResizeBitStream::validate()
|
||||
{
|
||||
if(getPosition() + mMinSpace > bufSize)
|
||||
{
|
||||
bufSize = getPosition() + mMinSpace * 2;
|
||||
dataPtr = (U8 *) dRealloc(dataPtr, bufSize);
|
||||
|
||||
maxReadBitNum = bufSize << 3;
|
||||
maxWriteBitNum = bufSize << 3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class HuffmanProcessor
|
||||
{
|
||||
static const U32 csm_charFreqs[256];
|
||||
bool m_tablesBuilt;
|
||||
|
||||
void buildTables();
|
||||
|
||||
struct HuffNode {
|
||||
U32 pop;
|
||||
|
||||
S16 index0;
|
||||
S16 index1;
|
||||
};
|
||||
struct HuffLeaf {
|
||||
U32 pop;
|
||||
|
||||
U8 numBits;
|
||||
U8 symbol;
|
||||
U32 code; // no code should be longer than 32 bits.
|
||||
};
|
||||
// We have to be a bit careful with these, mSince they are pointers...
|
||||
struct HuffWrap {
|
||||
HuffNode* pNode;
|
||||
HuffLeaf* pLeaf;
|
||||
|
||||
public:
|
||||
HuffWrap() : pNode(NULL), pLeaf(NULL) { }
|
||||
|
||||
void set(HuffLeaf* in_leaf) { pNode = NULL; pLeaf = in_leaf; }
|
||||
void set(HuffNode* in_node) { pLeaf = NULL; pNode = in_node; }
|
||||
|
||||
U32 getPop() { if (pNode) return pNode->pop; else return pLeaf->pop; }
|
||||
};
|
||||
|
||||
Vector<HuffNode> m_huffNodes;
|
||||
Vector<HuffLeaf> m_huffLeaves;
|
||||
|
||||
S16 determineIndex(HuffWrap&);
|
||||
|
||||
void generateCodes(BitStream&, S32, S32);
|
||||
|
||||
public:
|
||||
HuffmanProcessor() : m_tablesBuilt(false) { }
|
||||
|
||||
static HuffmanProcessor g_huffProcessor;
|
||||
|
||||
bool readHuffBuffer(BitStream* pStream, char* out_pBuffer);
|
||||
bool writeHuffBuffer(BitStream* pStream, const char* out_pBuffer, S32 maxLen);
|
||||
};
|
||||
|
||||
HuffmanProcessor HuffmanProcessor::g_huffProcessor;
|
||||
|
||||
void BitStream::setBuffer(void *bufPtr, S32 size, S32 maxSize)
|
||||
{
|
||||
dataPtr = (U8 *) bufPtr;
|
||||
bitNum = 0;
|
||||
bufSize = size;
|
||||
maxReadBitNum = size << 3;
|
||||
if(maxSize < 0)
|
||||
maxSize = size;
|
||||
maxWriteBitNum = maxSize << 3;
|
||||
error = false;
|
||||
}
|
||||
|
||||
U32 BitStream::getPosition() const
|
||||
{
|
||||
return (bitNum + 7) >> 3;
|
||||
}
|
||||
|
||||
|
||||
bool BitStream::setPosition(const U32 pos)
|
||||
{
|
||||
bitNum = pos << 3;
|
||||
return (true);
|
||||
}
|
||||
|
||||
U32 BitStream::getStreamSize()
|
||||
{
|
||||
AssertFatal(false, "Ambiguous call on BitStream: bytes or bits?");
|
||||
return 0;
|
||||
}
|
||||
|
||||
U8 *BitStream::getBytePtr()
|
||||
{
|
||||
return dataPtr + getPosition();
|
||||
}
|
||||
|
||||
|
||||
U32 BitStream::getReadByteSize()
|
||||
{
|
||||
return (maxReadBitNum >> 3) - getPosition();
|
||||
}
|
||||
|
||||
void BitStream::clear()
|
||||
{
|
||||
dMemset(dataPtr, 0, bufSize);
|
||||
}
|
||||
|
||||
void BitStream::writeBits(S32 bitCount, const void *bitPtr)
|
||||
{
|
||||
if(!bitCount)
|
||||
return;
|
||||
|
||||
if(bitCount + bitNum > maxWriteBitNum)
|
||||
{
|
||||
error = true;
|
||||
AssertFatal(false, "Out of range write");
|
||||
return;
|
||||
}
|
||||
const U8 *ptr = (U8 *) bitPtr;
|
||||
U8 *stPtr = dataPtr + (bitNum >> 3);
|
||||
U8 *endPtr = dataPtr + ((bitCount + bitNum - 1) >> 3);
|
||||
|
||||
S32 upShift = bitNum & 0x7;
|
||||
S32 downShift= 8 - upShift;
|
||||
U8 lastMask = 0xFF >> (7 - ((bitNum + bitCount - 1) & 0x7));
|
||||
U8 startMask = 0xFF >> downShift;
|
||||
|
||||
U8 curB = *ptr++;
|
||||
*stPtr = (curB << upShift) | (*stPtr & startMask);
|
||||
|
||||
stPtr++;
|
||||
while(stPtr <= endPtr)
|
||||
{
|
||||
U8 nextB = *ptr++;
|
||||
*stPtr++ = (curB >> downShift) | (nextB << upShift);
|
||||
curB = nextB;
|
||||
}
|
||||
*endPtr &= lastMask;
|
||||
|
||||
bitNum += bitCount;
|
||||
}
|
||||
|
||||
void BitStream::setBit(S32 bitCount, bool set)
|
||||
{
|
||||
if(set)
|
||||
*(dataPtr + (bitCount >> 3)) |= (1 << (bitCount & 0x7));
|
||||
else
|
||||
*(dataPtr + (bitCount >> 3)) &= ~(1 << (bitCount & 0x7));
|
||||
}
|
||||
|
||||
bool BitStream::testBit(S32 bitCount)
|
||||
{
|
||||
return (*(dataPtr + (bitCount >> 3)) & (1 << (bitCount & 0x7))) != 0;
|
||||
}
|
||||
|
||||
bool BitStream::writeFlag(bool val)
|
||||
{
|
||||
if(bitNum + 1 > maxWriteBitNum)
|
||||
{
|
||||
error = true;
|
||||
AssertFatal(false, "Out of range write");
|
||||
return false;
|
||||
}
|
||||
if(val)
|
||||
*(dataPtr + (bitNum >> 3)) |= (1 << (bitNum & 0x7));
|
||||
else
|
||||
*(dataPtr + (bitNum >> 3)) &= ~(1 << (bitNum & 0x7));
|
||||
bitNum++;
|
||||
return (val);
|
||||
}
|
||||
|
||||
void BitStream::readBits(S32 bitCount, void *bitPtr)
|
||||
{
|
||||
if(!bitCount)
|
||||
return;
|
||||
if(bitCount + bitNum > maxReadBitNum)
|
||||
{
|
||||
error = true;
|
||||
//AssertFatal(false, "Out of range read");
|
||||
AssertWarn(false, "Out of range read");
|
||||
return;
|
||||
}
|
||||
U8 *stPtr = dataPtr + (bitNum >> 3);
|
||||
S32 byteCount = (bitCount + 7) >> 3;
|
||||
|
||||
U8 *ptr = (U8 *) bitPtr;
|
||||
|
||||
S32 downShift = bitNum & 0x7;
|
||||
S32 upShift = 8 - downShift;
|
||||
|
||||
U8 curB = *stPtr;
|
||||
while(byteCount--)
|
||||
{
|
||||
U8 nextB = *++stPtr;
|
||||
*ptr++ = (curB >> downShift) | (nextB << upShift);
|
||||
curB = nextB;
|
||||
}
|
||||
|
||||
bitNum += bitCount;
|
||||
}
|
||||
|
||||
bool BitStream::_read(U32 size, void *dataPtr)
|
||||
{
|
||||
readBits(size << 3, dataPtr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitStream::_write(U32 size, const void *dataPtr)
|
||||
{
|
||||
writeBits(size << 3, dataPtr);
|
||||
return true;
|
||||
}
|
||||
|
||||
S32 BitStream::readInt(S32 bitCount)
|
||||
{
|
||||
S32 ret = 0;
|
||||
readBits(bitCount, &ret);
|
||||
ret = convertLEndianToHost(ret);
|
||||
if(bitCount == 32)
|
||||
return ret;
|
||||
else
|
||||
ret &= (1 << bitCount) - 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void BitStream::writeInt(S32 val, S32 bitCount)
|
||||
{
|
||||
val = convertHostToLEndian(val);
|
||||
writeBits(bitCount, &val);
|
||||
}
|
||||
|
||||
void BitStream::writeFloat(F32 f, S32 bitCount)
|
||||
{
|
||||
writeInt(f * ((1 << bitCount) - 1), bitCount);
|
||||
}
|
||||
|
||||
F32 BitStream::readFloat(S32 bitCount)
|
||||
{
|
||||
return readInt(bitCount) / F32((1 << bitCount) - 1);
|
||||
}
|
||||
|
||||
void BitStream::writeSignedFloat(F32 f, S32 bitCount)
|
||||
{
|
||||
writeInt( ((f + 1) * .5) * ((1 << bitCount) - 1), bitCount);
|
||||
}
|
||||
|
||||
F32 BitStream::readSignedFloat(S32 bitCount)
|
||||
{
|
||||
return readInt(bitCount) * 2 / F32((1 << bitCount) - 1) - 1.0f;
|
||||
}
|
||||
|
||||
void BitStream::writeSignedInt(S32 value, S32 bitCount)
|
||||
{
|
||||
if(writeFlag(value < 0))
|
||||
writeInt(-value, bitCount - 1);
|
||||
else
|
||||
writeInt(value, bitCount - 1);
|
||||
}
|
||||
|
||||
S32 BitStream::readSignedInt(S32 bitCount)
|
||||
{
|
||||
if(readFlag())
|
||||
return -readInt(bitCount - 1);
|
||||
else
|
||||
return readInt(bitCount - 1);
|
||||
}
|
||||
|
||||
void BitStream::writeNormalVector(const Point3F& vec, S32 bitCount)
|
||||
{
|
||||
F32 phi = mAtan(vec.x, vec.y) / M_PI;
|
||||
F32 theta = mAtan(vec.z, mSqrt(vec.x*vec.x + vec.y*vec.y)) / (M_PI/2.0);
|
||||
|
||||
writeSignedFloat(phi, bitCount+1);
|
||||
writeSignedFloat(theta, bitCount);
|
||||
}
|
||||
|
||||
void BitStream::readNormalVector(Point3F *vec, S32 bitCount)
|
||||
{
|
||||
F32 phi = readSignedFloat(bitCount+1) * M_PI;
|
||||
F32 theta = readSignedFloat(bitCount) * (M_PI/2.0);
|
||||
|
||||
vec->x = mSin(phi)*mCos(theta);
|
||||
vec->y = mCos(phi)*mCos(theta);
|
||||
vec->z = mSin(theta);
|
||||
}
|
||||
|
||||
Point3F BitStream::dumbDownNormal(const Point3F& vec, S32 bitCount)
|
||||
{
|
||||
U8 buffer[128];
|
||||
BitStream temp(buffer, 128);
|
||||
|
||||
temp.writeNormalVector(vec, bitCount);
|
||||
temp.setCurPos(0);
|
||||
|
||||
Point3F ret;
|
||||
temp.readNormalVector(&ret, bitCount);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void BitStream::writeNormalVector(const Point3F& vec, S32 angleBitCount, S32 zBitCount)
|
||||
{
|
||||
writeSignedFloat( vec.z, zBitCount );
|
||||
|
||||
// don't need to write x and y if they are both zero, which we can assess
|
||||
// by checking for |z| == 1
|
||||
if(!IsEqual(mFabs(vec.z), 1.0f))
|
||||
{
|
||||
writeSignedFloat( mAtan(vec.x,vec.y) / M_2PI, angleBitCount );
|
||||
}
|
||||
}
|
||||
|
||||
void BitStream::readNormalVector(Point3F * vec, S32 angleBitCount, S32 zBitCount)
|
||||
{
|
||||
vec->z = readSignedFloat(zBitCount);
|
||||
|
||||
// check to see if |z| == 1. If so, then we don't read x and y (they're zero)
|
||||
if(!IsEqual(mFabs(vec->z), 1.0f))
|
||||
{
|
||||
F32 angle = M_2PI * readSignedFloat(angleBitCount);
|
||||
|
||||
F32 mult = mSqrt(1.0f - vec->z * vec->z);
|
||||
vec->x = mult * mCos(angle);
|
||||
vec->y = mult * mSin(angle);
|
||||
}
|
||||
else
|
||||
{
|
||||
// no need to read, we know
|
||||
vec->x = 0.0f;
|
||||
vec->y = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void BitStream::writeAffineTransform(const MatrixF& matrix)
|
||||
{
|
||||
// AssertFatal(matrix.isAffine() == true,
|
||||
// "BitStream::writeAffineTransform: Error, must write only affine transforms!");
|
||||
|
||||
Point3F pos;
|
||||
matrix.getColumn(3, &pos);
|
||||
mathWrite(*this, pos);
|
||||
|
||||
QuatF q(matrix);
|
||||
q.normalize();
|
||||
write(q.x);
|
||||
write(q.y);
|
||||
write(q.z);
|
||||
writeFlag(q.w < 0.0);
|
||||
}
|
||||
|
||||
void BitStream::readAffineTransform(MatrixF* matrix)
|
||||
{
|
||||
Point3F pos;
|
||||
QuatF q;
|
||||
|
||||
mathRead(*this, &pos);
|
||||
read(&q.x);
|
||||
read(&q.y);
|
||||
read(&q.z);
|
||||
q.w = mSqrt(1.0 - getMin(F32(((q.x * q.x) + (q.y * q.y) + (q.z * q.z))), 1.f));
|
||||
if (readFlag())
|
||||
q.w = -q.w;
|
||||
|
||||
q.setMatrix(matrix);
|
||||
matrix->setColumn(3, pos);
|
||||
// AssertFatal(matrix->isAffine() == true,
|
||||
// "BitStream::readAffineTransform: Error, transform should be affine after this function!");
|
||||
}
|
||||
|
||||
|
||||
void BitStream::readString(char buf[256])
|
||||
{
|
||||
if(stringBuffer)
|
||||
{
|
||||
if(readFlag())
|
||||
{
|
||||
S32 offset = readInt(8);
|
||||
HuffmanProcessor::g_huffProcessor.readHuffBuffer(this, stringBuffer + offset);
|
||||
dStrcpy(buf, stringBuffer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
HuffmanProcessor::g_huffProcessor.readHuffBuffer(this, buf);
|
||||
if(stringBuffer)
|
||||
dStrcpy(stringBuffer, buf);
|
||||
}
|
||||
|
||||
void BitStream::writeString(const char *string, S32 maxLen)
|
||||
{
|
||||
if(!string)
|
||||
string = "";
|
||||
if(stringBuffer)
|
||||
{
|
||||
S32 j;
|
||||
for(j = 0; j < maxLen && stringBuffer[j] == string[j] && string[j];j++)
|
||||
;
|
||||
dStrncpy(stringBuffer, string, maxLen);
|
||||
stringBuffer[maxLen] = 0;
|
||||
|
||||
if(writeFlag(j > 2))
|
||||
{
|
||||
writeInt(j, 8);
|
||||
HuffmanProcessor::g_huffProcessor.writeHuffBuffer(this, string + j, maxLen - j);
|
||||
return;
|
||||
}
|
||||
}
|
||||
HuffmanProcessor::g_huffProcessor.writeHuffBuffer(this, string, maxLen);
|
||||
}
|
||||
|
||||
const static U32 csg_probBoost = 1;
|
||||
|
||||
void HuffmanProcessor::buildTables()
|
||||
{
|
||||
AssertFatal(m_tablesBuilt == false, "Cannot build tables twice!");
|
||||
m_tablesBuilt = true;
|
||||
|
||||
S32 i;
|
||||
|
||||
// First, construct the array of wraps...
|
||||
//
|
||||
m_huffLeaves.setSize(256);
|
||||
m_huffNodes.reserve(256);
|
||||
m_huffNodes.increment();
|
||||
for (i = 0; i < 256; i++) {
|
||||
HuffLeaf& rLeaf = m_huffLeaves[i];
|
||||
|
||||
rLeaf.pop = csm_charFreqs[i] + (dIsalnum(i) ? csg_probBoost : 0) + csg_probBoost;
|
||||
rLeaf.symbol = U8(i);
|
||||
|
||||
dMemset(&rLeaf.code, 0, sizeof(rLeaf.code));
|
||||
rLeaf.numBits = 0;
|
||||
}
|
||||
|
||||
S32 currWraps = 256;
|
||||
HuffWrap* pWrap = new HuffWrap[256];
|
||||
for (i = 0; i < 256; i++) {
|
||||
pWrap[i].set(&m_huffLeaves[i]);
|
||||
}
|
||||
|
||||
while (currWraps != 1) {
|
||||
U32 min1 = 0xfffffffe, min2 = 0xffffffff;
|
||||
S32 index1 = -1, index2 = -1;
|
||||
|
||||
for (i = 0; i < currWraps; i++) {
|
||||
if (pWrap[i].getPop() < min1) {
|
||||
min2 = min1;
|
||||
index2 = index1;
|
||||
|
||||
min1 = pWrap[i].getPop();
|
||||
index1 = i;
|
||||
} else if (pWrap[i].getPop() < min2) {
|
||||
min2 = pWrap[i].getPop();
|
||||
index2 = i;
|
||||
}
|
||||
}
|
||||
AssertFatal(index1 != -1 && index2 != -1 && index1 != index2, "hrph");
|
||||
|
||||
// Create a node for this...
|
||||
m_huffNodes.increment();
|
||||
HuffNode& rNode = m_huffNodes.last();
|
||||
rNode.pop = pWrap[index1].getPop() + pWrap[index2].getPop();
|
||||
rNode.index0 = determineIndex(pWrap[index1]);
|
||||
rNode.index1 = determineIndex(pWrap[index2]);
|
||||
|
||||
S32 mergeIndex = index1 > index2 ? index2 : index1;
|
||||
S32 nukeIndex = index1 > index2 ? index1 : index2;
|
||||
pWrap[mergeIndex].set(&rNode);
|
||||
|
||||
if (index2 != (currWraps - 1)) {
|
||||
pWrap[nukeIndex] = pWrap[currWraps - 1];
|
||||
}
|
||||
currWraps--;
|
||||
}
|
||||
AssertFatal(currWraps == 1, "wrong wraps?");
|
||||
AssertFatal(pWrap[0].pNode != NULL && pWrap[0].pLeaf == NULL, "Wrong wrap type!");
|
||||
|
||||
// Ok, now we have one wrap, which is a node. we need to make sure that this
|
||||
// is the first node in the node list.
|
||||
m_huffNodes[0] = *(pWrap[0].pNode);
|
||||
delete [] pWrap;
|
||||
|
||||
U32 code = 0;
|
||||
BitStream bs(&code, 4);
|
||||
|
||||
generateCodes(bs, 0, 0);
|
||||
}
|
||||
|
||||
void HuffmanProcessor::generateCodes(BitStream& rBS, S32 index, S32 depth)
|
||||
{
|
||||
if (index < 0) {
|
||||
// leaf node, copy the code in, and back out...
|
||||
HuffLeaf& rLeaf = m_huffLeaves[-(index + 1)];
|
||||
|
||||
dMemcpy(&rLeaf.code, rBS.dataPtr, sizeof(rLeaf.code));
|
||||
rLeaf.numBits = depth;
|
||||
} else {
|
||||
HuffNode& rNode = m_huffNodes[index];
|
||||
|
||||
S32 pos = rBS.getCurPos();
|
||||
|
||||
rBS.writeFlag(false);
|
||||
generateCodes(rBS, rNode.index0, depth + 1);
|
||||
|
||||
rBS.setCurPos(pos);
|
||||
rBS.writeFlag(true);
|
||||
generateCodes(rBS, rNode.index1, depth + 1);
|
||||
|
||||
rBS.setCurPos(pos);
|
||||
}
|
||||
}
|
||||
|
||||
S16 HuffmanProcessor::determineIndex(HuffWrap& rWrap)
|
||||
{
|
||||
if (rWrap.pLeaf != NULL) {
|
||||
AssertFatal(rWrap.pNode == NULL, "um, never.");
|
||||
|
||||
return -((rWrap.pLeaf - m_huffLeaves.address()) + 1);
|
||||
} else {
|
||||
AssertFatal(rWrap.pNode != NULL, "um, never.");
|
||||
|
||||
return rWrap.pNode - m_huffNodes.address();
|
||||
}
|
||||
}
|
||||
|
||||
bool HuffmanProcessor::readHuffBuffer(BitStream* pStream, char* out_pBuffer)
|
||||
{
|
||||
if (m_tablesBuilt == false)
|
||||
buildTables();
|
||||
|
||||
if (pStream->readFlag()) {
|
||||
S32 len = pStream->readInt(8);
|
||||
for (S32 i = 0; i < len; i++) {
|
||||
S32 index = 0;
|
||||
while (true) {
|
||||
if (index >= 0) {
|
||||
if (pStream->readFlag() == true) {
|
||||
index = m_huffNodes[index].index1;
|
||||
} else {
|
||||
index = m_huffNodes[index].index0;
|
||||
}
|
||||
} else {
|
||||
out_pBuffer[i] = m_huffLeaves[-(index+1)].symbol;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
out_pBuffer[len] = '\0';
|
||||
return true;
|
||||
} else {
|
||||
// Uncompressed string...
|
||||
U32 len = pStream->readInt(8);
|
||||
pStream->read(len, out_pBuffer);
|
||||
out_pBuffer[len] = '\0';
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool HuffmanProcessor::writeHuffBuffer(BitStream* pStream, const char* out_pBuffer, S32 maxLen)
|
||||
{
|
||||
if (out_pBuffer == NULL) {
|
||||
pStream->writeFlag(false);
|
||||
pStream->writeInt(0, 8);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_tablesBuilt == false)
|
||||
buildTables();
|
||||
|
||||
S32 len = out_pBuffer ? dStrlen(out_pBuffer) : 0;
|
||||
AssertWarn(len <= 255, "String TOO long for writeString");
|
||||
AssertWarn(len <= 255, out_pBuffer);
|
||||
if (len > maxLen)
|
||||
len = maxLen;
|
||||
|
||||
S32 numBits = 0;
|
||||
S32 i;
|
||||
for (i = 0; i < len; i++)
|
||||
numBits += m_huffLeaves[(unsigned char)out_pBuffer[i]].numBits;
|
||||
|
||||
if (numBits >= (len * 8)) {
|
||||
pStream->writeFlag(false);
|
||||
pStream->writeInt(len, 8);
|
||||
pStream->write(len, out_pBuffer);
|
||||
} else {
|
||||
pStream->writeFlag(true);
|
||||
pStream->writeInt(len, 8);
|
||||
for (i = 0; i < len; i++) {
|
||||
HuffLeaf& rLeaf = m_huffLeaves[((unsigned char)out_pBuffer[i])];
|
||||
pStream->writeBits(rLeaf.numBits, &rLeaf.code);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const U32 HuffmanProcessor::csm_charFreqs[256] = {
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
329 ,
|
||||
21 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
2809 ,
|
||||
68 ,
|
||||
0 ,
|
||||
27 ,
|
||||
0 ,
|
||||
58 ,
|
||||
3 ,
|
||||
62 ,
|
||||
4 ,
|
||||
7 ,
|
||||
0 ,
|
||||
0 ,
|
||||
15 ,
|
||||
65 ,
|
||||
554 ,
|
||||
3 ,
|
||||
394 ,
|
||||
404 ,
|
||||
189 ,
|
||||
117 ,
|
||||
30 ,
|
||||
51 ,
|
||||
27 ,
|
||||
15 ,
|
||||
34 ,
|
||||
32 ,
|
||||
80 ,
|
||||
1 ,
|
||||
142 ,
|
||||
3 ,
|
||||
142 ,
|
||||
39 ,
|
||||
0 ,
|
||||
144 ,
|
||||
125 ,
|
||||
44 ,
|
||||
122 ,
|
||||
275 ,
|
||||
70 ,
|
||||
135 ,
|
||||
61 ,
|
||||
127 ,
|
||||
8 ,
|
||||
12 ,
|
||||
113 ,
|
||||
246 ,
|
||||
122 ,
|
||||
36 ,
|
||||
185 ,
|
||||
1 ,
|
||||
149 ,
|
||||
309 ,
|
||||
335 ,
|
||||
12 ,
|
||||
11 ,
|
||||
14 ,
|
||||
54 ,
|
||||
151 ,
|
||||
0 ,
|
||||
0 ,
|
||||
2 ,
|
||||
0 ,
|
||||
0 ,
|
||||
211 ,
|
||||
0 ,
|
||||
2090 ,
|
||||
344 ,
|
||||
736 ,
|
||||
993 ,
|
||||
2872 ,
|
||||
701 ,
|
||||
605 ,
|
||||
646 ,
|
||||
1552 ,
|
||||
328 ,
|
||||
305 ,
|
||||
1240 ,
|
||||
735 ,
|
||||
1533 ,
|
||||
1713 ,
|
||||
562 ,
|
||||
3 ,
|
||||
1775 ,
|
||||
1149 ,
|
||||
1469 ,
|
||||
979 ,
|
||||
407 ,
|
||||
553 ,
|
||||
59 ,
|
||||
279 ,
|
||||
31 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0 ,
|
||||
68 ,
|
||||
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 ,
|
||||
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 ,
|
||||
0 ,
|
||||
0 ,
|
||||
0
|
||||
};
|
||||
|
||||
175
core/bitStream.h
Normal file
175
core/bitStream.h
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _BITSTREAM_H_
|
||||
#define _BITSTREAM_H_
|
||||
|
||||
//Includes
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "Platform/platform.h"
|
||||
#endif
|
||||
#ifndef _STREAM_H_
|
||||
#include "Core/stream.h"
|
||||
#endif
|
||||
|
||||
//-------------------------------------- Some caveats when using this class:
|
||||
// - Get/setPosition semantics are changed
|
||||
// to indicate bit position rather than
|
||||
// byte position.
|
||||
//
|
||||
|
||||
class Point3F;
|
||||
class MatrixF;
|
||||
class HuffmanProcessor;
|
||||
|
||||
class BitStream : public Stream
|
||||
{
|
||||
protected:
|
||||
U8 *dataPtr;
|
||||
S32 bitNum;
|
||||
S32 bufSize;
|
||||
bool error;
|
||||
S32 maxReadBitNum;
|
||||
S32 maxWriteBitNum;
|
||||
char *stringBuffer;
|
||||
|
||||
friend class HuffmanProcessor;
|
||||
public:
|
||||
static BitStream *getPacketStream(U32 writeSize = 0);
|
||||
static void sendPacketStream(const NetAddress *addr);
|
||||
|
||||
void setBuffer(void *bufPtr, S32 bufSize, S32 maxSize = 0);
|
||||
U8* getBuffer() { return dataPtr; }
|
||||
U8* getBytePtr();
|
||||
|
||||
U32 getReadByteSize();
|
||||
|
||||
S32 getCurPos() const;
|
||||
void setCurPos(const U32);
|
||||
|
||||
BitStream(void *bufPtr, S32 bufSize, S32 maxWriteSize = -1) { setBuffer(bufPtr, bufSize,maxWriteSize); stringBuffer = NULL; }
|
||||
void clear();
|
||||
|
||||
void setStringBuffer(char buffer[256]);
|
||||
void writeInt(S32 value, S32 bitCount);
|
||||
S32 readInt(S32 bitCount);
|
||||
|
||||
void writeSignedInt(S32 value, S32 bitCount);
|
||||
S32 readSignedInt(S32 bitCount);
|
||||
|
||||
void writeRangedU32(U32 value, U32 rangeStart, U32 rangeEnd);
|
||||
U32 readRangedU32(U32 rangeStart, U32 rangeEnd);
|
||||
|
||||
// read and write floats... floats are 0 to 1 inclusive, signed floats are -1 to 1 inclusive
|
||||
|
||||
F32 readFloat(S32 bitCount);
|
||||
F32 readSignedFloat(S32 bitCount);
|
||||
|
||||
void writeFloat(F32 f, S32 bitCount);
|
||||
void writeSignedFloat(F32 f, S32 bitCount);
|
||||
|
||||
// writes a normalized vector
|
||||
void writeNormalVector(const Point3F& vec, S32 bitCount);
|
||||
void readNormalVector(Point3F *vec, S32 bitCount);
|
||||
|
||||
// Uses the above method to reduce the precision of a normal vector so the server can
|
||||
// determine exactly what is on the client. (Pre-dumbing the vector before sending
|
||||
// to the client can result in precision errors...)
|
||||
static Point3F dumbDownNormal(const Point3F& vec, S32 bitCount);
|
||||
|
||||
|
||||
// writes a normalized vector using alternate method
|
||||
void writeNormalVector(const Point3F& vec, S32 angleBitCount, S32 zBitCount);
|
||||
void readNormalVector(Point3F *vec, S32 angleBitCount, S32 zBitCount);
|
||||
|
||||
// writes an affine transform (full precision version)
|
||||
void writeAffineTransform(const MatrixF&);
|
||||
void readAffineTransform(MatrixF*);
|
||||
|
||||
void writeBits(S32 bitCount, const void *bitPtr);
|
||||
void readBits(S32 bitCount, void *bitPtr);
|
||||
bool writeFlag(bool val);
|
||||
bool readFlag();
|
||||
|
||||
void setBit(S32 bitCount, bool set);
|
||||
bool testBit(S32 bitCount);
|
||||
|
||||
bool isFull() { return bitNum > (bufSize << 3); }
|
||||
bool isValid() { return !error; }
|
||||
|
||||
bool _read (const U32 size,void* d);
|
||||
bool _write(const U32 size,const void* d);
|
||||
|
||||
void readString(char stringBuf[256]);
|
||||
void writeString(const char *stringBuf, S32 maxLen=255);
|
||||
|
||||
bool hasCapability(const Capability) const { return true; }
|
||||
U32 getPosition() const;
|
||||
bool setPosition(const U32 in_newPosition);
|
||||
U32 getStreamSize();
|
||||
};
|
||||
|
||||
class ResizeBitStream : public BitStream
|
||||
{
|
||||
U32 mMinSpace;
|
||||
public:
|
||||
ResizeBitStream(U32 minSpace = 1500, U32 initialSize = 0);
|
||||
void validate();
|
||||
~ResizeBitStream();
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//-------------------------------------- INLINES
|
||||
//
|
||||
inline S32 BitStream::getCurPos() const
|
||||
{
|
||||
return bitNum;
|
||||
}
|
||||
|
||||
inline void BitStream::setCurPos(const U32 in_position)
|
||||
{
|
||||
AssertFatal(in_position < (U32)(bufSize << 3), "Out of range bitposition");
|
||||
bitNum = S32(in_position);
|
||||
}
|
||||
|
||||
inline bool BitStream::readFlag()
|
||||
{
|
||||
if(bitNum > maxReadBitNum)
|
||||
{
|
||||
error = true;
|
||||
AssertFatal(false, "Out of range read");
|
||||
return false;
|
||||
}
|
||||
S32 mask = 1 << (bitNum & 0x7);
|
||||
bool ret = (*(dataPtr + (bitNum >> 3)) & mask) != 0;
|
||||
bitNum++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline void BitStream::writeRangedU32(U32 value, U32 rangeStart, U32 rangeEnd)
|
||||
{
|
||||
AssertFatal(value >= rangeStart && value <= rangeEnd, "Out of bounds value!");
|
||||
AssertFatal(rangeEnd >= rangeStart, "error, end of range less than start");
|
||||
|
||||
U32 rangeSize = rangeEnd - rangeStart + 1;
|
||||
U32 rangeBits = getBinLog2(getNextPow2(rangeSize));
|
||||
|
||||
writeInt(S32(value - rangeStart), S32(rangeBits));
|
||||
}
|
||||
|
||||
inline U32 BitStream::readRangedU32(U32 rangeStart, U32 rangeEnd)
|
||||
{
|
||||
AssertFatal(rangeEnd >= rangeStart, "error, end of range less than start");
|
||||
|
||||
U32 rangeSize = rangeEnd - rangeStart + 1;
|
||||
U32 rangeBits = getBinLog2(getNextPow2(rangeSize));
|
||||
|
||||
U32 val = U32(readInt(S32(rangeBits)));
|
||||
return val + rangeStart;
|
||||
}
|
||||
|
||||
#endif //_BITSTREAM_H_
|
||||
27
core/bitTables.cc
Normal file
27
core/bitTables.cc
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "Core/BitTables.h"
|
||||
|
||||
bool BitTables::mTablesBuilt = false;
|
||||
S8 BitTables::mHighBit[256];
|
||||
S8 BitTables::mWhichOn[256][8];
|
||||
S8 BitTables::mNumOn[256];
|
||||
static BitTables sBuildTheTables; // invoke ctor first-time work
|
||||
|
||||
BitTables::BitTables()
|
||||
{
|
||||
if(! mTablesBuilt){
|
||||
// This code only happens once - it relies on the tables being clear.
|
||||
for( U32 byte = 0; byte < 256; byte++ )
|
||||
for( U32 bit = 0; bit < 8; bit++ )
|
||||
if( byte & (1 << bit) )
|
||||
mHighBit[byte] = (mWhichOn[byte][mNumOn[byte]++] = bit) + 1;
|
||||
|
||||
mTablesBuilt = true;
|
||||
}
|
||||
}
|
||||
38
core/bitTables.h
Normal file
38
core/bitTables.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _BITTABLES_H_
|
||||
#define _BITTABLES_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "Platform/platform.h"
|
||||
#endif
|
||||
|
||||
class BitTables
|
||||
{
|
||||
private:
|
||||
static bool mTablesBuilt; // For first time build
|
||||
static S8 mHighBit[256]; // I.e. crude logarithm
|
||||
static S8 mWhichOn[256][8]; // Unroll a bitset collection (note
|
||||
static S8 mNumOn[256]; // ptr table wastes same amt.)
|
||||
|
||||
public:
|
||||
BitTables();
|
||||
static const S32 numOn(U8 b) { return mNumOn[b]; }
|
||||
static const S8 * whichOn(U8 b) { return mWhichOn[b]; }
|
||||
static const S32 highBit(U8 b) { return mHighBit[b]; }
|
||||
|
||||
static S32 getPower16(U16 x) { return x<256 ? mHighBit[x] : mHighBit[x>>8]+8; }
|
||||
static S32 getPower32(U32 x){
|
||||
if( x < (1<<16) )
|
||||
return( x < (1<<8) ? mHighBit[x] : mHighBit[x>>8]+8 );
|
||||
else
|
||||
return( x < (1<<24) ? mHighBit[x>>16]+16 : mHighBit[x>>24]+24 );
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
155
core/bitVector.h
Normal file
155
core/bitVector.h
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _BITVECTOR_H_
|
||||
#define _BITVECTOR_H_
|
||||
|
||||
//Includes
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "Platform/platform.h"
|
||||
#endif
|
||||
|
||||
class BitVector
|
||||
{
|
||||
U8* mBits;
|
||||
U32 mByteSize;
|
||||
|
||||
U32 mSize;
|
||||
|
||||
static U32 calcByteSize(const U32 numBits);
|
||||
|
||||
public:
|
||||
BitVector();
|
||||
BitVector(const U32 _size);
|
||||
~BitVector();
|
||||
|
||||
void setSize(const U32 _size);
|
||||
U32 getSize() const;
|
||||
U32 getByteSize() const;
|
||||
U32 getAllocatedByteSize() const { return mByteSize; }
|
||||
|
||||
const U8* getBits() const { return mBits; }
|
||||
U8* getNCBits() { return mBits; }
|
||||
|
||||
bool copy(const BitVector& from);
|
||||
|
||||
void clear();
|
||||
void set();
|
||||
|
||||
void set(U32 bit);
|
||||
void clear(U32 bit);
|
||||
bool test(U32 bit) const;
|
||||
};
|
||||
|
||||
inline BitVector::BitVector()
|
||||
{
|
||||
mBits = NULL;
|
||||
mByteSize = 0;
|
||||
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
|
||||
inline BitVector::BitVector(const U32 _size)
|
||||
{
|
||||
mBits = NULL;
|
||||
mByteSize = 0;
|
||||
|
||||
mSize = 0;
|
||||
|
||||
setSize(_size);
|
||||
}
|
||||
|
||||
inline BitVector::~BitVector()
|
||||
{
|
||||
delete [] mBits;
|
||||
mBits = NULL;
|
||||
mByteSize = 0;
|
||||
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
inline U32 BitVector::calcByteSize(const U32 numBits)
|
||||
{
|
||||
// Make sure that we are 32 bit aligned
|
||||
//
|
||||
return (((numBits + 0x7) >> 3) + 0x3) & ~0x3;
|
||||
}
|
||||
|
||||
inline void BitVector::setSize(const U32 _size)
|
||||
{
|
||||
if (_size != 0) {
|
||||
U32 newSize = calcByteSize(_size);
|
||||
if (mByteSize < newSize) {
|
||||
delete [] mBits;
|
||||
mBits = new U8[newSize];
|
||||
mByteSize = newSize;
|
||||
}
|
||||
} else {
|
||||
delete [] mBits;
|
||||
mBits = NULL;
|
||||
mByteSize = 0;
|
||||
}
|
||||
|
||||
mSize = _size;
|
||||
}
|
||||
|
||||
inline U32 BitVector::getSize() const
|
||||
{
|
||||
return mSize;
|
||||
}
|
||||
|
||||
inline U32 BitVector::getByteSize() const
|
||||
{
|
||||
return calcByteSize(mSize);
|
||||
}
|
||||
|
||||
inline void BitVector::clear()
|
||||
{
|
||||
if (mSize != 0)
|
||||
dMemset(mBits, 0x00, calcByteSize(mSize));
|
||||
}
|
||||
|
||||
inline bool BitVector::copy(const BitVector& from)
|
||||
{
|
||||
U32 sourceSize = from.getSize();
|
||||
if (sourceSize) {
|
||||
setSize(sourceSize);
|
||||
dMemcpy(mBits, from.getBits(), getByteSize());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void BitVector::set()
|
||||
{
|
||||
if (mSize != 0)
|
||||
dMemset(mBits, 0xFF, calcByteSize(mSize));
|
||||
}
|
||||
|
||||
inline void BitVector::set(U32 bit)
|
||||
{
|
||||
AssertFatal(bit < mSize, "Error, out of range bit");
|
||||
|
||||
mBits[bit >> 3] |= U8(1 << (bit & 0x7));
|
||||
}
|
||||
|
||||
inline void BitVector::clear(U32 bit)
|
||||
{
|
||||
AssertFatal(bit < mSize, "Error, out of range bit");
|
||||
|
||||
mBits[bit >> 3] &= U8(~(1 << (bit & 0x7)));
|
||||
}
|
||||
|
||||
inline bool BitVector::test(U32 bit) const
|
||||
{
|
||||
AssertFatal(bit < mSize, "Error, out of range bit");
|
||||
|
||||
return (mBits[bit >> 3] & U8(1 << (bit & 0x7))) != 0;
|
||||
}
|
||||
|
||||
#endif //_BITVECTOR_H_
|
||||
92
core/bitVectorW.h
Normal file
92
core/bitVectorW.h
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _BITVECTORW_H_
|
||||
#define _BITVECTORW_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "Platform/platform.h"
|
||||
#endif
|
||||
|
||||
class BitVectorW
|
||||
{
|
||||
U32 mNumEntries;
|
||||
U32 mBitWidth;
|
||||
U32 mBitMask;
|
||||
U8 * mDataPtr;
|
||||
|
||||
public:
|
||||
BitVectorW() {mDataPtr=NULL; setDims(0,0);}
|
||||
~BitVectorW() {if(mDataPtr) delete [] mDataPtr;}
|
||||
|
||||
U32 bitWidth() const {return mBitWidth;}
|
||||
U32 numEntries() const {return mNumEntries;}
|
||||
U8 * dataPtr() const {return mDataPtr;}
|
||||
|
||||
U32 getU17(U32 idx) const; // get and set for bit widths
|
||||
void setU17(U32 idx, U32 val); // of 17 or less
|
||||
U32 numBytes() const;
|
||||
void setDims(U32 sz, U32 w);
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
inline U32 BitVectorW::numBytes() const
|
||||
{
|
||||
if (mNumEntries > 0)
|
||||
return (mBitWidth * mNumEntries) + 32 >> 3;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Alloc the data - note it does work for a bit width of zero (lookups return zero)
|
||||
inline void BitVectorW::setDims(U32 size, U32 width)
|
||||
{
|
||||
if (mDataPtr)
|
||||
delete [] mDataPtr;
|
||||
|
||||
if (size > 0 && width <= 17)
|
||||
{
|
||||
mBitWidth = width;
|
||||
mNumEntries = size;
|
||||
mBitMask = (1 << width) - 1;
|
||||
U32 dataSize = numBytes();
|
||||
mDataPtr = new U8 [dataSize];
|
||||
dMemset(mDataPtr, 0, dataSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
mDataPtr = NULL;
|
||||
mBitWidth = mBitMask = mNumEntries = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// For coding ease, the get and set methods might read or write an extra byte or two.
|
||||
// If more or less max bit width is ever needed, add or remove the x[] expressions.
|
||||
|
||||
inline U32 BitVectorW::getU17(U32 i) const
|
||||
{
|
||||
if (mDataPtr) {
|
||||
register U8 * x = &mDataPtr[(i *= mBitWidth) >> 3];
|
||||
return (U32(*x) + (U32(x[1])<<8) + (U32(x[2])<<16) >> (i&7)) & mBitMask;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline void BitVectorW::setU17(U32 i, U32 value)
|
||||
{
|
||||
if (mDataPtr) {
|
||||
register U8 * x = &mDataPtr[(i *= mBitWidth) >> 3];
|
||||
register U32 mask = mBitMask << (i &= 7);
|
||||
x[0] = (x[0] & (~mask >> 0)) | ((value <<= i) & (mask >> 0));
|
||||
x[1] = (x[1] & (~mask >> 8)) | ((value >> 8) & (mask >> 8));
|
||||
x[2] = (x[2] & (~mask >> 16)) | ((value >> 16) & (mask >> 16));
|
||||
}
|
||||
}
|
||||
|
||||
#endif //_BITVECTORW_H_
|
||||
442
core/color.h
Normal file
442
core/color.h
Normal file
|
|
@ -0,0 +1,442 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _COLOR_H_
|
||||
#define _COLOR_H_
|
||||
|
||||
//Includes
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "Platform/platform.h"
|
||||
#endif
|
||||
|
||||
|
||||
// Forward Declarations...
|
||||
class ColorI;
|
||||
|
||||
class ColorF
|
||||
{
|
||||
public:
|
||||
F32 red;
|
||||
F32 green;
|
||||
F32 blue;
|
||||
F32 alpha;
|
||||
|
||||
public:
|
||||
ColorF() { }
|
||||
ColorF(const ColorF& in_rCopy);
|
||||
ColorF(const F32 in_r,
|
||||
const F32 in_g,
|
||||
const F32 in_b,
|
||||
const F32 in_a = 1.0f);
|
||||
|
||||
void set(const F32 in_r,
|
||||
const F32 in_g,
|
||||
const F32 in_b,
|
||||
const F32 in_a = 1.0f);
|
||||
|
||||
ColorF& operator*=(const ColorF& in_mul); // Can be useful for lighting
|
||||
ColorF operator*(const ColorF& in_mul) const;
|
||||
ColorF& operator+=(const ColorF& in_rAdd);
|
||||
ColorF operator+(const ColorF& in_rAdd) const;
|
||||
ColorF& operator-=(const ColorF& in_rSub);
|
||||
ColorF operator-(const ColorF& in_rSub) const;
|
||||
|
||||
ColorF& operator*=(const F32 in_mul);
|
||||
ColorF operator*(const F32 in_mul) const;
|
||||
ColorF& operator/=(const F32 in_div);
|
||||
ColorF operator/(const F32 in_div) const;
|
||||
|
||||
ColorF operator-() const;
|
||||
|
||||
bool operator==(const ColorF&) const;
|
||||
bool operator!=(const ColorF&) const;
|
||||
|
||||
operator const F32*() const { return &red; }
|
||||
|
||||
U32 getARGBPack() const;
|
||||
U32 getRGBAPack() const;
|
||||
U32 getBGRAPack() const;
|
||||
|
||||
operator ColorI() const;
|
||||
|
||||
void interpolate(const ColorF& in_rC1,
|
||||
const ColorF& in_rC2,
|
||||
const F32 in_factor);
|
||||
|
||||
bool isValidColor() const { return (red >= 0.0f && red <= 1.0f) &&
|
||||
(green >= 0.0f && green <= 1.0f) &&
|
||||
(blue >= 0.0f && blue <= 1.0f) &&
|
||||
(alpha >= 0.0f && alpha <= 1.0f); }
|
||||
void clamp();
|
||||
};
|
||||
|
||||
|
||||
//-------------------------------------- ColorI's are missing some of the operations
|
||||
// present in ColorF since they cannot recover
|
||||
// properly from over/underflow. Also, structure
|
||||
// designed to be castable to PALETTEENTRY's in
|
||||
// Win32 environments...
|
||||
class ColorI
|
||||
{
|
||||
public:
|
||||
U8 red;
|
||||
U8 green;
|
||||
U8 blue;
|
||||
U8 alpha;
|
||||
|
||||
public:
|
||||
ColorI() { }
|
||||
ColorI(const ColorI& in_rCopy);
|
||||
ColorI(const U8 in_r,
|
||||
const U8 in_g,
|
||||
const U8 in_b,
|
||||
const U8 in_a = U8(255));
|
||||
|
||||
void set(const U8 in_r,
|
||||
const U8 in_g,
|
||||
const U8 in_b,
|
||||
const U8 in_a = U8(255));
|
||||
|
||||
ColorI& operator*=(const F32 in_mul);
|
||||
ColorI operator*(const F32 in_mul) const;
|
||||
|
||||
bool operator==(const ColorI&) const;
|
||||
bool operator!=(const ColorI&) const;
|
||||
|
||||
void interpolate(const ColorI& in_rC1,
|
||||
const ColorI& in_rC2,
|
||||
const F32 in_factor);
|
||||
|
||||
U32 getARGBPack() const;
|
||||
U32 getRGBAPack() const;
|
||||
|
||||
U16 get565() const;
|
||||
U16 get4444() const;
|
||||
|
||||
operator ColorF() const;
|
||||
|
||||
operator const U8*() const { return &red; }
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//-------------------------------------- INLINES (ColorF)
|
||||
//
|
||||
inline void ColorF::set(const F32 in_r,
|
||||
const F32 in_g,
|
||||
const F32 in_b,
|
||||
const F32 in_a)
|
||||
{
|
||||
red = in_r;
|
||||
green = in_g;
|
||||
blue = in_b;
|
||||
alpha = in_a;
|
||||
}
|
||||
|
||||
inline ColorF::ColorF(const ColorF& in_rCopy)
|
||||
{
|
||||
red = in_rCopy.red;
|
||||
green = in_rCopy.green;
|
||||
blue = in_rCopy.blue;
|
||||
alpha = in_rCopy.alpha;
|
||||
}
|
||||
|
||||
inline ColorF::ColorF(const F32 in_r,
|
||||
const F32 in_g,
|
||||
const F32 in_b,
|
||||
const F32 in_a)
|
||||
{
|
||||
set(in_r, in_g, in_b, in_a);
|
||||
}
|
||||
|
||||
inline ColorF& ColorF::operator*=(const ColorF& in_mul)
|
||||
{
|
||||
red *= in_mul.red;
|
||||
green *= in_mul.green;
|
||||
blue *= in_mul.blue;
|
||||
alpha *= in_mul.alpha;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline ColorF ColorF::operator*(const ColorF& in_mul) const
|
||||
{
|
||||
return ColorF(red * in_mul.red,
|
||||
green * in_mul.green,
|
||||
blue * in_mul.blue,
|
||||
alpha * in_mul.alpha);
|
||||
}
|
||||
|
||||
inline ColorF& ColorF::operator+=(const ColorF& in_rAdd)
|
||||
{
|
||||
red += in_rAdd.red;
|
||||
green += in_rAdd.green;
|
||||
blue += in_rAdd.blue;
|
||||
alpha += in_rAdd.alpha;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline ColorF ColorF::operator+(const ColorF& in_rAdd) const
|
||||
{
|
||||
return ColorF(red + in_rAdd.red,
|
||||
green + in_rAdd.green,
|
||||
blue + in_rAdd.blue,
|
||||
alpha + in_rAdd.alpha);
|
||||
}
|
||||
|
||||
inline ColorF& ColorF::operator-=(const ColorF& in_rSub)
|
||||
{
|
||||
red -= in_rSub.red;
|
||||
green -= in_rSub.green;
|
||||
blue -= in_rSub.blue;
|
||||
alpha -= in_rSub.alpha;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline ColorF ColorF::operator-(const ColorF& in_rSub) const
|
||||
{
|
||||
return ColorF(red - in_rSub.red,
|
||||
green - in_rSub.green,
|
||||
blue - in_rSub.blue,
|
||||
alpha - in_rSub.alpha);
|
||||
}
|
||||
|
||||
inline ColorF& ColorF::operator*=(const F32 in_mul)
|
||||
{
|
||||
red *= in_mul;
|
||||
green *= in_mul;
|
||||
blue *= in_mul;
|
||||
alpha *= in_mul;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline ColorF ColorF::operator*(const F32 in_mul) const
|
||||
{
|
||||
return ColorF(red * in_mul,
|
||||
green * in_mul,
|
||||
blue * in_mul,
|
||||
alpha * in_mul);
|
||||
}
|
||||
|
||||
inline ColorF& ColorF::operator/=(const F32 in_div)
|
||||
{
|
||||
AssertFatal(in_div != 0.0f, "Error, div by zero...");
|
||||
F32 inv = 1.0f / in_div;
|
||||
|
||||
red *= inv;
|
||||
green *= inv;
|
||||
blue *= inv;
|
||||
alpha *= inv;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline ColorF ColorF::operator/(const F32 in_div) const
|
||||
{
|
||||
AssertFatal(in_div != 0.0f, "Error, div by zero...");
|
||||
F32 inv = 1.0f / in_div;
|
||||
|
||||
return ColorF(red * inv,
|
||||
green * inv,
|
||||
blue * inv,
|
||||
alpha * inv);
|
||||
}
|
||||
|
||||
inline ColorF ColorF::operator-() const
|
||||
{
|
||||
return ColorF(-red, -green, -blue, -alpha);
|
||||
}
|
||||
|
||||
inline bool ColorF::operator==(const ColorF& in_Cmp) const
|
||||
{
|
||||
return (red == in_Cmp.red && green == in_Cmp.green && blue == in_Cmp.blue && alpha == in_Cmp.alpha);
|
||||
}
|
||||
|
||||
inline bool ColorF::operator!=(const ColorF& in_Cmp) const
|
||||
{
|
||||
return (red != in_Cmp.red || green != in_Cmp.green || blue != in_Cmp.blue || alpha != in_Cmp.alpha);
|
||||
}
|
||||
|
||||
inline U32 ColorF::getARGBPack() const
|
||||
{
|
||||
return (U32(alpha * 255.0f + 0.5) << 24) |
|
||||
(U32(red * 255.0f + 0.5) << 16) |
|
||||
(U32(green * 255.0f + 0.5) << 8) |
|
||||
(U32(blue * 255.0f + 0.5) << 0);
|
||||
}
|
||||
|
||||
inline U32 ColorF::getRGBAPack() const
|
||||
{
|
||||
return (U32(alpha * 255.0f + 0.5) << 0) |
|
||||
(U32(red * 255.0f + 0.5) << 24) |
|
||||
(U32(green * 255.0f + 0.5) << 16) |
|
||||
(U32(blue * 255.0f + 0.5) << 8);
|
||||
}
|
||||
|
||||
inline U32 ColorF::getBGRAPack() const
|
||||
{
|
||||
return (U32(alpha * 255.0f + 0.5) << 0) |
|
||||
(U32(red * 255.0f + 0.5) << 8) |
|
||||
(U32(green * 255.0f + 0.5) << 16) |
|
||||
(U32(blue * 255.0f + 0.5) << 24);
|
||||
}
|
||||
|
||||
inline void ColorF::interpolate(const ColorF& in_rC1,
|
||||
const ColorF& in_rC2,
|
||||
const F32 in_factor)
|
||||
{
|
||||
F32 f2 = 1.0f - in_factor;
|
||||
red = (in_rC1.red * f2) + (in_rC2.red * in_factor);
|
||||
green = (in_rC1.green * f2) + (in_rC2.green * in_factor);
|
||||
blue = (in_rC1.blue * f2) + (in_rC2.blue * in_factor);
|
||||
alpha = (in_rC1.alpha * f2) + (in_rC2.alpha * in_factor);
|
||||
}
|
||||
|
||||
inline void ColorF::clamp()
|
||||
{
|
||||
if (red > 1.0f)
|
||||
red = 1.0f;
|
||||
else if (red < 0.0f)
|
||||
red = 0.0f;
|
||||
|
||||
if (green > 1.0f)
|
||||
green = 1.0f;
|
||||
else if (green < 0.0f)
|
||||
green = 0.0f;
|
||||
|
||||
if (blue > 1.0f)
|
||||
blue = 1.0f;
|
||||
else if (blue < 0.0f)
|
||||
blue = 0.0f;
|
||||
|
||||
if (alpha > 1.0f)
|
||||
alpha = 1.0f;
|
||||
else if (alpha < 0.0f)
|
||||
alpha = 0.0f;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//-------------------------------------- INLINES (ColorI)
|
||||
//
|
||||
inline void ColorI::set(const U8 in_r,
|
||||
const U8 in_g,
|
||||
const U8 in_b,
|
||||
const U8 in_a)
|
||||
{
|
||||
red = in_r;
|
||||
green = in_g;
|
||||
blue = in_b;
|
||||
alpha = in_a;
|
||||
}
|
||||
|
||||
inline ColorI::ColorI(const ColorI& in_rCopy)
|
||||
{
|
||||
red = in_rCopy.red;
|
||||
green = in_rCopy.green;
|
||||
blue = in_rCopy.blue;
|
||||
alpha = in_rCopy.alpha;
|
||||
}
|
||||
|
||||
inline ColorI::ColorI(const U8 in_r,
|
||||
const U8 in_g,
|
||||
const U8 in_b,
|
||||
const U8 in_a)
|
||||
{
|
||||
set(in_r, in_g, in_b, in_a);
|
||||
}
|
||||
|
||||
inline ColorI& ColorI::operator*=(const F32 in_mul)
|
||||
{
|
||||
red = U8((F32(red) * in_mul) + 0.5f);
|
||||
green = U8((F32(green) * in_mul) + 0.5f);
|
||||
blue = U8((F32(blue) * in_mul) + 0.5f);
|
||||
alpha = U8((F32(alpha) * in_mul) + 0.5f);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline ColorI ColorI::operator*(const F32 in_mul) const
|
||||
{
|
||||
ColorI temp(*this);
|
||||
temp *= in_mul;
|
||||
return temp;
|
||||
}
|
||||
|
||||
inline bool ColorI::operator==(const ColorI& in_Cmp) const
|
||||
{
|
||||
return (red == in_Cmp.red && green == in_Cmp.green && blue == in_Cmp.blue && alpha == in_Cmp.alpha);
|
||||
}
|
||||
|
||||
inline bool ColorI::operator!=(const ColorI& in_Cmp) const
|
||||
{
|
||||
return (red != in_Cmp.red || green != in_Cmp.green || blue != in_Cmp.blue || alpha != in_Cmp.alpha);
|
||||
}
|
||||
|
||||
inline void ColorI::interpolate(const ColorI& in_rC1,
|
||||
const ColorI& in_rC2,
|
||||
const F32 in_factor)
|
||||
{
|
||||
F32 f2= 1.0f - in_factor;
|
||||
red = U8(((F32(in_rC1.red) * f2) + (F32(in_rC2.red) * in_factor)) + 0.5f);
|
||||
green = U8(((F32(in_rC1.green) * f2) + (F32(in_rC2.green) * in_factor)) + 0.5f);
|
||||
blue = U8(((F32(in_rC1.blue) * f2) + (F32(in_rC2.blue) * in_factor)) + 0.5f);
|
||||
alpha = U8(((F32(in_rC1.alpha) * f2) + (F32(in_rC2.alpha) * in_factor)) + 0.5f);
|
||||
}
|
||||
|
||||
inline U32 ColorI::getARGBPack() const
|
||||
{
|
||||
return (U32(alpha) << 24) |
|
||||
(U32(red) << 16) |
|
||||
(U32(green) << 8) |
|
||||
(U32(blue) << 0);
|
||||
}
|
||||
|
||||
inline U32 ColorI::getRGBAPack() const
|
||||
{
|
||||
return (U32(alpha) << 0) |
|
||||
(U32(red) << 24) |
|
||||
(U32(green) << 16) |
|
||||
(U32(blue) << 8);
|
||||
}
|
||||
|
||||
inline U16 ColorI::get565() const
|
||||
{
|
||||
return U16((U16(red >> 3) << 11) |
|
||||
(U16(green >> 2) << 5) |
|
||||
(U16(blue >> 3) << 0));
|
||||
}
|
||||
|
||||
inline U16 ColorI::get4444() const
|
||||
{
|
||||
return U16(U16(U16(alpha >> 4) << 12) |
|
||||
U16(U16(red >> 4) << 8) |
|
||||
U16(U16(green >> 4) << 4) |
|
||||
U16(U16(blue >> 4) << 0));
|
||||
}
|
||||
|
||||
//-------------------------------------- INLINE CONVERSION OPERATORS
|
||||
inline ColorF::operator ColorI() const
|
||||
{
|
||||
return ColorI(U8(red * 255.0f + 0.5),
|
||||
U8(green * 255.0f + 0.5),
|
||||
U8(blue * 255.0f + 0.5),
|
||||
U8(alpha * 255.0f + 0.5));
|
||||
}
|
||||
|
||||
inline ColorI::operator ColorF() const
|
||||
{
|
||||
const F32 inv255 = 1.0f / 255.0f;
|
||||
|
||||
return ColorF(F32(red) * inv255,
|
||||
F32(green) * inv255,
|
||||
F32(blue) * inv255,
|
||||
F32(alpha) * inv255);
|
||||
}
|
||||
|
||||
#endif //_COLOR_H_
|
||||
66
core/coreRes.h
Normal file
66
core/coreRes.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _CORERES_H_
|
||||
#define _CORERES_H_
|
||||
|
||||
#ifndef _RESMANAGER_H_
|
||||
#include "Core/resManager.h"
|
||||
#endif
|
||||
|
||||
class RawData
|
||||
{
|
||||
private:
|
||||
bool ownMemory;
|
||||
|
||||
public:
|
||||
char *data;
|
||||
S32 size;
|
||||
|
||||
RawData() { ownMemory = false; }
|
||||
RawData(Stream &s, S32 sz) {
|
||||
ownMemory = true;
|
||||
size = sz;
|
||||
data = new char[size];
|
||||
if (data)
|
||||
s.read(size, data);
|
||||
}
|
||||
~RawData() {
|
||||
if (ownMemory)
|
||||
delete [] data;
|
||||
data = NULL;
|
||||
ownMemory = false;
|
||||
size = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//-------------------------------------- RawData type
|
||||
class ResourceTypeRawData : public ResourceType
|
||||
{
|
||||
public:
|
||||
ResourceTypeRawData(const char *ext = ".dat"):
|
||||
ResourceType( ResourceType::typeof(ext) ) { }
|
||||
void* construct(Stream *stream, S32 size)
|
||||
{ return (void*)new RawData(*stream, size); }
|
||||
void destruct(void *p)
|
||||
{ delete (RawData*)p; }
|
||||
};
|
||||
|
||||
class ResourceTypeStaticRawData : public ResourceType
|
||||
{
|
||||
public:
|
||||
ResourceTypeStaticRawData(const char *ext = ".sdt"):
|
||||
ResourceType( ResourceType::typeof(ext) ) { }
|
||||
void* construct(Stream *stream, S32 size)
|
||||
{ return (void*)new RawData(*stream, size); }
|
||||
void destruct(void *p)
|
||||
{ }
|
||||
};
|
||||
|
||||
#endif //_CORERES_H_
|
||||
|
||||
74
core/crc.cc
Normal file
74
core/crc.cc
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "core/stream.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// simple crc function - generates lookup table on first call
|
||||
|
||||
static U32 crcTable[256];
|
||||
static bool crcTableValid;
|
||||
|
||||
static void calculateCRCTable()
|
||||
{
|
||||
U32 val;
|
||||
|
||||
for(S32 i = 0; i < 256; i++)
|
||||
{
|
||||
val = i;
|
||||
for(S32 j = 0; j < 8; j++)
|
||||
{
|
||||
if(val & 0x01)
|
||||
val = 0xedb88320 ^ (val >> 1);
|
||||
else
|
||||
val = val >> 1;
|
||||
}
|
||||
crcTable[i] = val;
|
||||
}
|
||||
|
||||
crcTableValid = true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
U32 calculateCRC(const void * buffer, S32 len, U32 crcVal )
|
||||
{
|
||||
// check if need to generate the crc table
|
||||
if(!crcTableValid)
|
||||
calculateCRCTable();
|
||||
|
||||
// now calculate the crc
|
||||
char * buf = (char*)buffer;
|
||||
for(S32 i = 0; i < len; i++)
|
||||
crcVal = crcTable[(crcVal ^ buf[i]) & 0xff] ^ (crcVal >> 8);
|
||||
return(crcVal);
|
||||
}
|
||||
|
||||
U32 calculateCRCStream(Stream *stream, U32 crcVal )
|
||||
{
|
||||
// check if need to generate the crc table
|
||||
if(!crcTableValid)
|
||||
calculateCRCTable();
|
||||
|
||||
// now calculate the crc
|
||||
stream->setPosition(0);
|
||||
S32 len = stream->getStreamSize();
|
||||
U8 buf[4096];
|
||||
|
||||
S32 segCount = (len + 4095) / 4096;
|
||||
|
||||
for(S32 j = 0; j < segCount; j++)
|
||||
{
|
||||
S32 slen = getMin(4096, len - (j * 4096));
|
||||
stream->read(slen, buf);
|
||||
crcVal = calculateCRC(buf, slen, crcVal);
|
||||
}
|
||||
stream->setPosition(0);
|
||||
return(crcVal);
|
||||
}
|
||||
19
core/crc.h
Normal file
19
core/crc.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _CRC_H_
|
||||
#define _CRC_H_
|
||||
|
||||
#define INITIAL_CRC_VALUE 0xffffffff
|
||||
|
||||
class Stream;
|
||||
|
||||
U32 calculateCRC(const void * buffer, S32 len, U32 crcVal = INITIAL_CRC_VALUE);
|
||||
U32 calculateCRCStream(Stream *stream, U32 crcVal = INITIAL_CRC_VALUE);
|
||||
|
||||
#endif
|
||||
|
||||
61
core/dataChunker.cc
Normal file
61
core/dataChunker.cc
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "Platform/platform.h"
|
||||
#include "Core/dataChunker.h"
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
DataChunker::DataChunker(S32 size)
|
||||
{
|
||||
chunkSize = size;
|
||||
curBlock = new DataBlock(size);
|
||||
curBlock->next = NULL;
|
||||
curBlock->curIndex = 0;
|
||||
}
|
||||
|
||||
DataChunker::~DataChunker()
|
||||
{
|
||||
freeBlocks();
|
||||
}
|
||||
|
||||
void *DataChunker::alloc(S32 size)
|
||||
{
|
||||
AssertFatal(size <= chunkSize, "Data chunk too large.");
|
||||
if(!curBlock || size + curBlock->curIndex > chunkSize)
|
||||
{
|
||||
DataBlock *temp = new DataBlock(chunkSize);
|
||||
temp->next = curBlock;
|
||||
temp->curIndex = 0;
|
||||
curBlock = temp;
|
||||
}
|
||||
void *ret = curBlock->data + curBlock->curIndex;
|
||||
curBlock->curIndex += (size + 3) & ~3; // dword align
|
||||
return ret;
|
||||
}
|
||||
|
||||
DataChunker::DataBlock::DataBlock(S32 size)
|
||||
{
|
||||
data = new U8[size];
|
||||
}
|
||||
|
||||
DataChunker::DataBlock::~DataBlock()
|
||||
{
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
void DataChunker::freeBlocks()
|
||||
{
|
||||
while(curBlock)
|
||||
{
|
||||
DataBlock *temp = curBlock->next;
|
||||
delete curBlock;
|
||||
curBlock = temp;
|
||||
}
|
||||
}
|
||||
|
||||
53
core/dataChunker.h
Normal file
53
core/dataChunker.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _DATACHUNKER_H_
|
||||
#define _DATACHUNKER_H_
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
class DataChunker
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
ChunkSize = 16376
|
||||
};
|
||||
|
||||
private:
|
||||
struct DataBlock
|
||||
{
|
||||
DataBlock *next;
|
||||
U8 *data;
|
||||
S32 curIndex;
|
||||
DataBlock(S32 size);
|
||||
~DataBlock();
|
||||
};
|
||||
DataBlock *curBlock;
|
||||
S32 chunkSize;
|
||||
public:
|
||||
void *alloc(S32 size);
|
||||
void freeBlocks();
|
||||
|
||||
DataChunker(S32 size=ChunkSize);
|
||||
~DataChunker();
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
template<class T>
|
||||
class Chunker: private DataChunker
|
||||
{
|
||||
public:
|
||||
Chunker(S32 size = DataChunker::ChunkSize) : DataChunker(size) {};
|
||||
T* alloc() { return reinterpret_cast<T*>(DataChunker::alloc(S32(sizeof(T)))); }
|
||||
void clear() { freeBlocks(); };
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
260
core/dnet.cc
Normal file
260
core/dnet.cc
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "core/dnet.h"
|
||||
#include "console/console.h"
|
||||
#include "core/bitStream.h"
|
||||
#include "console/consoleTypes.h"
|
||||
|
||||
bool gLogToConsole = false;
|
||||
|
||||
enum NetPacketType
|
||||
{
|
||||
DataPacket,
|
||||
PingPacket,
|
||||
AckPacket,
|
||||
InvalidPacketType,
|
||||
};
|
||||
|
||||
static const char *packetTypeNames[] =
|
||||
{
|
||||
"DataPacket",
|
||||
"PingPacket",
|
||||
"AckPacket",
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
//-----------------------------------------------------------------
|
||||
//-----------------------------------------------------------------
|
||||
ConsoleFunction(DNetSetLogging, void, 2, 2, "DNetSetLogging(bool);")
|
||||
{
|
||||
argc;
|
||||
gLogToConsole = dAtob(argv[1]);
|
||||
}
|
||||
|
||||
ConnectionProtocol::ConnectionProtocol()
|
||||
{
|
||||
mLastSeqRecvd = 0;
|
||||
mHighestAckedSeq = 0;
|
||||
mLastSendSeq = 0; // start sending at 1
|
||||
mAckMask = 0;
|
||||
mLastRecvAckAck = 0;
|
||||
}
|
||||
|
||||
void ConnectionProtocol::buildSendPacketHeader(BitStream *stream, S32 packetType)
|
||||
{
|
||||
S32 ackByteCount = ((mLastSeqRecvd - mLastRecvAckAck + 7) >> 3);
|
||||
AssertFatal(ackByteCount <= 4, "Doh!");
|
||||
|
||||
S32 headerSize = 3 + ackByteCount;
|
||||
S32 datalen = 0;
|
||||
|
||||
if(packetType == DataPacket)
|
||||
mLastSendSeq++;
|
||||
|
||||
stream->writeFlag(true);
|
||||
stream->writeInt(mConnectSequence & 1, 1);
|
||||
stream->writeInt(mLastSendSeq, 9);
|
||||
stream->writeInt(mLastSeqRecvd, 9);
|
||||
stream->writeInt(packetType, 2);
|
||||
stream->writeInt(ackByteCount, 3);
|
||||
stream->writeInt(mAckMask, ackByteCount * 8);
|
||||
|
||||
// if we're resending this header, we can't advance the
|
||||
// sequence recieved (in case this packet drops and the prev one
|
||||
// goes through)
|
||||
|
||||
if(gLogToConsole)
|
||||
Con::printf("build hdr %d %d", mLastSendSeq, packetType);
|
||||
|
||||
if(packetType == DataPacket)
|
||||
mLastSeqRecvdAtSend[mLastSendSeq & 0x1F] = mLastSeqRecvd;
|
||||
}
|
||||
|
||||
void ConnectionProtocol::sendPingPacket()
|
||||
{
|
||||
U8 buffer[16];
|
||||
BitStream bs(buffer, 16);
|
||||
buildSendPacketHeader(&bs, PingPacket);
|
||||
if(gLogToConsole)
|
||||
Con::printf("send ping %d", mLastSendSeq);
|
||||
|
||||
sendPacket(&bs);
|
||||
}
|
||||
|
||||
void ConnectionProtocol::sendAckPacket()
|
||||
{
|
||||
U8 buffer[16];
|
||||
BitStream bs(buffer, 16);
|
||||
buildSendPacketHeader(&bs, AckPacket);
|
||||
if(gLogToConsole)
|
||||
Con::printf("send ack %d", mLastSendSeq);
|
||||
|
||||
sendPacket(&bs);
|
||||
}
|
||||
|
||||
// packets are read directly into the data portion of
|
||||
// connection notify packets... makes the events easier to post into
|
||||
// the system.
|
||||
|
||||
void ConnectionProtocol::processRawPacket(BitStream *pstream)
|
||||
{
|
||||
// read in the packet header:
|
||||
|
||||
// Fixed packet header: 3 bytes
|
||||
//
|
||||
// 1 bit game packet flag
|
||||
// 1 bit connect sequence
|
||||
// 9 bits packet seq number
|
||||
// 9 bits ackstart seq number
|
||||
// 2 bits packet type
|
||||
// 2 bits ack byte count
|
||||
//
|
||||
// type is:
|
||||
// 00 data packet
|
||||
// 01 ping packet
|
||||
// 02 ack packet
|
||||
|
||||
// next 1-4 bytes are ack flags
|
||||
//
|
||||
// header len is 4-9 bytes
|
||||
// average case 4 byte header
|
||||
|
||||
pstream->readFlag(); // get rid of the game info packet bit
|
||||
U32 pkConnectSeqBit = pstream->readInt(1);
|
||||
U32 pkSequenceNumber = pstream->readInt(9);
|
||||
U32 pkHighestAck = pstream->readInt(9);
|
||||
U32 pkPacketType = pstream->readInt(2);
|
||||
S32 pkAckByteCount = pstream->readInt(3);
|
||||
|
||||
// check connection sequence bit
|
||||
if(pkConnectSeqBit != (mConnectSequence & 1))
|
||||
return;
|
||||
|
||||
if(pkAckByteCount > 4 || pkPacketType >= InvalidPacketType)
|
||||
return;
|
||||
|
||||
S32 pkAckMask = pstream->readInt(8 * pkAckByteCount);
|
||||
|
||||
// verify packet ordering and acking and stuff
|
||||
// check if the 9-bit sequence is within the packet window
|
||||
// (within 31 packets of the last received sequence number).
|
||||
|
||||
pkSequenceNumber |= (mLastSeqRecvd & 0xFFFFFE00);
|
||||
// account for wrap around
|
||||
if(pkSequenceNumber < mLastSeqRecvd)
|
||||
pkSequenceNumber += 0x200;
|
||||
|
||||
if(pkSequenceNumber > mLastSeqRecvd + 31)
|
||||
{
|
||||
// the sequence number is outside the window... must be out of order
|
||||
// discard.
|
||||
return;
|
||||
}
|
||||
|
||||
pkHighestAck |= (mHighestAckedSeq & 0xFFFFFE00);
|
||||
// account for wrap around
|
||||
|
||||
if(pkHighestAck < mHighestAckedSeq)
|
||||
pkHighestAck += 0x200;
|
||||
|
||||
if(pkHighestAck > mLastSendSeq)
|
||||
{
|
||||
// the ack number is outside the window... must be an out of order
|
||||
// packet, discard.
|
||||
return;
|
||||
}
|
||||
|
||||
if(gLogToConsole)
|
||||
{
|
||||
for(U32 i = mLastSeqRecvd+1; i < pkSequenceNumber; i++)
|
||||
Con::printf("Not recv %d", i);
|
||||
Con::printf("Recv %d %s", pkSequenceNumber, packetTypeNames[pkPacketType]);
|
||||
}
|
||||
|
||||
// shift up the ack mask by the packet difference
|
||||
// this essentially nacks all the packets dropped
|
||||
|
||||
mAckMask <<= pkSequenceNumber - mLastSeqRecvd;
|
||||
|
||||
// if this packet is a data packet (i.e. not a ping packet or an ack packet), ack it
|
||||
if(pkPacketType == DataPacket)
|
||||
mAckMask |= 1;
|
||||
|
||||
// do all the notifies...
|
||||
for(U32 i = mHighestAckedSeq+1; i <= pkHighestAck; i++)
|
||||
{
|
||||
bool packetTransmitSuccess = pkAckMask & (1 << (pkHighestAck - i));
|
||||
handleNotify(packetTransmitSuccess);
|
||||
if(gLogToConsole)
|
||||
Con::printf("Ack %d %d", i, packetTransmitSuccess);
|
||||
|
||||
if(packetTransmitSuccess)
|
||||
{
|
||||
mLastRecvAckAck = mLastSeqRecvdAtSend[i & 0x1F];
|
||||
if(!mConnectionEstablished)
|
||||
{
|
||||
mConnectionEstablished = true;
|
||||
handleConnectionEstablished();
|
||||
}
|
||||
}
|
||||
}
|
||||
// the other side knows more about its window than we do.
|
||||
if(pkSequenceNumber - mLastRecvAckAck > 32)
|
||||
mLastRecvAckAck = pkSequenceNumber - 32;
|
||||
|
||||
mHighestAckedSeq = pkHighestAck;
|
||||
|
||||
// first things first...
|
||||
// ackback any pings or accept connects
|
||||
|
||||
if(pkPacketType == PingPacket)
|
||||
{
|
||||
// send an ack to the other side
|
||||
// the ack will have the same packet sequence as our last sent packet
|
||||
// if the last packet we sent was the connection accepted packet
|
||||
// we must resend that packet
|
||||
sendAckPacket();
|
||||
}
|
||||
keepAlive(); // notification that the connection is ok
|
||||
|
||||
if(mLastSeqRecvd != pkSequenceNumber && pkPacketType == DataPacket)
|
||||
handlePacket(pstream);
|
||||
|
||||
mLastSeqRecvd = pkSequenceNumber;
|
||||
}
|
||||
|
||||
bool ConnectionProtocol::windowFull()
|
||||
{
|
||||
return mLastSendSeq - mHighestAckedSeq >= 30;
|
||||
}
|
||||
|
||||
void ConnectionProtocol::writeDemoStartBlock(ResizeBitStream *stream)
|
||||
{
|
||||
for(U32 i = 0; i < 32; i++)
|
||||
stream->write(mLastSeqRecvdAtSend[i]);
|
||||
stream->write(mLastSeqRecvd);
|
||||
stream->write(mHighestAckedSeq);
|
||||
stream->write(mLastSendSeq);
|
||||
stream->write(mAckMask);
|
||||
stream->write(mConnectSequence);
|
||||
stream->write(mLastRecvAckAck);
|
||||
stream->write(mConnectionEstablished);
|
||||
}
|
||||
|
||||
void ConnectionProtocol::readDemoStartBlock(BitStream *stream)
|
||||
{
|
||||
for(U32 i = 0; i < 32; i++)
|
||||
stream->read(&mLastSeqRecvdAtSend[i]);
|
||||
stream->read(&mLastSeqRecvd);
|
||||
stream->read(&mHighestAckedSeq);
|
||||
stream->read(&mLastSendSeq);
|
||||
stream->read(&mAckMask);
|
||||
stream->read(&mConnectSequence);
|
||||
stream->read(&mLastRecvAckAck);
|
||||
stream->read(&mConnectionEstablished);
|
||||
}
|
||||
56
core/dnet.h
Normal file
56
core/dnet.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _DNET_H_
|
||||
#define _DNET_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "Platform/platform.h"
|
||||
#endif
|
||||
#ifndef _EVENT_H_
|
||||
#include "Platform/event.h"
|
||||
#endif
|
||||
|
||||
class BitStream;
|
||||
class ResizeBitStream;
|
||||
|
||||
class ConnectionProtocol
|
||||
{
|
||||
protected:
|
||||
U32 mLastSeqRecvdAtSend[32];
|
||||
U32 mLastSeqRecvd;
|
||||
U32 mHighestAckedSeq;
|
||||
U32 mLastSendSeq;
|
||||
U32 mAckMask;
|
||||
U32 mConnectSequence;
|
||||
U32 mLastRecvAckAck;
|
||||
bool mConnectionEstablished;
|
||||
public:
|
||||
ConnectionProtocol();
|
||||
|
||||
void buildSendPacketHeader(BitStream *bstream, S32 packetType = 0);
|
||||
|
||||
void sendPingPacket();
|
||||
void sendAckPacket();
|
||||
void setConnectionEstablished() { mConnectionEstablished = true; }
|
||||
|
||||
bool windowFull();
|
||||
bool connectionEstablished();
|
||||
void setConnectSequence(U32 connectSeq) { mConnectSequence = connectSeq; }
|
||||
|
||||
virtual void writeDemoStartBlock(ResizeBitStream *stream);
|
||||
virtual void readDemoStartBlock(BitStream *stream);
|
||||
|
||||
virtual void processRawPacket(BitStream *bstream);
|
||||
virtual Net::Error sendPacket(BitStream *bstream) = 0;
|
||||
virtual void keepAlive() = 0;
|
||||
virtual void handleConnectionEstablished() = 0;
|
||||
virtual void handleNotify(bool recvd) = 0;
|
||||
virtual void handlePacket(BitStream *bstream) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
156
core/fileObject.cc
Normal file
156
core/fileObject.cc
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "Core/fileObject.h"
|
||||
|
||||
IMPLEMENT_CONOBJECT(FileObject);
|
||||
|
||||
bool FileObject::isEOF()
|
||||
{
|
||||
return mCurPos == mBufferSize;
|
||||
}
|
||||
|
||||
FileObject::FileObject()
|
||||
{
|
||||
mFileBuffer = NULL;
|
||||
mBufferSize = 0;
|
||||
mCurPos = 0;
|
||||
}
|
||||
|
||||
FileObject::~FileObject()
|
||||
{
|
||||
dFree(mFileBuffer);
|
||||
}
|
||||
|
||||
void FileObject::close()
|
||||
{
|
||||
stream.close();
|
||||
dFree(mFileBuffer);
|
||||
mFileBuffer = NULL;
|
||||
mBufferSize = mCurPos = 0;
|
||||
}
|
||||
|
||||
bool FileObject::openForWrite(const char *fileName, const bool append)
|
||||
{
|
||||
close();
|
||||
if ( !append )
|
||||
return( ResourceManager->openFileForWrite(stream, NULL, fileName) );
|
||||
|
||||
// Use the WriteAppend flag so it doesn't clobber the existing file:
|
||||
if ( !ResourceManager->openFileForWrite(stream, NULL, fileName, File::WriteAppend) )
|
||||
return( false );
|
||||
|
||||
stream.setPosition( stream.getStreamSize() );
|
||||
return( true );
|
||||
}
|
||||
|
||||
bool FileObject::openForRead(const char* /*fileName*/)
|
||||
{
|
||||
AssertFatal(false, "Error, not yet implemented!");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FileObject::readMemory(const char *fileName)
|
||||
{
|
||||
close();
|
||||
Stream *s = ResourceManager->openStream(fileName);
|
||||
if(!s)
|
||||
return false;
|
||||
mBufferSize = ResourceManager->getSize(fileName);
|
||||
mFileBuffer = (U8 *) dMalloc(mBufferSize + 1);
|
||||
mFileBuffer[mBufferSize] = 0;
|
||||
s->read(mBufferSize, mFileBuffer);
|
||||
ResourceManager->closeStream(s);
|
||||
mCurPos = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const U8 *FileObject::readLine()
|
||||
{
|
||||
if(!mFileBuffer)
|
||||
return (U8 *) "";
|
||||
U32 tokPos = mCurPos;
|
||||
for(;;)
|
||||
{
|
||||
if(mCurPos == mBufferSize)
|
||||
break;
|
||||
if(mFileBuffer[mCurPos] == '\r')
|
||||
{
|
||||
mFileBuffer[mCurPos++] = 0;
|
||||
if(mFileBuffer[mCurPos] == '\n')
|
||||
mCurPos++;
|
||||
break;
|
||||
}
|
||||
if(mFileBuffer[mCurPos] == '\n')
|
||||
{
|
||||
mFileBuffer[mCurPos++] = 0;
|
||||
break;
|
||||
}
|
||||
mCurPos++;
|
||||
}
|
||||
return mFileBuffer + tokPos;
|
||||
}
|
||||
|
||||
void FileObject::writeLine(const U8 *line)
|
||||
{
|
||||
stream.write(dStrlen((const char *) line), line);
|
||||
stream.write(2, "\r\n");
|
||||
}
|
||||
|
||||
static bool cFileOpenRead(SimObject *obj, S32, const char **argv)
|
||||
{
|
||||
FileObject *fo = (FileObject *) obj;
|
||||
return fo->readMemory(argv[2]);
|
||||
}
|
||||
|
||||
static bool cFileOpenWrite(SimObject *obj, S32, const char **argv)
|
||||
{
|
||||
FileObject *fo = (FileObject *) obj;
|
||||
return fo->openForWrite(argv[2]);
|
||||
}
|
||||
|
||||
static bool cFileOpenAppend(SimObject *obj, S32, const char **argv)
|
||||
{
|
||||
FileObject *fo = (FileObject *) obj;
|
||||
return fo->openForWrite(argv[2], true);
|
||||
}
|
||||
|
||||
static bool cFileIsEOF(SimObject *obj, S32, const char **)
|
||||
{
|
||||
FileObject *fo = (FileObject *) obj;
|
||||
return fo->isEOF();
|
||||
}
|
||||
|
||||
static const char * cFileReadLine(SimObject *obj, S32, const char **)
|
||||
{
|
||||
FileObject *fo = (FileObject *) obj;
|
||||
return (const char *) fo->readLine();
|
||||
}
|
||||
|
||||
static void cFileWriteLine(SimObject *obj, S32, const char **argv)
|
||||
{
|
||||
FileObject *fo = (FileObject *) obj;
|
||||
fo->writeLine((const U8 *) argv[2]);
|
||||
}
|
||||
|
||||
static void cFileClose(SimObject *obj, S32, const char **)
|
||||
{
|
||||
FileObject *fo = (FileObject *) obj;
|
||||
fo->close();
|
||||
}
|
||||
|
||||
void FileObject::consoleInit()
|
||||
{
|
||||
Con::addCommand("FileObject", "openForRead", cFileOpenRead, "file.openForRead(fileName)", 3, 3);
|
||||
Con::addCommand("FileObject", "openForWrite", cFileOpenWrite, "file.openForWrite(fileName", 3, 3);
|
||||
Con::addCommand("FileObject", "openForAppend", cFileOpenAppend, "file.openForAppend(fileName)", 3, 3);
|
||||
Con::addCommand("FileObject", "writeLine", cFileWriteLine, "file.writeLine(text)", 3, 3);
|
||||
Con::addCommand("FileObject", "isEOF", cFileIsEOF, "file.isEOF()", 2, 2);
|
||||
Con::addCommand("FileObject", "readLine", cFileReadLine, "file.readLine()", 2, 2);
|
||||
Con::addCommand("FileObject", "close", cFileClose, "file.close()", 2, 2);
|
||||
}
|
||||
45
core/fileObject.h
Normal file
45
core/fileObject.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _FILEOBJECT_H_
|
||||
#define _FILEOBJECT_H_
|
||||
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
#ifndef _RESMANAGER_H_
|
||||
#include "Core/resManager.h"
|
||||
#endif
|
||||
#ifndef _FILESTREAM_H_
|
||||
#include "Core/fileStream.h"
|
||||
#endif
|
||||
|
||||
class FileObject : public SimObject
|
||||
{
|
||||
typedef SimObject Parent;
|
||||
U8 *mFileBuffer;
|
||||
U32 mBufferSize;
|
||||
U32 mCurPos;
|
||||
FileStream stream;
|
||||
public:
|
||||
FileObject();
|
||||
~FileObject();
|
||||
|
||||
bool openForWrite(const char *fileName, const bool append = false);
|
||||
bool openForRead(const char *fileName);
|
||||
bool readMemory(const char *fileName);
|
||||
const U8 *readLine();
|
||||
bool isEOF();
|
||||
void writeLine(const U8 *line);
|
||||
void close();
|
||||
|
||||
static void consoleInit();
|
||||
|
||||
DECLARE_CONOBJECT(FileObject);
|
||||
};
|
||||
|
||||
#endif
|
||||
504
core/fileStream.cc
Normal file
504
core/fileStream.cc
Normal file
|
|
@ -0,0 +1,504 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/*******************************************************************************
|
||||
** FILENAME: D:\Tribes\darkstar\Core\fileStream.cc
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* CREATED: 07/26/99 08:43:29
|
||||
*
|
||||
* BY: PeteW
|
||||
*/
|
||||
|
||||
#include "Core/fileStream.h"
|
||||
#include "Platform/platform.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FileStream methods...
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
FileStream::FileStream()
|
||||
{
|
||||
// initialize the file stream
|
||||
init();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
FileStream::~FileStream()
|
||||
{
|
||||
// make sure the file stream is closed
|
||||
close();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool FileStream::hasCapability(const Capability i_cap) const
|
||||
{
|
||||
return(0 != (U32(i_cap) & mStreamCaps));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
U32 FileStream::getPosition() const
|
||||
{
|
||||
AssertFatal(0 != mStreamCaps, "FileStream::getPosition: the stream isn't open");
|
||||
AssertFatal(true == hasCapability(StreamPosition), "FileStream::getPosition(): lacks positioning capability");
|
||||
|
||||
// return the position inside the buffer if its valid, otherwise return the underlying file position
|
||||
return((BUFFER_INVALID != mBuffHead) ? mBuffPos : mFile.getPosition());
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool FileStream::setPosition(const U32 i_newPosition)
|
||||
{
|
||||
AssertFatal(0 != mStreamCaps, "FileStream::setPosition: the stream isn't open");
|
||||
AssertFatal(true == hasCapability(StreamPosition), "FileStream::setPosition: lacks positioning capability");
|
||||
|
||||
// if the buffer is valid, test the new position against the bounds of the buffer
|
||||
if ((BUFFER_INVALID != mBuffHead) && (i_newPosition >= mBuffHead) && (i_newPosition <= mBuffTail))
|
||||
{
|
||||
// set the position and return
|
||||
mBuffPos = i_newPosition;
|
||||
return(true);
|
||||
}
|
||||
// otherwise the new position lies in some block not in memory
|
||||
else
|
||||
{
|
||||
// flush the buffer if its dirty
|
||||
if (true == mDirty)
|
||||
flush();
|
||||
// and clear out the state of the file stream
|
||||
clearBuffer();
|
||||
|
||||
// and set the new position
|
||||
mFile.setPosition((S32)i_newPosition);
|
||||
// update the stream to reflect the file's state
|
||||
setStatus();
|
||||
// taking end-of-file into consideration
|
||||
if ((S32)EOS == (S32)(mFile.getStatus()))
|
||||
mEOF = true;
|
||||
// and return good states
|
||||
return(Ok == getStatus() || EOS == getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
U32 FileStream::getStreamSize()
|
||||
{
|
||||
AssertWarn(0 != mStreamCaps, "FileStream::getStreamSize: the stream isn't open");
|
||||
AssertFatal((BUFFER_INVALID != mBuffHead && true == mDirty) || false == mDirty, "FileStream::getStreamSize: buffer must be valid if its dirty");
|
||||
|
||||
// the stream size may not match the size on-disk if its been written to...
|
||||
if (true == mDirty)
|
||||
return(getMax(mFile.getSize(), mBuffTail + 1));
|
||||
// otherwise just get the size on disk...
|
||||
else
|
||||
return(mFile.getSize());
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool FileStream::open(const char *i_pFilename, AccessMode i_openMode)
|
||||
{
|
||||
AssertWarn(0 == mStreamCaps, "FileStream::setPosition: the stream is already open");
|
||||
AssertFatal(NULL != i_pFilename, "FileStream::open: NULL filename");
|
||||
|
||||
// make sure the file stream's state is clean
|
||||
clearBuffer();
|
||||
|
||||
if (File::Ok == mFile.open(i_pFilename, (File::AccessMode)i_openMode))
|
||||
{
|
||||
setStatus();
|
||||
switch (i_openMode)
|
||||
{
|
||||
case Read:
|
||||
mStreamCaps = U32(StreamRead) |
|
||||
U32(StreamPosition);
|
||||
break;
|
||||
case Write:
|
||||
case WriteAppend:
|
||||
mStreamCaps = U32(StreamWrite) |
|
||||
U32(StreamPosition);
|
||||
break;
|
||||
case ReadWrite:
|
||||
mStreamCaps = U32(StreamRead) |
|
||||
U32(StreamWrite) |
|
||||
U32(StreamPosition);
|
||||
break;
|
||||
default:
|
||||
AssertFatal(false, "FileStream::open: bad access mode");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setStatus();
|
||||
return(false);
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void FileStream::close()
|
||||
{
|
||||
if (Closed == getStatus())
|
||||
return;
|
||||
|
||||
// make sure nothing in the buffer differs from what is on disk
|
||||
if (true == mDirty)
|
||||
flush();
|
||||
// and close the file
|
||||
File::Status closeResult = mFile.close();
|
||||
|
||||
AssertFatal(File::Closed == closeResult, "FileStream::close: close failed");
|
||||
|
||||
// clear the file stream's state
|
||||
init();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool FileStream::flush()
|
||||
{
|
||||
AssertWarn(0 != mStreamCaps, "FileStream::flush: the stream isn't open");
|
||||
AssertFatal(false == mDirty || BUFFER_INVALID != mBuffHead, "FileStream::flush: buffer must be valid if its dirty");
|
||||
|
||||
// if the buffer is dirty
|
||||
if (true == mDirty)
|
||||
{
|
||||
AssertFatal(true == hasCapability(StreamWrite), "FileStream::flush: a buffer without write-capability should never be dirty");
|
||||
// align the file pointer to the buffer head
|
||||
if (mBuffHead != mFile.getPosition())
|
||||
{
|
||||
mFile.setPosition(mBuffHead);
|
||||
if (File::Ok != mFile.getStatus() && File::EOS != mFile.getStatus())
|
||||
return(false);
|
||||
}
|
||||
// write contents of the buffer to disk
|
||||
U32 blockHead;
|
||||
calcBlockHead(mBuffHead, &blockHead);
|
||||
mFile.write(mBuffTail - mBuffHead + 1, (char *)mBuffer + (mBuffHead - blockHead));
|
||||
// and update the file stream's state
|
||||
setStatus();
|
||||
if (EOS == getStatus())
|
||||
mEOF = true;
|
||||
|
||||
if (Ok == getStatus() || EOS == getStatus())
|
||||
// and update the status of the buffer
|
||||
mDirty = false;
|
||||
else
|
||||
return(false);
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool FileStream::_read(const U32 i_numBytes, void *o_pBuffer)
|
||||
{
|
||||
AssertFatal(0 != mStreamCaps, "FileStream::_read: the stream isn't open");
|
||||
AssertFatal(NULL != o_pBuffer || i_numBytes == 0, "FileStream::_read: NULL destination pointer with non-zero read request");
|
||||
|
||||
if (false == hasCapability(Stream::StreamRead))
|
||||
{
|
||||
AssertFatal(false, "FileStream::_read: file stream lacks capability");
|
||||
Stream::setStatus(IllegalCall);
|
||||
return(false);
|
||||
}
|
||||
|
||||
// exit on pre-existing errors
|
||||
if (Ok != getStatus())
|
||||
return(false);
|
||||
|
||||
// if a request of non-zero length was made
|
||||
if (0 != i_numBytes)
|
||||
{
|
||||
U8 *pDst = (U8 *)o_pBuffer;
|
||||
U32 readSize;
|
||||
U32 remaining = i_numBytes;
|
||||
U32 bytesRead;
|
||||
U32 blockHead;
|
||||
U32 blockTail;
|
||||
|
||||
// check if the buffer has some data in it
|
||||
if (BUFFER_INVALID != mBuffHead)
|
||||
{
|
||||
// copy as much as possible from the buffer into the destination
|
||||
readSize = ((mBuffTail + 1) >= mBuffPos) ? (mBuffTail + 1 - mBuffPos) : 0;
|
||||
readSize = getMin(readSize, remaining);
|
||||
calcBlockHead(mBuffPos, &blockHead);
|
||||
dMemcpy(pDst, mBuffer + (mBuffPos - blockHead), readSize);
|
||||
// reduce the remaining amount to read
|
||||
remaining -= readSize;
|
||||
// advance the buffer pointers
|
||||
mBuffPos += readSize;
|
||||
pDst += readSize;
|
||||
|
||||
if (mBuffPos > mBuffTail && remaining != 0)
|
||||
{
|
||||
flush();
|
||||
mBuffHead = BUFFER_INVALID;
|
||||
if (mEOF == true)
|
||||
Stream::setStatus(EOS);
|
||||
}
|
||||
}
|
||||
|
||||
// if the request wasn't satisfied by the buffer and the file has more data
|
||||
if (false == mEOF && 0 < remaining)
|
||||
{
|
||||
// flush the buffer if its dirty, since we now need to go to disk
|
||||
if (true == mDirty)
|
||||
flush();
|
||||
|
||||
// make sure we know the current read location in the underlying file
|
||||
mBuffPos = mFile.getPosition();
|
||||
calcBlockBounds(mBuffPos, &blockHead, &blockTail);
|
||||
|
||||
// check if the data to be read falls within a single block
|
||||
if ((mBuffPos + remaining) <= blockTail)
|
||||
{
|
||||
// fill the buffer from disk
|
||||
if (true == fillBuffer(mBuffPos))
|
||||
{
|
||||
// copy as much as possible from the buffer to the destination
|
||||
remaining = getMin(remaining, mBuffTail - mBuffPos + 1);
|
||||
dMemcpy(pDst, mBuffer + (mBuffPos - blockHead), remaining);
|
||||
// advance the buffer pointer
|
||||
mBuffPos += remaining;
|
||||
}
|
||||
else
|
||||
return(false);
|
||||
}
|
||||
// otherwise the remaining spans multiple blocks
|
||||
else
|
||||
{
|
||||
clearBuffer();
|
||||
// read from disk directly into the destination
|
||||
mFile.read(remaining, (char *)pDst, &bytesRead);
|
||||
setStatus();
|
||||
// check to make sure we read as much as expected
|
||||
if (Ok == getStatus() || EOS == getStatus())
|
||||
{
|
||||
// if not, update the end-of-file status
|
||||
if (0 != bytesRead && EOS == getStatus())
|
||||
{
|
||||
Stream::setStatus(Ok);
|
||||
mEOF = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool FileStream::_write(const U32 i_numBytes, const void *i_pBuffer)
|
||||
{
|
||||
AssertFatal(0 != mStreamCaps, "FileStream::_write: the stream isn't open");
|
||||
AssertFatal(NULL != i_pBuffer || i_numBytes == 0, "FileStream::_write: NULL source buffer pointer on non-zero write request");
|
||||
|
||||
if (false == hasCapability(Stream::StreamWrite))
|
||||
{
|
||||
AssertFatal(false, "FileStream::_write: file stream lacks capability");
|
||||
Stream::setStatus(IllegalCall);
|
||||
return(false);
|
||||
}
|
||||
|
||||
// exit on pre-existing errors
|
||||
if (Ok != getStatus() && EOS != getStatus())
|
||||
return(false);
|
||||
|
||||
// if a request of non-zero length was made
|
||||
if (0 != i_numBytes)
|
||||
{
|
||||
U8 *pSrc = (U8 *)i_pBuffer;
|
||||
U32 writeSize;
|
||||
U32 remaining = i_numBytes;
|
||||
U32 bytesWrit;
|
||||
U32 blockHead;
|
||||
U32 blockTail;
|
||||
|
||||
// check if the buffer is valid
|
||||
if (BUFFER_INVALID != mBuffHead)
|
||||
{
|
||||
// copy as much as possible from the source to the buffer
|
||||
calcBlockBounds(mBuffHead, &blockHead, &blockTail);
|
||||
writeSize = (mBuffPos > blockTail) ? 0 : blockTail - mBuffPos + 1;
|
||||
writeSize = getMin(writeSize, remaining);
|
||||
|
||||
AssertFatal(0 == writeSize || (mBuffPos - blockHead) < BUFFER_SIZE, "FileStream::_write: out of bounds buffer position");
|
||||
dMemcpy(mBuffer + (mBuffPos - blockHead), pSrc, writeSize);
|
||||
// reduce the remaining amount to be written
|
||||
remaining -= writeSize;
|
||||
// advance the buffer pointers
|
||||
mBuffPos += writeSize;
|
||||
mBuffTail = getMax(mBuffTail, mBuffPos - 1);
|
||||
pSrc += writeSize;
|
||||
// mark the buffer dirty
|
||||
if (0 < writeSize)
|
||||
mDirty = true;
|
||||
}
|
||||
|
||||
// if the request wasn't satisfied by the buffer
|
||||
if (0 < remaining)
|
||||
{
|
||||
// flush the buffer if its dirty, since we now need to go to disk
|
||||
if (true == mDirty)
|
||||
flush();
|
||||
|
||||
// make sure we know the current write location in the underlying file
|
||||
mBuffPos = mFile.getPosition();
|
||||
calcBlockBounds(mBuffPos, &blockHead, &blockTail);
|
||||
|
||||
// check if the data to be written falls within a single block
|
||||
if ((mBuffPos + remaining) <= blockTail)
|
||||
{
|
||||
// write the data to the buffer
|
||||
dMemcpy(mBuffer + (mBuffPos - blockHead), pSrc, remaining);
|
||||
// update the buffer pointers
|
||||
mBuffHead = mBuffPos;
|
||||
mBuffPos += remaining;
|
||||
mBuffTail = mBuffPos - 1;
|
||||
// mark the buffer dirty
|
||||
mDirty = true;
|
||||
}
|
||||
// otherwise the remaining spans multiple blocks
|
||||
else
|
||||
{
|
||||
clearBuffer();
|
||||
// write to disk directly from the source
|
||||
mFile.write(remaining, (char *)pSrc, &bytesWrit);
|
||||
setStatus();
|
||||
return(Ok == getStatus() || EOS == getStatus());
|
||||
}
|
||||
}
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void FileStream::init()
|
||||
{
|
||||
mStreamCaps = 0;
|
||||
Stream::setStatus(Closed);
|
||||
clearBuffer();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool FileStream::fillBuffer(const U32 i_startPosition)
|
||||
{
|
||||
AssertFatal(0 != mStreamCaps, "FileStream::fillBuffer: the stream isn't open");
|
||||
AssertFatal(false == mDirty, "FileStream::fillBuffer: buffer must be clean to fill");
|
||||
|
||||
// make sure start position and file pointer jive
|
||||
if (i_startPosition != mFile.getPosition())
|
||||
{
|
||||
mFile.setPosition(i_startPosition);
|
||||
if (File::Ok != mFile.getStatus() && File::EOS != mFile.getStatus())
|
||||
{
|
||||
setStatus();
|
||||
return(false);
|
||||
}
|
||||
else
|
||||
// update buffer pointer
|
||||
mBuffPos = i_startPosition;
|
||||
}
|
||||
|
||||
// check if file pointer is at end-of-file
|
||||
if (EOS == getStatus())
|
||||
{
|
||||
// invalidate the buffer
|
||||
mBuffHead = BUFFER_INVALID;
|
||||
// set the status to end-of-stream
|
||||
mEOF = true;
|
||||
}
|
||||
// otherwise
|
||||
else
|
||||
{
|
||||
U32 bytesRead = 0;
|
||||
U32 blockHead;
|
||||
// locate bounds of buffer containing current position
|
||||
calcBlockHead(mBuffPos, &blockHead);
|
||||
// read as much as possible from input file
|
||||
mFile.read(BUFFER_SIZE - (i_startPosition - blockHead), (char *)mBuffer + (i_startPosition - blockHead), &bytesRead);
|
||||
setStatus();
|
||||
if (Ok == getStatus() || EOS == getStatus())
|
||||
{
|
||||
// update buffer pointers
|
||||
mBuffHead = i_startPosition;
|
||||
mBuffPos = i_startPosition;
|
||||
mBuffTail = i_startPosition + bytesRead - 1;
|
||||
// update end-of-file status
|
||||
if (0 != bytesRead && EOS == getStatus())
|
||||
{
|
||||
Stream::setStatus(Ok);
|
||||
mEOF = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mBuffHead = BUFFER_INVALID;
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void FileStream::clearBuffer()
|
||||
{
|
||||
mBuffHead = BUFFER_INVALID;
|
||||
mBuffPos = 0;
|
||||
mBuffTail = 0;
|
||||
mDirty = false;
|
||||
mEOF = false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void FileStream::calcBlockHead(const U32 i_position, U32 *o_blockHead)
|
||||
{
|
||||
AssertFatal(NULL != o_blockHead, "FileStream::calcBlockHead: NULL pointer passed for block head");
|
||||
|
||||
*o_blockHead = i_position/BUFFER_SIZE * BUFFER_SIZE;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void FileStream::calcBlockBounds(const U32 i_position, U32 *o_blockHead, U32 *o_blockTail)
|
||||
{
|
||||
AssertFatal(NULL != o_blockHead, "FileStream::calcBlockBounds: NULL pointer passed for block head");
|
||||
AssertFatal(NULL != o_blockTail, "FileStream::calcBlockBounds: NULL pointer passed for block tail");
|
||||
|
||||
*o_blockHead = i_position/BUFFER_SIZE * BUFFER_SIZE;
|
||||
*o_blockTail = *o_blockHead + BUFFER_SIZE - 1;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void FileStream::setStatus()
|
||||
{
|
||||
switch (mFile.getStatus())
|
||||
{
|
||||
case File::Ok:
|
||||
Stream::setStatus(Ok);
|
||||
break;
|
||||
case File::IOError:
|
||||
Stream::setStatus(IOError);
|
||||
break;
|
||||
case File::EOS:
|
||||
Stream::setStatus(EOS);
|
||||
break;
|
||||
case File::IllegalCall:
|
||||
Stream::setStatus(IllegalCall);
|
||||
break;
|
||||
case File::Closed:
|
||||
Stream::setStatus(Closed);
|
||||
break;
|
||||
case File::UnknownError:
|
||||
Stream::setStatus(UnknownError);
|
||||
break;
|
||||
default:
|
||||
AssertFatal(false, "FileStream::setStatus: invalid error mode");
|
||||
}
|
||||
}
|
||||
88
core/fileStream.h
Normal file
88
core/fileStream.h
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/*******************************************************************************
|
||||
** FILENAME: D:\Tribes\darkstar\Core\fileStream.h
|
||||
*
|
||||
* DESCRIPTION: buffered file streams.
|
||||
* read, write, and read-write buffering is supported.
|
||||
*
|
||||
* CREATED: 07/26/99 08:24:55
|
||||
*
|
||||
* BY: PeteW
|
||||
*/
|
||||
|
||||
#ifndef _FILESTREAM_H_
|
||||
#define _FILESTREAM_H_
|
||||
|
||||
#ifndef _FILEIO_H_
|
||||
#include "Core/fileio.h"
|
||||
#endif
|
||||
#ifndef _STREAM_H_
|
||||
#include "Core/stream.h"
|
||||
#endif
|
||||
|
||||
class FileStream : public Stream
|
||||
{
|
||||
public:
|
||||
enum AccessMode
|
||||
{
|
||||
Read = File::Read,
|
||||
Write = File::Write,
|
||||
ReadWrite = File::ReadWrite,
|
||||
WriteAppend = File::WriteAppend
|
||||
};
|
||||
enum
|
||||
{
|
||||
BUFFER_SIZE = 8 * 1024, // this can be changed to anything appropriate
|
||||
BUFFER_INVALID = 0xffffffff // file offsets must all be less than this
|
||||
};
|
||||
|
||||
private:
|
||||
File mFile; // file being streamed
|
||||
U32 mStreamCaps; // dependent on access mode
|
||||
U8 mBuffer[BUFFER_SIZE];
|
||||
U32 mBuffHead; // first valid position of buffer (from start-of-file)
|
||||
U32 mBuffPos; // next read or write will occur here
|
||||
U32 mBuffTail; // last valid position in buffer (inclusive)
|
||||
bool mDirty; // whether buffer has been written to
|
||||
bool mEOF; // whether disk reads have reached the end-of-file
|
||||
|
||||
FileStream(const FileStream &i_fileStrm); // disable copy constructor
|
||||
FileStream& operator=(const FileStream &i_fileStrm); // disable assignment operator
|
||||
|
||||
public:
|
||||
FileStream(); // default constructor
|
||||
virtual ~FileStream(); // destructor
|
||||
|
||||
// mandatory methods from Stream base class...
|
||||
virtual bool hasCapability(const Capability i_cap) const;
|
||||
|
||||
virtual U32 getPosition() const;
|
||||
virtual bool setPosition(const U32 i_newPosition);
|
||||
virtual U32 getStreamSize();
|
||||
|
||||
// additional methods needed for a file stream...
|
||||
bool open(const char *i_pFilename, AccessMode i_openMode);
|
||||
void close();
|
||||
|
||||
bool flush();
|
||||
|
||||
protected:
|
||||
// more mandatory methods from Stream base class...
|
||||
virtual bool _read(const U32 i_numBytes, void *o_pBuffer);
|
||||
virtual bool _write(const U32 i_numBytes, const void* i_pBuffer);
|
||||
|
||||
void init();
|
||||
bool fillBuffer(const U32 i_startPosition);
|
||||
void clearBuffer();
|
||||
static void calcBlockHead(const U32 i_position, U32 *o_blockHead);
|
||||
static void calcBlockBounds(const U32 i_position, U32 *o_blockHead, U32 *o_blockTail);
|
||||
void setStatus();
|
||||
};
|
||||
|
||||
#endif // _FILE_STREAM_H
|
||||
68
core/fileio.h
Normal file
68
core/fileio.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _FILEIO_H_
|
||||
#define _FILEIO_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "Platform/platform.h"
|
||||
#endif
|
||||
|
||||
class File
|
||||
{
|
||||
public:
|
||||
enum Status
|
||||
{
|
||||
Ok = 0, // obvious
|
||||
IOError, // Read or Write error
|
||||
EOS, // End of Stream reached (mostly for reads)
|
||||
IllegalCall, // An unsupported operation used. Always w/ accompanied by AssertWarn
|
||||
Closed, // Tried to operate on a closed stream (or detached filter)
|
||||
UnknownError // Catchall
|
||||
};
|
||||
enum AccessMode
|
||||
{
|
||||
Read = 0,
|
||||
Write = 1,
|
||||
ReadWrite = 2,
|
||||
WriteAppend = 3
|
||||
};
|
||||
enum Capability
|
||||
{
|
||||
FileRead = (1<<0),
|
||||
FileWrite = (1<<1)
|
||||
};
|
||||
|
||||
private:
|
||||
void *handle; // pointer to the file handle
|
||||
Status currentStatus; // current status of the file (Ok, IOError, etc.)
|
||||
U32 capability; // keeps track of file capabilities
|
||||
|
||||
File(const File&); // disable copy constructor
|
||||
File& operator=(const File&); // disable assignment
|
||||
|
||||
public:
|
||||
File(); // default constructor
|
||||
virtual ~File(); // destructor
|
||||
|
||||
Status open(const char *filename, const AccessMode openMode);
|
||||
U32 getPosition() const;
|
||||
Status setPosition(S32 position, bool absolutePos = true);
|
||||
U32 getSize() const;
|
||||
Status flush();
|
||||
Status close();
|
||||
Status getStatus() const;
|
||||
Status read(U32 size, char *dst, U32 *bytesRead = NULL);
|
||||
Status write(U32 size, const char *src, U32 *bytesWritten = NULL);
|
||||
bool hasCapability(Capability cap) const;
|
||||
|
||||
protected:
|
||||
Status setStatus(); // called after error encountered
|
||||
Status setStatus(Status status); // assign specific value
|
||||
};
|
||||
|
||||
#endif // _FILE_IO_H_
|
||||
63
core/filterStream.cc
Normal file
63
core/filterStream.cc
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "Core/filterStream.h"
|
||||
|
||||
FilterStream::~FilterStream()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
bool FilterStream::_read(const U32 in_numBytes, void* out_pBuffer)
|
||||
{
|
||||
AssertFatal(getStream() != NULL, "Error no stream to pass to");
|
||||
|
||||
bool success = getStream()->read(in_numBytes, out_pBuffer);
|
||||
|
||||
setStatus(getStream()->getStatus());
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
bool FilterStream::_write(const U32, const void*)
|
||||
{
|
||||
AssertFatal(false, "No writing allowed to filter");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FilterStream::hasCapability(const Capability in_streamCap) const
|
||||
{
|
||||
// Fool the compiler. We know better...
|
||||
FilterStream* ncThis = const_cast<FilterStream*>(this);
|
||||
AssertFatal(ncThis->getStream() != NULL, "Error no stream to pass to");
|
||||
|
||||
return ncThis->getStream()->hasCapability(in_streamCap);
|
||||
}
|
||||
|
||||
U32 FilterStream::getPosition() const
|
||||
{
|
||||
// Fool the compiler. We know better...
|
||||
FilterStream* ncThis = const_cast<FilterStream*>(this);
|
||||
AssertFatal(ncThis->getStream() != NULL, "Error no stream to pass to");
|
||||
|
||||
return ncThis->getStream()->getPosition();
|
||||
}
|
||||
|
||||
bool FilterStream::setPosition(const U32 in_newPosition)
|
||||
{
|
||||
AssertFatal(getStream() != NULL, "Error no stream to pass to");
|
||||
|
||||
return getStream()->setPosition(in_newPosition);
|
||||
}
|
||||
|
||||
U32 FilterStream::getStreamSize()
|
||||
{
|
||||
AssertFatal(getStream() != NULL, "Error no stream to pass to");
|
||||
|
||||
return getStream()->getStreamSize();
|
||||
}
|
||||
|
||||
41
core/filterStream.h
Normal file
41
core/filterStream.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _FILTERSTREAM_H_
|
||||
#define _FILTERSTREAM_H_
|
||||
|
||||
//Includes
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "Platform/platform.h"
|
||||
#endif
|
||||
#ifndef _STREAM_H_
|
||||
#include "Core/stream.h"
|
||||
#endif
|
||||
|
||||
class FilterStream : public Stream
|
||||
{
|
||||
public:
|
||||
virtual ~FilterStream();
|
||||
|
||||
virtual bool attachStream(Stream* io_pSlaveStream) = 0;
|
||||
virtual void detachStream() = 0;
|
||||
virtual Stream* getStream() = 0;
|
||||
|
||||
// Mandatory overrides. By default, these are simply passed to
|
||||
// whatever is returned from getStream();
|
||||
protected:
|
||||
bool _read(const U32 in_numBytes, void* out_pBuffer);
|
||||
bool _write(const U32 in_numBytes, const void* in_pBuffer);
|
||||
public:
|
||||
bool hasCapability(const Capability) const;
|
||||
|
||||
U32 getPosition() const;
|
||||
bool setPosition(const U32 in_newPosition);
|
||||
U32 getStreamSize();
|
||||
};
|
||||
|
||||
#endif //_FILTERSTREAM_H_
|
||||
114
core/findMatch.cc
Normal file
114
core/findMatch.cc
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "Platform/platform.h"
|
||||
#include "Core/findMatch.h"
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// NAME
|
||||
// FindMatch::FindMatch( const char *_expression, S32 maxNumMatches )
|
||||
//
|
||||
// DESCRIPTION
|
||||
// Class to match regular expressions (file names)
|
||||
// only works with '*','?', and 'chars'
|
||||
//
|
||||
// ARGUMENTS
|
||||
// _expression - The regular expression you intend to match (*.??abc.bmp)
|
||||
// _maxMatches - The maximum number of strings you wish to match.
|
||||
//
|
||||
// RETURNS
|
||||
//
|
||||
// NOTES
|
||||
//
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
FindMatch::FindMatch( U32 _maxMatches )
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(matchList);
|
||||
|
||||
expression = NULL;
|
||||
maxMatches = _maxMatches;
|
||||
matchList.reserve( maxMatches );
|
||||
}
|
||||
|
||||
FindMatch::FindMatch( char *_expression, U32 _maxMatches )
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(matchList);
|
||||
|
||||
expression = NULL;
|
||||
setExpression( _expression );
|
||||
maxMatches = _maxMatches;
|
||||
matchList.reserve( maxMatches );
|
||||
}
|
||||
|
||||
FindMatch::~FindMatch()
|
||||
{
|
||||
delete [] expression;
|
||||
matchList.clear();
|
||||
}
|
||||
|
||||
void FindMatch::setExpression( const char *_expression )
|
||||
{
|
||||
delete [] expression;
|
||||
|
||||
expression = new char[dStrlen(_expression) + 1];
|
||||
dStrcpy(expression, _expression);
|
||||
dStrupr(expression);
|
||||
}
|
||||
|
||||
bool FindMatch::findMatch( const char *str, bool caseSensitive )
|
||||
{
|
||||
if ( isFull() )
|
||||
return false;
|
||||
|
||||
char nstr[512];
|
||||
dStrcpy( nstr,str );
|
||||
dStrupr(nstr);
|
||||
if ( isMatch( expression, nstr, caseSensitive ) )
|
||||
{
|
||||
matchList.push_back( (char*)str );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FindMatch::isMatch( const char *exp, const char *str, bool caseSensitive )
|
||||
{
|
||||
const char *e=exp;
|
||||
const char *s=str;
|
||||
bool match=true;
|
||||
|
||||
while ( match && *e && *s )
|
||||
{
|
||||
switch( *e )
|
||||
{
|
||||
case '*':
|
||||
e++;
|
||||
match = false;
|
||||
while( ((s=dStrchr(s,*e)) !=NULL) && !match )
|
||||
{
|
||||
match = isMatch( e, s, caseSensitive );
|
||||
s++;
|
||||
}
|
||||
return( match );
|
||||
case '?':
|
||||
e++;
|
||||
s++;
|
||||
break;
|
||||
default:
|
||||
if (caseSensitive) match = ( *e++ == *s++ );
|
||||
else match = ( dToupper(*e++) == dToupper(*s++) );
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (*e != *s) // both exp and str should be at '\0' if match was successfull
|
||||
match = false;
|
||||
|
||||
return ( match );
|
||||
}
|
||||
36
core/findMatch.h
Normal file
36
core/findMatch.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _FINDMATCH_H_
|
||||
#define _FINDMATCH_H_
|
||||
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "Core/tVector.h"
|
||||
#endif
|
||||
|
||||
class FindMatch
|
||||
{
|
||||
char* expression;
|
||||
U32 maxMatches;
|
||||
|
||||
public:
|
||||
static bool isMatch( const char *exp, const char *string, bool caseSensitive = false );
|
||||
Vector<char *> matchList;
|
||||
|
||||
FindMatch( U32 _maxMatches = 256 );
|
||||
FindMatch( char *_expression, U32 _maxMatches = 256 );
|
||||
~FindMatch();
|
||||
|
||||
bool findMatch(const char *string, bool caseSensitive = false);
|
||||
void setExpression( const char *_expression );
|
||||
|
||||
S32 numMatches() const { return(matchList.size()); }
|
||||
bool isFull() const { return (matchList.size() >= maxMatches); }
|
||||
void clear() { matchList.clear(); }
|
||||
};
|
||||
|
||||
#endif // _FINDMATCH_H_
|
||||
21
core/idGenerator.cc
Normal file
21
core/idGenerator.cc
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "Core/idGenerator.h"
|
||||
|
||||
void IdGenerator::reclaim()
|
||||
{
|
||||
// attempt to keep the pool vector as small as possible by reclaiming
|
||||
// pool entries back into the nextIdBlock variable
|
||||
|
||||
while (!mPool.empty() && (mPool.last() == (mNextId-1)) )
|
||||
{
|
||||
mNextId--;
|
||||
mPool.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
78
core/idGenerator.h
Normal file
78
core/idGenerator.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _IDGENERATOR_H_
|
||||
#define _IDGENERATOR_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "Platform/platform.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "Core/tVector.h"
|
||||
#endif
|
||||
|
||||
class IdGenerator
|
||||
{
|
||||
private:
|
||||
U32 mIdBlockBase;
|
||||
U32 mIdRangeSize;
|
||||
Vector<U32> mPool;
|
||||
U32 mNextId;
|
||||
|
||||
void reclaim();
|
||||
|
||||
public:
|
||||
IdGenerator(U32 base, U32 numIds)
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mPool);
|
||||
|
||||
mIdBlockBase = base;
|
||||
mIdRangeSize = numIds;
|
||||
mNextId = mIdBlockBase;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
mPool.clear();
|
||||
mNextId = mIdBlockBase;
|
||||
}
|
||||
|
||||
U32 alloc()
|
||||
{
|
||||
// fist check the pool:
|
||||
if(!mPool.empty())
|
||||
{
|
||||
U32 id = mPool.last();
|
||||
mPool.pop_back();
|
||||
reclaim();
|
||||
return id;
|
||||
}
|
||||
if(mIdRangeSize && mNextId >= mIdBlockBase + mIdRangeSize)
|
||||
return 0;
|
||||
|
||||
return mNextId++;
|
||||
}
|
||||
|
||||
void free(U32 id)
|
||||
{
|
||||
AssertFatal(id >= mIdBlockBase, "IdGenerator::alloc: invalid id, id does not belong to this IdGenerator.")
|
||||
if(id == mNextId - 1)
|
||||
{
|
||||
mNextId--;
|
||||
reclaim();
|
||||
}
|
||||
else
|
||||
mPool.push_back(id);
|
||||
}
|
||||
|
||||
U32 numIdsUsed()
|
||||
{
|
||||
return mNextId - mIdBlockBase - mPool.size();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
308
core/llist.h
Normal file
308
core/llist.h
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef LLIST_H
|
||||
#define LLIST_H
|
||||
|
||||
//***************************************************************************************
|
||||
// Linked List
|
||||
//***************************************************************************************
|
||||
// This template has an overhead of size LListNode for each entry in the linked list -
|
||||
// be aware of this for very large linked lists.
|
||||
//
|
||||
// This template has no destructor! You must explicitly call free() if you want the
|
||||
// contents of the list to be destroyed.
|
||||
//
|
||||
//
|
||||
// WARNING - this template has not been thoroughly tested so there may be bugs in it!
|
||||
//
|
||||
// -Bramage
|
||||
//---------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------
|
||||
// LListNode template data node
|
||||
//---------------------------------------------------------------------------------------
|
||||
template <class T> class LListNode
|
||||
{
|
||||
public:
|
||||
LListNode<T> * Next;
|
||||
LListNode<T> * Prev;
|
||||
T * Data;
|
||||
|
||||
LListNode()
|
||||
{
|
||||
Next = NULL;
|
||||
Prev = NULL;
|
||||
Data = NULL;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------
|
||||
// LList template
|
||||
//---------------------------------------------------------------------------------------
|
||||
template <class T> class LList
|
||||
{
|
||||
protected:
|
||||
LListNode< T > *first_entry;
|
||||
LListNode< T > *last_entry;
|
||||
int cnt;
|
||||
|
||||
public:
|
||||
|
||||
//---------------------------------------------------------------------------------------
|
||||
// Constructor initializes empty list
|
||||
//---------------------------------------------------------------------------------------
|
||||
LList()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------
|
||||
// Reset list to empty state by abandoning contents
|
||||
//---------------------------------------------------------------------------------------
|
||||
void reset(void)
|
||||
{
|
||||
first_entry = NULL;
|
||||
last_entry = NULL;
|
||||
cnt = 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------
|
||||
// Return entry count
|
||||
//---------------------------------------------------------------------------------------
|
||||
int size(void) const
|
||||
{
|
||||
return cnt;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------
|
||||
// Return first list entry (NULL if list empty)
|
||||
//---------------------------------------------------------------------------------------
|
||||
T *first(void) const
|
||||
{
|
||||
if( first_entry ){
|
||||
return first_entry->Data;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------
|
||||
// Return last list entry (NULL if list empty)
|
||||
//---------------------------------------------------------------------------------------
|
||||
T *last(void) const
|
||||
{
|
||||
if( last_entry )
|
||||
{
|
||||
return last_entry->Data;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------
|
||||
// Returns next entry - returns first entry if current == NULL
|
||||
//---------------------------------------------------------------------------------------
|
||||
T *next( T* current )
|
||||
{
|
||||
if( current == NULL )
|
||||
{
|
||||
return first();
|
||||
}
|
||||
|
||||
LListNode<T> *next = findNode( current )->Next;
|
||||
if( next )
|
||||
{
|
||||
return next->Data;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------
|
||||
// Returns prev entry - returns last entry if current == NULL
|
||||
//---------------------------------------------------------------------------------------
|
||||
T *prev( T *current )
|
||||
{
|
||||
if( current == NULL )
|
||||
{
|
||||
return last();
|
||||
}
|
||||
|
||||
LListNode<T> *prev = findNode( current )->Prev;
|
||||
if( prev )
|
||||
{
|
||||
return prev->Data;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------
|
||||
// Link new item into list before specified entry
|
||||
// If specified entry==NULL, insert at end of list
|
||||
//---------------------------------------------------------------------------------------
|
||||
T *link(T *entry, T *next = NULL)
|
||||
{
|
||||
LListNode<T> *prevNode = NULL;
|
||||
LListNode<T> *nextNode = findNode( next );
|
||||
LListNode<T> *newNode = new LListNode<T>;
|
||||
|
||||
newNode->Data = entry;
|
||||
|
||||
if( nextNode == NULL)
|
||||
{
|
||||
prevNode = last_entry;
|
||||
last_entry = newNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
prevNode = nextNode->Prev;
|
||||
nextNode->Prev = newNode;
|
||||
}
|
||||
|
||||
if( prevNode == NULL )
|
||||
{
|
||||
first_entry = newNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
prevNode->Next = newNode;
|
||||
}
|
||||
|
||||
newNode->Next = nextNode;
|
||||
newNode->Prev = prevNode;
|
||||
|
||||
++cnt;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------
|
||||
// Link new item into list before specified entry
|
||||
// If specified entry==NULL, insert at end of list
|
||||
//---------------------------------------------------------------------------------------
|
||||
T *link(T &entry, T *next = NULL)
|
||||
{
|
||||
T *newEntry = new T;
|
||||
*newEntry = entry;
|
||||
|
||||
return link( newEntry, next );
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------
|
||||
// Unlink item from list (without destroying it)
|
||||
//---------------------------------------------------------------------------------------
|
||||
void unlink(T *entry)
|
||||
{
|
||||
LListNode<T> *entryNode = findNode( entry );
|
||||
if( !entryNode ) return;
|
||||
|
||||
|
||||
if( entryNode->Prev == NULL )
|
||||
{
|
||||
first_entry = entryNode->Next;
|
||||
}
|
||||
else
|
||||
{
|
||||
entryNode->Prev->Next = entryNode->Next;
|
||||
}
|
||||
|
||||
if( entryNode->Next == NULL )
|
||||
{
|
||||
last_entry = entryNode->Prev;
|
||||
}
|
||||
else
|
||||
{
|
||||
entryNode->Next->Prev = entryNode->Prev;
|
||||
}
|
||||
|
||||
delete entryNode;
|
||||
|
||||
--cnt;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------
|
||||
// Allocate entry and insert before specified entry
|
||||
// If specified entry==NULL, insert at end of list
|
||||
//---------------------------------------------------------------------------------------
|
||||
T * alloc( T *next = NULL )
|
||||
{
|
||||
T *entry = new T;
|
||||
|
||||
if( entry == NULL )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return link( entry, next );
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------
|
||||
// Unlink item from list and destroy it
|
||||
//---------------------------------------------------------------------------------------
|
||||
void free(T *entry)
|
||||
{
|
||||
unlink(entry);
|
||||
delete entry;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------
|
||||
// Unlink and destroy all list items
|
||||
//---------------------------------------------------------------------------------------
|
||||
void free(void)
|
||||
{
|
||||
LListNode<T> *node = NULL;
|
||||
|
||||
while( bool( node = iterate( node ) ) )
|
||||
{
|
||||
LListNode<T> *nodeToKill = node;
|
||||
node = node->Prev;
|
||||
free( nodeToKill->Data );
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------
|
||||
// Find node
|
||||
//---------------------------------------------------------------------------------------
|
||||
LListNode<T> *findNode( T *entry )
|
||||
{
|
||||
LListNode<T> *it = NULL;
|
||||
while( bool( it = iterate( it ) ) )
|
||||
{
|
||||
if( it->Data == entry )
|
||||
{
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------
|
||||
// Returns the next node in list
|
||||
//---------------------------------------------------------------------------------------
|
||||
LListNode<T> *iterate( LListNode<T> *entry = NULL )
|
||||
{
|
||||
if( entry == NULL ) return first_entry;
|
||||
return entry->Next;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
159
core/memStream.cc
Normal file
159
core/memStream.cc
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "Platform/platform.h"
|
||||
#include "Core/memstream.h"
|
||||
|
||||
MemStream::MemStream(const U32 in_bufferSize,
|
||||
void* io_pBuffer,
|
||||
const bool in_allowRead,
|
||||
const bool in_allowWrite)
|
||||
: cm_bufferSize(in_bufferSize),
|
||||
m_pBufferBase(io_pBuffer),
|
||||
m_instCaps(0),
|
||||
m_currentPosition(0)
|
||||
{
|
||||
AssertFatal(io_pBuffer != NULL, "Invalid buffer pointer");
|
||||
AssertFatal(in_bufferSize > 0, "Invalid buffer size");
|
||||
AssertFatal(in_allowRead || in_allowWrite, "Either write or read must be allowed");
|
||||
|
||||
if (in_allowRead)
|
||||
m_instCaps |= Stream::StreamRead;
|
||||
if (in_allowWrite)
|
||||
m_instCaps |= Stream::StreamWrite;
|
||||
|
||||
setStatus(Ok);
|
||||
}
|
||||
|
||||
MemStream::~MemStream()
|
||||
{
|
||||
m_pBufferBase = NULL;
|
||||
m_currentPosition = 0;
|
||||
|
||||
setStatus(Closed);
|
||||
}
|
||||
|
||||
U32 MemStream::getStreamSize()
|
||||
{
|
||||
AssertFatal(getStatus() != Closed, "Stream not open, size undefined");
|
||||
|
||||
return cm_bufferSize;
|
||||
}
|
||||
|
||||
bool MemStream::hasCapability(const Capability in_cap) const
|
||||
{
|
||||
// Closed streams can't do anything
|
||||
//
|
||||
if (getStatus() == Closed)
|
||||
return false;
|
||||
|
||||
U32 totalCaps = U32(Stream::StreamPosition) | m_instCaps;
|
||||
|
||||
return (U32(in_cap) & totalCaps) != 0;
|
||||
}
|
||||
|
||||
U32 MemStream::getPosition() const
|
||||
{
|
||||
AssertFatal(getStatus() != Closed, "Position of a closed stream is undefined");
|
||||
|
||||
return m_currentPosition;
|
||||
}
|
||||
|
||||
bool MemStream::setPosition(const U32 in_newPosition)
|
||||
{
|
||||
AssertFatal(getStatus() != Closed, "SetPosition of a closed stream is not allowed");
|
||||
AssertFatal(in_newPosition <= cm_bufferSize, "Invalid position");
|
||||
|
||||
m_currentPosition = in_newPosition;
|
||||
if (m_currentPosition > cm_bufferSize) {
|
||||
// Never gets here in debug version, this is for the release builds...
|
||||
//
|
||||
setStatus(UnknownError);
|
||||
return false;
|
||||
} else if (m_currentPosition == cm_bufferSize) {
|
||||
setStatus(EOS);
|
||||
} else {
|
||||
setStatus(Ok);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemStream::_read(const U32 in_numBytes, void *out_pBuffer)
|
||||
{
|
||||
AssertFatal(getStatus() != Closed, "Attempted read from a closed stream");
|
||||
|
||||
if (in_numBytes == 0)
|
||||
return true;
|
||||
|
||||
AssertFatal(out_pBuffer != NULL, "Invalid output buffer");
|
||||
|
||||
if (hasCapability(StreamRead) == false) {
|
||||
AssertWarn(false, "Reading is disallowed on this stream");
|
||||
setStatus(IllegalCall);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
U32 actualBytes = in_numBytes;
|
||||
if ((m_currentPosition + in_numBytes) > cm_bufferSize) {
|
||||
success = false;
|
||||
actualBytes = cm_bufferSize - m_currentPosition;
|
||||
}
|
||||
|
||||
// Obtain a current pointer, and do the copy
|
||||
const void* pCurrent = (const void*)((const U8*)m_pBufferBase + m_currentPosition);
|
||||
dMemcpy(out_pBuffer, pCurrent, actualBytes);
|
||||
|
||||
// Advance the stream position
|
||||
m_currentPosition += actualBytes;
|
||||
|
||||
if (!success)
|
||||
setStatus(EOS);
|
||||
else
|
||||
setStatus(Ok);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool MemStream::_write(const U32 in_numBytes, const void *in_pBuffer)
|
||||
{
|
||||
AssertFatal(getStatus() != Closed, "Attempted write to a closed stream");
|
||||
|
||||
if (in_numBytes == 0)
|
||||
return true;
|
||||
|
||||
AssertFatal(in_pBuffer != NULL, "Invalid input buffer");
|
||||
|
||||
if (hasCapability(StreamWrite) == false) {
|
||||
AssertWarn(0, "Writing is disallowed on this stream");
|
||||
setStatus(IllegalCall);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
U32 actualBytes = in_numBytes;
|
||||
if ((m_currentPosition + in_numBytes) > cm_bufferSize) {
|
||||
success = false;
|
||||
actualBytes = cm_bufferSize - m_currentPosition;
|
||||
}
|
||||
|
||||
// Obtain a current pointer, and do the copy
|
||||
void* pCurrent = (void*)((U8*)m_pBufferBase + m_currentPosition);
|
||||
dMemcpy(pCurrent, in_pBuffer, actualBytes);
|
||||
|
||||
// Advance the stream position
|
||||
m_currentPosition += actualBytes;
|
||||
|
||||
if (m_currentPosition == cm_bufferSize)
|
||||
setStatus(EOS);
|
||||
else
|
||||
setStatus(Ok);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
47
core/memstream.h
Normal file
47
core/memstream.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _MEMSTREAM_H_
|
||||
#define _MEMSTREAM_H_
|
||||
|
||||
//Includes
|
||||
#ifndef _STREAM_H_
|
||||
#include "Core/stream.h"
|
||||
#endif
|
||||
|
||||
class MemStream : public Stream {
|
||||
typedef Stream Parent;
|
||||
|
||||
protected:
|
||||
U32 const cm_bufferSize;
|
||||
void* m_pBufferBase;
|
||||
|
||||
U32 m_instCaps;
|
||||
U32 m_currentPosition;
|
||||
|
||||
public:
|
||||
MemStream(const U32 in_bufferSize,
|
||||
void* io_pBuffer,
|
||||
const bool in_allowRead = true,
|
||||
const bool in_allowWrite = true);
|
||||
~MemStream();
|
||||
|
||||
// Mandatory overrides from Stream
|
||||
protected:
|
||||
bool _read(const U32 in_numBytes, void* out_pBuffer);
|
||||
bool _write(const U32 in_numBytes, const void* in_pBuffer);
|
||||
public:
|
||||
bool hasCapability(const Capability) const;
|
||||
U32 getPosition() const;
|
||||
bool setPosition(const U32 in_newPosition);
|
||||
|
||||
// Mandatory overrides from Stream
|
||||
public:
|
||||
U32 getStreamSize();
|
||||
};
|
||||
|
||||
#endif //_MEMSTREAM_H_
|
||||
155
core/nStream.cc
Normal file
155
core/nStream.cc
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "Platform/platform.h"
|
||||
#include "Core/stream.h"
|
||||
#include "Core/stringTable.h"
|
||||
#include "Core/color.h"
|
||||
|
||||
Stream::Stream()
|
||||
: m_streamStatus(Closed)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
Stream::~Stream()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
const char* Stream::getStatusString(const Status in_status)
|
||||
{
|
||||
switch (in_status) {
|
||||
case Ok:
|
||||
return "StreamOk";
|
||||
case IOError:
|
||||
return "StreamIOError";
|
||||
case EOS:
|
||||
return "StreamEOS";
|
||||
case IllegalCall:
|
||||
return "StreamIllegalCall";
|
||||
case Closed:
|
||||
return "StreamClosed";
|
||||
case UnknownError:
|
||||
return "StreamUnknownError";
|
||||
|
||||
default:
|
||||
return "Invalid Stream::Status";
|
||||
}
|
||||
}
|
||||
|
||||
void Stream::writeString(const char *string, S32 maxLen)
|
||||
{
|
||||
S32 len = string ? dStrlen(string) : 0;
|
||||
if(len > maxLen)
|
||||
len = maxLen;
|
||||
|
||||
write(U8(len));
|
||||
if(len)
|
||||
write(len, string);
|
||||
}
|
||||
|
||||
void Stream::readString(char buf[256])
|
||||
{
|
||||
U8 len;
|
||||
read(&len);
|
||||
read(S32(len), buf);
|
||||
buf[len] = 0;
|
||||
}
|
||||
|
||||
const char *Stream::readSTString(bool casesens)
|
||||
{
|
||||
char buf[256];
|
||||
readString(buf);
|
||||
return StringTable->insert(buf, casesens);
|
||||
}
|
||||
|
||||
void Stream::readLongString(U32 maxStringLen, char *stringBuf)
|
||||
{
|
||||
U32 len;
|
||||
read(&len);
|
||||
if(len > maxStringLen)
|
||||
{
|
||||
m_streamStatus = IOError;
|
||||
return;
|
||||
}
|
||||
read(len, stringBuf);
|
||||
stringBuf[len] = 0;
|
||||
}
|
||||
|
||||
void Stream::writeLongString(U32 maxStringLen, const char *string)
|
||||
{
|
||||
U32 len = dStrlen(string);
|
||||
if(len > maxStringLen)
|
||||
len = maxStringLen;
|
||||
write(len);
|
||||
write(len, string);
|
||||
}
|
||||
|
||||
void Stream::readLine(U8 *buffer, U32 bufferSize)
|
||||
{
|
||||
bufferSize--; // account for NULL terminator
|
||||
U8 *buff = buffer;
|
||||
U8 *buffEnd = buff + bufferSize;
|
||||
*buff = '\r';
|
||||
|
||||
// strip off preceding white space
|
||||
while ( *buff == '\r' )
|
||||
if ( !read(buff) || *buff == '\n' )
|
||||
{
|
||||
*buff = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// read line
|
||||
while ( buff != buffEnd && read(++buff) && *buff != '\n' )
|
||||
if ( *buff == '\r' )
|
||||
buff--;
|
||||
*buff = 0;
|
||||
}
|
||||
|
||||
void Stream::writeLine(U8 *buffer)
|
||||
{
|
||||
write(dStrlen((StringTableEntry)buffer), buffer);
|
||||
write(2, "\r\n");
|
||||
}
|
||||
|
||||
bool Stream::write(const ColorI& rColor)
|
||||
{
|
||||
bool success = write(rColor.red);
|
||||
success |= write(rColor.green);
|
||||
success |= write(rColor.blue);
|
||||
success |= write(rColor.alpha);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool Stream::write(const ColorF& rColor)
|
||||
{
|
||||
ColorI temp = rColor;
|
||||
return write(temp);
|
||||
}
|
||||
|
||||
bool Stream::read(ColorI* pColor)
|
||||
{
|
||||
bool success = read(&pColor->red);
|
||||
success |= read(&pColor->green);
|
||||
success |= read(&pColor->blue);
|
||||
success |= read(&pColor->alpha);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool Stream::read(ColorF* pColor)
|
||||
{
|
||||
ColorI temp;
|
||||
bool success = read(&temp);
|
||||
|
||||
*pColor = temp;
|
||||
return success;
|
||||
}
|
||||
|
||||
60
core/nTypes.cc
Normal file
60
core/nTypes.cc
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "Platform/platform.h"
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
U32 getNextPow2(U32 io_num)
|
||||
{
|
||||
S32 oneCount = 0;
|
||||
S32 shiftCount = -1;
|
||||
while (io_num) {
|
||||
if(io_num & 1)
|
||||
oneCount++;
|
||||
shiftCount++;
|
||||
io_num >>= 1;
|
||||
}
|
||||
if(oneCount > 1)
|
||||
shiftCount++;
|
||||
|
||||
return U32(1 << shiftCount);
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
U32 getBinLog2(U32 io_num)
|
||||
{
|
||||
AssertFatal(io_num != 0 && isPow2(io_num) == true,
|
||||
"Error, this only works on powers of 2 > 0");
|
||||
|
||||
S32 shiftCount = 0;
|
||||
while (io_num) {
|
||||
shiftCount++;
|
||||
io_num >>= 1;
|
||||
}
|
||||
|
||||
return U32(shiftCount - 1);
|
||||
}
|
||||
|
||||
#ifdef linux
|
||||
|
||||
// Linux has no stricmp function, we must provide it. Stolen from
|
||||
// the MS CRT source.
|
||||
//
|
||||
S32 stricmp(const char* in_p1, const char* in_p2)
|
||||
{
|
||||
S32 f, l;
|
||||
|
||||
do {
|
||||
f = dTolower( (unsigned char)(*(in_p1++)) );
|
||||
l = dTolower( (unsigned char)(*(in_p2++)) );
|
||||
} while ( f && (f == l) );
|
||||
|
||||
return f - l;
|
||||
}
|
||||
|
||||
#endif
|
||||
56
core/polyList.h
Normal file
56
core/polyList.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _POLYLIST_H_
|
||||
#define _POLYLIST_H_
|
||||
|
||||
//Includes
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "Platform/platform.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "Core/tVector.h"
|
||||
#endif
|
||||
#ifndef _MPOINT_H_
|
||||
#include "Math/mPoint.h"
|
||||
#endif
|
||||
#ifndef _MPLANE_H_
|
||||
#include "Math/mPlane.h"
|
||||
#endif
|
||||
|
||||
class PolyList
|
||||
{
|
||||
public:
|
||||
struct Poly {
|
||||
public:
|
||||
PlaneF plane;
|
||||
U32 material;
|
||||
|
||||
U16 vStart;
|
||||
U16 vCount;
|
||||
|
||||
// NOTE: USE THESE FUNCTIONS! The above exposed implementation is likely
|
||||
// to change soon.
|
||||
public:
|
||||
U16 getVIndex(U16 n) const { return U16(vStart + n); }
|
||||
U16 getVCount() const { return vCount; }
|
||||
|
||||
const PlaneF& getPlane() const { return plane; }
|
||||
U32 getMaterial() const { return material; }
|
||||
};
|
||||
|
||||
public:
|
||||
PolyList() {
|
||||
VECTOR_SET_ASSOCIATION(mPolys);
|
||||
VECTOR_SET_ASSOCIATION(mVertices);
|
||||
}
|
||||
|
||||
Vector<Poly> mPolys;
|
||||
Vector<Point3F> mVertices;
|
||||
};
|
||||
|
||||
#endif //_POLYLIST_H_
|
||||
29
core/realComp.h
Normal file
29
core/realComp.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _REALCOMP_H_
|
||||
#define _REALCOMP_H_
|
||||
|
||||
//Includes
|
||||
#ifndef _MMATHFN_H_
|
||||
#include "Math/mMathFn.h"
|
||||
#endif
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "Platform/platform.h"
|
||||
#endif
|
||||
|
||||
inline bool isEqual(F32 a, F32 b)
|
||||
{
|
||||
return mFabs(a - b) < __EQUAL_CONST_F;
|
||||
}
|
||||
|
||||
inline bool isZero(F32 a)
|
||||
{
|
||||
return mFabs(a) < __EQUAL_CONST_F;
|
||||
}
|
||||
|
||||
#endif //_REALCOMP_H_
|
||||
130
core/resDictionary.cc
Normal file
130
core/resDictionary.cc
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "Platform/platform.h"
|
||||
#include "Core/resManager.h"
|
||||
#include "Core/tAlgorithm.h"
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
ResDictionary::ResDictionary()
|
||||
{
|
||||
entryCount = 0;
|
||||
hashTableSize = 1023; //DefaultTableSize;
|
||||
hashTable = new ResourceObject *[hashTableSize];
|
||||
S32 i;
|
||||
for(i = 0; i < hashTableSize; i++)
|
||||
hashTable[i] = NULL;
|
||||
}
|
||||
|
||||
ResDictionary::~ResDictionary()
|
||||
{
|
||||
// we assume the resources are purged before we destroy
|
||||
// the dictionary
|
||||
|
||||
delete[] hashTable;
|
||||
}
|
||||
|
||||
S32 ResDictionary::hash(StringTableEntry path, StringTableEntry file)
|
||||
{
|
||||
return ((U32(path) >> 2) + (U32(file) >> 2) ) % hashTableSize;
|
||||
}
|
||||
|
||||
void ResDictionary::insert(ResourceObject *obj, StringTableEntry path, StringTableEntry file)
|
||||
{
|
||||
obj->name = file;
|
||||
obj->path = path;
|
||||
|
||||
S32 idx = hash(path, file);
|
||||
obj->nextEntry = hashTable[idx];
|
||||
hashTable[idx] = obj;
|
||||
entryCount++;
|
||||
|
||||
if(entryCount > hashTableSize) {
|
||||
ResourceObject *head = NULL, *temp, *walk;
|
||||
for(idx = 0; idx < hashTableSize;idx++) {
|
||||
walk = hashTable[idx];
|
||||
while(walk)
|
||||
{
|
||||
temp = walk->nextEntry;
|
||||
walk->nextEntry = head;
|
||||
head = walk;
|
||||
walk = temp;
|
||||
}
|
||||
}
|
||||
delete[] hashTable;
|
||||
hashTableSize = 2 * hashTableSize - 1;
|
||||
hashTable = new ResourceObject *[hashTableSize];
|
||||
for(idx = 0; idx < hashTableSize; idx++)
|
||||
hashTable[idx] = NULL;
|
||||
walk = head;
|
||||
while(walk)
|
||||
{
|
||||
temp = walk->nextEntry;
|
||||
idx = hash(walk);
|
||||
walk->nextEntry = hashTable[idx];
|
||||
hashTable[idx] = walk;
|
||||
walk = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ResourceObject* ResDictionary::find(StringTableEntry path, StringTableEntry name)
|
||||
{
|
||||
for(ResourceObject *walk = hashTable[hash(path, name)]; walk; walk = walk->nextEntry)
|
||||
if(walk->name == name && walk->path == path)
|
||||
return walk;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ResourceObject* ResDictionary::find(StringTableEntry path, StringTableEntry name, StringTableEntry filePath, StringTableEntry fileName)
|
||||
{
|
||||
for(ResourceObject *walk = hashTable[hash(path, name)]; walk; walk = walk->nextEntry)
|
||||
if(walk->name == name && walk->path == path && walk->fileName == fileName && walk->filePath == filePath)
|
||||
return walk;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ResourceObject* ResDictionary::find(StringTableEntry path, StringTableEntry name, U32 flags)
|
||||
{
|
||||
for(ResourceObject *walk = hashTable[hash(path, name)]; walk; walk = walk->nextEntry)
|
||||
if(walk->name == name && walk->path == path && U32(walk->flags) == flags)
|
||||
return walk;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ResDictionary::pushBehind(ResourceObject *resObj, S32 flagMask)
|
||||
{
|
||||
remove(resObj);
|
||||
entryCount++;
|
||||
ResourceObject **walk = &hashTable[hash(resObj)];
|
||||
for(; *walk; walk = &(*walk)->nextEntry)
|
||||
{
|
||||
if(!((*walk)->flags & flagMask))
|
||||
{
|
||||
resObj->nextEntry = *walk;
|
||||
*walk = resObj;
|
||||
return;
|
||||
}
|
||||
}
|
||||
resObj->nextEntry = NULL;
|
||||
*walk = resObj;
|
||||
}
|
||||
|
||||
void ResDictionary::remove(ResourceObject *resObj)
|
||||
{
|
||||
for(ResourceObject **walk = &hashTable[hash(resObj)]; *walk; walk = &(*walk)->nextEntry)
|
||||
{
|
||||
if(*walk == resObj)
|
||||
{
|
||||
entryCount--;
|
||||
*walk = resObj->nextEntry;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
970
core/resManager.cc
Normal file
970
core/resManager.cc
Normal file
|
|
@ -0,0 +1,970 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "core/tVector.h"
|
||||
#include "core/stream.h"
|
||||
|
||||
#include "core/fileStream.h"
|
||||
#include "core/zipSubStream.h"
|
||||
#include "core/zipAggregate.h"
|
||||
#include "core/zipHeaders.h"
|
||||
#include "core/resizeStream.h"
|
||||
#include "sim/frameAllocator.h"
|
||||
|
||||
#include "core/resManager.h"
|
||||
#include "core/findMatch.h"
|
||||
|
||||
#include "console/console.h"
|
||||
|
||||
ResManager* ResourceManager = NULL;
|
||||
static char sgCurExeDir[1024];
|
||||
static S32 sgCurExeDirStrLen;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ResourceObject::ResourceObject()
|
||||
{
|
||||
next = NULL;
|
||||
prev = NULL;
|
||||
lockCount = 0;
|
||||
mInstance = NULL;
|
||||
}
|
||||
|
||||
void ResourceObject::destruct()
|
||||
{
|
||||
// If the resource was not loaded because of an error, the resource
|
||||
// pointer will be NULL
|
||||
if (mInstance) {
|
||||
delete mInstance;
|
||||
mInstance = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
ResManager::ResManager()
|
||||
{
|
||||
echoFileNames = 0;
|
||||
primaryPath[0] = 0;
|
||||
writeablePath[0] = 0;
|
||||
pathList = NULL;
|
||||
resourceList.nextResource = NULL;
|
||||
resourceList.next = NULL;
|
||||
resourceList.prev = NULL;
|
||||
timeoutList.nextResource = NULL;
|
||||
timeoutList.next = NULL;
|
||||
timeoutList.prev = NULL;
|
||||
sgCurExeDir[0] = '\0';
|
||||
Platform::getCurrentDirectory(sgCurExeDir, 1024);
|
||||
sgCurExeDirStrLen = dStrlen(sgCurExeDir);
|
||||
registeredList = NULL;
|
||||
}
|
||||
|
||||
void ResourceObject::getFileTimes(FileTime *createTime, FileTime *modifyTime)
|
||||
{
|
||||
char buffer[1024];
|
||||
#ifdef __linux
|
||||
dSprintf( buffer, sizeof( buffer ), "%s/%s", filePath, fileName );
|
||||
#else
|
||||
dSprintf(buffer, sizeof(buffer), "%s/%s/%s", sgCurExeDir, filePath, fileName);
|
||||
#endif
|
||||
Platform::getFileTimes(buffer, createTime, modifyTime);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ResManager::~ResManager()
|
||||
{
|
||||
purge();
|
||||
// volume list should be gone.
|
||||
|
||||
if ( pathList )
|
||||
dFree( pathList );
|
||||
|
||||
for(ResourceObject *walk = resourceList.nextResource; walk; walk = walk->nextResource)
|
||||
walk->destruct();
|
||||
|
||||
while(resourceList.nextResource)
|
||||
freeResource(resourceList.nextResource);
|
||||
|
||||
while(registeredList)
|
||||
{
|
||||
RegisteredExtension *temp = registeredList->next;
|
||||
delete registeredList;
|
||||
registeredList = temp;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void ResManager::dumpLoadedResources()
|
||||
{
|
||||
ResourceObject* walk = resourceList.nextResource;
|
||||
while (walk != NULL)
|
||||
{
|
||||
if (walk->mInstance != NULL)
|
||||
{
|
||||
Con::errorf("LoadedRes: %s/%s (%d)", walk->path, walk->name, walk->lockCount);
|
||||
}
|
||||
walk = walk->nextResource;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void ResManager::create()
|
||||
{
|
||||
AssertFatal(ResourceManager == NULL, "ResourceManager::create: manager already exists.");
|
||||
ResourceManager = new ResManager;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void ResManager::destroy()
|
||||
{
|
||||
AssertFatal(ResourceManager != NULL, "ResourceManager::destroy: manager does not exist.");
|
||||
delete ResourceManager;
|
||||
ResourceManager = NULL;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ResManager::setFileNameEcho(bool on)
|
||||
{
|
||||
echoFileNames = on;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ResManager::isValidWriteFileName(const char *fn)
|
||||
{
|
||||
// all files must be based off the VFS
|
||||
if(fn[0] == '/' || dStrchr(fn, ':'))
|
||||
return false;
|
||||
|
||||
if(!writeablePath[0])
|
||||
return true;
|
||||
|
||||
// get the path to the file
|
||||
const char *path = dStrrchr(fn, '/');
|
||||
if(!path)
|
||||
path = fn;
|
||||
else
|
||||
{
|
||||
if(!dStrchr(path, '.'))
|
||||
return false;
|
||||
}
|
||||
// now loop through the writeable path.
|
||||
const char *start = writeablePath;
|
||||
S32 pathLen = path - fn;
|
||||
for(;;)
|
||||
{
|
||||
const char *end = dStrchr(writeablePath, ';');
|
||||
if(!end)
|
||||
end = writeablePath + dStrlen(writeablePath);
|
||||
|
||||
if(end - start == pathLen && !dStrnicmp(start, path, pathLen))
|
||||
return true;
|
||||
if(end[0])
|
||||
start = end + 1;
|
||||
else
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ResManager::setWriteablePath(const char *path)
|
||||
{
|
||||
dStrcpy(writeablePath, path);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static const char *buildPath(StringTableEntry path, StringTableEntry file)
|
||||
{
|
||||
static char buf[1024];
|
||||
if(path)
|
||||
dSprintf(buf, sizeof(buf), "%s/%s", path, file);
|
||||
else
|
||||
dStrcpy(buf, file);
|
||||
return buf;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static void getPaths(const char *fullPath, StringTableEntry &path, StringTableEntry &fileName)
|
||||
{
|
||||
static char buf[1024];
|
||||
char *ptr = (char *) dStrrchr(fullPath, '/');
|
||||
if(!ptr)
|
||||
{
|
||||
path = NULL;
|
||||
fileName = StringTable->insert(fullPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
S32 len = ptr - fullPath;
|
||||
dStrncpy(buf, fullPath, len);
|
||||
buf[len] = 0;
|
||||
fileName = StringTable->insert(ptr + 1);
|
||||
path = StringTable->insert(buf);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool ResManager::scanZip(ResourceObject *zipObject)
|
||||
{
|
||||
// now open the volume and add all its resources to the dictionary
|
||||
ZipAggregate zipAggregate;
|
||||
if (zipAggregate.openAggregate(buildPath(zipObject->filePath, zipObject->fileName)) == false) {
|
||||
AssertFatal(false, "Error opening zip, need to handle this better...");
|
||||
return false;
|
||||
}
|
||||
ZipAggregate::iterator itr;
|
||||
for (itr = zipAggregate.begin(); itr != zipAggregate.end(); itr++) {
|
||||
const ZipAggregate::FileEntry& rEntry = *itr;
|
||||
|
||||
ResourceObject* ro = createResource(rEntry.pPath, rEntry.pFileName,
|
||||
zipObject->filePath, zipObject->fileName);
|
||||
|
||||
ro->flags = ResourceObject::VolumeBlock;
|
||||
ro->fileSize = rEntry.fileSize;
|
||||
ro->compressedFileSize = rEntry.compressedFileSize;
|
||||
ro->fileOffset = rEntry.fileOffset;
|
||||
|
||||
dictionary.pushBehind(ro, ResourceObject::File);
|
||||
}
|
||||
zipAggregate.closeAggregate();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void ResManager::searchPath(const char* basePath)
|
||||
{
|
||||
AssertFatal(basePath != NULL, "No path to dump?");
|
||||
|
||||
Vector<Platform::FileInfo> fileInfoVec;
|
||||
Platform::dumpPath(basePath, fileInfoVec);
|
||||
|
||||
for (U32 i = 0; i < fileInfoVec.size(); i++) {
|
||||
Platform::FileInfo& rInfo = fileInfoVec[i];
|
||||
|
||||
// Create a resource for this file...
|
||||
//
|
||||
ResourceObject *ro = createResource(rInfo.pVirtPath, rInfo.pFileName, rInfo.pFullPath, rInfo.pFileName);
|
||||
dictionary.pushBehind(ro, ResourceObject::File);
|
||||
|
||||
ro->flags = ResourceObject::File;
|
||||
ro->fileOffset = 0;
|
||||
ro->fileSize = rInfo.fileSize;
|
||||
ro->compressedFileSize = rInfo.fileSize;
|
||||
|
||||
// see if it's a zip
|
||||
const char *extension = dStrrchr(ro->fileName, '.');
|
||||
if(extension && !dStricmp(extension, ".zip"))
|
||||
scanZip(ro);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void ResManager::setModPaths(U32 numPaths, const char **paths)
|
||||
{
|
||||
// detach all the files.
|
||||
for(ResourceObject *pwalk = resourceList.nextResource; pwalk; pwalk = pwalk->nextResource)
|
||||
pwalk->flags = ResourceObject::Added;
|
||||
|
||||
bool primaryWriteSet = false;
|
||||
primaryPath[0] = 0;
|
||||
U32 pathLen = 0;
|
||||
|
||||
U32 i;
|
||||
for(i = 0; i < numPaths; i++)
|
||||
{
|
||||
// silent fail on any invalid paths
|
||||
if(dStrchr(paths[i], '/') || dStrchr(paths[i], '.') || dStrchr(paths[i], ':') || dStrlen(paths[i]) == 0)
|
||||
continue;
|
||||
if(!primaryWriteSet)
|
||||
{
|
||||
dStrcpy(primaryPath, paths[i]);
|
||||
primaryWriteSet = true;
|
||||
}
|
||||
pathLen += ( dStrlen( paths[i] ) + 1 );
|
||||
searchPath(paths[i]);
|
||||
#ifdef __linux
|
||||
// FIXME: Add this to Platform:: and implement this for Windows/PPC.
|
||||
extern bool platformGetUserPath( const char*, char*, U32 );
|
||||
char user[256];
|
||||
|
||||
if( platformGetUserPath( paths[i], user, 256 ) ) {
|
||||
searchPath( user );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
pathList = (char*) dRealloc( pathList, pathLen );
|
||||
dStrcpy( pathList, paths[0] );
|
||||
U32 strlen;
|
||||
for ( i = 1; i < numPaths; i++ )
|
||||
{
|
||||
strlen = dStrlen( pathList );
|
||||
dSprintf( pathList + strlen, pathLen - strlen, ";%s", paths[i] );
|
||||
}
|
||||
|
||||
// unlink all the added baddies that aren't loaded.
|
||||
ResourceObject *rwalk = resourceList.nextResource, *rtemp;
|
||||
while(rwalk != NULL)
|
||||
{
|
||||
if((rwalk->flags & ResourceObject::Added) && !rwalk->mInstance)
|
||||
{
|
||||
rwalk->unlink();
|
||||
dictionary.remove(rwalk);
|
||||
rtemp = rwalk->nextResource;
|
||||
freeResource(rwalk);
|
||||
rwalk = rtemp;
|
||||
}
|
||||
else
|
||||
rwalk = rwalk->nextResource;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const char* ResManager::getModPaths()
|
||||
{
|
||||
return( (const char*) pathList );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
S32 ResManager::getSize(const char *fileName)
|
||||
{
|
||||
ResourceObject *ro = find(fileName);
|
||||
if(!ro)
|
||||
return 0;
|
||||
else
|
||||
return ro->fileSize;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const char* ResManager::getFullPath(const char* fileName, char *path, U32 pathlen)
|
||||
{
|
||||
AssertFatal(fileName, "ResourceManager::getFullPath: fileName is NULL");
|
||||
AssertFatal(path, "ResourceManager::getFullPath: path is NULL");
|
||||
ResourceObject *obj = find(fileName);
|
||||
if(!obj)
|
||||
dStrcpy(path, fileName);
|
||||
else
|
||||
dSprintf(path, pathlen, "%s/%s", obj->filePath, fileName);
|
||||
return path;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const char* ResManager::getPathOf(const char* fileName)
|
||||
{
|
||||
AssertFatal(fileName, "ResourceManager::getPathOf: fileName is NULL");
|
||||
ResourceObject *obj = find(fileName);
|
||||
if(!obj)
|
||||
return NULL;
|
||||
else
|
||||
return obj->filePath;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const char* ResManager::getModPathOf(const char* fileName)
|
||||
{
|
||||
AssertFatal(fileName, "ResourceManager::getModPathOf: fileName is NULL");
|
||||
|
||||
if (!pathList)
|
||||
return NULL;
|
||||
|
||||
ResourceObject *obj = find(fileName);
|
||||
if(!obj)
|
||||
return NULL;
|
||||
|
||||
char buffer[256];
|
||||
char *base;
|
||||
const char *list = pathList;
|
||||
do
|
||||
{
|
||||
base = buffer;
|
||||
*base = 0;
|
||||
while (*list && *list != ';')
|
||||
{
|
||||
*base++ = *list++;
|
||||
}
|
||||
if (*list == ';')
|
||||
++list;
|
||||
|
||||
*base = 0;
|
||||
|
||||
if (dStrncmp(buffer, obj->filePath, (base-buffer)) == 0)
|
||||
return StringTable->insert(buffer);
|
||||
|
||||
}while(*list);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const char* ResManager::getBasePath()
|
||||
{
|
||||
if (!pathList)
|
||||
return NULL;
|
||||
const char *base = dStrrchr(pathList, ';');
|
||||
return base ? (base+1) : pathList;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ResManager::registerExtension(const char *name, RESOURCE_CREATE_FN create_fn)
|
||||
{
|
||||
AssertFatal(!getCreateFunction(name), "ResourceManager::registerExtension: file extension already registered.");
|
||||
|
||||
const char *extension = dStrrchr( name, '.' );
|
||||
AssertFatal(extension, "ResourceManager::registerExtension: file has no extension.");
|
||||
|
||||
RegisteredExtension *add = new RegisteredExtension;
|
||||
add->mExtension = StringTable->insert(extension);
|
||||
add->mCreateFn = create_fn;
|
||||
add->next = registeredList;
|
||||
registeredList = add;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
RESOURCE_CREATE_FN ResManager::getCreateFunction( const char *name )
|
||||
{
|
||||
const char *s = dStrrchr( name, '.' );
|
||||
if (!s) return (NULL);
|
||||
|
||||
RegisteredExtension *itr = registeredList;
|
||||
while (itr)
|
||||
{
|
||||
if (dStricmp(s, itr->mExtension) == 0)
|
||||
return (itr->mCreateFn);
|
||||
itr = itr->next;
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void ResManager::unlock(ResourceObject *obj)
|
||||
{
|
||||
if (!obj) return;
|
||||
AssertFatal(obj->lockCount > 0, "ResourceManager::unlock: lock count is zero.");
|
||||
//set the timeout to the max requested
|
||||
if (--obj->lockCount == 0)
|
||||
obj->linkAfter(&timeoutList);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// gets the crc of the file, ignores the stream type
|
||||
bool ResManager::getCrc(const char * fileName, U32 & crcVal, const U32 crcInitialVal )
|
||||
{
|
||||
ResourceObject * obj = find(fileName);
|
||||
if(!obj)
|
||||
return(false);
|
||||
|
||||
// check if in a volume
|
||||
if(obj->flags & (ResourceObject::VolumeBlock | ResourceObject::File))
|
||||
{
|
||||
// can't crc locked resources...
|
||||
if(obj->lockCount)
|
||||
return false;
|
||||
|
||||
// get rid of the resource
|
||||
// have to make sure user can't have it sitting around in the resource cache
|
||||
|
||||
obj->unlink();
|
||||
obj->destruct();
|
||||
|
||||
Stream *stream = openStream(obj);
|
||||
|
||||
U32 waterMark = 0xFFFFFFFF;
|
||||
|
||||
U8 *buffer;
|
||||
U32 maxSize = FrameAllocator::getHighWaterMark() - FrameAllocator::getWaterMark();
|
||||
if(maxSize < obj->fileSize)
|
||||
buffer = new U8[obj->fileSize];
|
||||
else
|
||||
{
|
||||
waterMark = FrameAllocator::getWaterMark();
|
||||
buffer = (U8*) FrameAllocator::alloc(obj->fileSize);
|
||||
}
|
||||
stream->read(obj->fileSize, buffer);
|
||||
// get the crc value
|
||||
crcVal = calculateCRC(buffer, obj->fileSize, crcInitialVal);
|
||||
if(waterMark == 0xFFFFFFFF)
|
||||
delete [] buffer;
|
||||
else
|
||||
FrameAllocator::setWaterMark(waterMark);
|
||||
|
||||
closeStream(stream);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
ResourceObject* ResManager::load(const char *fileName, bool computeCRC)
|
||||
{
|
||||
// if filename is not known, exit now
|
||||
ResourceObject *obj = find(fileName);
|
||||
if(!obj)
|
||||
return NULL;
|
||||
|
||||
// if noone has a lock on this, but it's loaded and it needs to
|
||||
// be CRC'd, delete it and reload it.
|
||||
if(!obj->lockCount && computeCRC && obj->mInstance)
|
||||
obj->destruct();
|
||||
|
||||
obj->lockCount++;
|
||||
obj->unlink(); // remove from purge list
|
||||
if(!obj->mInstance)
|
||||
{
|
||||
obj->mInstance = loadInstance(obj, computeCRC);
|
||||
if(!obj->mInstance)
|
||||
{
|
||||
obj->lockCount--;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ResourceInstance *ResManager::loadInstance(const char *fileName, bool computeCRC)
|
||||
{
|
||||
// if filename is not known, exit now
|
||||
ResourceObject *obj = find(fileName);
|
||||
if(!obj)
|
||||
return NULL;
|
||||
|
||||
return loadInstance(obj, computeCRC);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static const char *alwaysCRCList = ".ter.dif.dts";
|
||||
|
||||
ResourceInstance *ResManager::loadInstance(ResourceObject *obj, bool computeCRC)
|
||||
{
|
||||
Stream *stream = openStream(obj);
|
||||
if(!stream)
|
||||
return NULL;
|
||||
|
||||
if(!computeCRC)
|
||||
{
|
||||
const char *x = dStrrchr(obj->name, '.');
|
||||
if(x && dStrstr(alwaysCRCList, x))
|
||||
computeCRC = true;
|
||||
}
|
||||
|
||||
if(computeCRC)
|
||||
obj->crc = calculateCRCStream(stream, InvalidCRC);
|
||||
else
|
||||
obj->crc = InvalidCRC;
|
||||
|
||||
RESOURCE_CREATE_FN createFunction = ResourceManager->getCreateFunction(obj->name);
|
||||
AssertFatal(createFunction, "ResourceObject::construct: NULL resource create function.");
|
||||
ResourceInstance *ret = createFunction( *stream );
|
||||
closeStream(stream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Stream* ResManager::openStream(const char * fileName)
|
||||
{
|
||||
ResourceObject *obj = find(fileName);
|
||||
if(!obj)
|
||||
return NULL;
|
||||
return openStream(obj);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Stream* ResManager::openStream(ResourceObject *obj)
|
||||
{
|
||||
// if filename is not known, exit now
|
||||
if(!obj)
|
||||
return NULL;
|
||||
|
||||
if(echoFileNames)
|
||||
Con::printf("FILE ACCESS: %s/%s", obj->path, obj->name);
|
||||
|
||||
// used for openStream stream access
|
||||
FileStream* diskStream = NULL;
|
||||
|
||||
if(obj->flags & (ResourceObject::File | ResourceObject::VolumeBlock))
|
||||
{
|
||||
diskStream = new FileStream;
|
||||
diskStream->open(buildPath(obj->filePath, obj->fileName), FileStream::Read);
|
||||
|
||||
if(obj->flags & ResourceObject::File) {
|
||||
obj->fileSize = diskStream->getStreamSize();
|
||||
}
|
||||
diskStream->setPosition(obj->fileOffset);
|
||||
|
||||
if (obj->flags & ResourceObject::VolumeBlock)
|
||||
{
|
||||
ZipLocalFileHeader zlfHeader;
|
||||
|
||||
if (zlfHeader.readFromStream(*diskStream) == false)
|
||||
{
|
||||
AssertFatal(false, avar("ResourceManager::loadStream: '%s' Not in the zip! (%s)", obj->name, obj->fileName));
|
||||
diskStream->close();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (zlfHeader.m_header.compressionMethod == ZipLocalFileHeader::Stored || obj->fileSize == 0)
|
||||
{
|
||||
// Just read straight from the stream...
|
||||
ResizeFilterStream *strm = new ResizeFilterStream;
|
||||
strm->attachStream(diskStream);
|
||||
strm->setStreamOffset(diskStream->getPosition(), obj->fileSize);
|
||||
return strm;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (zlfHeader.m_header.compressionMethod == ZipLocalFileHeader::Deflated)
|
||||
{
|
||||
ZipSubRStream* zipStream = new ZipSubRStream;
|
||||
zipStream->attachStream(diskStream);
|
||||
zipStream->setUncompressedSize(obj->fileSize);
|
||||
return zipStream;
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertFatal(false, avar("ResourceManager::loadStream: '%s' Compressed inappropriately in the zip! (%s)", obj->name, obj->fileName));
|
||||
diskStream->close();
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return diskStream;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void ResManager::closeStream(Stream *stream)
|
||||
{
|
||||
FilterStream *subStream = dynamic_cast<FilterStream *>(stream);
|
||||
if(subStream)
|
||||
{
|
||||
stream = subStream->getStream();
|
||||
subStream->detachStream();
|
||||
delete subStream;
|
||||
}
|
||||
delete stream;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ResourceObject* ResManager::find(const char *fileName)
|
||||
{
|
||||
if(!fileName)
|
||||
return NULL;
|
||||
StringTableEntry path, file;
|
||||
getPaths(fileName, path, file);
|
||||
return dictionary.find(path, file);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ResourceObject* ResManager::find(const char *fileName, U32 flags)
|
||||
{
|
||||
if(!fileName)
|
||||
return NULL;
|
||||
StringTableEntry path, file;
|
||||
getPaths(fileName, path, file);
|
||||
return dictionary.find(path, file, flags);
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Add resource constructed outside the manager
|
||||
|
||||
bool ResManager::add(const char* name, ResourceInstance *addInstance, bool extraLock)
|
||||
{
|
||||
StringTableEntry path, file;
|
||||
getPaths(name, path, file);
|
||||
|
||||
ResourceObject* obj = dictionary.find(path, file);
|
||||
if (obj && obj->mInstance)
|
||||
// Resource already exists?
|
||||
return false;
|
||||
|
||||
if (!obj)
|
||||
obj = createResource(path, file, NULL, NULL);
|
||||
|
||||
dictionary.pushBehind(obj, ResourceObject::File | ResourceObject::VolumeBlock);
|
||||
obj->mInstance = addInstance;
|
||||
obj->lockCount = extraLock ? 2 : 1;
|
||||
unlock(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ResManager::purge()
|
||||
{
|
||||
bool found;
|
||||
do {
|
||||
ResourceObject *obj = timeoutList.getNext();
|
||||
found = false;
|
||||
while(obj)
|
||||
{
|
||||
ResourceObject *temp = obj;
|
||||
obj = obj->next;
|
||||
temp->unlink();
|
||||
temp->destruct();
|
||||
found = true;
|
||||
if(temp->flags & ResourceObject::Added)
|
||||
freeResource(temp);
|
||||
}
|
||||
} while(found);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ResManager::purge( ResourceObject *obj )
|
||||
{
|
||||
AssertFatal(obj->lockCount == 0, "ResourceManager::purge: handle lock count is not ZERO.")
|
||||
obj->unlink();
|
||||
obj->destruct();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// serialize sorts a list of files by .zip and position within the zip
|
||||
// it allows an aggregate (material list, etc) to find the preffered
|
||||
// loading order for a set of files.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct ResourceObjectIndex
|
||||
{
|
||||
ResourceObject *ro;
|
||||
const char *fileName;
|
||||
|
||||
static S32 QSORT_CALLBACK compare(const void *s1, const void *s2)
|
||||
{
|
||||
const ResourceObjectIndex *r1 = (ResourceObjectIndex *) s1;
|
||||
const ResourceObjectIndex *r2 = (ResourceObjectIndex *) s2;
|
||||
|
||||
if(r1->ro->filePath != r2->ro->filePath)
|
||||
return r1->ro->filePath - r2->ro->filePath;
|
||||
if(r1->ro->fileName != r2->ro->fileName)
|
||||
return r1->ro->fileName - r2->ro->fileName;
|
||||
return r1->ro->fileOffset - r2->ro->fileOffset;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ResManager::serialize(VectorPtr<const char *> &filenames)
|
||||
{
|
||||
Vector<ResourceObjectIndex> sortVector;
|
||||
|
||||
sortVector.reserve(filenames.size());
|
||||
|
||||
U32 i;
|
||||
for(i = 0; i < filenames.size(); i++)
|
||||
{
|
||||
ResourceObjectIndex roi;
|
||||
roi.ro = find(filenames[i]);
|
||||
roi.fileName = filenames[i];
|
||||
sortVector.push_back(roi);
|
||||
}
|
||||
|
||||
dQsort((void *) &sortVector[0], sortVector.size(), sizeof(ResourceObjectIndex), ResourceObjectIndex::compare);
|
||||
for(i = 0; i < filenames.size(); i++)
|
||||
filenames[i] = sortVector[i].fileName;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ResourceObject* ResManager::findMatch(const char *expression, const char **fn, ResourceObject *start)
|
||||
{
|
||||
if(!start)
|
||||
start = resourceList.nextResource;
|
||||
else
|
||||
start = start->nextResource;
|
||||
while(start)
|
||||
{
|
||||
const char *fname = buildPath(start->path, start->name);
|
||||
if(FindMatch::isMatch(expression, fname, false))
|
||||
{
|
||||
*fn = fname;
|
||||
return start;
|
||||
}
|
||||
start = start->nextResource;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
S32 ResManager::findMatches( FindMatch *pFM )
|
||||
{
|
||||
static char buffer[16384];
|
||||
S32 bufl = 0;
|
||||
ResourceObject *walk;
|
||||
for(walk = resourceList.nextResource; walk && !pFM->isFull(); walk = walk->nextResource)
|
||||
{
|
||||
const char *fpath = buildPath(walk->path, walk->name);
|
||||
if(bufl + dStrlen(fpath) >= 16380)
|
||||
return pFM->numMatches();
|
||||
dStrcpy(buffer + bufl, fpath);
|
||||
if(pFM->findMatch(buffer + bufl))
|
||||
bufl += dStrlen(fpath) + 1;
|
||||
}
|
||||
return ( pFM->numMatches() );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool ResManager::findFile(const char *name)
|
||||
{
|
||||
return (bool) find(name);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ResourceObject *ResManager::createResource(StringTableEntry path, StringTableEntry file, StringTableEntry filePath, StringTableEntry fileName)
|
||||
{
|
||||
ResourceObject *newRO = dictionary.find(path, file, filePath, fileName);
|
||||
if(newRO)
|
||||
return newRO;
|
||||
|
||||
newRO = new ResourceObject;
|
||||
newRO->path = path;
|
||||
newRO->name = file;
|
||||
newRO->lockCount = 0;
|
||||
newRO->mInstance = NULL;
|
||||
// newRO->lockedData = NULL;
|
||||
newRO->flags = ResourceObject::Added;
|
||||
newRO->next = newRO->prev = NULL;
|
||||
newRO->nextResource = resourceList.nextResource;
|
||||
resourceList.nextResource = newRO;
|
||||
newRO->prevResource = &resourceList;
|
||||
if(newRO->nextResource)
|
||||
newRO->nextResource->prevResource = newRO;
|
||||
dictionary.insert(newRO, path, file);
|
||||
newRO->fileSize = newRO->fileOffset = newRO->compressedFileSize = 0;
|
||||
newRO->filePath = filePath;
|
||||
newRO->fileName = fileName;
|
||||
newRO->crc = InvalidCRC;
|
||||
|
||||
return newRO;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void ResManager::freeResource(ResourceObject *ro)
|
||||
{
|
||||
ro->destruct();
|
||||
ro->unlink();
|
||||
|
||||
// if((ro->flags & ResourceObject::File) && ro->lockedData)
|
||||
// delete[] ro->lockedData;
|
||||
|
||||
if(ro->prevResource)
|
||||
ro->prevResource->nextResource = ro->nextResource;
|
||||
if(ro->nextResource)
|
||||
ro->nextResource->prevResource = ro->prevResource;
|
||||
dictionary.remove(ro);
|
||||
delete ro;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// simple crc function - generates lookup table on first call
|
||||
|
||||
static U32 crcTable[256];
|
||||
static bool crcTableValid;
|
||||
|
||||
static void calculateCRCTable()
|
||||
{
|
||||
U32 val;
|
||||
|
||||
for(S32 i = 0; i < 256; i++)
|
||||
{
|
||||
val = i;
|
||||
for(S32 j = 0; j < 8; j++)
|
||||
{
|
||||
if(val & 0x01)
|
||||
val = 0xedb88320 ^ (val >> 1);
|
||||
else
|
||||
val = val >> 1;
|
||||
}
|
||||
crcTable[i] = val;
|
||||
}
|
||||
|
||||
crcTableValid = true;
|
||||
}
|
||||
|
||||
U32 calculateCRC(void * buffer, S32 len, U32 crcVal )
|
||||
{
|
||||
|
||||
// check if need to generate the crc table
|
||||
if(!crcTableValid)
|
||||
calculateCRCTable();
|
||||
|
||||
// now calculate the crc
|
||||
char * buf = (char*)buffer;
|
||||
for(S32 i = 0; i < len; i++)
|
||||
crcVal = crcTable[(crcVal ^ buf[i]) & 0xff] ^ (crcVal >> 8);
|
||||
return(crcVal);
|
||||
}
|
||||
|
||||
U32 calculateCRCStream(Stream *stream, U32 crcVal )
|
||||
{
|
||||
stream->setPosition(0);
|
||||
// check if need to generate the crc table
|
||||
if(!crcTableValid)
|
||||
calculateCRCTable();
|
||||
|
||||
// now calculate the crc
|
||||
S32 len = stream->getStreamSize();
|
||||
U8 buf[4096];
|
||||
|
||||
S32 segCount = (len + 4095) / 4096;
|
||||
|
||||
for(S32 j = 0; j < segCount; j++)
|
||||
{
|
||||
S32 slen = getMin(4096, len - (j * 4096));
|
||||
stream->read(slen, buf);
|
||||
crcVal = calculateCRC(buf, slen, crcVal);
|
||||
}
|
||||
stream->setPosition(0);
|
||||
return(crcVal);
|
||||
}
|
||||
|
||||
bool ResManager::openFileForWrite(FileStream &stream, const char *modPath, const char *fileName, U32 accessMode)
|
||||
{
|
||||
if(!primaryPath[0])
|
||||
return false;
|
||||
if(!isValidWriteFileName(fileName))
|
||||
return false;
|
||||
// tag it on to the first directory
|
||||
char fnBuf[1024];
|
||||
dSprintf(fnBuf, sizeof(fnBuf), "%s/%s", modPath ? modPath : primaryPath, fileName);
|
||||
if(!Platform::createPath(fnBuf)) // create directory tree
|
||||
return false;
|
||||
if(!stream.open(fnBuf, (FileStream::AccessMode) accessMode))
|
||||
return false;
|
||||
|
||||
// create a resource for the file.
|
||||
StringTableEntry rPath, rFileName, vPath, vFileName;
|
||||
getPaths(fnBuf, rPath, rFileName);
|
||||
getPaths(fileName, vPath, vFileName);
|
||||
|
||||
ResourceObject *ro = createResource(vPath, vFileName, rPath, rFileName);
|
||||
ro->flags = ResourceObject::File;
|
||||
ro->fileOffset = 0;
|
||||
ro->fileSize = 0;
|
||||
ro->compressedFileSize = 0;
|
||||
return true;
|
||||
}
|
||||
323
core/resManager.h
Normal file
323
core/resManager.h
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _RESMANAGER_H_
|
||||
#define _RESMANAGER_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/tVector.h"
|
||||
#endif
|
||||
#ifndef _STRINGTABLE_H_
|
||||
#include "core/stringTable.h"
|
||||
#endif
|
||||
|
||||
#ifndef _FILESTREAM_H_
|
||||
#include "core/fileStream.h"
|
||||
#endif
|
||||
#ifndef _ZIPSUBSTREAM_H_
|
||||
#include "core/zipSubStream.h"
|
||||
#endif
|
||||
#ifndef _ZIPAGGREGATE_H_
|
||||
#include "core/zipAggregate.h"
|
||||
#endif
|
||||
#ifndef _ZIPHEADERS_H_
|
||||
#include "core/zipHeaders.h"
|
||||
#endif
|
||||
|
||||
|
||||
#define RES_DEFAULT_TIMEOUT (5*60*1000) //5-minutes
|
||||
|
||||
class Stream;
|
||||
class FileStream;
|
||||
class ZipSubRStream;
|
||||
class ResManager;
|
||||
class FindMatch;
|
||||
|
||||
|
||||
extern ResManager *ResourceManager;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Basic resource manager behavior
|
||||
// -set the mod path
|
||||
// ResManager scans directory tree under listed base directories
|
||||
// Any .rpk (.zip) file in the root directory of a mod is added (scanned)
|
||||
// for resources
|
||||
// Any files currently in the resource manager become memory resources
|
||||
// They can be "reattached" to the first file that matches the file name
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
// all classes which wish to be handled by the resource manager
|
||||
// need to be
|
||||
// 1) derrived from ResourceInstance
|
||||
// 2) register a creation function and file extension with the manager
|
||||
|
||||
class ResourceInstance
|
||||
{
|
||||
private:
|
||||
public:
|
||||
virtual ~ResourceInstance() {}
|
||||
};
|
||||
|
||||
|
||||
typedef ResourceInstance* (*RESOURCE_CREATE_FN)(Stream &stream);
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#define InvalidCRC 0xFFFFFFFF
|
||||
|
||||
class ResourceObject
|
||||
{
|
||||
friend class ResDictionary;
|
||||
friend class ResManager;
|
||||
|
||||
ResourceObject *prev, *next; // timeout list
|
||||
ResourceObject *nextEntry; // objects are inserted by id or name
|
||||
ResourceObject *nextResource; // in linked list of all resources
|
||||
ResourceObject *prevResource;
|
||||
public:
|
||||
enum
|
||||
{
|
||||
VolumeBlock = 1 << 0,
|
||||
File = 1 << 1,
|
||||
Added = 1 << 2,
|
||||
};
|
||||
S32 flags;
|
||||
|
||||
StringTableEntry path; // resource path
|
||||
StringTableEntry name; // resource name
|
||||
|
||||
StringTableEntry filePath; // path/name of file of volume if in volume
|
||||
StringTableEntry fileName;
|
||||
|
||||
S32 fileOffset; // offset on disk in fileName file of resource
|
||||
S32 fileSize; // size on disk of resource block
|
||||
S32 compressedFileSize; // Actual size of resource data.
|
||||
|
||||
ResourceInstance *mInstance; // ptr ot actual object instance
|
||||
S32 lockCount;
|
||||
U32 crc;
|
||||
|
||||
ResourceObject();
|
||||
~ResourceObject() { unlink(); }
|
||||
|
||||
void destruct();
|
||||
|
||||
ResourceObject* getNext() const { return next; }
|
||||
void unlink();
|
||||
void linkAfter(ResourceObject* res);
|
||||
void getFileTimes(FileTime *createTime, FileTime *modifyTime);
|
||||
};
|
||||
|
||||
|
||||
inline void ResourceObject::unlink()
|
||||
{
|
||||
if (next)
|
||||
next->prev = prev;
|
||||
if (prev)
|
||||
prev->next = next;
|
||||
next = prev = 0;
|
||||
}
|
||||
|
||||
inline void ResourceObject::linkAfter(ResourceObject* res)
|
||||
{
|
||||
unlink();
|
||||
prev = res;
|
||||
if ((next = res->next) != 0)
|
||||
next->prev = this;
|
||||
res->next = this;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T> class Resource
|
||||
{
|
||||
private:
|
||||
ResourceObject *obj;
|
||||
// ***WARNING***
|
||||
// Using a faster lock that bypasses the resource manger.
|
||||
// void _lock() { if (obj) obj->rm->lockResource( obj ); }
|
||||
void _lock();
|
||||
void _unlock();
|
||||
|
||||
public:
|
||||
// If assigned a ResourceObject, it's assumed to already have
|
||||
// been locked, lock count is incremented only for copies or
|
||||
// assignment from another Resource.
|
||||
Resource() : obj(NULL) { ; }
|
||||
Resource(ResourceObject *p) : obj(p) { ; }
|
||||
Resource(const Resource &res) : obj(res.obj) { _lock(); }
|
||||
~Resource() { unlock(); }
|
||||
|
||||
const char *getFilePath() const { return (obj ? obj->path : NULL); }
|
||||
const char *getFileName() const { return (obj ? obj->name : NULL); }
|
||||
|
||||
Resource& operator= (ResourceObject *p) { _unlock(); obj = p; return *this; }
|
||||
Resource& operator= (const Resource &r) { _unlock(); obj = r.obj; _lock(); return *this; }
|
||||
|
||||
U32 getCRC() { return (obj ? obj->crc : 0); }
|
||||
operator bool() const { return ((obj != NULL) && (obj->mInstance != NULL)); }
|
||||
T* operator->() { return (T*)obj->mInstance; }
|
||||
T& operator*() { return *((T*)obj->mInstance); }
|
||||
operator T*() { return (obj) ? (T*)obj->mInstance : (T*)NULL; }
|
||||
const T* operator->() const { return (const T*)obj->mInstance; }
|
||||
const T& operator*() const { return *((const T*)obj->mInstance); }
|
||||
operator const T*() const { return (obj) ? (const T*)obj->mInstance : (const T*)NULL; }
|
||||
void unlock();
|
||||
void purge();
|
||||
};
|
||||
|
||||
template<class T> inline void Resource<T>::unlock()
|
||||
{
|
||||
if (obj) {
|
||||
ResourceManager->unlock( obj );
|
||||
obj=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T> inline void Resource<T>::purge()
|
||||
{
|
||||
if (obj) {
|
||||
ResourceManager->unlock( obj );
|
||||
if (obj->lockCount == 0)
|
||||
ResourceManager->purge(obj);
|
||||
obj = NULL;
|
||||
}
|
||||
}
|
||||
template <class T> inline void Resource<T>::_lock()
|
||||
{
|
||||
if (obj)
|
||||
obj->lockCount++;
|
||||
}
|
||||
|
||||
template <class T> inline void Resource<T>::_unlock()
|
||||
{
|
||||
if (obj)
|
||||
ResourceManager->unlock( obj );
|
||||
}
|
||||
|
||||
#define INVALID_ID ((U32)(~0))
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Map of Names and Object IDs to objects
|
||||
// Provides fast lookup for name->object, id->object and
|
||||
// for fast removal of an object given object*
|
||||
//
|
||||
class ResDictionary
|
||||
{
|
||||
enum { DefaultTableSize = 1029 };
|
||||
|
||||
ResourceObject **hashTable;
|
||||
S32 entryCount;
|
||||
S32 hashTableSize;
|
||||
DataChunker memPool;
|
||||
S32 hash(StringTableEntry path, StringTableEntry name);
|
||||
S32 hash(ResourceObject *obj) { return hash(obj->path, obj->name); }
|
||||
public:
|
||||
ResDictionary();
|
||||
~ResDictionary();
|
||||
|
||||
void insert(ResourceObject *obj, StringTableEntry path, StringTableEntry file);
|
||||
ResourceObject* find(StringTableEntry path, StringTableEntry file);
|
||||
ResourceObject* find(StringTableEntry path, StringTableEntry file, StringTableEntry filePath, StringTableEntry fileName);
|
||||
ResourceObject* find(StringTableEntry path, StringTableEntry file, U32 flags);
|
||||
void pushBehind(ResourceObject *obj, S32 mask);
|
||||
void remove(ResourceObject *obj);
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
class ResManager
|
||||
{
|
||||
private:
|
||||
char writeablePath[1024];
|
||||
char primaryPath[1024];
|
||||
char* pathList;
|
||||
|
||||
ResourceObject timeoutList;
|
||||
ResourceObject resourceList;
|
||||
|
||||
ResDictionary dictionary;
|
||||
bool echoFileNames;
|
||||
|
||||
bool scanZip(ResourceObject *zipObject);
|
||||
|
||||
ResourceObject* createResource(StringTableEntry path, StringTableEntry file, StringTableEntry filePath, StringTableEntry fileName);
|
||||
void freeResource(ResourceObject *resObject);
|
||||
void searchPath(const char *pathStart);
|
||||
|
||||
struct RegisteredExtension
|
||||
{
|
||||
StringTableEntry mExtension;
|
||||
RESOURCE_CREATE_FN mCreateFn;
|
||||
RegisteredExtension *next;
|
||||
};
|
||||
|
||||
RegisteredExtension *registeredList;
|
||||
|
||||
ResManager();
|
||||
public:
|
||||
RESOURCE_CREATE_FN getCreateFunction( const char *name );
|
||||
|
||||
~ResManager();
|
||||
static void create();
|
||||
static void destroy();
|
||||
|
||||
void setFileNameEcho(bool on);
|
||||
void setModPaths(U32 numPaths, const char **dirs);
|
||||
const char* getModPaths();
|
||||
|
||||
void registerExtension(const char *extension, RESOURCE_CREATE_FN create_fn);
|
||||
|
||||
S32 getSize(const char* filename);
|
||||
const char* getFullPath(const char * filename, char * path, U32 pathLen);
|
||||
const char* getModPathOf(const char* fileName);
|
||||
const char* getPathOf(const char * filename);
|
||||
const char* getBasePath();
|
||||
|
||||
ResourceObject* load(const char * fileName, bool computeCRC = false);
|
||||
Stream* openStream(const char * fileName);
|
||||
Stream* openStream(ResourceObject *object);
|
||||
void closeStream(Stream *stream);
|
||||
|
||||
void unlock( ResourceObject* );
|
||||
bool add(const char* name, ResourceInstance *addInstance, bool extraLock = false);
|
||||
|
||||
ResourceObject* find(const char * fileName);
|
||||
ResourceInstance* loadInstance(const char *fileName, bool computeCRC = false);
|
||||
ResourceInstance* loadInstance(ResourceObject *object, bool computeCRC = false);
|
||||
|
||||
ResourceObject* find(const char * fileName, U32 flags);
|
||||
ResourceObject* findMatch(const char *expression, const char **fn, ResourceObject *start = NULL);
|
||||
|
||||
void purge();
|
||||
void purge( ResourceObject *obj );
|
||||
void serialize(VectorPtr<const char *> &filenames);
|
||||
|
||||
S32 findMatches( FindMatch *pFM );
|
||||
bool findFile( const char *name );
|
||||
|
||||
bool getCrc(const char * fileName, U32 & crcVal, const U32 crcInitialVal = 0xffffffff );
|
||||
|
||||
void setWriteablePath(const char *path);
|
||||
bool isValidWriteFileName(const char *fn);
|
||||
|
||||
bool openFileForWrite(FileStream &fs, const char *modPath, const char *fileName, U32 accessMode = 1);
|
||||
|
||||
#ifdef DEBUG
|
||||
void dumpLoadedResources();
|
||||
#endif
|
||||
};
|
||||
|
||||
// simple crc - may need to move somewhere else someday
|
||||
// will generate the table on the first call
|
||||
U32 calculateCRC(void * buffer, S32 len, U32 crcVal = 0xffffffff);
|
||||
U32 calculateCRCStream(Stream *stream, U32 crcVal = 0xffffffff);
|
||||
|
||||
#endif //_RESMANAGER_H_
|
||||
141
core/resizeStream.cc
Normal file
141
core/resizeStream.cc
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "Core/resizeStream.h"
|
||||
|
||||
ResizeFilterStream::ResizeFilterStream()
|
||||
: m_pStream(NULL),
|
||||
m_startOffset(0),
|
||||
m_streamLen(0),
|
||||
m_currOffset(0)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
ResizeFilterStream::~ResizeFilterStream()
|
||||
{
|
||||
detachStream();
|
||||
}
|
||||
|
||||
bool ResizeFilterStream::attachStream(Stream* io_pSlaveStream)
|
||||
{
|
||||
AssertFatal(io_pSlaveStream != NULL, "NULL Slave stream?");
|
||||
|
||||
m_pStream = io_pSlaveStream;
|
||||
m_startOffset = 0;
|
||||
m_streamLen = m_pStream->getStreamSize();
|
||||
m_currOffset = 0;
|
||||
setStatus(EOS);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ResizeFilterStream::detachStream()
|
||||
{
|
||||
m_pStream = NULL;
|
||||
m_startOffset = 0;
|
||||
m_streamLen = 0;
|
||||
m_currOffset = 0;
|
||||
setStatus(Closed);
|
||||
}
|
||||
|
||||
Stream* ResizeFilterStream::getStream()
|
||||
{
|
||||
return m_pStream;
|
||||
}
|
||||
|
||||
bool ResizeFilterStream::setStreamOffset(const U32 in_startOffset, const U32 in_streamLen)
|
||||
{
|
||||
AssertFatal(m_pStream != NULL, "stream not attached!");
|
||||
if (m_pStream == NULL)
|
||||
return false;
|
||||
|
||||
U32 start = in_startOffset;
|
||||
U32 end = in_startOffset + in_streamLen;
|
||||
U32 actual = m_pStream->getStreamSize();
|
||||
|
||||
if (start >= actual || end > actual)
|
||||
return false;
|
||||
|
||||
m_startOffset = start;
|
||||
m_streamLen = in_streamLen;
|
||||
m_currOffset = 0;
|
||||
|
||||
if (m_streamLen != 0)
|
||||
setStatus(Ok);
|
||||
else
|
||||
setStatus(EOS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
U32 ResizeFilterStream::getPosition() const
|
||||
{
|
||||
AssertFatal(m_pStream != NULL, "Error, stream not attached");
|
||||
if (m_pStream == NULL)
|
||||
return 0;
|
||||
|
||||
return m_currOffset;
|
||||
}
|
||||
|
||||
bool ResizeFilterStream::setPosition(const U32 in_newPosition)
|
||||
{
|
||||
AssertFatal(m_pStream != NULL, "Error, stream not attached");
|
||||
if (m_pStream == NULL)
|
||||
return false;
|
||||
|
||||
if (in_newPosition < m_streamLen) {
|
||||
m_currOffset = in_newPosition;
|
||||
return true;
|
||||
} else {
|
||||
m_currOffset = m_streamLen;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
U32 ResizeFilterStream::getStreamSize()
|
||||
{
|
||||
AssertFatal(m_pStream != NULL, "Error, stream not attached");
|
||||
|
||||
return m_streamLen;
|
||||
}
|
||||
|
||||
bool ResizeFilterStream::_read(const U32 in_numBytes, void* out_pBuffer)
|
||||
{
|
||||
AssertFatal(m_pStream != NULL, "Error, stream not attached");
|
||||
|
||||
if (in_numBytes == 0)
|
||||
return true;
|
||||
|
||||
AssertFatal(out_pBuffer != NULL, "Invalid output buffer");
|
||||
if (getStatus() == Closed) {
|
||||
AssertFatal(false, "Attempted read from closed stream");
|
||||
return false;
|
||||
}
|
||||
|
||||
U32 savePosition = m_pStream->getPosition();
|
||||
if (m_pStream->setPosition(m_startOffset + m_currOffset) == false)
|
||||
return false;
|
||||
|
||||
U32 actualSize = in_numBytes;
|
||||
U32 position = m_startOffset + m_currOffset;
|
||||
if (in_numBytes + position > m_startOffset + m_streamLen)
|
||||
actualSize = m_streamLen - (position - m_startOffset);
|
||||
|
||||
if (actualSize == 0) {
|
||||
setStatus(EOS);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = m_pStream->read(actualSize, out_pBuffer);
|
||||
m_currOffset += actualSize;
|
||||
|
||||
setStatus(m_pStream->getStatus());
|
||||
|
||||
m_pStream->setPosition(savePosition);
|
||||
return success;
|
||||
}
|
||||
|
||||
49
core/resizeStream.h
Normal file
49
core/resizeStream.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _RESIZESTREAM_H_
|
||||
#define _RESIZESTREAM_H_
|
||||
|
||||
//Includes
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "Platform/platform.h"
|
||||
#endif
|
||||
#ifndef _FILTERSTREAM_H_
|
||||
#include "Core/filterStream.h"
|
||||
#endif
|
||||
|
||||
class ResizeFilterStream : public FilterStream
|
||||
{
|
||||
typedef FilterStream Parent;
|
||||
|
||||
Stream* m_pStream;
|
||||
U32 m_startOffset;
|
||||
U32 m_streamLen;
|
||||
U32 m_currOffset;
|
||||
|
||||
public:
|
||||
ResizeFilterStream();
|
||||
~ResizeFilterStream();
|
||||
|
||||
bool attachStream(Stream* io_pSlaveStream);
|
||||
void detachStream();
|
||||
Stream* getStream();
|
||||
|
||||
bool setStreamOffset(const U32 in_startOffset,
|
||||
const U32 in_streamLen);
|
||||
|
||||
// Mandatory overrides.
|
||||
protected:
|
||||
bool _read(const U32 in_numBytes, void* out_pBuffer);
|
||||
public:
|
||||
U32 getPosition() const;
|
||||
bool setPosition(const U32 in_newPosition);
|
||||
|
||||
U32 getStreamSize();
|
||||
};
|
||||
|
||||
#endif //_RESIZESTREAM_H_
|
||||
154
core/stream.h
Normal file
154
core/stream.h
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _STREAM_H_
|
||||
#define _STREAM_H_
|
||||
|
||||
//Includes
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "Platform/platform.h"
|
||||
#endif
|
||||
|
||||
|
||||
// This should ideally be done with templates...
|
||||
//
|
||||
#define DECLARE_OVERLOADED_READ(type) \
|
||||
bool read(type* out_read) { \
|
||||
return read(sizeof(type), out_read); \
|
||||
}
|
||||
#define DECLARE_OVERLOADED_WRITE(type) \
|
||||
bool write(type in_write) { \
|
||||
return write(sizeof(type), &in_write); \
|
||||
}
|
||||
|
||||
#define DECLARE_ENDIAN_OVERLOADED_READ(type) \
|
||||
bool read(type* out_read) { \
|
||||
type temp; \
|
||||
bool success = read(sizeof(type), &temp); \
|
||||
*out_read = convertLEndianToHost(temp); \
|
||||
return success; \
|
||||
}
|
||||
#define DECLARE_ENDIAN_OVERLOADED_WRITE(type) \
|
||||
bool write(type in_write) { \
|
||||
type temp = convertHostToLEndian(in_write); \
|
||||
return write(sizeof(type), &temp); \
|
||||
}
|
||||
|
||||
|
||||
class ColorI;
|
||||
class ColorF;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//-------------------------------------- Base Stream class
|
||||
//
|
||||
class Stream {
|
||||
// Public structs and enumerations...
|
||||
public:
|
||||
enum Status {
|
||||
Ok = 0, // obv.
|
||||
IOError, // Read or Write error
|
||||
EOS, // End of Stream reached (mostly for reads)
|
||||
IllegalCall, // An unsupported operation used. Always w/ accompanied by AssertWarn
|
||||
Closed, // Tried to operate on a closed stream (or detached filter)
|
||||
UnknownError // Catchall
|
||||
};
|
||||
|
||||
enum Capability {
|
||||
StreamWrite = U32(1 << 0),
|
||||
StreamRead = U32(1 << 1),
|
||||
StreamPosition = U32(1 << 2)
|
||||
};
|
||||
|
||||
// Accessible only through inline accessors
|
||||
private:
|
||||
Status m_streamStatus;
|
||||
|
||||
// Derived accessible data modifiers...
|
||||
protected:
|
||||
void setStatus(const Status in_newStatus) { m_streamStatus = in_newStatus; }
|
||||
|
||||
public:
|
||||
Stream();
|
||||
virtual ~Stream();
|
||||
|
||||
Status getStatus() const { return m_streamStatus; }
|
||||
static const char* getStatusString(const Status in_status);
|
||||
|
||||
// Derived classes must override these...
|
||||
protected:
|
||||
virtual bool _read(const U32 in_numBytes, void* out_pBuffer) = 0;
|
||||
virtual bool _write(const U32 in_numBytes, const void* in_pBuffer) = 0;
|
||||
public:
|
||||
virtual bool hasCapability(const Capability) const = 0;
|
||||
|
||||
virtual U32 getPosition() const = 0;
|
||||
virtual bool setPosition(const U32 in_newPosition) = 0;
|
||||
virtual U32 getStreamSize() = 0;
|
||||
|
||||
void readLine(U8 *buffer, U32 bufferSize);
|
||||
void writeLine(U8 *buffer);
|
||||
|
||||
const char *readSTString(bool casesens = false);
|
||||
virtual void readString(char stringBuf[256]);
|
||||
void readLongString(U32 maxStringLen, char *stringBuf);
|
||||
void writeLongString(U32 maxStringLen, const char *string);
|
||||
|
||||
virtual void writeString(const char *stringBuf, S32 maxLen=255);
|
||||
|
||||
bool write(const ColorI&);
|
||||
bool write(const ColorF&);
|
||||
bool read(ColorI*);
|
||||
bool read(ColorF*);
|
||||
|
||||
|
||||
// Overloaded write and read ops..
|
||||
public:
|
||||
bool read(const U32 in_numBytes, void* out_pBuffer) {
|
||||
return _read(in_numBytes, out_pBuffer);
|
||||
}
|
||||
bool write(const U32 in_numBytes, const void* in_pBuffer) {
|
||||
return _write(in_numBytes, in_pBuffer);
|
||||
}
|
||||
DECLARE_OVERLOADED_WRITE(S8)
|
||||
DECLARE_OVERLOADED_WRITE(U8)
|
||||
DECLARE_OVERLOADED_WRITE(F64)
|
||||
|
||||
DECLARE_ENDIAN_OVERLOADED_WRITE(S16)
|
||||
DECLARE_ENDIAN_OVERLOADED_WRITE(S32)
|
||||
DECLARE_ENDIAN_OVERLOADED_WRITE(U16)
|
||||
DECLARE_ENDIAN_OVERLOADED_WRITE(U32)
|
||||
DECLARE_ENDIAN_OVERLOADED_WRITE(F32)
|
||||
|
||||
DECLARE_OVERLOADED_READ(S8)
|
||||
DECLARE_OVERLOADED_READ(U8)
|
||||
DECLARE_OVERLOADED_READ(F64)
|
||||
|
||||
DECLARE_ENDIAN_OVERLOADED_READ(S16)
|
||||
DECLARE_ENDIAN_OVERLOADED_READ(S32)
|
||||
DECLARE_ENDIAN_OVERLOADED_READ(U16)
|
||||
DECLARE_ENDIAN_OVERLOADED_READ(U32)
|
||||
DECLARE_ENDIAN_OVERLOADED_READ(F32)
|
||||
|
||||
// We have to do the bool's by hand, since they are different sizes
|
||||
// on different compilers...
|
||||
//
|
||||
bool read(bool* out_pRead) {
|
||||
U8 translate;
|
||||
bool success = read(&translate);
|
||||
if (success == false)
|
||||
return false;
|
||||
|
||||
*out_pRead = translate != 0;
|
||||
return true;
|
||||
}
|
||||
bool write(const bool& in_rWrite) {
|
||||
U8 translate = in_rWrite ? U8(1) : U8(0);
|
||||
return write(translate);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //_STREAM_H_
|
||||
206
core/stringTable.cc
Normal file
206
core/stringTable.cc
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "Platform/platform.h"
|
||||
#include "Core/stringTable.h"
|
||||
|
||||
_StringTable *StringTable = NULL;
|
||||
const U32 _StringTable::csm_stInitSize = 29;
|
||||
|
||||
//---------------------------------------------------------------
|
||||
//
|
||||
// StringTable functions
|
||||
//
|
||||
//---------------------------------------------------------------
|
||||
|
||||
namespace {
|
||||
bool sgInitTable = true;
|
||||
U8 sgHashTable[256];
|
||||
|
||||
void initTolowerTable()
|
||||
{
|
||||
for (U32 i = 0; i < 256; i++) {
|
||||
U8 c = dTolower(i);
|
||||
sgHashTable[i] = c * c;
|
||||
}
|
||||
|
||||
sgInitTable = false;
|
||||
}
|
||||
|
||||
} // namespace {}
|
||||
|
||||
U32 _StringTable::hashString(const char* str)
|
||||
{
|
||||
if (sgInitTable)
|
||||
initTolowerTable();
|
||||
|
||||
U32 ret = 0;
|
||||
char c;
|
||||
while((c = *str++) != 0) {
|
||||
ret <<= 1;
|
||||
ret ^= sgHashTable[c];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
U32 _StringTable::hashStringn(const char* str, S32 len)
|
||||
{
|
||||
if (sgInitTable)
|
||||
initTolowerTable();
|
||||
|
||||
U32 ret = 0;
|
||||
char c;
|
||||
while((c = *str++) != 0 && len--) {
|
||||
ret <<= 1;
|
||||
ret ^= sgHashTable[c];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
_StringTable::_StringTable()
|
||||
{
|
||||
buckets = (Node **) dMalloc(csm_stInitSize * sizeof(Node *));
|
||||
for(U32 i = 0; i < csm_stInitSize; i++) {
|
||||
buckets[i] = 0;
|
||||
}
|
||||
|
||||
numBuckets = csm_stInitSize;
|
||||
itemCount = 0;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
_StringTable::~_StringTable()
|
||||
{
|
||||
dFree(buckets);
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
void _StringTable::create()
|
||||
{
|
||||
AssertFatal(StringTable == NULL, "StringTable::create: StringTable all ready exists.");
|
||||
StringTable = new _StringTable;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
void _StringTable::destroy()
|
||||
{
|
||||
AssertFatal(StringTable != NULL, "StringTable::destroy: StringTable does not exist.");
|
||||
delete StringTable;
|
||||
StringTable = NULL;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
StringTableEntry _StringTable::insert(const char* val, const bool caseSens)
|
||||
{
|
||||
Node **walk, *temp;
|
||||
U32 key = hashString(val);
|
||||
walk = &buckets[key % numBuckets];
|
||||
while((temp = *walk) != NULL) {
|
||||
if(caseSens && !dStrcmp(temp->val, val))
|
||||
return temp->val;
|
||||
else if(!caseSens && !dStricmp(temp->val, val))
|
||||
return temp->val;
|
||||
walk = &(temp->next);
|
||||
}
|
||||
char *ret = 0;
|
||||
if(!*walk) {
|
||||
*walk = (Node *) mempool.alloc(sizeof(Node));
|
||||
(*walk)->next = 0;
|
||||
(*walk)->val = (char *) mempool.alloc(dStrlen(val) + 1);
|
||||
dStrcpy((*walk)->val, val);
|
||||
ret = (*walk)->val;
|
||||
itemCount ++;
|
||||
}
|
||||
if(itemCount > 2 * numBuckets) {
|
||||
resize(4 * numBuckets - 1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
StringTableEntry _StringTable::insertn(const char* src, S32 len, const bool caseSens)
|
||||
{
|
||||
char val[256];
|
||||
AssertFatal(len < 255, "Invalid string to insertn");
|
||||
dStrncpy(val, src, len);
|
||||
val[len] = 0;
|
||||
return insert(val, caseSens);
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
StringTableEntry _StringTable::lookup(const char* val, const bool caseSens)
|
||||
{
|
||||
Node **walk, *temp;
|
||||
U32 key = hashString(val);
|
||||
walk = &buckets[key % numBuckets];
|
||||
while((temp = *walk) != NULL) {
|
||||
if(caseSens && !dStrcmp(temp->val, val))
|
||||
return temp->val;
|
||||
else if(!caseSens && !dStricmp(temp->val, val))
|
||||
return temp->val;
|
||||
walk = &(temp->next);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
StringTableEntry _StringTable::lookupn(const char* val, S32 len, const bool caseSens)
|
||||
{
|
||||
Node **walk, *temp;
|
||||
U32 key = hashStringn(val, len);
|
||||
walk = &buckets[key % numBuckets];
|
||||
while((temp = *walk) != NULL) {
|
||||
if(caseSens && !dStrncmp(temp->val, val, len))
|
||||
return temp->val;
|
||||
else if(!caseSens && !dStrnicmp(temp->val, val, len))
|
||||
return temp->val;
|
||||
walk = &(temp->next);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
void _StringTable::resize(const U32 newSize)
|
||||
{
|
||||
Node *head = NULL, *walk, *temp;
|
||||
U32 i;
|
||||
// reverse individual bucket lists
|
||||
// we do this because new strings are added at the end of bucket
|
||||
// lists so that case sens strings are always after their
|
||||
// corresponding case insens strings
|
||||
|
||||
for(i = 0; i < numBuckets; i++) {
|
||||
walk = buckets[i];
|
||||
while(walk)
|
||||
{
|
||||
temp = walk->next;
|
||||
walk->next = head;
|
||||
head = walk;
|
||||
walk = temp;
|
||||
}
|
||||
}
|
||||
buckets = (Node **) dRealloc(buckets, newSize * sizeof(Node));
|
||||
for(i = 0; i < newSize; i++) {
|
||||
buckets[i] = 0;
|
||||
}
|
||||
numBuckets = newSize;
|
||||
walk = head;
|
||||
while(walk) {
|
||||
U32 key;
|
||||
Node *temp = walk;
|
||||
|
||||
walk = walk->next;
|
||||
key = hashString(temp->val);
|
||||
temp->next = buckets[key % newSize];
|
||||
buckets[key % newSize] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
60
core/stringTable.h
Normal file
60
core/stringTable.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _STRINGTABLE_H_
|
||||
#define _STRINGTABLE_H_
|
||||
|
||||
//Includes
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "Platform/platform.h"
|
||||
#endif
|
||||
#ifndef _DATACHUNKER_H_
|
||||
#include "Core/dataChunker.h"
|
||||
#endif
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
class _StringTable
|
||||
{
|
||||
private:
|
||||
struct Node
|
||||
{
|
||||
char *val;
|
||||
Node *next;
|
||||
};
|
||||
|
||||
Node** buckets;
|
||||
U32 numBuckets;
|
||||
U32 itemCount;
|
||||
DataChunker mempool;
|
||||
|
||||
protected:
|
||||
static const U32 csm_stInitSize;
|
||||
|
||||
_StringTable();
|
||||
~_StringTable();
|
||||
|
||||
public:
|
||||
static void create();
|
||||
static void destroy();
|
||||
|
||||
StringTableEntry insert(const char *string, bool caseSens = false);
|
||||
StringTableEntry insertn(const char *string, S32 len, bool caseSens = false);
|
||||
StringTableEntry lookup(const char *string, bool caseSens = false);
|
||||
StringTableEntry lookupn(const char *string, S32 len, bool caseSens = false);
|
||||
void resize(const U32 newSize);
|
||||
|
||||
static U32 hashString(const char* in_pString);
|
||||
static U32 hashStringn(const char* in_pString, S32 len);
|
||||
};
|
||||
|
||||
|
||||
extern _StringTable *StringTable;
|
||||
|
||||
|
||||
#endif //_STRINGTABLE_H_
|
||||
|
||||
21
core/tAlgorithm.h
Normal file
21
core/tAlgorithm.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TALGORITHM_H_
|
||||
#define _TALGORITHM_H_
|
||||
|
||||
//Includes
|
||||
|
||||
template <class Iterator, class Value>
|
||||
Iterator find(Iterator first, Iterator last, Value value)
|
||||
{
|
||||
while (first != last && *first != value)
|
||||
++first;
|
||||
return first;
|
||||
}
|
||||
|
||||
#endif //_TALGORITHM_H_
|
||||
136
core/tSortedSceneObjectList.cc
Normal file
136
core/tSortedSceneObjectList.cc
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "core/tSortedSceneObjectList.h"
|
||||
|
||||
/**
|
||||
* Constructor, initalize pointers to null, counter to 0
|
||||
*/
|
||||
SortedSceneObjectList::SortedSceneObjectList() {
|
||||
mHead = NULL;
|
||||
}
|
||||
|
||||
SortedSceneObjectList::~SortedSceneObjectList() {
|
||||
|
||||
while( mHead != NULL ) {
|
||||
SceneObjectNode *temp = mHead;
|
||||
mHead = mHead->next;
|
||||
|
||||
delete temp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an object, no duplicates, adds it in order
|
||||
*/
|
||||
void SortedSceneObjectList::addObject( SceneObject *toAdd, Point3F &tag ) {
|
||||
|
||||
if( contains( toAdd ) ) {
|
||||
// Just update the tag
|
||||
SceneObjectNode *i = mHead;
|
||||
|
||||
while( i != NULL ) {
|
||||
if( i->object->getId() == toAdd->getId() ) {
|
||||
i->tag = tag;
|
||||
return;
|
||||
}
|
||||
|
||||
i = i->next;
|
||||
}
|
||||
}
|
||||
|
||||
SceneObjectNode *previous = mHead;
|
||||
SceneObjectNode *current = mHead;
|
||||
|
||||
while( current != NULL && toAdd->getId() > current->object->getId() ) {
|
||||
previous = current;
|
||||
current = current -> next;
|
||||
}
|
||||
|
||||
if( previous == current )
|
||||
mHead = new SceneObjectNode( toAdd, tag, mHead );
|
||||
else
|
||||
previous->next = new SceneObjectNode( toAdd, tag, current );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if this list contains the test object
|
||||
*/
|
||||
bool SortedSceneObjectList::contains( const SceneObject *test ) const {
|
||||
|
||||
SceneObjectNode *i = mHead;
|
||||
|
||||
while( i != NULL ) {
|
||||
if( i->object->getId() == test->getId() )
|
||||
return true;
|
||||
|
||||
i = i->next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an object from the list
|
||||
*/
|
||||
void SortedSceneObjectList::removeObject( const SceneObject *toRemove ) {
|
||||
|
||||
SceneObjectNode *previous = mHead;
|
||||
SceneObjectNode *current = mHead;
|
||||
|
||||
while( current != NULL && toRemove->getId() > current->object->getId() ) {
|
||||
previous = current;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
if( current == NULL )
|
||||
return;
|
||||
|
||||
if( previous == current)
|
||||
mHead = mHead->next;
|
||||
else
|
||||
previous->next = current->next;
|
||||
|
||||
delete current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tag on an object
|
||||
*/
|
||||
Point3F SortedSceneObjectList::getTag( const SceneObject *test ) const {
|
||||
|
||||
SceneObjectNode *current = mHead;
|
||||
SceneObjectNode *previous = mHead;
|
||||
|
||||
while( test != NULL && current != NULL && test->getId() > current->object->getId() ) {
|
||||
previous = current;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
if( current == NULL )
|
||||
return Point3F( 42.42f, 42.42f, 42.42f ); // Eh, it's easy to test for
|
||||
else
|
||||
return current->tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this as a vector
|
||||
*/
|
||||
Vector<SceneObject *> SortedSceneObjectList::toVector() const {
|
||||
|
||||
Vector<SceneObject *> retVector;
|
||||
|
||||
SceneObjectNode *i = mHead;
|
||||
|
||||
while( i != NULL ) {
|
||||
retVector.push_back( i->object );
|
||||
|
||||
i = i->next;
|
||||
}
|
||||
|
||||
return retVector;
|
||||
}
|
||||
53
core/tSortedSceneObjectList.h
Normal file
53
core/tSortedSceneObjectList.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSORTEDSCENEOBJECTLIST_H_
|
||||
#define _TSORTEDSCENEOBJECTLIST_H_
|
||||
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/tVector.h"
|
||||
#endif
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "sim/sceneObject.h"
|
||||
#endif
|
||||
|
||||
class SortedSceneObjectList {
|
||||
|
||||
private:
|
||||
|
||||
class SceneObjectNode {
|
||||
public:
|
||||
SceneObjectNode *next;
|
||||
|
||||
SceneObject *object;
|
||||
Point3F tag;
|
||||
|
||||
SceneObjectNode( SceneObject *obj, Point3F &tg, SceneObjectNode *nxt ) {
|
||||
object = obj;
|
||||
next = nxt;
|
||||
tag = tg;
|
||||
}
|
||||
};
|
||||
|
||||
SceneObjectNode *mHead;
|
||||
|
||||
public:
|
||||
|
||||
SortedSceneObjectList();
|
||||
~SortedSceneObjectList();
|
||||
|
||||
void addObject( SceneObject *toAdd, Point3F &tag );
|
||||
void removeObject( const SceneObject *toRemove );
|
||||
bool contains( const SceneObject *test ) const;
|
||||
|
||||
Point3F getTag( const SceneObject *test ) const;
|
||||
|
||||
Vector<SceneObject *> toVector() const;
|
||||
};
|
||||
|
||||
#endif
|
||||
135
core/tSparseArray.h
Normal file
135
core/tSparseArray.h
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSPARSEARRAY_H_
|
||||
#define _TSPARSEARRAY_H_
|
||||
|
||||
//Includes
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "Platform/platform.h"
|
||||
#endif
|
||||
#ifndef _PLATFORMASSERT_H_
|
||||
#include "Platform/platformAssert.h"
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
class SparseArray
|
||||
{
|
||||
protected:
|
||||
struct Node {
|
||||
T* pObject;
|
||||
U32 key;
|
||||
|
||||
Node* next;
|
||||
};
|
||||
|
||||
protected:
|
||||
U32 mModulus;
|
||||
Node* mSentryTables;
|
||||
|
||||
void clearTables(); // Note: _deletes_ the objects!
|
||||
|
||||
public:
|
||||
SparseArray(const U32 modulusSize = 64);
|
||||
~SparseArray();
|
||||
|
||||
void insert(T* pObject, U32 key);
|
||||
T* remove(U32 key);
|
||||
T* retreive(U32 key);
|
||||
};
|
||||
|
||||
template <class T>
|
||||
inline SparseArray<T>::SparseArray(const U32 modulusSize)
|
||||
{
|
||||
AssertFatal(modulusSize > 0, "Error, modulus must be > 0");
|
||||
|
||||
mModulus = modulusSize;
|
||||
mSentryTables = new Node[mModulus];
|
||||
for (U32 i = 0; i < mModulus; i++)
|
||||
mSentryTables[i].next = NULL;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline SparseArray<T>::~SparseArray()
|
||||
{
|
||||
clearTables();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void SparseArray<T>::clearTables()
|
||||
{
|
||||
for (U32 i = 0; i < mModulus; i++) {
|
||||
Node* pProbe = mSentryTables[i].next;
|
||||
while (pProbe != NULL) {
|
||||
Node* pNext = pProbe->next;
|
||||
delete pProbe->pObject;
|
||||
delete pProbe;
|
||||
pProbe = pNext;
|
||||
}
|
||||
}
|
||||
delete [] mSentryTables;
|
||||
mSentryTables = NULL;
|
||||
mModulus = 0;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void SparseArray<T>::insert(T* pObject, U32 key)
|
||||
{
|
||||
U32 insert = key % mModulus;
|
||||
Node* pNew = new Node;
|
||||
pNew->pObject = pObject;
|
||||
pNew->key = key;
|
||||
pNew->next = mSentryTables[insert].next;
|
||||
mSentryTables[insert].next = pNew;
|
||||
|
||||
#ifdef DEBUG
|
||||
Node* probe = pNew->next;
|
||||
while (probe != NULL) {
|
||||
AssertFatal(probe->key != key, "error, duplicate keys in sparse array!");
|
||||
probe = probe->next;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T* SparseArray<T>::remove(U32 key)
|
||||
{
|
||||
U32 remove = key % mModulus;
|
||||
Node* probe = mSentryTables[remove];
|
||||
while (probe->next != NULL) {
|
||||
if (probe->next->key == key) {
|
||||
Node* remove = probe->next;
|
||||
T* pReturn = remove->pObject;
|
||||
probe->next = remove->next;
|
||||
delete remove;
|
||||
return pReturn;
|
||||
}
|
||||
probe = probe->next;
|
||||
}
|
||||
|
||||
AssertFatal(false, "Key didn't exist in the array!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T* SparseArray<T>::retreive(U32 key)
|
||||
{
|
||||
U32 retrieve = key % mModulus;
|
||||
Node* probe = mSentryTables[retrieve];
|
||||
while (probe->next != NULL) {
|
||||
if (probe->next->key == key) {
|
||||
return probe->next->pObject;
|
||||
}
|
||||
probe = probe->next;
|
||||
}
|
||||
|
||||
AssertFatal(false, "Key didn't exist in the array!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif //_TSPARSEARRAY_H_
|
||||
|
||||
77
core/tVector.cc
Normal file
77
core/tVector.cc
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "Core/tVector.h"
|
||||
|
||||
#ifdef DEBUG_GUARD
|
||||
bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 elemSize,
|
||||
const char* fileName,
|
||||
const U32 lineNum)
|
||||
{
|
||||
if (newCount > 0) {
|
||||
U32 blocks = newCount / VectorBlockSize;
|
||||
if (newCount % VectorBlockSize)
|
||||
blocks++;
|
||||
S32 mem_size = blocks * VectorBlockSize * elemSize;
|
||||
|
||||
if (*arrayPtr != NULL)
|
||||
{
|
||||
if (fileName == NULL && mem_size <= 64)
|
||||
U32 test = 0;
|
||||
*arrayPtr = dRealloc(*arrayPtr,mem_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fileName == NULL && mem_size <= 64)
|
||||
U32 test = 0;
|
||||
const char* pUseFileName = fileName != NULL ? fileName : __FILE__;
|
||||
U32 useLineNum = fileName != NULL ? lineNum : __LINE__;
|
||||
*arrayPtr = dMalloc_r(mem_size, pUseFileName, useLineNum);
|
||||
}
|
||||
|
||||
*aCount = newCount;
|
||||
*aSize = blocks * VectorBlockSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (*arrayPtr) {
|
||||
dFree(*arrayPtr);
|
||||
*arrayPtr = 0;
|
||||
}
|
||||
|
||||
*aSize = 0;
|
||||
*aCount = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 elemSize)
|
||||
{
|
||||
if (newCount > 0) {
|
||||
U32 blocks = newCount / VectorBlockSize;
|
||||
if (newCount % VectorBlockSize)
|
||||
blocks++;
|
||||
S32 mem_size = blocks * VectorBlockSize * elemSize;
|
||||
*arrayPtr = *arrayPtr ? dRealloc(*arrayPtr,mem_size) :
|
||||
dMalloc(mem_size);
|
||||
|
||||
*aCount = newCount;
|
||||
*aSize = blocks * VectorBlockSize;
|
||||
return true;
|
||||
}
|
||||
if (*arrayPtr) {
|
||||
dFree(*arrayPtr);
|
||||
*arrayPtr = 0;
|
||||
}
|
||||
|
||||
*aSize = 0;
|
||||
*aCount = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
642
core/tVector.h
Normal file
642
core/tVector.h
Normal file
|
|
@ -0,0 +1,642 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TVECTOR_H_
|
||||
#define _TVECTOR_H_
|
||||
|
||||
//Includes
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "Platform/platform.h"
|
||||
#endif
|
||||
|
||||
#if defined(__BORLANDC__) && (__BORLANDC__ >= 0x500)
|
||||
#pragma warn -inl
|
||||
#endif
|
||||
|
||||
#define VectorBlockSize 16
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// A dynamic array class. The vector grows as you insert or append
|
||||
// elements. Insertion is fastest at the end of the array. Resizing
|
||||
// of the array can be avoided by pre-allocating space using the
|
||||
// reserve() method.
|
||||
|
||||
// ***WARNING***
|
||||
// This template does not initialize, construct or destruct any of
|
||||
// it's elements. This means don't use this template for elements
|
||||
// (classes) that need these operations. This template is intended
|
||||
// to be used for simple structures that have no constructors or
|
||||
// destructors.
|
||||
|
||||
#ifdef DEBUG_GUARD
|
||||
extern bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 elemSize,
|
||||
const char* fileName,
|
||||
const U32 lineNum);
|
||||
#else
|
||||
extern bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 elemSize);
|
||||
#endif
|
||||
|
||||
|
||||
// Use the following macro to bind a vector to a particular line
|
||||
// of the owning class for memory tracking purposes
|
||||
#ifdef DEBUG_GUARD
|
||||
#define VECTOR_SET_ASSOCIATION(x) x.setFileAssociation(__FILE__, __LINE__)
|
||||
#else
|
||||
#define VECTOR_SET_ASSOCIATION(x) x
|
||||
#endif
|
||||
|
||||
// =============================================================================
|
||||
template<class T>
|
||||
class Vector
|
||||
{
|
||||
protected:
|
||||
U32 mElementCount;
|
||||
U32 mArraySize;
|
||||
T* mArray;
|
||||
|
||||
#ifdef DEBUG_GUARD
|
||||
const char* mFileAssociation;
|
||||
U32 mLineAssociation;
|
||||
#endif
|
||||
|
||||
bool resize(U32);
|
||||
public:
|
||||
Vector(const U32 initialSize = 0);
|
||||
Vector(const U32 initialSize, const char* fileName, const U32 lineNum);
|
||||
Vector(const char* fileName, const U32 lineNum);
|
||||
Vector(const Vector&);
|
||||
~Vector();
|
||||
|
||||
#ifdef DEBUG_GUARD
|
||||
void setFileAssociation(const char* file, const U32 line);
|
||||
#endif
|
||||
|
||||
// STL interface
|
||||
typedef T value_type;
|
||||
typedef T& reference;
|
||||
typedef const T& const_reference;
|
||||
|
||||
typedef T* iterator;
|
||||
typedef const T* const_iterator;
|
||||
typedef S32 difference_type;
|
||||
typedef U32 size_type;
|
||||
|
||||
Vector<T>& operator=(const Vector<T>& p);
|
||||
|
||||
iterator begin();
|
||||
const_iterator begin() const;
|
||||
iterator end();
|
||||
const_iterator end() const;
|
||||
|
||||
S32 size() const;
|
||||
bool empty() const;
|
||||
|
||||
void insert(iterator, const T&);
|
||||
void erase(iterator);
|
||||
|
||||
T& front();
|
||||
const T& front() const;
|
||||
T& back();
|
||||
const T& back() const;
|
||||
|
||||
void push_front(const T&);
|
||||
void push_back(const T&);
|
||||
void pop_front();
|
||||
void pop_back();
|
||||
|
||||
T& operator[](U32);
|
||||
const T& operator[](U32) const;
|
||||
|
||||
T& operator[](S32 i) { return operator[](U32(i)); }
|
||||
const T& operator[](S32 i ) const { return operator[](U32(i)); }
|
||||
|
||||
void reserve(U32);
|
||||
U32 capacity() const;
|
||||
|
||||
// Extended interface
|
||||
U32 memSize() const;
|
||||
T* address() const;
|
||||
U32 setSize(U32);
|
||||
void increment(U32 = 1);
|
||||
void decrement(U32 = 1);
|
||||
void insert(U32);
|
||||
void erase(U32);
|
||||
void erase_fast(U32);
|
||||
void erase_fast(iterator);
|
||||
void clear();
|
||||
void compact();
|
||||
|
||||
T& first();
|
||||
T& last();
|
||||
const T& first() const;
|
||||
const T& last() const;
|
||||
|
||||
void set(void * addr, S32 sz);
|
||||
|
||||
// BJW 8/20/97
|
||||
// merge another vector into this one
|
||||
void merge(const Vector& p);
|
||||
};
|
||||
|
||||
template<class T> inline Vector<T>::~Vector()
|
||||
{
|
||||
dFree(mArray);
|
||||
}
|
||||
|
||||
template<class T> inline Vector<T>::Vector(const U32 initialSize)
|
||||
{
|
||||
#ifdef DEBUG_GUARD
|
||||
mFileAssociation = NULL;
|
||||
mLineAssociation = 0;
|
||||
#endif
|
||||
|
||||
mArray = 0;
|
||||
mElementCount = 0;
|
||||
mArraySize = 0;
|
||||
if(initialSize)
|
||||
reserve(initialSize);
|
||||
}
|
||||
|
||||
template<class T> inline Vector<T>::Vector(const U32 initialSize,
|
||||
const char* fileName,
|
||||
const U32 lineNum)
|
||||
{
|
||||
#ifdef DEBUG_GUARD
|
||||
mFileAssociation = fileName;
|
||||
mLineAssociation = lineNum;
|
||||
#else
|
||||
fileName;
|
||||
lineNum;
|
||||
#endif
|
||||
|
||||
mArray = 0;
|
||||
mElementCount = 0;
|
||||
mArraySize = 0;
|
||||
if(initialSize)
|
||||
reserve(initialSize);
|
||||
}
|
||||
|
||||
template<class T> inline Vector<T>::Vector(const char* fileName,
|
||||
const U32 lineNum)
|
||||
{
|
||||
#ifdef DEBUG_GUARD
|
||||
mFileAssociation = fileName;
|
||||
mLineAssociation = lineNum;
|
||||
#else
|
||||
fileName;
|
||||
lineNum;
|
||||
#endif
|
||||
|
||||
mArray = 0;
|
||||
mElementCount = 0;
|
||||
mArraySize = 0;
|
||||
}
|
||||
|
||||
template<class T> inline Vector<T>::Vector(const Vector& p)
|
||||
{
|
||||
#ifdef DEBUG_GUARD
|
||||
mFileAssociation = p.mFileAssociation;
|
||||
mLineAssociation = p.mLineAssociation;
|
||||
#endif
|
||||
|
||||
mArray = 0;
|
||||
resize(p.mElementCount);
|
||||
if (p.mElementCount)
|
||||
dMemcpy(mArray,p.mArray,mElementCount * sizeof(value_type));
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG_GUARD
|
||||
template<class T> inline void Vector<T>::setFileAssociation(const char* file,
|
||||
const U32 line)
|
||||
{
|
||||
mFileAssociation = file;
|
||||
mLineAssociation = line;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<class T> inline U32 Vector<T>::memSize() const
|
||||
{
|
||||
return capacity() * sizeof(T);
|
||||
}
|
||||
|
||||
template<class T> inline T* Vector<T>::address() const
|
||||
{
|
||||
return mArray;
|
||||
}
|
||||
|
||||
template<class T> inline U32 Vector<T>::setSize(U32 size)
|
||||
{
|
||||
if (size > mArraySize)
|
||||
resize(size);
|
||||
else
|
||||
mElementCount = size;
|
||||
return mElementCount;
|
||||
}
|
||||
|
||||
template<class T> inline void Vector<T>::increment(U32 delta)
|
||||
{
|
||||
if ((mElementCount += delta) > mArraySize)
|
||||
resize(mElementCount);
|
||||
}
|
||||
|
||||
template<class T> inline void Vector<T>::decrement(U32 delta)
|
||||
{
|
||||
if (mElementCount > delta)
|
||||
mElementCount -= delta;
|
||||
else
|
||||
mElementCount = 0;
|
||||
}
|
||||
|
||||
template<class T> inline void Vector<T>::insert(U32 index)
|
||||
{
|
||||
// Assert: index >= 0 && index < mElementCount
|
||||
increment();
|
||||
dMemmove(&mArray[index + 1],
|
||||
&mArray[index],
|
||||
(mElementCount - index - 1) * sizeof(value_type));
|
||||
}
|
||||
|
||||
template<class T> inline void Vector<T>::erase(U32 index)
|
||||
{
|
||||
// Assert: index >= 0 && index < mElementCount
|
||||
dMemmove(&mArray[index],
|
||||
&mArray[index + 1],
|
||||
(mElementCount - index - 1) * sizeof(value_type));
|
||||
decrement();
|
||||
}
|
||||
|
||||
template<class T> inline void Vector<T>::erase_fast(U32 index)
|
||||
{
|
||||
// CAUTION: this operator does maintain list order
|
||||
// Copy the last element into the deleted 'hole' and decrement the
|
||||
// size of the vector.
|
||||
// Assert: index >= 0 && index < mElementCount
|
||||
if (index < (mElementCount - 1))
|
||||
dMemmove(&mArray[index], &mArray[mElementCount - 1], sizeof(value_type));
|
||||
decrement();
|
||||
}
|
||||
|
||||
template<class T> inline T& Vector<T>::first()
|
||||
{
|
||||
return mArray[0];
|
||||
}
|
||||
|
||||
template<class T> inline const T& Vector<T>::first() const
|
||||
{
|
||||
return mArray[0];
|
||||
}
|
||||
|
||||
template<class T> inline T& Vector<T>::last()
|
||||
{
|
||||
AssertFatal(mElementCount != 0, "Error, no last element of a zero sized array!");
|
||||
return mArray[mElementCount - 1];
|
||||
}
|
||||
|
||||
template<class T> inline const T& Vector<T>::last() const
|
||||
{
|
||||
return mArray[mElementCount - 1];
|
||||
}
|
||||
|
||||
template<class T> inline void Vector<T>::clear()
|
||||
{
|
||||
mElementCount = 0;
|
||||
}
|
||||
|
||||
template<class T> inline void Vector<T>::compact()
|
||||
{
|
||||
resize(mElementCount);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template<class T> inline Vector<T>& Vector<T>::operator=(const Vector<T>& p)
|
||||
{
|
||||
resize(p.mElementCount);
|
||||
if (p.mElementCount)
|
||||
dMemcpy(mArray,p.mArray,mElementCount * sizeof(value_type));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class T> inline typename Vector<T>::iterator Vector<T>::begin()
|
||||
{
|
||||
return mArray;
|
||||
}
|
||||
|
||||
template<class T> inline typename Vector<T>::const_iterator Vector<T>::begin() const
|
||||
{
|
||||
return mArray;
|
||||
}
|
||||
|
||||
template<class T> inline typename Vector<T>::iterator Vector<T>::end()
|
||||
{
|
||||
return mArray + mElementCount;
|
||||
}
|
||||
|
||||
template<class T> inline typename Vector<T>::const_iterator Vector<T>::end() const
|
||||
{
|
||||
return mArray +mElementCount;
|
||||
}
|
||||
|
||||
template<class T> inline S32 Vector<T>::size() const
|
||||
{
|
||||
return S32(mElementCount);
|
||||
}
|
||||
|
||||
template<class T> inline bool Vector<T>::empty() const
|
||||
{
|
||||
return (mElementCount == 0);
|
||||
}
|
||||
|
||||
template<class T> inline void Vector<T>::insert(iterator p,const T& x)
|
||||
{
|
||||
S32 index = p - mArray;
|
||||
insert(U32(index));
|
||||
mArray[index] = x;
|
||||
}
|
||||
|
||||
template<class T> inline void Vector<T>::erase(iterator q)
|
||||
{
|
||||
erase(U32(q - mArray));
|
||||
}
|
||||
|
||||
template<class T> inline void Vector<T>::erase_fast(iterator q)
|
||||
{
|
||||
erase_fast(U32(q - mArray));
|
||||
}
|
||||
|
||||
template<class T> inline T& Vector<T>::front()
|
||||
{
|
||||
return *begin();
|
||||
}
|
||||
|
||||
template<class T> inline const T& Vector<T>::front() const
|
||||
{
|
||||
return *begin();
|
||||
}
|
||||
|
||||
template<class T> inline T& Vector<T>::back()
|
||||
{
|
||||
return *end();
|
||||
}
|
||||
|
||||
template<class T> inline const T& Vector<T>::back() const
|
||||
{
|
||||
return *end();
|
||||
}
|
||||
|
||||
template<class T> inline void Vector<T>::push_front(const T& x)
|
||||
{
|
||||
insert(0);
|
||||
mArray[0] = x;
|
||||
}
|
||||
|
||||
template<class T> inline void Vector<T>::push_back(const T& x)
|
||||
{
|
||||
increment();
|
||||
mArray[mElementCount - 1] = x;
|
||||
}
|
||||
|
||||
template<class T> inline void Vector<T>::pop_front()
|
||||
{
|
||||
erase(U32(0));
|
||||
}
|
||||
|
||||
template<class T> inline void Vector<T>::pop_back()
|
||||
{
|
||||
decrement();
|
||||
}
|
||||
|
||||
template<class T> inline T& Vector<T>::operator[](U32 index)
|
||||
{
|
||||
return mArray[index];
|
||||
}
|
||||
|
||||
template<class T> inline const T& Vector<T>::operator[](U32 index) const
|
||||
{
|
||||
return mArray[index];
|
||||
}
|
||||
|
||||
template<class T> inline void Vector<T>::reserve(U32 size)
|
||||
{
|
||||
if (size > mArraySize) {
|
||||
S32 ec = S32(mElementCount);
|
||||
if (resize(size))
|
||||
mElementCount = U32(ec);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T> inline U32 Vector<T>::capacity() const
|
||||
{
|
||||
return mArraySize;
|
||||
}
|
||||
|
||||
template<class T> inline void Vector<T>::set(void * addr, S32 sz)
|
||||
{
|
||||
setSize(sz);
|
||||
if (addr)
|
||||
dMemcpy(address(),addr,sz*sizeof(T));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template<class T> inline bool Vector<T>::resize(U32 ecount)
|
||||
{
|
||||
#ifdef DEBUG_GUARD
|
||||
return VectorResize(&mArraySize, &mElementCount, (void**) &mArray, ecount, sizeof(T),
|
||||
mFileAssociation, mLineAssociation);
|
||||
#else
|
||||
return VectorResize(&mArraySize, &mElementCount, (void**) &mArray, ecount, sizeof(T));
|
||||
#endif
|
||||
}
|
||||
|
||||
// BJW 8/20/97
|
||||
// code to merge a vector into this one
|
||||
template<class T> inline void Vector<T>::merge(const Vector& p)
|
||||
{
|
||||
if (p.size()) {
|
||||
S32 oldsize = size();
|
||||
resize(oldsize + p.size());
|
||||
dMemcpy( &mArray[oldsize], p.address(), p.size() * sizeof(T) );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Template for vectors of pointers.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template <class T>
|
||||
class VectorPtr : public Vector<void*>
|
||||
{
|
||||
|
||||
VectorPtr(const VectorPtr&); // disallowed
|
||||
public:
|
||||
VectorPtr();
|
||||
VectorPtr(const char* fileName, const U32 lineNum);
|
||||
|
||||
// STL interface
|
||||
typedef T value_type;
|
||||
typedef T& reference;
|
||||
typedef const T& const_reference;
|
||||
|
||||
typedef T* iterator;
|
||||
typedef const T* const_iterator;
|
||||
typedef U32 difference_type;
|
||||
typedef U32 size_type;
|
||||
|
||||
iterator begin();
|
||||
const_iterator begin() const;
|
||||
iterator end();
|
||||
const_iterator end() const;
|
||||
|
||||
void insert(iterator,const T&);
|
||||
void erase(iterator);
|
||||
|
||||
T& front();
|
||||
const T& front() const;
|
||||
T& back();
|
||||
const T& back() const;
|
||||
void push_front(const T&);
|
||||
void push_back(const T&);
|
||||
|
||||
T& operator[](U32);
|
||||
const T& operator[](U32) const;
|
||||
|
||||
// Extended interface
|
||||
typedef Vector<void*> Parent;
|
||||
T& first();
|
||||
T& last();
|
||||
const T& first() const;
|
||||
const T& last() const;
|
||||
void erase_fast(U32);
|
||||
void erase_fast(iterator);
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T> inline VectorPtr<T>::VectorPtr()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
template<class T> inline VectorPtr<T>::VectorPtr(const char* fileName,
|
||||
const U32 lineNum)
|
||||
: Vector<void*>(fileName, lineNum)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
template<class T> inline T& VectorPtr<T>::first()
|
||||
{
|
||||
return (T&)Parent::first();
|
||||
}
|
||||
|
||||
template<class T> inline const T& VectorPtr<T>::first() const
|
||||
{
|
||||
return (const T)Parent::first();
|
||||
}
|
||||
|
||||
template<class T> inline T& VectorPtr<T>::last()
|
||||
{
|
||||
return (T&)Parent::last();
|
||||
}
|
||||
|
||||
template<class T> inline const T& VectorPtr<T>::last() const
|
||||
{
|
||||
return (const T&)Parent::last();
|
||||
}
|
||||
|
||||
template<class T> inline typename VectorPtr<T>::iterator VectorPtr<T>::begin()
|
||||
{
|
||||
return (iterator)Parent::begin();
|
||||
}
|
||||
|
||||
template<class T> inline typename VectorPtr<T>::const_iterator VectorPtr<T>::begin() const
|
||||
{
|
||||
return (const_iterator)Parent::begin();
|
||||
}
|
||||
|
||||
template<class T> inline typename VectorPtr<T>::iterator VectorPtr<T>::end()
|
||||
{
|
||||
return (iterator)Parent::end();
|
||||
}
|
||||
|
||||
template<class T> inline typename VectorPtr<T>::const_iterator VectorPtr<T>::end() const
|
||||
{
|
||||
return (const_iterator)Parent::end();
|
||||
}
|
||||
|
||||
template<class T> inline void VectorPtr<T>::insert(iterator i,const T& x)
|
||||
{
|
||||
Parent::insert( (Parent::iterator)i, (Parent::reference)x );
|
||||
}
|
||||
|
||||
template<class T> inline void VectorPtr<T>::erase(iterator i)
|
||||
{
|
||||
Parent::erase( (Parent::iterator)i );
|
||||
}
|
||||
|
||||
template<class T> inline void VectorPtr<T>::erase_fast(U32 index)
|
||||
{
|
||||
// CAUTION: this operator does maintain list order
|
||||
// Copy the last element into the deleted 'hole' and decrement the
|
||||
// size of the vector.
|
||||
// Assert: index >= 0 && index < mElementCount
|
||||
if (index < (mElementCount - 1))
|
||||
mArray[index] = mArray[mElementCount - 1];
|
||||
decrement();
|
||||
}
|
||||
|
||||
template<class T> inline void VectorPtr<T>::erase_fast(iterator i)
|
||||
{
|
||||
erase_fast(U32(i - iterator(mArray)));
|
||||
}
|
||||
|
||||
template<class T> inline T& VectorPtr<T>::front()
|
||||
{
|
||||
return *begin();
|
||||
}
|
||||
|
||||
template<class T> inline const T& VectorPtr<T>::front() const
|
||||
{
|
||||
return *begin();
|
||||
}
|
||||
|
||||
template<class T> inline T& VectorPtr<T>::back()
|
||||
{
|
||||
return *end();
|
||||
}
|
||||
|
||||
template<class T> inline const T& VectorPtr<T>::back() const
|
||||
{
|
||||
return *end();
|
||||
}
|
||||
|
||||
template<class T> inline void VectorPtr<T>::push_front(const T& x)
|
||||
{
|
||||
Parent::push_front((Parent::const_reference)x);
|
||||
}
|
||||
|
||||
template<class T> inline void VectorPtr<T>::push_back(const T& x)
|
||||
{
|
||||
Parent::push_back((Parent::const_reference)x);
|
||||
}
|
||||
|
||||
template<class T> inline T& VectorPtr<T>::operator[](U32 index)
|
||||
{
|
||||
return (T&)Parent::operator[](index);
|
||||
}
|
||||
|
||||
template<class T> inline const T& VectorPtr<T>::operator[](U32 index) const
|
||||
{
|
||||
return (const T&)Parent::operator[](index);
|
||||
}
|
||||
|
||||
#endif //_TVECTOR_H_
|
||||
|
||||
305
core/tagDictionary.cc
Normal file
305
core/tagDictionary.cc
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "Platform/platform.h"
|
||||
#include "Core/tagDictionary.h"
|
||||
#include "Core/stream.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const char TAG_ASCII_ID[] = "[TAG]";
|
||||
const char TAG_ASCII_END[] = "[END]";
|
||||
const char TAG_ASCII_HEADER[] = "// Auto-Generated by TagDictionary class";
|
||||
|
||||
const S32 sg_tagDictAsciiUser = 1;
|
||||
|
||||
} // namespace
|
||||
|
||||
TagDictionary tagDictionary;
|
||||
|
||||
TagDictionary::TagDictionary()
|
||||
{
|
||||
numBuckets = 29;
|
||||
defineHashBuckets = (TagEntry **) dMalloc(numBuckets * sizeof(TagEntry *));
|
||||
idHashBuckets = (TagEntry **) dMalloc(numBuckets * sizeof(TagEntry *));
|
||||
|
||||
S32 i;
|
||||
for(i = 0; i < numBuckets; i++)
|
||||
{
|
||||
defineHashBuckets[i] = NULL;
|
||||
idHashBuckets[i] = NULL;
|
||||
}
|
||||
numEntries = 0;
|
||||
entryChain = NULL;
|
||||
}
|
||||
|
||||
TagDictionary::~TagDictionary()
|
||||
{
|
||||
dFree(defineHashBuckets);
|
||||
dFree(idHashBuckets);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static inline S32 hashId(S32 id, S32 tsize)
|
||||
{
|
||||
return id % tsize;
|
||||
}
|
||||
|
||||
static inline S32 hashDefine(StringTableEntry define, S32 tsize)
|
||||
{
|
||||
return (S32(define) >> 2) % tsize;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool TagDictionary::addEntry(S32 value, StringTableEntry define, StringTableEntry string)
|
||||
{
|
||||
if(!value)
|
||||
return false;
|
||||
//#pragma message "put console prints back"
|
||||
if(idToDefine(value))
|
||||
{
|
||||
AssertWarn(false, avar("Error: id %d already defined to a tag.", value));
|
||||
//Con::printf("Error: id %d already defined to a tag.", value);
|
||||
return false;
|
||||
}
|
||||
S32 tempTag;
|
||||
if((tempTag = defineToId(define)) != 0)
|
||||
{
|
||||
AssertWarn(false, avar("Error: define %s already defined to tag %d.", define, tempTag));
|
||||
//Con::printf("Error: define %s already defined to tag %d.", define, tempTag);
|
||||
return false;
|
||||
}
|
||||
TagEntry *newEntry = (TagEntry *) mempool.alloc(sizeof(TagEntry));
|
||||
|
||||
newEntry->id = value;
|
||||
newEntry->define = define;
|
||||
newEntry->string = string;
|
||||
|
||||
numEntries++;
|
||||
if(numEntries > numBuckets)
|
||||
{
|
||||
numBuckets = numBuckets * 2 + 1;
|
||||
defineHashBuckets = (TagEntry **) dRealloc(defineHashBuckets, numBuckets * sizeof(TagEntry *));
|
||||
idHashBuckets = (TagEntry **) dRealloc(idHashBuckets, numBuckets * sizeof(TagEntry *));
|
||||
S32 i;
|
||||
for(i = 0; i < numBuckets; i++)
|
||||
{
|
||||
defineHashBuckets[i] = NULL;
|
||||
idHashBuckets[i] = NULL;
|
||||
}
|
||||
TagEntry *walk = entryChain;
|
||||
|
||||
while(walk)
|
||||
{
|
||||
S32 index = hashId(walk->id, numBuckets);
|
||||
walk->idHashLink = idHashBuckets[index];
|
||||
idHashBuckets[index] = walk;
|
||||
|
||||
index = hashDefine(walk->define, numBuckets);
|
||||
walk->defineHashLink = defineHashBuckets[index];
|
||||
defineHashBuckets[index] = walk;
|
||||
|
||||
walk = walk->chain;
|
||||
}
|
||||
}
|
||||
newEntry->chain = entryChain;
|
||||
entryChain = newEntry;
|
||||
|
||||
S32 index = hashId(newEntry->id, numBuckets);
|
||||
newEntry->idHashLink = idHashBuckets[index];
|
||||
idHashBuckets[index] = newEntry;
|
||||
|
||||
index = hashDefine(newEntry->define, numBuckets);
|
||||
newEntry->defineHashLink = defineHashBuckets[index];
|
||||
defineHashBuckets[index] = newEntry;
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool TagDictionary::writeHeader(Stream& io_sio)
|
||||
{
|
||||
char buff[15000];
|
||||
Vector<S32> v;
|
||||
|
||||
TagEntry *walk = entryChain;
|
||||
while(walk)
|
||||
{
|
||||
v.push_back(walk->id);
|
||||
walk = walk->chain;
|
||||
}
|
||||
|
||||
sortIdVector(v);
|
||||
|
||||
io_sio.write( sizeof(TAG_ASCII_HEADER)-1, TAG_ASCII_HEADER);
|
||||
io_sio.write( 4, "\r\n\r\n");
|
||||
|
||||
char exclude[256];
|
||||
char tempBuf[256];
|
||||
dSprintf(exclude, sizeof(exclude), "_TD%10.10u_H_", Platform::getVirtualMilliseconds() / 4);
|
||||
|
||||
dSprintf(tempBuf, sizeof(tempBuf), "#ifndef %s\r\n", exclude);
|
||||
io_sio.write(dStrlen(tempBuf), tempBuf);
|
||||
dSprintf(tempBuf, sizeof(tempBuf), "#define %s\r\n\r\n", exclude);
|
||||
io_sio.write(dStrlen(tempBuf), tempBuf);
|
||||
|
||||
for (U32 i = 0; i < v.size(); i++)
|
||||
{
|
||||
dSprintf(buff, sizeof(buff), "#define %s (%d)\r\n", idToDefine(v[i]), v[i]);
|
||||
io_sio.write(dStrlen(buff), buff);
|
||||
}
|
||||
|
||||
dSprintf(tempBuf, sizeof(tempBuf), "\r\n#endif // %s\r\n", exclude);
|
||||
io_sio.write(dStrlen(tempBuf), tempBuf);
|
||||
|
||||
return (io_sio.getStatus() == Stream::Ok);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
StringTableEntry TagDictionary::defineToString(StringTableEntry tag)
|
||||
{
|
||||
S32 index = hashDefine(tag, numBuckets);
|
||||
if (index < 0) return NULL;
|
||||
TagEntry *walk = defineHashBuckets[index];
|
||||
while(walk)
|
||||
{
|
||||
if(walk->define == tag)
|
||||
return walk->string;
|
||||
walk = walk->defineHashLink;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
S32 TagDictionary::defineToId(StringTableEntry tag)
|
||||
{
|
||||
S32 index = hashDefine(tag, numBuckets);
|
||||
if (index < 0) return 0;
|
||||
TagEntry *walk = defineHashBuckets[index];
|
||||
while(walk)
|
||||
{
|
||||
if(walk->define == tag)
|
||||
return walk->id;
|
||||
walk = walk->defineHashLink;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
StringTableEntry TagDictionary::idToString(S32 id)
|
||||
{
|
||||
S32 index = hashId(id, numBuckets);
|
||||
if (index < 0) return NULL;
|
||||
TagEntry *walk = idHashBuckets[index];
|
||||
while(walk)
|
||||
{
|
||||
if(walk->id == id)
|
||||
return walk->string;
|
||||
walk = walk->idHashLink;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
StringTableEntry TagDictionary::idToDefine(S32 id)
|
||||
{
|
||||
S32 index = hashId(id, numBuckets);
|
||||
if (index < 0) return NULL;
|
||||
TagEntry *walk = idHashBuckets[index];
|
||||
while(walk)
|
||||
{
|
||||
if(walk->id == id)
|
||||
return walk->define;
|
||||
walk = walk->idHashLink;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void TagDictionary::findIDs(Vector<S32>& out_v,
|
||||
const S32 in_minID,
|
||||
const S32 in_maxID )
|
||||
{
|
||||
//locate all IDs that lie in between minID and maxID
|
||||
|
||||
TagEntry *walk = entryChain;
|
||||
while(walk)
|
||||
{
|
||||
if(walk->id > in_minID && walk->id < in_maxID)
|
||||
out_v.push_back(walk->id);
|
||||
walk = walk->chain;
|
||||
}
|
||||
sortIdVector(out_v);
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void TagDictionary::findStrings(Vector<S32>& out_v, const char* in_pPattern)
|
||||
{
|
||||
//locate all strings that match the pattern
|
||||
//
|
||||
TagEntry *walk = entryChain;
|
||||
while(walk)
|
||||
{
|
||||
if (match(in_pPattern, walk->string))
|
||||
out_v.push_back(walk->id);
|
||||
walk = walk->chain;
|
||||
}
|
||||
sortIdVector(out_v);
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void TagDictionary::findDefines(Vector<S32>& out_v, const char* in_pPattern)
|
||||
{
|
||||
//locate all define strings that match the pattern and add their ID
|
||||
//to the given vector
|
||||
//
|
||||
TagEntry *walk = entryChain;
|
||||
while(walk)
|
||||
{
|
||||
if (match(in_pPattern, walk->define))
|
||||
out_v.push_back(walk->id);
|
||||
walk = walk->chain;
|
||||
}
|
||||
sortIdVector(out_v);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool TagDictionary::match(const char* pattern, const char* str)
|
||||
{
|
||||
//quick and dirty recursive DOS-style wild-card string matcher
|
||||
//
|
||||
switch (*pattern) {
|
||||
case '\0':
|
||||
return !*str;
|
||||
|
||||
case '*':
|
||||
return match(pattern+1, str) || *str && match(pattern, str+1);
|
||||
|
||||
case '?':
|
||||
return *str && match(pattern+1, str+1);
|
||||
|
||||
default:
|
||||
return (*pattern == *str) && match(pattern+1, str+1);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static int QSORT_CALLBACK idCompare(const void *in_p1, const void *in_p2)
|
||||
{
|
||||
return *((S32 *) in_p1) - *((S32 *) in_p2);
|
||||
}
|
||||
|
||||
void TagDictionary::sortIdVector(Vector<S32>& out_v)
|
||||
{
|
||||
dQsort(out_v.address(), out_v.size(), sizeof(S32), idCompare);
|
||||
}
|
||||
|
||||
68
core/tagDictionary.h
Normal file
68
core/tagDictionary.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAGDICTIONARY_H_
|
||||
#define _TAGDICTIONARY_H_
|
||||
|
||||
#ifndef _STRINGTABLE_H_
|
||||
#include "Core/stringTable.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "Core/tVector.h"
|
||||
#endif
|
||||
|
||||
class Stream;
|
||||
|
||||
class TagDictionary
|
||||
{
|
||||
struct TagEntry
|
||||
{
|
||||
S32 id;
|
||||
StringTableEntry define;
|
||||
StringTableEntry string;
|
||||
TagEntry *chain; // for linear traversal
|
||||
TagEntry *defineHashLink;
|
||||
TagEntry *idHashLink;
|
||||
};
|
||||
|
||||
TagEntry **defineHashBuckets;
|
||||
TagEntry **idHashBuckets;
|
||||
|
||||
TagEntry *entryChain;
|
||||
DataChunker mempool;
|
||||
S32 numBuckets;
|
||||
S32 numEntries;
|
||||
|
||||
bool match(const char* pattern, const char* str);
|
||||
void sortIdVector(Vector<S32>& out_v);
|
||||
public:
|
||||
TagDictionary();
|
||||
~TagDictionary();
|
||||
|
||||
//IO functions
|
||||
//
|
||||
bool writeHeader(Stream &);
|
||||
|
||||
// String/Define retrieval and search functions...
|
||||
//
|
||||
|
||||
bool addEntry(S32 value, StringTableEntry define, StringTableEntry string);
|
||||
|
||||
StringTableEntry defineToString(StringTableEntry tag);
|
||||
StringTableEntry idToString(S32 tag);
|
||||
StringTableEntry idToDefine(S32 tag);
|
||||
S32 defineToId(StringTableEntry tag);
|
||||
|
||||
// get IDs such that minID < IDs < maxID
|
||||
void findIDs( Vector<S32> &v, const S32 minID, const S32 maxID );
|
||||
void findStrings( Vector<S32> &v, const char *pattern);
|
||||
void findDefines( Vector<S32> &v, const char *pattern);
|
||||
};
|
||||
|
||||
extern TagDictionary tagDictionary;
|
||||
|
||||
#endif //_TAGDICTIONARY_H_
|
||||
212
core/zipAggregate.cc
Normal file
212
core/zipAggregate.cc
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "Platform/platform.h"
|
||||
#include "Core/stringTable.h"
|
||||
|
||||
#include "Core/fileStream.h" // Streams
|
||||
|
||||
#include "Core/zipAggregate.h" // Own header, and private includes
|
||||
#include "Core/zipHeaders.h"
|
||||
|
||||
ZipAggregate::ZipAggregate()
|
||||
: m_pZipFileName(NULL)
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(m_fileList);
|
||||
}
|
||||
|
||||
ZipAggregate::~ZipAggregate()
|
||||
{
|
||||
closeAggregate();
|
||||
}
|
||||
|
||||
bool
|
||||
ZipAggregate::refreshAggregate()
|
||||
{
|
||||
AssertFatal(m_pZipFileName != NULL, "No filename? Must not be open. Disallowed");
|
||||
|
||||
char tmpBuff[512];
|
||||
dStrcpy(tmpBuff, m_pZipFileName);
|
||||
|
||||
return openAggregate(tmpBuff);
|
||||
}
|
||||
|
||||
bool
|
||||
ZipAggregate::openAggregate(const char* in_pFileName)
|
||||
{
|
||||
closeAggregate();
|
||||
|
||||
AssertFatal(in_pFileName != NULL, "No filename to open!");
|
||||
|
||||
m_pZipFileName = new char[dStrlen(in_pFileName) + 1];
|
||||
dStrcpy(m_pZipFileName, in_pFileName);
|
||||
|
||||
FileStream* pStream = new FileStream;
|
||||
if (pStream->open(m_pZipFileName, FileStream::Read) == false ||
|
||||
createZipDirectory(pStream) == false) {
|
||||
// Failure, abort the open...
|
||||
//
|
||||
delete pStream;
|
||||
|
||||
delete [] m_pZipFileName;
|
||||
m_pZipFileName = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finished! Open for business
|
||||
delete pStream;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ZipAggregate::closeAggregate()
|
||||
{
|
||||
destroyZipDirectory();
|
||||
|
||||
delete [] m_pZipFileName;
|
||||
m_pZipFileName = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
ZipAggregate::destroyZipDirectory()
|
||||
{
|
||||
m_fileList.clear();
|
||||
}
|
||||
|
||||
bool
|
||||
ZipAggregate::createZipDirectory(Stream* io_pStream)
|
||||
{
|
||||
AssertFatal(io_pStream != NULL, "Error, stream not open.");
|
||||
|
||||
U32 streamSize = io_pStream->getStreamSize();
|
||||
U32 initialPosition = io_pStream->getPosition();
|
||||
|
||||
// We assume that the CD is 22 bytes from the end. This will be invalid
|
||||
// in the case that the zip file has comments. Perhaps test the quick
|
||||
// way, then degrade to seaching the final 64k+22b (!) of the stream?
|
||||
//
|
||||
bool posSuccess = io_pStream->setPosition(streamSize - sizeof(ZipEOCDRecord::EOCDRecord));
|
||||
if (posSuccess == false) {
|
||||
AssertWarn(false, "Unable to position stream to start of EOCDRecord");
|
||||
return false;
|
||||
}
|
||||
|
||||
ZipEOCDRecord* pEOCDRecord = new ZipEOCDRecord;
|
||||
if (pEOCDRecord->readFromStream(*io_pStream) == false) {
|
||||
// This is where we would try to degrade to general case...
|
||||
//
|
||||
AssertWarn(false, "Unable to locate central directory. "
|
||||
"Zip File might have comments");
|
||||
delete pEOCDRecord;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the consistency of the zipFile.
|
||||
//
|
||||
if ((pEOCDRecord->m_record.diskNumber != pEOCDRecord->m_record.eocdDiskNumber) ||
|
||||
(pEOCDRecord->m_record.numCDEntriesDisk != pEOCDRecord->m_record.numCDEntriesTotal)) {
|
||||
AssertWarn(false, "Zipfile appears to be part of a "
|
||||
"multi-zip disk span set, unsupported");
|
||||
delete pEOCDRecord;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we're here, we're good! Scan to the start of the CDirectory, and
|
||||
// start scanning the entries into our directory structure...
|
||||
//
|
||||
U32 startCDPosition = pEOCDRecord->m_record.cdOffset;
|
||||
U32 endCDPosition = pEOCDRecord->m_record.cdOffset +
|
||||
pEOCDRecord->m_record.cdSize;
|
||||
|
||||
posSuccess = io_pStream->setPosition(startCDPosition);
|
||||
if (posSuccess == false) {
|
||||
AssertWarn(false, "Unable to position to CD entries.");
|
||||
delete pEOCDRecord;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dirReadSuccess = true;
|
||||
for (U16 i = 0; i < pEOCDRecord->m_record.numCDEntriesTotal; i++) {
|
||||
ZipDirFileHeader zdfHeader;
|
||||
|
||||
bool hrSuccess = zdfHeader.readFromStream(*io_pStream);
|
||||
if (hrSuccess == false) {
|
||||
AssertWarn(false, "Error reading a CD Entry in zip aggregate");
|
||||
dirReadSuccess = false;
|
||||
break;
|
||||
}
|
||||
|
||||
enterZipDirRecord(zdfHeader);
|
||||
}
|
||||
|
||||
delete pEOCDRecord;
|
||||
if (dirReadSuccess == true) {
|
||||
// Every thing went well, we're done, position the stream to the end of the
|
||||
// CD...
|
||||
//
|
||||
io_pStream->setPosition(endCDPosition);
|
||||
return true;
|
||||
} else {
|
||||
// Oh, crap.
|
||||
io_pStream->setPosition(initialPosition);
|
||||
destroyZipDirectory();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ZipAggregate::enterZipDirRecord(const ZipDirFileHeader& in_rHeader)
|
||||
{
|
||||
// Ok, the first thing to do is figure out whether this is
|
||||
// a directory or a file. Directories have a trailing /
|
||||
// in the file name, and a filelength (comp/uncomp) of 0
|
||||
// Note: this is not specified in
|
||||
// the file format spec I have, but seems fairly likely to
|
||||
// be correct.
|
||||
//
|
||||
if (in_rHeader.m_pFileName[dStrlen(in_rHeader.m_pFileName) - 1] == '/' &&
|
||||
(in_rHeader.m_header.compressedSize == 0 &&
|
||||
in_rHeader.m_header.uncompressedSize == 0))
|
||||
return;
|
||||
|
||||
// It's a file. Enter it into the directory...
|
||||
m_fileList.increment();
|
||||
FileEntry& rEntry = m_fileList.last();
|
||||
|
||||
char tempString[1024];
|
||||
dStrcpy(tempString, in_rHeader.m_pFileName);
|
||||
char* scan = tempString;
|
||||
while (*scan != '\0') {
|
||||
if (*scan == '\\')
|
||||
*scan = '/';
|
||||
scan++;
|
||||
}
|
||||
char* pPathEnd = dStrrchr(tempString, '/');
|
||||
if (pPathEnd != NULL) {
|
||||
pPathEnd[0] = '\0';
|
||||
rEntry.pPath = StringTable->insert(tempString);
|
||||
rEntry.pFileName = StringTable->insert(pPathEnd + 1);
|
||||
} else {
|
||||
rEntry.pPath = NULL;
|
||||
rEntry.pFileName = StringTable->insert(in_rHeader.m_pFileName);
|
||||
}
|
||||
|
||||
rEntry.fileSize = in_rHeader.m_header.uncompressedSize;
|
||||
rEntry.compressedFileSize = in_rHeader.m_header.compressedSize;
|
||||
rEntry.fileOffset = in_rHeader.m_header.relativeOffsetOfLocalHeader;
|
||||
|
||||
if (in_rHeader.m_header.compressionMethod == ZipDirFileHeader::Deflated) {
|
||||
rEntry.flags = FileEntry::Compressed;
|
||||
} else if (in_rHeader.m_header.compressionMethod == ZipDirFileHeader::Stored) {
|
||||
rEntry.flags = FileEntry::Uncompressed;
|
||||
} else {
|
||||
AssertWarn(0, avar("Warning, non-stored or deflated resource in %s",
|
||||
m_pZipFileName));
|
||||
m_fileList.decrement();
|
||||
}
|
||||
}
|
||||
|
||||
69
core/zipAggregate.h
Normal file
69
core/zipAggregate.h
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _ZIPAGGREGATE_H_
|
||||
#define _ZIPAGGREGATE_H_
|
||||
|
||||
//Includes
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "Platform/platform.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "Core/tVector.h"
|
||||
#endif
|
||||
|
||||
class Stream;
|
||||
class ZipDirFileHeader;
|
||||
|
||||
class ZipAggregate
|
||||
{
|
||||
public:
|
||||
struct FileEntry {
|
||||
enum {
|
||||
Uncompressed = 0,
|
||||
Compressed = 1 << 0
|
||||
};
|
||||
|
||||
const char* pPath;
|
||||
const char* pFileName;
|
||||
U32 fileOffset;
|
||||
U32 fileSize;
|
||||
U32 compressedFileSize;
|
||||
U32 flags;
|
||||
};
|
||||
|
||||
//-------------------------------------- Instance scope members and decls.
|
||||
private:
|
||||
char* m_pZipFileName;
|
||||
Vector<FileEntry> m_fileList;
|
||||
|
||||
void enterZipDirRecord(const ZipDirFileHeader& in_rHeader);
|
||||
bool createZipDirectory(Stream*);
|
||||
void destroyZipDirectory();
|
||||
|
||||
ZipAggregate(const ZipAggregate&); // disallowed
|
||||
public:
|
||||
ZipAggregate();
|
||||
~ZipAggregate();
|
||||
|
||||
// Opening/Manipulation interface...
|
||||
public:
|
||||
bool openAggregate(const char* in_pFileName);
|
||||
void closeAggregate();
|
||||
bool refreshAggregate();
|
||||
|
||||
// Entry iteration interface...
|
||||
public:
|
||||
typedef Vector<FileEntry>::const_iterator iterator;
|
||||
|
||||
U32 numEntries() const { return m_fileList.size(); }
|
||||
const FileEntry& operator[](const U32 idx) const { return m_fileList[idx]; }
|
||||
iterator begin() const { return m_fileList.begin(); }
|
||||
iterator end() const { return m_fileList.end(); }
|
||||
};
|
||||
|
||||
#endif //_ZIPAGGREGATE_H_
|
||||
120
core/zipHeaders.cc
Normal file
120
core/zipHeaders.cc
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "Platform/platform.h"
|
||||
#include "Core/stream.h"
|
||||
#include "Core/zipHeaders.h"
|
||||
|
||||
const U32 ZipLocalFileHeader::csm_localFileHeaderSig = 0x04034b50;
|
||||
const U32 ZipDirFileHeader::csm_dirFileHeaderSig = 0x02014b50;
|
||||
const U32 ZipEOCDRecord::csm_eocdRecordSig = 0x06054b50;
|
||||
|
||||
bool
|
||||
ZipLocalFileHeader::readFromStream(Stream& io_rStream)
|
||||
{
|
||||
AssertFatal(io_rStream.getStatus() == Stream::Ok,
|
||||
"Error, stream is closed or has an uncleared error.");
|
||||
AssertFatal(io_rStream.hasCapability(Stream::StreamPosition),
|
||||
"Must be positionable stream to read zip headers...");
|
||||
|
||||
// Read the initial header fields, marking the initial position...
|
||||
//
|
||||
U32 initialPosition = io_rStream.getPosition();
|
||||
bool success = io_rStream.read(sizeof(m_header), &m_header);
|
||||
|
||||
if (success == false || m_header.headerSig != csm_localFileHeaderSig) {
|
||||
AssertWarn(0, "Unable to retrieve local file header from stream position...");
|
||||
io_rStream.setPosition(initialPosition);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the variable length file name from the stream...
|
||||
//
|
||||
AssertFatal(m_header.fileNameLength < (MaxFileNameLength - 1),
|
||||
"Filename too long, increase structure size");
|
||||
success = io_rStream.read(m_header.fileNameLength, m_pFileName);
|
||||
m_pFileName[m_header.fileNameLength] = '\0';
|
||||
if (success == false) {
|
||||
AssertWarn(0, "Unable to read file name from stream position...");
|
||||
io_rStream.setPosition(initialPosition);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// And seek to the end of the header, ignoring the extra field.
|
||||
io_rStream.setPosition(initialPosition +
|
||||
(sizeof(m_header) +
|
||||
m_header.fileNameLength +
|
||||
m_header.extraFieldLength));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ZipDirFileHeader::readFromStream(Stream& io_rStream)
|
||||
{
|
||||
AssertFatal(io_rStream.getStatus() == Stream::Ok,
|
||||
"Error, stream is closed or has an uncleared error.");
|
||||
AssertFatal(io_rStream.hasCapability(Stream::StreamPosition),
|
||||
"Must be positionable stream to read zip headers...");
|
||||
|
||||
// Read the initial header fields, marking the initial position...
|
||||
//
|
||||
U32 initialPosition = io_rStream.getPosition();
|
||||
bool success = io_rStream.read(sizeof(m_header), &m_header);
|
||||
|
||||
if (success == false || m_header.headerSig != csm_dirFileHeaderSig) {
|
||||
AssertWarn(0, "Unable to retrieve local file header from stream position...");
|
||||
io_rStream.setPosition(initialPosition);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the variable length file name from the stream...
|
||||
//
|
||||
AssertFatal(m_header.fileNameLength < (MaxFileNameLength - 1),
|
||||
"Filename too long, increase structure size");
|
||||
success = io_rStream.read(m_header.fileNameLength, m_pFileName);
|
||||
m_pFileName[m_header.fileNameLength] = '\0';
|
||||
if (success == false) {
|
||||
AssertWarn(0, "Unable to read file name from stream position...");
|
||||
io_rStream.setPosition(initialPosition);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// And seek to the end of the header, ignoring the extra field.
|
||||
io_rStream.setPosition(initialPosition +
|
||||
(sizeof(m_header) +
|
||||
m_header.fileNameLength +
|
||||
m_header.extraFieldLength));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ZipEOCDRecord::readFromStream(Stream& io_rStream)
|
||||
{
|
||||
AssertFatal(io_rStream.getStatus() == Stream::Ok,
|
||||
"Error, stream is closed or has an uncleared error.");
|
||||
AssertFatal(io_rStream.hasCapability(Stream::StreamPosition),
|
||||
"Must be positionable stream to read zip headers...");
|
||||
|
||||
// Read the initial header fields, marking the initial position...
|
||||
//
|
||||
U32 initialPosition = io_rStream.getPosition();
|
||||
bool success = io_rStream.read(sizeof(m_record), &m_record);
|
||||
|
||||
if (success == false || m_record.eocdSig != csm_eocdRecordSig) {
|
||||
AssertWarn(0, "Unable to retrieve EOCD header from stream position...");
|
||||
io_rStream.setPosition(initialPosition);
|
||||
return false;
|
||||
}
|
||||
|
||||
// And seek to the end of the header, ignoring the extra field.
|
||||
io_rStream.setPosition(initialPosition +
|
||||
(sizeof(m_record) + m_record.zipFileCommentLength));
|
||||
return true;
|
||||
}
|
||||
|
||||
211
core/zipHeaders.h
Normal file
211
core/zipHeaders.h
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _ZIPHEADERS_H_
|
||||
#define _ZIPHEADERS_H_
|
||||
|
||||
//Includes
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "Platform/platform.h"
|
||||
#endif
|
||||
|
||||
//-------------------------------------- NB: Structures in this header are BYTE
|
||||
// aligned!
|
||||
#ifdef __BORLANDC__
|
||||
# pragma option -a1
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma pack(push,1)
|
||||
#endif
|
||||
|
||||
#ifdef __MWERKS__
|
||||
# pragma options align=packed
|
||||
#endif
|
||||
|
||||
|
||||
class Stream;
|
||||
|
||||
//-------------------------------------- Structure designed to fit exactly 256 bytes.
|
||||
class ZipLocalFileHeader
|
||||
{
|
||||
// NB: Extra field in the header is ignored, but the stream read seeks
|
||||
// past it...
|
||||
//
|
||||
private:
|
||||
static const U32 csm_localFileHeaderSig;
|
||||
|
||||
public:
|
||||
enum {
|
||||
MaxFileNameLength = 211
|
||||
};
|
||||
enum CompressionMethod {
|
||||
Stored = 0,
|
||||
Shrunk = 1,
|
||||
ReducedL1 = 2,
|
||||
ReducedL2 = 3,
|
||||
ReducedL3 = 4,
|
||||
ReducedL4 = 5,
|
||||
Imploded = 6,
|
||||
ReservedTokenized = 7,
|
||||
Deflated = 8,
|
||||
EnhDefalted = 9,
|
||||
DateCompression = 10
|
||||
};
|
||||
|
||||
struct LocalFileHeader {
|
||||
U32 headerSig;
|
||||
U16 versionToDecompress;
|
||||
U16 bitFlags;
|
||||
U16 compressionMethod;
|
||||
U16 lastModTime;
|
||||
U16 lastModDate;
|
||||
U32 crc32;
|
||||
U32 compressedSize;
|
||||
U32 uncompressedSize;
|
||||
|
||||
U16 fileNameLength;
|
||||
U16 extraFieldLength;
|
||||
#ifndef linux
|
||||
};
|
||||
#else
|
||||
} __attribute__ ((packed));
|
||||
#endif
|
||||
|
||||
LocalFileHeader m_header; // Fixed size header
|
||||
char m_pFileName[226]; // Variable size: FileName. Note that the
|
||||
// number of chars here is more than the
|
||||
// max allowed filename for alignment
|
||||
// purposes
|
||||
|
||||
// Stream read routines
|
||||
public:
|
||||
bool readFromStream(Stream& io_rStream);
|
||||
#ifndef linux
|
||||
};
|
||||
#else
|
||||
} __attribute__ ((packed));
|
||||
#endif
|
||||
|
||||
|
||||
//-------------------------------------- Also designed to fit into 256 bytes, note
|
||||
// that we ignore the extra and file comment
|
||||
// fields.
|
||||
class ZipDirFileHeader
|
||||
{
|
||||
private:
|
||||
static const U32 csm_dirFileHeaderSig;
|
||||
|
||||
public:
|
||||
enum {
|
||||
MaxFileNameLength = 211
|
||||
};
|
||||
enum CompressionMethod {
|
||||
Stored = 0,
|
||||
Shrunk = 1,
|
||||
ReducedL1 = 2,
|
||||
ReducedL2 = 3,
|
||||
ReducedL3 = 4,
|
||||
ReducedL4 = 5,
|
||||
Imploded = 6,
|
||||
ReservedTokenized = 7,
|
||||
Deflated = 8,
|
||||
EnhDefalted = 9,
|
||||
DateCompression = 10
|
||||
};
|
||||
|
||||
struct DirFileHeader {
|
||||
U32 headerSig;
|
||||
U16 versionMadeBy;
|
||||
U16 versionToDecompress;
|
||||
U16 bitFlags;
|
||||
U16 compressionMethod;
|
||||
U16 lastModTime;
|
||||
U16 lastModDate;
|
||||
U32 crc32;
|
||||
U32 compressedSize;
|
||||
U32 uncompressedSize;
|
||||
U16 fileNameLength;
|
||||
U16 extraFieldLength;
|
||||
U16 fileCommentLength;
|
||||
U16 diskNumberStart;
|
||||
U16 internalFileAttributes;
|
||||
U32 externalFileAttributes;
|
||||
U32 relativeOffsetOfLocalHeader;
|
||||
#ifndef linux
|
||||
};
|
||||
#else
|
||||
} __attribute__ ((packed));
|
||||
#endif
|
||||
|
||||
DirFileHeader m_header;
|
||||
char m_pFileName[212];
|
||||
|
||||
// Stream read routines
|
||||
public:
|
||||
bool readFromStream(Stream& io_rStream);
|
||||
#ifndef linux
|
||||
};
|
||||
#else
|
||||
} __attribute__ ((packed));
|
||||
#endif
|
||||
|
||||
|
||||
//-------------------------------------- Padded to 32 bytes. Note that we completely
|
||||
// ignore any zip file comments.
|
||||
class ZipEOCDRecord
|
||||
{
|
||||
private:
|
||||
static const U32 csm_eocdRecordSig;
|
||||
|
||||
public:
|
||||
enum {
|
||||
ProperRecordSize = 22
|
||||
};
|
||||
|
||||
struct EOCDRecord {
|
||||
U32 eocdSig;
|
||||
U16 diskNumber;
|
||||
U16 eocdDiskNumber;
|
||||
U16 numCDEntriesDisk;
|
||||
U16 numCDEntriesTotal;
|
||||
U32 cdSize;
|
||||
U32 cdOffset;
|
||||
U16 zipFileCommentLength;
|
||||
#ifndef linux
|
||||
};
|
||||
#else
|
||||
} __attribute__ ((packed));
|
||||
#endif
|
||||
|
||||
EOCDRecord m_record;
|
||||
char __padding[10];
|
||||
// Stream read routines
|
||||
public:
|
||||
bool readFromStream(Stream& io_rStream);
|
||||
#ifndef linux
|
||||
};
|
||||
#else
|
||||
} __attribute__ ((packed));
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef __BORLANDC__
|
||||
# pragma option -a.
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma pack(pop)
|
||||
#endif
|
||||
|
||||
#ifdef __MWERKS__
|
||||
# pragma options align=reset
|
||||
#endif
|
||||
|
||||
|
||||
#endif //_NZIPHEADERS_H_
|
||||
401
core/zipSubStream.cc
Normal file
401
core/zipSubStream.cc
Normal file
|
|
@ -0,0 +1,401 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "zlib.h"
|
||||
#include "core/zipSubStream.h"
|
||||
|
||||
|
||||
const U32 ZipSubRStream::csm_streamCaps = U32(Stream::StreamRead) | U32(Stream::StreamPosition);
|
||||
const U32 ZipSubRStream::csm_inputBufferSize = 4096;
|
||||
|
||||
const U32 ZipSubWStream::csm_streamCaps = U32(Stream::StreamWrite);
|
||||
const U32 ZipSubWStream::csm_bufferSize = (2048 * 1024);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//--------------------------------------
|
||||
//
|
||||
ZipSubRStream::ZipSubRStream()
|
||||
: m_pStream(NULL),
|
||||
m_uncompressedSize(0),
|
||||
m_currentPosition(0),
|
||||
|
||||
m_pZipStream(NULL),
|
||||
m_originalSlavePosition(0)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
ZipSubRStream::~ZipSubRStream()
|
||||
{
|
||||
detachStream();
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
bool ZipSubRStream::attachStream(Stream* io_pSlaveStream)
|
||||
{
|
||||
AssertFatal(io_pSlaveStream != NULL, "NULL Slave stream?");
|
||||
AssertFatal(m_pStream == NULL, "Already attached!");
|
||||
|
||||
m_pStream = io_pSlaveStream;
|
||||
m_originalSlavePosition = io_pSlaveStream->getPosition();
|
||||
m_uncompressedSize = 0;
|
||||
m_currentPosition = 0;
|
||||
|
||||
// Initialize zipStream state...
|
||||
m_pZipStream = new z_stream_s;
|
||||
m_pInputBuffer = new U8[csm_inputBufferSize];
|
||||
|
||||
m_pZipStream->zalloc = Z_NULL;
|
||||
m_pZipStream->zfree = Z_NULL;
|
||||
m_pZipStream->opaque = Z_NULL;
|
||||
|
||||
U32 buffSize = fillBuffer(csm_inputBufferSize);
|
||||
|
||||
m_pZipStream->next_in = m_pInputBuffer;
|
||||
m_pZipStream->avail_in = buffSize;
|
||||
m_pZipStream->total_in = 0;
|
||||
inflateInit2(m_pZipStream, -MAX_WBITS);
|
||||
|
||||
setStatus(Ok);
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
void ZipSubRStream::detachStream()
|
||||
{
|
||||
if (m_pZipStream != NULL) {
|
||||
// close out zip stream...
|
||||
inflateEnd(m_pZipStream);
|
||||
|
||||
delete [] m_pInputBuffer;
|
||||
m_pInputBuffer = NULL;
|
||||
delete m_pZipStream;
|
||||
m_pZipStream = NULL;
|
||||
}
|
||||
|
||||
m_pStream = NULL;
|
||||
m_originalSlavePosition = 0;
|
||||
m_uncompressedSize = 0;
|
||||
m_currentPosition = 0;
|
||||
setStatus(Closed);
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
Stream* ZipSubRStream::getStream()
|
||||
{
|
||||
return m_pStream;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
void ZipSubRStream::setUncompressedSize(const U32 in_uncSize)
|
||||
{
|
||||
AssertFatal(m_pStream != NULL, "error, no stream to set unc size for");
|
||||
|
||||
m_uncompressedSize = in_uncSize;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
bool ZipSubRStream::_read(const U32 in_numBytes, void *out_pBuffer)
|
||||
{
|
||||
if (in_numBytes == 0)
|
||||
return true;
|
||||
|
||||
AssertFatal(out_pBuffer != NULL, "NULL output buffer");
|
||||
if (getStatus() == Closed) {
|
||||
AssertFatal(false, "Attempted read from closed stream");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ok, we need to call inflate() until the output buffer is full.
|
||||
// first, set up the output portion of the z_stream
|
||||
//
|
||||
m_pZipStream->next_out = (Bytef*)out_pBuffer;
|
||||
m_pZipStream->avail_out = in_numBytes;
|
||||
m_pZipStream->total_out = 0;
|
||||
|
||||
while (m_pZipStream->avail_out != 0)
|
||||
{
|
||||
S32 retVal = Z_OK;
|
||||
|
||||
if(m_pZipStream->avail_in == 0)
|
||||
{
|
||||
// check if there is more output pending
|
||||
inflate(m_pZipStream, Z_SYNC_FLUSH);
|
||||
|
||||
if(m_pZipStream->total_out != in_numBytes)
|
||||
{
|
||||
// Need to provide more input bytes for the stream to read...
|
||||
U32 buffSize = fillBuffer(csm_inputBufferSize);
|
||||
AssertFatal(buffSize != 0, "Must find a more graceful way to handle this");
|
||||
|
||||
m_pZipStream->next_in = m_pInputBuffer;
|
||||
m_pZipStream->avail_in = buffSize;
|
||||
m_pZipStream->total_in = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// need to get more?
|
||||
if(m_pZipStream->total_out != in_numBytes)
|
||||
retVal = inflate(m_pZipStream, Z_SYNC_FLUSH);
|
||||
|
||||
AssertFatal(retVal != Z_BUF_ERROR, "Should never run into a buffer error");
|
||||
AssertFatal(retVal == Z_OK || retVal == Z_STREAM_END, "error in the stream");
|
||||
|
||||
if (retVal == Z_STREAM_END)
|
||||
{
|
||||
if (m_pZipStream->avail_out != 0)
|
||||
setStatus(EOS);
|
||||
else
|
||||
setStatus(Ok);
|
||||
m_currentPosition += m_pZipStream->total_out;
|
||||
return getStatus() == Ok;
|
||||
}
|
||||
}
|
||||
AssertFatal(m_pZipStream->total_out == in_numBytes,
|
||||
"Error, didn't finish the decompression!");
|
||||
|
||||
// If we're here, everything went peachy...
|
||||
setStatus(Ok);
|
||||
m_currentPosition += m_pZipStream->total_out;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
bool ZipSubRStream::hasCapability(const Capability in_cap) const
|
||||
{
|
||||
return (csm_streamCaps & U32(in_cap)) != 0;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
U32 ZipSubRStream::getPosition() const
|
||||
{
|
||||
AssertFatal(m_pStream != NULL, "Error, not attached");
|
||||
|
||||
return m_currentPosition;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
bool ZipSubRStream::setPosition(const U32 in_newPosition)
|
||||
{
|
||||
AssertFatal(m_pStream != NULL, "Error, not attached");
|
||||
|
||||
if (in_newPosition == 0)
|
||||
{
|
||||
Stream* pStream = getStream();
|
||||
U32 resetPosition = m_originalSlavePosition;
|
||||
U32 uncompressedSize = m_uncompressedSize;
|
||||
detachStream();
|
||||
pStream->setPosition(resetPosition);
|
||||
attachStream(pStream);
|
||||
setUncompressedSize(uncompressedSize);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertFatal(false, "Not implemented!");
|
||||
// Erk. How do we do this.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
U32 ZipSubRStream::getStreamSize()
|
||||
{
|
||||
AssertFatal(m_pStream != NULL, "No stream to size()");
|
||||
AssertFatal(m_uncompressedSize != 0, "No data? Properties probably not set...");
|
||||
|
||||
return m_uncompressedSize;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
U32 ZipSubRStream::fillBuffer(const U32 in_attemptSize)
|
||||
{
|
||||
AssertFatal(m_pStream != NULL, "No stream to fill from?");
|
||||
AssertFatal(m_pStream->getStatus() != Stream::Closed,
|
||||
"Fill from a closed stream?");
|
||||
|
||||
U32 streamSize = m_pStream->getStreamSize();
|
||||
U32 currPos = m_pStream->getPosition();
|
||||
|
||||
U32 actualReadSize;
|
||||
if (in_attemptSize + currPos > streamSize) {
|
||||
actualReadSize = streamSize - currPos;
|
||||
} else {
|
||||
actualReadSize = in_attemptSize;
|
||||
}
|
||||
|
||||
if (m_pStream->read(actualReadSize, m_pInputBuffer) == true) {
|
||||
return actualReadSize;
|
||||
} else {
|
||||
AssertWarn(false, "Read failed while trying to fill buffer");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
ZipSubWStream::ZipSubWStream()
|
||||
: m_pStream(NULL),
|
||||
m_currPosition(0),
|
||||
|
||||
m_pZipStream(NULL)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
ZipSubWStream::~ZipSubWStream()
|
||||
{
|
||||
detachStream();
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
bool ZipSubWStream::attachStream(Stream* io_pSlaveStream)
|
||||
{
|
||||
AssertFatal(io_pSlaveStream != NULL, "NULL Slave stream?");
|
||||
AssertFatal(m_pStream == NULL, "Already attached!");
|
||||
|
||||
m_pStream = io_pSlaveStream;
|
||||
m_currPosition = 0;
|
||||
|
||||
m_pOutputBuffer = new U8[csm_bufferSize];
|
||||
m_pInputBuffer = new U8[csm_bufferSize];
|
||||
|
||||
// Initialize zipStream state...
|
||||
m_pZipStream = new z_stream_s;
|
||||
|
||||
m_pZipStream->zalloc = Z_NULL;
|
||||
m_pZipStream->zfree = Z_NULL;
|
||||
m_pZipStream->opaque = Z_NULL;
|
||||
|
||||
m_pZipStream->next_in = m_pInputBuffer;
|
||||
m_pZipStream->avail_in = csm_bufferSize;
|
||||
m_pZipStream->total_in = 0;
|
||||
m_pZipStream->next_out = m_pOutputBuffer;
|
||||
m_pZipStream->avail_out = csm_bufferSize;
|
||||
m_pZipStream->total_out = 0;
|
||||
|
||||
deflateInit2(m_pZipStream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
|
||||
|
||||
setStatus(Ok);
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
void ZipSubWStream::detachStream()
|
||||
{
|
||||
// Must finish...
|
||||
if (m_pZipStream != NULL)
|
||||
{
|
||||
m_pZipStream->avail_in = 0;
|
||||
deflate(m_pZipStream, Z_FINISH);
|
||||
|
||||
// write the remainder
|
||||
m_pStream->write(csm_bufferSize - m_pZipStream->avail_out, m_pOutputBuffer);
|
||||
|
||||
// close out zip stream...
|
||||
deflateEnd(m_pZipStream);
|
||||
|
||||
delete m_pZipStream;
|
||||
m_pZipStream = NULL;
|
||||
|
||||
delete [] m_pInputBuffer;
|
||||
delete [] m_pOutputBuffer;
|
||||
m_pInputBuffer = NULL;
|
||||
m_pOutputBuffer = NULL;
|
||||
}
|
||||
|
||||
m_pStream = NULL;
|
||||
m_currPosition = 0;
|
||||
setStatus(Closed);
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
Stream* ZipSubWStream::getStream()
|
||||
{
|
||||
return m_pStream;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
bool ZipSubWStream::_read(const U32, void*)
|
||||
{
|
||||
AssertFatal(false, "Cannot read from a ZipSubWStream");
|
||||
|
||||
setStatus(IllegalCall);
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
bool ZipSubWStream::_write(const U32 numBytes, const void *pBuffer)
|
||||
{
|
||||
if (numBytes == 0)
|
||||
return true;
|
||||
|
||||
AssertFatal(pBuffer != NULL, "NULL input buffer");
|
||||
if (getStatus() == Closed)
|
||||
{
|
||||
AssertFatal(false, "Attempted write to a closed stream");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_pZipStream->next_in = (U8*)pBuffer;
|
||||
m_pZipStream->avail_in = numBytes;
|
||||
|
||||
// write as many bufferSize chunks as possible
|
||||
while(m_pZipStream->avail_in != 0)
|
||||
{
|
||||
if(m_pZipStream->avail_out == 0)
|
||||
{
|
||||
if(!m_pStream->write(csm_bufferSize, m_pOutputBuffer))
|
||||
return(false);
|
||||
|
||||
m_pZipStream->next_out = m_pOutputBuffer;
|
||||
m_pZipStream->avail_out = csm_bufferSize;
|
||||
}
|
||||
|
||||
S32 retVal = deflate(m_pZipStream, Z_NO_FLUSH);
|
||||
AssertFatal(retVal != Z_BUF_ERROR, "ZipSubWStream::_write: invalid buffer");
|
||||
}
|
||||
|
||||
setStatus(Ok);
|
||||
m_currPosition += m_pZipStream->total_out;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
bool ZipSubWStream::hasCapability(const Capability in_cap) const
|
||||
{
|
||||
return (csm_streamCaps & U32(in_cap)) != 0;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
U32 ZipSubWStream::getPosition() const
|
||||
{
|
||||
AssertFatal(m_pStream != NULL, "Error, not attached");
|
||||
|
||||
return m_currPosition;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
bool ZipSubWStream::setPosition(const U32 /*in_newPosition*/)
|
||||
{
|
||||
AssertFatal(m_pStream != NULL, "Error, not attached");
|
||||
AssertFatal(false, "Not implemented!");
|
||||
|
||||
// Erk. How do we do this.
|
||||
return false;
|
||||
}
|
||||
|
||||
U32 ZipSubWStream::getStreamSize()
|
||||
{
|
||||
AssertFatal(false, "Undecided how to implement this!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
98
core/zipSubStream.h
Normal file
98
core/zipSubStream.h
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _ZIPSUBSTREAM_H_
|
||||
#define _ZIPSUBSTREAM_H_
|
||||
|
||||
//Includes
|
||||
#ifndef _FILTERSTREAM_H_
|
||||
#include "Core/filterStream.h"
|
||||
#endif
|
||||
|
||||
struct z_stream_s;
|
||||
|
||||
class ZipSubRStream : public FilterStream
|
||||
{
|
||||
typedef FilterStream Parent;
|
||||
static const U32 csm_streamCaps;
|
||||
static const U32 csm_inputBufferSize;
|
||||
|
||||
Stream* m_pStream;
|
||||
U32 m_uncompressedSize;
|
||||
U32 m_currentPosition;
|
||||
|
||||
z_stream_s* m_pZipStream;
|
||||
U8* m_pInputBuffer;
|
||||
|
||||
U32 m_originalSlavePosition;
|
||||
|
||||
U32 fillBuffer(const U32 in_attemptSize);
|
||||
|
||||
public:
|
||||
ZipSubRStream();
|
||||
virtual ~ZipSubRStream();
|
||||
|
||||
// Overrides of NFilterStream
|
||||
public:
|
||||
bool attachStream(Stream* io_pSlaveStream);
|
||||
void detachStream();
|
||||
Stream* getStream();
|
||||
|
||||
void setUncompressedSize(const U32);
|
||||
|
||||
// Mandatory overrides. By default, these are simply passed to
|
||||
// whatever is returned from getStream();
|
||||
protected:
|
||||
bool _read(const U32 in_numBytes, void* out_pBuffer);
|
||||
public:
|
||||
bool hasCapability(const Capability) const;
|
||||
|
||||
U32 getPosition() const;
|
||||
bool setPosition(const U32 in_newPosition);
|
||||
|
||||
U32 getStreamSize();
|
||||
};
|
||||
|
||||
class ZipSubWStream : public FilterStream
|
||||
{
|
||||
typedef FilterStream Parent;
|
||||
static const U32 csm_streamCaps;
|
||||
static const U32 csm_bufferSize;
|
||||
|
||||
Stream* m_pStream;
|
||||
z_stream_s* m_pZipStream;
|
||||
|
||||
U32 m_currPosition; // Indicates number of _uncompressed_ bytes written
|
||||
|
||||
U8* m_pOutputBuffer;
|
||||
U8* m_pInputBuffer;
|
||||
|
||||
public:
|
||||
ZipSubWStream();
|
||||
virtual ~ZipSubWStream();
|
||||
|
||||
// Overrides of NFilterStream
|
||||
public:
|
||||
bool attachStream(Stream* io_pSlaveStream);
|
||||
void detachStream();
|
||||
Stream* getStream();
|
||||
|
||||
// Mandatory overrides. By default, these are simply passed to
|
||||
// whatever is returned from getStream();
|
||||
protected:
|
||||
bool _read(const U32 in_numBytes, void* out_pBuffer);
|
||||
bool _write(const U32 in_numBytes, const void* in_pBuffer);
|
||||
public:
|
||||
bool hasCapability(const Capability) const;
|
||||
|
||||
U32 getPosition() const;
|
||||
bool setPosition(const U32 in_newPosition);
|
||||
|
||||
U32 getStreamSize();
|
||||
};
|
||||
|
||||
#endif //_ZIPSUBSTREAM_H_
|
||||
Loading…
Add table
Add a link
Reference in a new issue