mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-23 18:24:59 +00:00
b9065aa2d2
svn-id: r49437
582 lines
16 KiB
C++
582 lines
16 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
#include "m4/assets.h"
|
|
#include "m4/globals.h"
|
|
#include "m4/compression.h"
|
|
#include "m4/graphics.h"
|
|
|
|
namespace M4 {
|
|
|
|
BaseAsset::BaseAsset(MadsM4Engine *vm) : _vm(vm) {
|
|
}
|
|
|
|
BaseAsset::~BaseAsset() {
|
|
}
|
|
|
|
MachineAsset::MachineAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm) {
|
|
uint32 stateCount = stream->readUint32LE();
|
|
for (uint32 curState = 0; curState < stateCount; curState++) {
|
|
uint32 stateOffset = stream->readUint32LE();
|
|
_stateTable.push_back(stateOffset);
|
|
}
|
|
_codeSize = size - 4 - 4 * stateCount;
|
|
_code = new byte[_codeSize];
|
|
stream->read(_code, _codeSize);
|
|
}
|
|
|
|
MachineAsset::~MachineAsset() {
|
|
delete[] _code;
|
|
}
|
|
|
|
void MachineAsset::getCode(byte *&code, uint32 &codeSize) {
|
|
code = _code;
|
|
codeSize = _codeSize;
|
|
}
|
|
|
|
uint32 MachineAsset::getStateOffset(uint32 state) {
|
|
assert(state < _stateTable.size());
|
|
return _stateTable[state];
|
|
}
|
|
|
|
SequenceAsset::SequenceAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm) {
|
|
_localVarCount = stream->readUint32LE();
|
|
_codeSize = size - 4;
|
|
_code = new byte[_codeSize];
|
|
stream->read(_code, _codeSize);
|
|
}
|
|
|
|
SequenceAsset::~SequenceAsset() {
|
|
delete[] _code;
|
|
}
|
|
|
|
void SequenceAsset::getCode(byte *&code, uint32 &codeSize) {
|
|
code = _code;
|
|
codeSize = _codeSize;
|
|
}
|
|
|
|
|
|
DataAsset::DataAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm) {
|
|
|
|
_recCount = stream->readUint32LE();
|
|
_recSize = stream->readUint32LE();
|
|
_dataSize = _recCount * _recSize;
|
|
_data = new long[_dataSize];
|
|
for (uint32 i = 0; i < _dataSize; i++)
|
|
_data[i] = (long)stream->readUint32LE();
|
|
|
|
}
|
|
|
|
DataAsset::~DataAsset() {
|
|
delete _data;
|
|
}
|
|
|
|
long *DataAsset::getRow(int index) {
|
|
assert(index < _recCount);
|
|
return &_data[_recSize * index];
|
|
}
|
|
|
|
SpriteAsset::SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name, bool asStream) :
|
|
BaseAsset(vm) {
|
|
_stream = stream;
|
|
_palInterface = NULL;
|
|
_paletteData = NULL;
|
|
|
|
if (_vm->isM4()) {
|
|
loadM4SpriteAsset(vm, stream, asStream);
|
|
} else {
|
|
loadMadsSpriteAsset(vm, stream);
|
|
}
|
|
}
|
|
|
|
SpriteAsset::SpriteAsset(MadsM4Engine *vm, const char *name): BaseAsset(vm) {
|
|
_stream = vm->res()->get(name);
|
|
_palInterface = NULL;
|
|
_paletteData = NULL;
|
|
|
|
if (_vm->isM4()) {
|
|
loadM4SpriteAsset(vm, _stream, true);
|
|
} else {
|
|
loadMadsSpriteAsset(vm, _stream);
|
|
}
|
|
|
|
vm->res()->toss(name);
|
|
}
|
|
|
|
SpriteAsset::~SpriteAsset() {
|
|
if (_palInterface) {
|
|
// Internally stored palette translation data, so release it
|
|
_palInterface->deleteRange(_paletteData);
|
|
delete _paletteData;
|
|
}
|
|
|
|
// Delete the individual frames
|
|
for (Common::Array<SpriteAssetFrame>::iterator it = _frames.begin(); it != _frames.end(); ++it) {
|
|
delete (*it).frame;
|
|
}
|
|
}
|
|
|
|
void SpriteAsset::loadM4SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, bool asStream) {
|
|
bool isBigEndian = false;
|
|
uint32 frameOffset;
|
|
|
|
uint32 header = _stream->readUint32LE();
|
|
if (header == HEAD_M4SS) {
|
|
printf("LE-encoded sprite\n");
|
|
} else {
|
|
printf("BE-encoded sprite\n");
|
|
isBigEndian = true;
|
|
}
|
|
|
|
_srcSize = parseSprite(isBigEndian);
|
|
|
|
_stream->readUint32LE();
|
|
_frameRate = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
|
_pixelSpeed = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
|
_maxWidth = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
|
_maxHeight = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
|
_stream->skip(6 * 4);
|
|
_frameCount = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
|
|
|
printf("SpriteAsset::SpriteAsset() srcSize = %d; frameRate = %04X; pixelSpeed = %04X; maxWidth = %d; maxHeight = %d; frameCount = %d\n", _srcSize, _frameRate, _pixelSpeed, _maxWidth, _maxHeight, _frameCount);
|
|
|
|
for (int curFrame = 0; curFrame < _frameCount; curFrame++) {
|
|
frameOffset = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
|
_frameOffsets.push_back(frameOffset);
|
|
}
|
|
_frameOffsets.push_back(_srcSize - 48 - _frameCount * 4);
|
|
|
|
_frameStartOffset = _stream->pos();
|
|
|
|
// We don't need to load frames when streaming
|
|
if (asStream)
|
|
return;
|
|
|
|
for (int curFrame = 0; curFrame < _frameCount; curFrame++) {
|
|
frameOffset = _frameStartOffset + _frameOffsets[curFrame];
|
|
_stream->seek(frameOffset);
|
|
|
|
SpriteAssetFrame frame;
|
|
loadFrameHeader(frame, isBigEndian);
|
|
|
|
// Load & unpack RLE data if it's not a streaming animation
|
|
if (frame.stream != 1) {
|
|
|
|
frame.frame = new M4Sprite(stream, frame.x, frame.y, frame.w, frame.h, true, frame.comp);
|
|
#if 0
|
|
char fn[512];
|
|
sprintf(fn, "%04d.raw", curFrame);
|
|
FILE *h = fopen(fn, "wb");
|
|
fwrite((byte*)frame.frame->getBasePtr(), frame.w * frame.h, 1, h);
|
|
fclose(h);
|
|
#endif
|
|
}
|
|
|
|
_frames.push_back(frame);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void SpriteAsset::loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream) {
|
|
int curFrame = 0;
|
|
uint32 frameOffset = 0;
|
|
MadsPack sprite(stream);
|
|
_frameRate = 0;
|
|
_pixelSpeed = 0;
|
|
_maxWidth = 0;
|
|
_maxHeight = 0;
|
|
|
|
Common::SeekableReadStream *spriteStream = sprite.getItemStream(0);
|
|
_mode = spriteStream->readByte();
|
|
spriteStream->skip(1);
|
|
int type1 = spriteStream->readUint16LE();
|
|
int type2 = spriteStream->readUint16LE();
|
|
_isBackground = (type1 != 0) && (type2 < 4);
|
|
spriteStream->skip(32);
|
|
_frameCount = spriteStream->readUint16LE();
|
|
// we skip the rest of the data
|
|
delete spriteStream;
|
|
|
|
// Get the palette data
|
|
spriteStream = sprite.getItemStream(2);
|
|
int numColors = 0;
|
|
RGB8 *palData = Palette::decodeMadsPalette(spriteStream, &numColors);
|
|
Common::copy(palData, &palData[numColors], &_palette[0]);
|
|
if (numColors < 256)
|
|
Common::set_to((byte *)&_palette[numColors], (byte *)&_palette[256], 0);
|
|
_colorCount = numColors;
|
|
delete[] palData;
|
|
delete spriteStream;
|
|
|
|
spriteStream = sprite.getItemStream(1);
|
|
Common::SeekableReadStream *spriteDataStream = sprite.getItemStream(3);
|
|
SpriteAssetFrame frame;
|
|
for (curFrame = 0; curFrame < _frameCount; curFrame++) {
|
|
frame.stream = 0;
|
|
frame.comp = 0;
|
|
frameOffset = spriteStream->readUint32LE();
|
|
_frameOffsets.push_back(frameOffset);
|
|
spriteStream->readUint32LE(); // frame size
|
|
frame.x = spriteStream->readUint16LE();
|
|
frame.y = spriteStream->readUint16LE();
|
|
frame.w = spriteStream->readUint16LE();
|
|
frame.h = spriteStream->readUint16LE();
|
|
if (curFrame == 0)
|
|
debugC(1, kDebugGraphics, "%i frames, x = %i, y = %i, w = %i, h = %i\n", _frameCount, frame.x, frame.y, frame.w, frame.h);
|
|
|
|
frame.frame = new M4Sprite(spriteDataStream, frame.x, frame.y, frame.w, frame.h, false);
|
|
_frames.push_back(frame);
|
|
}
|
|
delete spriteStream;
|
|
delete spriteDataStream;
|
|
}
|
|
|
|
int32 SpriteAsset::parseSprite(bool isBigEndian) {
|
|
|
|
uint32 format, chunkType, chunkSize = 0;
|
|
|
|
_colorCount = 0;
|
|
|
|
format = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
|
|
|
chunkType = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
|
|
|
if (chunkType == CELS__PAL) {
|
|
chunkSize = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
|
uint32 numColors = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
|
// TODO
|
|
//if (palette) {
|
|
// TODO: A sprite set palette specifies the indexes, which need not start at
|
|
// index 0. For now, I'm simply preloading the currently active palette
|
|
// before starting to replace existing entries
|
|
|
|
_vm->_palette->grabPalette((byte *) _palette, 0, 256);
|
|
_colorCount = 0;
|
|
|
|
for (uint32 i = 0; i < numColors; i++) {
|
|
uint32 paletteEntry = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
|
uint index = (paletteEntry >> 24) & 0xFF;
|
|
|
|
_palette[index].r = ((paletteEntry >> 16) & 0xFF) << 2;
|
|
_palette[index].g = ((paletteEntry >> 8) & 0xFF) << 2;
|
|
_palette[index].b = (paletteEntry & 0xFF) << 2;
|
|
|
|
_colorCount = MAX<int>(_colorCount, index);
|
|
}
|
|
|
|
/*
|
|
} else {
|
|
stream.seek(colorCount, )
|
|
data += colorCount * 4;
|
|
}
|
|
*/
|
|
chunkType = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
|
}
|
|
|
|
if (chunkType == CELS___SS) {
|
|
chunkSize = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
|
} else {
|
|
warning("SpriteAsset::parseSprite() Expected chunk type %08X, got %08X", CELS___SS, chunkType);
|
|
}
|
|
|
|
return chunkSize;
|
|
|
|
}
|
|
|
|
void SpriteAsset::loadFrameHeader(SpriteAssetFrame &frameHeader, bool isBigEndian) {
|
|
_stream->readUint32LE();
|
|
frameHeader.stream = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
|
frameHeader.x = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
|
frameHeader.y = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
|
frameHeader.w = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
|
frameHeader.h = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
|
frameHeader.comp = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
|
frameHeader.frame = NULL;
|
|
_stream->seek(8 * 4, SEEK_CUR);
|
|
//printf("SpriteAsset::loadFrameHeader() stream = %d; x = %d; y = %d; w = %d; h = %d; comp = %d\n", frameHeader.stream, frameHeader.x, frameHeader.y, frameHeader.w, frameHeader.h, frameHeader.comp);
|
|
}
|
|
|
|
M4Sprite *SpriteAsset::getFrame(int frameIndex) {
|
|
return _frames[frameIndex].frame;
|
|
}
|
|
|
|
void SpriteAsset::loadStreamingFrame(M4Sprite *frame, int frameIndex, int destX, int destY) {
|
|
uint32 frameOffset = _frameStartOffset + _frameOffsets[frameIndex];
|
|
_stream->seek(frameOffset);
|
|
|
|
SpriteAssetFrame frameHeader;
|
|
loadFrameHeader(frameHeader);
|
|
|
|
if (frameHeader.w > 0 && frameHeader.h > 0) {
|
|
Common::MemoryReadStream *frameData = _stream->readStream(getFrameSize(frameIndex));
|
|
if (frameHeader.stream) {
|
|
frame->loadDeltaRle(frameData, destX - frameHeader.x, destY - frameHeader.y);
|
|
} else {
|
|
frame->loadRle(frameData);
|
|
}
|
|
delete frameData;
|
|
}
|
|
|
|
}
|
|
|
|
RGBList *SpriteAsset::getRgbList() {
|
|
RGBList *result = new RGBList(_colorCount);
|
|
Common::copy((byte *)&_palette[0], (byte *)&_palette[_colorCount], (byte *)result->data());
|
|
return result;
|
|
}
|
|
|
|
void SpriteAsset::translate(RGBList *list, bool isTransparent) {
|
|
for (int frameIndex = 0; frameIndex < _frameCount; ++frameIndex)
|
|
_frames[frameIndex].frame->translate(list, isTransparent);
|
|
}
|
|
|
|
void SpriteAsset::translate(Palette *palette) {
|
|
_palInterface = palette;
|
|
_paletteData = this->getRgbList();
|
|
palette->addRange(_paletteData);
|
|
this->translate(_paletteData, true);
|
|
}
|
|
|
|
|
|
int32 SpriteAsset::getFrameSize(int index) {
|
|
/*
|
|
if (index + 1 == _frameCount) {
|
|
} else {
|
|
|
|
}
|
|
*/
|
|
return _frameOffsets[index + 1] - _frameOffsets[index];
|
|
}
|
|
|
|
AssetManager::AssetManager(MadsM4Engine *vm) {
|
|
|
|
_vm = vm;
|
|
|
|
/* Initialize asset arrays */
|
|
for (int i = 0; i < 256; i++) {
|
|
_MACH[i] = NULL;
|
|
_SEQU[i] = NULL;
|
|
_DATA[i] = NULL;
|
|
_CELS[i] = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
AssetManager::~AssetManager() {
|
|
// unload all remaining assets
|
|
clearAssets(kAssetTypeMACH, 0, 255);
|
|
clearAssets(kAssetTypeSEQU, 0, 255);
|
|
clearAssets(kAssetTypeCELS, 0, 255);
|
|
clearAssets(kAssetTypeDATA, 0, 255);
|
|
}
|
|
|
|
bool AssetManager::clearAssets(AssetType assetType, int32 minHash, int32 maxHash) {
|
|
|
|
minHash = MAX<int>(0, minHash);
|
|
maxHash = MIN<int>(maxHash, 255);
|
|
|
|
switch (assetType) {
|
|
case kAssetTypeMACH:
|
|
for (int i = minHash; i <= maxHash; i++)
|
|
if (_MACH[i]) {
|
|
delete _MACH[i];
|
|
_MACH[i] = NULL;
|
|
}
|
|
break;
|
|
case kAssetTypeSEQU:
|
|
for (int i = minHash; i <= maxHash; i++)
|
|
if (_SEQU[i]) {
|
|
delete _SEQU[i];
|
|
_SEQU[i] = NULL;
|
|
}
|
|
break;
|
|
case kAssetTypeDATA:
|
|
for (int i = minHash; i <= maxHash; i++)
|
|
if (_DATA[i]) {
|
|
delete _DATA[i];
|
|
_DATA[i] = NULL;
|
|
}
|
|
break;
|
|
case kAssetTypeCELS:
|
|
for (int i = minHash; i <= maxHash; i++)
|
|
if (_CELS[i]) {
|
|
delete _CELS[i];
|
|
_CELS[i] = NULL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// FIXME: no value is returned, returning true for now
|
|
return true;
|
|
}
|
|
|
|
bool AssetManager::loadAsset(const char *assetName, RGB8 *palette) {
|
|
|
|
printf("AssetManager::loadAsset() %s\n", assetName);
|
|
|
|
// TODO, better use MemoryReadStreamEndian?
|
|
//convertAssetToLE(assetData, assetSize);
|
|
|
|
Common::SeekableReadStream *assetS = _vm->res()->get(assetName);
|
|
|
|
while (assetS->pos() + 12 < assetS->size()) {
|
|
uint32 chunkType, chunkSize, chunkHash;
|
|
|
|
chunkType = assetS->readUint32LE();
|
|
chunkSize = assetS->readUint32LE() - 12; // sub 12 for the chunk header
|
|
chunkHash = assetS->readUint32LE();
|
|
|
|
printf("hash = %d\n", chunkHash);
|
|
|
|
// Until loading code is complete, so that chunks not fully read are skipped correctly
|
|
uint32 nextOfs = assetS->pos() + chunkSize;
|
|
|
|
switch (chunkType) {
|
|
case CHUNK_MACH:
|
|
printf("MACH\n");
|
|
clearAssets(kAssetTypeMACH, chunkHash, chunkHash);
|
|
_MACH[chunkHash] = new MachineAsset(_vm, assetS, chunkSize, assetName);
|
|
break;
|
|
case CHUNK_SEQU:
|
|
printf("SEQU\n");
|
|
clearAssets(kAssetTypeSEQU, chunkHash, chunkHash);
|
|
_SEQU[chunkHash] = new SequenceAsset(_vm, assetS, chunkSize, assetName);
|
|
break;
|
|
case CHUNK_DATA:
|
|
printf("DATA\n");
|
|
clearAssets(kAssetTypeDATA, chunkHash, chunkHash);
|
|
_DATA[chunkHash] = new DataAsset(_vm, assetS, chunkSize, assetName);
|
|
break;
|
|
case CHUNK_CELS:
|
|
printf("CELS\n");
|
|
clearAssets(kAssetTypeCELS, chunkHash, chunkHash);
|
|
_CELS[chunkHash] = new SpriteAsset(_vm, assetS, chunkSize, assetName);
|
|
break;
|
|
default:
|
|
printf("AssetManager::loadAsset() Unknown chunk type %08X\n", chunkType);
|
|
}
|
|
|
|
// Until loading code is complete (see above)
|
|
assetS->seek(nextOfs);
|
|
|
|
}
|
|
|
|
_vm->res()->toss(assetName);
|
|
|
|
// FIXME: no value is returned, returning true for now
|
|
return true;
|
|
}
|
|
|
|
int32 AssetManager::addSpriteAsset(const char *assetName, int32 hash, RGB8 *palette) {
|
|
|
|
bool alreadyLoaded = false;
|
|
|
|
if (hash < 0) {
|
|
for (int i = 0; i <= 255; i++) {
|
|
if (_CELS[i] != NULL) {
|
|
if (_CELS[i]->getName() == assetName) {
|
|
alreadyLoaded = true;
|
|
hash = i;
|
|
break;
|
|
}
|
|
} else {
|
|
hash = i;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
alreadyLoaded = _CELS[hash] != NULL && _CELS[hash]->getName() == assetName;
|
|
}
|
|
|
|
/* Not loaded and no empty slots */
|
|
if (hash < 0)
|
|
return -1;
|
|
|
|
if (!alreadyLoaded) {
|
|
|
|
printf("AssetManager::addSpriteAsset() asset %s not loaded, loading into %d\n", assetName, hash);
|
|
|
|
clearAssets(kAssetTypeCELS, hash, hash);
|
|
|
|
Common::SeekableReadStream *assetS = _vm->res()->get(assetName);
|
|
_CELS[hash] = new SpriteAsset(_vm, assetS, assetS->size(), assetName);
|
|
_vm->res()->toss(assetName);
|
|
|
|
} else {
|
|
|
|
printf("AssetManager::addSpriteAsset() asset %s already loaded in %d\n", assetName, hash);
|
|
|
|
/* TODO/FIXME
|
|
if (_CELS[hash].palOffset >= 0 && palette)
|
|
restorePalette(palette, _CELS[hash].data + _CELS[hash].palOffset);
|
|
*/
|
|
|
|
}
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
void AssetManager::restorePalette(RGB8 *palette, byte *data) {
|
|
// TODO
|
|
}
|
|
|
|
void AssetManager::convertAssetToLE(byte *assetData, uint32 assetSize) {
|
|
|
|
}
|
|
|
|
MachineAsset *AssetManager::getMachine(int32 hash) {
|
|
assert(_MACH[hash] != NULL);
|
|
return _MACH[hash];
|
|
}
|
|
|
|
SequenceAsset *AssetManager::getSequence(int32 hash) {
|
|
assert(_SEQU[hash] != NULL);
|
|
return _SEQU[hash];
|
|
}
|
|
|
|
DataAsset *AssetManager::getData(int32 hash) {
|
|
assert(_DATA[hash] != NULL);
|
|
return _DATA[hash];
|
|
}
|
|
|
|
SpriteAsset *AssetManager::getSprite(int32 hash) {
|
|
assert(_CELS[hash] != NULL);
|
|
return _CELS[hash];
|
|
}
|
|
|
|
M4Sprite *AssetManager::getSpriteFrame(int32 hash, int frameIndex) {
|
|
assert(_CELS[hash] != NULL);
|
|
return _CELS[hash]->getFrame(frameIndex);
|
|
}
|
|
|
|
int32 AssetManager::getSpriteFrameCount(int32 hash) {
|
|
assert(_CELS[hash] != NULL);
|
|
return _CELS[hash]->getCount();
|
|
}
|
|
|
|
} // End of namespace M4
|