Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,134 @@
/* 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/>.
*
*/
#ifndef DIRECTOR_CASTMEMBER_BITMAP_H
#define DIRECTOR_CASTMEMBER_BITMAP_H
#include "director/castmember/castmember.h"
namespace Image {
class ImageDecoder;
}
namespace Director {
class BitmapCastMember : public CastMember {
public:
BitmapCastMember(Cast *cast, uint16 castId);
BitmapCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint32 castTag, uint16 version, uint8 flags1 = 0);
BitmapCastMember(Cast *cast, uint16 castId, Image::ImageDecoder *img, uint8 flags1 = 0);
BitmapCastMember(Cast *cast, uint16 castId, BitmapCastMember &source);
~BitmapCastMember();
CastMember *duplicate(Cast *cast, uint16 castId) override { return (CastMember *)(new BitmapCastMember(cast, castId, *this)); }
Graphics::MacWidget *createWidget(Common::Rect &bbox, Channel *channel, SpriteType spriteType) override;
bool isModified() override;
void createMatte(const Common::Rect &bbox);
Graphics::Surface *getMatte(const Common::Rect &bbox);
Graphics::Surface *getDitherImg();
bool hasField(int field) override;
Datum getField(int field) override;
void setField(int field, const Datum &value) override;
Common::String formatInfo() override;
void load() override;
void unload() override;
PictureReference *getPicture() const;
void setPicture(PictureReference &picture);
void setPicture(Image::ImageDecoder &image, bool adjustSize);
Common::Point getRegistrationOffset() override;
Common::Point getRegistrationOffset(int16 width, int16 height) override;
CollisionTest isWithin(const Common::Rect &bbox, const Common::Point &pos, InkType ink) override;
void writeCastData(Common::SeekableWriteStream *writeStream) override;
uint32 writeBITDResource(Common::SeekableWriteStream *writeStream, uint32 offset);
uint32 getCastDataSize() override; // This is the size of the data in the 'CASt' resource
uint32 getBITDResourceSize();
Picture *_picture = nullptr;
Graphics::Surface *_ditheredImg;
Graphics::Surface *_matte;
int _version;
uint16 _flags2;
uint16 _bytes;
CastMemberID _clut;
CastMemberID _ditheredTargetClut;
uint32 _tag;
bool _noMatte;
bool _external;
// D4 stucture:
// uint16 _pitch;
// _initialRect // 2
// _boundingRect // 10
// int16 _regY; // 18
// int16 _regX; // 20
// uint8 _bitsPerPixel; // 22 when _pitch & 0x8000
// D6+ structure
uint16 _pitch;
// _initialRect // 2
// _boundingRect // 10 D%-
byte _alphaThreshold; // 10 D7+
// padding
uint16 _editVersion; // 12 D6+
Common::Point _scrollPoint; // 14
int16 _regY; // 18
int16 _regX; // 20
byte _updateFlags; // 21
uint8 _bitsPerPixel; // 22 when _pitch & 0x8000
// These sit in _flags1
enum {
kFlagCenterRegPointD4 = 0x01, // centerRegPoint property
kFlagMatteD4 = 0x20, // double check value
};
enum {
kFlagDither = 0x01, // Bitmap needs dithering
kFlagRemapPalette = 0x02, // Bitmap needs palette remap
kFlagSyncPalette = 0x04, // When bitmap comes from outside, sync its palette castmember
kFlagDitherOnUpdate = 0x08, // Forced dither
// D7+
kFlagFollowAlpha = 0x10, // Alpha channel for 32-bit images must be followed
kFlagCenterRegPoint = 0x20, // centerRegPoint property
kFlagMatte = 0x40, // it is used for matte image
kFlagNoAutoCrop = 0x80, // do not automatically crop the image
};
};
} // End of namespace Director
#endif

View File

@@ -0,0 +1,450 @@
/* 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/events.h"
#include "common/stream.h"
#include "director/director.h"
#include "director/cast.h"
#include "director/movie.h"
#include "director/castmember/castmember.h"
#include "director/lingo/lingo-the.h"
#include "director/util.h"
namespace Director {
void EditInfo::read(Common::ReadStreamEndian *stream) {
rect = Movie::readRect(*stream);
selStart = stream->readUint32();
selEnd = stream->readUint32();
version = stream->readByte();
rulerFlag = stream->readByte();
// We're ignoring 2 bytes here
valid = true;
debugC(3, kDebugLoading, " EditInfo: rect: [%s], selStart: %d, selEnd: %d, version: %d, rulerFlag: %d", rect.toString().c_str(), selStart, selEnd, version, rulerFlag);
}
void EditInfo::write(Common::WriteStream *stream) {
Movie::writeRect(stream, rect);
stream->writeUint32BE(selStart);
stream->writeUint32BE(selEnd);
stream->writeByte(version);
stream->writeByte(rulerFlag);
}
CastMember::CastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream) : Object<CastMember>("CastMember") {
_type = kCastTypeNull;
_cast = cast;
_castId = castId;
_hilite = false;
_purgePriority = 3;
_size = stream.size();
_flags1 = 0;
_loaded = false;
_modified = true;
_isChanged = false;
_needsReload = false;
_objType = kCastMemberObj;
_widget = nullptr;
_erase = false;
_index = -1;
}
CastMember::CastMember(Cast *cast, uint16 castId) : Object<CastMember>("CastMember") {
_type = kCastTypeNull;
_cast = cast;
_castId = castId;
_hilite = false;
_purgePriority = 3;
_size = 0;
_flags1 = 0;
_loaded = false;
_modified = true;
_isChanged = false;
_objType = kCastMemberObj;
_widget = nullptr;
_erase = false;
_index = -1;
}
CastMember *CastMember::duplicate(Cast *cast, uint16 castId) {
warning("CastMember::duplicate(): unsupported cast type %s", castType2str(_type));
return nullptr;
}
void CastMember::setModified(bool modified) {
_modified = modified;
if (modified)
_isChanged = true;
}
Common::Rect CastMember::getBbox() {
Common::Rect result(_initialRect);
Common::Point offset = getRegistrationOffset();
result.moveTo(-offset.x, -offset.y);
return result;
}
Common::Rect CastMember::getBbox(int16 currentWidth, int16 currentHeight) {
Common::Rect result(currentWidth, currentHeight);
Common::Point offset = getRegistrationOffset(currentWidth, currentHeight);
result.moveTo(-offset.x, -offset.y);
return result;
}
bool CastMember::hasProp(const Common::String &propName) {
Common::String fieldName = Common::String::format("%d%s", kTheCast, propName.c_str());
return g_lingo->_theEntityFields.contains(fieldName) && hasField(g_lingo->_theEntityFields[fieldName]->field);
}
Datum CastMember::getProp(const Common::String &propName) {
Common::String fieldName = Common::String::format("%d%s", kTheCast, propName.c_str());
if (g_lingo->_theEntityFields.contains(fieldName)) {
return getField(g_lingo->_theEntityFields[fieldName]->field);
}
warning("CastMember::getProp: unknown property '%s'", propName.c_str());
return Datum();
}
void CastMember::setProp(const Common::String &propName, const Datum &value, bool force) {
Common::String fieldName = Common::String::format("%d%s", kTheCast, propName.c_str());
if (g_lingo->_theEntityFields.contains(fieldName)) {
setField(g_lingo->_theEntityFields[fieldName]->field, value);
return;
}
warning("CastMember::setProp: unknown property '%s'", propName.c_str());
}
bool CastMember::hasField(int field) {
switch (field) {
case kTheBackColor:
case kTheCastLibNum:
case kTheCastType:
case kTheFileName:
case kTheForeColor:
case kTheHeight:
case kTheHilite:
case kTheLoaded:
case kTheMediaReady:
case kTheModified:
case kTheMemberNum:
case kTheName:
case kTheNumber:
case kTheRect:
case kThePreLoad:
case kThePurgePriority:
case kTheScriptText:
case kTheSize:
case kTheType:
case kTheWidth:
return true;
default:
break;
}
return false;
}
Datum CastMember::getField(int field) {
Datum d;
CastMemberInfo *castInfo = _cast->getCastMemberInfo(_castId);
if (!castInfo)
warning("CastMember::getField(): CastMember info for %d not found", _castId);
switch (field) {
case kTheBackColor:
d = (int)getBackColor();
break;
case kTheCastLibNum:
d = _cast->_castLibID;
break;
case kTheCastType:
case kTheType:
d = Common::String(castType2str(_type));
if (g_director->getVersion() >= 500 && _type == kCastText) {
// D5 changes this from "text" to "field"
d = Common::String("field");
}
d.type = SYMBOL;
break;
case kTheFileName:
if (castInfo)
d = Datum(castInfo->directory + g_director->_dirSeparator + castInfo->fileName);
break;
case kTheForeColor:
d = (int)getForeColor();
break;
case kTheHeight:
d = _cast->getCastMemberInitialRect(_castId).height();
break;
case kTheHilite:
d = (int)_hilite;
break;
case kTheLoaded:
d = 1; // Not loaded handled in Lingo::getTheCast
break;
case kTheMediaReady:
d = 1; // Media is always downloaded from internet in ScummVM
break;
case kTheModified:
d = (int)_isChanged;
break;
case kTheName:
if (castInfo)
d = Datum(castInfo->name);
break;
case kTheMemberNum:
d = _castId;
break;
case kTheNumber:
if (g_director->getVersion() >= 500) {
d = CastMemberID(_castId, _cast->_castLibID).toMultiplex();
} else {
d = _castId;
}
break;
case kTheRect:
// not sure get the initial rect would be fine to castmember
d = Datum(_cast->getCastMember(_castId)->_initialRect);
break;
/*
ScummVM does not do preloading so we will always return false here.
simpsonscartoonstudio checks this flag and if set to true does an updateStage
in a loop which causes performance issues loading cartoons.
*/
case kThePreLoad:
d = 0;
break;
case kThePurgePriority:
d = _purgePriority;
break;
case kTheScriptText:
if (castInfo)
d = Datum(castInfo->script);
break;
case kTheSize:
d = (int)_size;
break;
case kTheWidth:
d = _cast->getCastMemberInitialRect(_castId).width();
break;
default:
warning("CastMember::getField(): Unprocessed getting field \"%s\" of cast %d", g_lingo->field2str(field), _castId);
//TODO find out about String fields
}
return d;
}
void CastMember::setField(int field, const Datum &d) {
CastMemberInfo *castInfo = _cast->getCastMemberInfo(_castId);
switch (field) {
case kTheBackColor:
_cast->getCastMember(_castId)->setBackColor(d.asInt());
return;
case kTheCastType:
case kTheType:
warning("BUILDBOT: CastMember::setField(): Attempt to set read-only field %s of cast %d", g_lingo->entity2str(field), _castId);
return;
case kTheFileName:
if (!castInfo) {
warning("CastMember::setField(): CastMember info for %d not found", _castId);
return;
} else {
Common::String rawPath = d.asString();
Common::String filename = getFileName(rawPath);
castInfo->fileName = filename;
castInfo->directory = rawPath.substr(0, MAX((uint)0, rawPath.size() - filename.size() - 1));
_needsReload = true;
_modified = true;
}
return;
case kTheForeColor:
_cast->getCastMember(_castId)->setForeColor(d.asInt());
return;
case kTheHeight:
warning("BUILDBOT: CastMember::setField(): Attempt to set read-only field \"%s\" of cast %d", g_lingo->field2str(field), _castId);
return;
case kTheHilite:
_hilite = (bool)d.asInt();
_modified = true;
return;
case kTheName:
if (!castInfo) {
warning("CastMember::setField(): CastMember info for %d not found", _castId);
return;
}
castInfo->name = d.asString();
_cast->rebuildCastNameCache();
return;
case kTheRect:
warning("CastMember::setField(): Attempt to set read-only field \"%s\" of cast %d", g_lingo->field2str(field), _castId);
return;
/*
ScummVM does not do preloading so we will make this a no-op.
*/
case kThePreLoad:
return;
case kThePurgePriority:
_purgePriority = CLIP<int>(d.asInt(), 0, 3);
return;
case kTheScriptText:
if (!castInfo) {
warning("CastMember::setField(): CastMember info for %d not found", _castId);
return;
}
_cast->_lingoArchive->replaceCode(*d.u.s, kCastScript, _castId);
castInfo->script = d.asString();
return;
case kTheWidth:
warning("BUILDBOT: CastMember::setField(): Attempt to set read-only field \"%s\" of cast %d", g_lingo->field2str(field), _castId);
return;
default:
warning("CastMember::setField(): Unprocessed setting field \"%s\" of cast %d", g_lingo->field2str(field), _castId);
}
}
CastMemberInfo *CastMember::getInfo() {
return _cast->getCastMemberInfo(_castId);
}
void CastMember::load() {
if (_loaded)
return;
_loaded = true;
}
void CastMember::unload() {
if (!_loaded)
return;
_loaded = false;
}
// Cast members have two types of "information", one is _data_ and the other is _info_
// _data_ is the actual "data" (e.g. the pixel image data of a bitmap, sound data of a sound cast member)
// Whereas _info_ is metadata (size, name, flags, etc.)
// Some cast members have their _data_ as well as _info_ in this very 'CASt' resource, e.g. TextCastMember
// Whereas some other have their _info_ in a 'CASt' resource and _data_ in a dedicated resource (e.g. PaletteCastMember has 'CLUT' resource)
uint32 CastMember::writeCAStResource(Common::SeekableWriteStream *writeStream) {
uint32 castResourceSize = getCastResourceSize();
writeStream->writeUint32LE(MKTAG('C', 'A', 'S', 't'));
writeStream->writeUint32LE(castResourceSize); // this is excluding the 'CASt' header and the size itself (- 8 bytes)
uint32 castDataToWrite = getCastDataSize();
uint32 castInfoToWrite = getCastInfoSize();
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
writeStream->writeUint16BE(castDataToWrite);
writeStream->writeUint32BE(castInfoToWrite);
writeStream->writeByte((uint8)_type);
castDataToWrite -= 1;
if (_flags1 != 0xFF) { // In case of TextCastMember, this should be true
writeStream->writeByte(_flags1);
castDataToWrite -= 1;
}
// For cast members with dedicated resources for data, the castDataToWrite is zero
if (castDataToWrite) {
writeCastData(writeStream);
}
if (castInfoToWrite) {
_cast->writeCastInfo(writeStream, _castId);
}
} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
writeStream->writeUint32BE((uint32)_type);
writeStream->writeUint32BE(castInfoToWrite);
writeStream->writeUint32BE(castDataToWrite);
if (castInfoToWrite) {
_cast->writeCastInfo(writeStream, _castId);
}
// For cast members with dedicated resources for data, the castDataToWrite is zero
if (castDataToWrite) {
writeCastData(writeStream);
}
}
return 0;
}
// This is the data that is inside the 'CASt' resource
// These functions (getCastDataSize() and writeCastData() default implementations, are not supposed to be called
// If the data is modified in the cast member, we implement a custom getCastDataSize() and writeCastData() for that member
// If it is not modified, then we write it as it is from the original source in the overridden
// writeCAStResource(Common::MemoryWriteStream, uint32, uint32) function which doesn't call these default functions
uint32 CastMember::getCastDataSize() {
warning("CastMember::getDataSize(): Defualt implementation of 'CASt' resource data size");
return _castDataSize;
}
void CastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
warning("CastMember::getDataSize(): Defualt implementation of 'CASt' resource data");
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
if (_flags1 != 0xFF) {
writeStream->write(0, _castDataSize - 2);
} else {
writeStream->write(0, _castDataSize - 1);
}
} else {
writeStream->write(0, _castDataSize);
}
}
// This is the info that is inside the 'CASt' resource
uint32 CastMember::getCastInfoSize() {
return _cast->getCastInfoSize(_castId);
}
// getCastResourceSize only returns the size of the resource without the header
uint32 CastMember::getCastResourceSize() {
uint32 headerSize = 0;
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
// Header size for director version 4
headerSize = 7; // see Cast::loadCastData() for director version 4
if (_flags1 != 0xFF) {
headerSize += 1;
}
} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
// Header size for director version 5
headerSize = 12; // See Cast::loadCastData() for director version 5
}
return headerSize + getCastInfoSize() + getCastDataSize();
}
} // End of namespace Director

View File

