t2 engine svn checkout

This commit is contained in:
loop 2024-01-07 04:36:33 +00:00
commit ff569bd2ae
988 changed files with 394180 additions and 0 deletions

137
core/bitMatrix.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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_

View 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;
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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_