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