@@ -0,0 +1,208 @@
/* 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/>.
*
*/
#ifndef DIRECTOR_CASTMEMBER_CASTMEMBER_H
#define DIRECTOR_CASTMEMBER_CASTMEMBER_H
#include "common/rect.h"
#include "director/archive.h"
#include "director/stxt.h"
#include "director/lingo/lingo-object.h"
namespace Graphics {
class MacWidget;
}
namespace Common {
class SeekableReadStream;
class SeekableReadStreamEndian;
class MemoryWriteStream;
}
namespace Director {
struct CastMemberInfo;
class Channel;
struct Resource;
class CastMember : public Object<CastMember> {
public:
CastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream);
CastMember(Cast *cast, uint16 castId);
virtual ~CastMember() {}
virtual CastMember *duplicate(Cast *cast, uint16 castId);
Cast *getCast() { return _cast; }
uint16 getID() { return _castId; }
CastMemberInfo *getInfo();
virtual void load();
virtual void unload();
bool isLoaded() { return _loaded; }
virtual bool isEditable() { return false; }
virtual void setEditable(bool editable) {}
virtual bool isModified() { return _modified; }
virtual bool needsReload() { return _needsReload; }
void setModified(bool modified);
virtual Graphics::MacWidget *createWidget(Common::Rect &bbox, Channel *channel, SpriteType spriteType) { return nullptr; }
virtual void updateWidget(Graphics::MacWidget *widget, Channel *channel) {}
virtual void updateFromWidget(Graphics::MacWidget *widget, bool spriteEditable) {}
virtual Common::Rect getInitialRect() { return _initialRect; }
virtual void setColors(uint32 *fgcolor, uint32 *bgcolor) { return; }
virtual uint32 getForeColor() { return 0; }
virtual void setForeColor(uint32 fgCol) { return; }
virtual uint32 getBackColor() { return 0; }
virtual void setBackColor(uint32 bgCol) { return; }
bool hasProp(const Common::String &propName) override;
Datum getProp(const Common::String &propName) override;
void setProp(const Common::String &propName, const Datum &value, bool force = false) override;
bool hasField(int field) override;
Datum getField(int field) override;
void setField(int field, const Datum &value) override;
// release the control to widget, this happens when we are changing sprites. Because we are having the new cast member and the old one shall leave
void releaseWidget() { _widget = nullptr; }
virtual Common::String formatInfo() { return Common::String(); };
// Return the default bounding box of the cast member. The origin is at the registration offset.
virtual Common::Rect getBbox();
// Return the bounding box of the cast member, assuming a stretched width and height value.
// The origin is at the registration offset.
virtual Common::Rect getBbox(int16 currentWidth, int16 currentHeight);
// Return the default registration offset. Offset is relative to the top-left corner of the widget.
virtual Common::Point getRegistrationOffset() { return Common::Point(0, 0); }
// Return the registration offset, assuming a stretched width and height value.
// Offset is relative to the top-left corner of the widget.
virtual Common::Point getRegistrationOffset(int16 currentWidth, int16 currentHeight) { return Common::Point(0, 0); }
virtual CollisionTest isWithin(const Common::Rect &bbox, const Common::Point &pos, InkType ink) { return bbox.contains(pos) ? kCollisionYes : kCollisionNo; }
// When writing the 'CASt' resource, the general structure is the same for all the CastMembers
// Three parts to a 'CASt' resource (header + _info_, _data_)
// The headers, are common, the _info_ writing is handled by the Cast class, so no worries there
// So, the only thing that differs is the _data_, for which we'll have separate implementations for each CastMember
uint32 writeCAStResource(Common::SeekableWriteStream *writeStream);
uint32 getCastInfoSize();
uint32 getCastResourceSize();
virtual void writeCastData(Common::SeekableWriteStream *writeStream);
virtual uint32 getCastDataSize();
CastType _type;
Common::Rect _initialRect;
Common::Rect _boundingRect;
Common::Array<Resource> _children;
bool _hilite;
bool _erase;
int _purgePriority;
uint32 _size;
/* Data fields used when saving the Cast Member */
uint32 _castDataSize;
uint8 _flags1;
// This index is the index that it appears in the original movie archive
int16 _index;
protected:
Cast *_cast;
// This is the id of the cast member, this id is unique to only cast members
// Basically the cast members are given ids starting from _castArrayStart to _castArrayEnd
// e.g. 0, 1, 2, 3, 4
uint16 _castId;
// a link to the widget we created, we may use it later
Graphics::MacWidget *_widget;
bool _loaded;
bool _modified;
bool _isChanged;
bool _needsReload;
};
struct EditInfo {
Common::Rect rect;
int32 selStart;
int32 selEnd;
byte version;
byte rulerFlag;
bool valid;
EditInfo(): valid(false) {}
void read(Common::ReadStreamEndian *stream);
void write(Common::WriteStream *stream);
Common::String toString() {
return Common::String::format("rect: [%s] selStart: %d selEnd: %d version: %d rulerFlag: %d valid: %d",
rect.toString().c_str(), selStart, selEnd, version, rulerFlag, valid);
}
};
struct CastMemberInfo {
// Header
uint32 unk1;
uint32 unk2;
uint32 flags;
uint16 count;
bool autoHilite;
uint32 scriptId;
// List items
Common::String script; // 0 (removed on protecting)
Common::String name;
Common::String directory;
Common::String fileName;
Common::String fileType; // 4 pre-D5
Common::String propInit; // 4 post-D5
EditInfo scriptEditInfo; // 5 (removed on protecting)
FontStyle scriptStyle; // (removed on protecting)
EditInfo textEditInfo; // (removed on protecting)
EditInfo rteEditInfo; // (removed on protecting)
byte xtraGuid[16]; // 9
Common::String xtraDisplayName;
Common::Array<byte> bpTable; // (removed on protecting)
Common::Rect32 xtraRect; // Rect32
Common::Rect scriptRect; // (removed on protecting)
Common::Array<byte> dvWindowInfo; // (removed on protecting)
byte guid[16]; // 15 Seems to be a GUID
Common::String mediaFormatName; // 16 Used by DV cast members to store the media format name
uint32 creationTime; // (removed on protecting)
uint32 modifiedTime; // (removed on protecting)
Common::String modifiedBy; // (removed on protecting)
Common::String comments; // 20 (removed on protecting, but could be retained)
uint32 imageQuality; // 21
CastMemberInfo() : autoHilite(false), scriptId(0) {
memset(xtraGuid, 0, 16);
memset(guid, 0, 16);
creationTime = 0;
modifiedTime = 0;
imageQuality = 0;
}
};
} // End of namespace Director
#endif

View File

