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  * aint32 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  * Based on the original sources
23  *   Faery Tale II -- The Halls of the Dead
24  *   (c) 1993-1996 The Wyrmkeep Entertainment Co.
25  */
26 
27 #include "saga2/saga2.h"
28 #include "saga2/fta.h"
29 #include "saga2/blitters.h"
30 #include "saga2/sprite.h"
31 #include "saga2/tcoords.h"
32 #include "saga2/hresmgr.h"
33 
34 namespace Saga2 {
35 
36 const int           maxWeaponSpriteSets = 40;
37 
38 const uint32        spriteGroupID   = MKTAG('S', 'P', 'R', 'I'),
39                     frameGroupID    = MKTAG('F', 'R', 'M', 'L'),
40                     poseGroupID     = MKTAG('P', 'O', 'S', 'E'),
41                     schemeGroupID   = MKTAG('S', 'C', 'H', 'M'),
42                     objectSpriteID  = MKTAG('O', 'B', 'J', 'S'),
43                     mentalSpriteID  = MKTAG('M', 'E', 'N', 'T'),
44                     weaponSpriteBaseID  = MKTAG('W', 'P', 'N', 0),
45                     missileSpriteID = MKTAG('M', 'I', 'S', 'S');
46 
47 extern uint16 rippedRoofID;
48 extern void drawTileMask(
49     const Point16 &sPos,
50     gPixelMap &map,
51     TilePoint loc,
52     uint16 roofID = rippedRoofID);
53 
54 //  Color map ranges
55 extern uint8        *ColorMapRanges;
56 
57 /* ===================================================================== *
58    Exports
59  * ===================================================================== */
60 
61 /* ===================================================================== *
62    Locals
63  * ===================================================================== */
64 
65 //  Remap table for colors which are not remapped.
66 
67 const uint8 fixedColors[] = {
68 	0, 10, 12, 14, 16, 18, 21, 24,
69 	101, 104, 130, 132, 197, 199, 228, 230
70 };
71 
72 SpriteSet           *objectSprites,    // object sprites
73                     *mentalSprites,   // intangible object sprites
74                     *weaponSprites[maxWeaponSpriteSets], // weapon sprites
75                     *missileSprites;  // missile sprites
76 
77 hResContext         *spriteRes,         // sprite resource handle
78                     *frameRes,          // framelist resource handle
79                     *poseRes,           // poselist resource handle
80                     *schemeRes;         // schemelist resource handle
81 
82 //  An array of 32 actor appearances
83 static ActorAppearance appearanceTable[32];
84 
85 /* ===================================================================== *
86    Quick memory routines
87  * ===================================================================== */
88 
89 static uint8        *quickMemBase,
90        *quickMemPtr;
91 
92 int32               quickMemSize;
93 
initQuickMem(int32 size)94 void initQuickMem(int32 size) {
95 	quickMemBase = new uint8[size]();
96 	if (quickMemBase == nullptr)
97 		error("Error: Memory allocation size %d failed!", size);
98 	quickMemSize = size;
99 	quickMemPtr = quickMemBase;
100 }
101 
cleanupQuickMem(void)102 void cleanupQuickMem(void) {
103 	if (quickMemBase)
104 		delete[] quickMemBase;
105 	quickMemBase = nullptr;
106 }
107 
getQuickMem(int32 size)108 void *getQuickMem(int32 size) {
109 	if (quickMemPtr + size > quickMemBase + quickMemSize) {
110 		error("Error: QuickMem allocation failed, size %d", size);
111 	}
112 	quickMemPtr += size;
113 	return quickMemPtr - size;
114 }
115 
freeQuickMem(void * ptr)116 void freeQuickMem(void *ptr) {
117 	quickMemPtr = ptr ? (uint8 *)ptr : quickMemBase;
118 }
119 
120 /* ===================================================================== *
121    Sprite rendering routines
122  * ===================================================================== */
123 
DrawCompositeMaskedSprite(gPort & port,SpriteComponent * scList,int16 numParts,const Point16 & destPoint,const TilePoint & loc,int16 effects,bool * obscured)124 void DrawCompositeMaskedSprite(
125     gPort           &port,                  // destination gPort
126     SpriteComponent *scList,                // list of components
127     int16           numParts,               // number of components
128     const Point16   &destPoint,             // where to render to
129     const TilePoint &loc,                   // location on map
130     int16           effects,                // effects flags
131     bool            *obscured) {            // set if object obscured by terrain
132 	SpriteComponent *sc;                    // sprite component
133 	int16           xMax = 0,               // extent of composite
134 	                xMin = 0,
135 	                yMax = 0,
136 	                yMin = 0;
137 	Rect16          clip;                   // clip rect of port
138 	gPixelMap       compMap,                // pixel map for composite
139 	                sprMap;                 // sprite map
140 	Point16         org;
141 	int             x, y;
142 
143 	port.getClip(clip);
144 
145 	//  First, determine the enclosing rectangle which
146 	//  surrounds all of the sprites.
147 
148 	sc = scList;
149 
150 	for (int i = 0; i < numParts; i++, sc++) {
151 		Sprite      *sp = sc->sp;
152 		int16       left,
153 		            right,
154 		            bottom,
155 		            top;
156 
157 		//  Compute the rectangle of the sprites
158 
159 		if (sc->flipped)
160 			left = destPoint.x + sc->offset.x - sp->size.x - sp->offset.x;
161 		else left = destPoint.x + sc->offset.x + sp->offset.x;
162 
163 		top    = destPoint.y + sc->offset.y + sp->offset.y;
164 		right  = left + sp->size.x;
165 		bottom = top + sp->size.y;
166 
167 		if (i == 0) {
168 			xMin = left;
169 			xMax = right;
170 			yMin = top;
171 			yMax = bottom;
172 		} else {
173 			if (left   < xMin) xMin = left;
174 			if (right  > xMax) xMax = right;
175 			if (top    < yMin) yMin = top;
176 			if (bottom > yMax) yMax = bottom;
177 		}
178 	}
179 
180 	//  If the composite area is outside the screen rect, then
181 	//  nothing needs to be done.
182 
183 	if (xMax <= clip.x
184 	        || yMax <= clip.y
185 	        || xMin >= clip.x + clip.width
186 	        || yMin >= clip.y + clip.height)
187 		return;
188 
189 	//  Justify the x coords to the nearest tile boundary
190 
191 	xMin = xMin & ~31;
192 	xMax = (xMax + 31) & ~31;
193 
194 	//  Build a temporary bitmap to composite the sprite within
195 
196 	compMap.size.x = xMax - xMin;
197 	compMap.size.y = yMax - yMin;
198 	compMap.data = (uint8 *)getQuickMem(compMap.bytes());
199 	memset(compMap.data, 0, compMap.bytes());
200 
201 	//  Calculate the offset from the upper-left corner of
202 	//  our composite map to the origin point where the sprites
203 	//  should be drawn.
204 
205 	org.x = destPoint.x - xMin;
206 	org.y = destPoint.y - yMin;
207 
208 	//  First, determine the enclosing rectangle which
209 	//  surrounds all of the sprites.
210 	sc = scList;
211 	for (int i = 0; i < numParts; i++, sc++) {
212 		Sprite      *sp = sc->sp;
213 
214 		//  Create a temp map for the sprite to unpack in
215 
216 		sprMap.size = sp->size;
217 		if (sprMap.size.x <= 0 || sprMap.size.y <= 0) continue;
218 		sprMap.data = (uint8 *)getQuickMem(compMap.bytes());
219 
220 		//  Unpack the sprite into the temp map
221 
222 		unpackSprite(&sprMap, sp->_data, sp->_dataSize);
223 
224 		//  Blit the temp map onto the composite map
225 
226 		if (sc->flipped) {
227 			compositePixelsRvs(
228 			    &compMap,
229 			    &sprMap,
230 			    org.x + sc->offset.x - sp->offset.x,
231 			    org.y + sc->offset.y + sp->offset.y,
232 			    sc->colorTable);
233 		} else {
234 			compositePixels(
235 			    &compMap,
236 			    &sprMap,
237 			    org.x + sc->offset.x + sp->offset.x,
238 			    org.y + sc->offset.y + sp->offset.y,
239 			    sc->colorTable);
240 		}
241 		freeQuickMem(sprMap.data);
242 	}
243 
244 	//  do terrain masking
245 	if (effects & sprFXTerrainMask) {
246 		if (!(effects & sprFXGhostIfObscured)) {
247 			drawTileMask(
248 			    Point16(xMin, yMin),
249 			    compMap,
250 			    loc);
251 		} else {
252 			gPixelMap       tempMap;
253 			int32           compMapBytes = compMap.bytes(),
254 			                visiblePixels;
255 			bool            isObscured;
256 
257 			tempMap.size = compMap.size;
258 			tempMap.data = (uint8 *)getQuickMem(compMapBytes);
259 
260 			memcpy(tempMap.data, compMap.data, compMapBytes);
261 
262 			drawTileMask(
263 			    Point16(xMin, yMin),
264 			    compMap,
265 			    loc);
266 
267 			visiblePixels = 0;
268 			for (int i = 0; i < compMapBytes; i++) {
269 				if (compMap.data[i] != 0) {
270 					visiblePixels++;
271 					if (visiblePixels > 10) break;
272 				}
273 			}
274 
275 			isObscured = visiblePixels <= 10;
276 			if (isObscured) {
277 				memcpy(compMap.data, tempMap.data, compMapBytes);
278 				effects |= sprFXGhosted;
279 			}
280 
281 			if (obscured != nullptr) *obscured = isObscured;
282 
283 			freeQuickMem(tempMap.data);
284 		}
285 	}
286 
287 	//  Check if location is underwater
288 	if (loc.z < 0) {
289 		uint8   *submergedArea = &compMap.data[(-loc.z < compMap.size.y ?
290 		                                        (compMap.size.y + loc.z)
291 		                                        * compMap.size.x :
292 		                                        0)];
293 
294 		uint16  submergedSize = &compMap.data[compMap.bytes()] -
295 		                        submergedArea;
296 
297 		memset(submergedArea, 0, submergedSize);
298 	}
299 
300 	//  Add in "ghost" effects
301 	if (effects & sprFXGhosted) {
302 		uint32  *dstRow = (uint32 *)compMap.data;
303 
304 		uint32  mask = (yMin & 1) ? 0xff00ff00 : 0x00ff00ff;
305 
306 		for (y = 0; y < compMap.size.y; y++) {
307 			for (x = 0; x < compMap.size.x; x += 4) {
308 				*dstRow++ &= mask;
309 			}
310 			mask = ~mask;
311 		}
312 	}
313 
314 	//  Blit to the port
315 
316 	TBlit(port.map, &compMap, xMin, yMin);
317 
318 	freeQuickMem(compMap.data);
319 }
320 
DrawSprite(gPort & port,const Point16 & destPoint,Sprite * sp)321 void DrawSprite(
322     gPort           &port,                  // destination gPort
323     const Point16   &destPoint,             // where to render to
324     Sprite          *sp) {                  // sprite pointer
325 	gPixelMap       sprMap;                 // sprite map
326 
327 	//  Create a temp map for the sprite to unpack in
328 	sprMap.size = sp->size;
329 	sprMap.data = (uint8 *)getQuickMem(sprMap.bytes());
330 
331 	//  Unpack the sprite into the temp map
332 	unpackSprite(&sprMap, sp->_data, sp->_dataSize);
333 
334 	//  Blit to the port
335 	port.setMode(drawModeMatte);
336 	port.bltPixels(sprMap,
337 	               0, 0,
338 	               destPoint.x + sp->offset.x,
339 	               destPoint.y + sp->offset.y,
340 	               sprMap.size.x, sprMap.size.y);
341 
342 	freeQuickMem(sprMap.data);
343 }
344 
345 //  Draw a single sprite with no masking, but with color mapping.
346 
DrawColorMappedSprite(gPort & port,const Point16 & destPoint,Sprite * sp,uint8 * colorTable)347 void DrawColorMappedSprite(
348     gPort           &port,                  // destination gPort
349     const Point16   &destPoint,             // where to render to
350     Sprite          *sp,                    // sprite pointer
351     uint8           *colorTable) {          // color remapping table
352 	gPixelMap       sprMap,                 // sprite map
353 	                sprReMap;               // remapped sprite map
354 
355 	//  Create a temp map for the sprite to unpack in
356 	sprMap.size = sp->size;
357 	sprMap.data = (uint8 *)getQuickMem(sprMap.bytes());
358 	sprReMap.size = sp->size;
359 	sprReMap.data = (uint8 *)getQuickMem(sprReMap.bytes());
360 
361 	//  Unpack the sprite into the temp map
362 	unpackSprite(&sprMap, sp->_data, sp->_dataSize);
363 
364 	memset(sprReMap.data, 0, sprReMap.bytes());
365 
366 	//  remap the sprite to the color table given
367 	compositePixels(
368 	    &sprReMap,
369 	    &sprMap,
370 	    0,
371 	    0,
372 	    colorTable);
373 
374 	//  Blit to the port
375 	port.setMode(drawModeMatte);
376 	port.bltPixels(sprReMap,
377 	               0, 0,
378 	               destPoint.x + sp->offset.x,
379 	               destPoint.y + sp->offset.y,
380 	               sprReMap.size.x, sprReMap.size.y);
381 
382 	freeQuickMem(sprReMap.data);
383 	freeQuickMem(sprMap.data);
384 }
385 
386 //  Draw a single sprite with no masking, but with color mapping.
387 
ExpandColorMappedSprite(gPixelMap & map,Sprite * sp,uint8 * colorTable)388 void ExpandColorMappedSprite(
389     gPixelMap       &map,                   // destination gPixelMap
390     Sprite          *sp,                    // sprite pointer
391     uint8           *colorTable) {          // color remapping table
392 	gPixelMap       sprMap,                 // sprite map
393 	                sprReMap;               // remapped sprite map
394 
395 	//  Create a temp map for the sprite to unpack in
396 	sprMap.size = sp->size;
397 	sprMap.data = (uint8 *)getQuickMem(sprMap.bytes());
398 
399 	//  Unpack the sprite into the temp map
400 	unpackSprite(&sprMap, sp->_data, sp->_dataSize);
401 
402 	//  remap the sprite to the color table given
403 	compositePixels(
404 	    &map,
405 	    &sprMap,
406 	    0,
407 	    0,
408 	    colorTable);
409 
410 	freeQuickMem(sprMap.data);
411 }
412 
413 //  Unpacks a sprite for a moment and returns the value of a
414 //  specific pixel in the sprite. This is used to do hit testing
415 //  against sprites.
416 
GetSpritePixel(Sprite * sp,int16 flipped,const Point16 & testPoint)417 uint8 GetSpritePixel(
418     Sprite          *sp,                    // sprite pointer
419     int16           flipped,                // true if sprite was flipped
420     const Point16   &testPoint) {           // where to render to
421 	gPixelMap       sprMap;                 // sprite map
422 	uint8           result;
423 
424 	//  Create a temp map for the sprite to unpack in
425 	sprMap.size = sp->size;
426 	sprMap.data = (uint8 *)getQuickMem(sprMap.bytes());
427 
428 	//  Unpack the sprite into the temp map
429 	unpackSprite(&sprMap, sp->_data, sp->_dataSize);
430 
431 	//  Map the coords to the bitmap and return the pixel
432 	if (flipped) {
433 		result = sprMap.data[testPoint.y * sprMap.size.x
434 		                                  + sprMap.size.x - testPoint.x];
435 	} else {
436 		result = sprMap.data[testPoint.y * sprMap.size.x + testPoint.x];
437 	}
438 	freeQuickMem(sprMap.data);
439 
440 	return result;
441 }
442 
443 
444 //  Return the number of visible pixels in a sprite after terrain masking
visiblePixelsInSprite(Sprite * sp,bool flipped,ColorTable colors,Point16 drawPos,TilePoint loc,uint16 roofID)445 uint16 visiblePixelsInSprite(
446     Sprite          *sp,                    // sprite pointer
447     bool            flipped,                // is sprite flipped
448     ColorTable      colors,                 // sprite's color table
449     Point16         drawPos,                // XY position of sprite
450     TilePoint       loc,                    // UVZ coordinates of sprite
451     uint16          roofID) {               // ID of ripped roof
452 
453 	Point16     org;
454 	int16       xMin,                   // extent of sprite
455 	            xMax,
456 	            yMin,
457 	            yMax;
458 	gPixelMap   sprMap,                 // sprite map
459 	            compMap;
460 	uint16      compBytes,
461 	            i,
462 	            visiblePixels;
463 
464 	//  Determine the extent of the sprite
465 	xMin = drawPos.x + sp->offset.x;
466 	yMin = drawPos.y + sp->offset.y;
467 	xMax = xMin + sp->size.x;
468 	yMax = yMin + sp->size.y;
469 
470 	//  Justify the x coords to the nearest tile boundary
471 	xMin &= ~31;
472 	xMax = (xMax + 31) & ~31;
473 
474 	//  Build a temporary bitmap to composite the sprite within
475 	compMap.size.x = xMax - xMin;
476 	compMap.size.y = yMax - yMin;
477 	compMap.data = (uint8 *)getQuickMem(compBytes = compMap.bytes());
478 	memset(compMap.data, 0, compBytes);
479 
480 	//  Build bitmap in which to unpack the sprite
481 	sprMap.size = sp->size;
482 	sprMap.data = (uint8 *)getQuickMem(sprMap.bytes());
483 
484 	unpackSprite(&sprMap, sp->_data, sp->_dataSize);
485 
486 	org.x = drawPos.x - xMin;
487 	org.y = drawPos.y - yMin;
488 
489 	//  Blit the sprite onto the composite map
490 	if (!flipped) {
491 		compositePixels(
492 		    &compMap,
493 		    &sprMap,
494 		    org.x + sp->offset.x,
495 		    org.y + sp->offset.y,
496 		    colors);
497 	} else {
498 		compositePixelsRvs(
499 		    &compMap,
500 		    &sprMap,
501 		    org.x - sp->offset.x,
502 		    org.y + sp->offset.y,
503 		    colors);
504 	}
505 
506 	//  do terrain masking
507 	drawTileMask(
508 	    Point16(xMin, yMin),
509 	    compMap,
510 	    loc,
511 	    roofID);
512 
513 	//  count the visible pixels in the composite map
514 	for (i = 0, visiblePixels = 0; i < compBytes; i++)
515 		if (compMap.data[i]) visiblePixels++;
516 
517 #if DEBUG*0
518 	WriteStatusF(8, "Visible pixels = %u", visiblePixels);
519 #endif
520 
521 	freeQuickMem(sprMap.data);
522 
523 	freeQuickMem(compMap.data);
524 
525 	return visiblePixels;
526 }
527 
528 /* ===================================================================== *
529    Color table assembly
530  * ===================================================================== */
531 
buildColorTable(uint8 * colorTable,uint8 * colorOptions,int16 numOptions)532 void buildColorTable(
533     uint8           *colorTable,            // color table to build
534     uint8           *colorOptions,          // colors ranges chosen
535     int16           numOptions) {
536 	uint32           *src,
537 	                 *dst;
538 
539 	memcpy(colorTable, fixedColors, sizeof fixedColors);
540 	dst = (uint32 *)(colorTable + sizeof fixedColors);
541 
542 	while (numOptions--) {
543 		src = (uint32 *)&ColorMapRanges[*colorOptions * 8];
544 		colorOptions++;
545 		*dst++ = *src++;
546 		*dst++ = *src++;
547 	}
548 }
549 
550 /* ===================================================================== *
551    Load actor appearance
552  * ===================================================================== */
553 
loadSpriteBanks(int16 banksNeeded)554 void ActorAppearance::loadSpriteBanks(int16 banksNeeded) {
555 	int16           bank;
556 
557 	WriteStatusF(2, "Loading Banks: %x", banksNeeded);
558 
559 	//  Make this one the most recently used entry
560 	g_vm->_appearanceLRU.push_back(this);
561 
562 	//  Load in additional sprite banks if requested...
563 	for (bank = 0; bank < (long)ARRAYSIZE(spriteBanks); bank++) {
564 		//  Load the sprite handle...
565 		if (spriteBanks[bank] == nullptr && (banksNeeded & (1 << bank))) {
566 			Common::SeekableReadStream *stream = loadResourceToStream(spriteRes, id + MKTAG(0, 0, 0, bank), "sprite bank");
567 			if (stream) {
568 				spriteBanks[bank] = new SpriteSet(stream);
569 				delete stream;
570 			}
571 		}
572 	}
573 }
574 
ActorAnimation(Common::SeekableReadStream * stream)575 ActorAnimation::ActorAnimation(Common::SeekableReadStream *stream) {
576 	for (int i = 0; i < numPoseFacings; i++)
577 		start[i] = stream->readUint16LE();
578 
579 	for (int i = 0; i < numPoseFacings; i++)
580 		count[i] = stream->readUint16LE();
581 
582 	for (int i = 0; i < numPoseFacings; i++)
583 		debugC(2, kDebugLoading, "anim%d: start: %d count: %d", i, start[i], count[i]);
584 }
585 
ActorPose()586 ActorPose::ActorPose() {
587 	flags = 0;
588 
589 	actorFrameIndex = actorFrameBank = leftObjectIndex = rightObjectIndex = 0;
590 }
591 
592 
ActorPose(Common::SeekableReadStream * stream)593 ActorPose::ActorPose(Common::SeekableReadStream *stream) {
594 	load(stream);
595 }
596 
load(Common::SeekableReadStream * stream)597 void ActorPose::load(Common::SeekableReadStream *stream) {
598 	flags = stream->readUint16LE();
599 
600 	actorFrameIndex = stream->readByte();
601 	actorFrameBank = stream->readByte();
602 	leftObjectIndex = stream->readByte();
603 	rightObjectIndex = stream->readByte();
604 
605 	leftObjectOffset.load(stream);
606 	rightObjectOffset.load(stream);
607 }
608 
write(Common::MemoryWriteStreamDynamic * out)609 void ActorPose::write(Common::MemoryWriteStreamDynamic *out) {
610 	out->writeUint16LE(flags);
611 
612 	out->writeByte(actorFrameIndex);
613 	out->writeByte(actorFrameBank);
614 	out->writeByte(leftObjectIndex);
615 	out->writeByte(rightObjectIndex);
616 
617 	leftObjectOffset.write(out);
618 	rightObjectOffset.write(out);
619 }
620 
621 
ColorScheme(Common::SeekableReadStream * stream)622 ColorScheme::ColorScheme(Common::SeekableReadStream *stream) {
623 	for (int i = 0; i < 11; ++i)
624 		bank[i] = stream->readByte();
625 
626 	speechColor = stream->readByte();
627 
628 	for (int i = 0; i < 32; ++i)
629 		name[i] = stream->readSByte();
630 }
631 
ColorSchemeList(int count,Common::SeekableReadStream * stream)632 ColorSchemeList::ColorSchemeList(int count, Common::SeekableReadStream *stream) {
633 	_count = count;
634 
635 	_schemes = (ColorScheme **)malloc(_count * sizeof(ColorScheme *));
636 	for (int i = 0; i < _count; ++i)
637 		_schemes[i] = new ColorScheme(stream);
638 }
639 
~ColorSchemeList()640 ColorSchemeList::~ColorSchemeList() {
641 	for (int i = 0; i < _count; ++i)
642 		delete _schemes[i];
643 
644 	free(_schemes);
645 }
646 
LoadActorAppearance(uint32 id,int16 banksNeeded)647 ActorAppearance *LoadActorAppearance(uint32 id, int16 banksNeeded) {
648 	int16           bank;
649 	int schemeListSize;
650 	Common::SeekableReadStream *stream;
651 
652 	//  Search the table for either a matching appearance,
653 	//  or for an empty one.
654 	for (Common::List<ActorAppearance *>::iterator it = g_vm->_appearanceLRU.begin(); it != g_vm->_appearanceLRU.end(); ++it) {
655 		if ((*it)->id == id                    // If has same ID
656 		        && (*it)->poseList != nullptr) {      // and frames not dumped
657 			// then use this one!
658 			(*it)->useCount++;
659 			(*it)->loadSpriteBanks(banksNeeded);
660 			return *it;
661 		}
662 	}
663 
664 	//  If we couldn't find an extact match, search for an
665 	//  empty one.
666 	ActorAppearance *aa = nullptr;
667 	//  Search from LRU end of list.
668 	for (Common::List<ActorAppearance *>::iterator it = g_vm->_appearanceLRU.begin(); it != g_vm->_appearanceLRU.end(); ++it) {
669 		if ((*it)->useCount == 0)  {	// If not in use
670 			aa = *it;					// then use this one!
671 			break;
672 		}
673 	}
674 
675 	//  If none available, that's fatal...
676 	if (aa == nullptr) {
677 		error("All ActorAppearance records are in use!");
678 	}
679 
680 	//  Dump the sprites being stored
681 	for (bank = 0; bank < (long)ARRAYSIZE(aa->spriteBanks); bank++) {
682 		if (aa->spriteBanks[bank])
683 			delete aa->spriteBanks[bank];
684 		aa->spriteBanks[bank] = nullptr;
685 	}
686 
687 	if (aa->poseList) {
688 		for (uint i = 0; i < aa->poseList->numPoses; i++)
689 			delete aa->poseList->poses[i];
690 
691 		free(aa->poseList->poses);
692 
693 		for (uint i = 0; i < aa->poseList->numAnimations; i++)
694 			delete aa->poseList->animations[i];
695 
696 		free(aa->poseList->animations);
697 
698 		delete aa->poseList;
699 	}
700 	aa->poseList = nullptr;
701 
702 	if (aa->schemeList) {
703 		delete aa->schemeList;
704 	}
705 	aa->schemeList = nullptr;
706 
707 	//  Set ID and use count
708 	aa->id = id;
709 	aa->useCount = 1;
710 
711 	//  Load in new frame lists and sprite banks
712 	aa->loadSpriteBanks(banksNeeded);
713 
714 	Common::SeekableReadStream *poseStream = loadResourceToStream(poseRes, id, "pose list");
715 
716 	if (poseStream == nullptr) {
717 		warning("LoadActorAppearance: Could not load pose list");
718 	} else {
719 		ActorAnimSet *as = new ActorAnimSet;
720 		aa->poseList = as;
721 		as->numAnimations = poseStream->readUint32LE();
722 		as->poseOffset = poseStream->readUint32LE();
723 
724 		// compute number of ActorPoses
725 		uint32 poseBytes = poseStream->size() - as->poseOffset;
726 		const int poseSize = 14;
727 
728 		debugC(1, kDebugLoading, "Pose List: bytes: %ld numAnimations: %d  poseOffset: %d calculated offset: %d numPoses: %d",
729 			poseStream->size(), as->numAnimations, as->poseOffset, 8 + as->numAnimations * 32, poseBytes / poseSize);
730 
731 		if (poseBytes % poseSize != 0)
732 			warning("Incorrect number of poses, %d bytes more", poseBytes % poseSize);
733 
734 		as->numPoses = poseBytes / poseSize;
735 
736 		as->animations = (ActorAnimation **)malloc(as->numAnimations * sizeof(ActorAnimation *));
737 
738 		for (uint i = 0; i < as->numAnimations; i++)
739 			as->animations[i] = new ActorAnimation(poseStream);
740 
741 		as->poses = (ActorPose **)malloc(as->numPoses * sizeof(ActorPose *));
742 
743 		for (uint i = 0; i < as->numPoses; i++)
744 			as->poses[i] = new ActorPose(poseStream);
745 
746 		delete poseStream;
747 	}
748 
749 	if (schemeRes->seek(id) == 0) {
750 		warning("LoadActorAppearance: Could not load scheme list");
751 	} else {
752 		const int colorSchemeSize = 44;
753 
754 		if (schemeRes->size(id) % colorSchemeSize != 0)
755 			warning("Incorrect number of colorschemes, %d bytes more", schemeRes->size(id) % colorSchemeSize);
756 
757 		schemeListSize = schemeRes->size(id) / colorSchemeSize;
758 		stream = loadResourceToStream(schemeRes, id, "scheme list");
759 		aa->schemeList = new ColorSchemeList(schemeListSize, stream);
760 
761 		delete stream;
762 	}
763 
764 	return aa;
765 }
766 
ReleaseActorAppearance(ActorAppearance * aa)767 void ReleaseActorAppearance(ActorAppearance *aa) {
768 	aa->useCount--;
769 }
770 
771 /* ===================================================================== *
772    Sprite initialization routines
773  * ===================================================================== */
774 
Sprite(Common::SeekableReadStream * stream)775 Sprite::Sprite(Common::SeekableReadStream *stream) {
776 	size.load(stream);
777 	offset.load(stream);
778 
779 	_dataSize = size.x * size.y;
780 	_data = (byte *)malloc(_dataSize);
781 	stream->read(_data, _dataSize);
782 }
783 
~Sprite()784 Sprite::~Sprite() {
785 	free(_data);
786 }
787 
SpriteSet(Common::SeekableReadStream * stream)788 SpriteSet::SpriteSet(Common::SeekableReadStream *stream) {
789 	count = stream->readUint32LE();
790 	_sprites = (Sprite **)malloc(count * sizeof(Sprite *));
791 
792 	for (uint i = 0; i < count; ++i) {
793 		stream->seek(4 + i * 4);
794 		uint32 offset = stream->readUint32LE();
795 		stream->seek(offset);
796 		_sprites[i] = new Sprite(stream);
797 	}
798 }
799 
~SpriteSet()800 SpriteSet::~SpriteSet() {
801 	for (uint i = 0; i < count; ++i) {
802 		if (_sprites[i])
803 			delete _sprites[i];
804 	}
805 
806 	free(_sprites);
807 }
808 
initSprites(void)809 void initSprites(void) {
810 	int     i;
811 	Common::SeekableReadStream *stream = nullptr;
812 
813 	spriteRes = resFile->newContext(spriteGroupID, "sprite resources");
814 	if (!spriteRes->_valid)
815 		error("Error accessing sprite resource group.");
816 
817 	frameRes = resFile->newContext(frameGroupID, "frame resources");
818 	assert(frameRes && frameRes->_valid);
819 
820 	poseRes = resFile->newContext(poseGroupID, "pose resources");
821 	assert(poseRes && poseRes->_valid);
822 
823 	schemeRes = resFile->newContext(schemeGroupID, "scheme resources");
824 	assert(schemeRes && schemeRes->_valid);
825 
826 	// object sprites
827 	stream = loadResourceToStream(spriteRes, objectSpriteID, "object sprites");
828 	objectSprites = new SpriteSet(stream);
829 	delete stream;
830 	assert(objectSprites);
831 
832 	// intagible object sprites
833 	stream = loadResourceToStream(spriteRes, mentalSpriteID, "mental sprites");
834 	mentalSprites = new SpriteSet(stream);
835 	delete stream;
836 	assert(mentalSprites);
837 
838 	for (i = 0; i < maxWeaponSpriteSets; i++) {
839 		hResID      weaponSpriteID;
840 
841 		weaponSpriteID = weaponSpriteBaseID + MKTAG(0, 0, 0, i);
842 
843 		if (spriteRes->size(weaponSpriteID) == 0) {
844 			weaponSprites[i] = nullptr;
845 			continue;
846 		}
847 
848 		stream = loadResourceToStream(spriteRes, weaponSpriteID, "weapon sprite set");
849 		weaponSprites[i] = new SpriteSet(stream);
850 		delete stream;
851 	}
852 
853 	stream = loadResourceToStream(spriteRes, missileSpriteID, "missle sprites");
854 	missileSprites = new SpriteSet(stream);
855 	delete stream;
856 
857 	initQuickMem(0x10000);
858 
859 	//  Initialize actor appearance table
860 	for (i = 0; i < ARRAYSIZE(appearanceTable); i++) {
861 		ActorAppearance *aa = &appearanceTable[i];
862 
863 		aa->useCount = 0;
864 		g_vm->_appearanceLRU.push_front(aa);
865 	}
866 }
867 
cleanupSprites(void)868 void cleanupSprites(void) {
869 	int         i;
870 
871 	cleanupQuickMem();
872 
873 	if (objectSprites)
874 		delete objectSprites;
875 	objectSprites = nullptr;
876 
877 	if (mentalSprites)
878 		delete mentalSprites;
879 	mentalSprites = nullptr;
880 
881 	for (i = 0; i < maxWeaponSpriteSets; i++) {
882 		if (weaponSprites[i]) {
883 			delete weaponSprites[i];
884 			weaponSprites[i] = nullptr;
885 		}
886 	}
887 
888 	if (schemeRes) resFile->disposeContext(schemeRes);
889 	schemeRes = nullptr;
890 
891 	if (poseRes) resFile->disposeContext(poseRes);
892 	poseRes = nullptr;
893 
894 	if (frameRes) resFile->disposeContext(frameRes);
895 	frameRes = nullptr;
896 
897 	if (spriteRes) resFile->disposeContext(spriteRes);
898 	spriteRes = nullptr;
899 }
900 
901 } // end of namespace Saga2
902