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 "voyeur/files.h"
24 #include "voyeur/screen.h"
25 #include "voyeur/voyeur.h"
26 #include "voyeur/staticres.h"
27 #include "common/config-manager.h"
28 
29 namespace Voyeur {
30 
31 int ThreadResource::_useCount[8];
32 
init()33 void ThreadResource::init() {
34 	Common::fill(&_useCount[0], &_useCount[8], 0);
35 }
36 
ThreadResource(BoltFilesState & state,const byte * src)37 ThreadResource::ThreadResource(BoltFilesState &state, const byte *src):_vm(state._vm) {
38 	_stateId = READ_LE_UINT16(&src[0]);
39 	_stackId = READ_LE_UINT16(&src[0]);
40 	_savedStateId = READ_LE_UINT16(&src[0]);
41 	_savedStackId = READ_LE_UINT16(&src[0]);
42 	_ctlPtr = nullptr;
43 	_aptPos = Common::Point(-1, -1);
44 
45 	_newStateId = -1;
46 	_newStackId = -1;
47 	_stateFlags = 0;
48 	_stateCount = 0;
49 	_parseCount = 0;
50 	_nextStateId = 0;
51 	_threadInfoPtr = nullptr;
52 	_playCommandsPtr = nullptr;
53 }
54 
initThreadStruct(int idx,int id)55 void ThreadResource::initThreadStruct(int idx, int id) {
56 	_stackId = -1;
57 	if (loadAStack(idx)) {
58 		_savedStateId = _savedStackId = -1;
59 		_stateId = id;
60 		_newStateId = -1;
61 		_newStackId = -1;
62 
63 		doState();
64 	}
65 }
66 
loadAStack(int stackId)67 bool ThreadResource::loadAStack(int stackId) {
68 	if (_vm->_stampFlags & 1) {
69 		if (stackId < 0)
70 			error("loadAStack() - Invalid stackId %d", stackId);
71 
72 		unloadAStack(_stackId);
73 		if  (!_useCount[stackId]) {
74 			BoltEntry &boltEntry = _vm->_stampLibPtr->boltEntry(_vm->_controlPtr->_memberIds[stackId]);
75 			if (!boltEntry._data)
76 				return false;
77 
78 			_vm->_controlPtr->_entries[stackId] = boltEntry._data;
79 		}
80 
81 		++_useCount[stackId];
82 	}
83 
84 	_ctlPtr = _vm->_controlPtr->_entries[stackId];
85 	_stackId = stackId;
86 	return true;
87 }
88 
unloadAStack(int stackId)89 void ThreadResource::unloadAStack(int stackId) {
90 	if (stackId < 0)
91 		return;
92 
93 	if ((_vm->_stampFlags & 1) && _useCount[stackId]) {
94 		if (--_useCount[stackId] == 0) {
95 			_vm->_stampLibPtr->freeBoltMember(_vm->_controlPtr->_memberIds[stackId]);
96 		}
97 	}
98 }
99 
doState()100 bool ThreadResource::doState() {
101 	if (!getStateInfo())
102 		return false;
103 
104 	getButtonsFlags();
105 
106 	_vm->_glGoState = -1;
107 	_vm->_glGoStack = -1;
108 
109 	performOpenCard();
110 	if (_stateFlags & 1) {
111 		return chooseSTAMPButton(_vm->getRandomNumber(_stateCount - 1));
112 	} else {
113 		return true;
114 	}
115 }
116 
getStateInfo()117 bool ThreadResource::getStateInfo() {
118 	int id = READ_LE_UINT16(_ctlPtr);
119 
120 	if (id <= _stateId) {
121 		return false;
122 	} else {
123 		uint32 fld = READ_LE_UINT32(_ctlPtr + 2);
124 		fld += _stateId << 3;
125 		_nextStateId = READ_LE_UINT32(_ctlPtr + fld + 4);
126 
127 		fld = READ_LE_UINT32(_ctlPtr + fld);
128 		byte *baseP = _ctlPtr + fld;
129 		_stateCount = READ_LE_UINT16(baseP);
130 		_stateFlags = READ_LE_UINT16(baseP + 2);
131 		_parseCount = READ_LE_UINT16(baseP + 4);
132 
133 		_playCommandsPtr = getDataOffset();
134 		_playCommandsPtr += (READ_LE_UINT32(baseP + 6) / 2) << 1;
135 
136 		_threadInfoPtr = baseP + 10;
137 
138 		getButtonsText();
139 		return true;
140 	}
141 }
142 
getDataOffset()143 byte *ThreadResource::getDataOffset() {
144 	uint32 offset = READ_LE_UINT32(_ctlPtr + 10);
145 	return _ctlPtr + offset;
146 }
147 
getButtonsText()148 void ThreadResource::getButtonsText() {
149 	int idx = 0;
150 
151 	for (const byte *p = _threadInfoPtr; *p != 0x49; p = getNextRecord(p)) {
152 		if (*p == 0xC0) {
153 			++p;
154 			if (*p++ & 0x80) {
155 				assert(idx < 63);
156 				p += 4;
157 			}
158 
159 			++idx;
160 		}
161 	}
162 }
163 
getButtonsFlags()164 void ThreadResource::getButtonsFlags() {
165 	int idx = 0;
166 
167 	for (const byte *p = _threadInfoPtr; *p != 0x49; p = getNextRecord(p)) {
168 		if (*p == 0xC0) {
169 			if (*++p & 0x20)
170 				_stateFlags |= 2;
171 
172 			_buttonFlags[idx] = *p++;
173 			_buttonIds[idx] = *p++;
174 
175 			if (_buttonFlags[idx] & 0x80)
176 				p += 4;
177 
178 			++idx;
179 		}
180 	}
181 }
182 
unloadAllStacks(VoyeurEngine * vm)183 void ThreadResource::unloadAllStacks(VoyeurEngine *vm) {
184 	if (vm->_stampFlags & 1) {
185 		for (int i = 0; i < 8; ++i) {
186 			if (_useCount[i])
187 				vm->_stampLibPtr->freeBoltMember(vm->_controlPtr->_memberIds[i]);
188 		}
189 	}
190 }
191 
performOpenCard()192 void ThreadResource::performOpenCard() {
193 	for (const byte *p = _threadInfoPtr; *p != 0x49; p = getNextRecord(p)) {
194 		if (*p == 0x47) {
195 			cardAction(p + 1);
196 			return;
197 		}
198 	}
199 }
200 
initUseCount()201 void ThreadResource::initUseCount() {
202 	Common::fill(&_useCount[0], &_useCount[8], 0);
203 }
204 
getRecordOffset(const byte * p)205 const byte *ThreadResource::getRecordOffset(const byte *p) {
206 	uint32 recSize = READ_LE_UINT32(p) + READ_LE_UINT32(_ctlPtr + 6);
207 	return _ctlPtr + recSize;
208 }
209 
getNextRecord(const byte * p)210 const byte *ThreadResource::getNextRecord(const byte *p) {
211 	byte v = *p++;
212 
213 	switch (v) {
214 	case 2:
215 	case 4:
216 	case 6:
217 	case 8:
218 	case 10:
219 		return p + 8;
220 	case 1:
221 	case 3:
222 	case 5:
223 	case 7:
224 	case 9:
225 	case 11:
226 	case 21:
227 	case 22:
228 	case 25:
229 	case 26:
230 		return p + 5;
231 	case 17:
232 	case 23:
233 	case 24:
234 	case 27:
235 	case 28:
236 		return p + 2;
237 	case 19:
238 	case 41:
239 		return p + 6;
240 	case 18:
241 	case 51:
242 	case 52:
243 		return p + 1;
244 	case 74:
245 		return p + 4;
246 	case 192:
247 		if (*p & 0x80)
248 			p += 4;
249 		return p + 2;
250 	default:
251 		return p;
252 	}
253 }
254 
getSTAMPCard(int cardId)255 const byte *ThreadResource::getSTAMPCard(int cardId) {
256 	const byte *p;
257 	int count = 0;
258 
259 	for (p = _threadInfoPtr; count <= cardId && *p != 0x49; p = getNextRecord(p)) {
260 		if (*p == 0xC0)
261 			++count;
262 	}
263 
264 	return p;
265 }
266 
getStateFromID(uint32 id)267 int ThreadResource::getStateFromID(uint32 id) {
268 	int count = READ_LE_UINT16(_ctlPtr);
269 
270 	for (int i = 0; i < count; ++i) {
271 		uint32 sid = getSID(i);
272 		if (sid == id)
273 			return i;
274 	}
275 
276 	return -1;
277 }
278 
getSID(int sid)279 uint32 ThreadResource::getSID(int sid) {
280 	uint32 offset = READ_LE_UINT32(_ctlPtr + 2) + (sid << 3) + 4;
281 	return READ_LE_UINT32(_ctlPtr + offset);
282 }
283 
doSTAMPCardAction()284 void ThreadResource::doSTAMPCardAction() {
285 	for (const byte *p = _threadInfoPtr; *p != 0x49; p = getNextRecord(p)) {
286 		if (*p == 0x48) {
287 			cardAction(p + 1);
288 			return;
289 		}
290 	}
291 }
292 
cardAction(const byte * card)293 void ThreadResource::cardAction(const byte *card) {
294 	_vm->_glGoState = -1;
295 	_vm->_glGoStack = -1;
296 
297 	// Loop to perform card commands
298 	while (!_vm->shouldQuit() && *card < 70 && _vm->_glGoState == -1) {
299 		card = cardPerform(card);
300 	}
301 }
302 
chooseSTAMPButton(int buttonId)303 bool ThreadResource::chooseSTAMPButton(int buttonId) {
304 	for (int idx = 0; idx < _stateCount; ++idx) {
305 		if (_buttonIds[idx] == buttonId) {
306 			const byte *card = getSTAMPCard(idx);
307 			cardAction(card);
308 
309 			bool flag = true;
310 			while (!_vm->shouldQuit() && _vm->_glGoStack != -1 && flag) {
311 				doSTAMPCardAction();
312 				flag = goToStateID(_vm->_glGoStack, _vm->_glGoState);
313 			}
314 
315 			while (!_vm->shouldQuit() && _vm->_glGoState != -1 && flag) {
316 				doSTAMPCardAction();
317 				flag = goToState(-1, _vm->_glGoState);
318 			}
319 
320 			return flag;
321 		}
322 	}
323 
324 	return false;
325 }
326 
parsePlayCommands()327 void ThreadResource::parsePlayCommands() {
328 	_vm->_voy->_playStampMode = -1;
329 	_vm->_voy->_audioVisualStartTime = 0;
330 	_vm->_voy->_audioVisualDuration = 0;
331 	_vm->_voy->_boltGroupId2 = -1;
332 	_vm->_voy->_computerTextId = -1;
333 	_vm->_voy->_eventFlags &= ~EVTFLAG_8;
334 	_vm->_eventsManager->_videoDead = -1;
335 
336 	// Reset hotspot data
337 	_vm->_voy->_videoHotspotTimes.reset();
338 	_vm->_voy->_audioHotspotTimes.reset();
339 	_vm->_voy->_evidenceHotspotTimes.reset();
340 	Common::fill(&_vm->_voy->_roomHotspotsEnabled[0], &_vm->_voy->_roomHotspotsEnabled[20], false);
341 	byte *dataP = _playCommandsPtr;
342 	int v2, v3;
343 	PictureResource *pic;
344 	CMapResource *pal;
345 
346 	for (int parseIndex = 0; parseIndex < _parseCount; ++parseIndex) {
347 		uint16 id = READ_LE_UINT16(dataP);
348 		debugC(DEBUG_BASIC, kDebugScripts, "parsePlayCommands (%d of %d) - cmd #%d",
349 			parseIndex + 1, _parseCount, id);
350 		dataP += 2;
351 
352 		switch (id) {
353 		case 1:
354 			_vm->_currentVocId = READ_LE_UINT16(dataP);
355 			dataP += 2;
356 			break;
357 
358 		case 2:
359 			// Play an audio event
360 			v2 = READ_LE_UINT16(dataP);
361 
362 			if (v2 == 0 || _vm->_controlPtr->_state->_victimIndex == v2) {
363 				_vm->_audioVideoId = READ_LE_UINT16(dataP + 2) - 1;
364 				_vm->_voy->_audioVisualStartTime = READ_LE_UINT16(dataP + 4);
365 				_vm->_voy->_audioVisualDuration = READ_LE_UINT16(dataP + 6);
366 
367 				if (_vm->_voy->_RTVNum < _vm->_voy->_audioVisualStartTime ||
368 						(_vm->_voy->_audioVisualStartTime + _vm->_voy->_audioVisualDuration)  < _vm->_voy->_RTVNum) {
369 					_vm->_audioVideoId = -1;
370 				} else {
371 					_vm->_voy->_vocSecondsOffset = _vm->_voy->_RTVNum - _vm->_voy->_audioVisualStartTime;
372 					_vm->_voy->addAudioEventStart();
373 
374 					// Play the audio
375 					assert(_vm->_audioVideoId < 38);
376 					_vm->playAudio(_vm->_audioVideoId);
377 
378 					_vm->_voy->addAudioEventEnd();
379 					_vm->_eventsManager->incrementTime(1);
380 					_vm->_eventsManager->incrementTime(1);
381 					_vm->_audioVideoId = -1;
382 					parseIndex = 999;
383 				}
384 			}
385 
386 			dataP += 8;
387 			break;
388 
389 		case 3:
390 			// Play a video event
391 			v2 = READ_LE_UINT16(dataP);
392 
393 			if (v2 == 0 || _vm->_controlPtr->_state->_victimIndex == v2) {
394 				_vm->_audioVideoId = READ_LE_UINT16(dataP + 2) - 1;
395 				_vm->_voy->_audioVisualStartTime = READ_LE_UINT16(dataP + 4);
396 				_vm->_voy->_audioVisualDuration = READ_LE_UINT16(dataP + 6);
397 
398 				if (_vm->_voy->_RTVNum < _vm->_voy->_audioVisualStartTime ||
399 						(_vm->_voy->_audioVisualStartTime + _vm->_voy->_audioVisualDuration)  < _vm->_voy->_RTVNum) {
400 					_vm->_audioVideoId = -1;
401 				} else {
402 					_vm->_voy->_vocSecondsOffset = _vm->_voy->_RTVNum - _vm->_voy->_audioVisualStartTime;
403 					_vm->_voy->addVideoEventStart();
404 					_vm->_voy->_eventFlags &= ~EVTFLAG_TIME_DISABLED;
405 					_vm->_voy->_eventFlags |= EVTFLAG_RECORDING;
406 					_vm->playAVideo(_vm->_audioVideoId);
407 
408 					_vm->_voy->_eventFlags &= ~EVTFLAG_RECORDING;
409 					_vm->_voy->_eventFlags |= EVTFLAG_TIME_DISABLED;
410 					_vm->_voy->addVideoEventEnd();
411 					_vm->_eventsManager->incrementTime(1);
412 
413 					_vm->_audioVideoId = -1;
414 					_vm->_playStampGroupId = -1;
415 
416 					if (_vm->_eventsManager->_videoDead != -1) {
417 						_vm->_bVoy->freeBoltGroup(0xE00);
418 						_vm->_eventsManager->_videoDead = -1;
419 						_vm->flipPageAndWait();
420 					}
421 
422 					_vm->_eventsManager->_videoDead = -1;
423 					if (_stateCount == 2 && _vm->_eventsManager->_mouseClicked == 0) {
424 						_vm->_voy->_playStampMode = 132;
425 						parseIndex = 999;
426 					} else {
427 						_vm->_voy->_playStampMode = 129;
428 					}
429 				}
430 			}
431 
432 			dataP += 8;
433 			break;
434 
435 		case 4:
436 		case 22:
437 			// Case 22: Endgame news reports
438 			_vm->_audioVideoId = READ_LE_UINT16(dataP) - 1;
439 			dataP += 2;
440 
441 			if (id == 22) {
442 				int resolveIndex = READ_LE_UINT16(dataP);
443 				dataP += 2;
444 				_vm->_playStampGroupId = _vm->_resolvePtr[resolveIndex];
445 			}
446 
447 			_vm->_voy->_vocSecondsOffset = 0;
448 			_vm->_voy->_audioVisualStartTime = _vm->_voy->_RTVNum;
449 			_vm->_voy->_eventFlags &= ~(EVTFLAG_TIME_DISABLED | EVTFLAG_RECORDING);
450 			_vm->playAVideo(_vm->_audioVideoId);
451 			_vm->_voy->_eventFlags |= EVTFLAG_TIME_DISABLED;
452 
453 			if (id != 22) {
454 				_vm->_audioVideoId = -1;
455 				parseIndex = 999;
456 			} else {
457 				int count = _vm->_bVoy->getBoltGroup(_vm->_playStampGroupId)->_entries.size() / 2;
458 				_vm->_soundManager->stopVOCPlay();
459 				_vm->_eventsManager->getMouseInfo();
460 
461 				for (int i = 0; i < count; ++i) {
462 					pic = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + i * 2)._picResource;
463 					pal = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + i * 2 + 1)._cMapResource;
464 
465 					_vm->_screen->_vPort->setupViewPort(pic);
466 					pal->startFade();
467 
468 					_vm->flipPageAndWaitForFade();
469 
470 					if (i > 0) {
471 						_vm->_bVoy->freeBoltMember(_vm->_playStampGroupId + i * 2);
472 						_vm->_bVoy->freeBoltMember(_vm->_playStampGroupId + i * 2 + 1);
473 					}
474 
475 					Common::String file = Common::String::format("news%d.voc", i + 1);
476 					_vm->_soundManager->startVOCPlay(file);
477 
478 					while (!_vm->shouldQuit() && !_vm->_eventsManager->_mouseClicked &&
479 							_vm->_soundManager->getVOCStatus()) {
480 						_vm->_eventsManager->delayClick(1);
481 						_vm->_eventsManager->getMouseInfo();
482 					}
483 
484 					_vm->_soundManager->stopVOCPlay();
485 
486 					if (i == (count - 1))
487 						_vm->_eventsManager->delayClick(480);
488 
489 					if (_vm->shouldQuit() || _vm->_eventsManager->_mouseClicked)
490 						break;
491 				}
492 
493 				_vm->_bVoy->freeBoltGroup(_vm->_playStampGroupId);
494 				_vm->_playStampGroupId = -1;
495 				_vm->_audioVideoId = -1;
496 				parseIndex = 999;
497 			}
498 			break;
499 
500 		case 5:
501 			// Check whether transition to a given time period is allowed, and
502 			// if so, load the time information for the new time period
503 			v2 = READ_LE_UINT16(dataP);
504 			if (v2 == 0 || _vm->_controlPtr->_state->_victimIndex == v2) {
505 				_vm->_voy->_playStampMode = 5;
506 				int count = READ_LE_UINT16(dataP + 2);
507 				_vm->_voy->_RTVLimit = READ_LE_UINT16(dataP + 4);
508 
509 				if (_vm->_voy->_transitionId != count) {
510 					if (_vm->_voy->_transitionId > 1)
511 						_vm->_voy->_eventFlags &= ~EVTFLAG_100;
512 
513 					_vm->_voy->_transitionId = count;
514 					_vm->_gameMinute = LEVEL_M[count - 1];
515 					_vm->_gameHour = LEVEL_H[count - 1];
516 					//_vm->_v2A0A2 = 0;
517 					_vm->_voy->_RTVNum = 0;
518 					_vm->_voy->_RTANum = 255;
519 				}
520 
521 				_vm->_voy->_isAM = (_vm->_voy->_transitionId == 6);
522 			}
523 
524 			dataP += 6;
525 			break;
526 
527 		case 6:
528 			_vm->_voy->_playStampMode = 6;
529 			v2 = READ_LE_UINT16(dataP);
530 			_vm->_playStampGroupId = _vm->_resolvePtr[v2];
531 			dataP += 2;
532 			break;
533 
534 		case 7:
535 			// Load the video event scene hotspot times data
536 			v2 = READ_LE_UINT16(dataP);
537 			v3 = READ_LE_UINT16(dataP + 2) - 1;
538 
539 			if (v2 == 0 || _vm->_controlPtr->_state->_victimIndex == v2) {
540 				int idx = 0;
541 				while (_vm->_voy->_videoHotspotTimes._min[idx][v3] != 9999)
542 					++idx;
543 
544 				v2 = READ_LE_UINT16(dataP + 4);
545 				_vm->_voy->_videoHotspotTimes._min[idx][v3] = v2;
546 				_vm->_voy->_videoHotspotTimes._max[idx][v3] = v2 + READ_LE_UINT16(dataP + 6) - 2;
547 			}
548 
549 			dataP += 8;
550 			break;
551 
552 		case 8:
553 			// Load the audio event scene hotspot times data
554  			v2 = READ_LE_UINT16(dataP);
555 			v3 = READ_LE_UINT16(dataP + 2) - 1;
556 
557 			if (v2 == 0 || _vm->_controlPtr->_state->_victimIndex == v2) {
558 				int idx = 0;
559 				while (_vm->_voy->_audioHotspotTimes._min[idx][v3] != 9999)
560 					++idx;
561 
562 				v2 = READ_LE_UINT16(dataP + 4);
563 				_vm->_voy->_audioHotspotTimes._min[idx][v3] = v2;
564 				_vm->_voy->_audioHotspotTimes._max[idx][v3] = v2 + READ_LE_UINT16(dataP + 6) - 2;
565 			}
566 
567 			dataP += 8;
568 			break;
569 
570 		case 9:
571 			// Load up evidence event scene hotspot times data
572 			v2 = READ_LE_UINT16(dataP);
573 			v3 = READ_LE_UINT16(dataP + 2) - 1;
574 
575 			if (v2 == 0 || _vm->_controlPtr->_state->_victimIndex == v2) {
576 				int idx = 0;
577 				while (_vm->_voy->_evidenceHotspotTimes._min[idx][v3] != 9999)
578 					++idx;
579 
580 				v2 = READ_LE_UINT16(dataP + 4);
581 				_vm->_voy->_evidenceHotspotTimes._min[idx][v3] = v2;
582 				_vm->_voy->_evidenceHotspotTimes._max[idx][v3] = v2 + READ_LE_UINT16(dataP + 6) - 2;
583 			}
584 
585 			dataP += 8;
586 			break;
587 
588 		case 10:
589 			// Pick the person who is to die, during startup
590 			if (_vm->_iForceDeath == -1) {
591 				// No specific person has been preset to be killed, so pick one randomly.
592 				// The loop below ensures that a different victim is picked.
593 				int lastVictim = ConfMan.hasKey("lastVictim") ? ConfMan.getInt("lastVictim") : -1;
594 				int randomVictim;
595 				do {
596 					randomVictim = _vm->getRandomNumber(3) + 1;
597 				} while (randomVictim == lastVictim);
598 
599 				_vm->_controlPtr->_state->_victimIndex = randomVictim;
600 			} else {
601 				// Victim selected from command line
602 				_vm->_controlPtr->_state->_victimIndex = _vm->_iForceDeath;
603 			}
604 
605 			ConfMan.setInt("lastVictim", _vm->_controlPtr->_state->_victimIndex);
606 			ConfMan.flushToDisk();
607 			break;
608 
609 		case 11:
610 			_vm->_voy->_eventFlags |= EVTFLAG_2;
611 			break;
612 
613 		case 12:
614 			v2 = READ_LE_UINT16(dataP);
615 
616 			if (v2 == 0 || _vm->_controlPtr->_state->_victimIndex == v2) {
617 				_vm->_voy->_boltGroupId2 = _vm->_resolvePtr[READ_LE_UINT16(dataP + 2)];
618 				_vm->_voy->_roomHotspotsEnabled[READ_LE_UINT16(dataP + 4) - 1] = true;
619 			}
620 
621 			dataP += 6;
622 			break;
623 
624 		case 13:
625 			v2 = READ_LE_UINT16(dataP);
626 
627 			if (v2 == 0 || _vm->_controlPtr->_state->_victimIndex == v2) {
628 				_vm->_voy->_computerTextId = READ_LE_UINT16(dataP + 2) - 1;
629 				_vm->_voy->_computerTimeMin = READ_LE_UINT16(dataP + 4);
630 				_vm->_voy->_computerTimeMax = READ_LE_UINT16(dataP + 6);
631 
632 				_vm->_voy->_computerScreenRect.left = COMPUTER_SCREEN_TABLE[_vm->_voy->_computerTextId * 4];
633 				_vm->_voy->_computerScreenRect.top = COMPUTER_SCREEN_TABLE[_vm->_voy->_computerTextId * 4 + 1];
634 				_vm->_voy->_computerScreenRect.right = COMPUTER_SCREEN_TABLE[_vm->_voy->_computerTextId * 4 + 2];
635 				_vm->_voy->_computerScreenRect.bottom = COMPUTER_SCREEN_TABLE[_vm->_voy->_computerTextId * 4 + 3];
636 			}
637 
638 			dataP += 8;
639 			break;
640 
641 		case 14:
642 			_vm->_playStampGroupId = 2048;
643 			_vm->_voy->_playStampMode = 130;
644 			break;
645 
646 		case 15:
647 			_vm->showEndingNews();
648 			break;
649 
650 		case 16:
651 			_vm->_voy->_playStampMode = 16;
652 			break;
653 
654 		case 17:
655 			_vm->_voy->_playStampMode = 17;
656 			break;
657 
658 		case 18:
659 			// Called during the murder (Sunday 10:30PM) time period, to specify the
660 			// time expired point at which the murder takes place
661 			v2 = READ_LE_UINT16(dataP);
662 			v3 = READ_LE_UINT16(dataP + 2);
663 
664 			if (v2 == 0 || _vm->_controlPtr->_state->_victimIndex == v2)
665 				_vm->_voy->_murderThreshold = v3;
666 
667 			dataP += 4;
668 			break;
669 
670 		case 19:
671 			_vm->_voy->_aptLoadMode = 140;
672 			loadTheApt();
673 			_vm->_voy->_aptLoadMode = 141;
674 			freeTheApt();
675 			break;
676 
677 		case 20:
678 			_vm->_voy->_aptLoadMode = -1;
679 			loadTheApt();
680 			_vm->_voy->_aptLoadMode = 141;
681 			freeTheApt();
682 			break;
683 
684 		case 21:
685 			_vm->_voy->_aptLoadMode = -1;
686 			loadTheApt();
687 			_vm->_voy->_aptLoadMode = 140;
688 			freeTheApt();
689 			break;
690 
691 		case 23:
692 			_vm->_voy->_transitionId = 17;
693 			_vm->_voy->_aptLoadMode = -1;
694 			loadTheApt();
695 			_vm->_voy->_aptLoadMode = 144;
696 			freeTheApt();
697 			break;
698 
699 		default:
700 			break;
701 		}
702 	}
703 }
704 
cardPerform(const byte * card)705 const byte *ThreadResource::cardPerform(const byte *card) {
706 	uint16 id = *card++;
707 	int subId = 5;
708 	uint32 v2;
709 	byte bVal;
710 	uint32 idx1, idx2;
711 	debugC(DEBUG_BASIC, kDebugScripts, "cardPerform - %d", id);
712 
713 	switch (id) {
714 	case 1:
715 		v2 = READ_LE_UINT32(card);
716 		card += 4;
717 		_vm->_controlPtr->_state->_vals[*card++] = v2;
718 		break;
719 
720 	case 2:
721 		v2 = _vm->_controlPtr->_state->_vals[*card++];
722 		_vm->_controlPtr->_state->_vals[*card++] = v2;
723 		break;
724 
725 	case 3:
726 		v2 = READ_LE_UINT32(card);
727 		card += 4;
728 		_vm->_controlPtr->_state->_vals[*card++] = v2;
729 		break;
730 
731 	case 4:
732 		v2 = _vm->_controlPtr->_state->_vals[*card++];
733 		_vm->_controlPtr->_state->_vals[*card++] = v2;
734 		break;
735 
736 	case 5: {
737 		v2 = READ_LE_UINT32(card);
738 		card += 4;
739 		int &v = _vm->_controlPtr->_state->_vals[*card++];
740 		v -= v2;
741 		break;
742 	}
743 
744 	case 6: {
745 		idx1 = *card++;
746 		idx2 = *card++;
747 
748 		v2 = _vm->_controlPtr->_state->_vals[idx1];
749 		int &v = _vm->_controlPtr->_state->_vals[idx2];
750 		v -= v2;
751 		break;
752 	}
753 
754 	case 7: {
755 		int v3 = *card++;
756 		v2 = READ_LE_UINT32(card);
757 		card += 4;
758 		int &v = _vm->_controlPtr->_state->_vals[v3];
759 		v *= v2;
760 		break;
761 	}
762 
763 	case 8: {
764 		idx1 = *card++;
765 		idx2 = *card++;
766 
767 		int &v1 = _vm->_controlPtr->_state->_vals[idx1];
768 		v2 = _vm->_controlPtr->_state->_vals[idx2];
769 		v1 *= v2;
770 		break;
771 	}
772 
773 	case 9: {
774 		idx1 = *card++;
775 		v2 = READ_LE_UINT32(card);
776 		card += 4;
777 
778 		int &v = _vm->_controlPtr->_state->_vals[idx1];
779 		v /= v2;
780 		break;
781 	}
782 
783 	case 10: {
784 		idx1 = *card++;
785 		idx2 = *card++;
786 
787 		int &v1 = _vm->_controlPtr->_state->_vals[idx1];
788 		v2 = _vm->_controlPtr->_state->_vals[idx2];
789 		v1 /= v2;
790 		break;
791 	}
792 
793 	case 11:
794 		v2 = READ_LE_UINT32(card);
795 		card += 4;
796 		v2 = _vm->getRandomNumber(v2 - 1) + 1;
797 		_vm->_controlPtr->_state->_vals[*card++] = v2;
798 		break;
799 
800 	case 17:
801 		_vm->_glGoState = READ_LE_UINT16(card);
802 		card += 2;
803 		_vm->_glGoStack = -1;
804 		break;
805 
806 	case 18:
807 		v2 = _vm->_controlPtr->_state->_vals[*card++];
808 		_vm->_glGoState = getStateFromID(v2);
809 		break;
810 
811 	case 19:
812 		_vm->_glGoState = READ_LE_UINT32(card);
813 		card += 4;
814 		_vm->_glGoStack = READ_LE_UINT16(card);
815 		card += 2;
816 		break;
817 
818 	case 23:
819 	case 24:
820 	case 27:
821 	case 28:
822 		subId -= 3;
823 		// fall through
824 
825 	case 21:
826 	case 22:
827 	case 25:
828 	case 26:
829 		bVal = card[subId];
830 		if (bVal == 61) {
831 			if (cardPerform2(card, id)) {
832 				card += subId;
833 				while (*card != 30 && *card != 29)
834 					card = cardPerform(card);
835 
836 				if (*card == 29) {
837 					int count = 1;
838 					while (count > 0) {
839 						card = getNextRecord(card);
840 						if (*card == 30)
841 							--count;
842 						if (*card >= 21 && *card <= 28)
843 							++count;
844 					}
845 				}
846 			} else {
847 				card += subId;
848 				int count = 1;
849 				while (count > 0) {
850 					card = getNextRecord(card);
851 					if (*card == 29 || *card == 30)
852 						--count;
853 					if (*card < 21 || *card > 28)
854 						continue;
855 
856 					const byte *nextP = getNextRecord(card + 2);
857 					if (*nextP == 61)
858 						++count;
859 				}
860 			}
861 
862 			++card;
863 		} else {
864 			if (cardPerform2(card, id)) {
865 				card += subId;
866 				card = cardPerform(card);
867 				while (*card++ != 61) {}
868 			} else {
869 				card += subId;
870 				while (*card != 61 && *card != 29)
871 					++card;
872 			}
873 		}
874 		break;
875 
876 	case 41:
877 		bVal = *card++;
878 		assert(bVal < 8);
879 		card += 6;
880 		break;
881 
882 	case 45:
883 		_newStateId = _nextStateId;
884 		_newStackId = _stackId;
885 		break;
886 
887 	case 46:
888 		_vm->_glGoState = _newStateId;
889 		_vm->_glGoStack = _newStackId;
890 		_newStateId = -1;
891 		_newStackId = -1;
892 		break;
893 
894 	case 51:
895 		setButtonFlag(READ_LE_UINT16(card), 64);
896 		break;
897 
898 	case 52:
899 		clearButtonFlag(READ_LE_UINT16(card), 64);
900 		break;
901 
902 	default:
903 		break;
904 	}
905 
906 	return card;
907 }
908 
cardPerform2(const byte * pSrc,int cardCmdId)909 bool ThreadResource::cardPerform2(const byte *pSrc, int cardCmdId) {
910 	int vLong, vLong2;
911 
912 	switch (cardCmdId) {
913 	case 21:
914 		vLong = (int32)READ_LE_UINT32(pSrc + 1);
915 		return _vm->_controlPtr->_state->_vals[*pSrc] == vLong;
916 
917 	case 22:
918 		vLong = (int32)READ_LE_UINT32(pSrc + 1);
919 		return _vm->_controlPtr->_state->_vals[*pSrc] != vLong;
920 
921 	case 23:
922 		vLong = _vm->_controlPtr->_state->_vals[*pSrc];
923 		vLong2 = _vm->_controlPtr->_state->_vals[*(pSrc + 1)];
924 		return vLong == vLong2;
925 
926 	case 24:
927 		vLong = _vm->_controlPtr->_state->_vals[*pSrc];
928 		vLong2 = _vm->_controlPtr->_state->_vals[*(pSrc + 1)];
929 		return vLong != vLong2;
930 
931 	case 25:
932 		vLong = _vm->_controlPtr->_state->_vals[*pSrc];
933 		vLong2 = (int32)READ_LE_UINT32(pSrc + 1);
934 		return vLong < vLong2;
935 
936 	case 26:
937 		vLong = _vm->_controlPtr->_state->_vals[*pSrc];
938 		vLong2 = (int32)READ_LE_UINT32(pSrc + 1);
939 		return vLong > vLong2;
940 
941 	case 27:
942 		vLong = _vm->_controlPtr->_state->_vals[*pSrc];
943 		vLong2 = _vm->_controlPtr->_state->_vals[*(pSrc + 1)];
944 		return vLong < vLong2;
945 
946 	case 28:
947 		vLong = _vm->_controlPtr->_state->_vals[*pSrc];
948 		vLong2 = _vm->_controlPtr->_state->_vals[*(pSrc + 1)];
949 		return vLong > vLong2;
950 
951 	default:
952 		return false;
953 	}
954 }
955 
doApt()956 int ThreadResource::doApt() {
957 	loadTheApt();
958 
959 	_vm->_currentVocId = 151;
960 	_vm->_voy->_viewBounds = _vm->_bVoy->boltEntry(_vm->_playStampGroupId)._rectResource;
961 	Common::Array<RectEntry> &hotspots = _vm->_bVoy->boltEntry(
962 		_vm->_playStampGroupId + 1)._rectResource->_entries;
963 	_vm->_eventsManager->getMouseInfo();
964 
965 	// Very first time apartment is shown, start the phone message
966 	if (_aptPos.x == -1) {
967 		_aptPos.x = hotspots[2].left;
968 		_aptPos.y = hotspots[2].top;
969 		_vm->_currentVocId = 153;
970 	}
971 
972 	if (_vm->_voy->_playStampMode == 16) {
973 		hotspots[0].left = 999;
974 		hotspots[3].left = 999;
975 		_aptPos.x = hotspots[4].left + 28;
976 		_aptPos.y = hotspots[4].top + 28;
977 	}
978 
979 	_vm->_eventsManager->setMousePos(Common::Point(_aptPos.x, _aptPos.y));
980 	_vm->_soundManager->startVOCPlay(_vm->_soundManager->getVOCFileName(_vm->_currentVocId));
981 	_vm->_currentVocId = 151;
982 
983 	_vm->_screen->setColor(129, 82, 82, 82);
984 	_vm->_screen->setColor(130, 112, 112, 112);
985 	_vm->_screen->setColor(131, 215, 215, 215);
986 	_vm->_screen->setColor(132, 235, 235, 235);
987 
988 	_vm->_eventsManager->_intPtr._hasPalette = true;
989 
990 	// Set up the cursors
991 	PictureResource *unselectedCursor = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + 2)._picResource;
992 	PictureResource *selectedCursor = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + 3)._picResource;
993 	unselectedCursor->_keyColor = 0xff;
994 	selectedCursor->_keyColor = 0xff;
995 	_vm->_eventsManager->setCursor(unselectedCursor);
996 	_vm->_eventsManager->showCursor();
997 
998 	// Main loop to allow users to move the cursor and select hotspots
999 	int hotspotId;
1000 	int prevHotspotId = -1;
1001 	Common::Point pt;
1002 	PictureResource *pic;
1003 	Common::Rect gmmHotspot(75, 125, 130, 140);
1004 
1005 	do {
1006 		_vm->_voyeurArea = AREA_APARTMENT;
1007 
1008 		if (_vm->_loadGameSlot != -1) {
1009 			// Load a savegame
1010 			int slot = _vm->_loadGameSlot;
1011 			_vm->_loadGameSlot = -1;
1012 			_vm->loadGame(slot);
1013 
1014 			_vm->_eventsManager->showCursor();
1015 		}
1016 
1017 		_vm->_eventsManager->getMouseInfo();
1018 		if (!_vm->_soundManager->getVOCStatus()) {
1019 			// Previous sound ended, so start up a new one
1020 			_vm->_currentVocId = 151 - _vm->getRandomNumber(4);
1021 			_vm->_soundManager->startVOCPlay(_vm->_soundManager->getVOCFileName(_vm->_currentVocId));
1022 		}
1023 
1024 		// Loop through the hotspot list
1025 		hotspotId = -1;
1026 		pt = _vm->_eventsManager->getMousePos() + Common::Point(16, 16);
1027 		for (int idx = 0; idx < (int)hotspots.size(); ++idx) {
1028 			if (hotspots[idx].contains(pt)) {
1029 				// Cursor is within hotspot area
1030 
1031 				// Don't allow the camera to be highlighted on Monday morning.
1032 				if (idx == 0 && _vm->_voy->_transitionId == 17)
1033 					continue;
1034 
1035 				// Set the highlighted hotspot Id
1036 				hotspotId = idx;
1037 
1038 				if (hotspotId != prevHotspotId) {
1039 					// Check for whether to replace hotspot Id for "Watch TV" for
1040 					// "Review the Tape" if player has already watched the TV
1041 					if ((_vm->_voy->_eventFlags & EVTFLAG_100) && (hotspotId == 2))
1042 						hotspotId = 5;
1043 
1044 					// Draw the text description for the highlighted hotspot
1045 					pic = _vm->_bVoy->boltEntry(_vm->_playStampGroupId +
1046 						hotspotId + 6)._picResource;
1047 					_vm->_screen->sDrawPic(pic, _vm->_screen->_vPort,
1048 						Common::Point(106, 200));
1049 				}
1050 
1051 				break;
1052 			}
1053 		}
1054 
1055 		// Check for presence in ScummVM GMM
1056 		if (gmmHotspot.contains(pt))
1057 			hotspotId = 42;
1058 
1059 		// Update the cursor to either standard or highlighted eye cursor
1060 		_vm->_eventsManager->setCursor((hotspotId == -1) ? unselectedCursor : selectedCursor);
1061 		_vm->flipPageAndWait();
1062 
1063 		if (hotspotId == 42 && _vm->_eventsManager->_leftClick) {
1064 			// Show the ScummVM GMM
1065 			_vm->_eventsManager->getMouseInfo();
1066 			_vm->openMainMenuDialog();
1067 		}
1068 
1069 	} while (!_vm->shouldQuit() && (!_vm->_eventsManager->_leftClick || hotspotId == -1));
1070 
1071 	_vm->_eventsManager->hideCursor();
1072 	pt = _vm->_eventsManager->getMousePos();
1073 	_aptPos.x = pt.x;
1074 	_aptPos.y = pt.y;
1075 
1076 	switch (hotspotId) {
1077 	case 0:
1078 		_vm->_voy->_aptLoadMode = 140;
1079 		break;
1080 	case 1:
1081 		_vm->_voy->_aptLoadMode = 143;
1082 		break;
1083 	case 2:
1084 		_vm->_voy->_aptLoadMode = 142;
1085 		break;
1086 	case 5:
1087 		_vm->_voy->_aptLoadMode = 141;
1088 		break;
1089 	default:
1090 		_vm->_voy->_aptLoadMode = -1;
1091 		break;
1092 	}
1093 
1094 	freeTheApt();
1095 
1096 	if (_vm->_voy->_transitionId == 1 && hotspotId == 0)
1097 		_vm->checkTransition();
1098 
1099 	if (!hotspotId)
1100 		_vm->makeViewFinder();
1101 
1102 	return hotspotId;
1103 }
1104 
doRoom()1105 void ThreadResource::doRoom() {
1106 	VoyeurEngine &vm = *_vm;
1107 	SVoy voy = *vm._voy;
1108 
1109 	vm.makeViewFinderP();
1110 	voy._fadingType = 0;
1111 
1112 	if (!vm._bVoy->getBoltGroup(vm._playStampGroupId))
1113 		return;
1114 
1115 	vm._screen->_backColors = vm._bVoy->boltEntry(vm._playStampGroupId + 1)._cMapResource;
1116 	vm._screen->_backgroundPage = vm._bVoy->boltEntry(vm._playStampGroupId)._picResource;
1117 	vm._screen->_vPort->setupViewPort(vm._screen->_backgroundPage);
1118 	vm._screen->_backColors->startFade();
1119 
1120 	voy._fadingStep1 = 2;
1121 	voy._fadingStep2 = 0;
1122 	voy._fadingType = 1;
1123 
1124 	Common::Array<RectEntry> &hotspots = vm._bVoy->boltEntry(vm._playStampGroupId + 4)._rectResource->_entries;
1125 	int hotspotId = -1;
1126 
1127 	PictureResource *crosshairsCursor = vm._bVoy->boltEntry(vm._playStampGroupId + 2)._picResource;
1128 	PictureResource *magnifierCursor = vm._bVoy->boltEntry(vm._playStampGroupId + 3)._picResource;
1129 	vm._eventsManager->showCursor();
1130 
1131 	RectResource viewBounds(48, 38, 336, 202);
1132 	voy._viewBounds = &viewBounds;
1133 
1134 	vm._eventsManager->getMouseInfo();
1135 	vm._eventsManager->setMousePos(Common::Point(192, 120));
1136 	voy._fadingType = 0;
1137 	vm._currentVocId = 146;
1138 	voy._musicStartTime = voy._RTVNum;
1139 
1140 	voy._vocSecondsOffset = 0;
1141 	vm._soundManager->startVOCPlay(vm._currentVocId);
1142 	voy._eventFlags &= ~EVTFLAG_TIME_DISABLED;
1143 
1144 	bool breakFlag = false;
1145 	while (!vm.shouldQuit() && !breakFlag) {
1146 		_vm->_voyeurArea = AREA_ROOM;
1147 		vm._screen->setColor(128, 0, 255, 0);
1148 		vm._eventsManager->_intPtr._hasPalette = true;
1149 
1150 		do {
1151 			if (vm._currentVocId != -1 && !vm._soundManager->getVOCStatus()) {
1152 				voy._musicStartTime = voy._RTVNum;
1153 				voy._vocSecondsOffset = 0;
1154 				vm._soundManager->startVOCPlay(vm._currentVocId);
1155 			}
1156 
1157 			vm._eventsManager->getMouseInfo();
1158 			Common::Point pt = vm._eventsManager->getMousePos();
1159 			pt += Common::Point(30, 15);
1160 
1161 			hotspotId = -1;
1162 
1163 			if (voy._computerTextId != -1 && voy._computerScreenRect.contains(pt))
1164 				hotspotId = 999;
1165 
1166 			for (uint idx = 0; idx < hotspots.size(); ++idx) {
1167 				if (hotspots[idx].contains(pt)) {
1168 					int arrIndex = hotspots[idx]._arrIndex;
1169 					if (voy._roomHotspotsEnabled[arrIndex - 1]) {
1170 						hotspotId = idx;
1171 						break;
1172 					}
1173 				}
1174 			}
1175 
1176 			if (hotspotId == -1) {
1177 				vm._eventsManager->setCursorColor(128, 0);
1178 				vm._eventsManager->setCursor(crosshairsCursor);
1179 			} else if (hotspotId != 999 || voy._RTVNum < voy._computerTimeMin ||
1180 					(voy._computerTimeMax - 2) < voy._RTVNum) {
1181 				vm._eventsManager->setCursorColor(128, 1);
1182 				vm._eventsManager->setCursor(magnifierCursor);
1183 			} else {
1184 				vm._eventsManager->setCursorColor(128, 2);
1185 				vm._eventsManager->setCursor(magnifierCursor);
1186 			}
1187 
1188 			vm._eventsManager->_intPtr._hasPalette = true;
1189 			vm._screen->_vPort->_flags |= DISPFLAG_8;
1190 			vm._screen->flipPage();
1191 			vm._eventsManager->sWaitFlip();
1192 		} while (!vm.shouldQuit() && !vm._eventsManager->_mouseClicked);
1193 
1194 		if (!vm._eventsManager->_leftClick || hotspotId == -1) {
1195 			if (vm._eventsManager->_rightClick)
1196 				breakFlag = true;
1197 
1198 			Common::Point pt = vm._eventsManager->getMousePos();
1199 			vm._eventsManager->getMouseInfo();
1200 			vm._eventsManager->setMousePos(pt);
1201 		} else {
1202 			voy._eventFlags |= EVTFLAG_RECORDING;
1203 			vm._eventsManager->hideCursor();
1204 			vm._eventsManager->startCursorBlink();
1205 
1206 			if (hotspotId == 999) {
1207 				_vm->flipPageAndWait();
1208 
1209 				if (vm._currentVocId != -1) {
1210 					voy._vocSecondsOffset = voy._RTVNum - voy._musicStartTime;
1211 					vm._soundManager->stopVOCPlay();
1212 				}
1213 
1214 				vm.getComputerBrush();
1215 				_vm->flipPageAndWait();
1216 
1217 				vm._voy->addComputerEventStart();
1218 
1219 				vm._eventsManager->_mouseClicked = false;
1220 				vm._eventsManager->startCursorBlink();
1221 
1222 				int totalChars = vm.doComputerText(9999);
1223 				if (totalChars)
1224 					vm._voy->addComputerEventEnd(totalChars);
1225 
1226 				vm._bVoy->freeBoltGroup(0x4900);
1227 			} else {
1228 				vm.doEvidDisplay(hotspotId, 999);
1229 			}
1230 
1231 			voy._eventFlags &= ~EVTFLAG_RECORDING;
1232 			if (!vm._eventsManager->_mouseClicked)
1233 				vm._eventsManager->delayClick(18000);
1234 
1235 			// WORKAROUND: Skipped code from the original, that freed the group,
1236 			// reloaded it, and reloaded the cursors
1237 
1238 			vm._screen->_backColors = vm._bVoy->boltEntry(
1239 				vm._playStampGroupId + 1)._cMapResource;
1240 			vm._screen->_backgroundPage = vm._bVoy->boltEntry(
1241 				vm._playStampGroupId)._picResource;
1242 
1243 			vm._screen->_vPort->setupViewPort();
1244 			vm._screen->_backColors->startFade();
1245 			_vm->flipPageAndWait();
1246 
1247 			while (!vm.shouldQuit() && (vm._eventsManager->_fadeStatus & 1))
1248 				vm._eventsManager->delay(1);
1249 			vm._eventsManager->hideCursor();
1250 
1251 			while (!vm.shouldQuit() && voy._fadingAmount2 > 0) {
1252 				if (voy._fadingAmount1 < 63) {
1253 					voy._fadingAmount1 += 4;
1254 					if (voy._fadingAmount1 > 63)
1255 						voy._fadingAmount1 = 63;
1256 				}
1257 
1258 				if (voy._fadingAmount2 > 0) {
1259 					voy._fadingAmount2 -= 8;
1260 					if (voy._fadingAmount2 < 0)
1261 						voy._fadingAmount2 = 0;
1262 				}
1263 
1264 				vm._eventsManager->delay(1);
1265 			}
1266 
1267 			_vm->flipPageAndWait();
1268 
1269 			vm._screen->fadeUpICF1();
1270 			voy._eventFlags &= EVTFLAG_RECORDING;
1271 			vm._eventsManager->showCursor();
1272 		}
1273 	}
1274 
1275 	voy._eventFlags = EVTFLAG_TIME_DISABLED;
1276 	vm._eventsManager->incrementTime(1);
1277 	voy._viewBounds = nullptr;
1278 	voy._fadingType = 0;
1279 	vm.makeViewFinderP();
1280 
1281 	if (voy._boltGroupId2 != -1) {
1282 		vm._bVoy->freeBoltGroup(voy._boltGroupId2);
1283 		voy._boltGroupId2 = -1;
1284 	}
1285 
1286 	if (vm._playStampGroupId != -1) {
1287 		vm._bVoy->freeBoltGroup(vm._playStampGroupId);
1288 		vm._playStampGroupId = -1;
1289 	}
1290 
1291 	if (vm._currentVocId != -1) {
1292 		vm._soundManager->stopVOCPlay();
1293 		vm._currentVocId = -1;
1294 	}
1295 
1296 	vm._eventsManager->hideCursor();
1297 	chooseSTAMPButton(0);
1298 }
1299 
doInterface()1300 int ThreadResource::doInterface() {
1301 	PictureResource *pic;
1302 	Common::Point pt;
1303 
1304 	_vm->_voy->_eventFlags |= EVTFLAG_TIME_DISABLED;
1305 	if (_vm->_voy->_abortInterface) {
1306 		_vm->_voy->_abortInterface = false;
1307 		return -2;
1308 	}
1309 
1310 	_vm->_voy->_eventFlags &= ~EVTFLAG_100;
1311 	_vm->_playStampGroupId = -1;
1312 	_vm->_eventsManager->_intPtr._flashStep = 1;
1313 	_vm->_eventsManager->_intPtr._flashTimer = 0;
1314 
1315 	if (_vm->_voy->_RTVNum >= _vm->_voy->_RTVLimit || _vm->_voy->_RTVNum < 0)
1316 		_vm->_voy->_RTVNum = _vm->_voy->_RTVLimit - 1;
1317 
1318 	if (_vm->_voy->_transitionId < 15 && _vm->_debugger->_isTimeActive
1319 		&& (_vm->_voy->_RTVLimit - 3) < _vm->_voy->_RTVNum) {
1320 		_vm->_voy->_RTVNum = _vm->_voy->_RTVLimit;
1321 		_vm->makeViewFinder();
1322 
1323 		_vm->initIFace();
1324 		_vm->_eventsManager->hideCursor();
1325 		_vm->_voy->_RTVNum = _vm->_voy->_RTVLimit - 4;
1326 		_vm->_voy->_eventFlags &= ~EVTFLAG_TIME_DISABLED;
1327 
1328 		while (!_vm->shouldQuit() && _vm->_voy->_RTVNum < _vm->_voy->_RTVLimit) {
1329 			_vm->flashTimeBar();
1330 			_vm->_eventsManager->delayClick(1);
1331 		}
1332 
1333 		_vm->_voy->_eventFlags |= EVTFLAG_TIME_DISABLED;
1334 		chooseSTAMPButton(20);
1335 		parsePlayCommands();
1336 
1337 		_vm->_eventsManager->showCursor();
1338 	}
1339 
1340 	_vm->checkTransition();
1341 	_vm->makeViewFinder();
1342 	_vm->_eventsManager->getMouseInfo();
1343 	_vm->initIFace();
1344 
1345 	Common::Array<RectEntry> *hotspots = &_vm->_bVoy->boltEntry(
1346 		_vm->_playStampGroupId + 1)._rectResource->_entries;
1347 	_vm->_currentVocId = 151 - _vm->getRandomNumber(5);
1348 	_vm->_voy->_vocSecondsOffset = _vm->getRandomNumber(29);
1349 
1350 	Common::String fname = _vm->_soundManager->getVOCFileName(_vm->_currentVocId);
1351 	_vm->_soundManager->startVOCPlay(fname);
1352 	_vm->_eventsManager->getMouseInfo();
1353 
1354 	_vm->_screen->setColor(240, 220, 220, 220);
1355 	_vm->_eventsManager->_intPtr._hasPalette = true;
1356 	_vm->_voy->_eventFlags &= ~EVTFLAG_TIME_DISABLED;
1357 
1358 	// Set the cusor
1359 	PictureResource *crosshairsCursor = _vm->_bVoy->boltEntry(0x112)._picResource;
1360 	PictureResource *eyeCursor = _vm->_bVoy->boltEntry(0x113)._picResource;
1361 	PictureResource *listenCursor = _vm->_bVoy->boltEntry(0x114)._picResource;
1362 	PictureResource *mangifyCursor = _vm->_bVoy->boltEntry(0x115)._picResource;
1363 
1364 	_vm->_eventsManager->setCursor(crosshairsCursor);
1365 
1366 	// Main loop
1367 	int regionIndex = 0;
1368 	Common::Rect mansionViewBounds(MANSION_VIEW_X, MANSION_VIEW_Y,
1369 		MANSION_VIEW_X + MANSION_VIEW_WIDTH, MANSION_VIEW_Y + MANSION_VIEW_HEIGHT);
1370 
1371 	do {
1372 		_vm->_voyeurArea = AREA_INTERFACE;
1373 		_vm->doTimeBar();
1374 		_vm->_eventsManager->getMouseInfo();
1375 
1376 		if (checkMansionScroll())
1377 			_vm->doScroll(_vm->_mansionViewPos);
1378 
1379 		_vm->checkPhoneCall();
1380 		if (!_vm->_soundManager->getVOCStatus()) {
1381 			_vm->_currentVocId = 151 - _vm->getRandomNumber(5);
1382 			_vm->_soundManager->startVOCPlay(_vm->_soundManager->getVOCFileName(_vm->_currentVocId));
1383 		}
1384 
1385 		// Calculate the mouse position within the entire mansion
1386 		pt = _vm->_eventsManager->getMousePos();
1387 		if (!mansionViewBounds.contains(pt))
1388 			pt = Common::Point(-1, -1);
1389 		else
1390 			pt = _vm->_mansionViewPos +
1391 				Common::Point(pt.x - MANSION_VIEW_X, pt.y - MANSION_VIEW_Y);
1392 		regionIndex = -1;
1393 
1394 		for (uint hotspotIdx = 0; hotspotIdx < hotspots->size(); ++hotspotIdx) {
1395 			if ((*hotspots)[hotspotIdx].contains(pt)) {
1396 				// Rect check done
1397 				for (int arrIndex = 0; arrIndex < 3; ++arrIndex) {
1398 					if (_vm->_voy->_audioHotspotTimes.isInRange(arrIndex, hotspotIdx, _vm->_voy->_RTVNum)) {
1399 						// Set the ear cursor for an audio event
1400 						_vm->_eventsManager->setCursor(listenCursor);
1401 						regionIndex = hotspotIdx;
1402 					}
1403 
1404 					if (_vm->_voy->_evidenceHotspotTimes.isInRange(arrIndex, hotspotIdx, _vm->_voy->_RTVNum)) {
1405 						// Set the magnifier cursor for an evidence event
1406 						_vm->_eventsManager->setCursor(mangifyCursor);
1407 						regionIndex = hotspotIdx;
1408 					}
1409 				}
1410 
1411 				for (int arrIndex = 0; arrIndex < 8; ++arrIndex) {
1412 					if (_vm->_voy->_videoHotspotTimes.isInRange(arrIndex, hotspotIdx, _vm->_voy->_RTVNum)) {
1413 						// Set the eye cursor for a video event
1414 						_vm->_eventsManager->setCursor(eyeCursor);
1415 						regionIndex = hotspotIdx;
1416 					}
1417 				}
1418 			}
1419 		}
1420 
1421 		if (regionIndex == -1) {
1422 			// Reset back to the crosshairs cursor
1423 			_vm->_eventsManager->setCursor(crosshairsCursor);
1424 		}
1425 
1426 		// Regularly update the time display
1427 		if (_vm->_voy->_RTANum & 2) {
1428 			_vm->_screen->drawANumber(_vm->_screen->_vPort,
1429 				_vm->_gameMinute / 10, Common::Point(190, 25));
1430 			_vm->_screen->drawANumber(_vm->_screen->_vPort,
1431 				_vm->_gameMinute % 10, Common::Point(201, 25));
1432 
1433 			if (_vm->_voy->_RTANum & 4) {
1434 				int v = _vm->_gameHour / 10;
1435 				_vm->_screen->drawANumber(_vm->_screen->_vPort,
1436 					v == 0 ? 10 : v, Common::Point(161, 25));
1437 				_vm->_screen->drawANumber(_vm->_screen->_vPort,
1438 					_vm->_gameHour % 10, Common::Point(172, 25));
1439 
1440 				pic = _vm->_bVoy->boltEntry(_vm->_voy->_isAM ? 272 : 273)._picResource;
1441 				_vm->_screen->sDrawPic(pic, _vm->_screen->_vPort,
1442 					Common::Point(215, 27));
1443 			}
1444 		}
1445 
1446 		_vm->_voy->_RTANum = 0;
1447 		_vm->flipPageAndWait();
1448 
1449 		pt = _vm->_eventsManager->getMousePos();
1450 		if ((_vm->_voy->_RTVNum >= _vm->_voy->_RTVLimit) || ((_vm->_voy->_eventFlags & EVTFLAG_VICTIM_PRESET) &&
1451 				_vm->_eventsManager->_rightClick && (pt.x == 0))) {
1452 			// Time to transition to the next time period
1453 			_vm->_eventsManager->getMouseInfo();
1454 
1455 			if (_vm->_voy->_transitionId == 15) {
1456 				regionIndex = 20;
1457 				_vm->_voy->_transitionId = 17;
1458 				_vm->_soundManager->stopVOCPlay();
1459 				_vm->checkTransition();
1460 				_vm->_eventsManager->_leftClick = true;
1461 			} else {
1462 				_vm->_voy->_eventFlags |= EVTFLAG_TIME_DISABLED;
1463 
1464 				chooseSTAMPButton(20);
1465 				parsePlayCommands();
1466 				_vm->checkTransition();
1467 				_vm->makeViewFinder();
1468 
1469 				_vm->initIFace();
1470 
1471 				hotspots = &_vm->_bVoy->boltEntry(_vm->_playStampGroupId + 1)._rectResource->_entries;
1472 				_vm->_eventsManager->getMouseInfo();
1473 
1474 				_vm->_voy->_eventFlags &= ~EVTFLAG_TIME_DISABLED;
1475 				_vm->_eventsManager->_intPtr._flashStep = 1;
1476 				_vm->_eventsManager->_intPtr._flashTimer = 0;
1477 			}
1478 		}
1479 	} while (!_vm->_eventsManager->_rightClick && !_vm->shouldQuit() &&
1480 		(!_vm->_eventsManager->_leftClick || regionIndex == -1));
1481 
1482 	_vm->_eventsManager->hideCursor();
1483 	_vm->_voy->_eventFlags |= EVTFLAG_TIME_DISABLED;
1484 	_vm->_bVoy->freeBoltGroup(_vm->_playStampGroupId);
1485 	if (_vm->_currentVocId != -1)
1486 		_vm->_soundManager->stopVOCPlay();
1487 
1488 	return !_vm->_eventsManager->_rightClick ? regionIndex : -2;
1489 }
1490 
checkMansionScroll()1491 bool ThreadResource::checkMansionScroll() {
1492 	Common::Point pt = _vm->_eventsManager->getMousePos() -
1493 		Common::Point(MANSION_VIEW_X, MANSION_VIEW_Y);
1494 	Common::Point &viewPos = _vm->_mansionViewPos;
1495 	bool result = false;
1496 
1497 	// Scroll mansion view if close to any of the mansion edges
1498 	if (pt.x >= 0 && pt.x < MANSION_SCROLL_AREA_X && viewPos.x > 0) {
1499 		viewPos.x = MAX(viewPos.x - MANSION_SCROLL_INC_X, 0);
1500 		result = true;
1501 	}
1502 	if  (pt.x >= (MANSION_VIEW_WIDTH - MANSION_SCROLL_AREA_X) &&
1503 			pt.x < MANSION_VIEW_WIDTH && viewPos.x < MANSION_MAX_X) {
1504 		viewPos.x = MIN(viewPos.x + MANSION_SCROLL_INC_X, MANSION_MAX_X);
1505 		result = true;
1506 	}
1507 	if (pt.y >= 0 && pt.y < MANSION_SCROLL_AREA_Y && viewPos.y > 0) {
1508 		viewPos.y = MAX(viewPos.y - MANSION_SCROLL_INC_Y, 0);
1509 		result = true;
1510 	}
1511 	if  (pt.y >= (MANSION_VIEW_HEIGHT - MANSION_SCROLL_AREA_Y) &&
1512 			pt.y < MANSION_VIEW_HEIGHT && viewPos.y < MANSION_MAX_Y) {
1513 		viewPos.y = MIN(viewPos.y + MANSION_SCROLL_INC_Y, MANSION_MAX_Y);
1514 		result = true;
1515 	}
1516 
1517 	// Return whether mansion view area has changed
1518 	return result;
1519 }
1520 
goToStateID(int stackId,int id)1521 bool ThreadResource::goToStateID(int stackId, int id) {
1522 	debugC(DEBUG_BASIC, kDebugScripts, "goToStateID - %d, %d", stackId, id);
1523 
1524 	// Save current stack
1525 	savePrevious();
1526 
1527 	if (_stackId == stackId || stackId == -1 || loadAStack(stackId)) {
1528 		// Now in the correct state
1529 		_stateId = getStateFromID(id);
1530 
1531 		if (_stateId != -1) {
1532 			return doState();
1533 		} else {
1534 			_stateId = _savedStateId;
1535 			_stackId = _savedStackId;
1536 		}
1537 	}
1538 
1539 	return false;
1540 }
1541 
goToState(int stackId,int stateId)1542 bool ThreadResource::goToState(int stackId, int stateId) {
1543 	debugC(DEBUG_BASIC, kDebugScripts, "goToState - %d, %d", stackId, stateId);
1544 
1545 	savePrevious();
1546 	if (stackId == -1 || loadAStack(stackId)) {
1547 		if (stateId != -1)
1548 			_stateId = stateId;
1549 
1550 		return doState();
1551 	} else {
1552 		return false;
1553 	}
1554 }
1555 
savePrevious()1556 void ThreadResource::savePrevious() {
1557 	if (_savedStateId != _stateId || _stackId != _savedStackId) {
1558 		_savedStateId = _stateId;
1559 		_savedStackId = _stackId;
1560 	}
1561 }
1562 
setButtonFlag(int idx,byte bits)1563 void ThreadResource::setButtonFlag(int idx, byte bits) {
1564 	_buttonFlags[idx] |= bits;
1565 }
1566 
clearButtonFlag(int idx,byte bits)1567 void ThreadResource::clearButtonFlag(int idx, byte bits) {
1568 	_buttonFlags[idx] &= ~bits;
1569 }
1570 
loadTheApt()1571 void ThreadResource::loadTheApt() {
1572 	switch (_vm->_voy->_transitionId) {
1573 	case 1:
1574 	case 2:
1575 	case 5:
1576 	case 6:
1577 	case 7:
1578 	case 8:
1579 	case 9:
1580 	case 17:
1581 		_vm->_playStampGroupId = 0x5700;
1582 		break;
1583 	case 3:
1584 		_vm->_playStampGroupId = 0x5800;
1585 		break;
1586 	case 4:
1587 	case 10:
1588 	case 11:
1589 	case 12:
1590 	case 13:
1591 	case 14:
1592 	case 15:
1593 	case 16:
1594 		_vm->_playStampGroupId = 0x5900;
1595 		break;
1596 	default:
1597 		break;
1598 	}
1599 
1600 	if (_vm->_voy->_aptLoadMode == 143)
1601 		_vm->_voy->_aptLoadMode = -1;
1602 
1603 	if (_vm->_voy->_aptLoadMode  != -1) {
1604 		if (_vm->_loadGameSlot != -1)
1605 			doAptAnim(1);
1606 
1607 		_vm->_bVoy->getBoltGroup(_vm->_playStampGroupId);
1608 		_vm->_voy->_aptLoadMode = -1;
1609 		_vm->_screen->_backgroundPage = _vm->_bVoy->boltEntry(
1610 			_vm->_playStampGroupId + 5)._picResource;
1611 		_vm->_screen->_vPort->setupViewPort(
1612 			_vm->_screen->_backgroundPage);
1613 	} else {
1614 		_vm->_bVoy->getBoltGroup(_vm->_playStampGroupId);
1615 		_vm->_screen->_backgroundPage = _vm->_bVoy->boltEntry(
1616 			_vm->_playStampGroupId + 5)._picResource;
1617 		_vm->_screen->_vPort->setupViewPort(
1618 			_vm->_screen->_backgroundPage);
1619 	}
1620 
1621 	CMapResource *pal = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + 4)._cMapResource;
1622 	pal->_steps = 1;
1623 	pal->startFade();
1624 	_vm->flipPageAndWaitForFade();
1625 }
1626 
freeTheApt()1627 void ThreadResource::freeTheApt() {
1628 	_vm->_screen->fadeDownICF1(5);
1629 	_vm->flipPageAndWaitForFade();
1630 
1631 	_vm->_screen->fadeUpICF1();
1632 
1633 	if (_vm->_currentVocId != -1) {
1634 		_vm->_soundManager->stopVOCPlay();
1635 		_vm->_currentVocId = -1;
1636 	}
1637 
1638 	if (_vm->_voy->_aptLoadMode == -1) {
1639 		_vm->_screen->fadeDownICF(6);
1640 	} else {
1641 		doAptAnim(2);
1642 	}
1643 
1644 	if (_vm->_voy->_aptLoadMode == 140) {
1645 		_vm->_screen->screenReset();
1646 		_vm->_screen->resetPalette();
1647 	}
1648 
1649 	_vm->_screen->_vPort->setupViewPort(nullptr);
1650 	_vm->_bVoy->freeBoltGroup(_vm->_playStampGroupId);
1651 	_vm->_playStampGroupId = -1;
1652 	_vm->_voy->_viewBounds = nullptr;
1653 }
1654 
doAptAnim(int mode)1655 void ThreadResource::doAptAnim(int mode) {
1656 	_vm->_bVoy->freeBoltGroup(0x100);
1657 
1658 	// Figure out the resource to use
1659 	int id = 0;
1660 	switch (_vm->_voy->_aptLoadMode) {
1661 	case 140:
1662 		id = 0x5A00;
1663 		break;
1664 	case 141:
1665 		id = 0x6000;
1666 		break;
1667 	case 142:
1668 		id = 0x6600;
1669 		break;
1670 	case 143:
1671 		id = 0x6C00;
1672 		break;
1673 	case 144:
1674 		id = 0x6F00;
1675 		break;
1676 	default:
1677 		break;
1678 	}
1679 
1680 	int id2 = (id == 0x6C00 || id == 0x6F00) ? 1 : 2;
1681 	switch (_vm->_voy->_transitionId) {
1682 	case 3:
1683 		id += id2 << 8;
1684 		break;
1685 	case 4:
1686 	case 10:
1687 	case 11:
1688 	case 12:
1689 	case 13:
1690 	case 14:
1691 	case 15:
1692 	case 16:
1693 		id += id2 << 9;
1694 		break;
1695 	default:
1696 		break;
1697 	}
1698 
1699 	if (mode == 1)
1700 		id += 0x100;
1701 
1702 	// Do the display
1703 	if (_vm->_bVoy->getBoltGroup(id)) {
1704 		CMapResource *pal = _vm->_bVoy->boltEntry(id)._cMapResource;
1705 		pal->_steps = 1;
1706 
1707 		for (int idx = 0; (idx < 6) && !_vm->shouldQuit(); ++idx) {
1708 			PictureResource *pic = _vm->_bVoy->boltEntry(id + idx + 1)._picResource;
1709 			_vm->_screen->_vPort->setupViewPort(pic);
1710 			pal->startFade();
1711 
1712 			_vm->flipPageAndWait();
1713 			_vm->_eventsManager->delayClick(5);
1714 		}
1715 
1716 		_vm->_bVoy->freeBoltGroup(id);
1717 	}
1718 
1719 	_vm->_bVoy->getBoltGroup(0x100);
1720 }
1721 
synchronize(Common::Serializer & s)1722 void ThreadResource::synchronize(Common::Serializer &s) {
1723 	s.syncAsSint16LE(_aptPos.x);
1724 	s.syncAsSint16LE(_aptPos.y);
1725 
1726 	int stateId = _stateId;
1727 	int stackId = _stackId;
1728 	s.syncAsSint16LE(stateId);
1729 	s.syncAsSint16LE(stackId);
1730 
1731 	if (s.isLoading() && (stateId != _stateId || stackId != _stackId))
1732 		goToState(stackId, stateId);
1733 
1734 	s.syncAsSint16LE(_savedStateId);
1735 	s.syncAsSint16LE(_savedStackId);
1736 }
1737 
1738 } // End of namespace Voyeur
1739