@@ -0,0 +1,733 @@
/* 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 "audio/decoders/aiff.h"
#include "common/platform.h"
#include "common/stream.h"
#include "common/macresman.h"
#include "graphics/paletteman.h"
#include "graphics/surface.h"
#include "graphics/macgui/macwidget.h"
#include "video/video_decoder.h"
#include "video/avi_decoder.h"
#include "video/qt_decoder.h"
#include "director/director.h"
#include "director/cast.h"
#include "director/channel.h"
#include "director/images.h"
#include "director/movie.h"
#include "director/window.h"
#include "director/castmember/digitalvideo.h"
#include "director/lingo/lingo-the.h"
namespace Director {
class NoVideoAIFFDecoder : public Video::VideoDecoder {
protected:
class AIFFAudioTrack : public Video::VideoDecoder::AudioTrack {
private:
Audio::RewindableAudioStream *_audioStream = nullptr;
public:
AIFFAudioTrack(Audio::Mixer::SoundType soundType, Audio::RewindableAudioStream *stream) : AudioTrack(soundType) {
_audioStream = stream;
}
virtual Audio::AudioStream *getAudioStream() const {
return _audioStream;
}
};
public:
bool loadFile(const Common::Path &filename) override {
Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(filename);
if (!file) {
delete file;
return false;
}
bool result = loadStream(file);
if (!result)
delete file;
return result;
}
virtual bool loadStream(Common::SeekableReadStream *stream) override {
addTrack(new AIFFAudioTrack(Audio::Mixer::SoundType::kSFXSoundType, Audio::makeAIFFStream(stream, DisposeAfterUse::Flag::NO)));
return true;
}
};
DigitalVideoCastMember::DigitalVideoCastMember(Cast *cast, uint16 castId)
: CastMember(cast, castId) {
_type = kCastDigitalVideo;
_video = nullptr;
_lastFrame = nullptr;
_channel = nullptr;
_getFirstFrame = false;
_duration = 0;
_vflags = 0;
_frameRate = 0;
_frameRateType = kFrameRateDefault;
_videoType = kDVUnknown;
_qtmovie = true;
_avimovie = false;
_preload = false;
_enableVideo = true;
_pausedAtStart = false;
_showControls = false;
_directToStage = false;
_looping = false;
_enableSound = true;
_crop = false;
_center = false;
_dirty = false;
_emptyFile = false;
memset(_ditheringPalette, 0, 256*3);
}
DigitalVideoCastMember::DigitalVideoCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version)
: CastMember(cast, castId, stream) {
_type = kCastDigitalVideo;
_video = nullptr;
_lastFrame = nullptr;
_channel = nullptr;
_getFirstFrame = false;
_duration = 0;
_initialRect = Movie::readRect(stream);
_vflags = stream.readUint32();
_frameRate = (_vflags >> 24) & 0xff;
_frameRateType = kFrameRateDefault;
_videoType = kDVUnknown;
if (_vflags & 0x0800) {
_frameRateType = (FrameRateType)((_vflags & 0x3000) >> 12);
}
_qtmovie = _vflags & 0x8000;
_avimovie = _vflags & 0x4000;
_preload = _vflags & 0x0400;
_enableVideo = !(_vflags & 0x0200);
_pausedAtStart = _vflags & 0x0100;
_showControls = _vflags & 0x40;
_directToStage = _vflags & 0x20;
_looping = _vflags & 0x10;
_enableSound = _vflags & 0x08;
_crop = !(_vflags & 0x02);
_center = _vflags & 0x01;
_dirty = false;
_emptyFile = false;
memset(_ditheringPalette, 0, 256*3);
if (debugChannelSet(2, kDebugLoading))
_initialRect.debugPrint(2, "DigitalVideoCastMember(): rect:");
debugC(2, kDebugLoading, "DigitalVideoCastMember(): flags: (%d 0x%04x)", _vflags, _vflags);
debugC(2, kDebugLoading, "_frameRate: %d", _frameRate);
debugC(2, kDebugLoading, "_frameRateType: %d, _preload: %d, _enableVideo %d, _pausedAtStart %d",
_frameRateType, _preload, _enableVideo, _pausedAtStart);
debugC(2, kDebugLoading, "_showControls: %d, _looping: %d, _enableSound: %d, _crop %d, _center: %d, _directToStage: %d",
_showControls, _looping, _enableSound, _crop, _center, _directToStage);
debugC(2, kDebugLoading, "_avimovie: %d, _qtmovie: %d", _avimovie, _qtmovie);
}
DigitalVideoCastMember::DigitalVideoCastMember(Cast *cast, uint16 castId, DigitalVideoCastMember &source)
: CastMember(cast, castId) {
_type = kCastDigitalVideo;
_loaded = source._loaded;
_initialRect = source._initialRect;
_boundingRect = source._boundingRect;
if (cast == source._cast)
_children = source._children;
_filename = source._filename;
_vflags = source._vflags;
_looping = source._looping;
_pausedAtStart = source._pausedAtStart;
_enableVideo = source._enableVideo;
_enableSound = source._enableSound;
_crop = source._crop;
_center = source._center;
_preload = source._preload;
_showControls = source._showControls;
_directToStage = source._directToStage;
_avimovie = source._avimovie;
_qtmovie = source._qtmovie;
_dirty = source._dirty;
_frameRateType = source._frameRateType;
_videoType = source._videoType;
_frameRate = source._frameRate;
_getFirstFrame = source._getFirstFrame;
_duration = source._duration;
_video = nullptr;
_lastFrame = nullptr;
_channel = nullptr;
}
DigitalVideoCastMember::~DigitalVideoCastMember() {
if (_lastFrame) {
_lastFrame->free();
delete _lastFrame;
}
if (_video)
delete _video;
}
bool DigitalVideoCastMember::loadVideoFromCast() {
Common::String path = getCast()->getVideoPath(_castId);
if (!path.empty())
return loadVideo(path);
return false;
}
bool DigitalVideoCastMember::loadVideo(Common::String path) {
if (_filename == path) {
// we've already loaded this video, or not. no point trying again.
return _video ? true : false;
}
if (_video) {
delete _video;
_video = nullptr;
}
_filename = path;
Common::Path location = findPath(path);
if (location.empty()) {
warning("DigitalVideoCastMember::loadVideo(): unable to resolve path %s", path.c_str());
return false;
}
Common::SeekableReadStream *copiedStream = Common::MacResManager::openFileOrDataFork(location);
if (!copiedStream) {
warning("DigitalVideoCastMember::loadVideo Failed to open %s", path.c_str());
return false;
}
uint32 magic1 = copiedStream->readUint32BE();
uint32 magic2 = copiedStream->readUint32BE();
uint32 magic3 = copiedStream->readUint32BE();
delete copiedStream;
bool result = false;
debugC(2, kDebugLoading, "Loading video %s -> %s", path.c_str(), location.toString(Common::Path::kNativeSeparator).c_str());
if (magic1 == MKTAG('F', 'O', 'R', 'M') &&
(magic3 == MKTAG('A', 'I', 'F', 'F') || magic3 == MKTAG('A', 'I', 'F', 'C'))) {
_video = new NoVideoAIFFDecoder();
result = _video->loadFile(location);
if (!result) {
delete _video;
_video = nullptr;
return false;
} else {
// Pretend that this is our friend QuickTime
_videoType = kDVQuickTime;
}
} else if (magic2 == MKTAG('m', 'o', 'o', 'v') || magic2 == MKTAG('m', 'd', 'a', 't')) {
_video = new Video::QuickTimeDecoder();
result = _video->loadFile(location);
if (!result) {
delete _video;
_video = nullptr;
// Probe for empty file
Common::MacResManager mgr;
if (mgr.open(location)) {
if (!mgr.hasDataFork()) {
debugC(8, kDebugLevelGVideo, "DigitalVideoCastMember::loadVideo(): skipping empty stream");
_emptyFile = true;
}
return false;
}
} else {
_videoType = kDVQuickTime;
}
} else if (magic1 == MKTAG('R', 'I', 'F', 'F') && (magic3 == MKTAG('A', 'V', 'I', ' '))) {
_video = new Video::AVIDecoder();
result = _video->loadFile(location);
if (!result) {
warning("DigitalVideoCastMember::loadVideo(): format not supported, skipping video '%s'", path.c_str());
delete _video;
_video = nullptr;
return false;
} else {
_videoType = kDVVideoForWindows;
}
} else {
warning("DigitalVideoCastMember::loadVideo: Unknown file format for video '%s', skipping", path.c_str());
}
if (result && g_director->_pixelformat.bytesPerPixel == 1) {
// Director supports playing back RGB and paletted video in 256 colour mode.
// In both cases they are dithered to match the Director palette.
memcpy(_ditheringPalette, g_director->getPalette(), 256*3);
// In Windows, the first 8 and last 8 colors are reserved for the system palette.
// Generally you don't want these as part of the video, and Video for Windows
// seems to deliberately exclude them.
// Keep colour 0 and 255 as they are pure white and pure black, respectively.
if (g_director->_vfwPaletteHack && g_director->getPlatform() == Common::kPlatformWindows) {
for (int i = 1; i < 8; i++) {
_ditheringPalette[i*3+0] = _ditheringPalette[0];
_ditheringPalette[i*3+1] = _ditheringPalette[1];
_ditheringPalette[i*3+2] = _ditheringPalette[2];
}
for (int i = 248; i < 255; i++) {
_ditheringPalette[i*3+0] = _ditheringPalette[0];
_ditheringPalette[i*3+1] = _ditheringPalette[1];
_ditheringPalette[i*3+2] = _ditheringPalette[2];
}
}
_video->setDitheringPalette(_ditheringPalette);
}
_duration = getMovieTotalTime();
if (_video) {
// Setting the initial rect to the actual movie dimensions
_initialRect.setWidth(_video->getWidth());
_initialRect.setHeight(_video->getHeight());
}
return result;
}
bool DigitalVideoCastMember::isModified() {
if (!_video || !_video->isVideoLoaded())
return true;
if (_dirty) {
_dirty = false;
return true;
}
// Inelegant, but necessary. isModified will get called on
// every screen update, so use it to keep the playback
// status up to date.
if (_video->endOfVideo()) {
if (_looping) {
_video->rewind();
} else if (_channel) {
_channel->_movieRate = 0.0;
}
}
if (_getFirstFrame)
return true;
if (_channel && _channel->_movieRate == 0.0)
return false;
return _video->needsUpdate();
}
void DigitalVideoCastMember::startVideo() {
if (!_video || !_video->isVideoLoaded()) {
warning("DigitalVideoCastMember::startVideo: No video %s", !_video ? "decoder" : "loaded");
return;
}
if (_pausedAtStart) {
_getFirstFrame = true;
} else {
if (_channel && _channel->_movieRate == 0.0)
_channel->_movieRate = 1.0;
}
if (_video->isPlaying())
_video->rewind();
else
_video->start();
debugC(2, kDebugImages, "STARTING VIDEO %s", _filename.c_str());
if (_channel && _channel->_stopTime == 0)
_channel->_stopTime = getMovieTotalTime();
}
void DigitalVideoCastMember::stopVideo() {
if (!_video || !_video->isVideoLoaded()) {
if (!_emptyFile)
warning("DigitalVideoCastMember::stopVideo: No video decoder");
return;
}
_video->stop();
debugC(2, kDebugImages, "STOPPING VIDEO %s", _filename.c_str());
}
void DigitalVideoCastMember::rewindVideo() {
if (!_video || !_video->isVideoLoaded()) {
if (!_emptyFile)
warning("DigitalVideoCastMember::rewindVideo: No video decoder");
return;
}
_video->rewind();
debugC(2, kDebugImages, "REWINDING VIDEO %s", _filename.c_str());
}
Graphics::MacWidget *DigitalVideoCastMember::createWidget(Common::Rect &bbox, Channel *channel, SpriteType spriteType) {
if (_emptyFile)
return nullptr;
if (!_video || !_video->isVideoLoaded()) {
// try and load the video if not already
if (!loadVideoFromCast()) {
return nullptr;
}
}
Graphics::MacWidget *widget = new Graphics::MacWidget(g_director->getCurrentWindow()->getMacWindow(), bbox.left, bbox.top, bbox.width(), bbox.height(), g_director->_wm, false);
_channel = channel;
// Do not render stopped videos
if (_channel->_movieRate == 0.0 && !_getFirstFrame && _lastFrame) {
widget->getSurface()->blitFrom(*_lastFrame);
return widget;
}
const Graphics::Surface *frame = _video->decodeNextFrame();
// If the video gets stopped, for whatever reason, _video->getPalette() will not work.
// Cache it when possible.
if (g_director->_pixelformat.bytesPerPixel == 4) {
const byte *videoPalette = _video->getPalette();
if (videoPalette) {
memcpy(_ditheringPalette, videoPalette, 256*3);
}
}
debugC(1, kDebugImages, "Video time: %d rate: %f frame: %p dims: %d x %d", _channel->_movieTime, _channel->_movieRate, (const void *)frame, bbox.width(), bbox.height());
if (frame) {
if (_lastFrame) {
_lastFrame->free();
delete _lastFrame;
_lastFrame = nullptr;
}
if (frame->getPixels()) {
// Video should have the dithering palette set, decode using whatever palette we have now
_lastFrame = frame->convertTo(g_director->_pixelformat, _ditheringPalette);
} else {
warning("DigitalVideoCastMember::createWidget(): frame has no pixel data");
}
}
if (_lastFrame)
copyStretchImg(
_lastFrame,
widget->getSurface()->surfacePtr(),
Common::Rect((int16)_video->getWidth(), (int16)_video->getHeight()),
bbox
);
if (_getFirstFrame) {
_video->stop();
_getFirstFrame = false;
}
return widget;
}
uint DigitalVideoCastMember::getDuration() {
if (!_video || !_video->isVideoLoaded()) {
loadVideoFromCast();
}
return _duration;
}
uint DigitalVideoCastMember::getMovieCurrentTime() {
if (!_video)
return 0;
int ticks = 1 + ((_video->getTime() * 60 - 1)/1000);
int stamp = MIN<int>(ticks, getMovieTotalTime());
return stamp;
}
uint DigitalVideoCastMember::getMovieTotalTime() {
if (!_video)
return 0;
int ticks = 1 + ((_video->getDuration().msecs() * 60 - 1)/1000);
return ticks;
}
void DigitalVideoCastMember::seekMovie(int stamp) {
if (!_video)
return;
_channel->_startTime = stamp;
Audio::Timestamp dur = _video->getDuration();
_video->seek(Audio::Timestamp(_channel->_startTime * 1000 / 60, dur.framerate()));
if (_channel->_movieRate == 0.0) {
_getFirstFrame = true;
}
_dirty = true;
}
void DigitalVideoCastMember::setStopTime(int stamp) {
if (!_video)
return;
_channel->_stopTime = stamp;
Audio::Timestamp dur = _video->getDuration();
_video->setEndTime(Audio::Timestamp(_channel->_stopTime * 1000 / 60, dur.framerate()));
}
void DigitalVideoCastMember::setMovieRate(double rate) {
if (!_video)
return;
_channel->_movieRate = rate;
if (rate < 0.0)
warning("STUB: DigitalVideoCastMember::setMovieRate(%g)", rate);
else {
if (_getFirstFrame && rate != 0.0) {
// playback got started before we rendered the first
// frame in pause mode, keep going
_getFirstFrame = false;
}
_video->setRate(Common::Rational((int)(rate * 100.0), 100));
}
if (_video->endOfVideo())
_video->rewind();
}
void DigitalVideoCastMember::setFrameRate(int rate) {
if (!_video)
return;
warning("STUB: DigitalVideoCastMember::setFrameRate(%d)", rate);
}
Common::String DigitalVideoCastMember::formatInfo() {
return Common::String::format(
"initialRect: %dx%d@%d,%d, boundingRect: %dx%d@%d,%d, filename: \"%s\", duration: %d, enableVideo: %d, enableSound: %d, looping: %d, crop: %d, center: %d, showControls: %d",
_initialRect.width(), _initialRect.height(),
_initialRect.left, _initialRect.top,
_boundingRect.width(), _boundingRect.height(),
_boundingRect.left, _boundingRect.top,
_filename.c_str(), _duration,
_enableVideo, _enableSound,
_looping, _crop, _center, _showControls
);
}
Common::Point DigitalVideoCastMember::getRegistrationOffset() {
return Common::Point(_initialRect.width() / 2, _initialRect.height() / 2);
}
Common::Point DigitalVideoCastMember::getRegistrationOffset(int16 width, int16 height) {
return Common::Point(width / 2, height / 2);
}
bool DigitalVideoCastMember::hasField(int field) {
switch (field) {
case kTheCenter:
case kTheController:
case kTheCrop:
case kTheCuePointNames: // D6
case kTheCuePointTimes: // D6
case kTheCurrentTime: // D6
case kTheDigitalVideoType:
case kTheDirectToStage:
case kTheDuration:
case kTheFrameRate:
case kTheLoop:
case kThePausedAtStart:
case kThePreLoad:
case kTheSound:
case kTheTimeScale:
case kTheVideo:
return true;
default:
break;
}
return CastMember::hasField(field);
}
Datum DigitalVideoCastMember::getField(int field) {
Datum d;
switch (field) {
case kTheCenter:
d = _center;
break;
case kTheController:
d = _showControls;
break;
case kTheCrop:
d = _crop;
break;
case kTheDigitalVideoType:
if (_videoType == kDVVideoForWindows) {
d = Datum("videoForWindows");
} else {
// for unknown, just pretend QuickTime
d = Datum("quickTime");
}
d.type = SYMBOL;
break;
case kTheDirectToStage:
d = _directToStage;
break;
case kTheDuration:
// sometimes, we will get duration before we start video.
// _duration is initialized in startVideo, thus we will not get the correct number.
d = (int)getDuration();
break;
case kTheFrameRate:
d = _frameRate;
break;
case kTheLoop:
d = _looping;
break;
case kThePausedAtStart:
d = _pausedAtStart;
break;
case kThePreLoad:
d = _preload;
break;
case kTheSound:
d = _enableSound;
break;
case kTheTimeScale:
warning("STUB: DigitalVideoCastMember::getField(): timeScale not implemented");
d = Datum(600); // quicktime default
break;
case kTheVideo:
d = _enableVideo;
break;
default:
d = CastMember::getField(field);
}
return d;
}
void DigitalVideoCastMember::setField(int field, const Datum &d) {
switch (field) {
case kTheCenter:
_center = (bool)d.asInt();
return;
case kTheController:
_showControls = (bool)d.asInt();
return;
case kTheCrop:
_crop = (bool)d.asInt();
return;
case kTheDigitalVideoType:
warning("DigitalVideoCastMember::setField(): Attempt to set read-only field %s of cast %d", g_lingo->entity2str(field), _castId);
return;
case kTheDirectToStage:
_directToStage = (bool)d.asInt();
return;
case kTheDuration:
warning("DigitalVideoCastMember::setField(): Attempt to set read-only field %s of cast %d", g_lingo->entity2str(field), _castId);
return;
case kTheFrameRate:
_frameRate = d.asInt();
setFrameRate(d.asInt());
return;
case kTheLoop:
_looping = (bool)d.asInt();
if (_looping && _channel && _channel->_movieRate == 0.0) {
setMovieRate(1.0);
}
return;
case kThePausedAtStart:
_pausedAtStart = (bool)d.asInt();
return;
case kThePreLoad:
_preload = (bool)d.asInt();
return;
case kTheSound:
_enableSound = (bool)d.asInt();
return;
case kTheTimeScale:
warning("DigitalVideoCastMember::setField(): Attempt to set read-only field %s of cast %d", g_lingo->entity2str(field), _castId);
return;
case kTheVideo:
_enableVideo = (bool)d.asInt();
return;
default:
break;
}
CastMember::setField(field, d);
}
uint32 DigitalVideoCastMember::getCastDataSize() {
// We're only reading the _initialRect and _vflags from the Cast Data
// _initialRect : 8 bytes + _vflags : 4 bytes + castType and flags1 (see Cast::loadCastData() for Director 4 only) 2 byte
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
// It has been observed that the DigitalVideoCastMember has _flags set to 0x00
return (_flags1 == 0xFF) ? 13 : 14;
} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
return 8 + 4;
}
warning("DigitalVideoCastMember::getCastDataSize(): unhandled or invalid cast version: %d", _cast->_version);
return 0;
}
void DigitalVideoCastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
Movie::writeRect(writeStream, _initialRect);
writeStream->writeUint32BE(_vflags);
}
} // End of namespace Director

View File

@@ -0,0 +1,110 @@
/* 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/>.
*
*/
#ifndef DIRECTOR_CASTMEMBER_DIGITALVIDEO_H
#define DIRECTOR_CASTMEMBER_DIGITALVIDEO_H
#include "director/castmember/castmember.h"
namespace Video {
class VideoDecoder;
}
namespace Director {
enum DigitalVideoType {
kDVQuickTime,
kDVVideoForWindows,
kDVUnknown = -1,
};
class DigitalVideoCastMember : public CastMember {
public:
DigitalVideoCastMember(Cast *cast, uint16 castId);
DigitalVideoCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version);
DigitalVideoCastMember(Cast *cast, uint16 castId, DigitalVideoCastMember &source);
~DigitalVideoCastMember();
CastMember *duplicate(Cast *cast, uint16 castId) override { return (CastMember *)(new DigitalVideoCastMember(cast, castId, *this)); }
bool isModified() override;
Graphics::MacWidget *createWidget(Common::Rect &bbox, Channel *channel, SpriteType spriteType) override;
bool loadVideoFromCast();
bool loadVideo(Common::String path);
void setChannel(Channel *channel) { _channel = channel; }
void startVideo();
void stopVideo();
void rewindVideo();
uint getMovieCurrentTime();
uint getDuration();
uint getMovieTotalTime();
void seekMovie(int stamp);
void setStopTime(int stamp);
void setMovieRate(double rate);
void setFrameRate(int rate);
bool hasField(int field) override;
Datum getField(int field) override;
void setField(int field, const Datum &value) override;
Common::String formatInfo() override;
Common::Point getRegistrationOffset() override;
Common::Point getRegistrationOffset(int16 width, int16 height) override;
uint32 getCastDataSize() override;
void writeCastData(Common::SeekableWriteStream *writeStream) override;
Common::String _filename;
uint32 _vflags;
bool _looping;
bool _pausedAtStart;
bool _enableVideo;
bool _enableSound;
bool _crop;
bool _center;
bool _preload;
bool _showControls;
bool _directToStage;
bool _avimovie, _qtmovie;
bool _dirty;
bool _emptyFile;
FrameRateType _frameRateType;
DigitalVideoType _videoType;
byte _ditheringPalette[256*3];
uint16 _frameRate;
bool _getFirstFrame;
int _duration;
Video::VideoDecoder *_video;
Graphics::Surface *_lastFrame;
Channel *_channel;
};
} // End of namespace Director
#endif

View File

