1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "common/config-manager.h"
24 #include "common/file.h"
25 #include "common/memstream.h"
26 #include "common/substream.h"
27 
28 #include "graphics/macgui/macfontmanager.h"
29 #include "graphics/macgui/macwindowmanager.h"
30 #include "image/bmp.h"
31 
32 #include "director/director.h"
33 #include "director/cast.h"
34 #include "director/castmember.h"
35 #include "director/images.h"
36 #include "director/movie.h"
37 #include "director/score.h"
38 #include "director/sound.h"
39 #include "director/stxt.h"
40 #include "director/util.h"
41 #include "director/lingo/lingo.h"
42 #include "director/lingo/lingo-object.h"
43 
44 namespace Director {
45 
46 const char *scriptTypes[] = {
47 	"ScoreScript",
48 	"CastScript",
49 	"MovieScript",
50 	"EventScript",
51 	"TestScript"
52 };
53 
scriptType2str(ScriptType scr)54 const char *scriptType2str(ScriptType scr) {
55 	if (scr < 0)
56 		return "NoneScript";
57 
58 	if (scr > kMaxScriptType)
59 		return "<unknown>";
60 
61 	return scriptTypes[scr];
62 }
63 
Cast(Movie * movie,uint16 castLibID,bool isShared)64 Cast::Cast(Movie *movie, uint16 castLibID, bool isShared) {
65 	_movie = movie;
66 	_vm = _movie->getVM();
67 	_lingo = _vm->getLingo();
68 
69 	_castLibID = castLibID;
70 	_isShared = isShared;
71 
72 	_lingoArchive = new LingoArchive(this);
73 
74 	_castArrayStart = _castArrayEnd = 0;
75 
76 	_castIDoffset = 0;
77 
78 	_castArchive = nullptr;
79 	_version = 0;
80 	_platform = Common::kPlatformMacintosh;
81 
82 	_stageColor = 0;
83 
84 	_loadedStxts = nullptr;
85 	_loadedCast = nullptr;
86 
87 	_defaultPalette = -1;
88 }
89 
~Cast()90 Cast::~Cast() {
91 	if (_loadedStxts)
92 		for (Common::HashMap<int, const Stxt *>::iterator it = _loadedStxts->begin(); it != _loadedStxts->end(); ++it)
93 			delete it->_value;
94 
95 	if (_castArchive) {
96 		_castArchive->close();
97 		delete _castArchive;
98 		_castArchive = nullptr;
99 	}
100 
101 	if (_loadedCast)
102 		for (Common::HashMap<int, CastMember *>::iterator it = _loadedCast->begin(); it != _loadedCast->end(); ++it)
103 			delete it->_value;
104 
105 	for (Common::HashMap<uint16, CastMemberInfo *>::iterator it = _castsInfo.begin(); it != _castsInfo.end(); ++it)
106 		delete it->_value;
107 
108 	for (FontXPlatformMap::iterator it = _fontXPlatformMap.begin(); it != _fontXPlatformMap.end(); ++it)
109 		delete it->_value;
110 
111 	for (FontMap::iterator it = _fontMap.begin(); it != _fontMap.end(); ++it)
112 		delete it->_value;
113 
114 	delete _loadedStxts;
115 	delete _loadedCast;
116 	delete _lingoArchive;
117 }
118 
getCastMember(int castId)119 CastMember *Cast::getCastMember(int castId) {
120 	CastMember *result = nullptr;
121 
122 	if (_loadedCast && _loadedCast->contains(castId)) {
123 		result = _loadedCast->getVal(castId);
124 	}
125 	return result;
126 }
127 
releaseCastMemberWidget()128 void Cast::releaseCastMemberWidget() {
129 	if (_loadedCast)
130 		for (Common::HashMap<int, CastMember *>::iterator it = _loadedCast->begin(); it != _loadedCast->end(); ++it)
131 			it->_value->releaseWidget();
132 }
133 
getCastMemberByName(const Common::String & name)134 CastMember *Cast::getCastMemberByName(const Common::String &name) {
135 	CastMember *result = nullptr;
136 
137 	if (_castsNames.contains(name)) {
138 		result = _loadedCast->getVal(_castsNames[name]);
139 	}
140 	return result;
141 }
142 
getCastMemberByScriptId(int scriptId)143 CastMember *Cast::getCastMemberByScriptId(int scriptId) {
144 	CastMember *result = nullptr;
145 	if (_castsScriptIds.contains(scriptId))
146 		result = _loadedCast->getVal(_castsScriptIds[scriptId]);
147 	return result;
148 }
149 
getCastMemberInfo(int castId)150 CastMemberInfo *Cast::getCastMemberInfo(int castId) {
151 	CastMemberInfo *result = nullptr;
152 
153 	if (_castsInfo.contains(castId)) {
154 		result = _castsInfo[castId];
155 	}
156 	return result;
157 }
158 
getStxt(int castId)159 const Stxt *Cast::getStxt(int castId) {
160 	const Stxt *result = nullptr;
161 
162 	if (_loadedStxts->contains(castId)) {
163 		result = _loadedStxts->getVal(castId);
164 	}
165 	return result;
166 }
167 
getCastMemberInitialRect(int castId)168 Common::Rect Cast::getCastMemberInitialRect(int castId) {
169 	CastMember *cast = _loadedCast->getVal(castId);
170 
171 	if (!cast) {
172 		warning("Cast::getCastMemberInitialRect(%d): empty cast", castId);
173 		return Common::Rect(0, 0);
174 	}
175 
176 	return cast->_initialRect;
177 }
178 
setCastMemberModified(int castId)179 void Cast::setCastMemberModified(int castId) {
180 	CastMember *cast = _loadedCast->getVal(castId);
181 
182 	if (!cast) {
183 		warning("Cast::setCastMemberModified(%d): empty cast", castId);
184 		return;
185 	}
186 
187 	cast->setModified(true);
188 }
189 
setArchive(Archive * archive)190 void Cast::setArchive(Archive *archive) {
191 	_castArchive = archive;
192 
193 	if (archive->hasResource(MKTAG('M', 'C', 'N', 'M'), 0)) {
194 		_macName = archive->getName(MKTAG('M', 'C', 'N', 'M'), 0).c_str();
195 	} else {
196 		_macName = archive->getFileName();
197 	}
198 }
199 
loadArchive()200 void Cast::loadArchive() {
201 	loadConfig();
202 	loadCast();
203 }
204 
loadConfig()205 bool Cast::loadConfig() {
206 	if (!_castArchive->hasResource(MKTAG('V', 'W', 'C', 'F'), -1)) {
207 		warning("Cast::loadConfig(): Wrong format. VWCF resource missing");
208 		return false;
209 	}
210 
211 	Common::SeekableReadStreamEndian *stream = _castArchive->getFirstResource(MKTAG('V', 'W', 'C', 'F'));
212 
213 	debugC(1, kDebugLoading, "****** Loading Config VWCF");
214 
215 	if (debugChannelSet(5, kDebugLoading))
216 		stream->hexdump(stream->size());
217 
218 	uint16 len = stream->readUint16();
219 	uint16 fileVersion = stream->readUint16(); // TODO: very high fileVersion means protected
220 	_movieRect = Movie::readRect(*stream);
221 	if (!_isShared)
222 		_movie->_movieRect = _movieRect;
223 
224 	_castArrayStart = stream->readUint16();
225 	_castArrayEnd = stream->readUint16();
226 	byte currentFrameRate = stream->readByte();
227 	if (!_isShared) {
228 		_movie->getScore()->_currentFrameRate = currentFrameRate;
229 		if (_movie->getScore()->_currentFrameRate == 0)
230 			_movie->getScore()->_currentFrameRate = 20;
231 	}
232 
233 	byte lightswitch = stream->readByte();
234 	uint16 unk1 = stream->readUint16();
235 	uint16 commentFont = stream->readUint16();
236 	uint16 commentSize = stream->readUint16();
237 	uint16 commentStyle = stream->readUint16();
238 	_stageColor = stream->readUint16();
239 	if (!_isShared)
240 		_movie->_stageColor = _vm->transformColor(_stageColor);
241 
242 	uint16 bitdepth = stream->readUint16();
243 
244 	// byte color = stream.readByte();	// boolean, color = 1, B/W = 0
245 	// uint16 stageColorR = stream.readUint16();
246 	// uint16 stageColorG = stream.readUint16();
247 	// uint16 stageColorB = stream.readUint16();
248 
249 	debugC(1, kDebugLoading, "Cast::loadConfig(): len: %d, fileVersion: %d, framerate: %d, light: %d, unk: %d, font: %d, size: %d"
250 			", style: %d", len, fileVersion, currentFrameRate, lightswitch, unk1, commentFont, commentSize, commentStyle);
251 	debugC(1, kDebugLoading, "Cast::loadConfig(): stagecolor: %d, depth: %d",
252 			_stageColor, bitdepth);
253 	if (debugChannelSet(1, kDebugLoading))
254 		_movieRect.debugPrint(1, "Cast::loadConfig(): Movie rect: ");
255 
256 	_version = fileVersion;
257 
258 	// D3 fields - Macromedia did not increment the fileVersion from D2 to D3
259 	// so we just have to check if there are more bytes to read.
260 	if (stream->pos() < stream->size()) {
261 		for (int i = 0; i < 0x06; i++) {
262 			stream->readByte();
263 		}
264 		_version = stream->readUint16();
265 		for (int i = 0; i < 0x0a; i++) {
266 			stream->readByte();
267 		}
268 		debugC(1, kDebugLoading, "Cast::loadConfig(): directorVersion: %d", _version);
269 	}
270 
271 	if (_version >= kFileVer400) {
272 		for (int i = 0; i < 0x08; i++) {
273 			stream->readByte();
274 		}
275 		_platform = platformFromID(stream->readUint16());
276 		for (int i = 0; i < 0x0c; i++) {
277 			stream->readByte();
278 		}
279 		_defaultPalette = (int16)stream->readUint16();
280 		for (int i = 0; i < 0x08; i++) {
281 			stream->readByte();
282 		}
283 		debugC(1, kDebugLoading, "Cast::loadConfig(): platform: %s, defaultPalette: %d", getPlatformAbbrev(_platform), _defaultPalette);
284 	}
285 
286 	uint16 humanVer = humanVersion(_version);
287 	if (humanVer > _vm->getVersion()) {
288 		if (_vm->getVersion() > 0)
289 			warning("Movie is from later version v%d", humanVer);
290 		_vm->setVersion(humanVer);
291 	}
292 
293 	delete stream;
294 	return true;
295 }
296 
loadCast()297 void Cast::loadCast() {
298 	// Palette Information
299 	Common::Array<uint16> clutList = _castArchive->getResourceIDList(MKTAG('C', 'L', 'U', 'T'));
300 	if (clutList.size() == 0) {
301 		debugC(2, kDebugLoading, "CLUT resource not found, using default Mac palette");
302 	} else {
303 		for (uint i = 0; i < clutList.size(); i++) {
304 			Common::SeekableReadStreamEndian *pal = _castArchive->getResource(MKTAG('C', 'L', 'U', 'T'), clutList[i]);
305 
306 			debugC(2, kDebugLoading, "****** Loading Palette CLUT, #%d", clutList[i]);
307 			PaletteV4 p = loadPalette(*pal);
308 
309 			// for D2, we are using palette cast member id to resolve palette Id, so we are using lowest 1 bit to represent cast id. see Also loadCastChildren
310 			if (_version < kFileVer300)
311 				g_director->addPalette(clutList[i] & 0xff, p.palette, p.length);
312 			else
313 				g_director->addPalette(clutList[i], p.palette, p.length);
314 
315 			delete pal;
316 		}
317 	}
318 
319 	Common::SeekableReadStreamEndian *r = nullptr;
320 
321 	// Font Directory
322 	if (_castArchive->hasResource(MKTAG('F', 'O', 'N', 'D'), -1)) {
323 		debug("Cast::loadArchive(): Movie has fonts. Loading....");
324 
325 		_vm->_wm->_fontMan->loadFonts(_castArchive->getPathName());
326 	}
327 
328 	// CastMember Information Array
329 	if (_castArchive->hasResource(MKTAG('V', 'W', 'C', 'R'), -1)) {
330 		_castIDoffset = _castArchive->getResourceIDList(MKTAG('V', 'W', 'C', 'R'))[0];
331 		loadCastDataVWCR(*(r = _castArchive->getResource(MKTAG('V', 'W', 'C', 'R'), _castIDoffset)));
332 		delete r;
333 	}
334 
335 	// Font Mapping
336 	if (_castArchive->hasResource(MKTAG('V', 'W', 'F', 'M'), -1)) {
337 		loadFontMap(*(r = _castArchive->getFirstResource(MKTAG('V', 'W', 'F', 'M'))));
338 		delete r;
339 	}
340 
341 	// Cross-Platform Font Mapping
342 	if (_castArchive->hasResource(MKTAG('F', 'X', 'm', 'p'), -1)) {
343 		loadFXmp(*(r = _castArchive->getFirstResource(MKTAG('F', 'X', 'm', 'p'))));
344 		delete r;
345 	}
346 
347 	// Font Mapping V4
348 	if (_castArchive->hasResource(MKTAG('F', 'm', 'a', 'p'), -1)) {
349 		loadFontMapV4(*(r = _castArchive->getFirstResource(MKTAG('F', 'm', 'a', 'p'))));
350 		delete r;
351 	}
352 
353 	// Pattern Tiles
354 	if (_castArchive->hasResource(MKTAG('V', 'W', 'T', 'L'), -1)) {
355 		debug("STUB: Unhandled VWTL resource.");
356 	}
357 
358 	// Time code
359 	// TODO: Is this a score resource?
360 	if (_castArchive->hasResource(MKTAG('V', 'W', 't', 'c'), -1)) {
361 		debug("STUB: Unhandled VWtc resource");
362 	}
363 
364 	// Tape Key resource. Perhaps a lookup for labels?
365 	// TODO: Is this a score resource?
366 	if (_castArchive->hasResource(MKTAG('V', 'W', 't', 'k'), -1)) {
367 		debug("STUB: Unhandled VWtk resource");
368 	}
369 
370 	// External sound files
371 	if (_castArchive->hasResource(MKTAG('S', 'T', 'R', ' '), -1)) {
372 		loadExternalSound(*(r = _castArchive->getFirstResource(MKTAG('S', 'T', 'R', ' '))));
373 		delete r;
374 	}
375 
376 	Common::Array<uint16> vwci = _castArchive->getResourceIDList(MKTAG('V', 'W', 'C', 'I'));
377 	if (vwci.size() > 0) {
378 		debugC(2, kDebugLoading, "****** Loading %d CastInfos VWCI", vwci.size());
379 
380 		for (Common::Array<uint16>::iterator iterator = vwci.begin(); iterator != vwci.end(); ++iterator) {
381 			loadCastInfo(*(r = _castArchive->getResource(MKTAG('V', 'W', 'C', 'I'), *iterator)), *iterator - _castIDoffset);
382 			delete r;
383 		}
384 	}
385 
386 	Common::Array<uint16> cast = _castArchive->getResourceIDList(MKTAG('C', 'A', 'S', 't'));
387 	if (!_loadedCast)
388 		_loadedCast = new Common::HashMap<int, CastMember *>();
389 
390 	if (cast.size() > 0) {
391 		debugC(2, kDebugLoading, "****** Loading %d CASt resources", cast.size());
392 
393 		for (Common::Array<uint16>::iterator iterator = cast.begin(); iterator != cast.end(); ++iterator) {
394 			Common::SeekableReadStreamEndian *stream = _castArchive->getResource(MKTAG('C', 'A', 'S', 't'), *iterator);
395 			Resource res = _castArchive->getResourceDetail(MKTAG('C', 'A', 'S', 't'), *iterator);
396 			loadCastData(*stream, res.castId, &res);
397 			delete stream;
398 		}
399 	}
400 
401 	// For D4+ we may request to force Lingo scripts and skip precompiled bytecode
402 	if (_version >= kFileVer400 && !debugChannelSet(-1, kDebugNoBytecode)) {
403 		// Try to load script context
404 		Common::Array<uint16> lctx =  _castArchive->getResourceIDList(MKTAG('L','c','t','x'));
405 		if (lctx.size() > 0) {
406 			debugC(2, kDebugLoading, "****** Loading %d Lctx resources", lctx.size());
407 
408 			for (Common::Array<uint16>::iterator iterator = lctx.begin(); iterator != lctx.end(); ++iterator) {
409 				loadLingoContext(*(r = _castArchive->getResource(MKTAG('L','c','t','x'), *iterator)));
410 				delete r;
411 			}
412 		}
413 	}
414 
415 	// PICT resources
416 	if (_castArchive->hasResource(MKTAG('P', 'I', 'C', 'T'), -1)) {
417 		debug("STUB: Unhandled 'PICT' resource");
418 	}
419 
420 	// Film Loop resources
421 	if (_castArchive->hasResource(MKTAG('S', 'C', 'V', 'W'), -1)) {
422 		debug("STUB: Unhandled 'SCVW' resource");
423 	}
424 
425 	// External Cast Reference resources
426 	if (_castArchive->hasResource(MKTAG('S', 'C', 'R', 'F'), -1)) {
427 		debug("STUB: Unhandled 'SCRF' resource");
428 	}
429 
430 	// Score Order List resources
431 	if (_castArchive->hasResource(MKTAG('S', 'o', 'r', 'd'), -1)) {
432 		debug("STUB: Unhandled 'Sord' resource");
433 	}
434 
435 	// Now process STXTs
436 	Common::Array<uint16> stxt = _castArchive->getResourceIDList(MKTAG('S','T','X','T'));
437 	debugC(2, kDebugLoading, "****** Loading %d STXT resources", stxt.size());
438 
439 	_loadedStxts = new Common::HashMap<int, const Stxt *>();
440 
441 	for (Common::Array<uint16>::iterator iterator = stxt.begin(); iterator != stxt.end(); ++iterator) {
442 		_loadedStxts->setVal(*iterator - _castIDoffset,
443 				 new Stxt(this, *(r = _castArchive->getResource(MKTAG('S','T','X','T'), *iterator))));
444 
445 		delete r;
446 
447 		// Try to load movie script, it starts with a comment
448 		if (_version < kFileVer400) {
449 			if (debugChannelSet(-1, kDebugFewFramesOnly))
450 				warning("Compiling STXT %d", *iterator);
451 
452 			loadScriptText(*(r = _castArchive->getResource(MKTAG('S','T','X','T'), *iterator)), *iterator - _castIDoffset);
453 			delete r;
454 		}
455 
456 	}
457 	copyCastStxts();
458 
459 	loadCastChildren();
460 	loadSoundCasts();
461 }
462 
copyCastStxts()463 void Cast::copyCastStxts() {
464 	for (Common::HashMap<int, CastMember *>::iterator c = _loadedCast->begin(); c != _loadedCast->end(); ++c) {
465 		if (c->_value->_type != kCastText && c->_value->_type != kCastButton)
466 			continue;
467 
468 		uint stxtid;
469 		if (_version >= kFileVer400 && c->_value->_children.size() > 0)
470 			stxtid = c->_value->_children[0].index;
471 		else
472 			stxtid = c->_key;
473 
474 		if (_loadedStxts->getVal(stxtid)) {
475 			const Stxt *stxt = _loadedStxts->getVal(stxtid);
476 			TextCastMember *tc = (TextCastMember *)c->_value;
477 
478 			tc->importStxt(stxt);
479 			tc->_size = stxt->_size;
480 		}
481 	}
482 }
483 
loadCastChildren()484 void Cast::loadCastChildren() {
485 	debugC(1, kDebugLoading, "****** Preloading sprite palettes and images");
486 
487 	Cast *sharedCast = _movie ? _movie->getSharedCast() : nullptr;
488 	Common::HashMap<int, PaletteV4>::iterator p = _vm->getLoadedPalettes().find(0);
489 
490 	for (Common::HashMap<int, CastMember *>::iterator c = _loadedCast->begin(); c != _loadedCast->end(); ++c) {
491 		if (!c->_value)
492 			continue;
493 
494 		// First, handle palettes
495 		if (c->_value->_type == kCastPalette) {
496 			PaletteCastMember *member = ((PaletteCastMember *)c->_value);
497 
498 			// TODO: Verify how palettes work in >D4 versions
499 			if (_version >= kFileVer400 && _version < kFileVer500 && member->_children.size() == 1) {
500 				member->_palette = g_director->getPalette(member->_children[0].index);
501 			} else if (_version >= kFileVer300 && _version < kFileVer400) {
502 				// D3 palettes are always kept in this ascending order
503 				member->_palette = g_director->getPalette((++p)->_value.id);
504 			} else if (_version < kFileVer300) {
505 				// for D2, we shall use the castId to get the palette
506 				member->_palette = g_director->getPalette(member->getID());
507 			} else {
508 				warning("Cast::loadSpriteChildren(): Expected 1 child for palette cast, got %d", member->_children.size());
509 			}
510 			continue;
511 		}
512 
513 		if (c->_value->_type != kCastBitmap)
514 			continue;
515 
516 		// Then handle bitmaps
517 		BitmapCastMember *bitmapCast = (BitmapCastMember *)c->_value;
518 		uint32 tag = bitmapCast->_tag;
519 		uint16 imgId = c->_key;
520 		uint16 realId = 0;
521 
522 		Image::ImageDecoder *img = NULL;
523 		Common::SeekableReadStream *pic = NULL;
524 
525 		if (_version >= kFileVer400) {
526 			if (bitmapCast->_children.size() > 0) {
527 				imgId = bitmapCast->_children[0].index;
528 				tag = bitmapCast->_children[0].tag;
529 
530 				if (_castArchive->hasResource(tag, imgId))
531 					pic = _castArchive->getResource(tag, imgId);
532 				else if (sharedCast && sharedCast->getArchive()->hasResource(tag, imgId))
533 					pic = sharedCast->getArchive()->getResource(tag, imgId);
534 			}
535 		} else {
536 			if (_loadedCast->contains(imgId)) {
537 				bitmapCast->_tag = tag = ((BitmapCastMember *)_loadedCast->getVal(imgId))->_tag;
538 				realId = imgId + _castIDoffset;
539 				pic = _castArchive->getResource(tag, realId);
540 			} else if (sharedCast && sharedCast->_loadedCast && sharedCast->_loadedCast->contains(imgId)) {
541 				bitmapCast->_tag = tag = ((BitmapCastMember *)sharedCast->_loadedCast->getVal(imgId))->_tag;
542 				realId = imgId + sharedCast->_castIDoffset;
543 				pic = sharedCast->getArchive()->getResource(tag, realId);
544 			}
545 		}
546 
547 		if (pic == NULL) {
548 			warning("Cast::loadCastChildren(): Bitmap image %d not found", imgId);
549 			continue;
550 		}
551 
552 		int w = bitmapCast->_initialRect.width();
553 		int h = bitmapCast->_initialRect.height();
554 
555 		switch (tag) {
556 		case MKTAG('D', 'I', 'B', ' '):
557 			debugC(2, kDebugLoading, "****** Loading 'DIB ' id: %d (%d), %d bytes", imgId, realId, (int)pic->size());
558 			img = new DIBDecoder();
559 			break;
560 
561 		case MKTAG('B', 'I', 'T', 'D'):
562 			debugC(2, kDebugLoading, "****** Loading 'BITD' id: %d (%d), %d bytes", imgId, realId, (int)pic->size());
563 
564 			if (w > 0 && h > 0) {
565 				if (_version < kFileVer600) {
566 					img = new BITDDecoder(w, h, bitmapCast->_bitsPerPixel, bitmapCast->_pitch, _vm->getPalette(), _version);
567 				} else {
568 					img = new Image::BitmapDecoder();
569 				}
570 			} else {
571 				warning("Cast::loadCastChildren(): Bitmap image %d not found", imgId);
572 			}
573 
574 			break;
575 
576 		default:
577 			warning("Cast::loadCastChildren(): Unknown Bitmap CastMember Tag: [%d] %s", tag, tag2str(tag));
578 			break;
579 		}
580 
581 		if (!img)
582 			continue;
583 
584 		img->loadStream(*pic);
585 
586 		bitmapCast->_img = img;
587 		const Graphics::Surface *surf = img->getSurface();
588 		bitmapCast->_size = surf->pitch * surf->h + img->getPaletteColorCount() * 3;
589 
590 		delete pic;
591 
592 		debugC(4, kDebugImages, "Cast::loadCastChildren(): Bitmap: id: %d, w: %d, h: %d, flags1: %x, flags2: %x bytes: %x, bpp: %d clut: %x", imgId, w, h, bitmapCast->_flags1, bitmapCast->_flags2, bitmapCast->_bytes, bitmapCast->_bitsPerPixel, bitmapCast->_clut);
593 	}
594 }
595 
loadSoundCasts()596 void Cast::loadSoundCasts() {
597 	debugC(1, kDebugLoading, "****** Preloading sound casts");
598 
599 	for (Common::HashMap<int, CastMember *>::iterator c = _loadedCast->begin(); c != _loadedCast->end(); ++c) {
600 		if (!c->_value)
601 			continue;
602 
603 		if (c->_value->_type != kCastSound)
604 			continue;
605 
606 		SoundCastMember *soundCast = (SoundCastMember *)c->_value;
607 		uint32 tag = MKTAG('S', 'N', 'D', ' ');
608 		uint16 sndId = (uint16)(c->_key + _castIDoffset);
609 
610 		if (_version >= kFileVer400 && soundCast->_children.size() > 0) {
611 			sndId = soundCast->_children[0].index;
612 			tag = soundCast->_children[0].tag;
613 		}
614 
615 		Common::SeekableReadStreamEndian *sndData = NULL;
616 
617 		if (!_castArchive->hasResource(tag, sndId)) {
618 			if (_castArchive->hasResource(MKTAG('s', 'n', 'd', ' '), sndId))
619 				tag = MKTAG('s', 'n', 'd', ' ');
620 		}
621 
622 		if (_castArchive->hasResource(tag, sndId)) {
623 			debugC(2, kDebugLoading, "****** Loading '%s' id: %d", tag2str(tag), sndId);
624 			sndData = _castArchive->getResource(tag, sndId);
625 		}
626 
627 		if (sndData != NULL) {
628 			if (sndData->size() == 0) {
629 				// audio file is linked, load from the filesystem
630 				AudioFileDecoder *audio = new AudioFileDecoder(_castsInfo[c->_key]->fileName);
631 				soundCast->_audio = audio;
632 			} else {
633 				SNDDecoder *audio = new SNDDecoder();
634 				audio->loadStream(*sndData);
635 				soundCast->_audio = audio;
636 				soundCast->_size = sndData->size();
637 				if (_version < kFileVer400) {
638 					// The looping flag wasn't added to sound cast members until D4.
639 					// In older versions, always loop sounds that contain a loop start and end.
640 					soundCast->_looping = audio->hasLoopBounds();
641 				}
642 			}
643 			delete sndData;
644 		}
645 	}
646 }
647 
getVideoPath(int castId)648 Common::String Cast::getVideoPath(int castId) {
649 	Common::String res;
650 	CastMember *cast = _loadedCast->getVal(castId);
651 
652 	if (cast->_type != kCastDigitalVideo)
653 		return res;
654 
655 	DigitalVideoCastMember *digitalVideoCast = (DigitalVideoCastMember *)cast;
656 	uint32 tag = MKTAG('M', 'o', 'o', 'V');
657 	uint16 videoId = (uint16)(castId + _castIDoffset);
658 
659 	if (_version >= kFileVer400 && digitalVideoCast->_children.size() > 0) {
660 		videoId = digitalVideoCast->_children[0].index;
661 		tag = digitalVideoCast->_children[0].tag;
662 	}
663 
664 	Common::SeekableReadStreamEndian *videoData = NULL;
665 
666 	switch (tag) {
667 	case MKTAG('M', 'o', 'o', 'V'):
668 		if (_castArchive->hasResource(MKTAG('M', 'o', 'o', 'V'), videoId)) {
669 			debugC(2, kDebugLoading, "****** Loading 'MooV' id: %d", videoId);
670 			videoData = _castArchive->getResource(MKTAG('M', 'o', 'o', 'V'), videoId);
671 		}
672 		break;
673 	}
674 
675 	if (videoData == NULL || videoData->size() == 0) {
676 		// video file is linked, load from the filesystem
677 
678 		Common::String filename = _castsInfo[castId]->fileName;
679 		Common::String directory = _castsInfo[castId]->directory;
680 
681 		res = directory + g_director->_dirSeparator + filename;
682 	} else {
683 		warning("STUB: Cast::getVideoPath(%d): unsupported non-zero MooV block", castId);
684 	}
685 	if (videoData)
686 		delete videoData;
687 
688 	return res;
689 }
690 
loadPalette(Common::SeekableReadStreamEndian & stream)691 PaletteV4 Cast::loadPalette(Common::SeekableReadStreamEndian &stream) {
692 	uint16 steps = stream.size() / 6;
693 	uint16 index = (steps * 3) - 1;
694 	byte *_palette = new byte[index + 1];
695 
696 	debugC(3, kDebugLoading, "Cast::loadPalette(): %d steps, %d bytes", steps, (int)stream.size());
697 
698 	if (steps > 256) {
699 		warning("Cast::loadPalette(): steps > 256: %d", steps);
700 		steps = 256;
701 	}
702 
703 	for (int i = 0; i < steps; i++) {
704 		_palette[index - 2] = stream.readByte();
705 		stream.readByte();
706 
707 		_palette[index - 1] = stream.readByte();
708 		stream.readByte();
709 
710 		_palette[index] = stream.readByte();
711 		stream.readByte();
712 		index -= 3;
713 	}
714 
715 	return PaletteV4(0, _palette, steps);
716 }
717 
loadCastDataVWCR(Common::SeekableReadStreamEndian & stream)718 void Cast::loadCastDataVWCR(Common::SeekableReadStreamEndian &stream) {
719 	debugC(1, kDebugLoading, "****** Loading CastMember rects VWCR. start: %d, end: %d", _castArrayStart, _castArrayEnd);
720 
721 	_loadedCast = new Common::HashMap<int, CastMember *>();
722 
723 	for (uint16 id = _castArrayStart; id <= _castArrayEnd; id++) {
724 		byte size = stream.readByte();
725 		uint32 tag;
726 		if (size == 0)
727 			continue;
728 
729 		if (debugChannelSet(5, kDebugLoading))
730 			stream.hexdump(size);
731 
732 		// these bytes are common but included in cast size
733 		uint8 castType = stream.readByte();
734 		size -= 1;
735 		uint8 flags1 = 0;
736 		if (size) {
737 			flags1 = stream.readByte();
738 			size -= 1;
739 		}
740 
741 		int returnPos = stream.pos() + size;
742 		switch (castType) {
743 		case kCastBitmap:
744 			debugC(3, kDebugLoading, "Cast::loadCastDataVWCR(): CastTypes id: %d(%s) BitmapCastMember", id, numToCastNum(id));
745 			if (_castArchive->hasResource(MKTAG('B', 'I', 'T', 'D'), id + _castIDoffset))
746 				tag = MKTAG('B', 'I', 'T', 'D');
747 			else if (_castArchive->hasResource(MKTAG('D', 'I', 'B', ' '), id + _castIDoffset))
748 				tag = MKTAG('D', 'I', 'B', ' ');
749 			else
750 				error("Cast::loadCastDataVWCR(): non-existent reference to BitmapCastMember");
751 
752 			_loadedCast->setVal(id, new BitmapCastMember(this, id, stream, tag, _version, flags1));
753 			break;
754 		case kCastText:
755 			debugC(3, kDebugLoading, "Cast::loadCastDataVWCR(): CastTypes id: %d(%s) TextCastMember", id, numToCastNum(id));
756 			_loadedCast->setVal(id, new TextCastMember(this, id, stream, _version, flags1));
757 			break;
758 		case kCastShape:
759 			debugC(3, kDebugLoading, "Cast::loadCastDataVWCR(): CastTypes id: %d(%s) ShapeCastMember", id, numToCastNum(id));
760 			_loadedCast->setVal(id, new ShapeCastMember(this, id, stream, _version));
761 			break;
762 		case kCastButton:
763 			debugC(3, kDebugLoading, "Cast::loadCastDataVWCR(): CastTypes id: %d(%s) ButtonCast", id, numToCastNum(id));
764 			_loadedCast->setVal(id, new TextCastMember(this, id, stream, _version, flags1, true));
765 			break;
766 		case kCastSound:
767 			debugC(3, kDebugLoading, "Cast::loadCastDataVWCR(): CastTypes id: %d(%s) SoundCastMember", id, numToCastNum(id));
768 			_loadedCast->setVal(id, new SoundCastMember(this, id, stream, _version));
769 			break;
770 		case kCastDigitalVideo:
771 			debugC(3, kDebugLoading, "Cast::loadCastDataVWCR(): CastTypes id: %d(%s) DigitalVideoCastMember", id, numToCastNum(id));
772 			_loadedCast->setVal(id, new DigitalVideoCastMember(this, id, stream, _version));
773 			break;
774 		case kCastPalette:
775 			debugC(3, kDebugLoading, "Cast::loadCastDataVWCR(): CastTypes id: %d(%s) PaletteCastMember", id, numToCastNum(id));
776 			_loadedCast->setVal(id, new PaletteCastMember(this, id, stream, _version));
777 			break;
778 		default:
779 			warning("Cast::loadCastDataVWCR(): Unhandled cast id: %d(%s), type: %d, %d bytes", id, numToCastNum(id), castType, size);
780 			break;
781 		}
782 		stream.seek(returnPos);
783 	}
784 }
785 
loadExternalSound(Common::SeekableReadStreamEndian & stream)786 void Cast::loadExternalSound(Common::SeekableReadStreamEndian &stream) {
787 	Common::String str = stream.readString();
788 	str.trim();
789 	debugC(1, kDebugLoading, "****** Loading External Sound File %s", str.c_str());
790 
791 	Common::String resPath = g_director->getCurrentPath() + str;
792 
793 	if (!g_director->_openResFiles.contains(resPath)) {
794 		MacArchive *resFile = new MacArchive();
795 
796 		if (resFile->openFile(resPath)) {
797 			g_director->_openResFiles.setVal(resPath, resFile);
798 		} else {
799 			delete resFile;
800 		}
801 	}
802 }
803 
readEditInfo(EditInfo * info,Common::ReadStreamEndian * stream)804 static void readEditInfo(EditInfo *info, Common::ReadStreamEndian *stream) {
805 	info->rect = Movie::readRect(*stream);
806 	info->selStart = stream->readUint32();
807 	info->selEnd = stream->readUint32();
808 	info->version = stream->readByte();
809 	info->rulerFlag = stream->readByte();
810 
811 	if (debugChannelSet(3, kDebugLoading)) {
812 		info->rect.debugPrint(0, "EditInfo: ");
813 		debug("selStart: %d  selEnd: %d  version: %d  rulerFlag: %d", info->selStart,info->selEnd, info->version, info->rulerFlag);
814 	}
815 }
816 
loadCastData(Common::SeekableReadStreamEndian & stream,uint16 id,Resource * res)817 void Cast::loadCastData(Common::SeekableReadStreamEndian &stream, uint16 id, Resource *res) {
818 	// IDs are stored as relative to the start of the cast array.
819 	id += _castArrayStart;
820 
821 	// D4+ variant
822 	if (stream.size() == 0)
823 		return;
824 
825 	// TODO: Determine if there really is a minimum size.
826 	// This value was too small for Shape Casts.
827 	if (stream.size() < 10) {
828 		warning("Cast::loadCastData(): CASt data id %d is too small", id);
829 		return;
830 	}
831 
832 	debugC(3, kDebugLoading, "Cast::loadCastData(): CASt: id: %d", id);
833 
834 	if (debugChannelSet(5, kDebugLoading) && stream.size() < 2048)
835 		stream.hexdump(stream.size());
836 
837 	uint32 castSize, castInfoSize, size3, castType, castSizeToRead;
838 	byte flags1 = 0, unk1 = 0, unk2 = 0, unk3 = 0;
839 
840 	// D2-3 cast members should be loaded in loadCastDataVWCR
841 #if 0
842 	if (_version < kFileVer400) {
843 		size1 = stream.readUint16();
844 		sizeToRead = size1 +16; // 16 is for bounding rects
845 		size2 = stream.readUint32();
846 		size3 = 0;
847 		castType = stream.readByte();
848 		unk1 = stream.readByte();
849 		unk2 = stream.readByte();
850 		unk3 = stream.readByte();
851 	}
852 #endif
853 
854 	if (_version >= kFileVer400 && _version < kFileVer500) {
855 		castSize = stream.readUint16();
856 		castSizeToRead = castSize;
857 		castInfoSize = stream.readUint32();
858 		size3 = 0;
859 
860 		// these bytes are common but included in cast size
861 		castType = stream.readByte();
862 		castSizeToRead -= 1;
863 		if (castSizeToRead) {
864 			flags1 = stream.readByte();
865 			castSizeToRead -= 1;
866 		}
867 	} else if (_version >= kFileVer500 && _version < kFileVer600) {
868 		castType = stream.readUint32();
869 		size3 = stream.readUint32();
870 		castInfoSize = stream.readUint32();
871 		castSize = stream.readUint32();
872 		if (castType == 1) {
873 			if (size3 == 0)
874 				return;
875 			for (uint32 skip = 0; skip < (castSize - 4) / 4; skip++)
876 				stream.readUint32();
877 		}
878 
879 		castSizeToRead = stream.size();
880 	} else {
881 		error("Cast::loadCastData: unsupported Director version (%d)", _version);
882 	}
883 
884 	debugC(3, kDebugLoading, "Cast::loadCastData(): CASt: id: %d type: %x castSize: %d castInfoSize: %d (%x) size3: %d unk1: %d unk2: %d unk3: %d",
885 		id, castType, castSize, castInfoSize, castInfoSize, size3, unk1, unk2, unk3);
886 
887 	// read the cast member itself
888 
889 	byte *data = (byte *)calloc(castSizeToRead, 1);
890 	stream.read(data, castSizeToRead);
891 
892 	Common::MemoryReadStreamEndian castStream(data, castSizeToRead, stream.isBE());
893 
894 	switch (castType) {
895 	case kCastBitmap:
896 		debugC(3, kDebugLoading, "Cast::loadCastData(): loading kCastBitmap (%d children)", res->children.size());
897 		_loadedCast->setVal(id, new BitmapCastMember(this, id, castStream, res->tag, _version, flags1));
898 		break;
899 	case kCastSound:
900 		debugC(3, kDebugLoading, "Cast::loadCastData(): loading kCastSound (%d children)", res->children.size());
901 		_loadedCast->setVal(id, new SoundCastMember(this, id, castStream, _version));
902 		break;
903 	case kCastText:
904 		debugC(3, kDebugLoading, "Cast::loadCastData(): loading kCastText (%d children)", res->children.size());
905 		_loadedCast->setVal(id, new TextCastMember(this, id, castStream, _version, flags1));
906 		break;
907 	case kCastShape:
908 		debugC(3, kDebugLoading, "Cast::loadCastData(): loading kCastShape (%d children)", res->children.size());
909 		_loadedCast->setVal(id, new ShapeCastMember(this, id, castStream, _version));
910 		break;
911 	case kCastButton:
912 		debugC(3, kDebugLoading, "Cast::loadCastData(): loading kCastButton (%d children)", res->children.size());
913 		_loadedCast->setVal(id, new TextCastMember(this, id, castStream, _version, flags1, true));
914 		break;
915 	case kCastLingoScript:
916 		debugC(3, kDebugLoading, "Cast::loadCastData(): loading kCastLingoScript");
917 		_loadedCast->setVal(id, new ScriptCastMember(this, id, castStream, _version));
918 		break;
919 	case kCastRTE:
920 		debugC(3, kDebugLoading, "Cast::loadCastData(): loading kCastRTE (%d children)", res->children.size());
921 		_loadedCast->setVal(id, new RTECastMember(this, id, castStream, _version));
922 		break;
923 	case kCastDigitalVideo:
924 		debugC(3, kDebugLoading, "Cast::loadCastData(): loading kCastDigitalVideo (%d children)", res->children.size());
925 		_loadedCast->setVal(id, new DigitalVideoCastMember(this, id, castStream, _version));
926 		break;
927 	case kCastFilmLoop:
928 		warning("STUB: Cast::loadCastData(): kCastFilmLoop (%d children)", res->children.size());
929 		castInfoSize = 0;
930 		break;
931 	case kCastPalette:
932 		debugC(3, kDebugLoading, "Cast::loadCastData(): loading kCastPalette (%d children)", res->children.size());
933 		_loadedCast->setVal(id, new PaletteCastMember(this, id, castStream, _version));
934 		break;
935 	case kCastPicture:
936 		warning("BUILDBOT: STUB: Cast::loadCastData(): kCastPicture (%d children)", res->children.size());
937 		castInfoSize = 0;
938 		break;
939 	case kCastMovie:
940 		warning("STUB: Cast::loadCastData(): kCastMovie (%d children)", res->children.size());
941 		castInfoSize = 0;
942 		break;
943 	default:
944 		warning("Cast::loadCastData(): Unhandled cast type: %d [%s] (%d children)", castType, tag2str(castType), res->children.size());
945 		// also don't try and read the strings... we don't know what this item is.
946 		castInfoSize = 0;
947 		break;
948 	}
949 
950 	if (_loadedCast->contains(id)) { // Skip unhandled casts
951 		debugCN(3, kDebugLoading, "Children: ");
952 		for (uint child = 0; child < res->children.size(); child++) {
953 			debugCN(3, kDebugLoading, "%d ", res->children[child].index);
954 			_loadedCast->getVal(id)->_children.push_back(res->children[child]);
955 		}
956 		debugCN(3, kDebugLoading, "\n");
957 	}
958 
959 	free(data);
960 
961 	// read the cast member info
962 
963 	if (castInfoSize && _version < kFileVer500) {
964 		loadCastInfo(stream, id);
965 	}
966 
967 	if (size3)
968 		warning("Cast::loadCastData(): size3: %x", size3);
969 }
970 
971 struct LingoContextEntry {
972 	int32 index;
973 	int16 nextUnused;
974 	bool unused;
975 
976 	LingoContextEntry(int32 i, int16 n);
977 };
978 
LingoContextEntry(int32 i,int16 n)979 LingoContextEntry::LingoContextEntry(int32 i, int16 n)
980 	: index(i), nextUnused(n), unused(false) {}
981 
loadLingoContext(Common::SeekableReadStreamEndian & stream)982 void Cast::loadLingoContext(Common::SeekableReadStreamEndian &stream) {
983 	if (_version >= kFileVer400) {
984 		debugC(1, kDebugCompile, "Add V4 script context");
985 
986 		if (debugChannelSet(5, kDebugLoading)) {
987 			debugC(5, kDebugLoading, "Lctx header:");
988 			stream.hexdump(0x2a);
989 		}
990 
991 		stream.readUint16();
992 		stream.readUint16();
993 		stream.readUint16();
994 		stream.readUint16();
995 		int32 itemCount = stream.readSint32();
996 		/* int32 itemCount2 = */ stream.readSint32();
997 		uint16 itemsOffset = stream.readUint16();
998 		/* uint16 entrySize = */ stream.readUint16();
999 		/* uint32 unk1 = */ stream.readUint32();
1000 		/* uint32 fileType = */ stream.readUint32();
1001 		/* uint32 unk2 = */ stream.readUint32();
1002 		int32 nameTableId = stream.readSint32();
1003 		/* int16 validCount = */ stream.readSint16();
1004 		/* uint16 flags = */ stream.readUint16();
1005 		int16 firstUnused = stream.readSint16();
1006 
1007 		Common::SeekableReadStreamEndian *r;
1008 		debugC(2, kDebugLoading, "****** Loading Lnam resource (%d)", nameTableId);
1009 		_lingoArchive->addNamesV4(*(r = _castArchive->getResource(MKTAG('L','n','a','m'), nameTableId)));
1010 		delete r;
1011 
1012 		Common::Array<LingoContextEntry> entries;
1013 		stream.seek(itemsOffset);
1014 		for (int16 i = 1; i <= itemCount; i++) {
1015 			if (debugChannelSet(5, kDebugLoading)) {
1016 				debugC(5, kDebugLoading, "Context entry %d:", i);
1017 				stream.hexdump(0xc);
1018 			}
1019 
1020 			stream.readUint32();
1021 			int32 index = stream.readSint32();
1022 			/* uint16 entryFlags = */ stream.readUint16();
1023 			int16 nextUnused = stream.readSint16();
1024 			entries.push_back(LingoContextEntry(index, nextUnused));
1025 		}
1026 
1027 		// mark unused entries
1028 		int16 nextUnused = firstUnused ;
1029 		while (0 <= nextUnused && nextUnused < (int16)entries.size()) {
1030 			LingoContextEntry &entry = entries[nextUnused];
1031 			entry.unused = true;
1032 			nextUnused = entry.nextUnused;
1033 		}
1034 
1035 		// compile scripts
1036 		for (int16 i = 1; i <= (int16)entries.size(); i++) {
1037 			LingoContextEntry &entry = entries[i - 1];
1038 			if (entry.unused && entry.index < 0) {
1039 				debugC(1, kDebugCompile, "Cast::loadLingoContext: Script %d is unused and empty", i);
1040 				continue;
1041 			}
1042 			if (entry.unused) {
1043 				debugC(1, kDebugCompile, "Cast::loadLingoContext: Script %d is unused but not empty", i);
1044 				continue;
1045 			}
1046 			if (entry.index < 0) {
1047 				debugC(1, kDebugCompile, "Cast::loadLingoContext: Script %d is used but empty", i);
1048 				continue;
1049 			}
1050 			_lingoArchive->addCodeV4(*(r = _castArchive->getResource(MKTAG('L', 's', 'c', 'r'), entry.index)), i, _macName, _version);
1051 			delete r;
1052 		}
1053 
1054 		// actually define scripts
1055 		for (ScriptContextHash::iterator it = _lingoArchive->lctxContexts.begin(); it != _lingoArchive->lctxContexts.end(); ++it) {
1056 			ScriptContext *script = it->_value;
1057 			if (script->_id >= 0 && !script->isFactory()) {
1058 				if (_lingoArchive->getScriptContext(script->_scriptType, script->_id)) {
1059 					error("Cast::loadLingoContext: Script already defined for type %s, id %d", scriptType2str(script->_scriptType), script->_id);
1060 				}
1061 				_lingoArchive->scriptContexts[script->_scriptType][script->_id] = script;
1062 			}
1063 		}
1064 	} else {
1065 		error("Cast::loadLingoContext: unsuported Director version (%d)", _version);
1066 	}
1067 }
1068 
loadScriptText(Common::SeekableReadStreamEndian & stream,uint16 id)1069 void Cast::loadScriptText(Common::SeekableReadStreamEndian &stream, uint16 id) {
1070 	/*uint32 unk1 = */ stream.readUint32();
1071 	uint32 strLen = stream.readUint32();
1072 	/*uin32 dataLen = */ stream.readUint32();
1073 	Common::String script = stream.readString(0, strLen);
1074 
1075 	// Check if this is a script. It must start with a comment.
1076 	// See D2 Interactivity Manual pp.46-47 (Ch.2.11. Using a macro)
1077 	if (script.empty() || !script.hasPrefix("--"))
1078 		return;
1079 
1080 	if (ConfMan.getBool("dump_scripts"))
1081 		dumpScript(script.c_str(), kMovieScript, id);
1082 
1083 	if (script.contains("\nmenu:") || script.hasPrefix("menu:"))
1084 		return;
1085 
1086 	_lingoArchive->addCode(script.decode(Common::kMacRoman), kMovieScript, id);
1087 }
1088 
dumpScript(const char * script,ScriptType type,uint16 id)1089 void Cast::dumpScript(const char *script, ScriptType type, uint16 id) {
1090 	Common::DumpFile out;
1091 	Common::String buf = dumpScriptName(encodePathForDump(_macName).c_str(), type, id, "txt");
1092 
1093 	if (!out.open(buf, true)) {
1094 		warning("Cast::dumpScript(): Can not open dump file %s", buf.c_str());
1095 		return;
1096 	}
1097 
1098 	uint len = strlen(script);
1099 	char *scriptCopy = (char *)malloc(len + 1);
1100 	Common::strlcpy(scriptCopy, script, len + 1);
1101 
1102 	for (uint i = 0; i < len; i++)
1103 		if (scriptCopy[i] == '\r' && scriptCopy[i + 1] != '\n') // It is safe to check [i + 1], as '\0' != '\n'
1104 			scriptCopy[i] = '\n';
1105 
1106 	out.write(scriptCopy, len);
1107 
1108 	out.flush();
1109 	out.close();
1110 
1111 	free(scriptCopy);
1112 
1113 }
1114 
loadCastInfo(Common::SeekableReadStreamEndian & stream,uint16 id)1115 void Cast::loadCastInfo(Common::SeekableReadStreamEndian &stream, uint16 id) {
1116 	if (!_loadedCast->contains(id))
1117 		return;
1118 
1119 	InfoEntries castInfo = Movie::loadInfoEntries(stream, _version);
1120 
1121 	debugCN(4, kDebugLoading, "Cast::loadCastInfo(): castId: %s str(%d): '", numToCastNum(id), castInfo.strings.size());
1122 
1123 	for (uint i = 0; i < castInfo.strings.size(); i++) {
1124 		debugCN(4, kDebugLoading, "%s'", utf8ToPrintable(castInfo.strings[i].readString()).c_str());
1125 		if (i != castInfo.strings.size() - 1)
1126 			debugCN(4, kDebugLoading, ", '");
1127 	}
1128 	debugC(4, kDebugLoading, "'");
1129 
1130 	CastMemberInfo *ci = new CastMemberInfo();
1131 	Common::MemoryReadStreamEndian *entryStream;
1132 
1133 	// We have here variable number of strings. Thus, instead of
1134 	// adding tons of ifs, we use this switch()
1135 	switch (castInfo.strings.size()) {
1136 	default:
1137 		warning("Cast::loadCastInfo(): BUILDBOT: extra %d strings", castInfo.strings.size() - 8);
1138 		// fallthrough
1139 	case 8:
1140 		if (castInfo.strings[7].len) {
1141 			entryStream = new Common::MemoryReadStreamEndian(castInfo.strings[7].data, castInfo.strings[7].len, stream.isBE());
1142 			readEditInfo(&ci->textEditInfo, entryStream);
1143 			delete entryStream;
1144 		}
1145 		// fallthrough
1146 	case 7:
1147 		if (castInfo.strings[6].len) {
1148 			entryStream = new Common::MemoryReadStreamEndian(castInfo.strings[6].data, castInfo.strings[6].len, stream.isBE());
1149 
1150 			int16 count = entryStream->readUint16();
1151 
1152 			for (int16 i = 0; i < count; i++)
1153 				ci->scriptStyle.read(*entryStream, this);
1154 			delete entryStream;
1155 		}
1156 		// fallthrough
1157 	case 6:
1158 		if (castInfo.strings[5].len) {
1159 			entryStream = new Common::MemoryReadStreamEndian(castInfo.strings[5].data, castInfo.strings[5].len, stream.isBE());
1160 			readEditInfo(&ci->scriptEditInfo, entryStream);
1161 			delete entryStream;
1162 		}
1163 		// fallthrough
1164 	case 5:
1165 		ci->type = castInfo.strings[4].readString();
1166 		// fallthrough
1167 	case 4:
1168 		ci->fileName = castInfo.strings[3].readString();
1169 		// fallthrough
1170 	case 3:
1171 		ci->directory = castInfo.strings[2].readString();
1172 		// fallthrough
1173 	case 2:
1174 		ci->name = castInfo.strings[1].readString();
1175 
1176 		if (!ci->name.empty()) {
1177 			_castsNames[ci->name] = id;
1178 		}
1179 		// fallthrough
1180 	case 1:
1181 		ci->script = castInfo.strings[0].readString(false);
1182 		// fallthrough
1183 	case 0:
1184 		break;
1185 	}
1186 
1187 	CastMember *member = _loadedCast->getVal(id);
1188 	// For D4+ we may force Lingo scripts
1189 	if (_version < kFileVer400 || debugChannelSet(-1, kDebugNoBytecode)) {
1190 		if (!ci->script.empty()) {
1191 			ScriptType scriptType = kCastScript;
1192 			// the script type here could be wrong!
1193 			if (member->_type == kCastLingoScript) {
1194 				scriptType = ((ScriptCastMember *)member)->_scriptType;
1195 			}
1196 
1197 			if (ConfMan.getBool("dump_scripts"))
1198 				dumpScript(ci->script.c_str(), scriptType, id);
1199 
1200 			_lingoArchive->addCode(ci->script, scriptType, id, ci->name.c_str());
1201 		}
1202 	}
1203 
1204 	// For SoundCastMember, read the flags in the CastInfo
1205 	if (_version >= kFileVer400 && _version < kFileVer500 && member->_type == kCastSound) {
1206 		((SoundCastMember *)member)->_looping = castInfo.flags & 16 ? 0 : 1;
1207 	}
1208 
1209 	ci->autoHilite = castInfo.flags & 2;
1210 	ci->scriptId = castInfo.scriptId;
1211 	if (ci->scriptId != 0)
1212 		_castsScriptIds[ci->scriptId] = id;
1213 
1214 	_castsInfo[id] = ci;
1215 }
1216 
getPlatformEncoding()1217 Common::CodePage Cast::getPlatformEncoding() {
1218 	return getEncoding(_platform, _vm->getLanguage());
1219 }
1220 
decodeString(const Common::String & str)1221 Common::U32String Cast::decodeString(const Common::String &str) {
1222 	Common::CodePage encoding = getPlatformEncoding();
1223 
1224 	Common::String fixedStr;
1225 	if (encoding == Common::kWindows1252) {
1226 		/**
1227 		 * Director for Windows stores strings in a screwed up version of Mac Roman
1228 		 * where characters map directly to Windows-1252 characters.
1229 		 * We need to map this screwed up Mac Roman back to Windows-1252 before using it.
1230 		 * Comment from FXmp:
1231 		 *   Note: Some characters are not available in both character sets.
1232 		 *   However, the bi-directional mapping table below preserves these
1233 		 *   characters even if they are mapped to a different platform and
1234 		 *   later re-mapped back to the original platform.
1235 		 */
1236 
1237 		for (uint i = 0; i < str.size(); i++) {
1238 			if (_macCharsToWin.contains(str[i]))
1239 				fixedStr += _macCharsToWin[str[i]];
1240 			else
1241 				fixedStr += str[i];
1242 		}
1243 	} else {
1244 		fixedStr = str;
1245 	}
1246 
1247 	return fixedStr.decode(encoding);
1248 }
1249 
1250 } // End of namespace Director
1251