2019-03-05 18:28:12 +00:00
|
|
|
/* 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.
|
|
|
|
*
|
2021-12-26 17:47:58 +00:00
|
|
|
* 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 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
2019-03-05 18:28:12 +00:00
|
|
|
*
|
|
|
|
* 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
|
2021-12-26 17:47:58 +00:00
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2019-03-05 18:28:12 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "common/debug.h"
|
|
|
|
#include "common/file.h"
|
|
|
|
#include "graphics/surface.h"
|
|
|
|
|
2019-05-26 17:09:34 +00:00
|
|
|
#include "cryomni3d/sprites.h"
|
2019-03-05 18:28:12 +00:00
|
|
|
|
|
|
|
// #define SPRTIES_DEBUG
|
|
|
|
|
|
|
|
namespace CryOmni3D {
|
|
|
|
|
|
|
|
#define MAP_ID(id) \
|
2021-04-15 19:20:04 +00:00
|
|
|
do { \
|
|
|
|
if (_map) { \
|
|
|
|
id = (*_map)[id]; \
|
|
|
|
} \
|
|
|
|
} while(false)
|
2019-03-05 18:28:12 +00:00
|
|
|
|
|
|
|
Sprites::Sprites() : _map(nullptr) {
|
|
|
|
_surface = new Graphics::Surface();
|
|
|
|
}
|
|
|
|
|
|
|
|
Sprites::~Sprites() {
|
|
|
|
for (Common::Array<CryoCursor *>::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) {
|
2019-05-30 10:08:28 +00:00
|
|
|
uint32 magic = spr_fl.readUint32BE();
|
|
|
|
if (spr_fl.eos()) {
|
|
|
|
// We are EOS so last read likely failed
|
2019-03-05 18:28:12 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-05-30 10:08:28 +00:00
|
|
|
if (magic != MKTAG('S', 'P', 'R', 'I')) {
|
2019-03-05 18:28:12 +00:00
|
|
|
error("Invalid sprite magic");
|
|
|
|
}
|
|
|
|
|
|
|
|
// 2 unknown uint32
|
2019-05-30 13:44:30 +00:00
|
|
|
(void) spr_fl.readUint32BE();
|
|
|
|
(void) spr_fl.readUint32BE();
|
2019-03-05 18:28:12 +00:00
|
|
|
|
|
|
|
CryoCursor *cursor = new CryoCursor();
|
|
|
|
|
|
|
|
uint16 w = spr_fl.readUint16BE();
|
|
|
|
uint16 h = spr_fl.readUint16BE();
|
2019-05-26 16:29:57 +00:00
|
|
|
uint sz = cursor->setup(w, h);
|
2019-03-05 18:28:12 +00:00
|
|
|
cursor->_offX = spr_fl.readUint32BE();
|
|
|
|
cursor->_offY = spr_fl.readUint32BE();
|
|
|
|
|
|
|
|
spr_fl.read(cursor->_data, sz);
|
|
|
|
_cursors.push_back(cursor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-26 16:29:57 +00:00
|
|
|
void Sprites::setupMapTable(const uint *table, uint size) {
|
2019-03-05 18:28:12 +00:00
|
|
|
delete _map;
|
|
|
|
_map = nullptr;
|
|
|
|
// Reset the reverse mapping
|
|
|
|
for (Common::Array<CryoCursor *>::iterator it = _cursors.begin(); it != _cursors.end(); it++) {
|
2019-06-30 14:43:32 +00:00
|
|
|
(*it)->_constantId = uint(-1);
|
2019-03-05 18:28:12 +00:00
|
|
|
}
|
|
|
|
if (table) {
|
2019-05-26 16:29:57 +00:00
|
|
|
_map = new Common::Array<uint>(table, size);
|
2019-03-05 18:28:12 +00:00
|
|
|
|
|
|
|
// Sweep all the mapping and set its reverse values
|
2019-05-26 16:29:57 +00:00
|
|
|
uint i = 0;
|
|
|
|
for (Common::Array<uint>::const_iterator it = _map->begin(); it != _map->end(); it++, i++) {
|
2019-03-05 18:28:12 +00:00
|
|
|
_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
|
2019-05-26 16:29:57 +00:00
|
|
|
uint unswept = 0;
|
2019-03-05 18:28:12 +00:00
|
|
|
for (Common::Array<CryoCursor *>::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<CryoCursor *>::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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-26 16:29:57 +00:00
|
|
|
void Sprites::setSpriteHotspot(uint spriteId, uint x, uint y) {
|
2019-03-05 18:28:12 +00:00
|
|
|
MAP_ID(spriteId);
|
|
|
|
_cursors[spriteId]->_offX = x;
|
|
|
|
_cursors[spriteId]->_offY = y;
|
|
|
|
}
|
|
|
|
|
2019-05-26 16:29:57 +00:00
|
|
|
void Sprites::replaceSprite(uint oldSpriteId, uint newSpriteId) {
|
2019-03-05 18:28:12 +00:00
|
|
|
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++;
|
|
|
|
}
|
|
|
|
|
2019-05-26 16:29:57 +00:00
|
|
|
void Sprites::replaceSpriteColor(uint spriteId, byte currentColor, byte newColor) {
|
2019-03-09 23:49:56 +00:00
|
|
|
MAP_ID(spriteId);
|
|
|
|
|
|
|
|
byte *data = _cursors[spriteId]->_data;
|
2019-05-26 16:29:57 +00:00
|
|
|
uint size = _cursors[spriteId]->_width * _cursors[spriteId]->_height;
|
2019-03-09 23:49:56 +00:00
|
|
|
for (; size > 0; size--, data++) {
|
|
|
|
if (*data == currentColor) {
|
|
|
|
*data = newColor;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-26 16:29:57 +00:00
|
|
|
uint Sprites::getSpritesCount() const {
|
2019-03-05 18:28:12 +00:00
|
|
|
if (_map) {
|
|
|
|
return _map->size();
|
|
|
|
} else {
|
|
|
|
return _cursors.size();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-26 16:29:57 +00:00
|
|
|
uint Sprites::revMapSpriteId(uint id) const {
|
2019-03-05 18:28:12 +00:00
|
|
|
if (_map) {
|
|
|
|
if (id >= _cursors.size()) {
|
|
|
|
error("revMapSpriteId is out of bounds: %d/%d", id, _cursors.size());
|
|
|
|
}
|
|
|
|
id = _cursors[id]->_constantId;
|
|
|
|
}
|
|
|
|
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2019-05-26 16:29:57 +00:00
|
|
|
uint Sprites::calculateSpriteId(uint baseId, uint offset) const {
|
2019-03-05 18:28:12 +00:00
|
|
|
if (_map) {
|
|
|
|
MAP_ID(baseId);
|
|
|
|
baseId += offset;
|
|
|
|
if (baseId >= _cursors.size()) {
|
|
|
|
error("Calculate sprite is out of bounds: %d/%d", baseId, _cursors.size());
|
|
|
|
}
|
2019-05-26 16:29:57 +00:00
|
|
|
uint spriteId = _cursors[baseId]->_constantId;
|
2019-06-30 14:43:32 +00:00
|
|
|
if (spriteId == uint(-1)) {
|
2019-03-05 18:28:12 +00:00
|
|
|
error("Sprite %d is unreachable", baseId);
|
|
|
|
}
|
|
|
|
return spriteId;
|
|
|
|
} else {
|
|
|
|
return baseId + offset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-26 16:29:57 +00:00
|
|
|
const Graphics::Surface &Sprites::getSurface(uint spriteId) const {
|
2019-03-05 18:28:12 +00:00
|
|
|
MAP_ID(spriteId);
|
|
|
|
|
|
|
|
CryoCursor *cursor = _cursors[spriteId];
|
|
|
|
|
|
|
|
_surface->init(cursor->_width, cursor->_height, cursor->_width, cursor->_data,
|
|
|
|
Graphics::PixelFormat::createFormatCLUT8());
|
|
|
|
return *_surface;
|
|
|
|
}
|
|
|
|
|
2019-05-26 16:29:57 +00:00
|
|
|
const Graphics::Cursor &Sprites::getCursor(uint spriteId) const {
|
2019-03-05 18:28:12 +00:00
|
|
|
MAP_ID(spriteId);
|
|
|
|
|
|
|
|
return *_cursors[spriteId];
|
|
|
|
}
|
|
|
|
|
|
|
|
Sprites::CryoCursor::CryoCursor() : _width(0), _height(0), _offX(0), _offY(0), _data(nullptr),
|
2020-02-16 19:18:55 +00:00
|
|
|
refCnt(1), _constantId(uint(-1)) {
|
2019-03-05 18:28:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Sprites::CryoCursor::~CryoCursor() {
|
|
|
|
assert(refCnt == 1);
|
|
|
|
delete[] _data;
|
|
|
|
}
|
|
|
|
|
2019-05-26 16:29:57 +00:00
|
|
|
uint Sprites::CryoCursor::setup(uint16 width, uint16 height) {
|
2019-03-05 18:28:12 +00:00
|
|
|
_width = width;
|
|
|
|
_height = height;
|
2019-05-26 16:29:57 +00:00
|
|
|
uint sz = _width * _height;
|
2019-03-05 18:28:12 +00:00
|
|
|
_data = new byte[sz];
|
|
|
|
return sz;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // End of namespace CryOmni3D
|