@@ -0,0 +1,895 @@
/* 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/memstream.h"
#include "graphics/surface.h"
#include "graphics/macgui/macwidget.h"
#include "video/avi_decoder.h"
#include "video/qt_decoder.h"
#include "director/director.h"
#include "director/cast.h"
#include "director/channel.h"
#include "director/frame.h"
#include "director/movie.h"
#include "director/sprite.h"
#include "director/window.h"
#include "director/castmember/bitmap.h"
#include "director/castmember/filmloop.h"
namespace Director {
FilmLoopCastMember::FilmLoopCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version)
: CastMember(cast, castId, stream) {
_type = kCastFilmLoop;
_looping = true;
_enableSound = true;
_crop = false;
_center = false;
_index = -1;
// We are ignoring some of the bits in the flags
if (cast->_version >= kFileVer400 && cast->_version < kFileVer500) {
_initialRect = Movie::readRect(stream);
uint32 flags = stream.readUint32BE();
uint16 unk1 = stream.readUint16BE();
debugC(5, kDebugLoading, "FilmLoopCastMember::FilmLoopCastMember(): flags: %d, unk1: %d", flags, unk1);
_looping = flags & 64 ? 0 : 1;
_enableSound = flags & 8 ? 1 : 0;
_crop = flags & 2 ? 0 : 1;
_center = flags & 1 ? 1 : 0;
} else if (cast->_version >= kFileVer500 && cast->_version < kFileVer600) {
_initialRect = Movie::readRect(stream);
uint32 flags = stream.readUint32BE();
uint16 unk1 = stream.readUint16BE();
debugC(5, kDebugLoading, "FilmLoopCastMember::FilmLoopCastMember(): flags: %d, unk1: %d", flags, unk1);
_looping = flags & 32 ? 0 : 1;
_enableSound = flags & 8 ? 1 : 0;
_crop = flags & 2 ? 0 : 1;
_center = flags & 1 ? 1 : 0;
}
}
FilmLoopCastMember::FilmLoopCastMember(Cast *cast, uint16 castId, FilmLoopCastMember &source)
: CastMember(cast, castId) {
_type = kCastFilmLoop;
// force a load so we can copy the cast resource information
source.load();
_loaded = true;
_initialRect = source._initialRect;
_boundingRect = source._boundingRect;
if (cast == source._cast)
_children = source._children;
_enableSound = source._enableSound;
_crop = source._crop;
_center = source._center;
_frames = source._frames;
_subchannels = source._subchannels;
_looping = source._looping;
}
FilmLoopCastMember::~FilmLoopCastMember() {
}
bool FilmLoopCastMember::isModified() {
if (_frames.size())
return true;
if (_initialRect.width() && _initialRect.height())
return true;
return false;
}
Common::Array<Channel> *FilmLoopCastMember::getSubChannels(Common::Rect &bbox, uint frame) {
Common::Rect widgetRect(bbox.width() ? bbox.width() : _initialRect.width(), bbox.height() ? bbox.height() : _initialRect.height());
_subchannels.clear();
if (frame >= _frames.size()) {
warning("FilmLoopCastMember::getSubChannels(): Film loop frame %d requested, only %d available", frame, _frames.size());
return &_subchannels;
}
// get the list of sprite IDs for this frame
Common::Array<int> spriteIds;
for (auto &iter : _frames[frame].sprites) {
// channels 0 and 1 are used for audio
if (iter._key >= 2)
spriteIds.push_back(iter._key);
}
Common::sort(spriteIds.begin(), spriteIds.end());
debugC(5, kDebugImages, "FilmLoopCastMember::getSubChannels(): castId: %d, frame: %d, count: %d, initRect: %d,%d %dx%d, bbox: %d,%d %dx%d",
_castId, frame, spriteIds.size(),
_initialRect.left + _initialRect.width()/2,
_initialRect.top + _initialRect.height()/2,
_initialRect.width(), _initialRect.height(),
bbox.left + bbox.width()/2,
bbox.top + bbox.height()/2,
bbox.width(), bbox.height());
// copy the sprites in order to the list
for (auto &iter : spriteIds) {
Sprite src = _frames[frame].sprites[iter];
if (!src._cast)
continue;
// translate sprite relative to the global bounding box
int16 relX = (src._startPoint.x - _initialRect.left) * widgetRect.width() / _initialRect.width();
int16 relY = (src._startPoint.y - _initialRect.top) * widgetRect.height() / _initialRect.height();
int16 absX = relX + bbox.left;
int16 absY = relY + bbox.top;
int16 width = src._width * widgetRect.width() / _initialRect.width();
int16 height = src._height * widgetRect.height() / _initialRect.height();
debugC(5, kDebugImages, "FilmLoopCastMember::getSubChannels(): sprite: %d - cast: %s, orig: %d,%d %dx%d, trans: %d,%d %dx%d",
iter, src._castId.asString().c_str(),
src._startPoint.x, src._startPoint.y, src._width, src._height,
absX, absY, width, height);
// Re-inject the translated position into the Sprite.
// This saves the hassle of having to force the Channel to be in puppet mode.
src._width = width;
src._height = height;
src._startPoint = Common::Point(absX, absY);
src._stretch = true;
// Film loop frames are constructed as a series of Channels, much like how a normal frame
// is rendered by the Score. We don't include a pointer to the current Score here,
// that's only for querying the constraint channel which is not used.
Channel chan(nullptr, &src);
_subchannels.push_back(chan);
}
// Initialise the widgets on all of the subchannels.
// This has to be done once the list has been constructed, otherwise
// the list grow operation will erase the widgets as they aren't
// part of the Channel assignment constructor.
for (auto &iter : _subchannels) {
iter.replaceWidget();
}
return &_subchannels;
}
CastMemberID FilmLoopCastMember::getSubChannelSound1(uint frame) {
if (frame >= _frames.size()) {
warning("FilmLoopCastMember::getSubChannelSound1(): Film loop frame %d requested, only %d available", frame, _frames.size());
return CastMemberID();
}
if (_frames[frame].sprites.contains(0)) {
return _frames[frame].sprites[0]._castId;
}
return CastMemberID();
}
CastMemberID FilmLoopCastMember::getSubChannelSound2(uint frame) {
if (frame >= _frames.size()) {
warning("FilmLoopCastMember::getSubChannelSound2(): Film loop frame %d requested, only %d available", frame, _frames.size());
return CastMemberID();
}
if (_frames[frame].sprites.contains(1)) {
return _frames[frame].sprites[1]._castId;
}
return CastMemberID();
}
void FilmLoopCastMember::loadFilmLoopDataD2(Common::SeekableReadStreamEndian &stream) {
_initialRect = Common::Rect();
_frames.clear();
uint32 size = stream.readUint32BE();
if (debugChannelSet(5, kDebugLoading)) {
debugC(5, kDebugLoading, "loadFilmLoopDataD2: SCVW body:");
uint32 pos = stream.pos();
stream.seek(0);
stream.hexdump(size);
stream.seek(pos);
}
uint16 channelSize = kSprChannelSizeD2;
FilmLoopFrame newFrame;
while (stream.pos() < size) {
uint16 frameSize = stream.readUint16BE();
if (frameSize == 0) {
continue;
}
frameSize -= 2;
if (debugChannelSet(5, kDebugLoading)) {
debugC(5, kDebugLoading, "loadFilmLoopDataD2: Frame entry:");
stream.hexdump(frameSize);
}
while (frameSize > 0) {
int msgWidth = stream.readByte() * 2;
int order = stream.readByte() * 2 - 0x20;
frameSize -= 2;
int channel = order / channelSize;
int channelOffset = order % channelSize;
int offset = order;
debugC(8, kDebugLoading, "loadFilmLoopDataD2: Message: msgWidth %d, channel %d, channelOffset %d", msgWidth, channel, channelOffset);
if (debugChannelSet(8, kDebugLoading)) {
stream.hexdump(msgWidth);
}
uint16 segSize = msgWidth;
uint16 nextStart = (channel + 1) * kSprChannelSizeD2;
while (segSize > 0) {
Sprite sprite(nullptr);
sprite._movie = g_director->getCurrentMovie();
if (newFrame.sprites.contains(channel)) {
sprite = newFrame.sprites.getVal(channel);
}
sprite._spriteType = kCastMemberSprite;
sprite._stretch = true;
uint16 needSize = MIN((uint16)(nextStart - offset), segSize);
int startPosition = stream.pos() - channelOffset;
int finishPosition = stream.pos() + needSize;
readSpriteDataD2(stream, sprite, startPosition, finishPosition);
newFrame.sprites.setVal(channel, sprite);
segSize -= needSize;
offset += needSize;
channel += 1;
channelOffset = 0;
nextStart += kSprChannelSizeD2;
}
frameSize -= msgWidth;
}
for (auto &s : newFrame.sprites) {
debugC(5, kDebugLoading, "loadFilmLoopDataD2: Sprite: channel %d, castId %s, bbox %d %d %d %d", s._key,
s._value._castId.asString().c_str(), s._value._startPoint.x, s._value._startPoint.y,
s._value._width, s._value._height);
s._value.setCast(s._value._castId);
Common::Point topLeft = s._value._startPoint;
if (s._value._cast) {
topLeft -= s._value._cast->getRegistrationOffset(s._value._width, s._value._height);
}
Common::Rect spriteBbox(
topLeft.x,
topLeft.y,
topLeft.x + s._value._width,
topLeft.y + s._value._height
);
if (!((spriteBbox.width() == 0) && (spriteBbox.height() == 0))) {
if ((_initialRect.width() == 0) && (_initialRect.height() == 0)) {
_initialRect = spriteBbox;
} else {
_initialRect.extend(spriteBbox);
}
}
debugC(8, kDebugLoading, "loadFilmLoopDataD2: New bounding box: %d %d %d %d", _initialRect.left, _initialRect.top, _initialRect.width(), _initialRect.height());
}
_frames.push_back(newFrame);
}
debugC(5, kDebugLoading, "loadFilmLoopDataD2: Full bounding box: %d %d %d %d", _initialRect.left, _initialRect.top, _initialRect.width(), _initialRect.height());
}
void FilmLoopCastMember::loadFilmLoopDataD4(Common::SeekableReadStreamEndian &stream) {
_initialRect = Common::Rect();
_frames.clear();
uint32 size = stream.readUint32BE();
if (debugChannelSet(8, kDebugLoading)) {
debugC(8, kDebugLoading, "loadFilmLoopDataD4: SCVW body of size: %d", size);
uint32 pos = stream.pos();
stream.seek(0);
stream.hexdump(size);
stream.seek(pos);
}
uint32 framesOffset = stream.readUint32BE();
if (debugChannelSet(8, kDebugLoading)) {
debugC(8, kDebugLoading, "loadFilmLoopDataD4: SCVW header of size: %d", framesOffset - 8);
stream.hexdump(framesOffset - 8);
}
stream.skip(6);
uint16 channelSize = kSprChannelSizeD4;
stream.readUint16BE(); // should be kSprChannelSizeD4 = 20!
stream.skip(framesOffset - 16);
FilmLoopFrame newFrame;
while (stream.pos() < size) {
uint16 frameSize = stream.readUint16BE();
if (frameSize == 0) {
continue;
}
frameSize -= 2;
if (debugChannelSet(8, kDebugLoading)) {
debugC(8, kDebugLoading, "loadFilmLoopDataD4: Frame entry: %d", frameSize);
stream.hexdump(frameSize);
}
while (frameSize > 0) {
uint16 msgWidth = stream.readUint16BE();
uint16 order = stream.readUint16BE();
frameSize -= 4;
int channel = order / channelSize;
int channelOffset = order % channelSize;
int offset = order;
debugC(8, kDebugLoading, "loadFilmLoopDataD4: Message: msgWidth %d, order: %d, channel %d, channelOffset %d", msgWidth, order, channel, channelOffset);
if (debugChannelSet(8, kDebugLoading)) {
stream.hexdump(msgWidth);
}
uint16 segSize = msgWidth;
uint16 nextStart = (channel + 1) * kSprChannelSizeD4;
while (segSize > 0) {
Sprite sprite(nullptr);
sprite._movie = g_director->getCurrentMovie();
if (newFrame.sprites.contains(channel)) {
sprite = newFrame.sprites.getVal(channel);
}
sprite._stretch = true;
uint16 needSize = MIN((uint16)(nextStart - offset), segSize);
int startPosition = stream.pos() - channelOffset;
int finishPosition = stream.pos() + needSize;
readSpriteDataD4(stream, sprite, startPosition, finishPosition);
newFrame.sprites.setVal(channel, sprite);
segSize -= needSize;
offset += needSize;
channel += 1;
channelOffset = 0;
nextStart += kSprChannelSizeD4;
}
frameSize -= msgWidth;
}
for (auto &s : newFrame.sprites) {
debugC(8, kDebugLoading, "loadFilmLoopDataD4: Sprite: channel %d, castId %s, bbox %d %d %d %d", s._key,
s._value._castId.asString().c_str(), s._value._startPoint.x, s._value._startPoint.y,
s._value._width, s._value._height);
if (s._key == -1) {
debugC(8, kDebugLoading, "loadFilmLoopDataD4: Skipping channel -1");
if (s._value._startPoint.x != 0 || s._value._startPoint.y != 0 || s._value._width != 0 ||
(s._value._height != -256 && s._value._height != 0))
warning("BUILDBOT: loadFilmLoopDataD4: Malformed VWSC resource: Sprite: channel %d, castId %s, bbox %d %d %d %d", s._key,
s._value._castId.asString().c_str(), s._value._startPoint.x, s._value._startPoint.y,
s._value._width, s._value._height);
continue;
}
s._value.setCast(s._value._castId);
Common::Point topLeft = s._value._startPoint;
if (s._value._cast) {
topLeft -= s._value._cast->getRegistrationOffset(s._value._width, s._value._height);
}
Common::Rect spriteBbox(
topLeft.x,
topLeft.y,
topLeft.x + s._value._width,
topLeft.y + s._value._height
);
if (!((spriteBbox.width() == 0) && (spriteBbox.height() == 0))) {
if ((_initialRect.width() == 0) && (_initialRect.height() == 0)) {
_initialRect = spriteBbox;
} else {
_initialRect.extend(spriteBbox);
}
}
debugC(8, kDebugLoading, "loadFilmLoopDataD4: New bounding box: %d %d %d %d", _initialRect.left, _initialRect.top, _initialRect.width(), _initialRect.height());
}
_frames.push_back(newFrame);
}
debugC(5, kDebugLoading, "loadFilmLoopDataD4: Full bounding box: %d %d %d %d", _initialRect.left, _initialRect.top, _initialRect.width(), _initialRect.height());
}
void FilmLoopCastMember::loadFilmLoopDataD5(Common::SeekableReadStreamEndian &stream) {
_initialRect = Common::Rect();
_frames.clear();
uint32 size = stream.readUint32BE();
if (debugChannelSet(5, kDebugLoading)) {
debugC(5, kDebugLoading, "loadFilmLoopDataD5: SCVW body:");
uint32 pos = stream.pos();
stream.seek(0);
stream.hexdump(size);
stream.seek(pos);
}
uint32 framesOffset = stream.readUint32BE();
if (debugChannelSet(5, kDebugLoading)) {
debugC(5, kDebugLoading, "loadFilmLoopDataD5: SCVW header:");
stream.hexdump(framesOffset - 8);
}
stream.skip(6);
uint16 channelSize = kSprChannelSizeD5;
stream.readUint16BE(); // should be kSprChannelSizeD5 = 24!
stream.skip(framesOffset - 16);
FilmLoopFrame newFrame;
while (stream.pos() < size) {
uint16 frameSize = stream.readUint16BE();
if (frameSize == 0) {
continue;
}
frameSize -= 2;
if (debugChannelSet(5, kDebugLoading)) {
debugC(5, kDebugLoading, "loadFilmLoopDataD5: Frame entry:");
stream.hexdump(frameSize);
}
while (frameSize > 0) {
uint16 msgWidth = stream.readUint16BE();
uint16 order = stream.readUint16BE();
frameSize -= 4;
int channel = order / channelSize;
int channelOffset = order % channelSize;
int offset = order;
debugC(8, kDebugLoading, "loadFilmLoopDataD5: Message: msgWidth %d, channel %d, channelOffset %d", msgWidth, channel, channelOffset);
if (debugChannelSet(8, kDebugLoading)) {
stream.hexdump(msgWidth);
}
uint16 segSize = msgWidth;
uint16 nextStart = (channel + 1) * kSprChannelSizeD5;
while (segSize > 0) {
Sprite sprite(nullptr);
sprite._movie = g_director->getCurrentMovie();
if (newFrame.sprites.contains(channel)) {
sprite = newFrame.sprites.getVal(channel);
}
sprite._stretch = true;
uint16 needSize = MIN((uint16)(nextStart - offset), segSize);
int startPosition = stream.pos() - channelOffset;
int finishPosition = stream.pos() + needSize;
readSpriteDataD5(stream, sprite, startPosition, finishPosition);
// Swap castLib ID value of -1 for the film loop's castLib ID
if (sprite._castId.castLib == -1)
sprite._castId.castLib = _cast->_castLibID;
if (sprite._scriptId.castLib == -1)
sprite._scriptId.castLib = _cast->_castLibID;
newFrame.sprites.setVal(channel, sprite);
segSize -= needSize;
offset += needSize;
channel += 1;
channelOffset = 0;
nextStart += kSprChannelSizeD5;
}
frameSize -= msgWidth;
}
for (auto &s : newFrame.sprites) {
debugC(5, kDebugLoading, "loadFilmLoopDataD5: Sprite: channel %d, castId %s, bbox %d %d %d %d", s._key,
s._value._castId.asString().c_str(), s._value._startPoint.x, s._value._startPoint.y,
s._value._width, s._value._height);
if (s._key == -1) {
debugC(5, kDebugLoading, "loadFilmLoopDataD5: Skipping channel -1");
if (s._value._startPoint.x != 0 || s._value._startPoint.y != 0 || s._value._width != 0 ||
(s._value._height != -256 && s._value._height != 0))
warning("BUILDBOT: loadFilmLoopDataD5: Malformed VWSC resource: Sprite: channel %d, castId %s, bbox %d %d %d %d", s._key,
s._value._castId.asString().c_str(), s._value._startPoint.x, s._value._startPoint.y,
s._value._width, s._value._height);
continue;
}
s._value.setCast(s._value._castId);
Common::Point topLeft = s._value._startPoint;
if (s._value._cast) {
topLeft -= s._value._cast->getRegistrationOffset(s._value._width, s._value._height);
}
Common::Rect spriteBbox(
topLeft.x,
topLeft.y,
topLeft.x + s._value._width,
topLeft.y + s._value._height
);
if (!((spriteBbox.width() == 0) && (spriteBbox.height() == 0))) {
if ((_initialRect.width() == 0) && (_initialRect.height() == 0)) {
_initialRect = spriteBbox;
} else {
_initialRect.extend(spriteBbox);
}
}
debugC(8, kDebugLoading, "loadFilmLoopDataD5: New bounding box: %d %d %d %d", _initialRect.left, _initialRect.top, _initialRect.width(), _initialRect.height());
}
_frames.push_back(newFrame);
}
debugC(5, kDebugLoading, "loadFilmLoopDataD5: Full bounding box: %d %d %d %d", _initialRect.left, _initialRect.top, _initialRect.width(), _initialRect.height());
}
void FilmLoopCastMember::loadFilmLoopDataD6(Common::SeekableReadStreamEndian &stream) {
_initialRect = Common::Rect();
_frames.clear();
uint32 size = stream.readUint32BE();
if (debugChannelSet(5, kDebugLoading)) {
debugC(5, kDebugLoading, "loadFilmLoopDataD6: SCVW body:");
uint32 pos = stream.pos();
stream.seek(0);
stream.hexdump(size);
stream.seek(pos);
}
uint32 framesOffset = stream.readUint32BE();
if (debugChannelSet(5, kDebugLoading)) {
debugC(5, kDebugLoading, "loadFilmLoopDataD6: SCVW header:");
stream.hexdump(framesOffset - 8);
}
stream.skip(6);
uint16 channelSize = kSprChannelSizeD6;
stream.readUint16BE(); // should be kSprChannelSizeD6 = 24!
stream.skip(framesOffset - 16);
FilmLoopFrame newFrame;
while (stream.pos() < size) {
uint16 frameSize = stream.readUint16BE();
if (frameSize == 0) {
continue;
}
frameSize -= 2;
if (debugChannelSet(5, kDebugLoading)) {
debugC(5, kDebugLoading, "loadFilmLoopDataD6: Frame entry:");
stream.hexdump(frameSize);
}
while (frameSize > 0) {
uint16 msgWidth = stream.readUint16BE();
uint16 order = stream.readUint16BE();
frameSize -= 4;
int channel = order / channelSize;
int channelOffset = order % channelSize;
int offset = order;
debugC(8, kDebugLoading, "loadFilmLoopDataD6: Message: msgWidth %d, channel %d, channelOffset %d", msgWidth, channel, channelOffset);
if (debugChannelSet(8, kDebugLoading)) {
stream.hexdump(msgWidth);
}
uint16 segSize = msgWidth;
uint16 nextStart = (channel + 1) * kSprChannelSizeD4;
while (segSize > 0) {
Sprite sprite(nullptr);
sprite._movie = g_director->getCurrentMovie();
if (newFrame.sprites.contains(channel)) {
sprite = newFrame.sprites.getVal(channel);
}
sprite._stretch = true;
uint16 needSize = MIN((uint16)(nextStart - offset), segSize);
int startPosition = stream.pos() - channelOffset;
int finishPosition = stream.pos() + needSize;
readSpriteDataD6(stream, sprite, startPosition, finishPosition);
// Swap castLib ID value of -1 for the film loop's castLib ID
if (sprite._castId.castLib == -1)
sprite._castId.castLib = _cast->_castLibID;
if (sprite._scriptId.castLib == -1)
sprite._scriptId.castLib = _cast->_castLibID;
newFrame.sprites.setVal(channel, sprite);
segSize -= needSize;
offset += needSize;
channel += 1;
channelOffset = 0;
nextStart += kSprChannelSizeD6;
}
frameSize -= msgWidth;
}
for (auto &s : newFrame.sprites) {
debugC(5, kDebugLoading, "loadFilmLoopDataD6: Sprite: channel %d, castId %s, bbox %d %d %d %d", s._key,
s._value._castId.asString().c_str(), s._value._startPoint.x, s._value._startPoint.y,
s._value._width, s._value._height);
if (s._key == -1) {
debugC(5, kDebugLoading, "loadFilmLoopDataD6: Skipping channel -1");
if (s._value._startPoint.x != 0 || s._value._startPoint.y != 0 || s._value._width != 0 ||
(s._value._height != -256 && s._value._height != 0))
warning("BUILDBOT: loadFilmLoopDataD6: Malformed VWSC resource: Sprite: channel %d, castId %s, bbox %d %d %d %d", s._key,
s._value._castId.asString().c_str(), s._value._startPoint.x, s._value._startPoint.y,
s._value._width, s._value._height);
continue;
}
s._value.setCast(s._value._castId);
Common::Point topLeft = s._value._startPoint;
if (s._value._cast) {
topLeft -= s._value._cast->getRegistrationOffset(s._value._width, s._value._height);
}
Common::Rect spriteBbox(
topLeft.x,
topLeft.y,
topLeft.x + s._value._width,
topLeft.y + s._value._height
);
if (!((spriteBbox.width() == 0) && (spriteBbox.height() == 0))) {
if ((_initialRect.width() == 0) && (_initialRect.height() == 0)) {
_initialRect = spriteBbox;
} else {
_initialRect.extend(spriteBbox);
}
}
debugC(8, kDebugLoading, "loadFilmLoopDataD6: New bounding box: %d %d %d %d", _initialRect.left, _initialRect.top, _initialRect.width(), _initialRect.height());
}
_frames.push_back(newFrame);
}
debugC(5, kDebugLoading, "loadFilmLoopDataD6: Full bounding box: %d %d %d %d", _initialRect.left, _initialRect.top, _initialRect.width(), _initialRect.height());
}
Common::String FilmLoopCastMember::formatInfo() {
return Common::String::format(
"initialRect: %dx%d@%d,%d, boundingRect: %dx%d@%d,%d, frameCount: %d, subchannelCount: %d, enableSound: %d, looping: %d, crop: %d, center: %d",
_initialRect.width(), _initialRect.height(),
_initialRect.left, _initialRect.top,
_boundingRect.width(), _boundingRect.height(),
_boundingRect.left, _boundingRect.top,
_frames.size(), _subchannels.size(), _enableSound, _looping,
_crop, _center
);
}
void FilmLoopCastMember::load() {
if (_loaded)
return;
if (_cast->_version < kFileVer400) {
// Director 3 and below should have a SCVW resource
uint16 filmLoopId = _castId + _cast->_castIDoffset;
uint32 tag = MKTAG('S', 'C', 'V', 'W');
Common::SeekableReadStreamEndian *loop = _cast->getResource(tag, filmLoopId);
if (loop) {
debugC(2, kDebugLoading, "****** Loading '%s' id: %d, %d bytes", tag2str(tag), filmLoopId, (int)loop->size());
loadFilmLoopDataD2(*loop);
delete loop;
} else {
warning("FilmLoopCastMember::load(): Film loop not found");
}
} else if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer700) {
Common::SeekableReadStreamEndian *loop = nullptr;
uint16 filmLoopId = 0;
uint32 tag = 0;
for (auto &it : _children) {
if (it.tag == MKTAG('S', 'C', 'V', 'W')) {
filmLoopId = it.index;
tag = it.tag;
loop = _cast->getResource(tag, filmLoopId);
break;
}
}
if (loop) {
debugC(2, kDebugLoading, "****** Loading '%s' id: %d, %d bytes", tag2str(tag), filmLoopId, (int)loop->size());
if (_cast->_version < kFileVer500) {
loadFilmLoopDataD4(*loop);
} else if (_cast->_version < kFileVer600) {
loadFilmLoopDataD5(*loop);
} else if (_cast->_version < kFileVer700) {
loadFilmLoopDataD6(*loop);
}
delete loop;
} else {
warning("FilmLoopCastMember::load(): No SCVW resource found in %d children", _children.size());
}
} else {
warning("STUB: FilmLoopCastMember::load(): Film loops not yet supported for version v%d (%d)", humanVersion(_cast->_version), _cast->_version);
}
_loaded = true;
}
void FilmLoopCastMember::unload() {
// No unload necessary.
}
Common::Point FilmLoopCastMember::getRegistrationOffset() {
return Common::Point(_initialRect.width() / 2, _initialRect.height() / 2);
}
Common::Point FilmLoopCastMember::getRegistrationOffset(int16 currentWidth, int16 currentHeight) {
return Common::Point(currentWidth / 2, currentHeight / 2);
}
uint32 FilmLoopCastMember::getCastDataSize() {
// We're only reading the _initialRect and _vflags from the Cast Data
// _initialRect : 8 bytes + flags : 4 bytes + 2 bytes unk1 + 2 bytes (castType and _flags1 (see Cast::loadCastData() for Director 4 only)
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
// It has been observed that the FilmCastMember has _flags as 0x00
return 8 + 4 + 2 + 2;
} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
return 8 + 4 + 2;
}
warning("FilmLoopCastMember::getCastDataSize(): unhandled or invalid cast version: %d", _cast->_version);
return 0;
}
void FilmLoopCastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
Movie::writeRect(writeStream, _initialRect);
uint32 flags = 0;
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
flags |= (_looping) ? 0 : 64;
flags |= (_enableSound) ? 8 : 0;
flags |= (_crop) ? 0 : 2;
flags |= (_center) ? 1 : 0;
} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
flags |= (_looping) ? 0 : 32;
flags |= (_enableSound) ? 8 : 0;
flags |= (_crop) ? 0 : 2;
flags |= (_center) ? 1 : 0;
}
writeStream->writeUint32LE(flags);
writeStream->writeUint16LE(0); // May need to save proper value in the future, currently ignored
}
void FilmLoopCastMember::writeSCVWResource(Common::SeekableWriteStream *writeStream, uint32 offset) {
// Load it before writing
if (!_loaded) {
load();
}
uint32 channelSize = 0;
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
channelSize = kSprChannelSizeD4;
} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
channelSize = kSprChannelSizeD5;
} else {
warning("FilmLoopCastMember::writeSCVWResource: Writing Director Version 6+ not supported yet");
return;
}
// Go to the desired offset put in the memory map
writeStream->seek(offset);
uint32 filmloopSize = getSCVWResourceSize();
debugC(5, kDebugSaving, "FilmLoopCastmember::writeSCVWResource: Saving FilmLoop 'SCVW' data of size: %d", filmloopSize);
writeStream->writeUint32LE(MKTAG('S', 'C', 'V', 'W'));
writeStream->writeUint32LE(filmloopSize); // Size of the resource
writeStream->writeUint32BE(filmloopSize);
uint32 frameOffset = 20; // Should be greater than 20
writeStream->writeUint32BE(frameOffset); // framesOffset
writeStream->seek(6, SEEK_CUR); // Ignored data
writeStream->writeUint16BE(channelSize);
writeStream->seek(frameOffset - 16, SEEK_CUR); // Ignored data
// The structure of the filmloop 'SCVW' data is as follows
// The 'SCVW' tag -> the size of the resource ->
// frameoffset (This offset is where the frame date actually starts) ->
// Some headers which we ignore except the Sprite Channel Size (which we also ignore during loading) ->
// until there are no more frames
// size of the frame ->
// until there are no more channels in the frame
// width of message (One chunk of data) (This is the size of data for the sprite that needs to be read) ->
// order of message (this order tells us the channel we're reading) ->
// 1-20 bytes of Sprite data
for (FilmLoopFrame frame : _frames) {
writeStream->writeUint16BE(frame.sprites.size() * (channelSize + 4) + 2); // Frame Size
for (auto it : frame.sprites) {
int channel = it._key;
// TODO: For now writing the order considering that each sprite will have 20 bytes of data
// In the future, for optimization, we can actually calculate the data of each sprite
// And write the order accordingly
// But for this we'll need a way to find how many data values (out of 20) of a sprite are valid, i.e. determine message width
// this means while loading, the channelOffset will always be 0, order will always be multiple of 20
// And message width will always be 20
// Channel indexes start with 0
writeStream->writeUint16BE(channelSize); // message width
writeStream->writeUint16BE(channel * channelSize);
Sprite sprite = it._value;
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
writeSpriteDataD4(writeStream, sprite);
} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
writeSpriteDataD5(writeStream, sprite);
}
}
}
if (debugChannelSet(7, kDebugSaving)) {
// Adding +8 because the stream doesn't include the header and the entry for the size itself
byte *dumpData = (byte *)calloc(filmloopSize + 8, sizeof(byte));
Common::SeekableMemoryWriteStream *dumpStream = new Common::SeekableMemoryWriteStream(dumpData, filmloopSize + 8);
uint32 currentPos = writeStream->pos();
writeStream->seek(offset);
dumpStream->write(writeStream, filmloopSize + 8);
writeStream->seek(currentPos);
dumpFile("FilmLoopData", 0, MKTAG('V', 'W', 'C', 'F'), dumpData, filmloopSize);
free(dumpData);
delete dumpStream;
}
}
uint32 FilmLoopCastMember::getSCVWResourceSize() {
uint32 channelSize = 0;
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
channelSize = kSprChannelSizeD4;
} else if (_cast->_version >= kFileVer500) {
channelSize = kSprChannelSizeD5;
} else {
warning("FilmLoopCastMember::getSCVWResourceSize: Director version unsupported");
}
uint32 framesSize = 0;
for (FilmLoopFrame frame : _frames) {
// Frame size
framesSize += 2;
for (auto it : frame.sprites) {
// message width: 2 bytes
// order: 2 bytes
// Sprite data: 20 bytes
framesSize += 2 + 2 + channelSize;
}
}
// Size: 4 bytes
// frameoffset: 4 bytes
// Header (Ignored data): 16 bytes
return 4 + 4 + 16 + framesSize;
}
} // End of namespace Director

View File

@@ -0,0 +1,81 @@
/* 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/>.
*
*/
#ifndef DIRECTOR_CASTMEMBER_FILMLOOP_H
#define DIRECTOR_CASTMEMBER_FILMLOOP_H
#include "director/director.h"
#include "director/castmember/castmember.h"
namespace Director {
class Sprite;
struct FilmLoopFrame {
Common::HashMap<int, Sprite> sprites;
};
class FilmLoopCastMember : public CastMember {
public:
FilmLoopCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version);
FilmLoopCastMember(Cast *cast, uint16 castId, FilmLoopCastMember &source);
~FilmLoopCastMember();
CastMember *duplicate(Cast *cast, uint16 castId) override { return (CastMember *)(new FilmLoopCastMember(cast, castId, *this)); }
bool isModified() override;
//Graphics::MacWidget *createWidget(Common::Rect &bbox, Channel *channel, SpriteType spriteType) override;
virtual Common::Array<Channel> *getSubChannels(Common::Rect &bbox, uint frame);
virtual CastMemberID getSubChannelSound1(uint frame);
virtual CastMemberID getSubChannelSound2(uint frame);
void loadFilmLoopDataD2(Common::SeekableReadStreamEndian &stream);
void loadFilmLoopDataD4(Common::SeekableReadStreamEndian &stream);
void loadFilmLoopDataD5(Common::SeekableReadStreamEndian &stream);
void loadFilmLoopDataD6(Common::SeekableReadStreamEndian &stream);
Common::String formatInfo() override;
void load() override;
void unload() override;
Common::Point getRegistrationOffset() override;
Common::Point getRegistrationOffset(int16 currentWidth, int16 currentHeight) override;
uint32 getCastDataSize() override;
void writeCastData(Common::SeekableWriteStream *writeStream) override;
void writeSCVWResource(Common::SeekableWriteStream *writeStream, uint32 offset);
uint32 getSCVWResourceSize();
bool _enableSound;
bool _looping;
bool _crop;
bool _center;
Common::Array<FilmLoopFrame> _frames;
Common::Array<Channel> _subchannels;
};
} // End of namespace Director
#endif

