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 "scumm/scumm.h"
24 #include "scumm/file.h"
25 #include "scumm/he/intern_he.h"
26 #include "scumm/resource.h"
27 #include "scumm/he/resource_he.h"
28 #include "scumm/he/sound_he.h"
29 
30 #include "audio/decoders/wave.h"
31 #include "graphics/cursorman.h"
32 #include "graphics/maccursor.h"
33 #include "graphics/wincursor.h"
34 
35 #include "common/archive.h"
36 #include "common/memstream.h"
37 #include "common/system.h"
38 #include "common/winexe_pe.h"
39 
40 namespace Scumm {
41 
ResExtractor(ScummEngine_v70he * scumm)42 ResExtractor::ResExtractor(ScummEngine_v70he *scumm)
43 	: _vm(scumm) {
44 
45 	memset(_cursorCache, 0, sizeof(_cursorCache));
46 }
47 
~ResExtractor()48 ResExtractor::~ResExtractor() {
49 	for (int i = 0; i < MAX_CACHED_CURSORS; ++i) {
50 		CachedCursor *cc = &_cursorCache[i];
51 		if (cc->valid) {
52 			free(cc->bitmap);
53 			free(cc->palette);
54 		}
55 	}
56 
57 	memset(_cursorCache, 0, sizeof(_cursorCache));
58 }
59 
findCachedCursor(int id)60 ResExtractor::CachedCursor *ResExtractor::findCachedCursor(int id) {
61 	for (int i = 0; i < MAX_CACHED_CURSORS; ++i)
62 		if (_cursorCache[i].valid && _cursorCache[i].id == id)
63 			return &_cursorCache[i];
64 
65 	return NULL;
66 }
67 
getCachedCursorSlot()68 ResExtractor::CachedCursor *ResExtractor::getCachedCursorSlot() {
69 	uint32 minLastUsed = 0;
70 	CachedCursor *r = NULL;
71 
72 	for (int i = 0; i < MAX_CACHED_CURSORS; ++i) {
73 		CachedCursor *cc = &_cursorCache[i];
74 		if (!cc->valid)
75 			return cc;
76 
77 		if (minLastUsed == 0 || cc->lastUsed < minLastUsed) {
78 			minLastUsed = cc->lastUsed;
79 			r = cc;
80 		}
81 	}
82 
83 	assert(r);
84 	delete[] r->bitmap;
85 	delete[] r->palette;
86 	memset(r, 0, sizeof(CachedCursor));
87 	return r;
88 }
89 
setCursor(int id)90 void ResExtractor::setCursor(int id) {
91 	CachedCursor *cc = findCachedCursor(id);
92 
93 	if (cc != NULL) {
94 		debug(7, "Found cursor %d in cache slot %lu", id, (long)(cc - _cursorCache));
95 	} else {
96 		cc = getCachedCursorSlot();
97 		assert(cc && !cc->valid);
98 
99 		if (!extractResource(id, cc))
100 			error("Could not extract cursor %d", id);
101 
102 		debug(7, "Adding cursor %d to cache slot %lu", id, (long)(cc - _cursorCache));
103 
104 		cc->valid = true;
105 		cc->id = id;
106 		cc->lastUsed = g_system->getMillis();
107 	}
108 
109 	if (cc->palette)
110 		CursorMan.replaceCursorPalette(cc->palette, 0, cc->palSize);
111 
112 	_vm->setCursorHotspot(cc->hotspotX, cc->hotspotY);
113 	_vm->setCursorFromBuffer(cc->bitmap, cc->width, cc->height, cc->width);
114 }
115 
116 
Win32ResExtractor(ScummEngine_v70he * scumm)117 Win32ResExtractor::Win32ResExtractor(ScummEngine_v70he *scumm) : ResExtractor(scumm) {
118 	_exe = new Common::PEResources();
119 }
120 
~Win32ResExtractor()121 Win32ResExtractor::~Win32ResExtractor() {
122 	delete _exe;
123 }
124 
extractResource(int id,CachedCursor * cc)125 bool Win32ResExtractor::extractResource(int id, CachedCursor *cc) {
126 	if (_fileName.empty()) { // We are running for the first time
127 		_fileName = _vm->generateFilename(-3);
128 
129 		if (!_exe->loadFromEXE(_fileName))
130 			error("Cannot open file %s", _fileName.c_str());
131 	}
132 
133 	Graphics::WinCursorGroup *group = Graphics::WinCursorGroup::createCursorGroup(_exe, id);
134 
135 	if (!group)
136 		return false;
137 
138 	Graphics::Cursor *cursor = group->cursors[0].cursor;
139 
140 	cc->bitmap = new byte[cursor->getWidth() * cursor->getHeight()];
141 	cc->width = cursor->getWidth();
142 	cc->height = cursor->getHeight();
143 	cc->hotspotX = cursor->getHotspotX();
144 	cc->hotspotY = cursor->getHotspotY();
145 
146 	// Convert from the paletted format to the SCUMM palette
147 	const byte *srcBitmap = cursor->getSurface();
148 
149 	for (int i = 0; i < cursor->getWidth() * cursor->getHeight(); i++) {
150 		if (srcBitmap[i] == cursor->getKeyColor()) // Transparent
151 			cc->bitmap[i] = 255;
152 		else if (srcBitmap[i] == 0)                // Black
153 			cc->bitmap[i] = 253;
154 		else                                       // White
155 			cc->bitmap[i] = 254;
156 	}
157 
158 	delete group;
159 	return true;
160 }
161 
MacResExtractor(ScummEngine_v70he * scumm)162 MacResExtractor::MacResExtractor(ScummEngine_v70he *scumm) : ResExtractor(scumm) {
163 	_resMgr = NULL;
164 }
165 
extractResource(int id,CachedCursor * cc)166 bool MacResExtractor::extractResource(int id, CachedCursor *cc) {
167 	// Create the MacResManager if not created already
168 	if (_resMgr == NULL) {
169 		_resMgr = new Common::MacResManager();
170 		if (!_resMgr->open(_vm->generateFilename(-3)))
171 			error("Cannot open file %s", _fileName.c_str());
172 	}
173 
174 	Common::SeekableReadStream *dataStream = _resMgr->getResource('crsr', id + 1000);
175 
176 	if (!dataStream)
177 		return false;
178 
179 	// If we don't have a cursor palette, force monochrome cursors
180 	bool forceMonochrome = !_vm->_system->hasFeature(OSystem::kFeatureCursorPalette);
181 
182 	Graphics::MacCursor *macCursor = new Graphics::MacCursor();
183 
184 	if (!macCursor->readFromStream(*dataStream, forceMonochrome)) {
185 		delete dataStream;
186 		delete macCursor;
187 		return false;
188 	}
189 
190 	cc->bitmap = new byte[macCursor->getWidth() * macCursor->getHeight()];
191 	cc->width = macCursor->getWidth();
192 	cc->height = macCursor->getHeight();
193 	cc->hotspotX = macCursor->getHotspotX();
194 	cc->hotspotY = macCursor->getHotspotY();
195 
196 	if (forceMonochrome) {
197 		// Convert to the SCUMM palette
198 		const byte *srcBitmap = macCursor->getSurface();
199 
200 		for (int i = 0; i < macCursor->getWidth() * macCursor->getHeight(); i++) {
201 			if (srcBitmap[i] == macCursor->getKeyColor()) // Transparent
202 				cc->bitmap[i] = 255;
203 			else if (srcBitmap[i] == 0)                // Black
204 				cc->bitmap[i] = 253;
205 			else                                       // White
206 				cc->bitmap[i] = 254;
207 		}
208 	} else {
209 		// Copy data and palette
210 
211 		// Sanity check. This code assumes that the key color is the same
212 		assert(macCursor->getKeyColor() == 255);
213 
214 		memcpy(cc->bitmap, macCursor->getSurface(), macCursor->getWidth() * macCursor->getHeight());
215 
216 		cc->palette = new byte[256 * 3];
217 		cc->palSize = 256;
218 		memcpy(cc->palette, macCursor->getPalette(), 256 * 3);
219 	}
220 
221 	delete macCursor;
222 	delete dataStream;
223 	return true;
224 }
225 
readRoomsOffsets()226 void ScummEngine_v70he::readRoomsOffsets() {
227 	int num, i;
228 	byte *ptr;
229 
230 	debug(9, "readRoomOffsets()");
231 
232 	num = READ_LE_UINT16(_heV7RoomOffsets);
233 	ptr = _heV7RoomOffsets + 2;
234 	for (i = 0; i < num; i++) {
235 		_res->_types[rtRoom][i]._roomoffs = READ_LE_UINT32(ptr);
236 		ptr += 4;
237 	}
238 }
239 
readGlobalObjects()240 void ScummEngine_v70he::readGlobalObjects() {
241 	int num = _fileHandle->readUint16LE();
242 	assert(num == _numGlobalObjects);
243 	assert(_objectStateTable);
244 	assert(_objectOwnerTable);
245 
246 	_fileHandle->read(_objectStateTable, num);
247 	_fileHandle->read(_objectOwnerTable, num);
248 	_fileHandle->read(_objectRoomTable, num);
249 
250 	_fileHandle->read(_classData, num * sizeof(uint32));
251 
252 #if defined(SCUMM_BIG_ENDIAN)
253 	// Correct the endianess if necessary
254 	for (int i = 0; i != num; i++)
255 		_classData[i] = FROM_LE_32(_classData[i]);
256 #endif
257 }
258 
259 #ifdef ENABLE_HE
readMAXS(int blockSize)260 void ScummEngine_v99he::readMAXS(int blockSize) {
261 	if (blockSize == 52) {
262 		_numVariables = _fileHandle->readUint16LE();
263 		_fileHandle->readUint16LE();
264 		_numRoomVariables = _fileHandle->readUint16LE();
265 		_numLocalObjects = _fileHandle->readUint16LE();
266 		_numArray = _fileHandle->readUint16LE();
267 		_fileHandle->readUint16LE();
268 		_fileHandle->readUint16LE();
269 		_numFlObject = _fileHandle->readUint16LE();
270 		_numInventory = _fileHandle->readUint16LE();
271 		_numRooms = _fileHandle->readUint16LE();
272 		_numScripts = _fileHandle->readUint16LE();
273 		_numSounds = _fileHandle->readUint16LE();
274 		_numCharsets = _fileHandle->readUint16LE();
275 		_numCostumes = _fileHandle->readUint16LE();
276 		_numGlobalObjects = _fileHandle->readUint16LE();
277 		_numImages = _fileHandle->readUint16LE();
278 		_numSprites = _fileHandle->readUint16LE();
279 		_numLocalScripts = _fileHandle->readUint16LE();
280 		_HEHeapSize = _fileHandle->readUint16LE();
281 		_numPalettes = _fileHandle->readUint16LE();
282 		_numUnk = _fileHandle->readUint16LE();
283 		_numTalkies = _fileHandle->readUint16LE();
284 		_numNewNames = 10;
285 
286 		_objectRoomTable = (byte *)calloc(_numGlobalObjects, 1);
287 		_numGlobalScripts = 2048;
288 	} else
289 		ScummEngine_v90he::readMAXS(blockSize);
290 }
291 
readMAXS(int blockSize)292 void ScummEngine_v90he::readMAXS(int blockSize) {
293 	if (blockSize == 46) {
294 		_numVariables = _fileHandle->readUint16LE();
295 		_fileHandle->readUint16LE();
296 		_numRoomVariables = _fileHandle->readUint16LE();
297 		_numLocalObjects = _fileHandle->readUint16LE();
298 		_numArray = _fileHandle->readUint16LE();
299 		_fileHandle->readUint16LE();
300 		_fileHandle->readUint16LE();
301 		_numFlObject = _fileHandle->readUint16LE();
302 		_numInventory = _fileHandle->readUint16LE();
303 		_numRooms = _fileHandle->readUint16LE();
304 		_numScripts = _fileHandle->readUint16LE();
305 		_numSounds = _fileHandle->readUint16LE();
306 		_numCharsets = _fileHandle->readUint16LE();
307 		_numCostumes = _fileHandle->readUint16LE();
308 		_numGlobalObjects = _fileHandle->readUint16LE();
309 		_numImages = _fileHandle->readUint16LE();
310 		_numSprites = _fileHandle->readUint16LE();
311 		_numLocalScripts = _fileHandle->readUint16LE();
312 		_HEHeapSize = _fileHandle->readUint16LE();
313 		_numNewNames = 10;
314 
315 		_objectRoomTable = (byte *)calloc(_numGlobalObjects, 1);
316 		if (_game.features & GF_HE_985)
317 			_numGlobalScripts = 2048;
318 		else
319 			_numGlobalScripts = 200;
320 	} else
321 		ScummEngine_v72he::readMAXS(blockSize);
322 }
323 
readMAXS(int blockSize)324 void ScummEngine_v72he::readMAXS(int blockSize) {
325 	if (blockSize == 40) {
326 		_numVariables = _fileHandle->readUint16LE();
327 		_fileHandle->readUint16LE();
328 		_numBitVariables = _numRoomVariables = _fileHandle->readUint16LE();
329 		_numLocalObjects = _fileHandle->readUint16LE();
330 		_numArray = _fileHandle->readUint16LE();
331 		_fileHandle->readUint16LE();
332 		_numVerbs = _fileHandle->readUint16LE();
333 		_numFlObject = _fileHandle->readUint16LE();
334 		_numInventory = _fileHandle->readUint16LE();
335 		_numRooms = _fileHandle->readUint16LE();
336 		_numScripts = _fileHandle->readUint16LE();
337 		_numSounds = _fileHandle->readUint16LE();
338 		_numCharsets = _fileHandle->readUint16LE();
339 		_numCostumes = _fileHandle->readUint16LE();
340 		_numGlobalObjects = _fileHandle->readUint16LE();
341 		_numImages = _fileHandle->readUint16LE();
342 		_numNewNames = 10;
343 
344 		_objectRoomTable = (byte *)calloc(_numGlobalObjects, 1);
345 		_numGlobalScripts = 200;
346 	} else
347 		ScummEngine_v6::readMAXS(blockSize);
348 }
349 
getStringAddress(ResId idx)350 byte *ScummEngine_v72he::getStringAddress(ResId idx) {
351 	byte *addr = getResourceAddress(rtString, idx);
352 	if (addr == NULL)
353 		return NULL;
354 	return ((ScummEngine_v72he::ArrayHeader *)addr)->data;
355 }
356 
getSoundResourceSize(ResId id)357 int ScummEngine_v72he::getSoundResourceSize(ResId id) {
358 	const byte *ptr;
359 	int offs, size;
360 
361 	if (id > _numSounds) {
362 		if (!((SoundHE *)_sound)->getHEMusicDetails(id, offs, size)) {
363 			debug(0, "getSoundResourceSize: musicID %d not found", id);
364 			return 0;
365 		}
366 	} else {
367 		ptr = getResourceAddress(rtSound, id);
368 		if (!ptr)
369 			return 0;
370 
371 		if (READ_BE_UINT32(ptr) == MKTAG('R','I','F','F')) {
372 			byte flags;
373 			int rate;
374 
375 			size = READ_BE_UINT32(ptr + 4);
376 			Common::MemoryReadStream stream(ptr, size);
377 
378 			if (!Audio::loadWAVFromStream(stream, size, rate, flags)) {
379 				error("getSoundResourceSize: Not a valid WAV file");
380 			}
381 		} else {
382 			ptr += 8 + READ_BE_UINT32(ptr + 12);
383 			if (READ_BE_UINT32(ptr) == MKTAG('S','B','N','G')) {
384 				ptr += READ_BE_UINT32(ptr + 4);
385 			}
386 
387 			assert(READ_BE_UINT32(ptr) == MKTAG('S','D','A','T'));
388 			size = READ_BE_UINT32(ptr + 4) - 8;
389 		}
390 	}
391 
392 	return size;
393 }
394 
setResourceOffHeap(int typeId,int resId,int val)395 void ScummEngine_v90he::setResourceOffHeap(int typeId, int resId, int val) {
396 	debug(0, "setResourceOffHeap: type %d resId %d toggle %d", typeId, resId, val);
397 	ResType type;
398 
399 	switch (typeId) {
400 	case 1:
401 		type = rtRoom;
402 		break;
403 	case 2:
404 		type = rtScript;
405 		break;
406 	case 3:
407 		type = rtCostume;
408 		break;
409 	case 4:
410 		type = rtSound;
411 		break;
412 	case 6:
413 		type = rtCharset;
414 		break;
415 	case 19:
416 		type = rtImage;
417 		break;
418 	default:
419 		error("setResourceOffHeap: default case %d", typeId);
420 	}
421 
422 	if (val == 1) {
423 		_res->setOffHeap(type, resId);
424 	} else {
425 		_res->setOnHeap(type, resId);
426 	}
427 }
428 
429 #endif
430 
431 } // End of namespace Scumm
432