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 * Additional copyright for this file:
8 * Copyright (C) 1994-1998 Revolution Software Ltd.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 */
24
25 // ---------------------------------------------------------------------------
26 // BUILD_DISPLAY.CPP like the old spr_engi but slightly more aptly named
27 // ---------------------------------------------------------------------------
28
29
30 #include "common/system.h"
31 #include "common/events.h"
32 #include "common/textconsole.h"
33
34 #include "sword2/sword2.h"
35 #include "sword2/defs.h"
36 #include "sword2/header.h"
37 #include "sword2/console.h"
38 #include "sword2/logic.h"
39 #include "sword2/maketext.h"
40 #include "sword2/mouse.h"
41 #include "sword2/object.h"
42 #include "sword2/resman.h"
43 #include "sword2/screen.h"
44 #include "sword2/sound.h"
45
46 namespace Sword2 {
47
Screen(Sword2Engine * vm,int16 width,int16 height)48 Screen::Screen(Sword2Engine *vm, int16 width, int16 height) {
49 _vm = vm;
50
51 _dirtyGrid = _buffer = NULL;
52
53 _screenWide = width;
54 _screenDeep = height;
55
56 _gridWide = width / CELLWIDE;
57 _gridDeep = height / CELLDEEP;
58
59 if ((width % CELLWIDE) || (height % CELLDEEP))
60 error("Bad cell size");
61
62 _dirtyGrid = (byte *)calloc(_gridWide, _gridDeep);
63 if (!_dirtyGrid)
64 error("Could not initialize dirty grid");
65
66 _buffer = (byte *)malloc(width * height);
67 if (!_buffer)
68 error("Could not initialize display");
69
70 for (int i = 0; i < ARRAYSIZE(_blockSurfaces); i++)
71 _blockSurfaces[i] = NULL;
72
73 _lightMask = NULL;
74 _needFullRedraw = false;
75
76 memset(&_thisScreen, 0, sizeof(_thisScreen));
77
78 _fps = 0;
79 _frameCount = 0;
80 _cycleTime = 0;
81
82 _lastPaletteRes = 0;
83
84 _scrollFraction = 16;
85
86 _largestLayerArea = 0;
87 _largestSpriteArea = 0;
88
89 strcpy(_largestLayerInfo, "largest layer: none registered");
90 strcpy(_largestSpriteInfo, "largest sprite: none registered");
91
92 _fadeStatus = RDFADE_NONE;
93 _renderAverageTime = 60;
94
95 _layer = 0;
96
97 _dimPalette = false;
98
99 _pauseTicks = 0;
100 _pauseStartTick = 0;
101
102 // Clean the cache for PSX version SCREENS.CLU
103 _psxScrCache[0] = NULL;
104 _psxScrCache[1] = NULL;
105 _psxScrCache[2] = NULL;
106 _psxCacheEnabled[0] = true;
107 _psxCacheEnabled[1] = true;
108 _psxCacheEnabled[2] = true;
109 }
110
~Screen()111 Screen::~Screen() {
112 flushPsxScrCache();
113 free(_buffer);
114 free(_dirtyGrid);
115 closeBackgroundLayer();
116 free(_lightMask);
117 }
118
getTick()119 uint32 Screen::getTick() {
120 return _vm->getMillis() - _pauseTicks;
121 }
122
pauseScreen(bool pause)123 void Screen::pauseScreen(bool pause) {
124 if (pause) {
125 _pauseStartTick = _vm->_system->getMillis();
126 } else {
127 _pauseTicks += (_vm->_system->getMillis() - _pauseStartTick);
128 }
129 }
130
131 /**
132 * @return the graphics detail setting
133 */
134
getRenderLevel()135 int8 Screen::getRenderLevel() {
136 return _renderLevel;
137 }
138
setRenderLevel(int8 level)139 void Screen::setRenderLevel(int8 level) {
140 _renderLevel = level;
141
142 switch (_renderLevel) {
143 case 0:
144 // Lowest setting: no fancy stuff
145 _renderCaps = 0;
146 break;
147 case 1:
148 // Medium-low setting: transparency-blending
149 _renderCaps = RDBLTFX_SPRITEBLEND;
150 break;
151 case 2:
152 // Medium-high setting: transparency-blending + shading
153 _renderCaps = RDBLTFX_SPRITEBLEND | RDBLTFX_SHADOWBLEND;
154 break;
155 case 3:
156 // Highest setting: transparency-blending + shading +
157 // edge-blending + improved stretching
158 _renderCaps = RDBLTFX_SPRITEBLEND | RDBLTFX_SHADOWBLEND | RDBLTFX_EDGEBLEND;
159 break;
160 default:
161 break;
162 }
163 }
164
165 /**
166 * Tell updateDisplay() that the scene needs to be completely updated.
167 */
168
setNeedFullRedraw()169 void Screen::setNeedFullRedraw() {
170 _needFullRedraw = true;
171 }
172
173 /**
174 * Mark an area of the screen as dirty, first generation.
175 */
176
markAsDirty(int16 x0,int16 y0,int16 x1,int16 y1)177 void Screen::markAsDirty(int16 x0, int16 y0, int16 x1, int16 y1) {
178 int16 gridX0 = x0 / CELLWIDE;
179 int16 gridY0 = y0 / CELLDEEP;
180 int16 gridX1 = x1 / CELLWIDE;
181 int16 gridY1 = y1 / CELLDEEP;
182
183 for (int16 i = gridY0; i <= gridY1; i++)
184 for (int16 j = gridX0; j <= gridX1; j++)
185 _dirtyGrid[i * _gridWide + j] = 2;
186 }
187
188 /**
189 * This function has two purposes: It redraws the scene, and it handles input
190 * events, palette fading, etc. It should be called at a high rate (> 20 per
191 * second), but the scene is usually only redrawn about 12 times per second,
192 * except when then screen is scrolling.
193 *
194 * @param redrawScene If true, redraw the scene.
195 */
196
updateDisplay(bool redrawScene)197 void Screen::updateDisplay(bool redrawScene) {
198 _vm->parseInputEvents();
199 fadeServer();
200
201 if (redrawScene) {
202 int i;
203
204 // Note that the entire scene is always rendered, which is less
205 // than optimal, but at least we can try to be intelligent
206 // about updating the screen afterwards.
207
208 if (_needFullRedraw) {
209 // Update the entire screen. This is necessary when
210 // scrolling, fading, etc.
211
212 _vm->_system->copyRectToScreen(_buffer + MENUDEEP * _screenWide, _screenWide, 0, MENUDEEP, _screenWide, _screenDeep - 2 * MENUDEEP);
213 _needFullRedraw = false;
214 } else {
215 // Update only the dirty areas of the screen
216
217 int j, x, y;
218 int stripWide;
219
220 for (i = 0; i < _gridDeep; i++) {
221 stripWide = 0;
222
223 for (j = 0; j < _gridWide; j++) {
224 if (_dirtyGrid[i * _gridWide + j]) {
225 stripWide++;
226 } else if (stripWide) {
227 x = CELLWIDE * (j - stripWide);
228 y = CELLDEEP * i;
229 _vm->_system->copyRectToScreen(_buffer + y * _screenWide + x, _screenWide, x, y, stripWide * CELLWIDE, CELLDEEP);
230 stripWide = 0;
231 }
232 }
233
234 if (stripWide) {
235 x = CELLWIDE * (j - stripWide);
236 y = CELLDEEP * i;
237 _vm->_system->copyRectToScreen(_buffer + y * _screenWide + x, _screenWide, x, y, stripWide * CELLWIDE, CELLDEEP);
238 stripWide = 0;
239 }
240 }
241 }
242
243 // Age the dirty cells one generation. This way we keep track
244 // of both the cells that were updated this time, and the ones
245 // that were updated the last time.
246
247 for (i = 0; i < _gridWide * _gridDeep; i++)
248 _dirtyGrid[i] >>= 1;
249 }
250
251 // We always need to update because of fades, menu animations, etc.
252 _vm->_system->updateScreen();
253 }
254
255 /**
256 * Fill the screen buffer with palette color zero. Note that it does not
257 * touch the menu areas of the screen.
258 */
259
clearScene()260 void Screen::clearScene() {
261 memset(_buffer + MENUDEEP * _screenWide, 0, _screenWide * RENDERDEEP);
262 _needFullRedraw = true;
263 }
264
buildDisplay()265 void Screen::buildDisplay() {
266 if (_thisScreen.new_palette) {
267 // start the layer palette fading up
268 startNewPalette();
269
270 // should be reset to zero at start of each screen change
271 _largestLayerArea = 0;
272 _largestSpriteArea = 0;
273 }
274
275 // Does this ever happen?
276 if (!_thisScreen.background_layer_id)
277 return;
278
279 // there is a valid screen to run
280
281 setScrollTarget(_thisScreen.scroll_offset_x, _thisScreen.scroll_offset_y);
282 _vm->_mouse->animateMouse();
283 startRenderCycle();
284
285 byte *file = _vm->_resman->openResource(_thisScreen.background_layer_id);
286
287 MultiScreenHeader screenLayerTable;
288 memset(&screenLayerTable, 0, sizeof(screenLayerTable));
289
290 if (!Sword2Engine::isPsx()) // On PSX version, there would be nothing to read here
291 screenLayerTable.read(file + ResHeader::size());
292
293 // Render at least one frame, but if the screen is scrolling, and if
294 // there is time left, we will render extra frames to smooth out the
295 // scrolling.
296
297 do {
298 // first background parallax + related anims
299 if (Sword2Engine::isPsx() || screenLayerTable.bg_parallax[0]) { // No need to check on PSX version
300 renderParallax(_vm->fetchBackgroundParallaxLayer(file, 0), 0);
301 drawBackPar0Frames();
302 }
303
304 // second background parallax + related anims
305 if (!Sword2Engine::isPsx() && screenLayerTable.bg_parallax[1]) { // Nothing here in PSX version
306 renderParallax(_vm->fetchBackgroundParallaxLayer(file, 1), 1);
307 drawBackPar1Frames();
308 }
309
310 // normal backround layer (just the one!)
311 renderParallax(_vm->fetchBackgroundLayer(file), 2);
312
313 // sprites & layers
314 drawBackFrames(); // background sprites
315 drawSortFrames(file); // sorted sprites & layers
316 drawForeFrames(); // foreground sprites
317
318 // first foreground parallax + related anims
319
320 if (Sword2Engine::isPsx() || screenLayerTable.fg_parallax[0]) {
321 renderParallax(_vm->fetchForegroundParallaxLayer(file, 0), 3);
322 drawForePar0Frames();
323 }
324
325 // second foreground parallax + related anims
326
327 if (!Sword2Engine::isPsx() && screenLayerTable.fg_parallax[1]) {
328 renderParallax(_vm->fetchForegroundParallaxLayer(file, 1), 4);
329 drawForePar1Frames();
330 }
331
332 _vm->_debugger->drawDebugGraphics();
333 _vm->_fontRenderer->printTextBlocs();
334 _vm->_mouse->processMenu();
335
336 updateDisplay();
337
338 _frameCount++;
339 if (getTick() > _cycleTime) {
340 _fps = _frameCount;
341 _frameCount = 0;
342 _cycleTime = getTick() + 1000;
343 }
344 } while (!endRenderCycle());
345
346 _vm->_resman->closeResource(_thisScreen.background_layer_id);
347
348 }
349
350 /**
351 * Fades down and displays a message on the screen.
352 * @param text The message
353 * @param time The number of seconds to display the message, or 0 to display it
354 * until the user clicks the mouse or presses a key.
355 */
356
displayMsg(byte * text,int time)357 void Screen::displayMsg(byte *text, int time) {
358 byte pal[256 * 3];
359 byte oldPal[256 * 3];
360
361 debug(2, "DisplayMsg: %s", text);
362
363 if (getFadeStatus() != RDFADE_BLACK) {
364 fadeDown();
365 waitForFade();
366 }
367
368 _vm->_mouse->setMouse(0);
369 _vm->_mouse->setLuggage(0);
370 _vm->_mouse->closeMenuImmediately();
371
372 clearScene();
373
374 byte *text_spr = _vm->_fontRenderer->makeTextSprite(text, 640, 187, _vm->_speechFontId);
375
376 FrameHeader frame;
377
378 frame.read(text_spr);
379
380 SpriteInfo spriteInfo;
381
382 spriteInfo.x = _screenWide / 2 - frame.width / 2;
383 if (!time)
384 spriteInfo.y = _screenDeep / 2 - frame.height / 2 - MENUDEEP;
385 else
386 spriteInfo.y = 400 - frame.height;
387 spriteInfo.w = frame.width;
388 spriteInfo.h = frame.height;
389 spriteInfo.scale = 0;
390 spriteInfo.scaledWidth = 0;
391 spriteInfo.scaledHeight = 0;
392 spriteInfo.type = RDSPR_DISPLAYALIGN | RDSPR_NOCOMPRESSION | RDSPR_TRANS;
393 spriteInfo.blend = 0;
394 spriteInfo.data = text_spr + FrameHeader::size();
395 spriteInfo.colorTable = 0;
396 spriteInfo.isText = true;
397
398 uint32 rv = drawSprite(&spriteInfo);
399 if (rv)
400 error("Driver Error %.8x (in DisplayMsg)", rv);
401
402 memcpy(oldPal, _palette, sizeof(oldPal));
403 memset(pal, 0, sizeof(pal));
404
405 pal[187 * 3 + 0] = 255;
406 pal[187 * 3 + 1] = 255;
407 pal[187 * 3 + 2] = 255;
408
409 setPalette(0, 256, pal, RDPAL_FADE);
410 fadeUp();
411 free(text_spr);
412 waitForFade();
413
414 if (time > 0) {
415 uint32 targetTime = _vm->_system->getMillis() + (time * 1000);
416 _vm->sleepUntil(targetTime);
417 } else {
418 while (!_vm->shouldQuit()) {
419 MouseEvent *me = _vm->mouseEvent();
420 if (me && (me->buttons & (RD_LEFTBUTTONDOWN | RD_RIGHTBUTTONDOWN)))
421 break;
422
423 if (_vm->keyboardEvent())
424 break;
425
426 updateDisplay();
427 _vm->_system->delayMillis(50);
428 }
429 }
430
431 fadeDown();
432 waitForFade();
433 clearScene();
434 setPalette(0, 256, oldPal, RDPAL_FADE);
435 fadeUp();
436 }
437
drawBackPar0Frames()438 void Screen::drawBackPar0Frames() {
439 // frame attached to 1st background parallax
440 for (uint i = 0; i < _curBgp0; i++)
441 processImage(&_bgp0List[i]);
442 }
443
drawBackPar1Frames()444 void Screen::drawBackPar1Frames() {
445 // frame attached to 2nd background parallax
446 for (uint i = 0; i < _curBgp1; i++)
447 processImage(&_bgp1List[i]);
448 }
449
drawBackFrames()450 void Screen::drawBackFrames() {
451 // background sprite, fixed to main background
452 for (uint i = 0; i < _curBack; i++)
453 processImage(&_backList[i]);
454 }
455
drawSortFrames(byte * file)456 void Screen::drawSortFrames(byte *file) {
457 uint i, j;
458
459 // Sort the sort list. Used to be a separate function, but it was only
460 // called once, right before calling drawSortFrames().
461
462 if (_curSort > 1) {
463 for (i = 0; i < _curSort - 1; i++) {
464 for (j = 0; j < _curSort - 1; j++) {
465 if (_sortList[_sortOrder[j]].sort_y > _sortList[_sortOrder[j + 1]].sort_y) {
466 SWAP(_sortOrder[j], _sortOrder[j + 1]);
467 }
468 }
469 }
470 }
471
472 // Draw the sorted frames - layers, shrinkers & normal flat sprites
473
474 for (i = 0; i < _curSort; i++) {
475 if (_sortList[_sortOrder[i]].layer_number) {
476 // it's a layer - minus 1 for true layer number
477 // we need to know from the BuildUnit because the
478 // layers will have been sorted in random order
479 processLayer(file, _sortList[_sortOrder[i]].layer_number - 1);
480 } else {
481 // it's a sprite
482 processImage(&_sortList[_sortOrder[i]]);
483 }
484 }
485 }
486
drawForeFrames()487 void Screen::drawForeFrames() {
488 // foreground sprite, fixed to main background
489 for (uint i = 0; i < _curFore; i++)
490 processImage(&_foreList[i]);
491 }
492
drawForePar0Frames()493 void Screen::drawForePar0Frames() {
494 // frame attached to 1st foreground parallax
495 for (uint i = 0; i < _curFgp0; i++)
496 processImage(&_fgp0List[i]);
497 }
498
drawForePar1Frames()499 void Screen::drawForePar1Frames() {
500 // frame attached to 2nd foreground parallax
501 for (uint i = 0; i < _curFgp1; i++)
502 processImage(&_fgp1List[i]);
503 }
504
processLayer(byte * file,uint32 layer_number)505 void Screen::processLayer(byte *file, uint32 layer_number) {
506
507 LayerHeader layer_head;
508
509 layer_head.read(_vm->fetchLayerHeader(file, layer_number));
510
511 SpriteInfo spriteInfo;
512
513 spriteInfo.x = layer_head.x;
514 spriteInfo.y = layer_head.y;
515 spriteInfo.w = layer_head.width;
516 spriteInfo.scale = 0;
517 spriteInfo.scaledWidth = 0;
518 spriteInfo.scaledHeight = 0;
519 spriteInfo.h = layer_head.height;
520 spriteInfo.isText = false;
521
522 // Layers are uncompressed in PSX version, RLE256 compressed
523 // in PC version.
524 if (Sword2Engine::isPsx()) {
525 spriteInfo.type = RDSPR_TRANS | RDSPR_NOCOMPRESSION;
526 spriteInfo.data = file + layer_head.offset;
527 } else {
528 spriteInfo.type = RDSPR_TRANS | RDSPR_RLE256FAST;
529 spriteInfo.data = file + ResHeader::size() + layer_head.offset;
530 }
531
532 spriteInfo.blend = 0;
533 spriteInfo.colorTable = 0;
534
535 // check for largest layer for debug info
536
537 uint32 current_layer_area = layer_head.width * layer_head.height;
538
539 if (current_layer_area > _largestLayerArea) {
540 _largestLayerArea = current_layer_area;
541 sprintf(_largestLayerInfo,
542 "largest layer: %s layer(%d) is %dx%d",
543 _vm->_resman->fetchName(_thisScreen.background_layer_id),
544 layer_number, layer_head.width, layer_head.height);
545 }
546
547 uint32 rv = drawSprite(&spriteInfo);
548 if (rv)
549 error("Driver Error %.8x in processLayer(%d)", rv, layer_number);
550 }
551
processImage(BuildUnit * build_unit)552 void Screen::processImage(BuildUnit *build_unit) {
553
554 // We have some problematic animation frames in PSX demo (looks like there is missing data),
555 // so we just skip them.
556 if ( (Sword2Engine::isPsx() && _vm->_logic->readVar(DEMO)) &&
557 ((build_unit->anim_resource == 369 && build_unit->anim_pc == 0) ||
558 (build_unit->anim_resource == 296 && build_unit->anim_pc == 5) ||
559 (build_unit->anim_resource == 534 && build_unit->anim_pc == 13) ||
560 (build_unit->anim_resource == 416 && build_unit->anim_pc == 41)) )
561 return;
562
563 byte *file = _vm->_resman->openResource(build_unit->anim_resource);
564 byte *colTablePtr = NULL;
565
566 byte *frame = _vm->fetchFrameHeader(file, build_unit->anim_pc);
567
568 AnimHeader anim_head;
569 CdtEntry cdt_entry;
570 FrameHeader frame_head;
571
572 anim_head.read(_vm->fetchAnimHeader(file));
573 cdt_entry.read(_vm->fetchCdtEntry(file, build_unit->anim_pc));
574 frame_head.read(frame);
575
576 // so that 0-color is transparent
577 uint32 spriteType = RDSPR_TRANS;
578
579 if (anim_head.blend)
580 spriteType |= RDSPR_BLEND;
581
582 // if the frame is to be flipped (only really applicable to frames
583 // using offsets)
584 if (cdt_entry.frameType & FRAME_FLIPPED)
585 spriteType |= RDSPR_FLIP;
586
587 if (cdt_entry.frameType & FRAME_256_FAST) {
588 // scaling, shading & blending don't work with RLE256FAST
589 // but the same compression can be decompressed using the
590 // RLE256 routines!
591
592 // NOTE: If this restriction refers to drawSprite(), I don't
593 // think we have it any more. But I'm not sure.
594
595 if (build_unit->scale || anim_head.blend || build_unit->shadingFlag)
596 spriteType |= RDSPR_RLE256;
597 else
598 spriteType |= RDSPR_RLE256FAST;
599 } else {
600 switch (anim_head.runTimeComp) {
601 case NONE:
602 spriteType |= RDSPR_NOCOMPRESSION;
603 break;
604 case RLE256:
605 spriteType |= RDSPR_RLE256;
606 break;
607 case RLE16:
608 spriteType |= RDSPR_RLE16;
609 // points to just after last cdt_entry, ie.
610 // start of color table
611 colTablePtr = _vm->fetchAnimHeader(file) + AnimHeader::size() + anim_head.noAnimFrames * CdtEntry::size();
612 if (Sword2Engine::isPsx())
613 colTablePtr++; // There is one additional byte to skip before the table in psx version
614 break;
615 default:
616 break;
617 }
618 }
619
620 // if we want this frame to be affected by the shading mask,
621 // add the status bit
622 if (build_unit->shadingFlag)
623 spriteType |= RDSPR_SHADOW;
624
625 SpriteInfo spriteInfo;
626
627 spriteInfo.x = build_unit->x;
628 spriteInfo.y = build_unit->y;
629 spriteInfo.w = frame_head.width;
630 spriteInfo.h = frame_head.height;
631 spriteInfo.scale = build_unit->scale;
632 spriteInfo.scaledWidth = build_unit->scaled_width;
633 spriteInfo.scaledHeight = build_unit->scaled_height;
634 spriteInfo.type = spriteType;
635 spriteInfo.blend = anim_head.blend;
636 // points to just after frame header, ie. start of sprite data
637 spriteInfo.data = frame + FrameHeader::size();
638 spriteInfo.colorTable = colTablePtr;
639 spriteInfo.isText = false;
640
641 // check for largest layer for debug info
642 uint32 current_sprite_area = frame_head.width * frame_head.height;
643
644 if (current_sprite_area > _largestSpriteArea) {
645 _largestSpriteArea = current_sprite_area;
646 sprintf(_largestSpriteInfo,
647 "largest sprite: %s frame(%d) is %dx%d",
648 _vm->_resman->fetchName(build_unit->anim_resource),
649 build_unit->anim_pc,
650 frame_head.width,
651 frame_head.height);
652 }
653
654 if (_vm->_logic->readVar(SYSTEM_TESTING_ANIMS)) { // see anims.cpp
655 // bring the anim into the visible screen
656 // but leave extra pixel at edge for box
657 if (spriteInfo.x + spriteInfo.scaledWidth >= 639)
658 spriteInfo.x = 639 - spriteInfo.scaledWidth;
659
660 if (spriteInfo.y + spriteInfo.scaledHeight >= 399)
661 spriteInfo.y = 399 - spriteInfo.scaledHeight;
662
663 if (spriteInfo.x < 1)
664 spriteInfo.x = 1;
665
666 if (spriteInfo.y < 1)
667 spriteInfo.y = 1;
668
669 // create box to surround sprite - just outside sprite box
670 _vm->_debugger->_rectX1 = spriteInfo.x - 1;
671 _vm->_debugger->_rectY1 = spriteInfo.y - 1;
672 _vm->_debugger->_rectX2 = spriteInfo.x + spriteInfo.scaledWidth;
673 _vm->_debugger->_rectY2 = spriteInfo.y + spriteInfo.scaledHeight;
674 }
675
676 uint32 rv = drawSprite(&spriteInfo);
677 if (rv) {
678 error("Driver Error %.8x with sprite %s (%d, %d) in processImage",
679 rv,
680 _vm->_resman->fetchName(build_unit->anim_resource),
681 build_unit->anim_resource, build_unit->anim_pc);
682 }
683
684 // release the anim resource
685 _vm->_resman->closeResource(build_unit->anim_resource);
686 }
687
resetRenderLists()688 void Screen::resetRenderLists() {
689 // reset the sort lists - do this before a logic loop
690 // takes into account the fact that the start of the list is pre-built
691 // with the special sortable layers
692
693 _curBgp0 = 0;
694 _curBgp1 = 0;
695 _curBack = 0;
696 // beginning of sort list is setup with the special sort layers
697 _curSort = _thisScreen.number_of_layers;
698 _curFore = 0;
699 _curFgp0 = 0;
700 _curFgp1 = 0;
701
702 if (_curSort) {
703 // there are some layers - so rebuild the sort order list
704 for (uint i = 0; i < _curSort; i++)
705 _sortOrder[i] = i;
706 }
707 }
708
registerFrame(byte * ob_mouse,byte * ob_graph,byte * ob_mega,BuildUnit * build_unit)709 void Screen::registerFrame(byte *ob_mouse, byte *ob_graph, byte *ob_mega, BuildUnit *build_unit) {
710 ObjectGraphic obGraph(ob_graph);
711 ObjectMega obMega(ob_mega);
712
713 assert(obGraph.getAnimResource());
714
715 byte *file = _vm->_resman->openResource(obGraph.getAnimResource());
716
717 AnimHeader anim_head;
718 CdtEntry cdt_entry;
719 FrameHeader frame_head;
720
721 anim_head.read(_vm->fetchAnimHeader(file));
722 cdt_entry.read(_vm->fetchCdtEntry(file, obGraph.getAnimPc()));
723 frame_head.read(_vm->fetchFrameHeader(file, obGraph.getAnimPc()));
724
725 // update player graphic details for on-screen debug info
726 if (_vm->_logic->readVar(ID) == CUR_PLAYER_ID) {
727 _vm->_debugger->_graphType = obGraph.getType();
728 _vm->_debugger->_graphAnimRes = obGraph.getAnimResource();
729 // counting 1st frame as 'frame 1'
730 _vm->_debugger->_graphAnimPc = obGraph.getAnimPc() + 1;
731 _vm->_debugger->_graphNoFrames = anim_head.noAnimFrames;
732 }
733
734 // fill in the BuildUnit structure for this frame
735
736 build_unit->anim_resource = obGraph.getAnimResource();
737 build_unit->anim_pc = obGraph.getAnimPc();
738 build_unit->layer_number = 0;
739
740 // Affected by shading mask?
741 if (obGraph.getType() & SHADED_SPRITE)
742 build_unit->shadingFlag = true;
743 else
744 build_unit->shadingFlag = false;
745
746 // Check if this frame has offsets ie. this is a scalable mega frame
747
748 int scale = 0;
749
750 if (cdt_entry.frameType & FRAME_OFFSET) {
751 scale = obMega.calcScale();
752
753 // calc final render coordinates (top-left of sprite), based
754 // on feet coords & scaled offsets
755
756 // add scaled offsets to feet coords
757 build_unit->x = obMega.getFeetX() + (cdt_entry.x * scale) / 256;
758 build_unit->y = obMega.getFeetY() + (cdt_entry.y * scale) / 256;
759
760 // Work out new width and height. Always divide by 256 after
761 // everything else, to maintain accurary
762 build_unit->scaled_width = ((scale * frame_head.width) / 256);
763 build_unit->scaled_height = ((scale * frame_head.height) / 256);
764 } else {
765 // It's a non-scaling anim. Get render coords for sprite, from cdt
766 build_unit->x = cdt_entry.x;
767 build_unit->y = cdt_entry.y;
768
769 // Get width and height
770 build_unit->scaled_width = frame_head.width;
771 build_unit->scaled_height = frame_head.height;
772 }
773
774 // either 0 or required scale, depending on whether 'scale' computed
775 build_unit->scale = scale;
776
777 // calc the bottom y-coord for sorting purposes
778 build_unit->sort_y = build_unit->y + build_unit->scaled_height - 1;
779
780 if (ob_mouse) {
781 // passed a mouse structure, so add to the _mouseList
782 _vm->_mouse->registerMouse(ob_mouse, build_unit);
783
784 }
785
786 _vm->_resman->closeResource(obGraph.getAnimResource());
787 }
788
registerFrame(byte * ob_mouse,byte * ob_graph,byte * ob_mega)789 void Screen::registerFrame(byte *ob_mouse, byte *ob_graph, byte *ob_mega) {
790 ObjectGraphic obGraph(ob_graph);
791
792 // check low word for sprite type
793 switch (obGraph.getType() & 0x0000ffff) {
794 case BGP0_SPRITE:
795 assert(_curBgp0 < MAX_bgp0_sprites);
796 registerFrame(ob_mouse, ob_graph, ob_mega, &_bgp0List[_curBgp0]);
797 _curBgp0++;
798 break;
799 case BGP1_SPRITE:
800 assert(_curBgp1 < MAX_bgp1_sprites);
801 registerFrame(ob_mouse, ob_graph, ob_mega, &_bgp1List[_curBgp1]);
802 _curBgp1++;
803 break;
804 case BACK_SPRITE:
805 assert(_curBack < MAX_back_sprites);
806 registerFrame(ob_mouse, ob_graph, ob_mega, &_backList[_curBack]);
807 _curBack++;
808 break;
809 case SORT_SPRITE:
810 assert(_curSort < MAX_sort_sprites);
811 _sortOrder[_curSort] = _curSort;
812 registerFrame(ob_mouse, ob_graph, ob_mega, &_sortList[_curSort]);
813 _curSort++;
814 break;
815 case FORE_SPRITE:
816 assert(_curFore < MAX_fore_sprites);
817 registerFrame(ob_mouse, ob_graph, ob_mega, &_foreList[_curFore]);
818 _curFore++;
819 break;
820 case FGP0_SPRITE:
821 assert(_curFgp0 < MAX_fgp0_sprites);
822 registerFrame(ob_mouse, ob_graph, ob_mega, &_fgp0List[_curFgp0]);
823 _curFgp0++;
824 break;
825 case FGP1_SPRITE:
826 assert(_curFgp1 < MAX_fgp1_sprites);
827 registerFrame(ob_mouse, ob_graph, ob_mega, &_fgp1List[_curFgp1]);
828 _curFgp1++;
829 break;
830 default:
831 // NO_SPRITE no registering!
832 break;
833 }
834 }
835
836 // FIXME:
837 //
838 // The original credits used a different font. I think it's stored in the
839 // font.clu file, but I don't know how to interpret it.
840 //
841 // The original used the entire screen. This version cuts off the top and
842 // bottom of the screen, because that's where the menus would usually be.
843 //
844 // The original had some sort of smoke effect at the bottom of the screen.
845
846 enum {
847 LINE_LEFT,
848 LINE_CENTER,
849 LINE_RIGHT
850 };
851
852 struct CreditsLine {
853 Common::String str;
854 byte type;
855 int top;
856 int height;
857 byte *sprite;
858
CreditsLineSword2::CreditsLine859 CreditsLine() {
860 sprite = NULL;
861 }
862
~CreditsLineSword2::CreditsLine863 ~CreditsLine() {
864 free(sprite);
865 }
866 };
867
868 #define CREDITS_FONT_HEIGHT 25
869 #define CREDITS_LINE_SPACING 20
870
rollCredits()871 void Screen::rollCredits() {
872 uint32 loopingMusicId = _vm->_sound->getLoopingMusicId();
873
874 // Prepare for the credits by fading down, stoping the music, etc.
875
876 _vm->_mouse->setMouse(0);
877
878 _vm->_sound->muteFx(true);
879 _vm->_sound->muteSpeech(true);
880
881 waitForFade();
882 fadeDown();
883 waitForFade();
884
885 _vm->_mouse->closeMenuImmediately();
886
887 // There are three files which I believe are involved in showing the
888 // credits:
889 //
890 // credits.bmp - The "Smacker" logo, stored as follows:
891 //
892 // width 2 bytes, little endian
893 // height 2 bytes, little endian
894 // palette 3 * 256 bytes
895 // data width * height bytes
896 //
897 // Note that the maximum color component in the palette is 0x3F.
898 // This is the same resolution as the _paletteMatch table. I doubt
899 // that this is a coincidence, but let's use the image palette
900 // directly anyway, just to be safe.
901 //
902 // credits.clu - The credits text (credits.txt in PSX version)
903 //
904 // This is simply a text file with CRLF line endings.
905 // '^' is not shown, but used to mark the center of the line.
906 // '@' is used as a placeholder for the "Smacker" logo. At least
907 // when it appears alone.
908 // Remaining lines are centered.
909 // The German version also contains character code 9 for no
910 // apparent reason. We ignore them.
911 //
912 // fonts.clu - The credits font?
913 //
914 // FIXME: At this time I don't know how to interpret fonts.clu. For
915 // now, let's just the standard speech font instead.
916
917 SpriteInfo spriteInfo;
918 Common::File f;
919 int i;
920
921 spriteInfo.isText = false;
922
923 // Read the "Smacker" logo
924
925 uint16 logoWidth = 0;
926 uint16 logoHeight = 0;
927 byte *logoData = NULL;
928 byte palette[256 * 3];
929
930 if (f.open("credits.bmp")) {
931 logoWidth = f.readUint16LE();
932 logoHeight = f.readUint16LE();
933
934 for (i = 0; i < 256; i++) {
935 palette[i * 3 + 0] = f.readByte() << 2;
936 palette[i * 3 + 1] = f.readByte() << 2;
937 palette[i * 3 + 2] = f.readByte() << 2;
938 }
939
940 logoData = (byte *)malloc(logoWidth * logoHeight);
941
942 f.read(logoData, logoWidth * logoHeight);
943 f.close();
944 } else {
945 warning("Can't find credits.bmp");
946 memset(palette, 0, sizeof(palette));
947 palette[14 * 3 + 0] = 252;
948 palette[14 * 3 + 1] = 252;
949 palette[14 * 3 + 2] = 252;
950 }
951
952 setPalette(0, 256, palette, RDPAL_INSTANT);
953
954 // Read the credits text
955
956 Common::Array<CreditsLine *> creditsLines;
957
958 int lineCount = 0;
959 int lineTop = 400;
960 int paragraphStart = 0;
961 bool hasCenterMark = false;
962
963 if (Sword2Engine::isPsx()) {
964 if (!f.open("credits.txt")) {
965 warning("Can't find credits.txt");
966
967 free(logoData);
968 return;
969 }
970 } else {
971 if (!f.open("credits.clu")) {
972 warning("Can't find credits.clu");
973
974 free(logoData);
975 return;
976 }
977 }
978
979 while (1) {
980 char buffer[80];
981 char *line = f.readLine(buffer, sizeof(buffer));
982
983 if (line) {
984 // Replace invalid character codes prevent the 'dud'
985 // symbol from showing up in the credits.
986
987 for (byte *ptr = (byte *)line; *ptr; ptr++) {
988 switch (*ptr) {
989 case 9:
990 // The German credits contain these.
991 // Convert them to spaces.
992 *ptr = 32;
993 break;
994 case 10:
995 // LF is treated as end of line.
996 *ptr = 0;
997 break;
998 case 170:
999 // The Spanish credits contain these.
1000 // Convert them to periods.
1001 *ptr = '.';
1002 default:
1003 break;
1004 }
1005 }
1006 }
1007
1008 if (!line || *line == 0) {
1009 if (!hasCenterMark) {
1010 for (i = paragraphStart; i < lineCount; i++)
1011 creditsLines[i]->type = LINE_CENTER;
1012 }
1013 paragraphStart = lineCount;
1014 hasCenterMark = false;
1015 if (paragraphStart == lineCount)
1016 lineTop += CREDITS_LINE_SPACING;
1017
1018 if (!line)
1019 break;
1020
1021 continue;
1022 }
1023
1024 char *center_mark = strchr(line, '^');
1025
1026 if (center_mark) {
1027 // The current paragraph has at least one center mark.
1028 hasCenterMark = true;
1029
1030 if (center_mark != line) {
1031 creditsLines.push_back(new CreditsLine);
1032
1033 // The center mark is somewhere inside the
1034 // line. Split it into left and right side.
1035 *center_mark = 0;
1036
1037 creditsLines[lineCount]->top = lineTop;
1038 creditsLines[lineCount]->height = CREDITS_FONT_HEIGHT;
1039 creditsLines[lineCount]->type = LINE_LEFT;
1040 creditsLines[lineCount]->str = line;
1041
1042 lineCount++;
1043 *center_mark = '^';
1044 }
1045
1046 line = center_mark;
1047 }
1048
1049 creditsLines.push_back(new CreditsLine);
1050
1051 creditsLines[lineCount]->top = lineTop;
1052
1053 if (*line == '^') {
1054 creditsLines[lineCount]->type = LINE_RIGHT;
1055 line++;
1056 } else
1057 creditsLines[lineCount]->type = LINE_LEFT;
1058
1059 if (strcmp(line, "@") == 0) {
1060 creditsLines[lineCount]->height = logoHeight;
1061 lineTop += logoHeight;
1062 } else {
1063 creditsLines[lineCount]->height = CREDITS_FONT_HEIGHT;
1064 lineTop += CREDITS_LINE_SPACING;
1065 }
1066
1067 creditsLines[lineCount]->str = line;
1068 lineCount++;
1069 }
1070
1071 f.close();
1072
1073 // We could easily add some ScummVM stuff to the credits, if we wanted
1074 // to. On the other hand, anyone with the attention span to actually
1075 // read all the credits probably already knows. :-)
1076
1077 // Start the music and roll the credits
1078
1079 // The credits music (which can also be heard briefly in the "carib"
1080 // cutscene) is played once.
1081
1082 _vm->_sound->streamCompMusic(309, false);
1083
1084 clearScene();
1085 fadeUp(0);
1086
1087 spriteInfo.scale = 0;
1088 spriteInfo.scaledWidth = 0;
1089 spriteInfo.scaledHeight = 0;
1090 spriteInfo.type = RDSPR_DISPLAYALIGN | RDSPR_NOCOMPRESSION | RDSPR_TRANS;
1091 spriteInfo.blend = 0;
1092
1093 int startLine = 0;
1094 int scrollPos = 0;
1095
1096 bool abortCredits = false;
1097
1098 int scrollSteps = lineTop + CREDITS_FONT_HEIGHT;
1099 uint32 musicStart = getTick();
1100
1101 // Ideally the music should last just a tiny bit longer than the
1102 // credits. Note that musicTimeRemaining() will return 0 if the music
1103 // is muted, so we need a sensible fallback for that case.
1104
1105 uint32 musicLength = MAX((int32)(1000 * (_vm->_sound->musicTimeRemaining() - 3)), 25 * (int32)scrollSteps);
1106
1107 while (scrollPos < scrollSteps && !_vm->shouldQuit()) {
1108 clearScene();
1109
1110 for (i = startLine; i < lineCount; i++) {
1111 if (!creditsLines[i])
1112 continue;
1113
1114 // Free any sprites that have scrolled off the screen
1115
1116 if (creditsLines[i]->top + creditsLines[i]->height < scrollPos) {
1117 debug(2, "Freeing line %d: '%s'", i, creditsLines[i]->str.c_str());
1118
1119 delete creditsLines[i];
1120 creditsLines[i] = NULL;
1121
1122 startLine = i + 1;
1123 } else if (creditsLines[i]->top < scrollPos + 400) {
1124 if (!creditsLines[i]->sprite) {
1125 debug(2, "Creating line %d: '%s'", i, creditsLines[i]->str.c_str());
1126 creditsLines[i]->sprite = _vm->_fontRenderer->makeTextSprite((const byte *)creditsLines[i]->str.c_str(), 600, 14, _vm->_speechFontId, 0);
1127 }
1128
1129 FrameHeader frame;
1130
1131 frame.read(creditsLines[i]->sprite);
1132
1133 spriteInfo.y = creditsLines[i]->top - scrollPos;
1134 spriteInfo.w = frame.width;
1135 spriteInfo.h = frame.height;
1136 spriteInfo.data = creditsLines[i]->sprite + FrameHeader::size();
1137 spriteInfo.isText = true;
1138
1139 switch (creditsLines[i]->type) {
1140 case LINE_LEFT:
1141 spriteInfo.x = RENDERWIDE / 2 - 5 - frame.width;
1142 break;
1143 case LINE_RIGHT:
1144 spriteInfo.x = RENDERWIDE / 2 + 5;
1145 break;
1146 case LINE_CENTER:
1147 if (strcmp(creditsLines[i]->str.c_str(), "@") == 0) {
1148 spriteInfo.data = logoData;
1149 spriteInfo.x = (RENDERWIDE - logoWidth) / 2;
1150 spriteInfo.w = logoWidth;
1151 spriteInfo.h = logoHeight;
1152 } else {
1153 spriteInfo.x = (RENDERWIDE - frame.width) / 2;
1154 }
1155 break;
1156 default:
1157 break;
1158 }
1159
1160 if (spriteInfo.data)
1161 drawSprite(&spriteInfo);
1162 } else
1163 break;
1164 }
1165
1166 updateDisplay();
1167
1168 KeyboardEvent *ke = _vm->keyboardEvent();
1169
1170 if (ke && ke->kbd.keycode == Common::KEYCODE_ESCAPE) {
1171 if (!abortCredits) {
1172 abortCredits = true;
1173 fadeDown();
1174 }
1175 }
1176
1177 if (abortCredits && getFadeStatus() == RDFADE_BLACK)
1178 break;
1179
1180 _vm->sleepUntil(musicStart + (musicLength * scrollPos) / scrollSteps + _pauseTicks);
1181 scrollPos++;
1182 }
1183
1184 // We're done. Clean up and try to put everything back where it was
1185 // before the credits.
1186
1187 for (i = 0; i < lineCount; i++) {
1188 delete creditsLines[i];
1189 }
1190
1191 free(logoData);
1192
1193 if (!abortCredits) {
1194 fadeDown();
1195
1196 // The music should either have stopped or be about to stop, so
1197 // wait for it to really happen.
1198
1199 while (_vm->_sound->musicTimeRemaining() && !_vm->shouldQuit()) {
1200 updateDisplay(false);
1201 _vm->_system->delayMillis(100);
1202 }
1203 }
1204
1205 if (_vm->shouldQuit())
1206 return;
1207
1208 waitForFade();
1209
1210 _vm->_sound->muteFx(false);
1211 _vm->_sound->muteSpeech(false);
1212
1213 if (loopingMusicId)
1214 _vm->_sound->streamCompMusic(loopingMusicId, true);
1215 else
1216 _vm->_sound->stopMusic(false);
1217
1218 if (!_vm->_mouse->getMouseStatus() || _vm->_mouse->isChoosing())
1219 _vm->_mouse->setMouse(NORMAL_MOUSE_ID);
1220
1221 if (_vm->_logic->readVar(DEAD))
1222 _vm->_mouse->buildSystemMenu();
1223 }
1224
1225 // This image used to be shown by CacheNewCluster() while copying a data file
1226 // from the CD to the hard disk. ScummVM doesn't do that, so the image is never
1227 // shown. It'd be nice if we could do something useful with it some day...
1228
splashScreen()1229 void Screen::splashScreen() {
1230 byte *bgfile = _vm->_resman->openResource(2950);
1231
1232 initializeBackgroundLayer(NULL);
1233 initializeBackgroundLayer(NULL);
1234 initializeBackgroundLayer(_vm->fetchBackgroundLayer(bgfile));
1235 initializeBackgroundLayer(NULL);
1236 initializeBackgroundLayer(NULL);
1237
1238 _vm->fetchPalette(bgfile, _palette);
1239 setPalette(0, 256, _palette, RDPAL_FADE);
1240 renderParallax(_vm->fetchBackgroundLayer(bgfile), 2);
1241
1242 closeBackgroundLayer();
1243
1244 byte *loadingBar = _vm->_resman->openResource(2951);
1245 byte *frame = _vm->fetchFrameHeader(loadingBar, 0);
1246
1247 AnimHeader animHead;
1248 CdtEntry cdt;
1249 FrameHeader frame_head;
1250
1251 animHead.read(_vm->fetchAnimHeader(loadingBar));
1252 cdt.read(_vm->fetchCdtEntry(loadingBar, 0));
1253 frame_head.read(_vm->fetchFrameHeader(loadingBar, 0));
1254
1255 SpriteInfo barSprite;
1256
1257 barSprite.x = cdt.x;
1258 barSprite.y = cdt.y;
1259 barSprite.w = frame_head.width;
1260 barSprite.h = frame_head.height;
1261 barSprite.scale = 0;
1262 barSprite.scaledWidth = 0;
1263 barSprite.scaledHeight = 0;
1264 barSprite.type = RDSPR_RLE256FAST | RDSPR_TRANS;
1265 barSprite.blend = 0;
1266 barSprite.colorTable = 0;
1267 barSprite.data = frame + FrameHeader::size();
1268 barSprite.isText = false;
1269
1270 drawSprite(&barSprite);
1271
1272 fadeUp();
1273 waitForFade();
1274
1275 for (int i = 0; i < animHead.noAnimFrames; i++) {
1276 frame = _vm->fetchFrameHeader(loadingBar, i);
1277 barSprite.data = frame + FrameHeader::size();
1278 drawSprite(&barSprite);
1279 updateDisplay();
1280 _vm->_system->delayMillis(30);
1281 }
1282
1283 _vm->_resman->closeResource(2951);
1284
1285 fadeDown();
1286 waitForFade();
1287 }
1288
1289 // Following functions are used to manage screen cache for psx version.
1290
setPsxScrCache(byte * psxScrCache,uint8 level)1291 void Screen::setPsxScrCache(byte *psxScrCache, uint8 level) {
1292 if (level < 3) {
1293 if (psxScrCache)
1294 _psxCacheEnabled[level] = true;
1295 else
1296 _psxCacheEnabled[level] = false;
1297
1298 _psxScrCache[level] = psxScrCache;
1299 }
1300 }
1301
getPsxScrCache(uint8 level)1302 byte *Screen::getPsxScrCache(uint8 level) {
1303 if (level > 2) {
1304 level = 0;
1305 }
1306
1307 if (_psxCacheEnabled[level])
1308 return _psxScrCache[level];
1309 else
1310 return NULL;
1311 }
1312
getPsxScrCacheStatus(uint8 level)1313 bool Screen::getPsxScrCacheStatus(uint8 level) {
1314 if (level > 2) {
1315 level = 0;
1316 }
1317
1318 return _psxCacheEnabled[level];
1319 }
1320
flushPsxScrCache()1321 void Screen::flushPsxScrCache() {
1322 for (uint8 i = 0; i < 3; i++) {
1323 free(_psxScrCache[i]);
1324 _psxScrCache[i] = NULL;
1325 _psxCacheEnabled[i] = true;
1326 }
1327 }
1328
1329 } // End of namespace Sword2
1330