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