/* 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. * */ #include "common/debug.h" #include "common/file.h" #include "graphics/surface.h" #include "cryomni3d/sprites.h" // #define SPRTIES_DEBUG namespace CryOmni3D { #define MAP_ID(id) \ do { \ if (_map) { \ id = (*_map)[id]; \ } \ } while(false) Sprites::Sprites() : _map(nullptr) { _surface = new Graphics::Surface(); } Sprites::~Sprites() { for (Common::Array::iterator it = _cursors.begin(); it != _cursors.end(); it++) { if ((*it)->refCnt > 1) { (*it)->refCnt--; } else { delete *it; } } delete _map; delete _surface; } void Sprites::loadSprites(Common::ReadStream &spr_fl) { while (true) { uint32 magic = spr_fl.readUint32BE(); if (spr_fl.eos()) { // We are EOS so last read likely failed break; } if (magic != MKTAG('S', 'P', 'R', 'I')) { error("Invalid sprite magic"); } // 2 unknown uint32 (void) spr_fl.readUint32BE(); (void) spr_fl.readUint32BE(); CryoCursor *cursor = new CryoCursor(); uint16 w = spr_fl.readUint16BE(); uint16 h = spr_fl.readUint16BE(); uint sz = cursor->setup(w, h); cursor->_offX = spr_fl.readUint32BE(); cursor->_offY = spr_fl.readUint32BE(); spr_fl.read(cursor->_data, sz); _cursors.push_back(cursor); } } void Sprites::setupMapTable(const uint *table, uint size) { delete _map; _map = nullptr; // Reset the reverse mapping for (Common::Array::iterator it = _cursors.begin(); it != _cursors.end(); it++) { (*it)->_constantId = uint(-1); } if (table) { _map = new Common::Array(table, size); // Sweep all the mapping and set its reverse values uint i = 0; for (Common::Array::const_iterator it = _map->begin(); it != _map->end(); it++, i++) { _cursors[*it]->_constantId = i; } #ifdef SPRITES_DEBUG // Normally we don't have any unreachable sprties from constants, // as it could be time consuming, this should be fixed in the static map // Count unswept values uint unswept = 0; for (Common::Array::iterator it = _cursors.begin(); it != _cursors.end(); it++) { if ((*it)->_constantId == -1u) { unswept++; } } if (unswept) { warning("We got %d unreachable sprites from map table. This should not happen." " Fixing it for now", unswept); // Enlarge the map to hold new indexes _map->reserve(_map->size() + unswept); // Set new indexes to unswept sprites i = 0; for (Common::Array::iterator it = _cursors.begin(); it != _cursors.end(); it++, i++) { if ((*it)->_constantId == -1u) { warning("Fixing sprite the %d sprite", i); (*it)->_constantId = _map->size(); _map->push_back(i); } } } #endif } } void Sprites::setSpriteHotspot(uint spriteId, uint x, uint y) { MAP_ID(spriteId); _cursors[spriteId]->_offX = x; _cursors[spriteId]->_offY = y; } void Sprites::replaceSprite(uint oldSpriteId, uint newSpriteId) { MAP_ID(oldSpriteId); MAP_ID(newSpriteId); if (_cursors[oldSpriteId]->refCnt > 1) { _cursors[oldSpriteId]->refCnt--; } else { delete _cursors[oldSpriteId]; } _cursors[oldSpriteId] = _cursors[newSpriteId]; _cursors[oldSpriteId]->refCnt++; } void Sprites::replaceSpriteColor(uint spriteId, byte currentColor, byte newColor) { MAP_ID(spriteId); byte *data = _cursors[spriteId]->_data; uint size = _cursors[spriteId]->_width * _cursors[spriteId]->_height; for (; size > 0; size--, data++) { if (*data == currentColor) { *data = newColor; } } } uint Sprites::getSpritesCount() const { if (_map) { return _map->size(); } else { return _cursors.size(); } } uint Sprites::revMapSpriteId(uint id) const { if (_map) { if (id >= _cursors.size()) { error("revMapSpriteId is out of bounds: %d/%d", id, _cursors.size()); } id = _cursors[id]->_constantId; } return id; } uint Sprites::calculateSpriteId(uint baseId, uint offset) const { if (_map) { MAP_ID(baseId); baseId += offset; if (baseId >= _cursors.size()) { error("Calculate sprite is out of bounds: %d/%d", baseId, _cursors.size()); } uint spriteId = _cursors[baseId]->_constantId; if (spriteId == uint(-1)) { error("Sprite %d is unreachable", baseId); } return spriteId; } else { return baseId + offset; } } const Graphics::Surface &Sprites::getSurface(uint spriteId) const { MAP_ID(spriteId); CryoCursor *cursor = _cursors[spriteId]; _surface->init(cursor->_width, cursor->_height, cursor->_width, cursor->_data, Graphics::PixelFormat::createFormatCLUT8()); return *_surface; } const Graphics::Cursor &Sprites::getCursor(uint spriteId) const { MAP_ID(spriteId); return *_cursors[spriteId]; } Sprites::CryoCursor::CryoCursor() : _width(0), _height(0), _offX(0), _offY(0), _data(nullptr), refCnt(1) { } Sprites::CryoCursor::~CryoCursor() { assert(refCnt == 1); delete[] _data; } uint Sprites::CryoCursor::setup(uint16 width, uint16 height) { _width = width; _height = height; uint sz = _width * _height; _data = new byte[sz]; return sz; } } // End of namespace CryOmni3D