View File

@@ -0,0 +1,163 @@
/* 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 "director/director.h"
#include "director/movie.h"
#include "director/sprite.h"
#include "director/castmember/movie.h"
#include "director/lingo/lingo-the.h"
namespace Director {
MovieCastMember::MovieCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version)
: FilmLoopCastMember(cast, castId, stream, version) {
_type = kCastMovie;
_enableScripts = _flags & 0x10;
if (debugChannelSet(2, kDebugLoading))
_initialRect.debugPrint(2, "MovieCastMember(): rect:");
debugC(2, kDebugLoading, "MovieCastMember(): flags: (%d 0x%04x)", _flags, _flags);
debugC(2, kDebugLoading, "_looping: %d, _enableScripts %d, _enableSound: %d, _crop %d, _center: %d",
_looping, _enableScripts, _enableSound, _crop, _center);
}
MovieCastMember::MovieCastMember(Cast *cast, uint16 castId, MovieCastMember &source)
: FilmLoopCastMember(cast, castId, source) {
_type = kCastMovie;
_enableScripts = source._enableScripts;
}
Common::Array<Channel> *MovieCastMember::getSubChannels(Common::Rect &bbox, uint frame) {
if (_needsReload) {
_loaded = false;
load();
}
return FilmLoopCastMember::getSubChannels(bbox, frame);
}
void MovieCastMember::load() {
if (_loaded)
return;
FilmLoopCastMember::load();
_loaded = true;
_needsReload = false;
}
bool MovieCastMember::hasField(int field) {
switch (field) {
case kTheCenter:
case kTheIdleHandlerPeriod:
case kThePaletteMapping:
case kTheScoreSelection:
case kTheScriptsEnabled:
case kTheSound:
case kTheUpdateLock:
return true;
default:
break;
}
return CastMember::hasField(field);
}
Datum MovieCastMember::getField(int field) {
Datum d;
switch (field) {
case kTheCenter:
d = Datum((int)_center);
break;
case kTheIdleHandlerPeriod:
warning("STUB: MovieCastMember::getField(): idleHandlerPeriod not implemented");
break;
case kThePaletteMapping:
warning("STUB: MovieCastMember::getField(): paletteMapping not implemented");
break;
case kTheScoreSelection:
warning("STUB: MovieCastMember::getField(): scoreSelection not implemented");
break;
case kTheScriptsEnabled:
d = Datum(_enableScripts);
break;
case kTheSound:
d = Datum(_enableSound);
break;
case kTheUpdateLock:
warning("STUB: MovieCastMember::getField(): updateLock not implemented");
break;
default:
d = CastMember::getField(field);
break;
}
return d;
}
void MovieCastMember::setField(int field, const Datum &d) {
switch (field) {
case kTheCenter:
_center = (bool)d.asInt();
return;
case kTheIdleHandlerPeriod:
warning("STUB: MovieCastMember::setField(): idleHandlerPeriod not implemented");
return;
case kThePaletteMapping:
warning("STUB: MovieCastMember::setField(): paletteMapping not implemented");
return;
case kTheScoreSelection:
warning("STUB: MovieCastMember::setField(): scoreSelection not implemented");
return;
case kTheScriptsEnabled:
_enableScripts = (bool)d.asInt();
return;
case kTheSound:
_enableSound = (bool)d.asInt();
return;
case kTheUpdateLock:
warning("STUB: MovieCastMember::setField(): updateLock not implemented");
return;
default:
break;
}
CastMember::setField(field, d);
}
Common::String MovieCastMember::formatInfo() {
return Common::String::format(
"initialRect: %dx%d@%d,%d, boundingRect: %dx%d@%d,%d, enableScripts: %d, enableSound: %d, looping: %d, crop: %d, center: %d",
_initialRect.width(), _initialRect.height(),
_initialRect.left, _initialRect.top,
_boundingRect.width(), _boundingRect.height(),
_boundingRect.left, _boundingRect.top,
_enableScripts, _enableSound, _looping,
_crop, _center
);
}
} // End of namespace Director

View File

@@ -0,0 +1,51 @@
/* 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/>.
*
*/
#ifndef DIRECTOR_CASTMEMBER_MOVIE_H
#define DIRECTOR_CASTMEMBER_MOVIE_H
#include "director/castmember/filmloop.h"
namespace Director {
class MovieCastMember : public FilmLoopCastMember {
public:
MovieCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version);
MovieCastMember(Cast *cast, uint16 castId, MovieCastMember &source);
CastMember *duplicate(Cast *cast, uint16 castId) override { return (CastMember *)(new MovieCastMember(cast, castId, *this)); }
Common::Array<Channel> *getSubChannels(Common::Rect &bbox, uint frame) override;
void load() override;
bool hasField(int field) override;
Datum getField(int field) override;
void setField(int field, const Datum &value) override;
Common::String formatInfo() override;
uint32 _flags;
bool _enableScripts;
};
} // End of namespace Director
#endif

View File

