1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "common/scummsys.h"
24 #include "mads/game.h"
25 #include "mads/mads.h"
26 #include "mads/menu_views.h"
27 #include "mads/resources.h"
28 #include "mads/scene.h"
29 #include "mads/screen.h"
30 
31 namespace MADS {
32 
MenuView(MADSEngine * vm)33 MenuView::MenuView(MADSEngine *vm) : FullScreenDialog(vm) {
34 	_breakFlag = false;
35 	_redrawFlag = true;
36 	_palFlag = false;
37 }
38 
show()39 void MenuView::show() {
40  	Scene &scene = _vm->_game->_scene;
41 	EventsManager &events = *_vm->_events;
42 	_vm->_screenFade = SCREEN_FADE_FAST;
43 
44 	scene._spriteSlots.reset(true);
45 	display();
46 
47 	events.setEventTarget(this);
48 	events.hideCursor();
49 
50 	while (!_breakFlag && !_vm->shouldQuit()) {
51 		if (_redrawFlag) {
52 			scene._kernelMessages.update();
53 
54 			_vm->_game->_scene.drawElements(_vm->_game->_fx, _vm->_game->_fx);
55 			_redrawFlag = false;
56 		}
57 
58 		_vm->_events->waitForNextFrame();
59 		_vm->_game->_fx = kTransitionNone;
60 		doFrame();
61 	}
62 
63 	events.setEventTarget(nullptr);
64 	_vm->_sound->stop();
65 }
66 
display()67 void MenuView::display() {
68 	_vm->_palette->resetGamePalette(4, 8);
69 
70 	FullScreenDialog::display();
71 }
72 
onEvent(Common::Event & event)73 bool MenuView::onEvent(Common::Event &event) {
74 	if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONDOWN) {
75 		_breakFlag = true;
76 		_vm->_dialogs->_pendingDialog = DIALOG_MAIN_MENU;
77 		return true;
78 	}
79 
80 	return false;
81 }
82 
getResourceName()83 Common::String MenuView::getResourceName() {
84 	Common::String s(_filename);
85 	s.toLowercase();
86 	while (s.contains('.'))
87 		s.deleteLastChar();
88 
89 	return s;
90 }
91 
92 /*------------------------------------------------------------------------*/
93 
94 char TextView::_resourceName[100];
95 #define TEXTVIEW_LINE_SPACING 2
96 #define TEXT_ANIMATION_DELAY 100
97 #define TV_NUM_FADE_STEPS 40
98 #define TV_FADE_DELAY_MILLI 50
99 
execute(MADSEngine * vm,const Common::String & resName)100 void TextView::execute(MADSEngine *vm, const Common::String &resName) {
101 	assert(resName.size() < 100);
102 	Common::strlcpy(_resourceName, resName.c_str(), sizeof(_resourceName));
103 	vm->_dialogs->_pendingDialog = DIALOG_TEXTVIEW;
104 }
105 
TextView(MADSEngine * vm)106 TextView::TextView(MADSEngine *vm) : MenuView(vm) {
107 	_animating = false;
108 	_panSpeed = 0;
109 	_spareScreen = nullptr;
110 	_scrollCount = 0;
111 	_lineY = -1;
112 	_scrollTimeout = 0;
113 	_panCountdown = 0;
114 	_translationX = 0;
115 	_screenId = -1;
116 
117 	_font = _vm->_font->getFont(FONT_CONVERSATION);
118 	_vm->_palette->resetGamePalette(4, 0);
119 
120 	load();
121 }
122 
~TextView()123 TextView::~TextView() {
124 	// Turn off palette cycling as well as any playing sound
125 	Scene &scene = _vm->_game->_scene;
126 	scene._cyclingActive = false;
127 	_vm->_sound->stop();
128 }
129 
load()130 void TextView::load() {
131 	Common::String scriptName(_resourceName);
132 	scriptName += ".txr";
133 
134 	_filename = scriptName;
135 	if (!_script.open(scriptName))
136 		error("Could not open resource %s", _resourceName);
137 
138 	processLines();
139 }
140 
processLines()141 void TextView::processLines() {
142 	if (_script.eos())
143 		error("Attempted to read past end of response file");
144 
145 	while (!_script.eos()) {
146 		// Read in the next line
147 		_script.readLine(_currentLine, 79);
148 		char *p = _currentLine + strlen(_currentLine) - 1;
149 		if (*p == '\n')
150 			*p = '\0';
151 
152 		// Commented out line, so go loop for another
153 		if (_currentLine[0] == '#')
154 			continue;
155 
156 		// Process the line
157 		char *cStart = strchr(_currentLine, '[');
158 		if (cStart) {
159 			while (cStart) {
160 				// Loop for possible multiple commands on one line
161 				char *cEnd = strchr(_currentLine, ']');
162 				if (!cEnd)
163 					error("Unterminated command '%s' in response file", _currentLine);
164 
165 				*cEnd = '\0';
166 				processCommand();
167 
168 				// Copy rest of line (if any) to start of buffer
169 				Common::strlcpy(_currentLine, cEnd + 1, sizeof(_currentLine));
170 
171 				cStart = strchr(_currentLine, '[');
172 			}
173 
174 			if (_currentLine[0]) {
175 				processText();
176 				break;
177 			}
178 
179 		} else {
180 			processText();
181 			break;
182 		}
183 	}
184 }
185 
processCommand()186 void TextView::processCommand() {
187 	Scene &scene = _vm->_game->_scene;
188 	Common::String scriptLine(_currentLine + 1);
189 	scriptLine.toUppercase();
190 	const char *paramP;
191 	const char *commandStr = scriptLine.c_str();
192 
193 	if (!strncmp(commandStr, "BACKGROUND", 10)) {
194 		// Set the background
195 		paramP = commandStr + 10;
196 		resetPalette();
197 		int screenId = getParameter(&paramP);
198 
199 		SceneInfo *sceneInfo = SceneInfo::init(_vm);
200 		sceneInfo->load(screenId, 0, "", 0, scene._depthSurface, scene._backgroundSurface);
201 		scene._spriteSlots.fullRefresh();
202 		_redrawFlag = true;
203 
204 	} else if (!strncmp(commandStr, "GO", 2)) {
205 		_animating = true;
206 
207 	} else if (!strncmp(commandStr, "PAN", 3)) {
208 		// Set panning values
209 		paramP = commandStr + 3;
210 		int panX = getParameter(&paramP);
211 		int panY = getParameter(&paramP);
212 		int panSpeed = getParameter(&paramP);
213 
214 		if ((panX != 0) || (panY != 0)) {
215 			_pan = Common::Point(panX, panY);
216 			_panSpeed = panSpeed;
217 		}
218 
219 	} else if (!strncmp(commandStr, "DRIVER", 6)) {
220 		// Set the driver to use
221 		paramP = commandStr + 7;
222 
223 		if (!strncmp(paramP, "#SOUND.00", 9)) {
224 			int driverNum = paramP[9] - '0';
225 			_vm->_sound->init(driverNum);
226 		}
227 	} else if (!strncmp(commandStr, "SOUND", 5)) {
228 		// Set sound number
229 		paramP = commandStr + 5;
230 		int soundId = getParameter(&paramP);
231 		_vm->_sound->command(soundId);
232 
233 	} else if (!strncmp(commandStr, "COLOR", 5) && ((commandStr[5] == '0') ||
234 			(commandStr[5] == '1'))) {
235 		// Set the text colors
236 		int index = commandStr[5] - '0';
237 		paramP = commandStr + 6;
238 
239 		byte r = getParameter(&paramP);
240 		byte g = getParameter(&paramP);
241 		byte b = getParameter(&paramP);
242 
243 		_vm->_palette->setEntry(5 + index, r, g, b);
244 
245 	} else if (!strncmp(commandStr, "SPARE", 5)) {
246 		// Sets a secondary background number that can be later switched in with a PAGE command
247 		paramP = commandStr + 6;
248 		int spareIndex = commandStr[5] - '0';
249 		assert(spareIndex < 4);
250 		int screenId = getParameter(&paramP);
251 
252 		// Load the spare background
253 		SceneInfo *sceneInfo = SceneInfo::init(_vm);
254 		sceneInfo->_width = MADS_SCREEN_WIDTH;
255 		sceneInfo->_height = MADS_SCENE_HEIGHT;
256 		_spareScreens[spareIndex].create(MADS_SCREEN_WIDTH, MADS_SCENE_HEIGHT);
257 
258 		sceneInfo->loadMadsV1Background(screenId, "", SCENEFLAG_TRANSLATE,
259 			_spareScreens[spareIndex]);
260 		delete sceneInfo;
261 
262 	} else if (!strncmp(commandStr, "PAGE", 4)) {
263 		// Signals to change to a previous specified secondary background
264 		paramP = commandStr + 4;
265 		int spareIndex = getParameter(&paramP);
266 
267 		// Only allow background switches if one isn't currently in progress
268 		if (!_spareScreen && _spareScreens[spareIndex].getPixels() != nullptr) {
269 			_spareScreen = &_spareScreens[spareIndex];
270 			_translationX = 0;
271 		}
272 
273 	} else {
274 		error("Unknown response command: '%s'", commandStr);
275 	}
276 }
277 
getParameter(const char ** paramP)278 int TextView::getParameter(const char **paramP) {
279 	if ((**paramP != '=') && (**paramP != ','))
280 		return 0;
281 
282 	int result = 0;
283 	++*paramP;
284 	while ((**paramP >= '0') && (**paramP <= '9')) {
285 		result = result * 10 + (**paramP - '0');
286 		++*paramP;
287 	}
288 
289 	return result;
290 }
291 
processText()292 void TextView::processText() {
293 	int xStart;
294 
295 	if (!strcmp(_currentLine, "***")) {
296 		// Special signifier for end of script
297 		_scrollCount = _font->getHeight() * 13;
298 		_lineY = -1;
299 		return;
300 	}
301 
302 	_lineY = 0;
303 
304 	// Lines are always centered, except if line contains a '@', in which case the
305 	// '@' marks the position that must be horizontally centered
306 	char *centerP = strchr(_currentLine, '@');
307 	if (centerP) {
308 		*centerP = '\0';
309 		xStart = (MADS_SCREEN_WIDTH / 2) - _font->getWidth(_currentLine);
310 
311 		// Delete the @ character and shift back the remainder of the string
312 		char *p = centerP + 1;
313 		if (*p == ' ') ++p;
314 		strcpy(centerP, p);
315 
316 	} else {
317 		int lineWidth = _font->getWidth(_currentLine);
318 		xStart = (MADS_SCREEN_WIDTH - lineWidth) / 2;
319 	}
320 
321 	// Add the new line to the list of pending lines
322 	TextLine tl;
323 	tl._pos = Common::Point(xStart, MADS_SCENE_HEIGHT);
324 	tl._line = _currentLine;
325 	tl._textDisplayIndex = -1;
326 	_textLines.push_back(tl);
327 }
328 
display()329 void TextView::display() {
330 	FullScreenDialog::display();
331 }
332 
resetPalette()333 void TextView::resetPalette() {
334 	_vm->_palette->resetGamePalette(8, 8);
335 	_vm->_palette->setEntry(5, 0, 63, 63);
336 	_vm->_palette->setEntry(6, 0, 45, 45);
337 }
338 
doFrame()339 void TextView::doFrame() {
340 	Scene &scene = _vm->_game->_scene;
341 	if (!_animating)
342 		return;
343 
344 	// Only update state if wait period has expired
345 	uint32 currTime = g_system->getMillis();
346 
347 	// If a screen transition is in progress and it's time for another column, handle it
348 	if (_spareScreen) {
349 		const byte *srcP = (const byte *)_spareScreen->getBasePtr(_translationX, 0);
350 		byte *bgP = (byte *)scene._backgroundSurface.getBasePtr(_translationX, 0);
351 
352 		Graphics::Surface dest = _vm->_screen->getSubArea(Common::Rect(_translationX, 0, _translationX + 1, 0));
353 		byte *screenP = (byte *)dest.getBasePtr(0, 0);
354 
355 		for (int y = 0; y < MADS_SCENE_HEIGHT; ++y, srcP += MADS_SCREEN_WIDTH,
356 			bgP += MADS_SCREEN_WIDTH, screenP += MADS_SCREEN_WIDTH) {
357 			*bgP = *srcP;
358 			*screenP = *srcP;
359 		}
360 
361 		// Keep moving the column to copy to the right
362 		if (++_translationX == MADS_SCREEN_WIDTH) {
363 			// Surface transition is complete
364 			_spareScreen = nullptr;
365 		}
366 	}
367 
368 	// Make sure it's time for an update
369 	if (currTime < _scrollTimeout)
370 		return;
371 	_scrollTimeout = g_system->getMillis() + TEXT_ANIMATION_DELAY;
372 	_redrawFlag = true;
373 
374 	// If any panning values are set, pan the background surface
375 	if ((_pan.x != 0) || (_pan.y != 0)) {
376 		if (_panCountdown > 0) {
377 			--_panCountdown;
378 			return;
379 		}
380 
381 		// Handle horizontal panning
382 		if (_pan.x != 0) {
383 			byte *lineTemp = new byte[_pan.x];
384 			for (int y = 0; y < MADS_SCENE_HEIGHT; ++y) {
385 				byte *pixelsP = (byte *)scene._backgroundSurface.getBasePtr(0, y);
386 
387 				// Copy the first X pixels into temp buffer, move the rest of the line
388 				// to the start of the line, and then move temp buffer pixels to end of line
389 				Common::copy(pixelsP, pixelsP + _pan.x, lineTemp);
390 				Common::copy(pixelsP + _pan.x, pixelsP + MADS_SCREEN_WIDTH, pixelsP);
391 				Common::copy(lineTemp, lineTemp + _pan.x, pixelsP + MADS_SCREEN_WIDTH - _pan.x);
392 			}
393 
394 			delete[] lineTemp;
395 		}
396 
397 		// Handle vertical panning
398 		if (_pan.y != 0) {
399 			// Store the bottom Y lines into a temp buffer, move the rest of the lines down,
400 			// and then copy the stored lines back to the top of the screen
401 			byte *linesTemp = new byte[_pan.y * MADS_SCREEN_WIDTH];
402 			byte *pixelsP = (byte *)scene._backgroundSurface.getBasePtr(0, MADS_SCENE_HEIGHT - _pan.y);
403 			Common::copy(pixelsP, pixelsP + MADS_SCREEN_WIDTH * _pan.y, linesTemp);
404 
405 			for (int y = MADS_SCENE_HEIGHT - 1; y >= _pan.y; --y) {
406 				byte *destP = (byte *)scene._backgroundSurface.getBasePtr(0, y);
407 				byte *srcP = (byte *)scene._backgroundSurface.getBasePtr(0, y - _pan.y);
408 				Common::copy(srcP, srcP + MADS_SCREEN_WIDTH, destP);
409 			}
410 
411 			Common::copy(linesTemp, linesTemp + _pan.y * MADS_SCREEN_WIDTH,
412 				(byte *)scene._backgroundSurface.getPixels());
413 			delete[] linesTemp;
414 		}
415 
416 		// Flag for a full screen refresh
417 		scene._spriteSlots.fullRefresh();
418 	}
419 
420 	// Scroll all active text lines up
421 	for (int i = _textLines.size() - 1; i >= 0; --i) {
422 		TextLine &tl = _textLines[i];
423 		if (tl._textDisplayIndex != -1)
424 			// Expire the text line that's already on-screen
425 			scene._textDisplay.expire(tl._textDisplayIndex);
426 
427 		tl._pos.y--;
428 		if (tl._pos.y + _font->getHeight() < 0) {
429 			_textLines.remove_at(i);
430 		} else {
431 			tl._textDisplayIndex = scene._textDisplay.add(tl._pos.x, tl._pos.y,
432 				0x605, -1, tl._line, _font);
433 		}
434 	}
435 
436 	if (_scrollCount > 0) {
437 		// Handling final scrolling of text off of screen
438 		if (--_scrollCount == 0) {
439 			scriptDone();
440 			return;
441 		}
442 	} else {
443 		// Handling a text row
444 		if (++_lineY == (_font->getHeight() + TEXTVIEW_LINE_SPACING))
445 			processLines();
446 	}
447 }
448 
scriptDone()449 void TextView::scriptDone() {
450 	_breakFlag = true;
451 	_vm->_dialogs->_pendingDialog = DIALOG_MAIN_MENU;
452 }
453 
454 /*------------------------------------------------------------------------*/
455 
456 char AnimationView::_resourceName[100];
457 
execute(MADSEngine * vm,const Common::String & resName)458 void AnimationView::execute(MADSEngine *vm, const Common::String &resName) {
459 	assert(resName.size() < 100);
460 	Common::strlcpy(_resourceName, resName.c_str(), sizeof(_resourceName));
461 	vm->_dialogs->_pendingDialog = DIALOG_ANIMVIEW;
462 }
463 
AnimationView(MADSEngine * vm)464 AnimationView::AnimationView(MADSEngine *vm) : MenuView(vm) {
465 	_redrawFlag = false;
466 
467 	_soundDriverLoaded = false;
468 	_previousUpdate = 0;
469 	_screenId = -1;
470 	_resetPalette = false;
471 	_resyncMode = NEVER;
472 	_v1 = 0;
473 	_v2 = -1;
474 	_resourceIndex = -1;
475 	_currentAnimation = nullptr;
476 	_sfx = 0;
477 	_soundFlag = _bgLoadFlag = true;
478 	_showWhiteBars = true;
479 	_manualFrameNumber = 0;
480 	_manualSpriteSet = nullptr;
481 	_manualStartFrame = _manualEndFrame = 0;
482 	_manualFrame2 = 0;
483 	_animFrameNumber = 0;
484 	_nextCyclingActive = false;
485 	_sceneInfo = SceneInfo::init(_vm);
486 	_scrollFrameCtr = 0;
487 
488 	load();
489 }
490 
~AnimationView()491 AnimationView::~AnimationView() {
492 	// Turn off palette cycling as well as any playing sound
493 	Scene &scene = _vm->_game->_scene;
494 	scene._cyclingActive = false;
495 	_vm->_sound->stop();
496 	_vm->_audio->stop();
497 
498 	// Delete data
499 	delete _currentAnimation;
500 	delete _sceneInfo;
501 }
502 
load()503 void AnimationView::load() {
504 	Common::String resName(_resourceName);
505 	if (!resName.hasSuffix("."))
506 		resName += ".res";
507 
508 	_filename = resName;
509 	if (!_script.open(resName))
510 		error("Could not open resource %s", resName.c_str());
511 
512 	processLines();
513 }
514 
display()515 void AnimationView::display() {
516 	Scene &scene = _vm->_game->_scene;
517 	_vm->_palette->initPalette();
518 	Common::fill(&_vm->_palette->_cyclingPalette[0], &_vm->_palette->_cyclingPalette[PALETTE_SIZE], 0);
519 
520 	_vm->_palette->resetGamePalette(1, 8);
521 	scene._spriteSlots.reset();
522 	scene._paletteCycles.clear();
523 
524 	MenuView::display();
525 }
526 
onEvent(Common::Event & event)527 bool AnimationView::onEvent(Common::Event &event) {
528 	// Wait for the Escape key or a mouse press
529 	if (((event.type == Common::EVENT_KEYDOWN) && (event.kbd.keycode == Common::KEYCODE_ESCAPE)) ||
530 			(event.type == Common::EVENT_LBUTTONUP)) {
531 		scriptDone();
532 		return true;
533 	}
534 
535 	return false;
536 }
537 
doFrame()538 void AnimationView::doFrame() {
539 	Scene &scene = _vm->_game->_scene;
540 
541 	if (_resourceIndex == -1 || _currentAnimation->freeFlag()) {
542 		if (++_resourceIndex == (int)_resources.size()) {
543 			scriptDone();
544 		} else {
545 			scene._frameStartTime = 0;
546 			scene._spriteSlots.clear();
547 			loadNextResource();
548 		}
549 	} else if (_currentAnimation->getCurrentFrame() == 1) {
550 		scene._cyclingActive = _nextCyclingActive;
551 	}
552 
553 	if (_currentAnimation && (++_scrollFrameCtr >= _currentAnimation->_header._scrollTicks)) {
554 		_scrollFrameCtr = 0;
555 		scroll();
556 	}
557 
558 	if (_currentAnimation) {
559 		++scene._frameStartTime;
560 		_currentAnimation->update();
561 		_redrawFlag = true;
562 
563 		if (_currentAnimation->freeFlag())
564 			// We don't want the sprites removed after the last animation frame
565 			scene._spriteSlots.clear();
566 	}
567 }
568 
loadNextResource()569 void AnimationView::loadNextResource() {
570 	Scene &scene = _vm->_game->_scene;
571 	Palette &palette = *_vm->_palette;
572 	Screen &screen = *_vm->_screen;
573 	ResourceEntry &resEntry = _resources[_resourceIndex];
574 	Common::Array<PaletteCycle> paletteCycles;
575 
576 	if (resEntry._bgFlag)
577 		palette.resetGamePalette(1, 8);
578 
579 	palette._mainPalette[253 * 3] = palette._mainPalette[253 * 3 + 1]
580 		= palette._mainPalette[253 * 3 + 2] = 0xb4;
581 	palette.setPalette(&palette._mainPalette[253 * 3], 253, 1);
582 
583 	// Free any previous messages
584 	scene._kernelMessages.reset();
585 
586 	// Handle the bars at the top/bottom
587 	if (resEntry._showWhiteBars) {
588 		// For animations the screen has been clipped to the middle 156 rows.
589 		// So although it's slightly messy, temporarily reset clip bounds
590 		// so we can redraw the white lines
591 		Common::Rect clipBounds = screen.getClipBounds();
592 		screen.resetClipBounds();
593 
594 		screen.hLine(0, 20, MADS_SCREEN_WIDTH, 253);
595 		screen.hLine(0, 179, MADS_SCREEN_WIDTH, 253);
596 
597 		screen.setClipBounds(clipBounds);
598 	}
599 
600 	// Load the new animation
601 	delete _currentAnimation;
602 	_currentAnimation = Animation::init(_vm, &scene);
603 	int flags = ANIMFLAG_ANIMVIEW | (resEntry._bgFlag ? ANIMFLAG_LOAD_BACKGROUND : 0);
604 	_currentAnimation->load(scene._backgroundSurface, scene._depthSurface,
605 		resEntry._resourceName, flags, &paletteCycles, _sceneInfo);
606 
607 	// Signal for a screen refresh
608 	scene._spriteSlots.fullRefresh();
609 
610 	// If a sound driver has been specified, then load the correct one
611 	if (!_currentAnimation->_header._soundName.empty()) {
612 		const char *chP = strchr(_currentAnimation->_header._soundName.c_str(), '.');
613 		assert(chP);
614 
615 		// Handle both Rex naming (xxx.009) and naming in later games (e.g. xxx.ph9)
616 		int driverNum = atoi(chP + 3);
617 		// HACK for Dragon
618 		if (_currentAnimation->_header._soundName == "#SOUND.DRG")
619 			driverNum = 9;
620 		_vm->_sound->init(driverNum);
621 	}
622 
623 	// Handle any manual setup
624 	if (_currentAnimation->_header._manualFlag) {
625 		_manualFrameNumber = _currentAnimation->_header._spritesIndex;
626 		_manualSpriteSet = _currentAnimation->getSpriteSet(_manualFrameNumber);
627 	}
628 
629 	// Set the sound data for the animation
630 	_vm->_sound->setEnabled(resEntry._soundFlag);
631 
632 	Common::String dsrName = _currentAnimation->_header._dsrName;
633 	if (!dsrName.empty())
634 		_vm->_audio->setSoundGroup(dsrName);
635 
636 	// Start the new animation
637 	_currentAnimation->startAnimation(0);
638 
639 	// Handle the palette and cycling palette
640 	scene._cyclingActive = false;
641 	Common::copy(&palette._mainPalette[0], &palette._mainPalette[PALETTE_SIZE],
642 		&palette._cyclingPalette[0]);
643 
644 	_vm->_game->_fx = (ScreenTransition)resEntry._fx;
645 	_nextCyclingActive = paletteCycles.size() > 0;
646 	if (!_vm->_game->_fx) {
647 		palette.setFullPalette(palette._mainPalette);
648 	}
649 
650 	scene.initPaletteAnimation(paletteCycles, _nextCyclingActive && !_vm->_game->_fx);
651 }
652 
scroll()653 void AnimationView::scroll() {
654 	Scene &scene = _vm->_game->_scene;
655 	Common::Point pt = _currentAnimation->_header._scrollPosition;
656 
657 	if (pt.x != 0) {
658 		scene._backgroundSurface.scrollX(pt.x);
659 		scene._spriteSlots.fullRefresh();
660 	}
661 
662 	if (pt.y != 0) {
663 		scene._backgroundSurface.scrollY(pt.y);
664 		scene._spriteSlots.fullRefresh();
665 	}
666 }
667 
scriptDone()668 void AnimationView::scriptDone() {
669 	_breakFlag = true;
670 	_vm->_dialogs->_pendingDialog = DIALOG_MAIN_MENU;
671 }
672 
processLines()673 void AnimationView::processLines() {
674 	if (_script.eos()) {
675 		// end of script, end animation
676 		scriptDone();
677 		return;
678 	}
679 
680 	while (!_script.eos()) {
681 		// Get in next line
682 		_currentLine.clear();
683 		char c;
684 		while (!_script.eos() && (c = _script.readByte()) != '\n') {
685 			if (c != '\r' && c != '\0')
686 				_currentLine += c;
687 		}
688 
689 		// Check for comment line
690 		if (_currentLine.hasPrefix("#"))
691 			continue;
692 
693 		// Process the line
694 		while (!_currentLine.empty()) {
695 			if (_currentLine.hasPrefix("-")) {
696 				_currentLine.deleteChar(0);
697 
698 				processCommand();
699 			} else {
700 				// Get resource name
701 				Common::String resName;
702 				while (!_currentLine.empty() && (c = _currentLine[0]) != ' ') {
703 					_currentLine.deleteChar(0);
704 					resName += c;
705 				}
706 
707 				// Add resource into list along with any set state information
708 				_resources.push_back(ResourceEntry(resName, _sfx, _soundFlag,
709 					_bgLoadFlag, _showWhiteBars));
710 
711 				// Fx resets between resource entries
712 				_sfx = 0;
713 			}
714 
715 			// Skip any spaces
716 			while (_currentLine.hasPrefix(" "))
717 				_currentLine.deleteChar(0);
718 		}
719 	}
720 }
721 
processCommand()722 void AnimationView::processCommand() {
723 	// Get the command character
724 	char commandChar = toupper(_currentLine[0]);
725 	_currentLine.deleteChar(0);
726 
727 	// Handle the command
728 	switch (commandChar) {
729 	case 'B':
730 		_soundFlag = !_soundFlag;
731 		break;
732 	case 'H':
733 		// -h[:ex]  Disable EMS / XMS high memory support
734 		if (_currentLine.hasPrefix(":"))
735 			_currentLine.deleteChar(0);
736 		while (_currentLine.hasPrefix("e") || _currentLine.hasPrefix("x"))
737 			_currentLine.deleteChar(0);
738 		break;
739 	case 'O':
740 		// -o:xxx  Specify opening special effect
741 		assert(_currentLine[0] == ':');
742 		_currentLine.deleteChar(0);
743 		_sfx = getParameter();
744 		break;
745 	case 'P':
746 		// Switch to CONCAT mode, which is ignored anyway
747 		break;
748 	case 'R': {
749 		// Resynch timer (always, beginning, never)
750 		assert(_currentLine[0] == ':');
751 		_currentLine.deleteChar(0);
752 
753 		char v = toupper(_currentLine[0]);
754 		_currentLine.deleteChar(0);
755 		if (v == 'N')
756 			_resyncMode = NEVER;
757 		else if (v == 'A')
758 			_resyncMode = ALWAYS;
759 		else if (v == 'B')
760 			_resyncMode = BEGINNING;
761 		else
762 			error("Unknown parameter");
763 		break;
764 	}
765 	case 'W':
766 		// Switch white bars being visible
767 		_showWhiteBars = !_showWhiteBars;
768 		break;
769 	case 'X':
770 		// Exit after animation finishes. Ignore
771 		break;
772 	case 'D':
773 		// Unimplemented and ignored in the original. Ignore as well
774 		break;
775 	case 'Y':
776 		// Reset palette on startup
777 		_resetPalette = true;
778 		break;
779 	default:
780 		error("Unknown command char: '%c'", commandChar);
781 	}
782 }
783 
getParameter()784 int AnimationView::getParameter() {
785 	int result = 0;
786 
787 	while (!_currentLine.empty()) {
788 		char c = _currentLine[0];
789 
790 		if (c >= '0' && c <= '9') {
791 			_currentLine.deleteChar(0);
792 			result = result * 10 + (c - '0');
793 		} else {
794 			break;
795 		}
796 	}
797 
798 	return result;
799 }
800 
checkResource(const Common::String & resourceName)801 void AnimationView::checkResource(const Common::String &resourceName) {
802 	//bool hasSuffix = false;
803 
804 }
805 
scanResourceIndex(const Common::String & resourceName)806 int AnimationView::scanResourceIndex(const Common::String &resourceName) {
807 	int foundIndex = -1;
808 
809 	if (_v1) {
810 		const char *chP = strchr(resourceName.c_str(), '\\');
811 		if (!chP) {
812 			chP = strchr(resourceName.c_str(), '*');
813 		}
814 
815 		Common::String resName = chP ? Common::String(chP + 1) : resourceName;
816 
817 		if (_v2 != 3) {
818 			assert(_resIndex.size() == 0);
819 		}
820 
821 		// Scan for the resource name
822 		for (uint resIndex = 0; resIndex < _resIndex.size(); ++resIndex) {
823 			ResIndexEntry &resEntry = _resIndex[resIndex];
824 			if (resEntry._resourceName.compareToIgnoreCase(resourceName)) {
825 				foundIndex = resIndex;
826 				break;
827 			}
828 		}
829 	}
830 
831 	if (foundIndex >= 0) {
832 		// TODO
833 	}
834 	return -1;
835 }
836 
837 } // End of namespace MADS
838