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