Files
GTASource/rage/scaleform/Src/GFxPlayer/GFxStream.cpp

1106 lines
28 KiB
C++
Raw Normal View History

2025-02-23 17:40:52 +08:00
/**********************************************************************
Filename : GFxStream.cpp
Content : Byte/bit packed stream used for reading SWF files.
Created :
Authors :
Copyright : (c) 2001-2006 Scaleform Corp. All Rights Reserved.
Notes : wrapper class, for loading variable-length data from a
GFxStream, and keeping track of SWF tag boundaries.
Licensees may use this file in accordance with the valid Scaleform
Commercial License Agreement provided with the software.
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR ANY PURPOSE.
**********************************************************************/
#include "GFile.h"
#include "GFxStream.h"
#include "GFxLoader.h"
#include "GFxLog.h"
#include "GMsgFormat.h"
#include "GHeapNew.h"
#include "GFxStreamContext.h"
#include <string.h>
GFxStream::GFxStream(GFile* pinput, GMemoryHeap* pheap,
GFxLog *plog, GFxParseControl *pparseControl)
: FileName(pheap)
{
BufferSize = sizeof(BuiltinBuffer);
pBuffer = BuiltinBuffer;
Initialize(pinput, plog, pparseControl);
}
GFxStream::GFxStream(const UByte* pbuffer, UInt bufSize, GMemoryHeap* pheap,
GFxLog *plog, GFxParseControl *pparseControl)
: FileName(pheap)
{
pBuffer = const_cast<UByte*>(pbuffer);
if (pBuffer)
BufferSize = bufSize;
else
BufferSize = 0;
Initialize(NULL, plog, pparseControl);
DataSize = BufferSize;
FilePos = BufferSize;
}
GFxStream::~GFxStream()
{
}
void GFxStream::Initialize(GFile* pinput, GFxLog *plog, GFxParseControl *pparseControl)
{
pInput = pinput;
pLog = plog;
pParseControl = pparseControl;
ParseFlags = pparseControl ? pparseControl->GetParseFlags() : 0;
CurrentByte = 0;
UnusedBits = 0;
if (pinput)
FileName = pinput->GetFilePath();
else
FileName.Clear();
TagStackEntryCount = 0;
TagStack[0] = 0;
TagStack[1] = 0;
Pos = 0;
DataSize = 0;
ResyncFile = false;
FilePos = pinput ? pinput->Tell() : 0;
}
void GFxStream::ShutDown()
{
// Clear FileName since it contains allocation from the users heap.
FileName.Clear();
pInput = 0;
pLog = 0;
pParseControl = 0;
}
// Reads a bit-packed unsigned integer from the GFxStream
// and returns it. The given bitcount determines the
// number of bits to read.
UInt GFxStream::ReadUInt(UInt bitcount)
{
GASSERT(bitcount <= 32);
UInt32 value = 0;
int bitsNeeded = bitcount;
// RAGE - Added unusedBitsLocal to remove a bunch of LHSes
UByte currentByteLocal = CurrentByte;
UByte unusedBitsLocal = UnusedBits;
while (bitsNeeded > 0)
{
if (unusedBitsLocal)
{
if (bitsNeeded >= unusedBitsLocal)
{
// Consume all the unused bits.
value |= (currentByteLocal << (bitsNeeded - unusedBitsLocal));
bitsNeeded -= unusedBitsLocal;
unusedBitsLocal = 0;
}
else
{
int ubits = unusedBitsLocal - bitsNeeded;
// Consume some of the unused bits.
value |= (currentByteLocal >> ubits);
// mask off the bits we consumed.
currentByteLocal &= ((1 << ubits) - 1);
// We're done.
unusedBitsLocal = (UByte)ubits;
bitsNeeded = 0;
}
}
else
{
currentByteLocal = ReadU8();
unusedBitsLocal = 8;
}
}
CurrentByte = currentByteLocal;
UnusedBits = unusedBitsLocal;
GASSERT(bitsNeeded == 0);
return value;
}
// RAGE - Added this function for faster loading (no variable length shifts)
UInt GFxStream::ReadUInt4()
{
UInt32 value = 0;
UByte currentByteLocal = CurrentByte;
UByte unusedBitsLocal = UnusedBits;
UByte nextByte;
switch(unusedBitsLocal)
{
case 8:
value = currentByteLocal >> 4;
currentByteLocal &= 0xF;
unusedBitsLocal = 4;
break;
case 7:
value = currentByteLocal >> 3;
currentByteLocal &= 0x7;
unusedBitsLocal = 3;
break;
case 6:
value = currentByteLocal >> 2;
currentByteLocal &= 0x3;
unusedBitsLocal = 2;
break;
case 5:
value = currentByteLocal >> 1;
currentByteLocal &= 0x1;
unusedBitsLocal = 1;
break;
case 4:
value = currentByteLocal;
currentByteLocal = 0;
unusedBitsLocal = 0;
break;
case 3:
nextByte = ReadU8();
value = (currentByteLocal << 1) | (nextByte >> 7);
currentByteLocal = nextByte & 0x7f;
unusedBitsLocal = 7;
break;
case 2:
nextByte = ReadU8();
value = (currentByteLocal << 2) | (nextByte >> 6);
currentByteLocal = nextByte & 0x3f;
unusedBitsLocal = 6;
break;
case 1:
nextByte = ReadU8();
value = (currentByteLocal << 3) | (nextByte >> 5);
currentByteLocal = nextByte & 0x1f;
unusedBitsLocal = 5;
break;
case 0:
nextByte = ReadU8();
value = (nextByte >> 4);
currentByteLocal = nextByte & 0x0f;
unusedBitsLocal = 4;
break;
default:
FastAssert(0);
}
CurrentByte = currentByteLocal;
UnusedBits = unusedBitsLocal;
return value;
}
UInt GFxStream::ReadUInt1()
{
UInt32 value;
UByte currentByteLocal = CurrentByte;
UByte unusedBitsLocal = UnusedBits;
if (UnusedBits == 0)
{
currentByteLocal = ReadU8();
unusedBitsLocal = 7;
value = (currentByteLocal >> 7);
currentByteLocal &= ((1 << 7) - 1);
}
else
{
unusedBitsLocal--;
// Consume some of the unused bits.
value = (currentByteLocal >> unusedBitsLocal);
currentByteLocal &= ((1 << unusedBitsLocal) - 1);
}
CurrentByte = currentByteLocal;
UnusedBits = unusedBitsLocal;
return value;
}
// Reads a bit-packed little-endian signed integer
// from the GFxStream. The given bitcount determines the
// number of bits to read.
SInt GFxStream::ReadSInt(UInt bitcount)
{
GASSERT(bitcount <= 32);
SInt32 value = (SInt32) ReadUInt(bitcount);
// Sign extend...
if (value & (1 << (bitcount - 1)))
{
value |= -1 << bitcount;
}
return value;
}
// Makes data available in buffer. Stores zeros if not available.
bool GFxStream::PopulateBuffer(SInt size)
{
if (DataSize == 0)
{
// In case Underlying file position was changed.
if (pInput)
{
FilePos = pInput->Tell();
ResyncFile = false;
}
}
GASSERT(ResyncFile == false);
// move data to front
if (Pos < DataSize)
{
memmove(pBuffer, pBuffer + Pos, DataSize - Pos);
DataSize = DataSize - Pos;
Pos = 0;
}
else if (Pos >= DataSize)
{
GASSERT(Pos == DataSize);
DataSize = 0;
Pos = 0;
}
bool rv;
if (pInput)
{
SInt readSize = BufferSize - (SInt)DataSize;
SInt bytes = pInput->Read(pBuffer + DataSize, readSize);
if (bytes < readSize)
{
if (bytes > 0)
{
DataSize += (UInt) bytes;
FilePos += (UInt) bytes;
}
// Did not read enough. Set to zeros - error case.
memset(pBuffer + DataSize, 0, BufferSize - DataSize);
rv = (size <= (SInt)(DataSize - Pos)); //!AB: what if 'size' is greater than BufferSize???
if ((SInt)(DataSize - Pos) < size)
{
SInt extraBytes = size - (SInt)(DataSize - Pos);
// There is an attempt to read beyond the end-of-file. To avoid
// assertion we simulate reading of all zeros.
GFC_DEBUG_ERROR3(1, "Read error: attempt to read %d bytes beyond the EOF, file %s, filepos %d",
extraBytes, FileName.ToCStr(), (int)FilePos);
DataSize += (UInt) extraBytes;
}
}
else
{
DataSize += (UInt) bytes;
FilePos += (UInt) bytes;
rv = true;
}
}
else
{
// there is no file set to the stream - just
pBuffer = BuiltinBuffer;
BufferSize = sizeof(BuiltinBuffer);
memset(pBuffer, 0, BufferSize);
Pos = 0;
DataSize = BufferSize;
FilePos += DataSize;
rv = false;
}
GASSERT(DataSize <= BufferSize );
return rv;
}
bool GFxStream::PopulateBuffer1()
{
// Non-inline to simplify the call in ReadU8.
return PopulateBuffer(1);
}
// Reposition the underlying file to the desired location
void GFxStream::SyncFileStream()
{
SInt pos = pInput->Seek(Tell());
if (pos != -1)
{
Pos = 0;
FilePos = (UInt) pos;
DataSize = 0;
}
}
bool GFxStream::ReadString(GString* pstr)
{
Align();
// RAGE - try reading the string into a buffer on the stack first, to avoid having to do temporary allocations.
char c = 0;
const int localBufferSize = 1023;
char localBuffer[localBufferSize+1];
int bufIdx = 0;
while (bufIdx < localBufferSize && (c = ReadU8()) != 0)
{
localBuffer[bufIdx++] = c;
}
if (bufIdx < localBufferSize)
{
localBuffer[bufIdx] = '\0';
pstr->AssignString(localBuffer, bufIdx);
return true;
}
GArray<char> buffer;
for(int i = 0; i < bufIdx; i++)
{
buffer.PushBack(localBuffer[i]);
}
while ((c = ReadU8()) != 0)
buffer.PushBack(c);
buffer.PushBack(0);
if (buffer.GetSize() == 0)
{
pstr->Clear();
return false;
}
pstr->AssignString(&buffer[0], buffer.GetSize()-1);
return true;
}
bool GFxStream::ReadStringWithLength(GString* pstr)
{
// Use special initializer to avoid allocation copy.
struct StringReader : public GString::InitStruct
{
GFxStream* pStream;
StringReader(GFxStream* pstream) : pStream(pstream) { }
StringReader(StringReader&) { } // avoid warning
virtual void InitString(char* pbuffer, UPInt size) const
{
for (UPInt i = 0; i < size; i++)
pbuffer[i] = pStream->ReadU8();
}
};
Align();
int len = ReadU8();
if (len <= 0)
{
pstr->Clear();
return false;
}
// String data is read in from InitStrng callback.
StringReader sreader(this);
pstr->AssignString(sreader, len);
return true;
}
// Reads *and GALLOC()'s* the string from the given file.
// Ownership passes to the caller; caller must delete[] the
// string when it is done with it.
char* GFxStream::ReadString(GMemoryHeap* pheap)
{
Align();
char c;
GArray<char> buffer;
while ((c = ReadU8()) != 0)
buffer.PushBack(c);
buffer.PushBack(0);
if (!buffer.GetSize())
return 0;
char* retval = (char*)GHEAP_ALLOC(pheap, buffer.GetSize(), GStat_Default_Mem);
memcpy(retval, &buffer[0], buffer.GetSize());
return retval;
}
// Reads *and new[]'s* the string from the given file.
// Ownership passes to the caller; caller must delete[] the
// string when it is done with it.
char* GFxStream::ReadStringWithLength(GMemoryHeap* pheap)
{
Align();
int len = ReadU8();
if (len <= 0)
return 0;
char* buffer = (char*)GHEAP_ALLOC(pheap, len + 1, GStat_Default_Mem);
int i;
for (i = 0; i < len; i++)
buffer[i] = ReadU8();
buffer[i] = 0;
return buffer;
}
int GFxStream::ReadToBuffer(UByte* pdestBuf, UInt sz)
{
UInt bytes_read = 0;
if (DataSize == 0)
{
// In case Underlying file position was changed.
FilePos = pInput->Tell();
ResyncFile = false;
}
GASSERT(ResyncFile == false);
// move existing data to front of destination buffer
if (Pos < DataSize)
{
UInt szFromBuf = G_Min(sz, DataSize - Pos);
memmove(pdestBuf, pBuffer + Pos, szFromBuf);
Pos += szFromBuf;
sz -= szFromBuf;
pdestBuf += szFromBuf;
bytes_read += szFromBuf;
}
if (Pos >= DataSize)
{
DataSize = 0;
Pos = 0;
}
if (sz)
{
SInt bytes = pInput->Read(pdestBuf, (SInt)sz);
bytes_read += bytes;
FilePos += (UInt) bytes;
if (bytes < (SInt)sz)
{
// Did not read enough. Set to zeros - error case.
memset(pdestBuf + bytes, 0, sz - bytes);
}
}
return (int)bytes_read;
}
// Set the file position to the given value.
void GFxStream::SetPosition(int pos)
{
Align();
// If we're in a tag, make sure we're not seeking outside the tag.
if (TagStackEntryCount > 0)
{
GASSERT(pos <= GetTagEndPosition());
// @@ check start pos somehow???
}
if ((pos >= (int)(FilePos - DataSize) && (pos < (int)FilePos)))
{
// If data falls within the buffer, reposition the pointer.
// Note that if we are here we know that (DataSize != 0) because
// otherwise the (pos < (int)FilePos) condition would fail.
GASSERT((DataSize != 0) && (ResyncFile == false));
Pos = UInt(pos - FilePos + DataSize);
}
else
{
if (!ResyncFile && (Tell() == pos))
{
// If we are already at the right location, nothing to do.
// This may happen frequently if we have just read all data and
// are being repositioned to the same location, so just return.
GASSERT(FilePos == (UInt)pInput->Tell());
}
else if (pInput->Seek(pos) >= 0)
{
ResyncFile = false;
// MA DEBUG
//GFC_DEBUG_MESSAGE3(1, "GFxStream::SetPosition(%d) - seek out of buffer; Tell() == %d, BuffStart = %d",
// pos, Tell(), FilePos - DataSize);
Pos = 0;
DataSize = 0;
FilePos = pos;
}
}
}
// *** Tag handling.
// Return the tag type.
GFxTagType GFxStream::OpenTag(GFxTagInfo* pTagInfo)
{
Align();
int tagOffset = Tell();
int tagHeader = ReadU16();
int tagType = tagHeader >> 6;
int tagLength = tagHeader & 0x3F;
GASSERT(UnusedBits == 0);
if (tagLength == 0x3F)
tagLength = ReadS32();
pTagInfo->TagOffset = tagOffset;
pTagInfo->TagType = (GFxTagType)tagType;
pTagInfo->TagLength = tagLength;
pTagInfo->TagDataOffset = Tell();
if (IsVerboseParse())
LogParse("---------------Tag type = %d, Tag length = %d, offset = %d\n", tagType, tagLength, tagOffset);
// Remember where the end of the tag is, so we can
// fast-forward past it when we're done reading it.
GASSERT(TagStackEntryCount < Stream_TagStackSize);
TagStack[TagStackEntryCount] = Tell() + tagLength;
TagStackEntryCount++;
GASSERT((TagStackEntryCount == 1) ||
(TagStack[TagStackEntryCount-1] >= TagStack[TagStackEntryCount]) );
return (GFxTagType)tagType;
}
// Simplified OpenTag - no info reported back.
GFxTagType GFxStream::OpenTag()
{
Align();
int tagHeader = ReadU16();
int tagType = tagHeader >> 6;
int tagLength = tagHeader & 0x3F;
GASSERT(UnusedBits == 0);
if (tagLength == 0x3F)
tagLength = ReadS32();
if (IsVerboseParse())
LogParse("---------------Tag type = %d, Tag length = %d\n", tagType, tagLength);
// Remember where the end of the tag is, so we can
// fast-forward past it when we're done reading it.
GASSERT(TagStackEntryCount < Stream_TagStackSize);
TagStack[TagStackEntryCount] = Tell() + tagLength;
TagStackEntryCount++;
return (GFxTagType)tagType;
}
// Seek to the end of the most-recently-opened tag.
void GFxStream::CloseTag()
{
GASSERT(TagStackEntryCount > 0);
TagStackEntryCount--;
SetPosition(TagStack[TagStackEntryCount]);
UnusedBits = 0;
}
// Return the file position of the end of the current tag.
int GFxStream::GetTagEndPosition()
{
// Bounds check - assumes UInt.
GASSERT((TagStackEntryCount-1) < Stream_TagStackSize);
return ((TagStackEntryCount-1) < Stream_TagStackSize) ?
TagStack[TagStackEntryCount-1] : 0;
}
// *** Reading SWF data types
// Loading functions
void GFxStream::ReadMatrix(GRenderer::Matrix *pm)
{
Align();
pm->SetIdentity();
int hasScale = ReadUInt1();
if (hasScale)
{
UInt ScaleNbits = ReadUInt(5);
pm->M_[0][0] = ReadSInt(ScaleNbits) / 65536.0f;
pm->M_[1][1] = ReadSInt(ScaleNbits) / 65536.0f;
}
int hasRotate = ReadUInt1();
if (hasRotate)
{
UInt rotateNbits = ReadUInt(5);
pm->M_[1][0] = ReadSInt(rotateNbits) / 65536.0f;
pm->M_[0][1] = ReadSInt(rotateNbits) / 65536.0f;
}
int translateNbits = ReadUInt(5);
if (translateNbits > 0)
{
pm->M_[0][2] = (Float) ReadSInt(translateNbits);
pm->M_[1][2] = (Float) ReadSInt(translateNbits);
}
}
void GFxStream::ReadCxformRgb(GRenderer::Cxform *pcxform)
{
Align();
UInt hasAdd = ReadUInt1();
UInt hasMult = ReadUInt1();
UInt nbits = ReadUInt(4);
if (hasMult)
{
// The divisor value must be 256,
// since multiply factor 1.0 has value 0x100, not 0xFF
pcxform->M_[0][0] = ReadSInt(nbits) / 256.0f;
pcxform->M_[1][0] = ReadSInt(nbits) / 256.0f;
pcxform->M_[2][0] = ReadSInt(nbits) / 256.0f;
pcxform->M_[3][0] = 1.0f;
}
else
{
for (UInt i = 0; i < 4; i++) { pcxform->M_[i][0] = 1.0f; }
}
if (hasAdd)
{
pcxform->M_[0][1] = (Float) ReadSInt(nbits);
pcxform->M_[1][1] = (Float) ReadSInt(nbits);
pcxform->M_[2][1] = (Float) ReadSInt(nbits);
pcxform->M_[3][1] = 1.0f;
}
else
{
for (UInt i = 0; i < 4; i++) { pcxform->M_[i][1] = 0.0f; }
}
}
void GFxStream::ReadCxformRgba(GRenderer::Cxform *pcxform)
{
Align();
UInt hasAdd = ReadUInt1();
UInt hasMult = ReadUInt1();
UInt nbits = ReadUInt(4);
if (hasMult)
{
// The divisor value must be 256,
// since multiply factor 1.0 has value 0x100, not 0xFF
pcxform->M_[0][0] = ReadSInt(nbits) / 256.0f;
pcxform->M_[1][0] = ReadSInt(nbits) / 256.0f;
pcxform->M_[2][0] = ReadSInt(nbits) / 256.0f;
pcxform->M_[3][0] = ReadSInt(nbits) / 256.0f;
}
else
{
for (int i = 0; i < 4; i++) { pcxform->M_[i][0] = 1.0f; }
}
if (hasAdd)
{
pcxform->M_[0][1] = (Float) ReadSInt(nbits);
pcxform->M_[1][1] = (Float) ReadSInt(nbits);
pcxform->M_[2][1] = (Float) ReadSInt(nbits);
pcxform->M_[3][1] = (Float) ReadSInt(nbits);
}
else
{
for (int i = 0; i < 4; i++) { pcxform->M_[i][1] = 0.0f; }
}
}
void GFxStream::ReadRect(GRectF *pr)
{
Align();
int nbits = ReadUInt(5);
pr->Left = (Float) ReadSInt(nbits);
pr->Right = (Float) ReadSInt(nbits);
pr->Top = (Float) ReadSInt(nbits);
pr->Bottom = (Float) ReadSInt(nbits);
}
void GFxStream::ReadPoint(GPointF *ppt)
{
GUNUSED(ppt);
GFC_DEBUG_ERROR(1, "GFxStream::ReadPoint not implemented");
}
// Color
void GFxStream::ReadRgb(GColor *pc)
{
// Read: R, G, B
pc->SetRed(ReadU8());
pc->SetGreen(ReadU8());
pc->SetBlue(ReadU8());
pc->SetAlpha(0xFF);
}
void GFxStream::ReadRgba(GColor *pc)
{
// Read: RGB, A
ReadRgb(pc);
pc->SetAlpha(ReadU8());
}
// GFxLogBase impl
GFxLog* GFxStream::GetLog() const
{ return pLog; }
bool GFxStream::IsVerboseParse() const
{ return (ParseFlags & GFxParseControl::VerboseParse) != 0; }
bool GFxStream::IsVerboseParseShape() const
{ return (ParseFlags & GFxParseControl::VerboseParseShape) != 0; }
bool GFxStream::IsVerboseParseMorphShape() const
{ return (ParseFlags & GFxParseControl::VerboseParseMorphShape) != 0; }
bool GFxStream::IsVerboseParseAction() const
{ return (ParseFlags & GFxParseControl::VerboseParseAction) != 0; }
// *** Helper functions to log contents or required types
#ifndef GFC_NO_FXPLAYER_VERBOSE_PARSE
#define GFX_STREAM_BUFFER_SIZE 512
void GFxStream::LogParseClass(GColor color)
{
char buff[GFX_STREAM_BUFFER_SIZE];
color.Format(buff);
LogParse("%s", buff);
}
void GFxStream::LogParseClass(const GRectF &rc)
{
char buff[GFX_STREAM_BUFFER_SIZE];
G_sprintf(buff, GFX_STREAM_BUFFER_SIZE,
"xmin = %g, ymin = %g, xmax = %g, ymax = %g\n",
TwipsToPixels(rc.Left),
TwipsToPixels(rc.Top),
TwipsToPixels(rc.Right),
TwipsToPixels(rc.Bottom));
//G_Format(
// GStringDataPtr(buff, sizeof(buff)),
// "xmin = {0:g}, ymin = {1:g}, xmax = {2:g}, ymax = {3:g}\n",
// TwipsToPixels(rc.Left),
// TwipsToPixels(rc.Top),
// TwipsToPixels(rc.Right),
// TwipsToPixels(rc.Bottom)
// );
LogParse("%s", buff);
}
void GFxStream::LogParseClass(const GRenderer::Cxform &cxform)
{
char buff[GFX_STREAM_BUFFER_SIZE];
cxform.Format(buff);
LogParse("%s", buff);
}
void GFxStream::LogParseClass(const GRenderer::Matrix &matrix)
{
char buff[GFX_STREAM_BUFFER_SIZE];
matrix.Format(buff);
LogParse("%s", buff);
}
// Log the contents of the current tag, in hex.
void GFxStream::LogTagBytes()
{
static const int ROW_BYTES = 16;
char rowBuf[ROW_BYTES];
int rowCount = 0;
while(Tell() < GetTagEndPosition())
{
UInt c = ReadU8();
LogParse("%02X", c);
if (c < 32) c = '.';
if (c > 127) c = '.';
rowBuf[rowCount] = (UByte)c;
rowCount++;
if (rowCount >= ROW_BYTES)
{
LogParse(" ");
for (int i = 0; i < ROW_BYTES; i++)
{
LogParse("%c", rowBuf[i]);
}
LogParse("\n");
rowCount = 0;
}
else
{
LogParse(" ");
}
}
if (rowCount > 0)
{
LogParse("\n");
}
}
#endif //GFC_NO_FXPLAYER_VERBOSE_PARSE
UInt GFxStreamContext::ReadUInt(UInt bitcount)
{
static const UInt8 bytesInBits[] = { 0,1,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,
3,3,3,3,3,3,3,3,
4,4,4,4,4,4,4,4 };
UInt v;
UInt mask = ((1 << (8 - CurBitIndex)) - 1);
UInt rshift;
UInt lastBitIndex = CurBitIndex + bitcount;
GASSERT(bitcount < sizeof(bytesInBits)/sizeof(bytesInBits[0]));
switch(bytesInBits[bitcount])
{
case 0: return 0;
case 1: // 1 octet 1..8 bits
if (lastBitIndex <= 8) // whole value fits in 1 bytes
{
v = pData[CurByteIndex] & mask;
rshift = (8 - lastBitIndex);
}
else
{
// assemble a preliminary value using 2 bytes
// and init rshift accordingly
v = (UInt(pData[CurByteIndex] & mask) << 8) |
UInt(pData[CurByteIndex + 1]);
++CurByteIndex;
rshift = (16 - lastBitIndex);
}
break;
case 2: // 2 octets 9..16 bits
if (lastBitIndex <= 16) // whole value fits in 2 bytes
{
v = ((UInt(pData[CurByteIndex]) & mask) << 8) | UInt(pData[CurByteIndex + 1]);
++CurByteIndex;
rshift = (16 - lastBitIndex);
}
else
{
// assemble a preliminary value using 3 bytes
// and init rshift accordingly
v = (UInt(pData[CurByteIndex] & mask) << 16) |
(UInt(pData[CurByteIndex + 1]) << 8) |
UInt(pData[CurByteIndex + 2]);
CurByteIndex += 2;
rshift = (24 - lastBitIndex);
}
break;
case 3: // 3 octets 17..24 bits
if (lastBitIndex <= 24) // whole value fits in 3 bytes
{
v = (UInt(pData[CurByteIndex] & mask) << 16) |
(UInt(pData[CurByteIndex + 1]) << 8) |
UInt(pData[CurByteIndex + 2]);
CurByteIndex += 2;
rshift = (24 - lastBitIndex);
}
else
{
// assemble a preliminary value using 4 bytes
// and init rshift accordingly
v = (UInt(pData[CurByteIndex] & mask) << 24) |
(UInt(pData[CurByteIndex + 1]) << 16) |
(UInt(pData[CurByteIndex + 2]) << 8) |
UInt(pData[CurByteIndex + 3]);
CurByteIndex += 3;
rshift = (32 - lastBitIndex);
}
break;
case 4: // 4 octets 25..32 bits
if (lastBitIndex <= 32) // whole value fits in 4 bytes
{
v = (UInt(pData[CurByteIndex] & mask) << 24) |
(UInt(pData[CurByteIndex + 1]) << 16) |
(UInt(pData[CurByteIndex + 2]) << 8) |
UInt(pData[CurByteIndex + 3]);
CurByteIndex += 3;
rshift = (32 - lastBitIndex);
}
else
{
// assemble a preliminary value using 4 bytes
v = (UInt(pData[CurByteIndex] & mask) << 24) |
(UInt(pData[CurByteIndex + 1]) << 16) |
(UInt(pData[CurByteIndex + 2]) << 8) |
UInt(pData[CurByteIndex + 3]);
CurByteIndex += 4;
// need to read 5th byte and take "extraBits" bits from it
UInt extraBits = (lastBitIndex - 32);
v <<= extraBits;
v |= UInt(pData[CurByteIndex]) >> (8 - extraBits);
// value is already shifted correctly.
// just modify CurBitIndex and return
CurBitIndex = extraBits;
GASSERT(CurBitIndex < 8);
return v;
}
break;
default: GASSERT(0); v = 0; rshift = 0; // only up to 32-bits values supported
}
if (rshift)
{
v >>= rshift;
CurBitIndex = 8 - rshift;
}
else
{
++CurByteIndex;
CurBitIndex = 0;
}
GASSERT(CurBitIndex < 8);
return v;
}
void GFxStreamContext::ReadMatrix(GRenderer::Matrix *pm)
{
Align();
pm->SetIdentity();
int hasScale = ReadUInt1();
if (hasScale)
{
UInt ScaleNbits = ReadUInt(5);
pm->M_[0][0] = ReadSInt(ScaleNbits) / 65536.0f;
pm->M_[1][1] = ReadSInt(ScaleNbits) / 65536.0f;
}
int hasRotate = ReadUInt1();
if (hasRotate)
{
UInt rotateNbits = ReadUInt(5);
pm->M_[1][0] = ReadSInt(rotateNbits) / 65536.0f;
pm->M_[0][1] = ReadSInt(rotateNbits) / 65536.0f;
}
int translateNbits = ReadUInt(5);
if (translateNbits > 0)
{
pm->M_[0][2] = (Float) ReadSInt(translateNbits);
pm->M_[1][2] = (Float) ReadSInt(translateNbits);
}
}
void GFxStreamContext::ReadCxformRgb(GRenderer::Cxform *pcxform)
{
Align();
UInt hasAdd = ReadUInt1();
UInt hasMult = ReadUInt1();
UInt nbits = ReadUInt(4);
if (hasMult)
{
// The divisor value must be 256,
// since multiply factor 1.0 has value 0x100, not 0xFF
pcxform->M_[0][0] = ReadSInt(nbits) / 256.0f;
pcxform->M_[1][0] = ReadSInt(nbits) / 256.0f;
pcxform->M_[2][0] = ReadSInt(nbits) / 256.0f;
pcxform->M_[3][0] = 1.0f;
}
else
{
for (UInt i = 0; i < 4; i++) { pcxform->M_[i][0] = 1.0f; }
}
if (hasAdd)
{
pcxform->M_[0][1] = (Float) ReadSInt(nbits);
pcxform->M_[1][1] = (Float) ReadSInt(nbits);
pcxform->M_[2][1] = (Float) ReadSInt(nbits);
pcxform->M_[3][1] = 1.0f;
}
else
{
for (UInt i = 0; i < 4; i++) { pcxform->M_[i][1] = 0.0f; }
}
}
void GFxStreamContext::ReadCxformRgba(GRenderer::Cxform *pcxform)
{
Align();
UInt hasAdd = ReadUInt1();
UInt hasMult = ReadUInt1();
UInt nbits = ReadUInt(4);
if (hasMult)
{
// The divisor value must be 256,
// since multiply factor 1.0 has value 0x100, not 0xFF
pcxform->M_[0][0] = ReadSInt(nbits) / 256.0f;
pcxform->M_[1][0] = ReadSInt(nbits) / 256.0f;
pcxform->M_[2][0] = ReadSInt(nbits) / 256.0f;
pcxform->M_[3][0] = ReadSInt(nbits) / 256.0f;
}
else
{
for (int i = 0; i < 4; i++) { pcxform->M_[i][0] = 1.0f; }
}
if (hasAdd)
{
pcxform->M_[0][1] = (Float) ReadSInt(nbits);
pcxform->M_[1][1] = (Float) ReadSInt(nbits);
pcxform->M_[2][1] = (Float) ReadSInt(nbits);
pcxform->M_[3][1] = (Float) ReadSInt(nbits);
}
else
{
for (int i = 0; i < 4; i++) { pcxform->M_[i][1] = 0.0f; }
}
}