scummvm/engines/made/resource.cpp
2008-05-26 11:07:18 +00:00

434 lines
10 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 "common/endian.h"
#include "sound/mixer.h"
#include "made/resource.h"
#include "made/graphics.h"
#include "made/sound.h"
namespace Made {
/* Resource */
Resource::~Resource() {
}
/* PictureResource */
PictureResource::PictureResource() : _picture(NULL), _picturePalette(NULL) {
_hasPalette = false;
}
PictureResource::~PictureResource() {
if (_picture) {
delete _picture;
_picture = 0;
}
if (_picturePalette) {
delete[] _picturePalette;
_picturePalette = 0;
}
}
void PictureResource::load(byte *source, int size) {
Common::MemoryReadStream *sourceS = new Common::MemoryReadStream(source, size);
_hasPalette = (sourceS->readByte() != 0);
byte cmdFlags = sourceS->readByte();
byte pixelFlags = sourceS->readByte();
byte maskFlags = sourceS->readByte();
uint16 cmdOffs = sourceS->readUint16LE();
uint16 pixelOffs = sourceS->readUint16LE();
uint16 maskOffs = sourceS->readUint16LE();
uint16 lineSize = sourceS->readUint16LE();
/*uint16 u = */sourceS->readUint16LE();
uint16 width = sourceS->readUint16LE();
uint16 height = sourceS->readUint16LE();
if (cmdFlags || pixelFlags || maskFlags) {
warning("PictureResource::load() Graphic has flags set");
}
_paletteColorCount = (cmdOffs - 18) / 3; // 18 = sizeof header
debug(2, "width = %d; height = %d\n", width, height);
if (_hasPalette) {
_picturePalette = new byte[_paletteColorCount * 3];
sourceS->read(_picturePalette, _paletteColorCount * 3);
}
_picture = new Graphics::Surface();
_picture->create(width, height, 1);
decompressImage(source, *_picture, cmdOffs, pixelOffs, maskOffs, lineSize, cmdFlags, pixelFlags, maskFlags);
delete sourceS;
}
/* AnimationResource */
AnimationResource::AnimationResource() {
}
AnimationResource::~AnimationResource() {
for (uint i = 0; i < _frames.size(); i++)
delete _frames[i];
}
void AnimationResource::load(byte *source, int size) {
Common::MemoryReadStream *sourceS = new Common::MemoryReadStream(source, size);
sourceS->readUint32LE();
sourceS->readUint32LE();
sourceS->readUint16LE();
_flags = sourceS->readUint16LE();
_width = sourceS->readUint16LE();
_height = sourceS->readUint16LE();
sourceS->readUint32LE();
uint16 frameCount = sourceS->readUint16LE();
sourceS->readUint16LE();
sourceS->readUint16LE();
for (uint16 i = 0; i < frameCount; i++) {
sourceS->seek(26 + i * 4);
uint32 frameOffs = sourceS->readUint32LE();
sourceS->seek(frameOffs);
sourceS->readUint32LE();
sourceS->readUint32LE();
uint16 frameWidth = sourceS->readUint16LE();
uint16 frameHeight = sourceS->readUint16LE();
uint16 cmdOffs = sourceS->readUint16LE();
sourceS->readUint16LE();
uint16 pixelOffs = sourceS->readUint16LE();
sourceS->readUint16LE();
uint16 maskOffs = sourceS->readUint16LE();
sourceS->readUint16LE();
uint16 lineSize = sourceS->readUint16LE();
Graphics::Surface *frame = new Graphics::Surface();
frame->create(frameWidth, frameHeight, 1);
decompressImage(source + frameOffs, *frame, cmdOffs, pixelOffs, maskOffs, lineSize, 0, 0, 0, _flags & 1);
_frames.push_back(frame);
}
delete sourceS;
}
/* SoundResource */
SoundResource::SoundResource() : _soundSize(0), _soundData(NULL) {
}
SoundResource::~SoundResource() {
if (_soundData)
delete[] _soundData;
}
void SoundResource::load(byte *source, int size) {
uint16 chunkCount = READ_LE_UINT16(source + 8);
uint16 chunkSize = READ_LE_UINT16(source + 12);
_soundSize = chunkCount * chunkSize;
_soundData = new byte[_soundSize];
decompressSound(source + 14, _soundData, chunkSize, chunkCount);
}
Audio::AudioStream *SoundResource::getAudioStream(int soundRate, bool loop) {
byte flags = Audio::Mixer::FLAG_UNSIGNED;
if (loop)
flags |= Audio::Mixer::FLAG_LOOP;
return Audio::makeLinearInputStream(_soundData, _soundSize, soundRate, flags, 0, 0);
}
/* MenuResource */
MenuResource::MenuResource() {
}
MenuResource::~MenuResource() {
}
void MenuResource::load(byte *source, int size) {
_strings.clear();
Common::MemoryReadStream *sourceS = new Common::MemoryReadStream(source, size);
sourceS->skip(4); // skip "MENU"
uint16 count = sourceS->readUint16LE();
for (uint16 i = 0; i < count; i++) {
uint16 offs = sourceS->readUint16LE();
const char *string = (const char*)(source + offs);
_strings.push_back(string);
debug(2, "%02d: %s\n", i, string);
}
fflush(stdout);
delete sourceS;
}
const char *MenuResource::getString(uint index) const {
if (index < _strings.size())
return _strings[index].c_str();
else
return NULL;
}
/* FontResource */
FontResource::FontResource() : _data(NULL), _size(0) {
}
FontResource::~FontResource() {
if (_data)
delete[] _data;
}
void FontResource::load(byte *source, int size) {
_data = new byte[size];
_size = size;
memcpy(_data, source, size);
}
int FontResource::getHeight() const {
return _data[0];
}
int FontResource::getCharWidth(uint c) const {
byte *charData = getCharData(c);
if (charData)
return charData[0];
else
return 0;
}
byte *FontResource::getChar(uint c) const {
byte *charData = getCharData(c);
if (charData)
return charData + 1;
else
return NULL;
}
int FontResource::getTextWidth(const char *text) {
int width = 0;
if (text) {
int len = strlen(text);
for (int pos = 0; pos < len; pos++)
width += getCharWidth(text[pos]);
}
return width;
}
byte *FontResource::getCharData(uint c) const {
if (c < 28 || c > 255)
return NULL;
return _data + 1 + (c - 28) * (getHeight() + 1);
}
/* GenericResource */
GenericResource::GenericResource() : _data(NULL), _size(0) {
}
GenericResource::~GenericResource() {
if (_data)
delete[] _data;
}
void GenericResource::load(byte *source, int size) {
_data = new byte[size];
_size = size;
memcpy(_data, source, size);
}
/* ProjectReader */
ProjectReader::ProjectReader() {
}
ProjectReader::~ProjectReader() {
}
void ProjectReader::open(const char *filename) {
_fd = new Common::File();
_fd->open(filename);
_fd->skip(0x18); // skip header for now
uint16 indexCount = _fd->readUint16LE();
for (uint16 i = 0; i < indexCount; i++) {
uint32 resType = _fd->readUint32BE();
uint32 indexOffs = _fd->readUint32LE();
_fd->readUint32LE();
_fd->readUint32LE();
_fd->readUint32LE();
_fd->readUint16LE();
_fd->readUint16LE();
// We don't need ARCH, FREE and OMNI resources
if (resType == kResARCH || resType == kResFREE || resType == kResOMNI)
continue;
//debug(2, "resType = %08X; indexOffs = %d\n", resType, indexOffs);
uint32 oldOffs = _fd->pos();
ResourceSlots *resSlots = new ResourceSlots();
_fd->seek(indexOffs);
loadIndex(resSlots);
_resSlots[resType] = resSlots;
_fd->seek(oldOffs);
}
_cacheCount = 0;
}
PictureResource *ProjectReader::getPicture(int index) {
return createResource<PictureResource>(kResFLEX, index);
}
AnimationResource *ProjectReader::getAnimation(int index) {
return createResource<AnimationResource>(kResANIM, index);
}
SoundResource *ProjectReader::getSound(int index) {
return createResource<SoundResource>(kResSNDS, index);
}
MenuResource *ProjectReader::getMenu(int index) {
return createResource<MenuResource>(kResMENU, index);
}
FontResource *ProjectReader::getFont(int index) {
return createResource<FontResource>(kResFONT, index);
}
GenericResource *ProjectReader::getXmidi(int index) {
return createResource<GenericResource>(kResXMID, index);
}
GenericResource *ProjectReader::getMidi(int index) {
return createResource<GenericResource>(kResMIDI, index);
}
void ProjectReader::loadIndex(ResourceSlots *slots) {
_fd->readUint32LE(); // skip INDX
_fd->readUint32LE(); // skip index size
_fd->readUint32LE(); // skip unknown
_fd->readUint32LE(); // skip res type
uint16 count = _fd->readUint16LE();
_fd->readUint16LE(); // skip unknown count
_fd->readUint16LE(); // skip unknown count
for (uint16 i = 0; i < count; i++) {
uint32 offs = _fd->readUint32LE();
uint32 size = _fd->readUint32LE();
slots->push_back(ResourceSlot(offs, size));
}
}
void ProjectReader::freeResource(Resource *resource) {
tossResourceFromCache(resource->slot);
}
bool ProjectReader::loadResource(ResourceSlot *slot, byte *&buffer, uint32 &size) {
if (slot && slot->size > 0) {
size = slot->size - 62;
buffer = new byte[size];
debug(2, "ProjectReader::loadResource() %08X", slot->offs + 62);
_fd->seek(slot->offs + 62);
_fd->read(buffer, size);
return true;
} else {
return false;
}
}
ResourceSlot *ProjectReader::getResourceSlot(uint32 resType, uint index) {
ResourceSlots *slots = _resSlots[resType];
if (index >= 1 && index < slots->size()) {
return &slots->operator[](index);
} else {
return NULL;
}
}
Resource *ProjectReader::getResourceFromCache(ResourceSlot *slot) {
if (slot->res)
slot->refCount++;
return slot->res;
}
void ProjectReader::addResourceToCache(ResourceSlot *slot, Resource *res) {
if (_cacheCount >= kMaxResourceCacheCount)
purgeCache();
slot->res = res;
slot->refCount = 1;
_cacheCount++;
}
void ProjectReader::tossResourceFromCache(ResourceSlot *slot) {
if (slot->res)
slot->refCount--;
}
void ProjectReader::purgeCache() {
debug(2, "ProjectReader::purgeCache()");
for (ResMap::const_iterator resTypeIter = _resSlots.begin(); resTypeIter != _resSlots.end(); ++resTypeIter) {
ResourceSlots *slots = (*resTypeIter)._value;
for (ResourceSlots::iterator slotIter = slots->begin(); slotIter != slots->end(); ++slotIter) {
ResourceSlot *slot = &(*slotIter);
if (slot->refCount <= 0 && slot->res) {
delete slot->res;
slot->res = NULL;
slot->refCount = 0;
_cacheCount--;
}
}
}
}
} // End of namespace Made