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 
28 namespace Voyeur {
29 
30 #define BOLT_GROUP_SIZE 16
31 
BoltFilesState(VoyeurEngine * vm)32 BoltFilesState::BoltFilesState(VoyeurEngine *vm) : _vm(vm) {
33 	_curLibPtr = NULL;
34 	_curGroupPtr = NULL;
35 	_curMemberPtr = NULL;
36 	_bufferEnd = 0;
37 	_bufferBegin = 0;
38 	_bytesLeft = 0;
39 	_bufSize = 0;
40 	_bufStart = NULL;
41 	_bufPos = NULL;
42 	_historyIndex = 0;
43 	_runLength = 0;
44 	_decompState = false;
45 	_runType = 0;
46 	_runValue = 0;
47 	_runOffset = 0;
48 	Common::fill(&_historyBuffer[0], &_historyBuffer[0x200], 0);
49 	_curFd = NULL;
50 	_boltPageFrame = NULL;
51 }
52 
53 #define NEXT_BYTE if (--_bytesLeft < 0) nextBlock()
54 
decompress(byte * buf,int size,int mode)55 byte *BoltFilesState::decompress(byte *buf, int size, int mode) {
56 	if (!buf) {
57 		buf = new byte[size];
58 		Common::fill(buf, buf + size, 0);
59 	}
60 	byte *bufP = buf;
61 
62 	if (mode & 8) {
63 		_decompState = true;
64 		_runType = 0;
65 		_runLength = size;
66 	}
67 
68 	while (size > 0) {
69 		if (!_decompState) {
70 			NEXT_BYTE;
71 			byte nextByte = *_bufPos++;
72 
73 			switch (nextByte & 0xC0) {
74 			case 0:
75 				_runType = 0;
76 				_runLength = 30 - (nextByte & 0x1f) + 1;
77 				break;
78 			case 0x40:
79 				_runType = 1;
80 				_runLength = 35 - (nextByte & 0x1f);
81 				NEXT_BYTE;
82 				_runOffset = *_bufPos++ + ((nextByte & 0x20) << 3);
83 				break;
84 			case 0x80:
85 				_runType = 1;
86 				_runLength = (nextByte & 0x20) ? ((32 - (nextByte & 0x1f)) << 2) + 2 :
87 					(32 - (nextByte & 0x1f)) << 2;
88 				NEXT_BYTE;
89 				_runOffset = *_bufPos++ << 1;
90 				break;
91 			default:
92 				_runType = 2;
93 
94 				if (nextByte & 0x20) {
95 					_runLength = 0;
96 				} else {
97 					NEXT_BYTE;
98 					_runLength = ((32 - (nextByte & 0x1f)) + (*_bufPos++ << 5)) << 2;
99 					NEXT_BYTE;
100 					_bufPos++;
101 					NEXT_BYTE;
102 					_runValue = *_bufPos++;
103 				}
104 				break;
105 			}
106 
107 			_runOffset = _historyIndex - _runOffset;
108 		}
109 
110 		int runOffset = _runOffset & 0x1ff;
111 		int len;
112 		if (_runLength <= size) {
113 			len = _runLength;
114 			_decompState = false;
115 		} else {
116 			_decompState = true;
117 			len = size;
118 			_runLength -= size;
119 			if (_runType == 1)
120 				_runOffset += len;
121 		}
122 
123 		// Reduce the remaining size
124 		size -= len;
125 
126 		// Handle the run lengths
127 		switch (_runType) {
128 		case 0:
129 			while (len-- > 0) {
130 				NEXT_BYTE;
131 				byte v = *_bufPos++;
132 				_historyBuffer[_historyIndex] = v;
133 				*bufP++ = v;
134 				_historyIndex = (_historyIndex + 1) & 0x1ff;
135 			}
136 			break;
137 		case 1:
138 			while (len-- > 0) {
139 				_historyBuffer[_historyIndex] = _historyBuffer[runOffset];
140 				*bufP++ = _historyBuffer[runOffset];
141 				_historyIndex = (_historyIndex + 1) & 0x1ff;
142 				runOffset = (runOffset + 1) & 0x1ff;
143 			}
144 			break;
145 		default:
146 			while (len-- > 0) {
147 				_historyBuffer[_historyIndex] = _runValue;
148 				*bufP++ = _runValue;
149 				_historyIndex = (_historyIndex + 1) & 0x1ff;
150 			}
151 			break;
152 		}
153 	}
154 
155 	return buf;
156 }
157 
158 #undef NEXT_BYTE
159 
nextBlock()160 void BoltFilesState::nextBlock() {
161 	if (&_curLibPtr->_file != _curFd || _curFd->pos() != _bufferEnd)
162 		_curLibPtr->_file.seek(_bufferEnd);
163 
164 	_curFd = &_curLibPtr->_file;
165 	_bufferBegin = _bufferEnd;
166 	int bytesRead = _curFd->read(_bufStart, _bufSize);
167 
168 	_bufferEnd = _curFd->pos();
169 	_bytesLeft = bytesRead - 1;
170 	_bufPos = _bufStart;
171 }
172 
173 /*------------------------------------------------------------------------*/
174 
FilesManager(VoyeurEngine * vm)175 FilesManager::FilesManager(VoyeurEngine *vm) {
176 	_curLibPtr = nullptr;
177 	_boltFilesState = new BoltFilesState(vm);
178 }
179 
~FilesManager()180 FilesManager::~FilesManager() {
181 	delete _boltFilesState;
182 }
183 
openBoltLib(const Common::String & filename,BoltFile * & boltFile)184 bool FilesManager::openBoltLib(const Common::String &filename, BoltFile *&boltFile) {
185 	if (boltFile != NULL) {
186 		_boltFilesState->_curLibPtr = boltFile;
187 		return true;
188 	}
189 
190 	// Create the bolt file interface object and load the index
191 	if (filename == "bvoy.blt")
192 		boltFile = _boltFilesState->_curLibPtr = new BVoyBoltFile(*_boltFilesState);
193 	else if (filename == "stampblt.blt")
194 		boltFile = _boltFilesState->_curLibPtr = new StampBoltFile(*_boltFilesState);
195 	else
196 		error("Unknown bolt file specified");
197 
198 	return true;
199 }
200 
fload(const Common::String & filename,int * size)201 byte *FilesManager::fload(const Common::String &filename, int *size) {
202 	Common::File f;
203 	int filesize;
204 	byte *data = NULL;
205 
206 	if (f.open(filename)) {
207 		// Read in the file
208 		filesize = f.size();
209 		data = new byte[filesize];
210 		f.read(data, filesize);
211 	} else {
212 		filesize = 0;
213 	}
214 
215 	if (size)
216 		*size = filesize;
217 	return data;
218 }
219 
220 /*------------------------------------------------------------------------*/
221 
BoltFile(const Common::String & filename,BoltFilesState & state)222 BoltFile::BoltFile(const Common::String &filename, BoltFilesState &state): _state(state) {
223 	if (!_file.open(filename))
224 		error("Could not open %s", filename.c_str());
225 
226 	// Read in the file header
227 	byte header[16];
228 	_file.read(&header[0], 16);
229 
230 	if (strncmp((const char *)&header[0], "BOLT", 4) != 0)
231 		error("Tried to load non-bolt file");
232 
233 	int totalGroups = header[11] ? header[11] : 0x100;
234 	for (int i = 0; i < totalGroups; ++i)
235 		_groups.push_back(BoltGroup(&_file));
236 }
237 
~BoltFile()238 BoltFile::~BoltFile() {
239 	_file.close();
240 	if (_state._curFd == &_file)
241 		_state._curFd = NULL;
242 	if (_state._curLibPtr == this)
243 		_state._curLibPtr = NULL;
244 }
245 
getBoltGroup(uint16 id)246 BoltGroup *BoltFile::getBoltGroup(uint16 id) {
247 	_state._curLibPtr = this;
248 	_state._curGroupPtr = &_groups[(id >> 8) & 0xff];
249 
250 	if (!_state._curGroupPtr->_loaded) {
251 		// Load the group index
252 		_state._curGroupPtr->load(id & 0xff00);
253 	}
254 
255 	// Pre-process the resources
256 	id &= 0xff00;
257 	for (int idx = 0; idx < _state._curGroupPtr->_count; ++idx, ++id) {
258 		byte *member = getBoltMember(id);
259 		assert(member);
260 	}
261 
262 	resolveAll();
263 
264 	return _state._curGroupPtr;
265 }
266 
freeBoltGroup(uint16 id)267 void BoltFile::freeBoltGroup(uint16 id) {
268 	_state._curLibPtr = this;
269 	_state._curGroupPtr = &_groups[(id >> 8) & 0xff];
270 
271 	// Unload the group
272 	_state._curGroupPtr->unload();
273 }
274 
freeBoltMember(uint32 id)275 void BoltFile::freeBoltMember(uint32 id) {
276 	// No implementation in ScummVM
277 }
278 
getBoltEntryFromLong(uint32 id)279 BoltEntry &BoltFile::getBoltEntryFromLong(uint32 id) {
280 	BoltGroup &group = _groups[id >> 24];
281 	assert(group._loaded);
282 
283 	BoltEntry &entry = group._entries[(id >> 16) & 0xff];
284 	assert(!entry.hasResource() || (id & 0xffff) == 0);
285 
286 	return entry;
287 }
288 
boltEntry(uint16 id)289 BoltEntry &BoltFile::boltEntry(uint16 id) {
290 	BoltGroup &group = _groups[id >> 8];
291 	assert(group._loaded);
292 
293 	BoltEntry &entry = group._entries[id & 0xff];
294 	assert(entry.hasResource());
295 
296 	return entry;
297 }
298 
getPictureResource(uint32 id)299 PictureResource *BoltFile::getPictureResource(uint32 id) {
300 	if ((int32)id == -1)
301 		return NULL;
302 
303 	if (id & 0xffff)
304 		id <<= 16;
305 	return getBoltEntryFromLong(id)._picResource;
306 }
307 
getCMapResource(uint32 id)308 CMapResource *BoltFile::getCMapResource(uint32 id) {
309 	if ((int32)id == -1)
310 		return NULL;
311 
312 	if (id & 0xffff)
313 		id <<= 16;
314 
315 	return getBoltEntryFromLong(id)._cMapResource;
316 }
317 
memberAddr(uint32 id)318 byte *BoltFile::memberAddr(uint32 id) {
319 	BoltGroup &group = _groups[id >> 8];
320 	if (!group._loaded)
321 		return NULL;
322 
323 	// If an entry already has a processed representation, we shouldn't
324 	// still be accessing the raw data
325 	BoltEntry &entry = group._entries[id & 0xff];
326 	assert(!entry.hasResource());
327 
328 	return entry._data;
329 }
330 
memberAddrOffset(uint32 id)331 byte *BoltFile::memberAddrOffset(uint32 id) {
332 	BoltGroup &group = _groups[id >> 24];
333 	if (!group._loaded)
334 		return NULL;
335 
336 	// If an entry already has a processed representation, we shouldn't
337 	// still be accessing the raw data
338 	BoltEntry &entry = group._entries[(id >> 16) & 0xff];
339 	assert(!entry.hasResource());
340 
341 	return entry._data + (id & 0xffff);
342 }
343 
344 /**
345  * Resolves an Id to an offset within a loaded resource
346  */
resolveIt(uint32 id,byte ** p)347 void BoltFile::resolveIt(uint32 id, byte **p) {
348 	if ((int32)id == -1) {
349 		*p = NULL;
350 	} else {
351 		byte *ptr = memberAddrOffset(id);
352 		if (ptr) {
353 			*p = ptr;
354 		} else {
355 			*p = NULL;
356 			assert(_state._resolves.size() < 1000);
357 			_state._resolves.push_back(ResolveEntry(id, p));
358 		}
359 	}
360 }
361 
resolveFunction(uint32 id,ScreenMethodPtr * fn)362 void BoltFile::resolveFunction(uint32 id, ScreenMethodPtr *fn) {
363 	if ((int32)id == -1)
364 		*fn = NULL;
365 	else
366 		error("Function fnTermGro array not supported");
367 }
368 
369 /**
370  * Resolve any data references to within resources that weren't
371  * previously loaded, but are now
372  */
resolveAll()373 void BoltFile::resolveAll() {
374 	for (uint idx = 0; idx < _state._resolves.size(); ++idx)
375 		*_state._resolves[idx]._p = memberAddrOffset(_state._resolves[idx]._id);
376 
377 	_state._resolves.clear();
378 }
379 
getBoltMember(uint32 id)380 byte *BoltFile::getBoltMember(uint32 id) {
381 	_state._curLibPtr = this;
382 
383 	// Get the group, and load it's entry list if not already loaded
384 	_state._curGroupPtr = &_groups[(id >> 8) & 0xff];
385 	if (!_state._curGroupPtr->_loaded)
386 		_state._curGroupPtr->load(id & 0xff00);
387 
388 	// Get the entry
389 	_state._curMemberPtr = &_state._curGroupPtr->_entries[id & 0xff];
390 
391 	// Return the data for the entry if it's already been loaded
392 	if (_state._curMemberPtr->_data)
393 		return _state._curMemberPtr->_data;
394 
395 	if (_state._curGroupPtr->_processed) {
396 		error("Processed resources are not supported");
397 	} else {
398 		_state._bufStart = _state._decompressBuf;
399 		_state._bufSize = DECOMPRESS_SIZE;
400 
401 		if ((_state._curFd != &_file) || (_state._curMemberPtr->_fileOffset < _state._bufferBegin)
402 				|| (_state._curMemberPtr->_fileOffset >= _state._bufferEnd)) {
403 			_state._bytesLeft = 0;
404 			_state._bufPos = _state._bufStart;
405 			_state._bufferBegin = -1;
406 			_state._bufferEnd = _state._curMemberPtr->_fileOffset;
407 		} else {
408 			_state._bufPos = _state._curMemberPtr->_fileOffset - _state._bufferBegin + _state._bufStart;
409 			_state._bytesLeft = _state._bufSize - (_state._bufPos - _state._bufStart);
410 		}
411 	}
412 
413 	_state._decompState = false;
414 	_state._historyIndex = 0;
415 
416 	// Initialize the resource
417 	assert(_state._curMemberPtr->_initMethod < 25);
418 	initResource(_state._curMemberPtr->_initMethod);
419 
420 	return _state._curMemberPtr->_data;
421 }
422 
initDefault()423 void BoltFile::initDefault() {
424 	_state._curMemberPtr->_data = _state.decompress(NULL, _state._curMemberPtr->_size,
425 		_state._curMemberPtr->_mode);
426 }
427 
428 /*------------------------------------------------------------------------*/
429 
BVoyBoltFile(BoltFilesState & state)430 BVoyBoltFile::BVoyBoltFile(BoltFilesState &state): BoltFile("bvoy.blt", state) {
431 }
432 
initResource(int resType)433 void BVoyBoltFile::initResource(int resType) {
434 	switch (resType) {
435 	case 2:
436 		// Also used for point list, and ending credits credit data
437 		sInitRect();
438 		break;
439 	case 8:
440 		sInitPic();
441 		break;
442 	case 10:
443 		vInitCMap();
444 		break;
445 	case 11:
446 		vInitCycl();
447 		break;
448 	case 15:
449 		initViewPort();
450 		break;
451 	case 16:
452 		initViewPortList();
453 		break;
454 	case 17:
455 		initFont();
456 		break;
457 	case 18:
458 		initFontInfo();
459 		break;
460 	case 19:
461 		initSoundMap();
462 		break;
463 	default:
464 		initDefault();
465 		break;
466 	}
467 }
468 
initViewPort()469 void BVoyBoltFile::initViewPort() {
470 	initDefault();
471 
472 	ViewPortResource *viewPort;
473 	byte *src = _state._curMemberPtr->_data;
474 	_state._curMemberPtr->_viewPortResource = viewPort = new ViewPortResource(_state, src);
475 
476 	// This is done post-constructor, since viewports can be self referential, so
477 	// we need the _viewPortResource field to have been set before resolving the pointer
478 	viewPort->_parent = getBoltEntryFromLong(READ_LE_UINT32(src + 2))._viewPortResource;
479 }
480 
initViewPortList()481 void BVoyBoltFile::initViewPortList() {
482 	initDefault();
483 
484 	ViewPortListResource *res;
485 	_state._curMemberPtr->_viewPortListResource = res = new ViewPortListResource(
486 		_state, _state._curMemberPtr->_data);
487 
488 	_state._vm->_screen->_viewPortListPtr = res;
489 	_state._vm->_screen->_vPort = res->_entries[0];
490 }
491 
initFontInfo()492 void BVoyBoltFile::initFontInfo() {
493 	initDefault();
494 	_state._curMemberPtr->_fontInfoResource = new FontInfoResource(
495 		_state, _state._curMemberPtr->_data);
496 }
497 
initFont()498 void BVoyBoltFile::initFont() {
499 	initDefault();
500 	_state._curMemberPtr->_fontResource = new FontResource(_state, _state._curMemberPtr->_data);
501 }
502 
initSoundMap()503 void BVoyBoltFile::initSoundMap() {
504 	initDefault();
505 }
506 
sInitRect()507 void BVoyBoltFile::sInitRect() {
508 	_state._curMemberPtr->_data = _state.decompress(NULL, _state._curMemberPtr->_size,
509 		_state._curMemberPtr->_mode);
510 
511 	// Check whether the resource Id is in the list of extended rects
512 	bool isExtendedRects = false;
513 	for (int i = 0; i < 49 && !isExtendedRects; ++i)
514 		isExtendedRects = RESOLVE_TABLE[i] == (_state._curMemberPtr->_id & 0xff00);
515 
516 	int rectSize = isExtendedRects ? 12 : 8;
517 	if ((_state._curMemberPtr->_size % rectSize) == 0 || (_state._curMemberPtr->_size % rectSize) == 2)
518 		_state._curMemberPtr->_rectResource = new RectResource(_state._curMemberPtr->_data,
519 			_state._curMemberPtr->_size, isExtendedRects);
520 }
521 
sInitPic()522 void BVoyBoltFile::sInitPic() {
523 	// Read in the header data
524 	_state._curMemberPtr->_data = _state.decompress(NULL, 24, _state._curMemberPtr->_mode);
525 	_state._curMemberPtr->_picResource = new PictureResource(_state,
526 		_state._curMemberPtr->_data);
527 }
528 
vInitCMap()529 void BVoyBoltFile::vInitCMap() {
530 	initDefault();
531 	_state._curMemberPtr->_cMapResource = new CMapResource(
532 		_state, _state._curMemberPtr->_data);
533 }
534 
vInitCycl()535 void BVoyBoltFile::vInitCycl() {
536 	initDefault();
537 	_state._curMemberPtr->_vInitCycleResource = new VInitCycleResource(
538 		_state, _state._curMemberPtr->_data);
539 	_state._curMemberPtr->_vInitCycleResource->vStopCycle();
540 }
541 
542 /*------------------------------------------------------------------------*/
543 
StampBoltFile(BoltFilesState & state)544 StampBoltFile::StampBoltFile(BoltFilesState &state): BoltFile("stampblt.blt", state) {
545 }
546 
initResource(int resType)547 void StampBoltFile::initResource(int resType) {
548 	switch (resType) {
549 	case 0:
550 		initThread();
551 		break;
552 	case 4:
553 		initState();
554 		break;
555 	case 6:
556 		initPtr();
557 		break;
558 	case 24:
559 		initControl();
560 		break;
561 	default:
562 		initDefault();
563 		break;
564 	}
565 }
566 
initThread()567 void StampBoltFile::initThread() {
568 	initDefault();
569 
570 	_state._curMemberPtr->_threadResource = new ThreadResource(_state,
571 		_state._curMemberPtr->_data);
572 }
573 
initPtr()574 void StampBoltFile::initPtr() {
575 	initDefault();
576 
577 	_state._curMemberPtr->_ptrResource = new PtrResource(_state,
578 		_state._curMemberPtr->_data);
579 }
580 
581 	void initControlData();
582 
583 
initControl()584 void StampBoltFile::initControl() {
585 	initDefault();
586 
587 	ControlResource *res;
588 	_state._curMemberPtr->_controlResource = res = new ControlResource(_state,
589 		_state._curMemberPtr->_data);
590 
591 	_state._vm->_controlGroupPtr = _state._curGroupPtr;
592 	_state._vm->_controlPtr = res;
593 }
594 
initState()595 void StampBoltFile::initState() {
596 	initDefault();
597 
598 	assert(_state._curMemberPtr->_size == 16);
599 	_state._curMemberPtr->_stateResource = new StateResource(_state,
600 		_state._curMemberPtr->_data);
601 }
602 
603 /*------------------------------------------------------------------------*/
604 
BoltGroup(Common::SeekableReadStream * f)605 BoltGroup::BoltGroup(Common::SeekableReadStream *f): _file(f) {
606 	byte buffer[BOLT_GROUP_SIZE];
607 
608 	_loaded = false;
609 
610 	_file->read(&buffer[0], BOLT_GROUP_SIZE);
611 	_processed = buffer[0] != 0;
612 	_count = buffer[3] ? buffer[3] : 256;
613 	_fileOffset = READ_LE_UINT32(&buffer[8]);
614 }
615 
~BoltGroup()616 BoltGroup::~BoltGroup() {
617 }
618 
load(uint16 groupId)619 void BoltGroup::load(uint16 groupId) {
620 	_file->seek(_fileOffset);
621 
622 	// Read the entries
623 	for (int i = 0; i < _count; ++i)
624 		_entries.push_back(BoltEntry(_file, groupId + i));
625 
626 	_loaded = true;
627 }
628 
unload()629 void BoltGroup::unload() {
630 	if (!_loaded)
631 		return;
632 
633 	_entries.clear();
634 	_loaded = false;
635 }
636 
637 /*------------------------------------------------------------------------*/
638 
BoltEntry(Common::SeekableReadStream * f,uint16 id)639 BoltEntry::BoltEntry(Common::SeekableReadStream *f, uint16 id): _file(f), _id(id) {
640 	_data = nullptr;
641 	_rectResource = nullptr;
642 	_picResource = nullptr;
643 	_viewPortResource = nullptr;
644 	_viewPortListResource = nullptr;
645 	_fontResource = nullptr;
646 	_fontInfoResource = nullptr;
647 	_cMapResource = nullptr;
648 	_vInitCycleResource = nullptr;
649 
650 	_ptrResource = nullptr;
651 	_stateResource = nullptr;
652 	_controlResource = nullptr;
653 	_vInitCycleResource = nullptr;
654 	_threadResource = nullptr;
655 
656 	byte buffer[16];
657 	_file->read(&buffer[0], 16);
658 	_mode = buffer[0];
659 	_initMethod = buffer[3];
660 	_size = READ_LE_UINT32(&buffer[4]) & 0xffffff;
661 	_fileOffset = READ_LE_UINT32(&buffer[8]);
662 }
663 
~BoltEntry()664 BoltEntry::~BoltEntry() {
665 	delete[] _data;
666 	delete _rectResource;
667 	delete _picResource;
668 	delete _viewPortResource;
669 	delete _viewPortListResource;
670 	delete _fontResource;
671 	delete _fontInfoResource;
672 	delete _cMapResource;
673 
674 	delete _ptrResource;
675 	delete _controlResource;
676 	delete _stateResource;
677 	delete _vInitCycleResource;
678 	delete _threadResource;
679 }
680 
load()681 void BoltEntry::load() {
682 	// Currently, all entry loading and decompression is done in BoltFile::memberAddr.
683 }
684 
685 /**
686  * Returns true if the given bolt entry has an attached resource
687  */
hasResource() const688 bool BoltEntry::hasResource() const {
689 	return _rectResource ||  _picResource || _viewPortResource || _viewPortListResource
690 		|| _fontResource || _fontInfoResource || _cMapResource || _vInitCycleResource
691 		|| _ptrResource || _controlResource || _stateResource || _threadResource;
692 }
693 
694 /*------------------------------------------------------------------------*/
695 
RectEntry(int x1,int y1,int x2,int y2,int arrIndex,int count)696 RectEntry::RectEntry(int x1, int y1, int x2, int y2, int arrIndex, int count):
697 		Common::Rect(x1, y1, x2, y2), _arrIndex(arrIndex), _count(count) {
698 }
699 
700 /*------------------------------------------------------------------------*/
701 
RectResource(const byte * src,int size,bool isExtendedRects)702 RectResource::RectResource(const byte *src, int size, bool isExtendedRects) {
703 	int count;
704 	int rectSize = isExtendedRects ? 12 : 8;
705 	if ((size % rectSize) == 2) {
706 		count = READ_LE_UINT16(src);
707 		src += 2;
708 	} else {
709 		count = size / rectSize;
710 	}
711 
712 	for (int i = 0; i < count; ++i, src += 8) {
713 		int arrIndex = 0, rectCount = 0;
714 		if (isExtendedRects) {
715 			arrIndex = READ_LE_UINT16(src);
716 			rectCount = READ_LE_UINT16(src + 2);
717 			src += 4;
718 		}
719 
720 		int x1 = READ_LE_UINT16(src);
721 		int y1 = READ_LE_UINT16(src + 2);
722 		int x2 = READ_LE_UINT16(src + 4);
723 		int y2 = READ_LE_UINT16(src + 6);
724 
725 		_entries.push_back(RectEntry(x1, y1, x2, y2, arrIndex, rectCount));
726 	}
727 
728 	left = _entries[0].left;
729 	top = _entries[0].top;
730 	right = _entries[0].right;
731 	bottom = _entries[0].bottom;
732 }
733 
RectResource(int x1,int y1,int x2,int y2)734 RectResource::RectResource(int x1, int y1, int x2, int y2) {
735 	left = x1;
736 	top = y1;
737 	right = x2;
738 	bottom = y2;
739 }
740 
741 /*------------------------------------------------------------------------*/
742 
DisplayResource()743 DisplayResource::DisplayResource() {
744 	_vm = NULL;
745 	_flags = DISPFLAG_NONE;
746 }
747 
DisplayResource(VoyeurEngine * vm)748 DisplayResource::DisplayResource(VoyeurEngine *vm) {
749 	_vm = vm;
750 	_flags = DISPFLAG_NONE;
751 }
752 
sFillBox(int width,int height)753 void DisplayResource::sFillBox(int width, int height) {
754 	assert(_vm);
755 	bool saveBack = _vm->_screen->_saveBack;
756 	_vm->_screen->_saveBack = false;
757 
758 	PictureResource pr;
759 	pr._flags = DISPFLAG_1;
760 	pr._select = 0xff;
761 	pr._pick = 0;
762 	pr._onOff = _vm->_screen->_drawPtr->_penColor;
763 	pr._bounds = Common::Rect(0, 0, width, height);
764 
765 	_vm->_screen->sDrawPic(&pr, this, _vm->_screen->_drawPtr->_pos);
766 	_vm->_screen->_saveBack = saveBack;
767 }
768 
clipRect(Common::Rect & rect)769 bool DisplayResource::clipRect(Common::Rect &rect) {
770 	Common::Rect clippingRect;
771 	if (_vm->_screen->_clipPtr) {
772 		clippingRect = *_vm->_screen->_clipPtr;
773 	} else if (_flags & DISPFLAG_VIEWPORT) {
774 		clippingRect = ((ViewPortResource *)this)->_clipRect;
775 	} else {
776 		clippingRect = ((PictureResource *)this)->_bounds;
777 	}
778 
779 	Common::Rect r = rect;
780 	if (r.left < clippingRect.left) {
781 		if (r.right <= clippingRect.left)
782 			return false;
783 		r.setWidth(r.right - clippingRect.left);
784 	}
785 	if (r.right >= clippingRect.right) {
786 		if (r.left >= clippingRect.left)
787 			return false;
788 		r.setWidth(clippingRect.right - r.left);
789 	}
790 
791 	if (r.top < clippingRect.top) {
792 		if (r.bottom <= clippingRect.top)
793 			return false;
794 		r.setHeight(r.bottom - clippingRect.top);
795 	}
796 	if (r.bottom >= clippingRect.bottom) {
797 		if (r.top >= clippingRect.top)
798 			return false;
799 		r.setWidth(clippingRect.bottom - r.top);
800 	}
801 
802 	rect = r;
803 	return true;
804 }
805 
drawText(const Common::String & msg)806 int DisplayResource::drawText(const Common::String &msg) {
807 	Screen &screen = *_vm->_screen;
808 	assert(screen._fontPtr);
809 	assert(screen._fontPtr->_curFont);
810 	FontInfoResource &fontInfo = *screen._fontPtr;
811 	PictureResource &fontChar = *_vm->_screen->_fontChar;
812 	FontResource &fontData = *fontInfo._curFont;
813 	int xShadows[9] = { 0, 1, 1, 1, 0, -1, -1, -1, 0 };
814 	int yShadows[9] = { 0, 1, 0, -1, -1, -1, 0, 1, 1 };
815 
816 	Common::Rect *clipPtr = screen._clipPtr;
817 	if (!(fontInfo._picFlags & DISPFLAG_1))
818 		screen._clipPtr = NULL;
819 
820 	int minChar = fontData._minChar;
821 	int padding = fontData._padding;
822 	int fontHeight = fontData._fontHeight;
823 	int totalChars = fontData._maxChar - fontData._minChar;
824 	int msgWidth = 0;
825 	int xp = 0, yp = 0;
826 
827 	Common::Point pos = Common::Point(fontInfo._pos.x, fontInfo._pos.y + fontData._topPadding);
828 
829 	fontChar._flags = fontInfo._picFlags | DISPFLAG_2;
830 	fontChar._select = fontInfo._picSelect;
831 	fontChar._bounds.setHeight(fontHeight);
832 
833 	ViewPortResource *viewPort = !(_flags & DISPFLAG_VIEWPORT) ? NULL :
834 		(ViewPortResource *)this;
835 
836 	if ((fontInfo._fontFlags & DISPFLAG_1) || fontInfo._justify ||
837 			(screen._saveBack && fontInfo._fontSaveBack && (_flags & DISPFLAG_VIEWPORT))) {
838 		msgWidth = viewPort->textWidth(msg);
839 		yp = pos.y;
840 		xp = pos.x;
841 
842 		switch (fontInfo._justify) {
843 		case 1:
844 			xp = pos.x + (fontInfo._justifyWidth / 2) - (msgWidth / 2);
845 			break;
846 		case 2:
847 			xp = pos.x + fontInfo._justifyWidth - msgWidth;
848 			break;
849 		default:
850 			break;
851 		}
852 
853 		if (!(fontInfo._fontFlags & (DISPFLAG_1 | DISPFLAG_2))) {
854 			viewPort->_fontRect.left = xp;
855 			viewPort->_fontRect.top = yp;
856 			viewPort->_fontRect.setWidth(msgWidth);
857 			viewPort->_fontRect.setHeight(fontHeight);
858 		} else {
859 			viewPort->_fontRect.left = pos.x;
860 			viewPort->_fontRect.top = pos.y;
861 			viewPort->_fontRect.setWidth(fontInfo._justifyWidth);
862 			viewPort->_fontRect.setHeight(fontInfo._justifyHeight);
863 		}
864 
865 		pos.x = xp;
866 		pos.y = yp;
867 
868 		if (fontInfo._fontFlags & DISPFLAG_4) {
869 			if (fontInfo._shadow.x <= 0) {
870 				viewPort->_fontRect.left += fontInfo._shadow.x;
871 				viewPort->_fontRect.right -= fontInfo._shadow.x * 2;
872 			} else {
873 				viewPort->_fontRect.right += fontInfo._shadow.x;
874 			}
875 
876 			if (fontInfo._shadow.y <= 0) {
877 				viewPort->_fontRect.top += fontInfo._shadow.y;
878 				viewPort->_fontRect.bottom -= fontInfo._shadow.y * 2;
879 			} else {
880 				viewPort->_fontRect.bottom += fontInfo._shadow.y;
881 			}
882 		} else if (fontInfo._fontFlags & 8) {
883 			if (fontInfo._shadow.x <= 0) {
884 				viewPort->_fontRect.left += fontInfo._shadow.x;
885 				viewPort->_fontRect.right -= fontInfo._shadow.x * 3;
886 			} else {
887 				viewPort->_fontRect.right += fontInfo._shadow.x * 3;
888 				viewPort->_fontRect.left -= fontInfo._shadow.x;
889 			}
890 
891 			if (fontInfo._shadow.y <= 0) {
892 				viewPort->_fontRect.top += fontInfo._shadow.y;
893 				viewPort->_fontRect.bottom -= fontInfo._shadow.y * 3;
894 			} else {
895 				viewPort->_fontRect.bottom += fontInfo._shadow.y * 3;
896 				viewPort->_fontRect.top -= fontInfo._shadow.y;
897 			}
898 		}
899 	}
900 
901 	if (screen._saveBack && fontInfo._fontSaveBack && (_flags & DISPFLAG_VIEWPORT)) {
902 		viewPort->addSaveRect(viewPort->_pageIndex, viewPort->_fontRect);
903 	}
904 
905 	if (fontInfo._fontFlags & DISPFLAG_1) {
906 		screen._drawPtr->_pos = Common::Point(viewPort->_fontRect.left, viewPort->_fontRect.top);
907 		screen._drawPtr->_penColor = fontInfo._backColor;
908 		sFillBox(viewPort->_fontRect.width(), viewPort->_fontRect.height());
909 	}
910 
911 	bool saveBack = screen._saveBack;
912 	screen._saveBack = false;
913 
914 	int count = 0;
915 	if (fontInfo._fontFlags & DISPFLAG_4)
916 		count = 1;
917 	else if (fontInfo._fontFlags & DISPFLAG_8)
918 		count = 8;
919 
920 	for (int i = count; i >= 0; --i) {
921 		xp = pos.x;
922 		yp = pos.y;
923 
924 		switch (xShadows[i]) {
925 		case -1:
926 			xp -= fontInfo._shadow.x;
927 			break;
928 		case 1:
929 			xp += fontInfo._shadow.x;
930 			break;
931 		default:
932 			break;
933 		}
934 
935 		switch (yShadows[i]) {
936 		case -1:
937 			yp -= fontInfo._shadow.y;
938 			break;
939 		case 1:
940 			yp += fontInfo._shadow.y;
941 			break;
942 		default:
943 			break;
944 		}
945 
946 		if (i != 0) {
947 			fontChar._pick = 0;
948 			fontChar._onOff = fontInfo._shadowColor;
949 		} else if (fontData._fontDepth == 1 || (fontInfo._fontFlags & DISPFLAG_10)) {
950 			fontChar._pick = 0;
951 			fontChar._onOff = fontInfo._foreColor;
952 		} else {
953 			fontChar._pick = fontInfo._picPick;
954 			fontChar._onOff = fontInfo._picOnOff;
955 		}
956 
957 		// Loop to draw each character in turn
958 		msgWidth = -padding;
959 		const char *msgP = msg.c_str();
960 		char ch;
961 
962 		while ((ch = *msgP++) != '\0') {
963 			int charValue = (int)ch - minChar;
964 			if (charValue < 0 || charValue >= totalChars || fontData._charWidth[charValue] == 0)
965 				charValue = fontData._maxChar - minChar;
966 
967 			int charWidth = fontData._charWidth[charValue];
968 			fontChar._bounds.setWidth(charWidth);
969 
970 			uint16 offset = READ_LE_UINT16(fontData._charOffsets + charValue * 2);
971 			fontChar._imgData = fontData._charImages + offset * 2;
972 
973 			screen.sDrawPic(&fontChar, this, Common::Point(xp, yp));
974 
975 			fontChar._imgData = NULL;
976 			xp += charWidth + padding;
977 			msgWidth += charWidth + padding;
978 		}
979 	}
980 
981 	msgWidth = MAX(msgWidth, 0);
982 	if (fontInfo._justify == ALIGN_LEFT)
983 		fontInfo._pos.x = xp;
984 
985 	screen._saveBack = saveBack;
986 	screen._clipPtr = clipPtr;
987 
988 	return msgWidth;
989 }
990 
textWidth(const Common::String & msg)991 int DisplayResource::textWidth(const Common::String &msg) {
992 	if (msg.size() == 0)
993 		return 0;
994 
995 	const char *msgP = msg.c_str();
996 	FontResource &fontData = *_vm->_screen->_fontPtr->_curFont;
997 	int minChar = fontData._minChar;
998 	int maxChar = fontData._maxChar;
999 	int padding = fontData._padding;
1000 	int totalWidth = -padding;
1001 	char ch;
1002 
1003 	// Loop through the characters
1004 	while ((ch = *msgP++) != '\0') {
1005 		int charValue = (int)ch;
1006 		if (charValue < minChar || charValue > maxChar)
1007 			charValue = maxChar;
1008 
1009 		if (!fontData._charWidth[charValue - minChar])
1010 			charValue = maxChar;
1011 
1012 		totalWidth += fontData._charWidth[charValue - minChar] + padding;
1013 	}
1014 
1015 	if (totalWidth < 0)
1016 		totalWidth = 0;
1017 	return totalWidth;
1018 }
1019 
1020 /*------------------------------------------------------------------------*/
1021 
PictureResource(BoltFilesState & state,const byte * src)1022 PictureResource::PictureResource(BoltFilesState &state, const byte *src):
1023 		DisplayResource(state._vm) {
1024 	_flags = READ_LE_UINT16(src);
1025 	_select = src[2];
1026 	_pick = src[3];
1027 	_onOff = src[4];
1028 	// depth is in src[5], unused.
1029 
1030 	int xs = READ_LE_UINT16(&src[6]);
1031 	int ys = READ_LE_UINT16(&src[8]);
1032 	_bounds = Common::Rect(xs, ys, xs + READ_LE_UINT16(&src[10]),
1033 		ys + READ_LE_UINT16(&src[12]));
1034 	_maskData = READ_LE_UINT32(&src[14]);
1035 	_planeSize = READ_LE_UINT16(&src[22]);
1036 
1037 	_keyColor = 0;
1038 	_imgData = NULL;
1039 	_freeImgData = DisposeAfterUse::YES;
1040 
1041 	int nbytes = _bounds.width() * _bounds.height();
1042 	if (_flags & PICFLAG_20) {
1043 		if (_flags & (PICFLAG_VFLIP | PICFLAG_HFLIP)) {
1044 			// Get the raw data for the picture from another resource
1045 			uint32 id = READ_LE_UINT32(&src[18]);
1046 			const byte *srcData = state._curLibPtr->boltEntry(id)._data;
1047 			_imgData = new byte[nbytes];
1048 
1049 			// Flip the image data either horizontally or vertically
1050 			if (_flags & PICFLAG_HFLIP)
1051 				flipHorizontal(srcData);
1052 			else
1053 				flipVertical(srcData);
1054 		} else {
1055 			uint32 id = READ_LE_UINT32(&src[18]) >> 16;
1056 			byte *imgData = state._curLibPtr->boltEntry(id)._picResource->_imgData;
1057 			_freeImgData = DisposeAfterUse::NO;
1058 
1059 #if 0
1060 			// TODO: Double check code below. Despite different coding in the
1061 			// original, both looked like they do the same formula.
1062 			// Until it's clarified, this check is disabled and replaced by the
1063 			// common code.
1064 			if (_flags & PICFLAG_PIC_OFFSET) {
1065 				_imgData = imgData + (READ_LE_UINT32(&src[18]) & 0xffff);
1066 			} else {
1067 				_imgData = imgData + (READ_LE_UINT32(&src[18]) & 0xffff);
1068 			}
1069 #endif
1070 			_imgData = imgData + (READ_LE_UINT32(&src[18]) & 0xffff);
1071 		}
1072 	} else if (_flags & PICFLAG_PIC_OFFSET) {
1073 		int mode = 0;
1074 		if (_bounds.width() == 320)
1075 			mode = 147;
1076 		else {
1077 			if (_bounds.width() == 640) {
1078 				if (_bounds.height() == 400)
1079 					mode = 220;
1080 				else
1081 					mode = 221;
1082 			} else if (_bounds.width() == 800)
1083 				mode = 222;
1084 			else if (_bounds.width() == 1024)
1085 				mode = 226;
1086 		}
1087 
1088 		if (mode != state._vm->_screen->_SVGAMode) {
1089 			state._vm->_screen->_SVGAMode = mode;
1090 			state._vm->_screen->clearPalette();
1091 		}
1092 
1093 		int screenOffset = READ_LE_UINT32(&src[18]) & 0xffff;
1094 		assert(screenOffset == 0);
1095 
1096 		if (_flags & PICFLAG_CLEAR_SCREEN) {
1097 			// Clear screen picture. That's right. This game actually has a picture
1098 			// resource flag to clear the screen! Bizarre.
1099 			state._vm->_screen->clear();
1100 		} else {
1101 			// Direct screen loading picture. In this case, the raw data of the resource
1102 			// is directly decompressed into the screen surface. Again, bizarre.
1103 			Screen &screen = *state._vm->_screen;
1104 			byte *pDest = (byte *)screen.getPixels();
1105 			state.decompress(pDest, SCREEN_WIDTH * SCREEN_HEIGHT, state._curMemberPtr->_mode);
1106 			screen.markAllDirty();
1107 		}
1108 	} else {
1109 		if (_flags & PICFLAG_CLEAR_SCREEN00) {
1110 			if (!(_flags & PICFLAG_CLEAR_SCREEN))
1111 				nbytes = state._curMemberPtr->_size - 24;
1112 
1113 			int mask = (nbytes + 0x3FFF) >> 14;
1114 			_imgData = NULL;
1115 
1116 			if (state._boltPageFrame != NULL) {
1117 				_maskData = mask;
1118 				state.decompress(state._boltPageFrame, nbytes, state._curMemberPtr->_mode);
1119 				return;
1120 			}
1121 		}
1122 
1123 		if (_flags & PICFLAG_CLEAR_SCREEN) {
1124 			_imgData = new byte[nbytes];
1125 			Common::fill(_imgData, _imgData + nbytes, 0);
1126 		} else {
1127 			_imgData = state.decompress(NULL, nbytes, state._curMemberPtr->_mode);
1128 		}
1129 	}
1130 }
1131 
PictureResource(Graphics::Surface * surface)1132 PictureResource::PictureResource(Graphics::Surface *surface) {
1133 	_flags = DISPFLAG_NONE;
1134 	_select = 0;
1135 	_pick = 0;
1136 	_onOff = 0;
1137 	_maskData = 0;
1138 	_planeSize = 0;
1139 	_keyColor = 0;
1140 
1141 	_bounds = Common::Rect(0, 0, surface->w, surface->h);
1142 	_imgData = (byte *)surface->getPixels();
1143 	_freeImgData = DisposeAfterUse::NO;
1144 }
1145 
PictureResource()1146 PictureResource::PictureResource() {
1147 	_flags = DISPFLAG_NONE;
1148 	_select = 0;
1149 	_pick = 0;
1150 	_onOff = 0;
1151 	_maskData = 0;
1152 	_planeSize = 0;
1153 	_keyColor = 0;
1154 
1155 	_imgData = NULL;
1156 	_freeImgData = DisposeAfterUse::NO;
1157 }
1158 
PictureResource(int flags,int select,int pick,int onOff,const Common::Rect & bounds,int maskData,byte * imgData,int planeSize)1159 PictureResource::PictureResource(int flags, int select, int pick, int onOff,
1160 		const Common::Rect &bounds, int maskData, byte *imgData, int planeSize) {
1161 	_flags = flags;
1162 	_select = select;
1163 	_pick = pick;
1164 	_onOff = onOff;
1165 	_bounds = bounds;
1166 	_maskData = maskData;
1167 	_imgData = imgData;
1168 	_planeSize = planeSize;
1169 	_freeImgData = DisposeAfterUse::NO;
1170 	_keyColor = 0;
1171 }
1172 
~PictureResource()1173 PictureResource::~PictureResource() {
1174 	if (_freeImgData == DisposeAfterUse::YES)
1175 		delete[] _imgData;
1176 }
1177 
flipHorizontal(const byte * data)1178 void PictureResource::flipHorizontal(const byte *data) {
1179 	const byte *srcP = data + 18;
1180 	byte *destP = _imgData + _bounds.width() - 1;
1181 
1182 	for (int y = 0; y < _bounds.height(); ++y) {
1183 		for (int x = 0; x < _bounds.width(); ++x, ++srcP, --destP)
1184 			*destP = *srcP;
1185 
1186 		srcP += _bounds.width();
1187 		destP += _bounds.width();
1188 	}
1189 }
1190 
flipVertical(const byte * data)1191 void PictureResource::flipVertical(const byte *data) {
1192 	const byte *srcP = data + 18;
1193 	byte *destP = _imgData + _bounds.width() * (_bounds.height() - 1);
1194 
1195 	for (int y = 0; y < _bounds.height(); ++y) {
1196 		Common::copy(srcP, srcP + _bounds.width(), destP);
1197 		srcP += _bounds.width();
1198 		destP -= _bounds.width();
1199 	}
1200 }
1201 
1202 /*------------------------------------------------------------------------*/
1203 
ViewPortResource(BoltFilesState & state,const byte * src)1204 ViewPortResource::ViewPortResource(BoltFilesState &state, const byte *src):
1205 		_state(state), DisplayResource(state._vm) {
1206 	_flags = READ_LE_UINT16(src);
1207 	_parent = NULL;
1208 	_pageCount = READ_LE_UINT16(src + 6);
1209 	_pageIndex = READ_LE_UINT16(src + 8);
1210 	_lastPage = READ_LE_UINT16(src + 10);
1211 
1212 	int xs = READ_LE_UINT16(src + 12);
1213 	int ys = READ_LE_UINT16(src + 14);
1214 	_bounds = Common::Rect(xs, ys, xs + READ_LE_UINT16(src + 16),
1215 		ys + READ_LE_UINT16(src + 18));
1216 
1217 	_currentPic = state._curLibPtr->getPictureResource(READ_LE_UINT32(src + 0x20));
1218 	_activePage = state._curLibPtr->getPictureResource(READ_LE_UINT32(src + 0x24));
1219 	_pages[0] = state._curLibPtr->getPictureResource(READ_LE_UINT32(src + 0x28));
1220 	_pages[1] = state._curLibPtr->getPictureResource(READ_LE_UINT32(src + 0x2C));
1221 
1222 	byte *dummy;
1223 	state._curLibPtr->resolveIt(READ_LE_UINT32(src + 0x30), &dummy);
1224 
1225 	// Get the rect list
1226 	for (int listIndex = 0; listIndex < 3; ++listIndex) {
1227 		_rectListCount[listIndex] = (int16)READ_LE_UINT16(src + 0x40 + 2 * listIndex);
1228 		int id = (int)READ_LE_UINT32(src + 0x34 + listIndex * 4);
1229 
1230 		if (id == -1) {
1231 			_rectListPtr[listIndex] = NULL;
1232 		} else {
1233 			_rectListPtr[listIndex] = new Common::Array<Common::Rect>();
1234 
1235 			if (_rectListCount[listIndex] > 0) {
1236 				int16 *rectList = (int16 *)state._curLibPtr->memberAddrOffset(id);
1237 				for (int i = 0; i < _rectListCount[listIndex]; ++i) {
1238 					xs = FROM_LE_16(rectList[0]);
1239 					ys = FROM_LE_16(rectList[1]);
1240 					_rectListPtr[i]->push_back(Common::Rect(xs, ys, xs + FROM_LE_16(rectList[2]),
1241 						ys + FROM_LE_16(rectList[3])));
1242 				}
1243 			}
1244 		}
1245 	}
1246 
1247 	xs = READ_LE_UINT16(src + 0x46);
1248 	ys = READ_LE_UINT16(src + 0x48);
1249 	_clipRect = Common::Rect(xs, ys, xs + READ_LE_UINT16(src + 0x4A),
1250 		ys + READ_LE_UINT16(src + 0x4C));
1251 
1252 	state._curLibPtr->resolveIt(READ_LE_UINT32(src + 0x7A), &dummy);
1253 	state._curLibPtr->resolveFunction(READ_LE_UINT32(src + 0x7E), (ScreenMethodPtr *)&_fn1);
1254 	state._curLibPtr->resolveFunction(READ_LE_UINT32(src + 0x82), (ScreenMethodPtr *)&_setupFn);
1255 	state._curLibPtr->resolveFunction(READ_LE_UINT32(src + 0x86), (ScreenMethodPtr *)&_addFn);
1256 	state._curLibPtr->resolveFunction(READ_LE_UINT32(src + 0x8A), (ScreenMethodPtr *)&_restoreFn);
1257 
1258 	if (!_restoreFn && _addFn)
1259 		_addFn = &Screen::addRectNoSaveBack;
1260 }
1261 
~ViewPortResource()1262 ViewPortResource::~ViewPortResource() {
1263 	for (int i = 0; i < 3; ++i)
1264 		delete _rectListPtr[i];
1265 }
1266 
setupViewPort(PictureResource * page,Common::Rect * clippingRect,ViewPortSetupPtr setupFn,ViewPortAddPtr addFn,ViewPortRestorePtr restoreFn)1267 void ViewPortResource::setupViewPort(PictureResource *page, Common::Rect *clippingRect,
1268 		ViewPortSetupPtr setupFn, ViewPortAddPtr addFn, ViewPortRestorePtr restoreFn) {
1269 	PictureResource *pic = _currentPic;
1270 	Common::Rect r = _bounds;
1271 	r.translate(pic->_bounds.left, pic->_bounds.top);
1272 	int xDiff, yDiff;
1273 
1274 	if (page) {
1275 		// Clip based on the passed picture resource
1276 		xDiff = page->_bounds.left - r.left;
1277 		yDiff = page->_bounds.top - r.top;
1278 
1279 		if (xDiff > 0) {
1280 			int width = r.width();
1281 			r.left = page->_bounds.left;
1282 			r.setWidth(xDiff <= width ? width - xDiff : 0);
1283 		}
1284 		if (yDiff > 0) {
1285 			int height = r.height();
1286 			r.top = page->_bounds.top;
1287 			r.setHeight(yDiff <= height ? height - yDiff : 0);
1288 		}
1289 
1290 		xDiff = r.right - page->_bounds.right;
1291 		yDiff = r.bottom - page->_bounds.bottom;
1292 
1293 		if (xDiff > 0)
1294 			r.setWidth(xDiff <= r.width() ? r.width() - xDiff : 0);
1295 		if (yDiff > 0)
1296 			r.setHeight(yDiff <= r.height() ? r.height() - yDiff : 0);
1297 	}
1298 
1299 	if (clippingRect) {
1300 		// Clip based on the passed clip rectangles
1301 		xDiff = clippingRect->left - r.left;
1302 		yDiff = clippingRect->top - r.top;
1303 
1304 		if (xDiff > 0) {
1305 			int width = r.width();
1306 			r.left = clippingRect->left;
1307 			r.setWidth(xDiff <= width ? width - xDiff : 0);
1308 		}
1309 		if (yDiff > 0) {
1310 			int height = r.height();
1311 			r.top = clippingRect->top;
1312 			r.setHeight(yDiff <= height ? height - yDiff : 0);
1313 		}
1314 
1315 		xDiff = r.right - clippingRect->right;
1316 		yDiff = r.bottom - clippingRect->bottom;
1317 
1318 		if (xDiff > 0)
1319 			r.setWidth(xDiff <= r.width() ? r.width() - xDiff : 0);
1320 		if (yDiff > 0)
1321 			r.setHeight(yDiff <= r.height() ? r.height() - yDiff : 0);
1322 	}
1323 
1324 	_activePage = page;
1325 	_clipRect = r;
1326 	_setupFn = setupFn;
1327 	_addFn = addFn;
1328 	_restoreFn = restoreFn;
1329 
1330 	if (setupFn)
1331 		(_state._vm->_screen->*setupFn)(this);
1332 }
1333 
setupViewPort()1334 void ViewPortResource::setupViewPort() {
1335 	setupViewPort(_state._vm->_screen->_backgroundPage, NULL,
1336 		&Screen::setupMCGASaveRect, &Screen::addRectOptSaveRect,
1337 		&Screen::restoreMCGASaveRect);
1338 }
1339 
setupViewPort(PictureResource * pic,Common::Rect * clippingRect)1340 void ViewPortResource::setupViewPort(PictureResource *pic, Common::Rect *clippingRect) {
1341 	setupViewPort(pic, clippingRect,
1342 		&Screen::setupMCGASaveRect, &Screen::addRectOptSaveRect,
1343 		&Screen::restoreMCGASaveRect);
1344 }
1345 
addSaveRect(int pageIndex,const Common::Rect & r)1346 void ViewPortResource::addSaveRect(int pageIndex, const Common::Rect &r) {
1347 	Common::Rect rect = r;
1348 
1349 	if (clipRect(rect)) {
1350 		if (_addFn) {
1351 			(_state._vm->_screen->*_addFn)(this, pageIndex, rect);
1352 		} else if (_rectListCount[pageIndex] != -1) {
1353 			_rectListPtr[pageIndex]->push_back(rect);
1354 		}
1355 	}
1356 }
1357 
fillPic(byte onOff)1358 void ViewPortResource::fillPic(byte onOff) {
1359 	_state._vm->_screen->fillPic(this, onOff);
1360 }
1361 
drawIfaceTime()1362 void ViewPortResource::drawIfaceTime() {
1363 	// Hour display
1364 	_state._vm->_screen->drawANumber(_state._vm->_screen->_vPort,
1365 		(_state._vm->_gameHour / 10) == 0 ? 10 : _state._vm->_gameHour / 10,
1366 		Common::Point(161, 25));
1367 	_state._vm->_screen->drawANumber(_state._vm->_screen->_vPort,
1368 		_state._vm->_gameHour % 10, Common::Point(172, 25));
1369 
1370 	// Minute display
1371 	_state._vm->_screen->drawANumber(_state._vm->_screen->_vPort,
1372 		_state._vm->_gameMinute / 10, Common::Point(190, 25));
1373 	_state._vm->_screen->drawANumber(_state._vm->_screen->_vPort,
1374 		_state._vm->_gameMinute % 10, Common::Point(201, 25));
1375 
1376 	// AM/PM indicator
1377 	PictureResource *pic = _state._vm->_bVoy->boltEntry(_state._vm->_voy->_isAM ? 272 : 273)._picResource;
1378 	_state._vm->_screen->sDrawPic(pic, _state._vm->_screen->_vPort,
1379 		Common::Point(215, 27));
1380 }
1381 
drawPicPerm(PictureResource * pic,const Common::Point & pt)1382 void ViewPortResource::drawPicPerm(PictureResource *pic, const Common::Point &pt) {
1383 	Common::Rect bounds = pic->_bounds;
1384 	bounds.translate(pt.x, pt.y);
1385 
1386 	bool saveBack = _state._vm->_screen->_saveBack;
1387 	_state._vm->_screen->_saveBack = false;
1388 	_state._vm->_screen->sDrawPic(pic, this, pt);
1389 	clipRect(bounds);
1390 
1391 	for (int pageIndex = 0; pageIndex < _pageCount; ++pageIndex) {
1392 		if (_pageIndex != pageIndex) {
1393 			addSaveRect(pageIndex, bounds);
1394 		}
1395 	}
1396 
1397 	_state._vm->_screen->_saveBack = saveBack;
1398 }
1399 /*------------------------------------------------------------------------*/
1400 
ViewPortListResource(BoltFilesState & state,const byte * src)1401 ViewPortListResource::ViewPortListResource(BoltFilesState &state, const byte *src) {
1402 	uint count = READ_LE_UINT16(src);
1403 	_palIndex = READ_LE_UINT16(src + 2);
1404 
1405 	// Load palette map
1406 	byte *palData = state._curLibPtr->memberAddr(READ_LE_UINT32(src + 4));
1407 	for (uint i = 0; i < 256; ++i, palData += 16)
1408 		_palette.push_back(ViewPortPalEntry(palData));
1409 
1410 	// Load view port pointer list
1411 	const uint32 *idP = (const uint32 *)&src[8];
1412 	for (uint i = 0; i < count; ++i, ++idP) {
1413 		uint32 id = READ_LE_UINT32(idP);
1414 		BoltEntry &entry = state._curLibPtr->getBoltEntryFromLong(id);
1415 
1416 		assert(entry._viewPortResource);
1417 		_entries.push_back(entry._viewPortResource);
1418 	}
1419 }
1420 
1421 /*------------------------------------------------------------------------*/
1422 
ViewPortPalEntry(const byte * src)1423 ViewPortPalEntry::ViewPortPalEntry(const byte *src) {
1424 	const uint16 *v = (const uint16 *)src;
1425 	_rEntry = READ_LE_UINT16(v++);
1426 	_gEntry = READ_LE_UINT16(v++);
1427 	_bEntry = READ_LE_UINT16(v++);
1428 	_rChange = READ_LE_UINT16(v++);
1429 	_gChange = READ_LE_UINT16(v++);
1430 	_bChange = READ_LE_UINT16(v++);
1431 	_palIndex = READ_LE_UINT16(v++);
1432 }
1433 
1434 
1435 /*------------------------------------------------------------------------*/
1436 
FontResource(BoltFilesState & state,byte * src)1437 FontResource::FontResource(BoltFilesState &state, byte *src) {
1438 	_minChar = src[0];
1439 	_maxChar = src[1];
1440 	_fontDepth = src[2];
1441 	_padding = src[3];
1442 	_fontHeight = src[5];
1443 	_topPadding = (int8)src[6];
1444 
1445 	int totalChars = _maxChar - _minChar + 1;
1446 	_charWidth = new int[totalChars];
1447 	for (int i = 0; i < totalChars; ++i)
1448 		_charWidth[i] = READ_LE_UINT16(src + 8 + 2 * i);
1449 
1450 	_charOffsets = src + 8 + totalChars * 2;
1451 	_charImages = _charOffsets + totalChars * 2;
1452 }
1453 
~FontResource()1454 FontResource::~FontResource() {
1455 	delete[] _charWidth;
1456 }
1457 
1458 /*------------------------------------------------------------------------*/
1459 
FontInfoResource(BoltFilesState & state,const byte * src)1460 FontInfoResource::FontInfoResource(BoltFilesState &state, const byte *src) {
1461 	_curFont = NULL;
1462 	_picFlags = src[4];
1463 	_picSelect = src[5];
1464 	_picPick = src[6];
1465 	_picOnOff = src[7];
1466 	_fontFlags = src[8];
1467 	_justify = (FontJustify)src[9];
1468 	_fontSaveBack = READ_LE_UINT16(src + 10);
1469 	_pos.x = (int16)READ_LE_UINT16(src + 12);
1470 	_pos.y = (int16)READ_LE_UINT16(src + 14);
1471 	_justifyWidth = READ_LE_UINT16(src + 16);
1472 	_justifyHeight = READ_LE_UINT16(src + 18);
1473 	_shadow.x = READ_LE_UINT16(src + 20);
1474 	_shadow.y = READ_LE_UINT16(src + 22);
1475 	_foreColor = READ_LE_UINT16(src + 24);
1476 	_backColor = READ_LE_UINT16(src + 26);
1477 	_shadowColor = READ_LE_UINT16(src + 28);
1478 }
1479 
FontInfoResource()1480 FontInfoResource::FontInfoResource() {
1481 	_curFont = NULL;
1482 	_picFlags = DISPFLAG_1 | DISPFLAG_2;
1483 	_picSelect = 0xff;
1484 	_picPick = 0xff;
1485 	_picOnOff = 0;
1486 	_fontFlags = DISPFLAG_NONE;
1487 	_justify = ALIGN_LEFT;
1488 	_fontSaveBack = 0;
1489 	_justifyWidth = 1;
1490 	_justifyHeight = 1;
1491 	_shadow = Common::Point(1, 1);
1492 	_foreColor = 1;
1493 	_backColor = 0;
1494 	_shadowColor = 0;
1495 }
1496 
FontInfoResource(byte picFlags,byte picSelect,byte picPick,byte picOnOff,byte fontFlags,FontJustify justify,int fontSaveBack,const Common::Point & pos,int justifyWidth,int justifyHeight,const Common::Point & shadow,int foreColor,int backColor,int shadowColor)1497 FontInfoResource::FontInfoResource(byte picFlags, byte picSelect, byte picPick, byte picOnOff,
1498 		byte fontFlags, FontJustify justify, int fontSaveBack, const Common::Point &pos,
1499 		int justifyWidth, int justifyHeight, const Common::Point &shadow, int foreColor,
1500 		int backColor, int shadowColor) {
1501 	_curFont = NULL;
1502 	_picFlags = picFlags;
1503 	_picSelect = picSelect;
1504 	_picPick = picPick;
1505 	_picOnOff = picOnOff;
1506 	_fontFlags = fontFlags;
1507 	_justify = justify;
1508 	_fontSaveBack = fontSaveBack;
1509 	_pos = pos;
1510 	_justifyWidth = justifyWidth;
1511 	_justifyHeight = justifyHeight;
1512 	_shadow = shadow;
1513 	_foreColor = foreColor;
1514 	_backColor = backColor;
1515 	_shadowColor = shadowColor;
1516 }
1517 
1518 /*------------------------------------------------------------------------*/
1519 
CMapResource(BoltFilesState & state,const byte * src)1520 CMapResource::CMapResource(BoltFilesState &state, const byte *src): _vm(state._vm) {
1521 	_steps = src[0];
1522 	_fadeStatus = src[1];
1523 	_start = READ_LE_UINT16(src + 2);
1524 	_end = READ_LE_UINT16(src + 4);
1525 
1526 	int count = _end - _start + 1;
1527 	_entries = new byte[count * 3];
1528 	Common::copy(src + 6, src + 6 + 3 * count, _entries);
1529 
1530 	int palIndex = state._vm->_screen->_viewPortListPtr->_palIndex;
1531 	if (_end > palIndex)
1532 		_end = palIndex;
1533 	if (_start > palIndex)
1534 		_start = palIndex;
1535 }
1536 
~CMapResource()1537 CMapResource::~CMapResource() {
1538 	delete[] _entries;
1539 }
1540 
startFade()1541 void CMapResource::startFade() {
1542 	_vm->_eventsManager->startFade(this);
1543 }
1544 
1545 /*------------------------------------------------------------------------*/
1546 
VInitCycleResource(BoltFilesState & state,const byte * src)1547 VInitCycleResource::VInitCycleResource(BoltFilesState &state, const byte *src):
1548 		_state(state) {
1549 	// Set up arrays
1550 	for (int i = 0; i < 4; ++i) {
1551 		_type[i] = READ_LE_UINT16(src + i * 2);
1552 		state._curLibPtr->resolveIt(READ_LE_UINT32(src + 8 + i * 4), &_ptr[i]);
1553 	}
1554 }
1555 
vStartCycle()1556 void VInitCycleResource::vStartCycle() {
1557 	EventsManager &evt = *_state._vm->_eventsManager;
1558 	evt._cycleIntNode._flags |= 1;
1559 	evt._cyclePtr = this;
1560 
1561 	for (int i = 0; i < 4; ++i) {
1562 		evt._cycleNext[i] = _ptr[i];
1563 		evt._cycleTime[i] = 0;
1564 	}
1565 
1566 	evt._cycleStatus = 1;
1567 	evt._cycleIntNode._flags &= ~1;
1568 }
1569 
vStopCycle()1570 void VInitCycleResource::vStopCycle() {
1571 	EventsManager &evt = *_state._vm->_eventsManager;
1572 	evt._cycleIntNode._flags |= 1;
1573 	evt._cycleStatus &= ~1;
1574 }
1575 
1576 /*------------------------------------------------------------------------*/
1577 
PtrResource(BoltFilesState & state,const byte * src)1578 PtrResource::PtrResource(BoltFilesState &state, const byte *src) {
1579 	// Load pointer list
1580 	const uint32 *idP = (const uint32 *)&src[0];
1581 	int size = state._curMemberPtr->_size;
1582 
1583 	for (int i = 0; i < size / 4; ++i, ++idP) {
1584 		uint32 id = READ_LE_UINT32(idP);
1585 		BoltEntry &entry = state._curLibPtr->getBoltEntryFromLong(id);
1586 
1587 		_entries.push_back(&entry);
1588 	}
1589 }
1590 
1591 /*------------------------------------------------------------------------*/
1592 
ControlResource(BoltFilesState & state,const byte * src)1593 ControlResource::ControlResource(BoltFilesState &state, const byte *src) {
1594 	// Get Id for the state data. Since it refers to a following entry in the same
1595 	// group, for simplicity we set the _state back in the main playStamp method
1596 	_stateId = READ_LE_UINT32(&src[0x32]);
1597 	_state = nullptr;
1598 
1599 	for (int i = 0; i < 8; ++i)
1600 		_memberIds[i] = READ_LE_UINT16(src + i * 2);
1601 
1602 	// Load pointer list
1603 	const uint32 *idP = (const uint32 *)&src[0x10];
1604 	int count = READ_LE_UINT16(&src[0x36]);
1605 
1606 	Common::fill(&_entries[0], &_entries[8], (byte *)nullptr);
1607 	for (int i = 0; i < count; ++i, ++idP) {
1608 		uint32 id = READ_LE_UINT32(idP);
1609 		state._curLibPtr->resolveIt(id, &_entries[i]);
1610 	}
1611 }
1612 
1613 /*------------------------------------------------------------------------*/
1614 
StateResource(BoltFilesState & state,const byte * src)1615 StateResource::StateResource(BoltFilesState &state, const byte *src):
1616 		_victimIndex(_vals[1]), _victimEvidenceIndex(_vals[2]),
1617 		_victimMurderIndex(_vals[3]) {
1618 	for (int i = 0; i < 4; ++i)
1619 		_vals[i] = READ_LE_UINT32(src + i * 4);
1620 }
1621 
synchronize(Common::Serializer & s)1622 void StateResource::synchronize(Common::Serializer &s) {
1623 	for (int i = 0; i < 4; ++i)
1624 		s.syncAsSint32LE(_vals[i]);
1625 }
1626 
1627 } // End of namespace Voyeur
1628