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