@@ -0,0 +1,202 @@
/* 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/memstream.h"
#include "director/director.h"
#include "director/cast.h"
#include "director/castmember/palette.h"
namespace Director {
PaletteCastMember::PaletteCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version)
: CastMember(cast, castId, stream) {
stream.hexdump(stream.size());
_type = kCastPalette;
_palette = nullptr;
}
PaletteCastMember::PaletteCastMember(Cast *cast, uint16 castId, PaletteCastMember &source)
: CastMember(cast, castId) {
_type = kCastPalette;
// force a load so we can copy the cast resource information
source.load();
_loaded = true;
_palette = source._palette ? new PaletteV4(*source._palette) : nullptr;
}
PaletteCastMember::PaletteCastMember(Cast *cast, uint16 castId, byte *paletteData, PaletteV4 *pal)
: CastMember(cast, castId) {
_type = kCastPalette;
_palette = new PaletteV4(pal->id, paletteData, pal->length);
_loaded = true;
}
// Need to make a deep copy
CastMember *PaletteCastMember::duplicate(Cast *cast, uint16 castId) {
byte *buf = (byte *)malloc(_palette->length);
memcpy(buf, _palette, _palette->length);
return (CastMember *)(new PaletteCastMember(cast, castId, buf, _palette));
}
PaletteCastMember::~PaletteCastMember() {
if (_palette) {
delete[] _palette->palette;
delete _palette;
}
}
CastMemberID PaletteCastMember::getPaletteId() {
load();
return _palette ? _palette->id : CastMemberID();
}
void PaletteCastMember::activatePalette() {
load();
if (_palette)
g_director->setPalette(_palette->id);
}
Common::String PaletteCastMember::formatInfo() {
Common::String result;
if (_palette) {
result = "data: ";
for (size_t i = 0; i < (size_t)_palette->length; i++) {
result += Common::String::format("%02X%02X%02X", _palette->palette[3 * i], _palette->palette[3 * i + 1], _palette->palette[3 * i + 2]);
}
}
return result;
}
void PaletteCastMember::load() {
if (_loaded)
return;
// TODO: Verify how palettes work in >D4 versions
int paletteId = 0;
if (_cast->_version < kFileVer400) {
// For D3 and below, palette IDs are stored in the CLUT resource as cast ID + 1024
paletteId = _castId + _cast->_castIDoffset;
} else if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer1100) {
for (auto &it : _children) {
if (it.tag == MKTAG('C', 'L', 'U', 'T')) {
paletteId = it.index;
break;
}
}
if (!paletteId) {
warning("PaletteCastMember::load(): No CLUT resource found in %d children", _children.size());
}
} else {
warning("STUB: PaletteCastMember::load(): Palettes not yet supported for version v%d (%d)", humanVersion(_cast->_version), _cast->_version);
}
if (paletteId) {
uint32 tag = MKTAG('C', 'L', 'U', 'T');
Archive *arch = _cast->getArchive();
if (arch->hasResource(tag, paletteId)) {
Common::SeekableReadStreamEndian *pal = arch->getResource(MKTAG('C', 'L', 'U', 'T'), paletteId);
debugC(2, kDebugImages, "PaletteCastMember::load(): linking palette id %d to cast index %d", paletteId, _castId);
PaletteV4 palData = _cast->loadPalette(*pal, paletteId);
palData.id = CastMemberID(_castId, _cast->_castLibID);
g_director->addPalette(palData.id, palData.palette, palData.length);
_palette = new PaletteV4(palData);
delete pal;
} else {
warning("PaletteCastMember::load(): no CLUT palette %d for cast index %d found", paletteId, _castId);
}
}
_loaded = true;
}
void PaletteCastMember::unload() {
// No unload necessary.
}
// PaletteCastMember has no data in the 'CASt' resource or is ignored
// This is the data in 'CASt' resource
uint32 PaletteCastMember::getCastDataSize() {
if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
// It has been observed as well that the Data size in PaletteCastMember's CASt resource is 0 for d5
return 0;
} else if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
// (castType (see Cast::loadCastData() for Director 4 only) 1 byte
return 1; // Since SoundCastMember doesn't have any flags
}
return 0;
}
void PaletteCastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
// This should never get triggered
// Since there is no data to write
}
uint32 PaletteCastMember::getPaletteDataSize() {
// This is the actual Palette data, in the 'CLUT' resource
// PaletteCastMembers data stored in the 'CLUT' resource does not change in size (may change in content) (need to verify)
// Hence their original size can be written
// This is the length of the 'CLUT' resource without the header and size
return _palette->length * 6;
}
void PaletteCastMember::writePaletteData(Common::SeekableWriteStream *writeStream, uint32 offset) {
// Load it before writing
if (!_loaded) {
load();
}
uint32 castSize = getPaletteDataSize();
writeStream->seek(offset);
writeStream->writeUint32LE(MKTAG('C', 'L', 'U', 'T'));
writeStream->writeUint32LE(castSize);
const byte *pal = _palette->palette;
for (int i = 0; i < _palette->length; i++) {
// Duplicating the data to convert to 16-bit
// The palette data is converted to 8-bit at the time of loading
writeStream->writeUint16BE(pal[3 * i] << 8);
writeStream->writeUint16BE(pal[3 * i + 1] << 8);
writeStream->writeUint16BE(pal[3 * i + 2] << 8);
}
if (debugChannelSet(7, kDebugSaving)) {
byte *dumpData = nullptr;
dumpData = (byte *)calloc(castSize, sizeof(byte));
auto dumpStream = new Common::SeekableMemoryWriteStream(dumpData, castSize + 8);
uint32 currentPos = writeStream->pos();
writeStream->seek(offset);
dumpStream->write(writeStream, castSize);
writeStream->seek(currentPos);
dumpFile("PaletteData", _castId, MKTAG('C', 'L', 'U', 'T'), dumpData, castSize);
free(dumpData);
delete dumpStream;
}
}
} // End of namespace Director

View File

@@ -0,0 +1,57 @@
/* 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/>.
*
*/
#ifndef DIRECTOR_CASTMEMBER_PALETTE_H
#define DIRECTOR_CASTMEMBER_PALETTE_H
#include "director/castmember/castmember.h"
namespace Director {
class PaletteCastMember : public CastMember {
public:
PaletteCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version);
PaletteCastMember(Cast *cast, uint16 castId, PaletteCastMember &source);
PaletteCastMember(Cast *cast, uint16 castId, byte *paletteData, PaletteV4 *palette);
~PaletteCastMember();
CastMember *duplicate(Cast *cast, uint16 castId) override;
CastMemberID getPaletteId();
void activatePalette();
Common::String formatInfo() override;
void load() override;
void unload() override;
uint32 getCastDataSize() override; // This is the size of the data in the 'CASt' resource
void writeCastData(Common::SeekableWriteStream *writeStream) override;
void writePaletteData(Common::SeekableWriteStream *writeStream, uint32 offset);
uint32 getPaletteDataSize();
PaletteV4 *_palette;
};
} // End of namespace Director
#endif

View File

@@ -0,0 +1,312 @@
/* 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/stream.h"
#include "graphics/macgui/macwidget.h"
#include "director/director.h"
#include "director/cast.h"
#include "director/channel.h"
#include "director/images.h"
#include "director/movie.h"
#include "director/picture.h"
#include "director/rte.h"
#include "director/score.h"
#include "director/window.h"
#include "director/castmember/richtext.h"
#include "director/lingo/lingo-the.h"
namespace Director {
RichTextCastMember::RichTextCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version)
: CastMember(cast, castId, stream) {
_pf32 = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
if (version >= kFileVer500 && version < kFileVer1100) {
if (debugChannelSet(5, kDebugLoading)) {
debugC(5, kDebugLoading, "RichTextCastMember():");
stream.hexdump(stream.size());
}
_initialRect = Movie::readRect(stream);
_boundingRect = Movie::readRect(stream);
_antialiasFlag = stream.readByte();
_cropFlags = stream.readByte();
_scrollPos = stream.readUint16BE();
_antialiasFontSize = stream.readUint16BE();
_displayHeight = stream.readUint16BE();
uint8 r = 0, g = 0, b = 0;
stream.readByte(); // skip one byte
r = stream.readByte();
g = stream.readByte();
b = stream.readByte();
_foreColor = _pf32.RGBToColor(r, g, b);
r = (stream.readUint16BE() >> 8);
g = (stream.readUint16BE() >> 8);
b = (stream.readUint16BE() >> 8);
_bgColor = _pf32.RGBToColor(r, g, b);
debugC(3, kDebugLoading, " RichTextCastMember(): initialRect: [%s], boundingRect: [%s], antialiasFlag: 0x%02x, cropFlags: 0x%02x, scrollPos: %d, antialiasFontSize: %d, displayHeight: %d",
_initialRect.toString().c_str(),
_boundingRect.toString().c_str(),
_antialiasFlag,
_cropFlags,
_scrollPos,
_antialiasFontSize,
_displayHeight);
debugC(3, kDebugLoading, " RichTextCastMember(): foreColor: 0x%08x, bgColor: 0x%08x", _foreColor, _bgColor);
} else {
warning("STUB: RichTextCastMember: RTE not yet supported for version v%d (%d)", humanVersion(_cast->_version), _cast->_version);
}
_type = kCastRichText;
_picture = nullptr;
_pictureWithBg = nullptr;
}
RichTextCastMember::RichTextCastMember(Cast *cast, uint16 castId, RichTextCastMember &source)
: CastMember(cast, castId) {
_type = kCastRichText;
_initialRect = source._initialRect;
_boundingRect = source._boundingRect;
_bgColor = source._bgColor;
if (cast == source._cast)
_children = source._children;
}
RichTextCastMember::~RichTextCastMember() {
if (_picture)
delete _picture;
if (_pictureWithBg)
delete _pictureWithBg;
}
void RichTextCastMember::load() {
if (_loaded)
return;
// RichText casts consist of 3 files:
// RTE0: Editor data, used only by the Authoring Tool
// RTE1: Plain text data
// RTE2: Bitmap representation for rendering
//
// RTE0 is using Paige editor by Hermes, which was recently
// open sourced. So, if anyone wants to look into internals,
// https://github.com/nmatavka/Hermes-Paige/tree/main
// the pgReadDoc() is the code entry:
// https://github.com/nmatavka/Hermes-Paige/blob/main/PGSOURCE/PGREAD.C#L767
uint rte0id = 0;
uint rte1id = 0;
uint rte2id = 0;
for (auto &it : _children) {
if (it.tag == MKTAG('R', 'T', 'E', '0')) {
rte0id = it.index;
} else if (it.tag == MKTAG('R', 'T', 'E', '1')) {
rte1id = it.index;
} else if (it.tag == MKTAG('R', 'T', 'E', '2')) {
rte2id = it.index;
}
}
if (_cast->_loadedRTE0s.contains(rte0id)) {
// TODO: Copy the formatted text data
// There doesn't appear to be a way of using it outside of the Director editor.
} else {
warning("RichTextCastMember::load(): rte0tid %i isn't loaded", rte0id);
}
if (_cast->_loadedRTE1s.contains(rte1id)) {
const RTE1 *rte1 = _cast->_loadedRTE1s.getVal(rte1id);
if (!rte1->data.empty())
_plainText = Common::U32String((const char *)&rte1->data[0], rte1->data.size(), g_director->getPlatformEncoding());
} else {
warning("RichTextCastMember::load(): rte1tid %i isn't loaded, no plain text!", rte1id);
}
if (_cast->_loadedRTE2s.contains(rte2id)) {
_picture = new Picture();
const RTE2 *rte2 = _cast->_loadedRTE2s.getVal(rte2id);
Graphics::ManagedSurface *surface = rte2->createSurface(_foreColor, _bgColor, _pf32, false);
if (surface) {
_picture->_surface.copyFrom(surface->rawSurface());
surface->free();
delete surface;
}
_pictureWithBg = new Picture();
surface = rte2->createSurface(_foreColor, _bgColor, _pf32, true);
if (surface) {
_pictureWithBg->_surface.copyFrom(surface->rawSurface());
surface->free();
delete surface;
}
} else {
warning("RichTextCastMember::load(): rte2tid %i isn't loaded, no bitmap text!", rte2id);
}
_loaded = true;
}
Graphics::MacWidget *RichTextCastMember::createWidget(Common::Rect &bbox, Channel *channel, SpriteType spriteType) {
if (!_picture) {
warning("RichTextCastMember::createWidget: No picture");
return nullptr;
}
// skip creating widget when the bbox is not available, maybe we should create it using initialRect
if (!bbox.width() || !bbox.height())
return nullptr;
// Check if we need to dither the image
int dstBpp = g_director->_wm->_pixelformat.bytesPerPixel;
Graphics::MacWidget *widget = new Graphics::MacWidget(g_director->getCurrentWindow()->getMacWindow(), bbox.left, bbox.top, bbox.width(), bbox.height(), g_director->_wm, false);
Graphics::Surface *dithered = nullptr;
Picture *src = _pictureWithBg;
if (channel->_sprite->_ink == kInkTypeBackgndTrans)
src = _picture;
if (dstBpp == 1) {
dithered = src->_surface.convertTo(g_director->_wm->_pixelformat, nullptr, 0, g_director->_wm->getPalette(), g_director->_wm->getPaletteSize());
}
// scale for drawing a different size sprite
copyStretchImg(
dithered ? dithered : &src->_surface,
widget->getSurface()->surfacePtr(),
_initialRect,
bbox,
g_director->_wm->getPalette()
);
if (dithered) {
dithered->free();
delete dithered;
}
return widget;
}
bool RichTextCastMember::hasField(int field) {
switch (field) {
case kTheText:
case kThePageHeight:
case kTheScrollTop:
return true;
default:
break;
}
return CastMember::hasField(field);
}
Datum RichTextCastMember::getField(int field) {
Datum d;
switch (field) {
case kTheText:
d = Datum(Common::String(_plainText));
break;
case kThePageHeight:
case kTheScrollTop:
default:
d = CastMember::getField(field);
break;
}
return d;
}
void RichTextCastMember::setField(int field, const Datum &d) {
switch (field) {
case kTheText:
_plainText = Common::U32String(d.asString());
warning("STUB: RichTextCastMember::setField: text set to \"%s\", but won't rerender!", d.asString().c_str());
return;
case kThePageHeight:
case kTheScrollTop:
default:
break;
}
CastMember::setField(field, d);
}
Common::String RichTextCastMember::formatInfo() {
// need to pull the data from the STXT resource before the
// debug output will be visible
load();
Common::String format = formatStringForDump(_plainText.encode());
return Common::String::format(
"initialRect: %dx%d@%d,%d, boundingRect: %dx%d@%d,%d, text: \"%s\"",
_initialRect.width(), _initialRect.height(),
_initialRect.left, _initialRect.top,
_boundingRect.width(), _boundingRect.height(),
_boundingRect.left, _boundingRect.top,
format.c_str()
);
}
uint32 RichTextCastMember::getCastDataSize() {
if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
// 8 bytes (_initialRect)
// 8 bytes (_boundingRect)
// Ignored 9 bytes
// 3 bytes r, g, b (foreground, each a byte)
// 6 bytes r, g, b (background, each 2 bytes)
return 26;
} else {
warning("RichTextCastMember()::getCastDataSize():>D5 isn't handled");
return 0;
}
}
void RichTextCastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
Movie::writeRect(writeStream, _initialRect);
Movie::writeRect(writeStream, _boundingRect);
writeStream->write(0, 8);
writeStream->writeByte(0);
uint8 r, g, b;
_pf32.colorToRGB(_foreColor, r, g, b);
writeStream->writeByte(r);
writeStream->writeByte(g);
writeStream->writeByte(b);
_pf32.colorToRGB(_bgColor, r, g, b);
writeStream->writeUint16BE(r << 8);
writeStream->writeUint16BE(g << 8);
writeStream->writeUint16BE(b << 8);
} else {
warning("RichTextCastMember()::writeCastData(): >D5 isn't handled");
}
}
} // End of namespace Director

View File

@@ -0,0 +1,73 @@
/* 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/>.
*
*/
#ifndef DIRECTOR_CASTMEMBER_RICHTEXT_H
#define DIRECTOR_CASTMEMBER_RICHTEXT_H
#include "graphics/pixelformat.h"
#include "director/types.h"
#include "director/castmember/castmember.h"
namespace Director {
class RichTextCastMember : public CastMember {
public:
RichTextCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version);
RichTextCastMember(Cast *cast, uint16 castId, RichTextCastMember &source);
~RichTextCastMember();
CastMember *duplicate(Cast *cast, uint16 castId) override { return (CastMember *)(new RichTextCastMember(cast, castId, *this)); }
void load() override;
Graphics::MacWidget *createWidget(Common::Rect &bbox, Channel *channel, SpriteType spriteType) override;
bool hasField(int field) override;
Datum getField(int field) override;
void setField(int field, const Datum &value) override;
Common::String getText() { return Common::String(_plainText); }
uint32 getCastDataSize() override;
void writeCastData(Common::SeekableWriteStream *writeStream) override;
Common::String formatInfo() override;
private:
Common::U32String _plainText;
Graphics::PixelFormat _pf32;
Picture *_picture;
Picture *_pictureWithBg;
// _initialRect
// _boundingRect
byte _antialiasFlag;
byte _cropFlags;
uint16 _scrollPos;
uint16 _antialiasFontSize; // Seems to be always 12
uint16 _displayHeight;
uint32 _foreColor;
uint32 _bgColor;
};
} // End of namespace Director
#endif

