Initial commit
This commit is contained in:
1280
engines/director/castmember/bitmap.cpp
Normal file
1280
engines/director/castmember/bitmap.cpp
Normal file
File diff suppressed because it is too large
Load Diff
134
engines/director/castmember/bitmap.h
Normal file
134
engines/director/castmember/bitmap.h
Normal 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
|
||||
450
engines/director/castmember/castmember.cpp
Normal file
450
engines/director/castmember/castmember.cpp
Normal 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
|
||||
208
engines/director/castmember/castmember.h
Normal file
208
engines/director/castmember/castmember.h
Normal 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
|
||||
733
engines/director/castmember/digitalvideo.cpp
Normal file
733
engines/director/castmember/digitalvideo.cpp
Normal 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
|
||||
110
engines/director/castmember/digitalvideo.h
Normal file
110
engines/director/castmember/digitalvideo.h
Normal 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
|
||||
895
engines/director/castmember/filmloop.cpp
Normal file
895
engines/director/castmember/filmloop.cpp
Normal 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
|
||||
81
engines/director/castmember/filmloop.h
Normal file
81
engines/director/castmember/filmloop.h
Normal 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
|
||||
163
engines/director/castmember/movie.cpp
Normal file
163
engines/director/castmember/movie.cpp
Normal 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
|
||||
51
engines/director/castmember/movie.h
Normal file
51
engines/director/castmember/movie.h
Normal 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
|
||||
202
engines/director/castmember/palette.cpp
Normal file
202
engines/director/castmember/palette.cpp
Normal 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
|
||||
57
engines/director/castmember/palette.h
Normal file
57
engines/director/castmember/palette.h
Normal 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
|
||||
312
engines/director/castmember/richtext.cpp
Normal file
312
engines/director/castmember/richtext.cpp
Normal 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
|
||||
73
engines/director/castmember/richtext.h
Normal file
73
engines/director/castmember/richtext.h
Normal 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
|
||||
180
engines/director/castmember/script.cpp
Normal file
180
engines/director/castmember/script.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
50
engines/director/castmember/script.h
Normal file
50
engines/director/castmember/script.h
Normal 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
|
||||
250
engines/director/castmember/shape.cpp
Normal file
250
engines/director/castmember/shape.cpp
Normal 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
|
||||
64
engines/director/castmember/shape.h
Normal file
64
engines/director/castmember/shape.h
Normal 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
|
||||
320
engines/director/castmember/sound.cpp
Normal file
320
engines/director/castmember/sound.cpp
Normal 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
|
||||
60
engines/director/castmember/sound.h
Normal file
60
engines/director/castmember/sound.h
Normal 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
|
||||
1123
engines/director/castmember/text.cpp
Normal file
1123
engines/director/castmember/text.cpp
Normal file
File diff suppressed because it is too large
Load Diff
143
engines/director/castmember/text.h
Normal file
143
engines/director/castmember/text.h
Normal 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
|
||||
154
engines/director/castmember/transition.cpp
Normal file
154
engines/director/castmember/transition.cpp
Normal 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
|
||||
54
engines/director/castmember/transition.h
Normal file
54
engines/director/castmember/transition.h
Normal 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
|
||||
92
engines/director/castmember/xtra.cpp
Normal file
92
engines/director/castmember/xtra.cpp
Normal 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
|
||||
48
engines/director/castmember/xtra.h
Normal file
48
engines/director/castmember/xtra.h
Normal 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
|
||||
Reference in New Issue
Block a user