1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "common/debug.h"
24 #include "common/file.h"
25 #include "graphics/surface.h"
26
27 #include "cryomni3d/sprites.h"
28
29 // #define SPRTIES_DEBUG
30
31 namespace CryOmni3D {
32
33 #define MAP_ID(id) \
34 do { \
35 if (_map) { \
36 id = (*_map)[id]; \
37 } \
38 } while(false)
39
Sprites()40 Sprites::Sprites() : _map(nullptr) {
41 _surface = new Graphics::Surface();
42 }
43
~Sprites()44 Sprites::~Sprites() {
45 for (Common::Array<CryoCursor *>::iterator it = _cursors.begin(); it != _cursors.end(); it++) {
46 if ((*it)->refCnt > 1) {
47 (*it)->refCnt--;
48 } else {
49 delete *it;
50 }
51 }
52 delete _map;
53 delete _surface;
54 }
55
loadSprites(Common::ReadStream & spr_fl)56 void Sprites::loadSprites(Common::ReadStream &spr_fl) {
57 while (true) {
58 uint32 magic = spr_fl.readUint32BE();
59 if (spr_fl.eos()) {
60 // We are EOS so last read likely failed
61 break;
62 }
63 if (magic != MKTAG('S', 'P', 'R', 'I')) {
64 error("Invalid sprite magic");
65 }
66
67 // 2 unknown uint32
68 (void) spr_fl.readUint32BE();
69 (void) spr_fl.readUint32BE();
70
71 CryoCursor *cursor = new CryoCursor();
72
73 uint16 w = spr_fl.readUint16BE();
74 uint16 h = spr_fl.readUint16BE();
75 uint sz = cursor->setup(w, h);
76 cursor->_offX = spr_fl.readUint32BE();
77 cursor->_offY = spr_fl.readUint32BE();
78
79 spr_fl.read(cursor->_data, sz);
80 _cursors.push_back(cursor);
81 }
82 }
83
setupMapTable(const uint * table,uint size)84 void Sprites::setupMapTable(const uint *table, uint size) {
85 delete _map;
86 _map = nullptr;
87 // Reset the reverse mapping
88 for (Common::Array<CryoCursor *>::iterator it = _cursors.begin(); it != _cursors.end(); it++) {
89 (*it)->_constantId = uint(-1);
90 }
91 if (table) {
92 _map = new Common::Array<uint>(table, size);
93
94 // Sweep all the mapping and set its reverse values
95 uint i = 0;
96 for (Common::Array<uint>::const_iterator it = _map->begin(); it != _map->end(); it++, i++) {
97 _cursors[*it]->_constantId = i;
98 }
99
100 #ifdef SPRITES_DEBUG
101 // Normally we don't have any unreachable sprties from constants,
102 // as it could be time consuming, this should be fixed in the static map
103 // Count unswept values
104 uint unswept = 0;
105 for (Common::Array<CryoCursor *>::iterator it = _cursors.begin(); it != _cursors.end(); it++) {
106 if ((*it)->_constantId == -1u) {
107 unswept++;
108 }
109 }
110
111 if (unswept) {
112 warning("We got %d unreachable sprites from map table. This should not happen."
113 " Fixing it for now", unswept);
114 // Enlarge the map to hold new indexes
115 _map->reserve(_map->size() + unswept);
116
117 // Set new indexes to unswept sprites
118 i = 0;
119 for (Common::Array<CryoCursor *>::iterator it = _cursors.begin(); it != _cursors.end(); it++, i++) {
120 if ((*it)->_constantId == -1u) {
121 warning("Fixing sprite the %d sprite", i);
122 (*it)->_constantId = _map->size();
123 _map->push_back(i);
124 }
125 }
126 }
127 #endif
128 }
129 }
130
setSpriteHotspot(uint spriteId,uint x,uint y)131 void Sprites::setSpriteHotspot(uint spriteId, uint x, uint y) {
132 MAP_ID(spriteId);
133 _cursors[spriteId]->_offX = x;
134 _cursors[spriteId]->_offY = y;
135 }
136
replaceSprite(uint oldSpriteId,uint newSpriteId)137 void Sprites::replaceSprite(uint oldSpriteId, uint newSpriteId) {
138 MAP_ID(oldSpriteId);
139 MAP_ID(newSpriteId);
140 if (_cursors[oldSpriteId]->refCnt > 1) {
141 _cursors[oldSpriteId]->refCnt--;
142 } else {
143 delete _cursors[oldSpriteId];
144 }
145 _cursors[oldSpriteId] = _cursors[newSpriteId];
146 _cursors[oldSpriteId]->refCnt++;
147 }
148
replaceSpriteColor(uint spriteId,byte currentColor,byte newColor)149 void Sprites::replaceSpriteColor(uint spriteId, byte currentColor, byte newColor) {
150 MAP_ID(spriteId);
151
152 byte *data = _cursors[spriteId]->_data;
153 uint size = _cursors[spriteId]->_width * _cursors[spriteId]->_height;
154 for (; size > 0; size--, data++) {
155 if (*data == currentColor) {
156 *data = newColor;
157 }
158 }
159 }
160
getSpritesCount() const161 uint Sprites::getSpritesCount() const {
162 if (_map) {
163 return _map->size();
164 } else {
165 return _cursors.size();
166 }
167 }
168
revMapSpriteId(uint id) const169 uint Sprites::revMapSpriteId(uint id) const {
170 if (_map) {
171 if (id >= _cursors.size()) {
172 error("revMapSpriteId is out of bounds: %d/%d", id, _cursors.size());
173 }
174 id = _cursors[id]->_constantId;
175 }
176
177 return id;
178 }
179
calculateSpriteId(uint baseId,uint offset) const180 uint Sprites::calculateSpriteId(uint baseId, uint offset) const {
181 if (_map) {
182 MAP_ID(baseId);
183 baseId += offset;
184 if (baseId >= _cursors.size()) {
185 error("Calculate sprite is out of bounds: %d/%d", baseId, _cursors.size());
186 }
187 uint spriteId = _cursors[baseId]->_constantId;
188 if (spriteId == uint(-1)) {
189 error("Sprite %d is unreachable", baseId);
190 }
191 return spriteId;
192 } else {
193 return baseId + offset;
194 }
195 }
196
getSurface(uint spriteId) const197 const Graphics::Surface &Sprites::getSurface(uint spriteId) const {
198 MAP_ID(spriteId);
199
200 CryoCursor *cursor = _cursors[spriteId];
201
202 _surface->init(cursor->_width, cursor->_height, cursor->_width, cursor->_data,
203 Graphics::PixelFormat::createFormatCLUT8());
204 return *_surface;
205 }
206
getCursor(uint spriteId) const207 const Graphics::Cursor &Sprites::getCursor(uint spriteId) const {
208 MAP_ID(spriteId);
209
210 return *_cursors[spriteId];
211 }
212
CryoCursor()213 Sprites::CryoCursor::CryoCursor() : _width(0), _height(0), _offX(0), _offY(0), _data(nullptr),
214 refCnt(1) {
215 }
216
~CryoCursor()217 Sprites::CryoCursor::~CryoCursor() {
218 assert(refCnt == 1);
219 delete[] _data;
220 }
221
setup(uint16 width,uint16 height)222 uint Sprites::CryoCursor::setup(uint16 width, uint16 height) {
223 _width = width;
224 _height = height;
225 uint sz = _width * _height;
226 _data = new byte[sz];
227 return sz;
228 }
229
230 } // End of namespace CryOmni3D
231