View File

@@ -0,0 +1,180 @@
/* 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/stream.h"
#include "director/director.h"
#include "director/cast.h"
#include "director/castmember/script.h"
#include "director/lingo/lingo-the.h"
namespace Director {
ScriptCastMember::ScriptCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version)
: CastMember(cast, castId, stream) {
_type = kCastLingoScript;
_scriptType = kNoneScript;
if (debugChannelSet(5, kDebugLoading)) {
debugC(5, kDebugLoading, "ScriptCastMember::ScriptCastMember(): Contents");
stream.hexdump(stream.size());
}
if (version < kFileVer400) {
error("Unhandled Script cast");
} else if (version >= kFileVer400 && version < kFileVer1100) {
uint16 type = stream.readUint16BE();
switch (type) {
case 0:
_scriptType = kNoneScript;
break;
case 1:
_scriptType = kScoreScript;
break;
case 3:
_scriptType = kMovieScript;
break;
case 7:
_scriptType = kParentScript;
warning("Unhandled kParentScript %d", castId);
break;
default:
error("ScriptCastMember: Unprocessed script type: %d", type);
}
debugC(3, kDebugLoading, " CASt: Script type: %s (%d)", scriptType2str(_scriptType), type);
assert(stream.pos() == stream.size()); // There should be no more data
} else {
warning("STUB: ScriptCastMember::ScriptCastMember(): Scripts not yet supported for version v%d (%d)", humanVersion(version), version);
}
}
ScriptCastMember::ScriptCastMember(Cast *cast, uint16 castId, ScriptCastMember &source)
: CastMember(cast, castId) {
_type = kCastLingoScript;
_scriptType = source._scriptType;
warning("ScriptCastMember(): Duplicating source %d to target %d! This is unlikely to work properly, as the actual scripts aren't yet copied", source._castId, castId);
}
bool ScriptCastMember::hasField(int field) {
switch (field) {
case kTheScriptType:
return true;
default:
break;
}
return CastMember::hasField(field);
}
Datum ScriptCastMember::getField(int field) {
Datum d;
switch (field) {
case kTheScriptType:
switch (_scriptType) {
case kMovieScript:
d = Common::String("movie");
d.type = SYMBOL;
break;
case kScoreScript:
d = Common::String("score");
d.type = SYMBOL;
break;
case kParentScript:
d = Common::String("parent");
d.type = SYMBOL;
break;
default:
break;
}
break;
default:
d = CastMember::getField(field);
break;
}
return d;
}
void ScriptCastMember::setField(int field, const Datum &d) {
switch (field) {
case kTheScriptType:
warning("ScriptCastMember::setField(): setting scriptType! This probably isn't going to work as it doesn't recategorize the script.");
if (d.type == SYMBOL) {
if (d.u.s->equalsIgnoreCase("movie")) {
_scriptType = kMovieScript;
} else if (d.u.s->equalsIgnoreCase("score")) {
_scriptType = kScoreScript;
} else if (d.u.s->equalsIgnoreCase("parent")) {
_scriptType = kParentScript;
}
}
return;
default:
break;
}
CastMember::setField(field, d);
}
Common::String ScriptCastMember::formatInfo() {
return Common::String::format(
"scriptType: %s", scriptType2str(_scriptType)
);
}
uint32 ScriptCastMember::getCastDataSize() {
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
// 2 bytes for type and unk1 + 2 byte for castType and flags ma(see Cast::loadCastData() for Director 4 only
return 2 + 2;
} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
// type and unk1: 2 bytes
return 2;
} else {
warning("ScriptCastMember::writeCastData(): invalid or unhandled Script version: %d", _cast->_version);
return 0;
}
}
void ScriptCastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer600) {
writeStream->writeByte(0); // unknown
switch (_scriptType) {
case kScoreScript:
writeStream->writeByte(1);
break;
case kMovieScript:
writeStream->writeByte(3);
break;
case kParentScript:
writeStream->writeByte(7);
break;
default:
break;
}
} else {
warning("ScriptCastMember::writeCastData(): invalid or unhandled Script version: %d", _cast->_version);
}
}
}

View File

@@ -0,0 +1,50 @@
/* 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/>.
*
*/
#ifndef DIRECTOR_CASTMEMBER_SCRIPT_H
#define DIRECTOR_CASTMEMBER_SCRIPT_H
#include "director/castmember/castmember.h"
namespace Director {
class ScriptCastMember : public CastMember {
public:
ScriptCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version);
ScriptCastMember(Cast *cast, uint16 castId, ScriptCastMember &source);
CastMember *duplicate(Cast *cast, uint16 castId) override { return (CastMember *)(new ScriptCastMember(cast, castId, *this)); }
ScriptType _scriptType;
bool hasField(int field) override;
Datum getField(int field) override;
void setField(int field, const Datum &value) override;
uint32 getCastDataSize() override;
void writeCastData(Common::SeekableWriteStream *writeStream) override;
Common::String formatInfo() override;
};
} // End of namespace Director
#endif

View File

@@ -0,0 +1,250 @@
/* 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/stream.h"
#include "director/director.h"
#include "director/cast.h"
#include "director/movie.h"
#include "director/castmember/shape.h"
#include "director/lingo/lingo-the.h"
namespace Director {
ShapeCastMember::ShapeCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version)
: CastMember(cast, castId, stream) {
_type = kCastShape;
_ink = kInkTypeCopy;
if (debugChannelSet(5, kDebugLoading)) {
debugC(5, kDebugLoading, "ShapeCastMember::ShapeCastMember(): Shape data");
stream.hexdump(stream.size());
}
if (version < kFileVer400) {
_shapeType = static_cast<ShapeType>(stream.readUint16BE());
_initialRect = Movie::readRect(stream);
_pattern = stream.readUint16BE();
// Normalize D2 and D3 colors from -128 ... 127 to 0 ... 255.
_fgCol = g_director->transformColor((128 + stream.readByte()) & 0xff);
_bgCol = g_director->transformColor((128 + stream.readByte()) & 0xff);
_fillType = stream.readByte();
_ink = static_cast<InkType>(_fillType & 0x3f);
_lineThickness = stream.readByte();
_lineDirection = stream.readByte();
} else if (version >= kFileVer400 && version < kFileVer1100) {
_shapeType = static_cast<ShapeType>(stream.readUint16BE());
_initialRect = Movie::readRect(stream);
_pattern = stream.readUint16BE();
_fgCol = g_director->transformColor((uint8)stream.readByte());
_bgCol = g_director->transformColor((uint8)stream.readByte());
_fillType = stream.readByte();
_ink = static_cast<InkType>(_fillType & 0x3f);
_lineThickness = stream.readByte();
_lineDirection = stream.readByte();
} else {
warning("STUB: ShapeCastMember::ShapeCastMember(): Shapes not yet supported for version v%d (%d)", humanVersion(_cast->_version), _cast->_version);
_shapeType = kShapeRectangle;
_pattern = 0;
_fgCol = _bgCol = 0;
_fillType = 0;
_lineThickness = 1;
_lineDirection = 0;
}
_modified = false;
debugC(3, kDebugLoading, "ShapeCastMember: type: %d pat: %d fg: %d bg: %d fill: %d thick: %d dir: %d",
_shapeType, _pattern, _fgCol, _bgCol, _fillType, _lineThickness, _lineDirection);
if (debugChannelSet(3, kDebugLoading))
_initialRect.debugPrint(0, "ShapeCastMember: rect:");
}
ShapeCastMember::ShapeCastMember(Cast *cast, uint16 castId, ShapeCastMember &source)
: CastMember(cast, castId) {
_type = kCastShape;
_loaded = source._loaded;
_initialRect = source._initialRect;
_boundingRect = source._boundingRect;
if (cast == source._cast)
_children = source._children;
_shapeType = source._shapeType;
_pattern = source._pattern;
_fillType = source._fillType;
_lineThickness = source._lineThickness;
_lineDirection = source._lineDirection;
_ink = source._ink;
}
void ShapeCastMember::setBackColor(uint32 bgCol) {
_bgCol = bgCol;
_modified = true;
}
void ShapeCastMember::setForeColor(uint32 fgCol) {
_fgCol = fgCol;
_modified = true;
}
bool ShapeCastMember::hasField(int field) {
switch (field) {
case kTheFilled:
case kTheLineSize:
case kThePattern:
case kTheShapeType:
return true;
default:
break;
}
return CastMember::hasField(field);
}
Datum ShapeCastMember::getField(int field) {
Datum d;
switch (field) {
case kTheFilled:
d = Datum((bool)_fillType);
break;
case kTheLineSize:
d = Datum(_lineThickness);
break;
case kThePattern:
d = Datum(_pattern);
break;
case kTheShapeType:
switch (_shapeType) {
case kShapeRectangle:
d = Datum("rect");
d.type = SYMBOL;
break;
case kShapeRoundRect:
d = Datum("roundRect");
d.type = SYMBOL;
break;
case kShapeOval:
d = Datum("oval");
d.type = SYMBOL;
break;
case kShapeLine:
d = Datum("line");
d.type = SYMBOL;
break;
default:
break;
}
break;
default:
d = CastMember::getField(field);
break;
}
return d;
}
void ShapeCastMember::setField(int field, const Datum &d) {
switch (field) {
case kTheFilled:
_fillType = d.asInt() ? 1 : 0;
return;
case kTheLineSize:
_lineThickness = d.asInt();
return;
case kThePattern:
_pattern = d.asInt();
return;
case kTheShapeType:
if (d.type == SYMBOL) {
Common::String name = *d.u.s;
if (name.equalsIgnoreCase("rect")) {
_shapeType = kShapeRectangle;
} else if (name.equalsIgnoreCase("roundRect")) {
_shapeType = kShapeRoundRect;
} else if (name.equalsIgnoreCase("oval")) {
_shapeType = kShapeOval;
} else if (name.equalsIgnoreCase("line")) {
_shapeType = kShapeLine;
}
}
break;
default:
break;
}
CastMember::setField(field, d);
}
Common::String ShapeCastMember::formatInfo() {
return Common::String::format(
"initialRect: %dx%d@%d,%d, boundingRect: %dx%d@%d,%d, foreColor: %d, backColor: %d, shapeType: %d, pattern: %d, fillType: %d, lineThickness: %d, lineDirection: %d, ink: %d",
_initialRect.width(), _initialRect.height(),
_initialRect.left, _initialRect.top,
_boundingRect.width(), _boundingRect.height(),
_boundingRect.left, _boundingRect.top,
getForeColor(), getBackColor(),
_shapeType, _pattern, _fillType,
_lineThickness, _lineDirection, _ink
);
}
uint32 ShapeCastMember::getCastDataSize() {
// unk1 : 1 byte
// _shapeType : 1 byte
// _initalRect : 8 bytes
// _pattern : 2 bytes
// _fgCol : 1 byte
// _bgCol : 1 byte
// _fillType : 1 byte
// _lineThickness : 1 byte
// _lineDirection : 1 byte
// Total : 17 bytes
// For Director 4 : 1 byte extra for casttype (See Cast::loadCastData())
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
return 17 + 1;
} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
return 17;
} else {
warning("ScriptCastMember::writeCastData(): invalid or unhandled Script version: %d", _cast->_version);
return 0;
}
}
void ShapeCastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
writeStream->writeByte(0);
writeStream->writeByte(1);
Movie::writeRect(writeStream, _initialRect);
writeStream->writeUint16LE(_pattern);
// The foreground and background colors are transformed
// Need to retrieve the original colors for saving
writeStream->writeByte(_fgCol);
writeStream->writeByte(_bgCol);
writeStream->writeByte(_fillType);
writeStream->writeByte(_lineThickness);
writeStream->writeByte(_lineDirection);
}
} // End of namespace Director

View File

@@ -0,0 +1,64 @@
/* 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/>.
*
*/
#ifndef DIRECTOR_CASTMEMBER_SHAPE_H
#define DIRECTOR_CASTMEMBER_SHAPE_H
#include "director/castmember/castmember.h"
namespace Director {
class ShapeCastMember : public CastMember {
public:
ShapeCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version);
ShapeCastMember(Cast *cast, uint16 castId, ShapeCastMember &source);
CastMember *duplicate(Cast *cast, uint16 castId) override { return (CastMember *)(new ShapeCastMember(cast, castId, *this)); }
uint32 getForeColor() override { return _fgCol; }
uint32 getBackColor() override { return _bgCol; }
void setBackColor(uint32 bgCol) override;
void setForeColor(uint32 fgCol) override;
bool hasField(int field) override;
Datum getField(int field) override;
void setField(int field, const Datum &value) override;
Common::String formatInfo() override;
uint32 getCastDataSize() override;
void writeCastData(Common::SeekableWriteStream *writeStream) override;
ShapeType _shapeType;
uint16 _pattern;
byte _fillType;
byte _lineThickness;
byte _lineDirection;
InkType _ink;
private:
uint32 _fgCol;
uint32 _bgCol;
};
} // End of namespace Director
#endif

View File

