769 lines
23 KiB
C++
769 lines
23 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "common/config-manager.h"
|
|
#include "common/memstream.h"
|
|
#include "common/substream.h"
|
|
|
|
#include "director/types.h"
|
|
#include "engines/util.h"
|
|
|
|
#include "graphics/macgui/macwindowmanager.h"
|
|
|
|
#include "director/director.h"
|
|
#include "director/archive.h"
|
|
#include "director/cast.h"
|
|
#include "director/movie.h"
|
|
#include "director/score.h"
|
|
#include "director/window.h"
|
|
#include "director/castmember/castmember.h"
|
|
|
|
namespace Director {
|
|
|
|
#include "director/blank-score.h"
|
|
|
|
Movie::Movie(Window *window) {
|
|
_window = window;
|
|
_vm = _window->getVM();
|
|
_lingo = _vm->getLingo();
|
|
|
|
_flags = 0;
|
|
_stageColor = _vm->_wm->_colorWhite;
|
|
|
|
_lastClickedSpriteId = 0;
|
|
_currentSpriteNum = 0;
|
|
_currentEditableTextChannel = 0;
|
|
_lastEventTime = _vm->getMacTicks();
|
|
_lastKeyTime = _lastEventTime;
|
|
_lastClickTime = _lastEventTime;
|
|
_lastClickTime2 = 0;
|
|
_lastRollTime = _lastEventTime;
|
|
_lastTimerReset = _lastEventTime;
|
|
_nextEventId = 0;
|
|
|
|
_videoPlayback = false;
|
|
|
|
_key = 0;
|
|
_keyCode = 0;
|
|
_keyFlags = 0;
|
|
|
|
_currentDraggedChannel = nullptr;
|
|
_currentHiliteChannelId = 0;
|
|
_mouseDownWasInButton = false;
|
|
_lastEnteredChannelId = 0;
|
|
_currentHoveredSpriteId = 0;
|
|
|
|
_version = 0;
|
|
_platform = Common::kPlatformMacintosh;
|
|
_allowOutdatedLingo = false;
|
|
_remapPalettesWhenNeeded = false;
|
|
|
|
_movieArchive = nullptr;
|
|
|
|
_cast = new Cast(this, DEFAULT_CAST_LIB);
|
|
_casts.setVal(_cast->_castLibID, _cast);
|
|
_sharedCast = nullptr;
|
|
_score = new Score(this);
|
|
|
|
_selEnd = -1;
|
|
_selStart = -1;
|
|
|
|
_checkBoxType = 0;
|
|
_checkBoxAccess = 0;
|
|
|
|
_lastTimeOut = _lastEventTime;
|
|
_timeOutLength = 10800; // D4 dictionary p297, default value is 3minutes
|
|
// default value of keydown and mouse is true, for timeOutPlay is false. check D4 dictionary p297
|
|
_timeOutKeyDown = true;
|
|
_timeOutMouse = true;
|
|
_timeOutPlay = false;
|
|
|
|
_isBeepOn = false; // Beep is off by default in the original
|
|
}
|
|
|
|
Movie::~Movie() {
|
|
if (_sharedCast && _sharedCast->getArchive()) {
|
|
debug(0, "@@ Clearing shared cast '%s'", _sharedCast->getArchive()->getPathName().toString().c_str());
|
|
|
|
g_director->_allOpenResFiles.remove(_sharedCast->getArchive()->getPathName());
|
|
}
|
|
|
|
if (_cast && _cast->getArchive()) {
|
|
debug(0, "@@ Clearing movie cast '%s'", _cast->getArchive()->getPathName().toString().c_str());
|
|
g_director->_allOpenResFiles.remove(_cast->getArchive()->getPathName());
|
|
}
|
|
|
|
delete _cast;
|
|
delete _sharedCast;
|
|
delete _score;
|
|
}
|
|
|
|
void Movie::setArchive(Archive *archive) {
|
|
_movieArchive = archive;
|
|
|
|
if (archive->hasResource(MKTAG('M', 'C', 'N', 'M'), 0)) {
|
|
_macName = archive->getName(MKTAG('M', 'C', 'N', 'M'), 0).c_str();
|
|
} else {
|
|
_macName = archive->getFileName();
|
|
}
|
|
|
|
Common::SeekableReadStreamEndian *r = nullptr;
|
|
if ((r = archive->getMovieResourceIfPresent(MKTAG('M', 'C', 's', 'L'))) != nullptr) {
|
|
// D5 archive, can contain multiple internal/external casts
|
|
loadCastLibMapping(*r);
|
|
delete r;
|
|
} else {
|
|
// D4 or lower, only 1 cast
|
|
_cast->setArchive(archive);
|
|
}
|
|
// Frame Labels
|
|
if ((r = archive->getMovieResourceIfPresent(MKTAG('V', 'W', 'L', 'B')))) {
|
|
_score->loadLabels(*r);
|
|
delete r;
|
|
}
|
|
}
|
|
|
|
void Movie::loadCastLibMapping(Common::SeekableReadStreamEndian &stream) {
|
|
debugC(5, kDebugLoading, "Movie::loadCastLibMapping: loading cast libraries");
|
|
if (debugChannelSet(8, kDebugLoading)) {
|
|
stream.hexdump(stream.size());
|
|
}
|
|
stream.readUint32(); // header size
|
|
uint32 count = stream.readUint32();
|
|
stream.readUint16();
|
|
uint32 unkCount = stream.readUint32() + 1;
|
|
for (uint32 i = 0; i < unkCount; i++) {
|
|
stream.readUint32();
|
|
}
|
|
for (uint32 i = 0; i < count; i++) {
|
|
int nameSize = stream.readByte();
|
|
Common::String name = stream.readString('\0', nameSize);
|
|
stream.readByte(); // null
|
|
int pathSize = stream.readByte();
|
|
Common::String path = stream.readString('\0', pathSize);
|
|
stream.readByte(); // null
|
|
if (pathSize > 1)
|
|
stream.readUint16();
|
|
stream.readUint16();
|
|
uint16 itemCount = stream.readUint16();
|
|
stream.readUint16();
|
|
uint16 libResourceId = stream.readUint16();
|
|
uint16 libId = i + 1;
|
|
debugC(5, kDebugLoading, "Movie::loadCastLibMapping: name: %s, path: %s, itemCount: %d, libResourceId: %d, libId: %d", utf8ToPrintable(name).c_str(), utf8ToPrintable(path).c_str(), itemCount, libResourceId, libId);
|
|
Archive *castArchive = _movieArchive;
|
|
bool isExternal = !path.empty();
|
|
if (isExternal) {
|
|
Common::Path archivePath = findMoviePath(path);
|
|
castArchive = loadExternalCastFrom(archivePath);
|
|
if (!castArchive) {
|
|
continue; // couldn't load external cast
|
|
}
|
|
}
|
|
|
|
Cast *cast = nullptr;
|
|
if (_casts.contains(libId)) {
|
|
cast = _casts.getVal(libId);
|
|
} else {
|
|
cast = new Cast(this, libId, false, isExternal, libResourceId);
|
|
cast->setCastName(name);
|
|
_casts.setVal(libId, cast);
|
|
}
|
|
_castNames[name] = libId;
|
|
cast->setArchive(castArchive);
|
|
}
|
|
return;
|
|
}
|
|
|
|
bool Movie::loadArchive() {
|
|
Common::SeekableReadStreamEndian *r = nullptr;
|
|
|
|
// Config
|
|
if (!_cast->loadConfig())
|
|
return false;
|
|
|
|
_version = _cast->_version;
|
|
_platform = _cast->_platform;
|
|
_movieRect = _cast->_movieRect;
|
|
_score->_currentFrameRate = _cast->_frameRate;
|
|
_stageColor = _vm->transformColor(_cast->_stageColor);
|
|
// Wait to handle _stageColor until palette is loaded in loadCast...
|
|
|
|
// File Info
|
|
if ((r = _movieArchive->getMovieResourceIfPresent(MKTAG('V', 'W', 'F', 'I'))) != nullptr) {
|
|
loadFileInfo(*r);
|
|
delete r;
|
|
}
|
|
|
|
// Cast
|
|
for (auto &it : _casts) {
|
|
if (it._value != _cast)
|
|
it._value->loadConfig();
|
|
it._value->loadCast();
|
|
}
|
|
_stageColor = _vm->transformColor(_cast->_stageColor);
|
|
// Need to check if the default palette is valid; if not, assume it's the Mac one.
|
|
if (g_director->hasPalette(_cast->_defaultPalette)) {
|
|
_defaultPalette = _cast->_defaultPalette;
|
|
} else {
|
|
_defaultPalette = CastMemberID(kClutSystemMac, -1);
|
|
}
|
|
g_director->_lastPalette = CastMemberID();
|
|
|
|
bool recenter = false;
|
|
// If the stage dimensions are different, delete it and start again.
|
|
// Otherwise, do not clear it so there can be a nice transition.
|
|
if (_window->getSurface()->w != _movieRect.width() || _window->getSurface()->h != _movieRect.height()) {
|
|
_window->resizeInner(_movieRect.width(), _movieRect.height());
|
|
recenter = true;
|
|
}
|
|
|
|
// TODO: Add more options for desktop dimensions
|
|
if (_window == _vm->getStage()) {
|
|
uint16 windowWidth = g_director->desktopEnabled() ? g_director->_wmWidth : _movieRect.width();
|
|
uint16 windowHeight = g_director->desktopEnabled() ? g_director->_wmHeight : _movieRect.height();
|
|
if (_vm->_wm->_screenDims.width() != windowWidth || _vm->_wm->_screenDims.height() != windowHeight) {
|
|
_vm->_wm->resizeScreen(windowWidth, windowHeight);
|
|
recenter = true;
|
|
|
|
initGraphics(windowWidth, windowHeight, &_vm->_pixelformat);
|
|
}
|
|
}
|
|
|
|
if (recenter && g_director->desktopEnabled())
|
|
_window->center(g_director->_centerStage);
|
|
|
|
_window->setStageColor(_stageColor, true);
|
|
|
|
// Score
|
|
if (!(r = _movieArchive->getMovieResourceIfPresent(MKTAG('V', 'W', 'S', 'C')))) {
|
|
warning("Movie::loadArchive(): No VWSC resource, injecting a blank score with 1 frame");
|
|
if (_version < kFileVer400) {
|
|
r = new Common::MemoryReadStreamEndian(kBlankScoreD2, sizeof(kBlankScoreD2), true);
|
|
} else if (_version < kFileVer600) {
|
|
r = new Common::MemoryReadStreamEndian(kBlankScoreD4, sizeof(kBlankScoreD4), true);
|
|
} else {
|
|
error("Movie::loadArchive(): score format not yet supported for version v%d (%d)", humanVersion(_version), _version);
|
|
}
|
|
}
|
|
|
|
_score->loadFrames(*r, _version);
|
|
delete r;
|
|
|
|
// Action list
|
|
if ((r = _movieArchive->getMovieResourceIfPresent(MKTAG('V', 'W', 'A', 'C'))) != nullptr) {
|
|
_score->loadActions(*r);
|
|
delete r;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
Common::Rect Movie::readRect(Common::ReadStreamEndian &stream) {
|
|
Common::Rect rect;
|
|
rect.top = stream.readSint16();
|
|
rect.left = stream.readSint16();
|
|
rect.bottom = stream.readSint16();
|
|
rect.right = stream.readSint16();
|
|
|
|
return rect;
|
|
}
|
|
|
|
void Movie::writeRect(Common::WriteStream *writeStream, Common::Rect rect) {
|
|
writeStream->writeSint16BE(rect.top);
|
|
writeStream->writeSint16BE(rect.left);
|
|
writeStream->writeSint16BE(rect.bottom);
|
|
writeStream->writeSint16BE(rect.right);
|
|
}
|
|
|
|
InfoEntries Movie::loadInfoEntries(Common::SeekableReadStreamEndian &stream, uint16 version) {
|
|
uint32 offset = stream.pos();
|
|
offset += stream.readUint32();
|
|
|
|
InfoEntries res;
|
|
res.unk1 = stream.readUint32();
|
|
res.unk2 = stream.readUint32();
|
|
res.flags = stream.readUint32();
|
|
|
|
if (version >= kFileVer400)
|
|
res.scriptId = stream.readUint32();
|
|
|
|
stream.seek(offset);
|
|
uint16 count = stream.readUint16();
|
|
|
|
debugC(3, kDebugLoading, "Movie::loadInfoEntries(): InfoEntry: %d entries, unk1: 0x%08x, unk2: 0x%08x flags: 0x%08x", count, res.unk1, res.unk2, res.flags);
|
|
|
|
if (count == 0)
|
|
return res;
|
|
|
|
uint32 *entries = (uint32 *)calloc(count + 1, sizeof(uint32));
|
|
|
|
for (int i = 0; i < count + 1; i++)
|
|
entries[i] = stream.readUint32();
|
|
|
|
res.strings.resize(count);
|
|
|
|
for (uint16 i = 0; i < count; i++) {
|
|
res.strings[i].len = entries[i + 1] - entries[i];
|
|
res.strings[i].data = (byte *)malloc(res.strings[i].len);
|
|
stream.read(res.strings[i].data, res.strings[i].len);
|
|
|
|
debugC(6, kDebugLoading, " InfoEntry %d: %d bytes", i, res.strings[i].len);
|
|
}
|
|
|
|
free(entries);
|
|
|
|
return res;
|
|
}
|
|
|
|
void Movie::saveInfoEntries(Common::SeekableWriteStream *writeStream, InfoEntries info) {
|
|
// The writing functionality was intrioduced in Director 4
|
|
writeStream->writeUint32BE(20); // offset: d4 and up movies is always 20
|
|
writeStream->writeUint32BE(info.unk1);
|
|
writeStream->writeUint32BE(info.unk2);
|
|
writeStream->writeUint32BE(info.flags);
|
|
writeStream->writeUint32BE(info.scriptId);
|
|
writeStream->writeUint16BE(info.strings.size()); // count of strings in the info
|
|
|
|
uint32 length = 0;
|
|
writeStream->writeUint32BE(length);
|
|
for (uint16 i = 0; i < info.strings.size(); i++) {
|
|
length += info.strings[i].len;
|
|
writeStream->writeUint32BE(length);
|
|
}
|
|
|
|
for (uint16 i = 0; i < info.strings.size(); i++) {
|
|
writeStream->write(info.strings[i].data, info.strings[i].len);
|
|
}
|
|
}
|
|
|
|
void Movie::loadFileInfo(Common::SeekableReadStreamEndian &stream) {
|
|
debugC(2, kDebugLoading, "****** Loading FileInfo VWFI");
|
|
|
|
InfoEntries fileInfo = Movie::loadInfoEntries(stream, _version);
|
|
|
|
_allowOutdatedLingo = (fileInfo.flags & kMovieFlagAllowOutdatedLingo) != 0;
|
|
_remapPalettesWhenNeeded = (fileInfo.flags & kMovieFlagRemapPalettesWhenNeeded) != 0;
|
|
|
|
_script = fileInfo.strings[0].readString(false);
|
|
|
|
if (!_script.empty() && ConfMan.getBool("dump_scripts"))
|
|
_cast->dumpScript(_script.c_str(), kMovieScript, 0);
|
|
|
|
if (!_script.empty())
|
|
_cast->_lingoArchive->addCode(_script, kMovieScript, 0, nullptr, kLPPTrimGarbage);
|
|
|
|
_changedBy = fileInfo.strings[1].readString();
|
|
_createdBy = fileInfo.strings[2].readString();
|
|
_origDirectory = fileInfo.strings[3].readString();
|
|
|
|
uint16 preload = 0;
|
|
if (fileInfo.strings[4].len) {
|
|
if (stream.isBE())
|
|
preload = READ_BE_INT16(fileInfo.strings[4].data);
|
|
else
|
|
preload = READ_LE_INT16(fileInfo.strings[4].data);
|
|
}
|
|
|
|
if (debugChannelSet(3, kDebugLoading)) {
|
|
debug("VWFI: flags: %d", fileInfo.flags);
|
|
debug("VWFI: allow outdated lingo: %d", _allowOutdatedLingo);
|
|
debug("VWFI: script: '%s'", _script.c_str());
|
|
debug("VWFI: changed by: '%s'", _changedBy.c_str());
|
|
debug("VWFI: created by: '%s'", _createdBy.c_str());
|
|
debug("VWFI: original directory: '%s'", _origDirectory.c_str());
|
|
debug("VWFI: preload: %d (0x%x)", preload, preload);
|
|
|
|
for (uint i = 5; i < fileInfo.strings.size(); i++) {
|
|
debug("VWFI: entry %d (%d bytes)", i, fileInfo.strings[i].len);
|
|
Common::hexdump(fileInfo.strings[i].data, fileInfo.strings[i].len);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Movie::clearSharedCast() {
|
|
if (!_sharedCast)
|
|
return;
|
|
|
|
debug(0, "@@ Clearing shared cast '%s'", _sharedCast->getArchive()->getPathName().toString().c_str());
|
|
|
|
g_director->_allOpenResFiles.remove(_sharedCast->getArchive()->getPathName());
|
|
|
|
delete _sharedCast;
|
|
_sharedCast = nullptr;
|
|
}
|
|
|
|
void Movie::loadSharedCastsFrom(Common::Path &filename) {
|
|
clearSharedCast();
|
|
|
|
Archive *sharedCast = _vm->openArchive(filename);
|
|
|
|
if (!sharedCast) {
|
|
warning("loadSharedCastsFrom(): No shared cast %s", filename.toString().c_str());
|
|
|
|
return;
|
|
}
|
|
sharedCast->setPathName(filename);
|
|
|
|
debug(0, "\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
|
|
debug(0, "@@@@ Loading shared cast '%s' in '%s'", sharedCast->getFileName().c_str(), filename.getParent().toString().c_str());
|
|
debug(0, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
|
|
|
|
_sharedCast = new Cast(this, DEFAULT_CAST_LIB, true, false);
|
|
_sharedCast->setArchive(sharedCast);
|
|
_sharedCast->loadArchive();
|
|
}
|
|
|
|
Archive *Movie::loadExternalCastFrom(Common::Path &filename) {
|
|
Archive *externalCast = nullptr;
|
|
externalCast = _vm->openArchive(filename);
|
|
|
|
if (!externalCast) {
|
|
warning("Movie::loadExternalCastFrom(): Cast file %s not found", filename.toString().c_str());
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
debug(0, "\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
|
|
debug(0, "@@@@ Loading external cast '%s'", externalCast->getFileName().c_str());
|
|
debug(0, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
|
|
|
|
return externalCast;
|
|
}
|
|
|
|
bool Movie::loadCastLibFrom(uint16 libId, Common::Path &filename) {
|
|
if (_casts.contains(libId)) {
|
|
Cast *cast = _casts[libId];
|
|
if (cast->getArchive()->getPathName() == filename) {
|
|
// CastLib is already loaded, change nothing
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Archive *castArchive = loadExternalCastFrom(filename);
|
|
if (!castArchive) {
|
|
return false;
|
|
}
|
|
|
|
uint16 libResourceId = 1024;
|
|
Common::String name;
|
|
if (_casts.contains(libId)) {
|
|
Cast *cast = _casts[libId];
|
|
libResourceId = cast->_libResourceId;
|
|
name = cast->getCastName();
|
|
delete cast;
|
|
_casts.erase(libId);
|
|
}
|
|
|
|
Cast *cast = new Cast(this, libId, false, true, libResourceId);
|
|
cast->setArchive(castArchive);
|
|
cast->loadConfig();
|
|
cast->loadCast();
|
|
|
|
_casts.setVal(libId, cast);
|
|
_score->refreshPointersForCastLib(libId);
|
|
return true;
|
|
}
|
|
|
|
CastMember *Movie::getCastMember(CastMemberID memberID) {
|
|
CastMember *result = nullptr;
|
|
if (_casts.contains(memberID.castLib)) {
|
|
if (memberID.member == 0)
|
|
return nullptr;
|
|
result = _casts.getVal(memberID.castLib)->getCastMember(memberID.member);
|
|
if (result == nullptr && _sharedCast) {
|
|
result = _sharedCast->getCastMember(memberID.member);
|
|
}
|
|
} else if (memberID.castLib != 0) {
|
|
warning("Movie::getCastMember: Unknown castLib %d", memberID.castLib);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Cast *Movie::getCast(CastMemberID memberID) {
|
|
if (memberID.castLib == SHARED_CAST_LIB)
|
|
return _sharedCast;
|
|
|
|
if (_casts.contains(memberID.castLib)) {
|
|
return _casts.getVal(memberID.castLib);
|
|
} else if (memberID.castLib != 0) {
|
|
warning("Movie::getCast: Unknown castLib %d", memberID.castLib);
|
|
return nullptr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Cast *Movie::getCastByLibResourceID(int libresourceID) {
|
|
for (auto it : _casts) {
|
|
if (it._value->_libResourceId == libresourceID) {
|
|
debugC(3, kDebugSaving, "Movie::getCastByLibResourceID: Found cast with libresourceID: %d", libresourceID);
|
|
return it._value;
|
|
}
|
|
}
|
|
|
|
warning("Movie::getCastByLibResourceID: No cast with libresourceID: %d", libresourceID);
|
|
return nullptr;
|
|
}
|
|
|
|
CastMember* Movie::createOrReplaceCastMember(CastMemberID memberID, CastMember* cast) {
|
|
warning("Movie::createOrReplaceCastMember: stubbed: functions only handles create");
|
|
CastMember *result = nullptr;
|
|
|
|
if (_casts.contains(memberID.castLib)) {
|
|
// Delete existing cast member
|
|
_casts.getVal(memberID.castLib)->eraseCastMember(memberID.member);
|
|
|
|
_casts.getVal(memberID.castLib)->setCastMember(memberID.member, cast);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool Movie::eraseCastMember(CastMemberID memberID) {
|
|
if (_casts.contains(memberID.castLib)) {
|
|
bool result = _casts.getVal(memberID.castLib)->eraseCastMember(memberID.member);
|
|
_score->refreshPointersForCastMemberID(memberID);
|
|
return result;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Movie::duplicateCastMember(CastMemberID source, CastMemberID target) {
|
|
Cast *sourceCast = nullptr;
|
|
Cast *targetCast = nullptr;
|
|
if (_casts.contains(source.castLib)) {
|
|
if (_casts[source.castLib]->getCastMember(source.member)) {
|
|
sourceCast = _casts[source.castLib];
|
|
} else if (_sharedCast && _sharedCast->getCastMember(source.member)) {
|
|
sourceCast = _sharedCast;
|
|
}
|
|
}
|
|
// for shared + movie casts, duplications from the shared cast should be
|
|
// in the shared cast namespace
|
|
if (source.castLib == target.castLib) {
|
|
targetCast = sourceCast;
|
|
} else if (_casts.contains(target.castLib)) {
|
|
targetCast = _casts.getVal(target.castLib);
|
|
}
|
|
if (!sourceCast) {
|
|
warning("Movie::duplicateCastMember(): couldn't find source cast member %s", source.asString().c_str());
|
|
} else if (!targetCast) {
|
|
warning("Movie::duplicateCastMember(): couldn't find destination castLib %d", target.castLib);
|
|
} else if (source == target) {
|
|
warning("Movie::duplicateCastMember(): trying to duplicate cast member %s over itself", source.asString().c_str());
|
|
return false;
|
|
} else {
|
|
CastMember *sourceMember = sourceCast->getCastMember(source.member);
|
|
CastMemberInfo *sourceInfo = sourceCast->getCastMemberInfo(source.member);
|
|
debugC(3, kDebugLoading, "Movie::DuplicateCastMember(): copying cast data from %s to %s (%s)", source.asString().c_str(), target.asString().c_str(), castType2str(sourceMember->_type));
|
|
bool result = targetCast->duplicateCastMember(sourceMember, sourceInfo, target.member);
|
|
_score->refreshPointersForCastMemberID(target);
|
|
return result;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
CastMemberID Movie::getCastMemberIDByMember(int memberID) {
|
|
CastMemberID result(-1, 0);
|
|
// Search all cast libraries for a match
|
|
for (auto &cast : _casts) {
|
|
CastMember *member = cast._value->getCastMember(memberID);
|
|
if (member) {
|
|
result = CastMemberID(member->getID(), cast._key);
|
|
break;
|
|
}
|
|
}
|
|
if (result.member == -1 && _sharedCast) {
|
|
CastMember *member = _sharedCast->getCastMember(memberID);
|
|
if (member)
|
|
result = CastMemberID(member->getID(), DEFAULT_CAST_LIB);
|
|
}
|
|
if (result.member == -1) {
|
|
warning("Movie::getCastMemberIDByMemberID: No match found for member ID %d", memberID);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int Movie::getCastLibIDByName(const Common::String &name) {
|
|
for (auto &it : _castNames) {
|
|
if (it._key.equalsIgnoreCase(name)) {
|
|
return it._value;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void Movie::setCastLibName(const Common::String &name, int castLib) {
|
|
if (!_casts.contains(castLib)) {
|
|
warning("Movie::setCastLibName: castLib %d not found", castLib);
|
|
return;
|
|
}
|
|
for (auto &it : _castNames) {
|
|
if (it._value == castLib) {
|
|
_castNames.erase(it._key);
|
|
}
|
|
}
|
|
|
|
_castNames[name] = castLib;
|
|
_casts[castLib]->setCastName(name);
|
|
}
|
|
|
|
CastMemberID Movie::getCastMemberIDByName(const Common::String &name) {
|
|
return getCastMemberIDByNameAndType(name, 0, kCastTypeAny);
|
|
}
|
|
|
|
CastMemberID Movie::getCastMemberIDByNameAndType(const Common::String &name, int castLib, CastType type) {
|
|
CastMemberID result(-1, 0);
|
|
if (_casts.contains(castLib)) {
|
|
CastMember *member = _casts.getVal(castLib)->getCastMemberByNameAndType(name, type);
|
|
if (member) {
|
|
result = CastMemberID(member->getID(), castLib);
|
|
}
|
|
if (result.member == -1 && _sharedCast) {
|
|
member = _sharedCast->getCastMemberByNameAndType(name, type);
|
|
if (member) {
|
|
result = CastMemberID(member->getID(), castLib);
|
|
}
|
|
}
|
|
} else if (castLib == 0) {
|
|
// Search all cast libraries for a match
|
|
for (auto &cast : _casts) {
|
|
CastMember *member = cast._value->getCastMemberByNameAndType(name, type);
|
|
if (member) {
|
|
result = CastMemberID(member->getID(), cast._key);
|
|
break;
|
|
}
|
|
}
|
|
if (result.member == -1 && _sharedCast) {
|
|
CastMember *member = _sharedCast->getCastMemberByNameAndType(name, type);
|
|
if (member)
|
|
result = CastMemberID(member->getID(), DEFAULT_CAST_LIB);
|
|
}
|
|
} else {
|
|
warning("Movie::getCastMemberIDByNameAndType: Unknown castLib %d", castLib);
|
|
}
|
|
if (result.member == -1) {
|
|
warning("Movie::getCastMemberIDByNameAndType: No match found for member name %s and lib %d", name.c_str(), castLib);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
CastMemberInfo *Movie::getCastMemberInfo(CastMemberID memberID) {
|
|
CastMemberInfo *result = nullptr;
|
|
if (_casts.contains(memberID.castLib)) {
|
|
result = _casts.getVal(memberID.castLib)->getCastMemberInfo(memberID.member);
|
|
if (result == nullptr && _sharedCast) {
|
|
result = _sharedCast->getCastMemberInfo(memberID.member);
|
|
}
|
|
} else {
|
|
warning("Movie::getCastMemberInfo: Unknown castLib %d", memberID.castLib);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool Movie::isValidCastMember(CastMemberID memberID, CastType type) {
|
|
CastMember *test = getCastMember(memberID);
|
|
return test && ((test->_type == type) || (type == kCastTypeAny));
|
|
}
|
|
|
|
const Stxt *Movie::getStxt(CastMemberID memberID) {
|
|
const Stxt *result = nullptr;
|
|
if (_casts.contains(memberID.castLib)) {
|
|
result = _casts.getVal(memberID.castLib)->getStxt(memberID.member);
|
|
if (result == nullptr && _sharedCast) {
|
|
result = _sharedCast->getStxt(memberID.member);
|
|
}
|
|
} else {
|
|
warning("Movie::getStxt: Unknown castLib %d", memberID.castLib);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
LingoArchive *Movie::getMainLingoArch() {
|
|
return _casts.getVal(DEFAULT_CAST_LIB)->_lingoArchive;
|
|
}
|
|
|
|
LingoArchive *Movie::getSharedLingoArch() {
|
|
return _sharedCast ? _sharedCast->_lingoArchive : nullptr;
|
|
}
|
|
|
|
ScriptContext *Movie::getScriptContext(ScriptType type, CastMemberID id) {
|
|
ScriptContext *result = nullptr;
|
|
if (_casts.contains(id.castLib)) {
|
|
result = _casts.getVal(id.castLib)->_lingoArchive->getScriptContext(type, id.member);
|
|
if (result == nullptr && _sharedCast) {
|
|
result = _sharedCast->_lingoArchive->getScriptContext(type, id.member);
|
|
}
|
|
} else if (!id.isNull()) {
|
|
warning("Movie::getScriptContext: Unknown castLib %d", id.castLib);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Symbol Movie::getHandler(const Common::String &name, uint16 castLibHint) {
|
|
// Always check the current cast library for a match first
|
|
if (castLibHint && _casts.contains(castLibHint)) {
|
|
Cast *cast = _casts.getVal(castLibHint);
|
|
if (cast->_lingoArchive->functionHandlers.contains(name))
|
|
return cast->_lingoArchive->functionHandlers[name];
|
|
}
|
|
for (auto &it : _casts) {
|
|
if (it._value->_lingoArchive->functionHandlers.contains(name))
|
|
return it._value->_lingoArchive->functionHandlers[name];
|
|
}
|
|
|
|
if (_sharedCast && _sharedCast->_lingoArchive->functionHandlers.contains(name))
|
|
return _sharedCast->_lingoArchive->functionHandlers[name];
|
|
|
|
return Symbol();
|
|
}
|
|
|
|
Common::String InfoEntry::readString(bool pascal) {
|
|
Common::String res;
|
|
|
|
if (len == 0)
|
|
return res;
|
|
|
|
uint start = pascal ? 1 : 0; // skip length for Pascal string
|
|
|
|
Common::String encodedStr;
|
|
for (uint i = start; i < len; i++) {
|
|
if (!Common::isCntrl(data[i]) || Common::isSpace(data[i]))
|
|
encodedStr += data[i];
|
|
}
|
|
|
|
// FIXME: Use the case which contains this string, not the main cast.
|
|
return g_director->getCurrentMovie()->getCast()->decodeString(encodedStr).encode(Common::kUtf8);
|
|
}
|
|
|
|
void InfoEntry::writeString(Common::String string, bool pascal) {
|
|
if (string.size() == 0) {
|
|
return;
|
|
}
|
|
|
|
data = (byte *)malloc(len);
|
|
|
|
uint16 start = pascal ? 1 : 0;
|
|
memcpy(data + start, string.c_str(), string.size());
|
|
}
|
|
|
|
} // End of namespace Director
|