@@ -0,0 +1,320 @@
/* 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 "director/director.h"
#include "director/cast.h"
#include "director/sound.h"
#include "director/castmember/sound.h"
#include "director/lingo/lingo-the.h"
namespace Director {
SoundCastMember::SoundCastMember(Cast *cast, uint16 castId)
: CastMember(cast, castId) {
_type = kCastSound;
_audio = nullptr;
_looping = 0;
}
SoundCastMember::SoundCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version)
: CastMember(cast, castId, stream) {
_type = kCastSound;
_audio = nullptr;
_looping = 0;
}
SoundCastMember::SoundCastMember(Cast *cast, uint16 castId, SoundCastMember &source)
: CastMember(cast, castId) {
_type = kCastSound;
_loaded = false;
_audio = nullptr;
_looping = source._looping;
if (cast == source._cast)
_children = source._children;
warning("SoundCastMember(): Duplicating source %d to target %d! This is unlikely to work properly, as the resource loader is based on the cast ID", source._castId, castId);
}
SoundCastMember::~SoundCastMember() {
if (_audio)
delete _audio;
}
Common::String SoundCastMember::formatInfo() {
return Common::String::format(
"looping: %d", _looping
);
}
void SoundCastMember::load() {
if (_loaded)
return;
uint32 tag = 0;
uint16 sndId = 0;
MoaSoundFormatDecoder *sndFormat = nullptr;
if (_cast->_version < kFileVer400) {
tag = MKTAG('S', 'N', 'D', ' ');
sndId = (uint16)(_castId + _cast->_castIDoffset);
} else if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer600) {
for (auto &it : _children) {
if (it.tag == MKTAG('s', 'n', 'd', ' ') || it.tag == MKTAG('S', 'N', 'D', ' ')) {
sndId = it.index;
tag = it.tag;
break;
}
}
if (!sndId) {
warning("SoundCastMember::load(): No snd resource found in %d children, falling back to D3", _children.size());
tag = MKTAG('S', 'N', 'D', ' ');
sndId = (uint16)(_castId + _cast->_castIDoffset);
}
} else if (_cast->_version >= kFileVer600 && _cast->_version < kFileVer700) {
for (auto &it : _children) {
if (it.tag == MKTAG('s', 'n', 'd', ' ')) {
sndId = it.index;
tag = it.tag;
} else if (it.tag == MKTAG('s', 'n', 'd', 'H')) {
Common::SeekableReadStreamEndian *sndData = _cast->getResource(it.tag, it.index);
if (!sndFormat)
sndFormat = new MoaSoundFormatDecoder();
sndFormat->loadHeaderStream(*sndData);
delete sndData;
} else if (it.tag == MKTAG('s', 'n', 'd', 'S')) {
Common::SeekableReadStreamEndian *sndData = _cast->getResource(it.tag, it.index);
if (!sndFormat)
sndFormat = new MoaSoundFormatDecoder();
sndFormat->loadSampleStream(*sndData);
delete sndData;
} else if (it.tag == MKTAG('e', 'd', 'i', 'M')) {
Common::SeekableReadStreamEndian *sndData = _cast->getResource(it.tag, it.index);
Common::String format = _cast->getCastMemberInfo(_castId)->mediaFormatName.c_str();
if (!_audio) {
if (format.equalsIgnoreCase("kMoaCfFormat_AIFF")) {
_audio = new MoaStreamDecoder(format, sndData);
_loaded = true;
return;
} else {
warning("SoundCastMember::load(): Unsupported ediM format '%s' in sound cast member %d", format.c_str(), _castId);
}
} else {
warning("SoundCastMember::load(): Multiple ediM resources in sound cast member %d", _castId);
}
delete sndData;
} else if (it.tag == MKTAG('c', 'u', 'p', 't')) {
Common::SeekableReadStreamEndian *sndData = _cast->getResource(it.tag, it.index);
int32 numCuePoints = sndData->readSint32BE();
char cuePointName[32];
for (int i = 0; i < numCuePoints; i++) {
int32 cuePoint = sndData->readSint32BE();
_cuePoints.push_back(cuePoint);
sndData->read(cuePointName, 32);
cuePointName[31] = '\0';
_cuePointNames.push_back(cuePointName);
debugC(2, kDebugLoading, " Cue point %d: %d (%s) in sound cast member %d", i, cuePoint, cuePointName, _castId);
}
} else {
debugC(2, kDebugLoading, "SoundCastMember::load(): Ignoring unknown tag '%s' in sound cast member %d", tag2str(it.tag), _castId);
}
}
} else {
warning("STUB: SoundCastMember::SoundCastMember(): Sounds not yet supported for version v%d (%d)", humanVersion(_cast->_version), _cast->_version);
}
if (sndFormat) {
_audio = sndFormat;
_loaded = true;
return;
}
Common::SeekableReadStreamEndian *sndData = _cast->getResource(tag, sndId);
if (!sndData) {
tag = MKTAG('s', 'n', 'd', ' ');
sndData = _cast->getResource(tag, sndId);
}
if (sndData == nullptr || sndData->size() == 0) {
// audio file is linked, load from the filesystem
Common::String res = _cast->getLinkedPath(_castId);
if (!res.empty()) {
debugC(2, kDebugLoading, "****** Loading file '%s', cast id: %d", res.c_str(), sndId);
AudioFileDecoder *audio = new AudioFileDecoder(res);
_audio = audio;
// Linked sound files always have the loop flag disabled
_looping = 0;
} else {
warning("Sound::load(): no resource or info found for cast member %d, skipping", _castId);
}
} else {
debugC(2, kDebugLoading, "****** Loading '%s' id: %d, %d bytes", tag2str(tag), sndId, (int)sndData->size());
SNDDecoder *audio = new SNDDecoder();
audio->loadStream(*sndData);
_audio = audio;
_size = sndData->size();
if (_cast->_version < kFileVer400) {
// The looping flag wasn't added to sound cast members until D4.
// In older versions, always loop sounds that contain a loop start and end.
_looping = audio->hasLoopBounds();
} else {
// Some sound cast members at version kFileVer400 have looping=true with
// invalid loop bounds (bigger than sample size or non-consecutive).
// Resetting loop bounds to sample bounds and disabling looping similar
// to how D4 playback seems to work.
if (!audio->hasValidLoopBounds()) {
// only emit a warning for files > kFileVer400 as it's only kFileVer400 files that should be affected
if (_cast->_version > kFileVer400) {
warning("Sound::load(): Invalid loop bounds detected. Disabling looping for cast member id %d, sndId %d", _castId, sndId);
} else {
debugC(2, "Sound::load(): Invalid loop bounds detected. Disabling looping for cast member id %d, sndId %d", _castId, sndId);
}
_looping = false;
audio->resetLoopBounds();
}
}
}
if (sndData)
delete sndData;
_loaded = true;
}
void SoundCastMember::unload() {
if (!_loaded)
return;
delete _audio;
_audio = nullptr;
_size = 0;
_looping = false;
_loaded = false;
}
bool SoundCastMember::hasField(int field) {
switch (field) {
case kTheChannelCount:
case kTheCuePointNames: // D6
case kTheCuePointTimes: // D6
case kTheCurrentTime: // D6
case kTheLoop:
case kTheSampleRate:
case kTheSampleSize:
return true;
default:
break;
}
return CastMember::hasField(field);
}
Datum SoundCastMember::getField(int field) {
Datum d;
load();
if (!_audio) {
warning("SoundCastMember::getField(): Audio not found");
return d;
}
switch (field) {
case kTheChannelCount:
d = _audio->getChannelCount();
break;
case kTheCuePointNames:
{
FArray *arr = new FArray();
for (size_t i = 0; i < _cuePointNames.size(); i++) {
arr->arr.push_back(Datum(_cuePointNames[i]));
}
d.type = ARRAY;
d.u.farr = arr;
}
break;
case kTheCuePointTimes:
{
FArray *arr = new FArray();
for (size_t i = 0; i < _cuePoints.size(); i++) {
arr->arr.push_back(Datum((int)_cuePoints[i]));
}
d.type = ARRAY;
d.u.farr = arr;
}
break;
case kTheLoop:
d = _looping ? 1 : 0;
break;
case kTheSampleRate:
d = _audio->getSampleRate();
break;
case kTheSampleSize:
d = _audio->getSampleSize();
break;
default:
d = CastMember::getField(field);
}
return d;
}
void SoundCastMember::setField(int field, const Datum &d) {
switch (field) {
case kTheChannelCount:
case kTheSampleRate:
case kTheSampleSize:
warning("SoundCastMember::setField(): Attempt to set read-only field %s of cast %d", g_lingo->field2str(field), _castId);
return;
case kTheLoop:
_looping = bool(d.asInt());
warning("STUB: SoundCastMember::setField(): Set looping to %d for cast %d", _looping, _castId);
break;
default:
break;
}
CastMember::setField(field, d);
}
// Similar to PaletteCastMember, SoundCastMember has no data in the 'CASt' resource or is ignored
// This is the data in 'CASt' resource
uint32 SoundCastMember::getCastDataSize() {
if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
return 0;
} else if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
// (castType (see Cast::loadCastData() for Director 4 only) 1 byte
return 1; // Since SoundCastMember doesn't have any flags
}
return 0;
}
void SoundCastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
// This should never get triggered
// since there is no data to write
}
} // End of namespace Director

View File

@@ -0,0 +1,60 @@
/* 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/>.
*
*/
#ifndef DIRECTOR_CASTMEMBER_SOUND_H
#define DIRECTOR_CASTMEMBER_SOUND_H
#include "director/castmember/castmember.h"
namespace Director {
class AudioDecoder;
class SoundCastMember : public CastMember {
public:
SoundCastMember(Cast *cast, uint16 castId);
SoundCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version);
SoundCastMember(Cast *cast, uint16 castId, SoundCastMember &source);
~SoundCastMember();
CastMember *duplicate(Cast *cast, uint16 castId) override { return (CastMember *)(new SoundCastMember(cast, castId, *this)); }
void load() override;
void unload() override;
Common::String formatInfo() override;
bool hasField(int field) override;
Datum getField(int field) override;
void setField(int field, const Datum &value) override;
uint32 getCastDataSize() override;
void writeCastData(Common::SeekableWriteStream *writeStream) override;
bool _looping;
AudioDecoder *_audio;
Common::Array<int32> _cuePoints;
Common::StringArray _cuePointNames;
};
} // End of namespace Director
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,143 @@
/* 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/>.
*
*/
#ifndef DIRECTOR_CASTMEMBER_TEXT_H
#define DIRECTOR_CASTMEMBER_TEXT_H
#include "director/castmember/castmember.h"
namespace Graphics {
class MacText;
}
namespace Director {
class TextCastMember : public CastMember {
public:
TextCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version, uint8 flags1 = 0, bool asButton = false);
TextCastMember(Cast *cast, uint16 castId, TextCastMember &source);
CastMember *duplicate(Cast *cast, uint16 castId) override { return (CastMember *)(new TextCastMember(cast, castId, *this)); }
void setColors(uint32 *fgcolor, uint32 *bgcolor) override;
Graphics::MacWidget *createWidget(Common::Rect &bbox, Channel *channel, SpriteType spriteType) override;
Graphics::MacText *getWidget();
CollisionTest isWithin(const Common::Rect &bbox, const Common::Point &pos, InkType ink) override;
bool isEditable() override { return _editable; }
void setEditable(bool editable) override { _editable = editable; }
void updateFromWidget(Graphics::MacWidget *widget, bool spriteEditable) override;
Graphics::TextAlign getAlignment();
uint32 getBackColor() override { return _bgcolor; }
void setBackColor(uint32 bgCol) override;
uint32 getForeColor() override { return _fgcolor; }
uint32 getForeColor(int start, int end);
void setForeColor(uint32 fgCol) override;
void setForeColor(uint32 fgCol, int start, int end);
bool hasField(int field) override;
Datum getField(int field) override;
void setField(int field, const Datum &value) override;
bool hasChunkField(int field);
Datum getChunkField(int field, int start, int end);
bool setChunkField(int field, int start, int end, const Datum &value);
int getLineCount();
int getLineHeight(int line);
int getTextHeight();
Common::String getTextFont();
Common::String getTextFont(int start, int end);
void setTextFont(const Common::String &fontName);
void setTextFont(const Common::String &fontName, int start, int end);
int getTextSize();
int getTextSize(int start, int end);
void setTextSize(int textSize);
void setTextSize(int textSize, int start, int end);
Common::String getTextStyle();
Common::String getTextStyle(int start, int end);
void setTextStyle(const Common::String &textStyle);
void setTextStyle(const Common::String &textStyle, int start, int end);
void scrollByLine(int count);
Common::String formatInfo() override;
void load() override;
void unload() override;
void writeCastData(Common::SeekableWriteStream *writeStream) override;
uint32 getCastDataSize() override; // This is the size of the data in the 'CASt' resource
uint32 getSTXTResourceSize();
uint32 writeSTXTResource(Common::SeekableWriteStream *writeStream, uint32 offset);
uint8 getFormattingCount();
uint8 _borderSize;
uint8 _gutterSize;
uint8 _boxShadow;
uint16 _maxHeight;
uint16 _textHeight;
uint32 _fontId;
uint16 _height;
uint16 _ascent;
uint16 _fontSize;
TextType _textType;
TextAlignType _textAlign;
uint8 _textShadow;
uint16 _scroll;
byte _textSlant;
byte _textFlags;
uint16 _bgpalinfo1, _bgpalinfo2, _bgpalinfo3;
uint16 _fgpalinfo1, _fgpalinfo2, _fgpalinfo3;
ButtonType _buttonType;
bool _editable;
int _lineSpacing;
Common::U32String _ftext;
Common::U32String _ptext;
Common::String _rtext;
void importStxt(const Stxt *stxt);
void importRTE(byte *text);
Common::U32String getText();
Common::String getRawText();
void setRawText(const Common::String &text);
private:
Graphics::MacWidget *createWindowOrWidget(Common::Rect &bbox, Common::Rect dims, Graphics::MacFont *macFont);
uint32 _bgcolor;
uint32 _fgcolor;
};
} // End of namespace Director
#endif

View File

@@ -0,0 +1,154 @@
/* 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 "director/director.h"
#include "director/cast.h"
#include "director/movie.h"
#include "director/castmember/transition.h"
#include "director/lingo/lingo-the.h"
namespace Director {
TransitionCastMember::TransitionCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version)
: CastMember(cast, castId, stream) {
_type = kCastTransition;
_transType = kTransNone;
_durationMillis = 0;
_chunkSize = 0;
_area = false;
if (debugChannelSet(5, kDebugLoading)) {
stream.hexdump(stream.size());
}
if (_cast->_version < kFileVer1100) {
stream.readByte();
_chunkSize = stream.readByte();
_transType = static_cast<TransitionType>(stream.readByte());
_flags = stream.readByte();
_area = !(_flags & 1);
_durationMillis = stream.readUint16BE();
debugC(3, kDebugLoading, " TransitionCastMember: transType: %d, durationMillis: %d, flags: %d, chunkSize: %d, area: %d", _transType, _durationMillis, _flags, _chunkSize, _area);
} else {
warning("STUB: TransitionCastMember::TransitionCastMember(): Transitions not yet supported for version v%d (%d)", humanVersion(_cast->_version), _cast->_version);
}
}
TransitionCastMember::TransitionCastMember(Cast *cast, uint16 castId, TransitionCastMember &source)
: CastMember(cast, castId) {
_transType = source._transType;
_loaded = source._loaded;
_durationMillis = source._durationMillis;
_flags = source._flags;
_chunkSize = source._chunkSize;
_area = source._area;
}
bool TransitionCastMember::hasField(int field) {
switch (field) {
case kTheChangeArea:
case kTheChunkSize:
case kTheDuration:
case kTheTransitionType:
return true;
default:
break;
}
return CastMember::hasField(field);
}
Datum TransitionCastMember::getField(int field) {
Datum d;
switch (field) {
case kTheChangeArea:
d = Datum((int)_area);
break;
case kTheChunkSize:
d = Datum(_chunkSize);
break;
case kTheDuration:
d = Datum(_durationMillis);
break;
case kTheTransitionType:
d = Datum((int)_transType);
break;
default:
d = CastMember::getField(field);
break;
}
return d;
}
void TransitionCastMember::setField(int field, const Datum &d) {
switch (field) {
case kTheChangeArea:
_area = (bool)d.asInt();
return;
case kTheChunkSize:
_chunkSize = d.asInt();
return;
case kTheDuration:
_durationMillis = d.asInt();
return;
case kTheTransitionType:
_transType = (TransitionType)d.asInt();
return;
default:
break;
}
CastMember::setField(field, d);
}
Common::String TransitionCastMember::formatInfo() {
return Common::String::format("transType: %d, durationMillis: %d, flags: %d, chunkSize: %d, area: %d", _transType, _durationMillis, _flags, _chunkSize, _area);
}
uint32 TransitionCastMember::getCastDataSize() {
if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
// Ignored 1 byte
// _chunkSize 1 byte
// _transType 1 byte
// _flags 1 byte
// _durationMiilis 2 bytes
return 6;
} else {
warning("TransitionCastMember()::getCastDataSize(): CastMember version invalid or not handled");
return 0;
}
}
void TransitionCastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer600) {
writeStream->writeByte(0);
writeStream->writeByte(_chunkSize);
writeStream->writeByte((uint8)_transType);
writeStream->writeByte(_flags);
writeStream->writeUint16LE(_durationMillis);
} else {
warning("TransitionCastMember()::writeCastData(): CastMember version invalid or not handled");
}
}
} // End of namespace Director

View File

@@ -0,0 +1,54 @@
/* 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/>.
*
*/
#ifndef DIRECTOR_CASTMEMBER_TRANSITION_H
#define DIRECTOR_CASTMEMBER_TRANSITION_H
#include "director/castmember/castmember.h"
namespace Director {
class TransitionCastMember : public CastMember {
public:
TransitionCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version);
TransitionCastMember(Cast *cast, uint16 castId, TransitionCastMember &source);
CastMember *duplicate(Cast *cast, uint16 castId) override { return (CastMember *)(new TransitionCastMember(cast, castId, *this)); }
bool hasField(int field) override;
Datum getField(int field) override;
void setField(int field, const Datum &value) override;
Common::String formatInfo() override;
uint32 getCastDataSize() override;
void writeCastData(Common::SeekableWriteStream *writeStream) override;
TransitionType _transType;
uint16 _durationMillis;
uint8 _flags;
uint8 _chunkSize;
bool _area;
};
} // End of namespace Director
#endif

View File

@@ -0,0 +1,92 @@
/* 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 "director/director.h"
#include "director/cast.h"
#include "director/movie.h"
#include "director/castmember/xtra.h"
#include "director/lingo/lingo-the.h"
namespace Director {
XtraCastMember::XtraCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version)
: CastMember(cast, castId, stream) {
_type = kCastXtra;
if (debugChannelSet(5, kDebugLoading)) {
stream.hexdump(stream.size());
}
warning("STUB: XtraCastMember::XtraCastMember(): Xtra cast members not yet supported for version v%d (%d)", humanVersion(_cast->_version), _cast->_version);
}
XtraCastMember::XtraCastMember(Cast *cast, uint16 castId, XtraCastMember &source)
: CastMember(cast, castId) {
}
bool XtraCastMember::hasField(int field) {
switch (field) {
case kTheCuePointNames: // D6
case kTheCuePointTimes: // D6
case kTheCurrentTime: // D6
case kTheMediaBusy: // D6, undocumented
return true;
default:
break;
}
return CastMember::hasField(field);
}
Datum XtraCastMember::getField(int field) {
Datum d;
switch (field) {
default:
d = CastMember::getField(field);
break;
}
return d;
}
void XtraCastMember::setField(int field, const Datum &d) {
switch (field) {
default:
break;
}
CastMember::setField(field, d);
}
Common::String XtraCastMember::formatInfo() {
return Common::String::format("Xtra");
}
uint32 XtraCastMember::getCastDataSize() {
warning("XtraCastMember()::getCastDataSize(): CastMember version invalid or not handled");
return 0;
}
void XtraCastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
warning("XtraCastMember()::writeCastData(): CastMember version invalid or not handled");
}
} // End of namespace Director

View File

@@ -0,0 +1,48 @@
/* 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/>.
*
*/
#ifndef DIRECTOR_CASTMEMBER_XTRA_H
#define DIRECTOR_CASTMEMBER_XTRA_H
#include "director/castmember/castmember.h"
namespace Director {
class XtraCastMember : public CastMember {
public:
XtraCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version);
XtraCastMember(Cast *cast, uint16 castId, XtraCastMember &source);
CastMember *duplicate(Cast *cast, uint16 castId) override { return (CastMember *)(new XtraCastMember(cast, castId, *this)); }
bool hasField(int field) override;
Datum getField(int field) override;
void setField(int field, const Datum &value) override;
Common::String formatInfo() override;
uint32 getCastDataSize() override;
void writeCastData(Common::SeekableWriteStream *writeStream) override;
};
} // End of namespace Director
#endif