[Scummvm-git-logs] scummvm master -> 8cb29bd921b65e4495a07e8c46eff3e616d3fca3
sev-
noreply at scummvm.org
Sat Apr 29 12:20:19 UTC 2023
This automated email contains information about 21 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
88ba80555a DIRECTOR: Add initial support for film loops in D3
ff959da626 DIRECTOR: LINGO: Fix consts table loading for D5
459115fb78 DIRECTOR: Fix castInfo loading for D5
cc353d9acf DIRECTOR: Fix BitmapCastMember for D5
1d84409629 DIRECTOR: Fix ScriptCastMember for D5
b2c0255912 DIRECTOR: Improve frame parser for D5
7b8fee4599 DIRECTOR: Add stubs for V5 cast library info loader
f0343d6f02 DIRECTOR: Add stubs for BatQT XObj
9ff4641c68 DIRECTOR: LINGO: Add RECT support to getObjectProp
f4cbadaca2 DIRECTOR: XOBJ: FindSys must return a path with a slash
a407f165a6 DIRECTOR: XOBJ: Remove directory from fileio paths
4e1b32c0d4 DIRECTOR: Add filesystem quirk for teamxtreme2
8ff91ce024 DIRECTOR: LINGO: Make b_getNthFileNameInFolder always check cache
ef3d360552 DIRECTOR: Unify arguments to Cast::loadXData functions
f703ee6ecf DIRECTOR: Split CastMember classes into files
e289ea5896 DIRECTOR: Remove shared cast check from Cast::loadBitmapData()
1fd0692f6b DIRECTOR: Move load operations inside cast member
4557c52258 DIRECTOR: Lazily load cast member data from archive
14af094515 DIRECTOR: XOBJ: Replace stub boilerplate with macro
2730ccc7ed DIRECTOR: LINGO: Make arithmetic issues invoke lingoError()
8cb29bd921 DIRECTOR: LINGO: Add special override mechanics for List builtins
Commit: 88ba80555aa268db16118ba303a7306f156e7993
https://github.com/scummvm/scummvm/commit/88ba80555aa268db16118ba303a7306f156e7993
Author: Scott Percival (code at moral.net.au)
Date: 2023-04-29T14:20:05+02:00
Commit Message:
DIRECTOR: Add initial support for film loops in D3
Changed paths:
engines/director/cast.cpp
engines/director/castmember.cpp
engines/director/castmember.h
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index 8461c6c623c..8e586c20466 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -623,7 +623,7 @@ void Cast::loadPaletteData(PaletteCastMember *member) {
paletteId = member->_children[0].index;
} else if (_version < kFileVer400) {
// For D3 and below, palette IDs are stored in the CLUT resource as cast ID + 1024
- paletteId = member->getID() + 1024;
+ paletteId = member->getID() + _castIDoffset;
} else {
warning("Cast::loadPaletteData(): Expected 1 child for palette cast, got %d", member->_children.size());
}
@@ -634,14 +634,22 @@ void Cast::loadPaletteData(PaletteCastMember *member) {
}
void Cast::loadFilmLoopData(FilmLoopCastMember *member) {
- if (_version >= kFileVer400 && _version < kFileVer500) {
+ if (_version < kFileVer400) {
+ // Director 3 and below should have a SCVW resource
+ uint16 filmLoopId = member->getID() + _castIDoffset;
+ uint32 tag = MKTAG('S', 'C', 'V', 'W');
+ Common::SeekableReadStreamEndian *loop = _castArchive->getResource(tag, filmLoopId);
+ debugC(2, kDebugLoading, "****** Loading '%s' id: %d, %d bytes", tag2str(tag), filmLoopId, (int)loop->size());
+ member->loadFilmLoopData(*loop);
+ delete loop;
+ } else if (_version >= kFileVer400 && _version < kFileVer500) {
if (member->_children.size() == 1) {
uint16 filmLoopId = member->_children[0].index;
uint32 tag = member->_children[0].tag;
if (_castArchive->hasResource(tag, filmLoopId)) {
Common::SeekableReadStreamEndian *loop = _castArchive->getResource(tag, filmLoopId);
debugC(2, kDebugLoading, "****** Loading '%s' id: %d, %d bytes", tag2str(tag), filmLoopId, (int)loop->size());
- member->loadFilmLoopData(*loop);
+ member->loadFilmLoopDataV4(*loop);
delete loop;
} else {
warning("Cast::loadFilmLoopData(): Film loop not found");
@@ -1023,6 +1031,10 @@ void Cast::loadCastDataVWCR(Common::SeekableReadStreamEndian &stream) {
debugC(3, kDebugLoading, "Cast::loadCastDataVWCR(): CastTypes id: %d(%s) PaletteCastMember", id, numToCastNum(id));
_loadedCast->setVal(id, new PaletteCastMember(this, id, stream, _version));
break;
+ case kCastFilmLoop:
+ debugC(3, kDebugLoading, "Cast::loadCastDataVWCR(): CastTypes id: %d(%s) FilmLoopCastMember", id, numToCastNum(id));
+ _loadedCast->setVal(id, new FilmLoopCastMember(this, id, stream, _version));
+ break;
default:
warning("Cast::loadCastDataVWCR(): Unhandled cast id: %d(%s), type: %d, %d bytes", id, numToCastNum(id), castType, size);
break;
@@ -1603,8 +1615,12 @@ Common::String Cast::formatCastSummary(int castId = -1) {
CastMember *castMember = getCastMember(*it);
CastMemberInfo *castMemberInfo = getCastMemberInfo(*it);
Common::String info = castMember->formatInfo();
- result += Common::String::format("%5d: type=%s, name=\"%s\"",
- *it, castType2str(castMember->_type),
+ result += Common::String::format("%5d", *it);
+ if (_version < kFileVer400) {
+ result += Common::String::format(" (%s)", numToCastNum(*it));
+ }
+ result += Common::String::format(": type=%s, name=\"%s\"",
+ castType2str(castMember->_type),
castMemberInfo ? castMemberInfo->name.c_str() : ""
);
diff --git a/engines/director/castmember.cpp b/engines/director/castmember.cpp
index a60a2bfd4f7..0794cb8eda8 100644
--- a/engines/director/castmember.cpp
+++ b/engines/director/castmember.cpp
@@ -995,6 +995,137 @@ void FilmLoopCastMember::loadFilmLoopData(Common::SeekableReadStreamEndian &stre
_initialRect = Common::Rect();
_frames.clear();
+ uint32 size = stream.readUint32BE();
+ if (debugChannelSet(5, kDebugLoading)) {
+ debugC(5, kDebugLoading, "SCVW body:");
+ uint32 pos = stream.pos();
+ stream.seek(0);
+ stream.hexdump(size);
+ stream.seek(pos);
+ }
+ uint16 channelSize = 16;
+ FilmLoopFrame newFrame;
+
+ while (stream.pos() < size) {
+ uint16 frameSize = stream.readUint16BE() - 2;
+ if (debugChannelSet(5, kDebugLoading)) {
+ debugC(5, kDebugLoading, "Frame entry:");
+ stream.hexdump(frameSize);
+ }
+
+ while (frameSize > 0) {
+ int msgWidth = stream.readByte() * 2;
+ int order = stream.readByte() * 2 - 0x20;
+ frameSize -= 2;
+ debugC(8, kDebugLoading, "Message: msgWidth %d, order %d", msgWidth, order);
+ if (debugChannelSet(8, kDebugLoading)) {
+ stream.hexdump(msgWidth);
+ }
+
+ int fieldPosition = order;
+ int finishPosition = order + msgWidth;
+ while (fieldPosition < finishPosition) {
+ int channel = (fieldPosition / channelSize);
+ int channelOffset = fieldPosition % channelSize;
+
+ Sprite sprite(nullptr);
+ sprite._movie = g_director->getCurrentMovie();
+ if (newFrame.sprites.contains(channel)) {
+ sprite = newFrame.sprites.getVal(channel);
+ }
+ sprite._spriteType = kCastMemberSprite;
+ sprite._puppet = 1;
+ sprite._stretch = 1;
+
+ switch (channelOffset) {
+ case kSpritePositionUnk1:
+ stream.readByte();
+ fieldPosition++;
+ break;
+ case kSpritePositionEnabled:
+ sprite._enabled = stream.readByte() != 0;
+ fieldPosition++;
+ break;
+ case kSpritePositionUnk2:
+ stream.readUint16BE();
+ fieldPosition += 2;
+ break;
+ case kSpritePositionFlags:
+ sprite._thickness = stream.readByte();
+ sprite._inkData = stream.readByte();
+ sprite._ink = static_cast<InkType>(sprite._inkData & 0x3f);
+
+ if (sprite._inkData & 0x40)
+ sprite._trails = 1;
+ else
+ sprite._trails = 0;
+
+ fieldPosition += 2;
+ break;
+ case kSpritePositionCastId:
+ sprite.setCast(CastMemberID(stream.readUint16(), 0));
+ fieldPosition += 2;
+ break;
+ case kSpritePositionY:
+ sprite._startPoint.y = stream.readUint16();
+ fieldPosition += 2;
+ break;
+ case kSpritePositionX:
+ sprite._startPoint.x = stream.readUint16();
+ fieldPosition += 2;
+ break;
+ case kSpritePositionWidth:
+ sprite._width = stream.readUint16();
+ fieldPosition += 2;
+ break;
+ case kSpritePositionHeight:
+ sprite._height = stream.readUint16();
+ fieldPosition += 2;
+ break;
+ default:
+ stream.readUint16BE();
+ fieldPosition += 2;
+ break;
+ }
+ newFrame.sprites.setVal(channel, sprite);
+ }
+
+ frameSize -= msgWidth;
+ }
+
+ for (Common::HashMap<int, Sprite>::iterator s = newFrame.sprites.begin(); s != newFrame.sprites.end(); ++s) {
+ debugC(5, kDebugLoading, "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);
+
+ Common::Point topLeft = s->_value._startPoint + s->_value.getRegistrationOffset();
+ 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, "New bounding box: %d %d %d %d", _initialRect.left, _initialRect.top, _initialRect.width(), _initialRect.height());
+
+ }
+
+ _frames.push_back(newFrame);
+
+ }
+ debugC(5, kDebugLoading, "Full bounding box: %d %d %d %d", _initialRect.left, _initialRect.top, _initialRect.width(), _initialRect.height());
+}
+
+void FilmLoopCastMember::loadFilmLoopDataV4(Common::SeekableReadStreamEndian &stream) {
+ _initialRect = Common::Rect();
+ _frames.clear();
+
uint32 size = stream.readUint32BE();
if (debugChannelSet(5, kDebugLoading)) {
debugC(5, kDebugLoading, "SCVW body:");
diff --git a/engines/director/castmember.h b/engines/director/castmember.h
index 1f38c9be2cf..18fd6b99039 100644
--- a/engines/director/castmember.h
+++ b/engines/director/castmember.h
@@ -228,6 +228,7 @@ public:
Common::Array<Channel> *getSubChannels(Common::Rect &bbox, Channel *channel);
void loadFilmLoopData(Common::SeekableReadStreamEndian &stream);
+ void loadFilmLoopDataV4(Common::SeekableReadStreamEndian &stream);
Common::String formatInfo() override;
Commit: ff959da62627e14b41debe2a96ef4929d8592eea
https://github.com/scummvm/scummvm/commit/ff959da62627e14b41debe2a96ef4929d8592eea
Author: Scott Percival (code at moral.net.au)
Date: 2023-04-29T14:20:05+02:00
Commit Message:
DIRECTOR: LINGO: Fix consts table loading for D5
Changed paths:
engines/director/lingo/lingo-bytecode.cpp
diff --git a/engines/director/lingo/lingo-bytecode.cpp b/engines/director/lingo/lingo-bytecode.cpp
index 88616da98fb..adb6b30e540 100644
--- a/engines/director/lingo/lingo-bytecode.cpp
+++ b/engines/director/lingo/lingo-bytecode.cpp
@@ -1146,6 +1146,11 @@ ScriptContext *LingoCompiler::compileLingoV4(Common::SeekableReadStreamEndian &s
// read each entry in the reference table.
stream.seek(constsOffset);
+ int constsIndexSize = MAX((int)constsStoreOffset - (int)constsOffset, 0);
+ if (debugChannelSet(5, kDebugLoading)) {
+ debugC(5, kDebugLoading, "Lscr consts index:");
+ stream.hexdump(constsIndexSize);
+ }
for (uint16 i = 0; i < constsCount; i++) {
Datum constant;
uint32 constType = 0;
@@ -1154,6 +1159,7 @@ ScriptContext *LingoCompiler::compileLingoV4(Common::SeekableReadStreamEndian &s
} else {
constType = (uint32)stream.readUint16();
}
+
uint32 value = stream.readUint32();
switch (constType) {
case 1: // String type
@@ -1225,7 +1231,6 @@ ScriptContext *LingoCompiler::compileLingoV4(Common::SeekableReadStreamEndian &s
warning("Unknown constant type %d", constType);
break;
}
-
_assemblyContext->_constants.push_back(constant);
}
free(constsStore);
@@ -1368,15 +1373,27 @@ ScriptContext *LingoCompiler::compileLingoV4(Common::SeekableReadStreamEndian &s
Common::hexdump(codeStore, length, 16, startOffset);
}
- uint16 pointer = startOffset - codeStoreOffset;
+ uint32 pointer = startOffset - codeStoreOffset;
Common::Array<uint32> offsetList;
Common::Array<uint32> jumpList;
+
+ // Size of an entry in the consts index.
+ int constEntrySize = 0;
+ if (version >= kFileVer500) {
+ // For V5 this is uint32 type + uint32 offset
+ constEntrySize = 8;
+ } else {
+ // For V4 this is uint16 type + uint32 offset
+ constEntrySize = 6;
+ }
+
while (pointer < startOffset + length - codeStoreOffset) {
uint8 opcode = codeStore[pointer];
pointer += 1;
if (opcode == 0x44 || opcode == 0x84) {
- // push a constant
+ // Opcode for pushing a value from the constants table.
+ // Rewrite these to inline the constant into our bytecode.
offsetList.push_back(_currentAssembly->size());
int arg = 0;
if (opcode == 0x84) {
@@ -1386,11 +1403,12 @@ ScriptContext *LingoCompiler::compileLingoV4(Common::SeekableReadStreamEndian &s
arg = (uint8)codeStore[pointer];
pointer += 1;
}
- // remove struct size alignment
- if (arg % 6) {
- warning("Opcode 0x%02x arg %d not a multiple of 6!", opcode, arg);
+ // The argument is a byte offset to an entry in the consts index.
+ // As such, it should be an exact multiple of the entry size.
+ if (arg % constEntrySize) {
+ warning("Opcode 0x%02x arg %d not a multiple of %d", opcode, arg, constEntrySize);
}
- arg /= 6;
+ arg /= constEntrySize;
Datum constant = _assemblyContext->_constants[arg];
switch (constant.type) {
case INT:
@@ -1468,10 +1486,10 @@ ScriptContext *LingoCompiler::compileLingoV4(Common::SeekableReadStreamEndian &s
break;
case 'p':
// argument is some kind of denormalised offset
- if (arg % 6) {
- warning("Argument %d was expected to be a multiple of 6", arg);
+ if (arg % constEntrySize) {
+ warning("Argument %d was expected to be a multiple of %d", arg, constEntrySize);
}
- arg /= 6;
+ arg /= constEntrySize;
break;
case 'a':
// argument is a function argument ID
Commit: 459115fb7855cfe10c6db3273cc1be0045b21254
https://github.com/scummvm/scummvm/commit/459115fb7855cfe10c6db3273cc1be0045b21254
Author: Scott Percival (code at moral.net.au)
Date: 2023-04-29T14:20:05+02:00
Commit Message:
DIRECTOR: Fix castInfo loading for D5
Changed paths:
engines/director/archive.cpp
engines/director/cast.cpp
diff --git a/engines/director/archive.cpp b/engines/director/archive.cpp
index d7131bb12f1..7f163c71b51 100644
--- a/engines/director/archive.cpp
+++ b/engines/director/archive.cpp
@@ -339,7 +339,7 @@ void MacArchive::readTags() {
res.name = _resFork->getResName(tagArray[i], idArray[j]);
res.tag = tagArray[i];
res.index = idArray[j];
- debug(3, "Found MacArchive resource '%s' %d: %s", tag2str(tagArray[i]), idArray[j], res.name.c_str());
+ debug(3, "MacArchive::readTags(): Found MacArchive resource '%s' %d: %s", tag2str(tagArray[i]), idArray[j], res.name.c_str());
if (ConfMan.getBool("dump_scripts"))
dumpChunk(res, out);
}
@@ -349,6 +349,12 @@ void MacArchive::readTags() {
_types[tagArray[i]] = resMap;
}
}
+ if (debugChannelSet(5, kDebugLoading)) {
+ debugC(5, kDebugLoading, "MacArchive::readTags(): Resources found:");
+ for (const auto &it : _types) {
+ debugC(5, kDebugLoading, "%s: %d", tag2str(it._key), it._value.size());
+ }
+ }
}
Common::SeekableReadStreamEndian *MacArchive::getResource(uint32 tag, uint16 id) {
@@ -449,6 +455,12 @@ bool RIFFArchive::openStream(Common::SeekableReadStream *stream, uint32 startOff
stream->seek(startResPos);
}
+ if (debugChannelSet(5, kDebugLoading)) {
+ debugC(5, kDebugLoading, "RIFFArchive::openStream(): Resources found:");
+ for (const auto &it : _types) {
+ debugC(5, kDebugLoading, "%s: %d", tag2str(it._key), it._value.size());
+ }
+ }
return true;
}
@@ -741,6 +753,13 @@ bool RIFXArchive::readMemoryMap(Common::SeekableReadStreamEndian &stream, uint32
_resources.push_back(&res);
}
+ if (debugChannelSet(5, kDebugLoading)) {
+ debugC(5, kDebugLoading, "RIFXArchive::readMemoryMap(): Resources found:");
+ for (const auto &it : _types) {
+ debugC(5, kDebugLoading, "%s: %d", tag2str(it._key), it._value.size());
+ }
+ }
+
return true;
}
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index 8e586c20466..7d3f905bcf7 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -1094,7 +1094,7 @@ void Cast::loadCastData(Common::SeekableReadStreamEndian &stream, uint16 id, Res
if (debugChannelSet(5, kDebugLoading) && stream.size() < 2048)
stream.hexdump(stream.size());
- uint32 castSize, castInfoSize, size3, castType, castSizeToRead;
+ uint32 castDataSize, castInfoSize, castType, castDataSizeToRead, castDataOffset, castInfoOffset;
byte flags1 = 0, unk1 = 0, unk2 = 0, unk3 = 0;
// D2-3 cast members should be loaded in loadCastDataVWCR
@@ -1103,7 +1103,6 @@ void Cast::loadCastData(Common::SeekableReadStreamEndian &stream, uint16 id, Res
size1 = stream.readUint16();
sizeToRead = size1 +16; // 16 is for bounding rects
size2 = stream.readUint32();
- size3 = 0;
castType = stream.readByte();
unk1 = stream.readByte();
unk2 = stream.readByte();
@@ -1112,44 +1111,38 @@ void Cast::loadCastData(Common::SeekableReadStreamEndian &stream, uint16 id, Res
#endif
if (_version >= kFileVer400 && _version < kFileVer500) {
- castSize = stream.readUint16();
- castSizeToRead = castSize;
+ castDataSize = stream.readUint16();
+ castDataSizeToRead = castDataSize;
castInfoSize = stream.readUint32();
- size3 = 0;
// these bytes are common but included in cast size
castType = stream.readByte();
- castSizeToRead -= 1;
- if (castSizeToRead) {
+ castDataSizeToRead -= 1;
+ if (castDataSizeToRead) {
flags1 = stream.readByte();
- castSizeToRead -= 1;
+ castDataSizeToRead -= 1;
}
+ castDataOffset = stream.pos();
+ castInfoOffset = stream.pos() + castDataSizeToRead;
} else if (_version >= kFileVer500 && _version < kFileVer600) {
castType = stream.readUint32();
- size3 = stream.readUint32();
castInfoSize = stream.readUint32();
- castSize = stream.readUint32();
- if (castType == 1) {
- if (size3 == 0)
- return;
- for (uint32 skip = 0; skip < (castSize - 4) / 4; skip++)
- stream.readUint32();
- }
-
- castSizeToRead = stream.size();
+ castDataSize = stream.readUint32();
+ castDataSizeToRead = castDataSize;
+ castInfoOffset = stream.pos();
+ castDataOffset = stream.pos() + castInfoSize;
} else {
error("Cast::loadCastData: unsupported Director version (%d)", _version);
}
- debugC(3, kDebugLoading, "Cast::loadCastData(): CASt: id: %d type: %x castSize: %d castInfoSize: %d (%x) size3: %d unk1: %d unk2: %d unk3: %d",
- id, castType, castSize, castInfoSize, castInfoSize, size3, unk1, unk2, unk3);
+ debugC(3, kDebugLoading, "Cast::loadCastData(): CASt: id: %d type: %x castDataSize: %d castInfoSize: %d (%x) unk1: %d unk2: %d unk3: %d",
+ id, castType, castDataSize, castInfoSize, castInfoSize, unk1, unk2, unk3);
// read the cast member itself
-
- byte *data = (byte *)calloc(castSizeToRead, 1);
- stream.read(data, castSizeToRead);
-
- Common::MemoryReadStreamEndian castStream(data, castSizeToRead, stream.isBE());
+ byte *data = (byte *)calloc(castDataSizeToRead, 1);
+ stream.seek(castDataOffset);
+ stream.read(data, castDataSizeToRead);
+ Common::MemoryReadStreamEndian castStream(data, castDataSizeToRead, stream.isBE());
if (_loadedCast->contains(id)) {
warning("Cast::loadCastData(): Multiple cast members with ID %d, overwriting", id);
@@ -1225,13 +1218,14 @@ void Cast::loadCastData(Common::SeekableReadStreamEndian &stream, uint16 id, Res
free(data);
// read the cast member info
-
- if (castInfoSize && _version < kFileVer500) {
- loadCastInfo(stream, id);
+ if (castInfoSize) {
+ data = (byte *)calloc(castInfoSize, 1);
+ stream.seek(castInfoOffset);
+ stream.read(data, castInfoSize);
+ Common::MemoryReadStreamEndian castInfoStream(data, castInfoSize, stream.isBE());
+ loadCastInfo(castInfoStream, id);
+ free(data);
}
-
- if (size3)
- warning("Cast::loadCastData(): size3: %x", size3);
}
struct LingoContextEntry {
Commit: cc353d9acf082a270b87dfde9bda88c71ef47f94
https://github.com/scummvm/scummvm/commit/cc353d9acf082a270b87dfde9bda88c71ef47f94
Author: Scott Percival (code at moral.net.au)
Date: 2023-04-29T14:20:05+02:00
Commit Message:
DIRECTOR: Fix BitmapCastMember for D5
Changed paths:
engines/director/castmember.cpp
diff --git a/engines/director/castmember.cpp b/engines/director/castmember.cpp
index 0794cb8eda8..9a9dce3a874 100644
--- a/engines/director/castmember.cpp
+++ b/engines/director/castmember.cpp
@@ -134,7 +134,7 @@ BitmapCastMember::BitmapCastMember(Cast *cast, uint16 castId, Common::SeekableRe
_pitch *= _bitsPerPixel;
_pitch >>= 3;
- } else if (version >= kFileVer400 && version < kFileVer500) {
+ } else if (version >= kFileVer400 && version < kFileVer600) {
_flags1 = flags1;
_pitch = stream.readUint16();
_pitch &= 0x0fff;
@@ -149,6 +149,9 @@ BitmapCastMember::BitmapCastMember(Cast *cast, uint16 castId, Common::SeekableRe
if (stream.eos()) {
_bitsPerPixel = 0;
} else {
+ if (version >= kFileVer500) {
+ stream.readSint16(); // ff ff
+ }
_clut = stream.readSint16();
if (_clut <= 0) // builtin palette
_clut -= 1;
@@ -182,24 +185,6 @@ BitmapCastMember::BitmapCastMember(Cast *cast, uint16 castId, Common::SeekableRe
debug("BitmapCastMember: tail");
Common::hexdump(buf, tail);
}
- } else if (version >= kFileVer500) {
- uint16 count = stream.readUint16();
- for (uint16 cc = 0; cc < count; cc++)
- stream.readUint32();
-
- uint32 stringLength = stream.readUint32();
- for (uint32 s = 0; s < stringLength; s++)
- stream.readByte();
-
- /*uint16 width =*/ stream.readUint16LE(); //maybe?
- _initialRect = Movie::readRect(stream);
-
- /*uint32 somethingElse =*/ stream.readUint32();
- _boundingRect = Movie::readRect(stream);
-
- _bitsPerPixel = stream.readUint16();
-
- stream.readUint32();
}
_tag = castTag;
Commit: 1d84409629602847153cb5a1d06d44b58fcc7b24
https://github.com/scummvm/scummvm/commit/1d84409629602847153cb5a1d06d44b58fcc7b24
Author: Scott Percival (code at moral.net.au)
Date: 2023-04-29T14:20:05+02:00
Commit Message:
DIRECTOR: Fix ScriptCastMember for D5
Changed paths:
engines/director/castmember.cpp
diff --git a/engines/director/castmember.cpp b/engines/director/castmember.cpp
index 9a9dce3a874..f3d38d47e79 100644
--- a/engines/director/castmember.cpp
+++ b/engines/director/castmember.cpp
@@ -1712,9 +1712,14 @@ ScriptCastMember::ScriptCastMember(Cast *cast, uint16 castId, Common::SeekableRe
_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 < kFileVer500) {
+ } else if (version >= kFileVer400 && version < kFileVer600) {
byte unk1 = stream.readByte();
byte type = stream.readByte();
@@ -1733,13 +1738,6 @@ ScriptCastMember::ScriptCastMember(Cast *cast, uint16 castId, Common::SeekableRe
stream.readByte(); // There should be no more data
assert(stream.eos());
- } else if (version >= kFileVer500) {
- stream.readByte();
- stream.readByte();
-
- debugC(4, kDebugLoading, "CASt: Script");
-
- // WIP need to complete this!
}
}
Commit: b2c02559123fdf335003dcdad061b9ac7002d255
https://github.com/scummvm/scummvm/commit/b2c02559123fdf335003dcdad061b9ac7002d255
Author: Scott Percival (code at moral.net.au)
Date: 2023-04-29T14:20:05+02:00
Commit Message:
DIRECTOR: Improve frame parser for D5
Changed paths:
engines/director/castmember.cpp
engines/director/frame.cpp
engines/director/frame.h
engines/director/score.cpp
diff --git a/engines/director/castmember.cpp b/engines/director/castmember.cpp
index f3d38d47e79..cc0fd428dbf 100644
--- a/engines/director/castmember.cpp
+++ b/engines/director/castmember.cpp
@@ -150,7 +150,7 @@ BitmapCastMember::BitmapCastMember(Cast *cast, uint16 castId, Common::SeekableRe
_bitsPerPixel = 0;
} else {
if (version >= kFileVer500) {
- stream.readSint16(); // ff ff
+ stream.readSint16(); // is this the castlib? was ff ff
}
_clut = stream.readSint16();
if (_clut <= 0) // builtin palette
diff --git a/engines/director/frame.cpp b/engines/director/frame.cpp
index f3d58d92c02..0f27ffe6831 100644
--- a/engines/director/frame.cpp
+++ b/engines/director/frame.cpp
@@ -258,17 +258,47 @@ void Frame::readChannels(Common::SeekableReadStreamEndian *stream, uint16 versio
warning("Frame::readChannels(): STUB: unk5: %d 0x%x", unk1, unk1);
} else if (version >= kFileVer500 && version < kFileVer600) {
+ if (debugChannelSet(8, kDebugLoading)) {
+ debugC(8, kDebugLoading, "Frame::readChannels(): 48 byte header");
+ stream->hexdump(48);
+ }
// Sound/Tempo/Transition channel
- stream->read(unk, 24);
+ uint16 actionCastLib = stream->readUint16();
+ uint16 actionId = stream->readUint16();
+ _actionId = CastMemberID(actionId, actionCastLib);
+ uint16 sound1CastLib = stream->readUint16();
+ uint16 sound1Id = stream->readUint16();
+ _sound1 = CastMemberID(sound1Id, sound1CastLib);
+ uint16 sound2CastLib = stream->readUint16();
+ uint16 sound2Id = stream->readUint16();
+ _sound2 = CastMemberID(sound2Id, sound2CastLib);
+ uint16 transCastLib = stream->readUint16();
+ uint16 transId = stream->readUint16();
+ _trans = CastMemberID(transId, transCastLib);
+
+ stream->read(unk, 5);
+
+ _tempo = stream->readByte();
+
+ stream->read(unk, 2);
// palette
- stream->read(unk, 24);
- } else {
- // Sound[2]
- // palette
- // Transition
- // Tempo
- // Script
+ stream->read(unk, 2);
+
+ _palette.paletteId = stream->readSint16();
+ _palette.speed = stream->readByte();
+ _palette.flags = stream->readByte();
+ _palette.colorCycling = (_palette.flags & 0x80) != 0;
+ _palette.normal = (_palette.flags & 0x60) == 0x00;
+ _palette.fadeToBlack = (_palette.flags & 0x60) == 0x60;
+ _palette.fadeToWhite = (_palette.flags & 0x60) == 0x40;
+ _palette.autoReverse = (_palette.flags & 0x10) != 0;
+ _palette.overTime = (_palette.flags & 0x04) != 0;
+ _palette.firstColor = g_director->transformColor(stream->readByte() + 0x80);
+ _palette.lastColor = g_director->transformColor(stream->readByte() + 0x80);
+ _palette.frameCount = stream->readUint16();
+ _palette.cycleCount = stream->readUint16();
+ stream->read(unk, 12);
}
_transChunkSize = CLIP<byte>(_transChunkSize, 0, 128);
diff --git a/engines/director/frame.h b/engines/director/frame.h
index a5b71897d17..3aa51733749 100644
--- a/engines/director/frame.h
+++ b/engines/director/frame.h
@@ -115,6 +115,7 @@ public:
uint8 _transArea; // 1 - Whole Window, 0 - Changing Area
uint8 _transChunkSize;
TransitionType _transType;
+ CastMemberID _trans;
PaletteInfo _palette;
uint8 _tempo;
diff --git a/engines/director/score.cpp b/engines/director/score.cpp
index 1c1019656e7..46361104600 100644
--- a/engines/director/score.cpp
+++ b/engines/director/score.cpp
@@ -1292,14 +1292,16 @@ void Score::playQueuedSound() {
void Score::loadFrames(Common::SeekableReadStreamEndian &stream, uint16 version) {
debugC(1, kDebugLoading, "****** Loading frames VWSC");
- //stream.hexdump(stream.size());
+ if (debugChannelSet(8, kDebugLoading)) {
+ stream.hexdump(stream.size());
+ }
uint32 size = stream.readUint32();
size -= 4;
if (version < kFileVer400) {
_numChannelsDisplayed = 30;
- } else if (version >= kFileVer400 && version < kFileVer500) {
+ } else if (version >= kFileVer400 && version < kFileVer600) {
uint32 frame1Offset = stream.readUint32();
uint32 numFrames = stream.readUint32();
uint16 framesVersion = stream.readUint16();
@@ -1323,38 +1325,6 @@ void Score::loadFrames(Common::SeekableReadStreamEndian &stream, uint16 version)
warning("STUB: Score::loadFrames. frame1Offset: %x numFrames: %x version: %x spriteRecordSize: %x numChannels: %x numChannelsDisplayed: %x",
frame1Offset, numFrames, framesVersion, spriteRecordSize, numChannels, _numChannelsDisplayed);
// Unknown, some bytes - constant (refer to contuinity).
- } else if (version >= kFileVer500) {
- //what data is up the top of D5 VWSC?
- uint32 unk1 = stream.readUint32();
- uint32 unk2 = stream.readUint32();
-
- uint16 unk3, unk4, unk5, unk6;
-
- if (unk2 > 0) {
- uint32 blockSize = stream.readUint32() - 1;
- stream.readUint32();
- stream.readUint32();
- stream.readUint32();
- stream.readUint32();
- for (uint32 skip = 0; skip < blockSize * 4; skip++)
- stream.readByte();
-
- //header number two... this is our actual score entry point.
- unk1 = stream.readUint32();
- unk2 = stream.readUint32();
- stream.readUint32();
- unk3 = stream.readUint16();
- unk4 = stream.readUint16();
- unk5 = stream.readUint16();
- unk6 = stream.readUint16();
- } else {
- unk3 = stream.readUint16();
- unk4 = stream.readUint16();
- unk5 = stream.readUint16();
- unk6 = stream.readUint16();
- size -= 16;
- }
- warning("STUB: Score::loadFrames. unk1: %x unk2: %x unk3: %x unk4: %x unk5: %x unk6: %x", unk1, unk2, unk3, unk4, unk5, unk6);
}
uint16 channelSize;
@@ -1378,6 +1348,9 @@ void Score::loadFrames(Common::SeekableReadStreamEndian &stream, uint16 version)
while (size != 0 && !stream.eos()) {
uint16 frameSize = stream.readUint16();
debugC(3, kDebugLoading, "++++++++++ score frame %d (frameSize %d) size %d", _frames.size(), frameSize, size);
+ if (debugChannelSet(8, kDebugLoading)) {
+ stream.hexdump(frameSize);
+ }
if (frameSize > 0) {
Frame *frame = new Frame(this, _numChannelsDisplayed);
Commit: 7b8fee4599bdcc701c4eda6648c7e6a58395f6b7
https://github.com/scummvm/scummvm/commit/7b8fee4599bdcc701c4eda6648c7e6a58395f6b7
Author: Scott Percival (code at moral.net.au)
Date: 2023-04-29T14:20:05+02:00
Commit Message:
DIRECTOR: Add stubs for V5 cast library info loader
Changed paths:
engines/director/cast.cpp
engines/director/cast.h
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index 7d3f905bcf7..ec0eaae4a78 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -518,6 +518,22 @@ void Cast::loadCast() {
delete r;
}
+ // Cast library mapping, used in V5 and up
+ if ((r = _castArchive->getMovieResourceIfPresent(MKTAG('M', 'C', 's', 'L'))) != nullptr) {
+ loadCastLibMapping(*r);
+ delete r;
+ }
+
+ Common::Array<uint16> cinf = _castArchive->getResourceIDList(MKTAG('C', 'i', 'n', 'f'));
+ if (cinf.size() > 0) {
+ debugC(2, kDebugLoading, "****** Loading %d CastLibInfos Cinf", cinf.size());
+
+ for (Common::Array<uint16>::iterator iterator = cinf.begin(); iterator != cinf.end(); ++iterator) {
+ loadCastLibInfo(*(r = _castArchive->getResource(MKTAG('C', 'i', 'n', 'f'), *iterator)), *iterator);
+ delete r;
+ }
+ }
+
Common::Array<uint16> vwci = _castArchive->getResourceIDList(MKTAG('V', 'W', 'C', 'I'));
if (vwci.size() > 0) {
debugC(2, kDebugLoading, "****** Loading %d CastInfos VWCI", vwci.size());
@@ -1499,6 +1515,40 @@ void Cast::loadCastInfo(Common::SeekableReadStreamEndian &stream, uint16 id) {
_castsInfo[id] = ci;
}
+void Cast::loadCastLibMapping(Common::SeekableReadStreamEndian &stream) {
+ if (debugChannelSet(8, kDebugLoading)) {
+ stream.hexdump(stream.size());
+ }
+ stream.readUint32(); // header size
+ uint32 count = stream.readUint32();
+ stream.readUint16();
+ uint32 unkCount = stream.readUint32() + 1;
+ for (uint32 i = 0; i < unkCount; i++) {
+ stream.readUint32();
+ }
+ for (uint32 i = 0; i < count; i++) {
+ int nameSize = stream.readByte() + 1;
+ Common::String name = stream.readString('\0', nameSize);
+ int pathSize = stream.readByte() + 1;
+ Common::String path = stream.readString('\0', pathSize);
+ if (pathSize > 1)
+ stream.readUint16();
+ stream.readUint16();
+ uint16 itemCount = stream.readUint16();
+ stream.readUint16();
+ uint16 libId = stream.readUint16();
+ debugC(5, kDebugLoading, "Cast::loadCastLibMapping: name: %s, path: %s, itemCount: %d, libId: %d", name.c_str(), path.c_str(), itemCount, libId);
+ }
+ return;
+}
+
+void Cast::loadCastLibInfo(Common::SeekableReadStreamEndian &stream, uint16 id) {
+ if (debugChannelSet(8, kDebugLoading)) {
+ stream.hexdump(stream.size());
+ }
+ debugC(5, kDebugLoading, "Cast::loadCastLibInfo(): %d", id);
+}
+
Common::CodePage Cast::getFileEncoding() {
// Returns the default encoding for the file this cast is contained in.
// This depends on which platform the file was made on.
diff --git a/engines/director/cast.h b/engines/director/cast.h
index 55aa3ed9987..b94c86982f1 100644
--- a/engines/director/cast.h
+++ b/engines/director/cast.h
@@ -89,6 +89,8 @@ public:
void loadCastDataVWCR(Common::SeekableReadStreamEndian &stream);
void loadCastData(Common::SeekableReadStreamEndian &stream, uint16 id, Resource *res);
void loadCastInfo(Common::SeekableReadStreamEndian &stream, uint16 id);
+ void loadCastLibMapping(Common::SeekableReadStreamEndian &stream);
+ void loadCastLibInfo(Common::SeekableReadStreamEndian &stream, uint16 id);
void loadLingoContext(Common::SeekableReadStreamEndian &stream);
void loadExternalSound(Common::SeekableReadStreamEndian &stream);
void loadSord(Common::SeekableReadStreamEndian &stream);
Commit: f0343d6f02e11414a0e781068e0cf9e459c8f36b
https://github.com/scummvm/scummvm/commit/f0343d6f02e11414a0e781068e0cf9e459c8f36b
Author: Scott Percival (code at moral.net.au)
Date: 2023-04-29T14:20:05+02:00
Commit Message:
DIRECTOR: Add stubs for BatQT XObj
Changed paths:
engines/director/lingo/xlibs/batqt.cpp
engines/director/lingo/xlibs/batqt.h
diff --git a/engines/director/lingo/xlibs/batqt.cpp b/engines/director/lingo/xlibs/batqt.cpp
index 819851fd1a6..4e28933ffdf 100644
--- a/engines/director/lingo/xlibs/batqt.cpp
+++ b/engines/director/lingo/xlibs/batqt.cpp
@@ -68,7 +68,27 @@ const char *BatQT::fileNames[] = {
};
static MethodProto xlibMethods[] = {
- { "new", BatQT::m_new, 0, 0, 400 }, // D4
+ { "new", BatQT::m_new, 0, 0, 400 }, // D4
+ { "dispose", BatQT::m_dispose, 1, 1, 400 }, // D4
+ { "name", BatQT::m_name, 0, 0, 400 }, // D4
+ { "status", BatQT::m_status, 0, 0, 400 }, // D4
+ { "error", BatQT::m_error, 1, 1, 400 }, // D4
+ { "lastError", BatQT::m_lastError, 0, 0, 400 }, // D4
+ { "open", BatQT::m_open, 2, 2, 400 }, // D4
+ { "play", BatQT::m_play, 3, 3, 400 }, // D4
+ { "stop", BatQT::m_stop, 0, 0, 400 }, // D4
+ { "getTimeRange", BatQT::m_getTimeRange, 0, 0, 400 }, // D4
+ { "getMovieBox", BatQT::m_getMovieBox, 0, 0, 400 }, // D4
+ { "getTime", BatQT::m_getTime, 0, 0, 400 }, // D4
+ { "setTime", BatQT::m_setTime, 1, 1, 400 }, // D4
+ { "setVolume", BatQT::m_setVolume, 1, 1, 400 }, // D4
+ { "length", BatQT::m_length, 0, 0, 400 }, // D4
+ { "setMovieBox", BatQT::m_setMovieBox, 4, 4, 400 }, // D4
+ { "setTimeRange", BatQT::m_setTimeRange, 2, 2, 400 }, // D4
+ { "addCallback", BatQT::m_addCallback, 1, 1, 400 }, // D4
+ { "removeCallback", BatQT::m_removeCallback,1, 1, 400 }, // D4
+ { "resetCallbacks", BatQT::m_resetCallbacks,0, 0, 400 }, // D4
+ { "setBatch", BatQT::m_setBatch, 1, 1, 400 }, // D4
{ nullptr, nullptr, 0, 0, 0 }
};
@@ -88,7 +108,7 @@ void BatQT::close(int type) {
}
-BatQTXObject::BatQTXObject(ObjectType ObjectType) : Object<BatQTXObject>("FindSys") {
+BatQTXObject::BatQTXObject(ObjectType ObjectType) : Object<BatQTXObject>("BatQt") {
_objType = ObjectType;
}
@@ -96,4 +116,123 @@ void BatQT::m_new(int nargs) {
g_lingo->push(g_lingo->_state->me);
}
+void BatQT::m_dispose(int nargs) {
+ g_lingo->printSTUBWithArglist("BatQT::m_dispose", nargs);
+ g_lingo->dropStack(nargs);
+}
+
+void BatQT::m_name(int nargs) {
+ g_lingo->printSTUBWithArglist("BatQT::m_name", nargs);
+ g_lingo->dropStack(nargs);
+ g_lingo->push(Datum(""));
+}
+
+void BatQT::m_status(int nargs) {
+ g_lingo->printSTUBWithArglist("BatQT::m_status", nargs);
+ g_lingo->dropStack(nargs);
+ g_lingo->push(Datum(0));
+}
+
+void BatQT::m_error(int nargs) {
+ g_lingo->printSTUBWithArglist("BatQT::m_error", nargs);
+ g_lingo->dropStack(nargs);
+ g_lingo->push(Datum(""));
+}
+
+void BatQT::m_lastError(int nargs) {
+ g_lingo->printSTUBWithArglist("BatQT::m_lastError", nargs);
+ g_lingo->dropStack(nargs);
+ g_lingo->push(Datum(""));
+}
+
+void BatQT::m_open(int nargs) {
+ g_lingo->printSTUBWithArglist("BatQT::m_open", nargs);
+ g_lingo->dropStack(nargs);
+ g_lingo->push(Datum(0));
+}
+
+void BatQT::m_play(int nargs) {
+ g_lingo->printSTUBWithArglist("BatQT::m_play", nargs);
+ g_lingo->dropStack(nargs);
+ g_lingo->push(Datum(0));
+}
+
+void BatQT::m_stop(int nargs) {
+ g_lingo->printSTUBWithArglist("BatQT::m_stop", nargs);
+ g_lingo->dropStack(nargs);
+ g_lingo->push(Datum(0));
+}
+
+void BatQT::m_getTimeRange(int nargs) {
+ g_lingo->printSTUBWithArglist("BatQT::m_getTimeRange", nargs);
+ g_lingo->dropStack(nargs);
+ g_lingo->push(Datum(""));
+}
+
+void BatQT::m_getMovieBox(int nargs) {
+ g_lingo->printSTUBWithArglist("BatQT::m_getMovieBox", nargs);
+ g_lingo->dropStack(nargs);
+ g_lingo->push(Datum("0,0,320,240"));
+}
+
+void BatQT::m_getTime(int nargs) {
+ g_lingo->printSTUBWithArglist("BatQT::m_getTime", nargs);
+ g_lingo->dropStack(nargs);
+ g_lingo->push(Datum(0));
+}
+
+void BatQT::m_setTime(int nargs) {
+ g_lingo->printSTUBWithArglist("BatQT::m_setTime", nargs);
+ g_lingo->dropStack(nargs);
+ g_lingo->push(Datum(""));
+}
+
+void BatQT::m_setVolume(int nargs) {
+ g_lingo->printSTUBWithArglist("BatQT::m_setVolume", nargs);
+ g_lingo->dropStack(nargs);
+ g_lingo->push(Datum(""));
+}
+
+void BatQT::m_length(int nargs) {
+ g_lingo->printSTUBWithArglist("BatQT::m_length", nargs);
+ g_lingo->dropStack(nargs);
+ g_lingo->push(Datum(0));
+}
+
+void BatQT::m_setMovieBox(int nargs) {
+ g_lingo->printSTUBWithArglist("BatQT::m_setMovieBox", nargs);
+ g_lingo->dropStack(nargs);
+ g_lingo->push(Datum(0));
+}
+
+void BatQT::m_setTimeRange(int nargs) {
+ g_lingo->printSTUBWithArglist("BatQT::m_setTimeRange", nargs);
+ g_lingo->dropStack(nargs);
+ g_lingo->push(Datum(0));
+}
+
+void BatQT::m_addCallback(int nargs) {
+ g_lingo->printSTUBWithArglist("BatQT::m_addCallback", nargs);
+ g_lingo->dropStack(nargs);
+ g_lingo->push(Datum(0));
+}
+
+void BatQT::m_removeCallback(int nargs) {
+ g_lingo->printSTUBWithArglist("BatQT::m_removeCallback", nargs);
+ g_lingo->dropStack(nargs);
+ g_lingo->push(Datum(0));
+}
+
+void BatQT::m_resetCallbacks(int nargs) {
+ g_lingo->printSTUBWithArglist("BatQT::m_resetCallbacks", nargs);
+ g_lingo->dropStack(nargs);
+ g_lingo->push(Datum(0));
+}
+
+void BatQT::m_setBatch(int nargs) {
+ g_lingo->printSTUBWithArglist("BatQT::m_setBatch", nargs);
+ g_lingo->dropStack(nargs);
+}
+
+
} // End of namespace Director
diff --git a/engines/director/lingo/xlibs/batqt.h b/engines/director/lingo/xlibs/batqt.h
index fb75f5b4573..f6faa25b7a1 100644
--- a/engines/director/lingo/xlibs/batqt.h
+++ b/engines/director/lingo/xlibs/batqt.h
@@ -38,6 +38,26 @@ void open(int type);
void close(int type);
void m_new(int nargs);
+void m_dispose(int nargs);
+void m_name(int nargs);
+void m_status(int nargs);
+void m_error(int nargs);
+void m_lastError(int nargs);
+void m_open(int nargs);
+void m_play(int nargs);
+void m_stop(int nargs);
+void m_getTimeRange(int nargs);
+void m_getMovieBox(int nargs);
+void m_getTime(int nargs);
+void m_setTime(int nargs);
+void m_setVolume(int nargs);
+void m_length(int nargs);
+void m_setMovieBox(int nargs);
+void m_setTimeRange(int nargs);
+void m_addCallback(int nargs);
+void m_removeCallback(int nargs);
+void m_resetCallbacks(int nargs);
+void m_setBatch(int nargs);
} // End of namespace BatQT
Commit: 9ff4641c68026f16ad16a6725528b0fa05fe8d8c
https://github.com/scummvm/scummvm/commit/9ff4641c68026f16ad16a6725528b0fa05fe8d8c
Author: Scott Percival (code at moral.net.au)
Date: 2023-04-29T14:20:05+02:00
Commit Message:
DIRECTOR: LINGO: Add RECT support to getObjectProp
Fixes starting a new game in Operation: Eco-Nightmare
Changed paths:
engines/director/lingo/lingo-the.cpp
diff --git a/engines/director/lingo/lingo-the.cpp b/engines/director/lingo/lingo-the.cpp
index 3c05ed2a730..cd249334012 100644
--- a/engines/director/lingo/lingo-the.cpp
+++ b/engines/director/lingo/lingo-the.cpp
@@ -1889,6 +1889,21 @@ void Lingo::getObjectProp(Datum &obj, Common::String &propName) {
g_lingo->push(d);
return;
}
+ if (obj.type == RECT) {
+ if (propName.equalsIgnoreCase("left")) {
+ d = obj.u.farr->arr[0];
+ } else if (propName.equalsIgnoreCase("top")) {
+ d = obj.u.farr->arr[1];
+ } else if (propName.equalsIgnoreCase("right")) {
+ d = obj.u.farr->arr[2];
+ } else if (propName.equalsIgnoreCase("bottom")) {
+ d = obj.u.farr->arr[3];
+ } else {
+ g_lingo->lingoError("Lingo::getObjectProp: Rect <%s> has no property '%s'", obj.asString(true).c_str(), propName.c_str());
+ }
+ g_lingo->push(d);
+ return;
+ }
if (obj.type == CASTREF) {
Movie *movie = _vm->getCurrentMovie();
if (!movie) {
Commit: f4cbadaca27e42bde2a7905cd9e8e5eea255e233
https://github.com/scummvm/scummvm/commit/f4cbadaca27e42bde2a7905cd9e8e5eea255e233
Author: Scott Percival (code at moral.net.au)
Date: 2023-04-29T14:20:05+02:00
Commit Message:
DIRECTOR: XOBJ: FindSys must return a path with a slash
Changed paths:
engines/director/lingo/xlibs/findsys.cpp
diff --git a/engines/director/lingo/xlibs/findsys.cpp b/engines/director/lingo/xlibs/findsys.cpp
index 3021e6e47a7..7fd46316687 100644
--- a/engines/director/lingo/xlibs/findsys.cpp
+++ b/engines/director/lingo/xlibs/findsys.cpp
@@ -84,7 +84,7 @@ void FindSys::m_new(int nargs) {
}
void FindSys::m_do(int nargs) {
- g_lingo->push(Common::String("C:\\WINDOWS"));
+ g_lingo->push(Common::String("C:\\WINDOWS\\"));
}
} // End of namespace Director
Commit: a407f165a6c227db7e11128eae66d5b2b4fedd81
https://github.com/scummvm/scummvm/commit/a407f165a6c227db7e11128eae66d5b2b4fedd81
Author: Scott Percival (code at moral.net.au)
Date: 2023-04-29T14:20:05+02:00
Commit Message:
DIRECTOR: XOBJ: Remove directory from fileio paths
Jamming an absolute Windows path into a filename is allowed on Linux,
however Windows isn't as forgiving.
It would be nice to preserve the full path of the saved file;
unfortunately the SaveFileManager API doesn't support subdirectories.
For now just keep the old pattern of [gameid]-[filename].txt.
Fixes save games in Operation: Eco-Nightmare
Changed paths:
engines/director/lingo/xlibs/fileio.cpp
diff --git a/engines/director/lingo/xlibs/fileio.cpp b/engines/director/lingo/xlibs/fileio.cpp
index bcb89c98916..1462469c9f2 100644
--- a/engines/director/lingo/xlibs/fileio.cpp
+++ b/engines/director/lingo/xlibs/fileio.cpp
@@ -232,11 +232,11 @@ void FileIO::m_new(int nargs) {
}
// Enforce target to the created files so they do not mix up
- Common::String filename = lastPathComponent(path, dirSeparator);
- Common::String dir = firstPathComponents(path, dirSeparator);
+ Common::String filenameOrig = lastPathComponent(path, dirSeparator);
+ Common::String filename = filenameOrig;
if (!filename.hasPrefixIgnoreCase(prefix))
- filename = dir + prefix + filename;
+ filename = prefix + filenameOrig;
if (option.equalsIgnoreCase("read")) {
me->_inStream = saves->openForLoading(filename);
Commit: 4e1b32c0d4f2443f0f4d8d823296d2c587cb01fc
https://github.com/scummvm/scummvm/commit/4e1b32c0d4f2443f0f4d8d823296d2c587cb01fc
Author: Scott Percival (code at moral.net.au)
Date: 2023-04-29T14:20:05+02:00
Commit Message:
DIRECTOR: Add filesystem quirk for teamxtreme2
Changed paths:
engines/director/game-quirks.cpp
diff --git a/engines/director/game-quirks.cpp b/engines/director/game-quirks.cpp
index b2d8ec5d6cc..3e75f4da65c 100644
--- a/engines/director/game-quirks.cpp
+++ b/engines/director/game-quirks.cpp
@@ -73,6 +73,13 @@ struct CachedFile {
"WOLFGANG.dat", // It needs an empty file
(const byte *)"", 0
},
+ { "teamxtreme2", Common::kPlatformWindows,
+ // In Operation: Eco-Nightmare, the game will try and check if the
+ // save file exists with getNthFileNameInFolder before attempting to
+ // read it with FileIO (which uses the save data store).
+ "WINDOWS/TX2SAVES",
+ (const byte *)"", 0
+ },
{ nullptr, Common::kPlatformUnknown, nullptr, nullptr, 0 }
};
Commit: 8ff91ce024c550c177706dd46b6799dc2fb25c30
https://github.com/scummvm/scummvm/commit/8ff91ce024c550c177706dd46b6799dc2fb25c30
Author: Scott Percival (code at moral.net.au)
Date: 2023-04-29T14:20:05+02:00
Commit Message:
DIRECTOR: LINGO: Make b_getNthFileNameInFolder always check cache
It's possible for games to request files that don't exist in the game
path, e.g. C:\WINDOWS\TX2SAVES. As such, the method needs to always
include the quirk files, regardless of whether the path exists.
Fixes loading save games in Operation: Eco-Nightmare
Changed paths:
engines/director/lingo/lingo-builtins.cpp
diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index 79cdd393542..64dd09278fd 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -1154,36 +1154,37 @@ void LB::b_getNthFileNameInFolder(int nargs) {
}
Datum r;
+ Common::Array<Common::String> fileNameList;
+
+ // First, mix in any files injected from the quirks
+ Common::Archive *cache = SearchMan.getArchive(kQuirksCacheArchive);
+ if (cache) {
+ Common::ArchiveMemberList files;
+
+ cache->listMatchingMembers(files, path + (path.empty() ? "*" : "/*"), true);
+
+ for (auto &fi : files) {
+ fileNameList.push_back(Common::lastPathComponent(fi->getName(), '/'));
+ }
+ }
+
+ // Next, mix in files from the game filesystem (if they exist)
if (d.exists()) {
Common::FSList f;
if (!d.getChildren(f, Common::FSNode::kListAll)) {
warning("Cannot access directory %s", path.c_str());
} else {
- if ((uint)fileNum < f.size()) {
- // here, we sort all the fileNames
- Common::Array<Common::String> fileNameList;
- for (uint i = 0; i < f.size(); i++)
- fileNameList.push_back(f[i].getName());
-
- // Now mix in any files coming from the quirks
- Common::Archive *cache = SearchMan.getArchive(kQuirksCacheArchive);
-
- if (cache) {
- Common::ArchiveMemberList files;
-
- cache->listMatchingMembers(files, path + (path.empty() ? "*" : "/*"), true);
-
- for (auto &fi : files) {
- fileNameList.push_back(fi->getName().c_str());
- }
- }
-
- Common::sort(fileNameList.begin(), fileNameList.end());
- r = Datum(fileNameList[fileNum]);
- }
+ for (uint i = 0; i < f.size(); i++)
+ fileNameList.push_back(f[i].getName());
}
}
+ if (!fileNameList.empty() && (uint)fileNum < fileNameList.size()) {
+ // Sort files alphabetically
+ Common::sort(fileNameList.begin(), fileNameList.end());
+ r = Datum(fileNameList[fileNum]);
+ }
+
g_lingo->push(r);
}
Commit: ef3d3605527654c7ed258b1ccefa9eaf04cba263
https://github.com/scummvm/scummvm/commit/ef3d3605527654c7ed258b1ccefa9eaf04cba263
Author: Scott Percival (code at moral.net.au)
Date: 2023-04-29T14:20:05+02:00
Commit Message:
DIRECTOR: Unify arguments to Cast::loadXData functions
Changed paths:
engines/director/cast.cpp
engines/director/cast.h
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index ec0eaae4a78..0c919af0d5a 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -632,27 +632,27 @@ void Cast::loadStxtData(int key, TextCastMember *member) {
}
}
-void Cast::loadPaletteData(PaletteCastMember *member) {
+void Cast::loadPaletteData(int key, PaletteCastMember *member) {
// TODO: Verify how palettes work in >D4 versions
int paletteId = 0;
if (_version >= kFileVer400 && _version < kFileVer500 && member->_children.size() == 1) {
paletteId = member->_children[0].index;
} else if (_version < kFileVer400) {
// For D3 and below, palette IDs are stored in the CLUT resource as cast ID + 1024
- paletteId = member->getID() + _castIDoffset;
+ paletteId = key + _castIDoffset;
} else {
warning("Cast::loadPaletteData(): Expected 1 child for palette cast, got %d", member->_children.size());
}
if (paletteId) {
- debugC(2, kDebugImages, "Cast::loadPaletteData(): linking palette id %d to cast member %d", paletteId, member->getID());
+ debugC(2, kDebugImages, "Cast::loadPaletteData(): linking palette id %d to cast index %d", paletteId, key);
member->_palette = g_director->getPalette(paletteId);
}
}
-void Cast::loadFilmLoopData(FilmLoopCastMember *member) {
+void Cast::loadFilmLoopData(int key, FilmLoopCastMember *member) {
if (_version < kFileVer400) {
// Director 3 and below should have a SCVW resource
- uint16 filmLoopId = member->getID() + _castIDoffset;
+ uint16 filmLoopId = key + _castIDoffset;
uint32 tag = MKTAG('S', 'C', 'V', 'W');
Common::SeekableReadStreamEndian *loop = _castArchive->getResource(tag, filmLoopId);
debugC(2, kDebugLoading, "****** Loading '%s' id: %d, %d bytes", tag2str(tag), filmLoopId, (int)loop->size());
@@ -868,10 +868,10 @@ void Cast::loadCastMemberData() {
switch (c->_value->_type){
case kCastPalette:
- loadPaletteData((PaletteCastMember *)c->_value);
+ loadPaletteData(c->_key, (PaletteCastMember *)c->_value);
break;
case kCastFilmLoop:
- loadFilmLoopData((FilmLoopCastMember *)c->_value);
+ loadFilmLoopData(c->_key, (FilmLoopCastMember *)c->_value);
break;
case kCastBitmap:
loadBitmapData(c->_key, (BitmapCastMember *)c->_value);
diff --git a/engines/director/cast.h b/engines/director/cast.h
index b94c86982f1..7de16e2b8fb 100644
--- a/engines/director/cast.h
+++ b/engines/director/cast.h
@@ -97,8 +97,8 @@ public:
void loadCastMemberData();
void loadStxtData(int key, TextCastMember *member);
- void loadPaletteData(PaletteCastMember *member);
- void loadFilmLoopData(FilmLoopCastMember *member);
+ void loadPaletteData(int key, PaletteCastMember *member);
+ void loadFilmLoopData(int key, FilmLoopCastMember *member);
void loadBitmapData(int key, BitmapCastMember *bitmapCast);
void loadSoundData(int key, SoundCastMember *soundCast);
Commit: f703ee6ecf52b69f56da42f0f7b13d536d78bde8
https://github.com/scummvm/scummvm/commit/f703ee6ecf52b69f56da42f0f7b13d536d78bde8
Author: Scott Percival (code at moral.net.au)
Date: 2023-04-29T14:20:05+02:00
Commit Message:
DIRECTOR: Split CastMember classes into files
Changed paths:
A engines/director/castmember/bitmap.cpp
A engines/director/castmember/bitmap.h
A engines/director/castmember/castmember.cpp
A engines/director/castmember/castmember.h
A engines/director/castmember/digitalvideo.cpp
A engines/director/castmember/digitalvideo.h
A engines/director/castmember/filmloop.cpp
A engines/director/castmember/filmloop.h
A engines/director/castmember/movie.cpp
A engines/director/castmember/movie.h
A engines/director/castmember/palette.cpp
A engines/director/castmember/palette.h
A engines/director/castmember/script.cpp
A engines/director/castmember/script.h
A engines/director/castmember/shape.cpp
A engines/director/castmember/shape.h
A engines/director/castmember/sound.cpp
A engines/director/castmember/sound.h
A engines/director/castmember/text.cpp
A engines/director/castmember/text.h
R engines/director/castmember.cpp
R engines/director/castmember.h
engines/director/cast.cpp
engines/director/channel.cpp
engines/director/cursor.cpp
engines/director/debugger.cpp
engines/director/events.cpp
engines/director/graphics.cpp
engines/director/lingo/lingo-builtins.cpp
engines/director/lingo/lingo-bytecode.cpp
engines/director/lingo/lingo-code.cpp
engines/director/lingo/lingo-funcs.cpp
engines/director/lingo/lingo-object.cpp
engines/director/lingo/lingo-the.cpp
engines/director/lingo/lingo.cpp
engines/director/module.mk
engines/director/resource.cpp
engines/director/score.cpp
engines/director/score.h
engines/director/sound.cpp
engines/director/sprite.cpp
engines/director/window.cpp
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index 0c919af0d5a..301b26061fc 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -33,13 +33,22 @@
#include "director/director.h"
#include "director/cast.h"
-#include "director/castmember.h"
#include "director/images.h"
#include "director/movie.h"
#include "director/score.h"
#include "director/sound.h"
#include "director/stxt.h"
#include "director/util.h"
+#include "director/castmember/castmember.h"
+#include "director/castmember/bitmap.h"
+#include "director/castmember/digitalvideo.h"
+#include "director/castmember/filmloop.h"
+#include "director/castmember/movie.h"
+#include "director/castmember/palette.h"
+#include "director/castmember/script.h"
+#include "director/castmember/shape.h"
+#include "director/castmember/sound.h"
+#include "director/castmember/text.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
diff --git a/engines/director/castmember.cpp b/engines/director/castmember.cpp
deleted file mode 100644
index cc0fd428dbf..00000000000
--- a/engines/director/castmember.cpp
+++ /dev/null
@@ -1,1791 +0,0 @@
-/* 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 "graphics/macgui/macbutton.h"
-#include "graphics/surface.h"
-#include "image/image_decoder.h"
-#include "video/avi_decoder.h"
-#include "video/qt_decoder.h"
-
-#include "director/director.h"
-#include "director/cast.h"
-#include "director/castmember.h"
-#include "director/cursor.h"
-#include "director/channel.h"
-#include "director/movie.h"
-#include "director/picture.h"
-#include "director/score.h"
-#include "director/sprite.h"
-#include "director/sound.h"
-#include "director/window.h"
-#include "director/stxt.h"
-#include "director/sprite.h"
-
-namespace Director {
-
-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;
-
- _modified = true;
- _isChanged = false;
-
- _objType = kCastMemberObj;
-
- _widget = nullptr;
- _erase = false;
-}
-
-CastMember::CastMember(Cast *cast, uint16 castId) : Object<CastMember>("CastMember") {
- _type = kCastTypeNull;
- _cast = cast;
- _castId = castId;
- _hilite = false;
- _purgePriority = 3;
- _size = 0;
- _flags1 = 0;
-
- _modified = true;
- _isChanged = false;
-
- _objType = kCastMemberObj;
-
- _widget = nullptr;
- _erase = false;
-}
-
-CastMemberInfo *CastMember::getInfo() {
- return _cast->getCastMemberInfo(_castId);
-}
-
-void CastMember::setModified(bool modified) {
- _modified = modified;
- if (modified)
- _isChanged = true;
-}
-
-
-/////////////////////////////////////
-// Bitmap
-/////////////////////////////////////
-
-BitmapCastMember::BitmapCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint32 castTag, uint16 version, uint8 flags1)
- : CastMember(cast, castId, stream) {
- _type = kCastBitmap;
- _picture = nullptr;
- _ditheredImg = nullptr;
- _matte = nullptr;
- _noMatte = false;
- _bytes = 0;
- _pitch = 0;
- _flags2 = 0;
- _regX = _regY = 0;
- _clut = 0;
- _ditheredTargetClut = 0;
- _bitsPerPixel = 0;
- _external = false;
-
- if (version < kFileVer400) {
- _flags1 = flags1; // region: 0 - auto, 1 - matte, 2 - disabled
-
- _bytes = stream.readUint16();
- _initialRect = Movie::readRect(stream);
- _boundingRect = Movie::readRect(stream);
- _regY = stream.readUint16();
- _regX = stream.readUint16();
-
- if (_bytes & 0x8000) {
- _bitsPerPixel = stream.readUint16();
- _clut = stream.readSint16();
- if (_clut <= 0) // builtin palette
- _clut -= 1;
- } else {
- _bitsPerPixel = 1;
- _clut = kClutSystemMac;
- }
-
- _pitch = _initialRect.width();
- if (_pitch % 16)
- _pitch += 16 - (_initialRect.width() % 16);
-
- _pitch *= _bitsPerPixel;
- _pitch >>= 3;
-
- } else if (version >= kFileVer400 && version < kFileVer600) {
- _flags1 = flags1;
- _pitch = stream.readUint16();
- _pitch &= 0x0fff;
-
- _initialRect = Movie::readRect(stream);
- _boundingRect = Movie::readRect(stream);
- _regY = stream.readUint16();
- _regX = stream.readUint16();
-
- _bitsPerPixel = stream.readUint16();
-
- if (stream.eos()) {
- _bitsPerPixel = 0;
- } else {
- if (version >= kFileVer500) {
- stream.readSint16(); // is this the castlib? was ff ff
- }
- _clut = stream.readSint16();
- if (_clut <= 0) // builtin palette
- _clut -= 1;
- stream.readUint16();
- /* uint16 unk1 = */ stream.readUint16();
- stream.readUint16();
-
- stream.readUint32();
- stream.readUint32();
-
- _flags2 = stream.readUint16();
- }
-
- if (_bitsPerPixel == 0)
- _bitsPerPixel = 1;
-
- int tail = 0;
- byte buf[256];
-
- while (!stream.eos()) {
- byte c = stream.readByte();
- if (tail < 256)
- buf[tail] = c;
- tail++;
- }
-
- if (tail)
- warning("BUILDBOT: BitmapCastMember: %d bytes left", tail);
-
- if (tail && debugChannelSet(2, kDebugLoading)) {
- debug("BitmapCastMember: tail");
- Common::hexdump(buf, tail);
- }
- }
-
- _tag = castTag;
-}
-
-BitmapCastMember::BitmapCastMember(Cast *cast, uint16 castId, Image::ImageDecoder *img, uint8 flags1)
- : CastMember(cast, castId) {
- _type = kCastBitmap;
- _matte = nullptr;
- _noMatte = false;
- _bytes = 0;
- if (img != nullptr) {
- _picture = new Picture(*img);
- }
- _ditheredImg = nullptr;
- _clut = -1;
- _ditheredTargetClut = 0;
- _initialRect = Common::Rect(0, 0, img->getSurface()->w, img->getSurface()->h);
- _pitch = img->getSurface()->pitch;
- _bitsPerPixel = img->getSurface()->format.bytesPerPixel * 8;
- _regY = img->getSurface()->h / 2;
- _regX = img->getSurface()->w / 2;
- _flags1 = flags1;
- _flags2 = 0;
- _tag = 0;
- _external = false;
-}
-
-BitmapCastMember::~BitmapCastMember() {
- delete _picture;
-
- if (_ditheredImg) {
- _ditheredImg->free();
- delete _ditheredImg;
- }
-
- if (_matte)
- delete _matte;
-}
-
-Graphics::MacWidget *BitmapCastMember::createWidget(Common::Rect &bbox, Channel *channel, SpriteType spriteType) {
- if (!_picture) {
- warning("BitmapCastMember::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;
- int srcBpp = _picture->_surface.format.bytesPerPixel;
-
- const byte *pal = _picture->_palette;
- bool previouslyDithered = _ditheredImg != nullptr;
- if (_ditheredImg) {
- _ditheredImg->free();
- delete _ditheredImg;
- _ditheredImg = nullptr;
- _ditheredTargetClut = 0;
- }
-
- if (dstBpp == 1) {
- if (srcBpp > 1
- // At least early directors were not remapping 8bpp images. But in case it is
- // needed, here is the code
-#if 0
- || (srcBpp == 1 &&
- memcmp(g_director->_wm->getPalette(), _img->_palette, _img->_paletteSize))
-#endif
- ) {
-
- _ditheredImg = _picture->_surface.convertTo(g_director->_wm->_pixelformat, _picture->_palette, _picture->_paletteColors, g_director->_wm->getPalette(), g_director->_wm->getPaletteSize());
-
- pal = g_director->_wm->getPalette();
- } else {
- // Convert indexed image to indexed palette
- Movie *movie = g_director->getCurrentMovie();
- Cast *cast = movie->getCast();
- Score *score = movie->getScore();
- // Get the current score palette. Note that this is the ID of the palette in the list, not the cast member!
- int currentPaletteId = score->resolvePaletteId(score->getCurrentPalette());
- if (!currentPaletteId)
- currentPaletteId = cast->_defaultPalette;
- PaletteV4 *currentPalette = g_director->getPalette(currentPaletteId);
- if (!currentPalette) {
- currentPaletteId = kClutSystemMac;
- currentPalette = g_director->getPalette(currentPaletteId);
- }
- int castPaletteId = score->resolvePaletteId(_clut);
- // It is possible for Director to have saved an invalid ID in _clut;
- // if this is the case, do no dithering.
- if (!castPaletteId)
- castPaletteId = currentPaletteId;
-
- // Check if the palette is in the middle of a color fade event
- bool isColorCycling = score->isPaletteColorCycling();
-
- // First, check if the palettes are different
- switch (_bitsPerPixel) {
- // 1bpp - this is preconverted to 0x00 and 0xff, change nothing.
- case 1:
- break;
- // 2bpp - convert to nearest using the standard 2-bit palette.
- case 2:
- {
- const PaletteV4 &srcPal = g_director->getLoaded4Palette();
- _ditheredImg = _picture->_surface.convertTo(g_director->_wm->_pixelformat, srcPal.palette, srcPal.length, currentPalette->palette, currentPalette->length, Graphics::kDitherNaive);
- }
- break;
- // 4bpp - if using a builtin palette, use one of the corresponding 4-bit ones.
- case 4:
- {
- const auto pals = g_director->getLoaded16Palettes();
- // in D4 you aren't allowed to use custom palettes for 4-bit images, so uh...
- // I guess default to the mac palette?
- int palIndex = pals.contains(castPaletteId) ? castPaletteId : kClutSystemMac;
- const PaletteV4 &srcPal = pals.getVal(palIndex);
- _ditheredImg = _picture->_surface.convertTo(g_director->_wm->_pixelformat, srcPal.palette, srcPal.length, currentPalette->palette, currentPalette->length, Graphics::kDitherNaive);
- }
- break;
- // 8bpp - if using a different palette, and we're not doing a color cycling operation, convert using nearest colour matching
- case 8:
- // Only redither 8-bit images if we have the flag set, or it is external
- if (!movie->_remapPalettesWhenNeeded && !_external)
- break;
- if (_external || (castPaletteId != currentPaletteId && !isColorCycling)) {
- const auto pals = g_director->getLoadedPalettes();
- int palIndex = pals.contains(castPaletteId) ? castPaletteId : kClutSystemMac;
- const PaletteV4 &srcPal = pals.getVal(palIndex);
-
- // If it is an external image, use the included palette.
- // For BMP images especially, they'll often have the right colors
- // but in the wrong palette order.
- const byte *palPtr = _external ? pal : srcPal.palette;
- int palLength = _external ? _picture->getPaletteSize() : srcPal.length;
- _ditheredImg = _picture->_surface.convertTo(g_director->_wm->_pixelformat, palPtr, palLength, currentPalette->palette, currentPalette->length, Graphics::kDitherNaive);
- }
- break;
- default:
- break;
- }
-
- if (_ditheredImg) {
- debugC(4, kDebugImages, "BitmapCastMember::createWidget(): Dithering image from source palette %d to target palette %d", _clut, score->getCurrentPalette());
- // Save the palette ID so we can check if a redraw is required
- _ditheredTargetClut = currentPaletteId;
-
- if (!_external) {
- // Finally, the first and last colours in the palette are special. No matter what the palette remap
- // does, we need to scrub those to be the same.
- const Graphics::Surface *src = &_picture->_surface;
- for (int y = 0; y < src->h; y++) {
- for (int x = 0; x < src->w; x++) {
- const int test = *(const byte *)src->getBasePtr(x, y);
- if (test == 0 || test == (1 << _bitsPerPixel) - 1) {
- *(byte *)_ditheredImg->getBasePtr(x, y) = test == 0 ? 0x00 : 0xff;
- }
- }
- }
- }
- } else if (previouslyDithered) {
- debugC(4, kDebugImages, "BitmapCastMember::createWidget(): Removed dithered image, score palette %d matches cast member", score->getCurrentPalette());
- }
-
- }
- }
-
- Graphics::MacWidget *widget = new Graphics::MacWidget(g_director->getCurrentWindow(), bbox.left, bbox.top, bbox.width(), bbox.height(), g_director->_wm, false);
-
- // scale for drawing a different size sprite
- copyStretchImg(widget->getSurface()->surfacePtr(), bbox, pal);
-
- return widget;
-}
-
-void BitmapCastMember::copyStretchImg(Graphics::Surface *surface, const Common::Rect &bbox, const byte *pal) {
- const Graphics::Surface *srcSurf;
-
- if (_ditheredImg)
- srcSurf = _ditheredImg;
- else
- srcSurf = &_picture->_surface;
-
- if (bbox.width() != _initialRect.width() || bbox.height() != _initialRect.height()) {
-
- int scaleX = SCALE_THRESHOLD * _initialRect.width() / bbox.width();
- int scaleY = SCALE_THRESHOLD * _initialRect.height() / bbox.height();
-
- for (int y = 0, scaleYCtr = 0; y < bbox.height(); y++, scaleYCtr += scaleY) {
- if (g_director->_wm->_pixelformat.bytesPerPixel == 1) {
- for (int x = 0, scaleXCtr = 0; x < bbox.width(); x++, scaleXCtr += scaleX) {
- const byte *src = (const byte *)srcSurf->getBasePtr(scaleXCtr / SCALE_THRESHOLD, scaleYCtr / SCALE_THRESHOLD);
- *(byte *)surface->getBasePtr(x, y) = *src;
- }
- } else {
- for (int x = 0, scaleXCtr = 0; x < bbox.width(); x++, scaleXCtr += scaleX) {
- const void *ptr = srcSurf->getBasePtr(scaleXCtr / SCALE_THRESHOLD, scaleYCtr / SCALE_THRESHOLD);
- int32 color;
-
- switch (srcSurf->format.bytesPerPixel) {
- case 1:
- {
- color = *(const byte *)ptr * 3;
- color = surface->format.RGBToColor(pal[color], pal[color + 1], pal[color + 2]);
- }
- break;
- case 4:
- color = *(const int32 *)ptr;
- break;
- default:
- error("Unimplemented src bpp: %d", srcSurf->format.bytesPerPixel);
- }
-
- *(int32 *)surface->getBasePtr(x, y) = color;
- }
- }
- }
- } else {
- surface->copyFrom(*srcSurf);
- }
-
- if (g_director->_debugDraw & kDebugDrawCast) {
- surface->frameRect(Common::Rect(0, 0, surface->w, surface->h), g_director->_wm->_colorWhite);
-
- const Graphics::Font *font = FontMan.getFontByUsage(Graphics::FontManager::kConsoleFont);
- font->drawString(surface, Common::String::format("%d", _castId), 2, 2, 10, g_director->_wm->_colorWhite);
- }
-}
-
-bool BitmapCastMember::isModified() {
- if (CastMember::isModified()) {
- // Let's us use "setChanged" when changing the picture through Lingo
- return true;
- }
- // Check for palette changes.
- // If a bitmap has a custom palette assigned to it, createWidget()
- // will dither the image so that it fits within the current palette.
- // When the score palette changes, we need to flag that the widget needs
- // to be recreated.
- if (_clut) {
- Movie *movie = g_director->getCurrentMovie();
- Cast *cast = movie->getCast();
- Score *score = movie->getScore();
- int currentPaletteId = score->resolvePaletteId(score->getCurrentPalette());
- if (!currentPaletteId)
- currentPaletteId = cast->_defaultPalette;
- PaletteV4 *currentPalette = g_director->getPalette(currentPaletteId);
- if (!currentPalette) {
- currentPaletteId = kClutSystemMac;
- currentPalette = g_director->getPalette(currentPaletteId);
- }
- int castPaletteId = score->resolvePaletteId(_clut);
- if (!castPaletteId)
- castPaletteId = cast->_defaultPalette;
-
- if (currentPaletteId == castPaletteId) {
- return _ditheredTargetClut != 0;
- } else {
- return _ditheredTargetClut != currentPaletteId;
- }
- }
- return false;
-}
-
-void BitmapCastMember::createMatte(Common::Rect &bbox) {
- // Like background trans, but all white pixels NOT ENCLOSED by coloured pixels
- // are transparent
- Graphics::Surface tmp;
- tmp.create(bbox.width(), bbox.height(), g_director->_pixelformat);
-
- copyStretchImg(&tmp, bbox);
-
- _noMatte = true;
-
- // Searching white color in the corners
- uint32 whiteColor = 0;
- bool colorFound = false;
-
- if (g_director->_pixelformat.bytesPerPixel == 1) {
- for (int y = 0; y < tmp.h; y++) {
- for (int x = 0; x < tmp.w; x++) {
- byte color = *(byte *)tmp.getBasePtr(x, y);
-
- if (g_director->getPalette()[color * 3 + 0] == 0xff &&
- g_director->getPalette()[color * 3 + 1] == 0xff &&
- g_director->getPalette()[color * 3 + 2] == 0xff) {
- whiteColor = color;
- colorFound = true;
- break;
- }
- }
- }
- } else {
- whiteColor = g_director->_wm->_colorWhite;
- colorFound = true;
- }
-
- if (!colorFound) {
- debugC(1, kDebugImages, "BitmapCastMember::createMatte(): No white color for matte image");
- } else {
- delete _matte;
-
- _matte = new Graphics::FloodFill(&tmp, whiteColor, 0, true);
-
- for (int yy = 0; yy < tmp.h; yy++) {
- _matte->addSeed(0, yy);
- _matte->addSeed(tmp.w - 1, yy);
- }
-
- for (int xx = 0; xx < tmp.w; xx++) {
- _matte->addSeed(xx, 0);
- _matte->addSeed(xx, tmp.h - 1);
- }
-
- _matte->fillMask();
- _noMatte = false;
- }
-
- tmp.free();
-}
-
-Graphics::Surface *BitmapCastMember::getMatte(Common::Rect &bbox) {
- // Lazy loading of mattes
- if (!_matte && !_noMatte) {
- createMatte(bbox);
- }
-
- // check for the scale matte
- Graphics::Surface *surface = _matte ? _matte->getMask() : nullptr;
- if (surface && (surface->w != bbox.width() || surface->h != bbox.height())) {
- createMatte(bbox);
- }
-
- return _matte ? _matte->getMask() : nullptr;
-}
-
-Common::String BitmapCastMember::formatInfo() {
- return Common::String::format(
- "initialRect: %dx%d@%d,%d, boundingRect: %dx%d@%d,%d, foreColor: %d, backColor: %d, regX: %d, regY: %d, pitch: %d, bitsPerPixel: %d, palette: %d",
- _initialRect.width(), _initialRect.height(),
- _initialRect.left, _initialRect.top,
- _boundingRect.width(), _boundingRect.height(),
- _boundingRect.left, _boundingRect.top,
- getForeColor(), getBackColor(),
- _regX, _regY, _pitch, _bitsPerPixel, _clut
- );
-}
-
-PictureReference *BitmapCastMember::getPicture() const {
- auto picture = new PictureReference;
-
- // Not sure if we can make the assumption that the owning
- // BitmapCastMember will live as long as any reference,
- // so we'll make a copy of the Picture.
- picture->_picture = new Picture(*_picture);
-
- return picture;
-}
-
-void BitmapCastMember::setPicture(PictureReference &picture) {
- delete _picture;
- _picture = new Picture(*picture._picture);
-
- // Force redither
- delete _ditheredImg;
- _ditheredImg = nullptr;
-
- // Make sure we get redrawn
- setModified(true);
- // TODO: Should size be adjusted?
-}
-
-void BitmapCastMember::setPicture(Image::ImageDecoder &image, bool adjustSize) {
- delete _picture;
- _picture = new Picture(image);
- if (adjustSize) {
- auto surf = image.getSurface();
- _size = surf->pitch * surf->h + _picture->getPaletteSize();
- }
- // Make sure we get redrawn
- setModified(true);
-}
-
-/////////////////////////////////////
-// DigitalVideo
-/////////////////////////////////////
-
-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;
- 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;
-
- 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() {
- if (_lastFrame) {
- _lastFrame->free();
- delete _lastFrame;
- }
-
- if (_video)
- delete _video;
-}
-
-bool DigitalVideoCastMember::loadVideo(Common::String path) {
- // TODO: detect file type (AVI, QuickTime, FLIC) based on magic number,
- // insert the right video decoder
-
- if (_video)
- delete _video;
-
- _filename = path;
- _video = new Video::QuickTimeDecoder();
-
- Common::String path1 = pathMakeRelative(path);
-
- debugC(2, kDebugLoading | kDebugImages, "Loading video %s -> %s", path.c_str(), path1.c_str());
- bool result = _video->loadFile(Common::Path(path1, g_director->_dirSeparator));
- if (!result) {
- delete _video;
- _video = new Video::AVIDecoder();
- result = _video->loadFile(Common::Path(path1, g_director->_dirSeparator));
- if (!result) {
- warning("DigitalVideoCastMember::loadVideo(): format not supported, skipping");
- delete _video;
- _video = nullptr;
- }
- }
-
- 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.
- byte palette[256 * 3];
- g_system->getPaletteManager()->grabPalette(palette, 0, 256);
- _video->setDitheringPalette(palette);
- }
-
- return result;
-}
-
-bool DigitalVideoCastMember::isModified() {
- if (!_video || !_video->isVideoLoaded())
- return true;
-
- if (_getFirstFrame)
- return true;
-
- if (_channel->_movieRate == 0.0)
- return false;
-
- return _video->needsUpdate();
-}
-
-void DigitalVideoCastMember::startVideo(Channel *channel) {
- _channel = channel;
-
- if (!_video || !_video->isVideoLoaded()) {
- warning("DigitalVideoCastMember::startVideo: No video %s", !_video ? "decoder" : "loaded");
- return;
- }
-
- if (_pausedAtStart) {
- _getFirstFrame = true;
- } else {
- if (_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->_stopTime == 0)
- _channel->_stopTime = getMovieTotalTime();
-
- _duration = getMovieTotalTime();
-}
-
-void DigitalVideoCastMember::stopVideo() {
- if (!_video || !_video->isVideoLoaded()) {
- 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()) {
- 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) {
- Graphics::MacWidget *widget = new Graphics::MacWidget(g_director->getCurrentWindow(), bbox.left, bbox.top, bbox.width(), bbox.height(), g_director->_wm, false);
-
- _channel = channel;
-
- if (!_video || !_video->isVideoLoaded()) {
- warning("DigitalVideoCastMember::createWidget: No video decoder");
- delete widget;
-
- return nullptr;
- }
-
- // Do not render stopped videos
- if (_channel->_movieRate == 0.0 && !_getFirstFrame && _lastFrame) {
- widget->getSurface()->blitFrom(*_lastFrame);
-
- return widget;
- }
-
- const Graphics::Surface *frame = _video->decodeNextFrame();
-
- debugC(1, kDebugImages, "Video time: %d rate: %f", _channel->_movieTime, _channel->_movieRate);
-
- if (frame) {
- if (_lastFrame) {
- _lastFrame->free();
- delete _lastFrame;
- }
-
- _lastFrame = frame->convertTo(g_director->_pixelformat, g_director->getPalette());
- }
- if (_lastFrame)
- widget->getSurface()->blitFrom(*_lastFrame);
-
- if (_getFirstFrame) {
- _video->stop();
- _getFirstFrame = false;
- }
-
- if (_video->endOfVideo()) {
- if (_looping) {
- _video->rewind();
- } else {
- _channel->_movieRate = 0.0;
- }
- }
-
- return widget;
-}
-
-uint DigitalVideoCastMember::getDuration() {
- if (!_video || !_video->isVideoLoaded()) {
- Common::String path = getCast()->getVideoPath(_castId);
- if (!path.empty())
- loadVideo(pathMakeRelative(path));
-
- _duration = getMovieTotalTime();
- }
- return _duration;
-}
-
-uint DigitalVideoCastMember::getMovieCurrentTime() {
- if (!_video)
- return 0;
-
- int stamp = MIN<int>(_video->getTime() * 60 / 1000, getMovieTotalTime());
-
- return stamp;
-}
-
-uint DigitalVideoCastMember::getMovieTotalTime() {
- if (!_video)
- return 0;
-
- int stamp = _video->getDuration().msecs() * 60 / 1000;
-
- return stamp;
-}
-
-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()));
-}
-
-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
- _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
- );
-}
-
-
-/////////////////////////////////////
-// MovieCasts
-/////////////////////////////////////
-
-MovieCastMember::MovieCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version)
- : CastMember(cast, castId, stream) {
- _type = kCastMovie;
-
- _initialRect = Movie::readRect(stream);
- _flags = stream.readUint32();
-
- _looping = !(_flags & 0x20);
- _enableScripts = _flags & 0x10;
- _enableSound = _flags & 0x08;
- _crop = !(_flags & 0x02);
- _center = _flags & 0x01;
-
- 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);
-
-}
-
-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
- );
-}
-
-/////////////////////////////////////
-// Film loops
-/////////////////////////////////////
-
-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;
-}
-
-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, Channel *channel) {
- Common::Rect widgetRect(bbox.width() ? bbox.width() : _initialRect.width(), bbox.height() ? bbox.height() : _initialRect.height());
-
- _subchannels.clear();
-
- if (channel->_filmLoopFrame >= _frames.size()) {
- warning("Film loop frame %d requested, only %d available", channel->_filmLoopFrame, _frames.size());
- return &_subchannels;
- }
-
- // get the list of sprite IDs for this frame
- Common::Array<int> spriteIds;
- for (Common::HashMap<int, Director::Sprite>::iterator iter = _frames[channel->_filmLoopFrame].sprites.begin(); iter != _frames[channel->_filmLoopFrame].sprites.end(); ++iter) {
- spriteIds.push_back(iter->_key);
- }
- Common::sort(spriteIds.begin(), spriteIds.end());
-
- // copy the sprites in order to the list
- for (Common::Array<int>::iterator iter = spriteIds.begin(); iter != spriteIds.end(); ++iter) {
- Sprite src = _frames[channel->_filmLoopFrame].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();
-
- Channel chan(&src);
- chan._currentPoint = Common::Point(absX, absY);
- chan._width = width;
- chan._height = height;
-
- _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;
-}
-
-void FilmLoopCastMember::loadFilmLoopData(Common::SeekableReadStreamEndian &stream) {
- _initialRect = Common::Rect();
- _frames.clear();
-
- uint32 size = stream.readUint32BE();
- if (debugChannelSet(5, kDebugLoading)) {
- debugC(5, kDebugLoading, "SCVW body:");
- uint32 pos = stream.pos();
- stream.seek(0);
- stream.hexdump(size);
- stream.seek(pos);
- }
- uint16 channelSize = 16;
- FilmLoopFrame newFrame;
-
- while (stream.pos() < size) {
- uint16 frameSize = stream.readUint16BE() - 2;
- if (debugChannelSet(5, kDebugLoading)) {
- debugC(5, kDebugLoading, "Frame entry:");
- stream.hexdump(frameSize);
- }
-
- while (frameSize > 0) {
- int msgWidth = stream.readByte() * 2;
- int order = stream.readByte() * 2 - 0x20;
- frameSize -= 2;
- debugC(8, kDebugLoading, "Message: msgWidth %d, order %d", msgWidth, order);
- if (debugChannelSet(8, kDebugLoading)) {
- stream.hexdump(msgWidth);
- }
-
- int fieldPosition = order;
- int finishPosition = order + msgWidth;
- while (fieldPosition < finishPosition) {
- int channel = (fieldPosition / channelSize);
- int channelOffset = fieldPosition % channelSize;
-
- Sprite sprite(nullptr);
- sprite._movie = g_director->getCurrentMovie();
- if (newFrame.sprites.contains(channel)) {
- sprite = newFrame.sprites.getVal(channel);
- }
- sprite._spriteType = kCastMemberSprite;
- sprite._puppet = 1;
- sprite._stretch = 1;
-
- switch (channelOffset) {
- case kSpritePositionUnk1:
- stream.readByte();
- fieldPosition++;
- break;
- case kSpritePositionEnabled:
- sprite._enabled = stream.readByte() != 0;
- fieldPosition++;
- break;
- case kSpritePositionUnk2:
- stream.readUint16BE();
- fieldPosition += 2;
- break;
- case kSpritePositionFlags:
- sprite._thickness = stream.readByte();
- sprite._inkData = stream.readByte();
- sprite._ink = static_cast<InkType>(sprite._inkData & 0x3f);
-
- if (sprite._inkData & 0x40)
- sprite._trails = 1;
- else
- sprite._trails = 0;
-
- fieldPosition += 2;
- break;
- case kSpritePositionCastId:
- sprite.setCast(CastMemberID(stream.readUint16(), 0));
- fieldPosition += 2;
- break;
- case kSpritePositionY:
- sprite._startPoint.y = stream.readUint16();
- fieldPosition += 2;
- break;
- case kSpritePositionX:
- sprite._startPoint.x = stream.readUint16();
- fieldPosition += 2;
- break;
- case kSpritePositionWidth:
- sprite._width = stream.readUint16();
- fieldPosition += 2;
- break;
- case kSpritePositionHeight:
- sprite._height = stream.readUint16();
- fieldPosition += 2;
- break;
- default:
- stream.readUint16BE();
- fieldPosition += 2;
- break;
- }
- newFrame.sprites.setVal(channel, sprite);
- }
-
- frameSize -= msgWidth;
- }
-
- for (Common::HashMap<int, Sprite>::iterator s = newFrame.sprites.begin(); s != newFrame.sprites.end(); ++s) {
- debugC(5, kDebugLoading, "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);
-
- Common::Point topLeft = s->_value._startPoint + s->_value.getRegistrationOffset();
- 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, "New bounding box: %d %d %d %d", _initialRect.left, _initialRect.top, _initialRect.width(), _initialRect.height());
-
- }
-
- _frames.push_back(newFrame);
-
- }
- debugC(5, kDebugLoading, "Full bounding box: %d %d %d %d", _initialRect.left, _initialRect.top, _initialRect.width(), _initialRect.height());
-}
-
-void FilmLoopCastMember::loadFilmLoopDataV4(Common::SeekableReadStreamEndian &stream) {
- _initialRect = Common::Rect();
- _frames.clear();
-
- uint32 size = stream.readUint32BE();
- if (debugChannelSet(5, kDebugLoading)) {
- debugC(5, kDebugLoading, "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, "SCVW header:");
- stream.hexdump(framesOffset - 8);
- }
- stream.skip(6);
- uint16 channelSize = stream.readUint16BE(); // should be 20!
- stream.skip(framesOffset - 16);
-
- FilmLoopFrame newFrame;
-
- while (stream.pos() < size) {
- uint16 frameSize = stream.readUint16BE() - 2;
- if (debugChannelSet(5, kDebugLoading)) {
- debugC(5, kDebugLoading, "Frame entry:");
- stream.hexdump(frameSize);
- }
-
- while (frameSize > 0) {
- uint16 msgWidth = stream.readUint16BE();
- uint16 order = stream.readUint16BE();
- frameSize -= 4;
-
- int channel = (order / channelSize) - 1;
- int channelOffset = order % channelSize;
-
- Sprite sprite(nullptr);
- sprite._movie = g_director->getCurrentMovie();
- if (newFrame.sprites.contains(channel)) {
- sprite = newFrame.sprites.getVal(channel);
- }
- debugC(8, kDebugLoading, "Message: msgWidth %d, channel %d, channelOffset %d", msgWidth, channel, channelOffset);
- if (debugChannelSet(8, kDebugLoading)) {
- stream.hexdump(msgWidth);
- }
- sprite._puppet = 1;
- sprite._stretch = 1;
-
- int fieldPosition = channelOffset;
- int finishPosition = channelOffset + msgWidth;
- while (fieldPosition < finishPosition) {
- switch (fieldPosition) {
- case kSpritePositionUnk1:
- stream.readByte();
- fieldPosition++;
- break;
- case kSpritePositionEnabled:
- sprite._enabled = stream.readByte() != 0;
- fieldPosition++;
- break;
- case kSpritePositionUnk2:
- stream.readUint16BE();
- fieldPosition += 2;
- break;
- case kSpritePositionFlags:
- sprite._thickness = stream.readByte();
- sprite._inkData = stream.readByte();
- sprite._ink = static_cast<InkType>(sprite._inkData & 0x3f);
-
- if (sprite._inkData & 0x40)
- sprite._trails = 1;
- else
- sprite._trails = 0;
-
- fieldPosition += 2;
- break;
- case kSpritePositionCastId:
- sprite.setCast(CastMemberID(stream.readUint16(), 0));
- fieldPosition += 2;
- break;
- case kSpritePositionY:
- sprite._startPoint.y = stream.readUint16();
- fieldPosition += 2;
- break;
- case kSpritePositionX:
- sprite._startPoint.x = stream.readUint16();
- fieldPosition += 2;
- break;
- case kSpritePositionWidth:
- sprite._width = stream.readUint16();
- fieldPosition += 2;
- break;
- case kSpritePositionHeight:
- sprite._height = stream.readUint16();
- fieldPosition += 2;
- break;
- default:
- stream.readUint16BE();
- fieldPosition += 2;
- break;
- }
- }
-
- frameSize -= msgWidth;
-
- newFrame.sprites.setVal(channel, sprite);
- }
-
- for (Common::HashMap<int, Sprite>::iterator s = newFrame.sprites.begin(); s != newFrame.sprites.end(); ++s) {
- debugC(5, kDebugLoading, "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);
-
- Common::Point topLeft = s->_value._startPoint + s->_value.getRegistrationOffset();
- 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, "New bounding box: %d %d %d %d", _initialRect.left, _initialRect.top, _initialRect.width(), _initialRect.height());
-
- }
-
- _frames.push_back(newFrame);
-
- }
- debugC(5, kDebugLoading, "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
- );
-}
-
-/////////////////////////////////////
-// Sound
-/////////////////////////////////////
-
-SoundCastMember::SoundCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version)
- : CastMember(cast, castId, stream) {
- _type = kCastSound;
- _audio = nullptr;
- _looping = 0;
-}
-
-SoundCastMember::~SoundCastMember() {
- if (_audio)
- delete _audio;
-}
-
-Common::String SoundCastMember::formatInfo() {
- return Common::String::format(
- "looping: %d", _looping
- );
-}
-
-/////////////////////////////////////
-// Text
-/////////////////////////////////////
-
-TextCastMember::TextCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version, uint8 flags1, bool asButton)
- : CastMember(cast, castId, stream) {
- _type = kCastText;
-
- _borderSize = kSizeNone;
- _gutterSize = kSizeNone;
- _boxShadow = kSizeNone;
- _buttonType = kTypeButton;
- _editable = false;
- _maxHeight = _textHeight = 0;
-
- _bgcolor = 0;
- _fgcolor = 0xff;
-
- _textFlags = 0;
- _scroll = 0;
- _fontId = 1;
- _fontSize = 12;
- _textType = kTextTypeFixed;
- _textAlign = kTextAlignLeft;
- _textShadow = kSizeNone;
- _textSlant = 0;
- _bgpalinfo1 = _bgpalinfo2 = _bgpalinfo3 = 0;
- _fgpalinfo1 = _fgpalinfo2 = _fgpalinfo3 = 0xff;
-
- // seems like the line spacing is default to 1 in D4
- _lineSpacing = g_director->getVersion() >= 400 ? 1 : 0;
-
- if (version < kFileVer400) {
- _flags1 = flags1; // region: 0 - auto, 1 - matte, 2 - disabled
- _borderSize = static_cast<SizeType>(stream.readByte());
- _gutterSize = static_cast<SizeType>(stream.readByte());
- _boxShadow = static_cast<SizeType>(stream.readByte());
- _textType = static_cast<TextType>(stream.readByte());
- _textAlign = static_cast<TextAlignType>(stream.readUint16());
- _bgpalinfo1 = stream.readUint16();
- _bgpalinfo2 = stream.readUint16();
- _bgpalinfo3 = stream.readUint16();
-
- uint32 pad2;
- uint16 pad3;
- uint16 pad4 = 0;
- uint16 totalTextHeight;
-
- if (version < kFileVer300) {
- pad2 = stream.readUint16();
- if (pad2 != 0) { // In D2 there are values
- warning("TextCastMember: pad2: %x", pad2);
- }
-
- _initialRect = Movie::readRect(stream);
- pad3 = stream.readUint16();
-
- _textShadow = static_cast<SizeType>(stream.readByte());
- _textFlags = stream.readByte();
- if (_textFlags & 0xf8)
- warning("Unprocessed text cast flags: %x", _textFlags & 0xf8);
-
- totalTextHeight = stream.readUint16();
- } else {
- pad2 = stream.readUint16();
- _initialRect = Movie::readRect(stream);
- pad3 = stream.readUint16();
- _textFlags = stream.readUint16(); // 1: editable, 2: auto tab, 4: don't wrap
- _editable = _textFlags & 0x1;
- totalTextHeight = stream.readUint16();
- }
-
- debugC(2, kDebugLoading, "TextCastMember(): flags1: %d, border: %d gutter: %d shadow: %d textType: %d align: %04x",
- _flags1, _borderSize, _gutterSize, _boxShadow, _textType, _textAlign);
- debugC(2, kDebugLoading, "TextCastMember(): background rgb: 0x%04x 0x%04x 0x%04x, pad2: %x pad3: %d pad4: %d shadow: %d flags: %d totHeight: %d",
- _bgpalinfo1, _bgpalinfo2, _bgpalinfo3, pad2, pad3, pad4, _textShadow, _textFlags, totalTextHeight);
- if (debugChannelSet(2, kDebugLoading)) {
- _initialRect.debugPrint(2, "TextCastMember(): rect:");
- }
- } else if (version >= kFileVer400 && version < kFileVer500) {
- _flags1 = flags1;
- _borderSize = static_cast<SizeType>(stream.readByte());
- _gutterSize = static_cast<SizeType>(stream.readByte());
- _boxShadow = static_cast<SizeType>(stream.readByte());
- _textType = static_cast<TextType>(stream.readByte());
- _textAlign = static_cast<TextAlignType>(stream.readSint16()); // this is because 'right' is -1? or should that be 255?
- _bgpalinfo1 = stream.readUint16();
- _bgpalinfo2 = stream.readUint16();
- _bgpalinfo3 = stream.readUint16();
- _scroll = stream.readUint16();
-
- _fontId = 1; // this is in STXT
-
- _initialRect = Movie::readRect(stream);
- _maxHeight = stream.readUint16();
- _textShadow = static_cast<SizeType>(stream.readByte());
- _textFlags = stream.readByte(); // 1: editable, 2: auto tab 4: don't wrap
- _editable = _textFlags & 0x1;
-
- _textHeight = stream.readUint16();
- _textSlant = 0;
- debugC(2, kDebugLoading, "TextCastMember(): flags1: %d, border: %d gutter: %d shadow: %d textType: %d align: %04x",
- _flags1, _borderSize, _gutterSize, _boxShadow, _textType, _textAlign);
- debugC(2, kDebugLoading, "TextCastMember(): background rgb: 0x%04x 0x%04x 0x%04x, shadow: %d flags: %d textHeight: %d",
- _bgpalinfo1, _bgpalinfo2, _bgpalinfo3, _textShadow, _textFlags, _textHeight);
- if (debugChannelSet(2, kDebugLoading)) {
- _initialRect.debugPrint(2, "TextCastMember(): rect:");
- }
- } else {
- _fontId = 1;
-
- stream.readUint32();
- stream.readUint32();
- stream.readUint32();
- stream.readUint32();
- uint16 skip = stream.readUint16();
- for (int i = 0; i < skip; i++)
- stream.readUint32();
-
- stream.readUint32();
- stream.readUint32();
- stream.readUint32();
- stream.readUint32();
- stream.readUint32();
- stream.readUint32();
-
- _initialRect = Movie::readRect(stream);
- _boundingRect = Movie::readRect(stream);
-
- stream.readUint32();
- stream.readUint16();
- stream.readUint16();
- }
-
- if (asButton) {
- _type = kCastButton;
-
- if (version < kFileVer500) {
- _buttonType = static_cast<ButtonType>(stream.readUint16BE() - 1);
- } else {
- warning("TextCastMember(): Attempting to initialize >D4 button castmember");
- _buttonType = kTypeButton;
- }
- }
-
- _bgcolor = g_director->_wm->findBestColor(_bgpalinfo1 & 0xff, _bgpalinfo2 & 0xff, _bgpalinfo3 & 0xff);
-
- _modified = true;
-}
-
-void TextCastMember::setColors(uint32 *fgcolor, uint32 *bgcolor) {
- if (fgcolor)
- _fgcolor = *fgcolor;
-
- if (bgcolor)
- _bgcolor = *bgcolor;
-
- // if we want to keep the format unchanged, then we need to modify _ftext as well
- if (_widget)
- ((Graphics::MacText *)_widget)->setColors(_fgcolor, _bgcolor);
- else
- _modified = true;
-}
-
-Graphics::TextAlign TextCastMember::getAlignment() {
- switch (_textAlign) {
- case kTextAlignRight:
- return Graphics::kTextAlignRight;
- case kTextAlignCenter:
- return Graphics::kTextAlignCenter;
- case kTextAlignLeft:
- default:
- return Graphics::kTextAlignLeft;
- }
-}
-
-void TextCastMember::setBackColor(uint32 bgCol) {
- _bgcolor = bgCol;
- _modified = true;
-}
-
-void TextCastMember::setForeColor(uint32 fgCol) {
- _fgcolor = fgCol;
- _modified = true;
-}
-
-void TextCastMember::importStxt(const Stxt *stxt) {
- _fontId = stxt->_style.fontId;
- _textSlant = stxt->_style.textSlant;
- _fontSize = stxt->_style.fontSize;
- _fgpalinfo1 = stxt->_style.r;
- _fgpalinfo2 = stxt->_style.g;
- _fgpalinfo3 = stxt->_style.b;
- _ftext = stxt->_ftext;
- _ptext = stxt->_ptext;
- _rtext = stxt->_rtext;
-
- // Rectifying _fontId in case of a fallback font
- Graphics::MacFont macFont(_fontId, _fontSize, _textSlant);
- g_director->_wm->_fontMan->getFont(&macFont);
- _fontId = macFont.getId();
-}
-
-Graphics::MacWidget *TextCastMember::createWidget(Common::Rect &bbox, Channel *channel, SpriteType spriteType) {
- Graphics::MacFont *macFont = new Graphics::MacFont(_fontId, _fontSize, _textSlant);
- Graphics::MacWidget *widget = nullptr;
- Common::Rect dims(bbox);
-
- CastType type = _type;
- ButtonType buttonType = _buttonType;
-
- // WORKAROUND: In D2/D3 there can be text casts that have button
- // information set in the sprite.
- if (type == kCastText && isButtonSprite(spriteType)) {
- type = kCastButton;
- buttonType = ButtonType(spriteType - 8);
- }
-
- switch (type) {
- case kCastText:
- // for mactext, we can expand now, but we can't shrink. so we may pass the small size when we have adjustToFit text style
- if (_textType == kTextTypeAdjustToFit) {
- dims.right = MIN<int>(dims.right, dims.left + _initialRect.width());
- dims.bottom = MIN<int>(dims.bottom, dims.top + _initialRect.height());
- } else if (_textType == kTextTypeFixed){
- // use initialRect to create widget for fixed style text, this maybe related to version.
- dims.right = MAX<int>(dims.right, dims.left + _initialRect.width());
- dims.bottom = MAX<int>(dims.bottom, dims.top + _initialRect.height());
- }
- widget = new Graphics::MacText(g_director->getCurrentWindow(), bbox.left, bbox.top, dims.width(), dims.height(), g_director->_wm, _ftext, macFont, getForeColor(), getBackColor(), _initialRect.width(), getAlignment(), _lineSpacing, _borderSize, _gutterSize, _boxShadow, _textShadow, _textType == kTextTypeFixed);
- ((Graphics::MacText *)widget)->setSelRange(g_director->getCurrentMovie()->_selStart, g_director->getCurrentMovie()->_selEnd);
- ((Graphics::MacText *)widget)->setEditable(channel->_sprite->_editable);
- ((Graphics::MacText *)widget)->draw();
-
- // since we disable the ability of setActive in setEdtiable, then we need to set active widget manually
- if (channel->_sprite->_editable) {
- Graphics::MacWidget *activeWidget = g_director->_wm->getActiveWidget();
- if (activeWidget == nullptr || !activeWidget->isEditable())
- g_director->_wm->setActiveWidget(widget);
- }
- break;
-
- case kCastButton:
- // note that we use _initialRect for the dimensions of the button;
- // the values provided in the sprite bounding box are ignored
- widget = new Graphics::MacButton(Graphics::MacButtonType(buttonType), getAlignment(), g_director->getCurrentWindow(), bbox.left, bbox.top, _initialRect.width(), _initialRect.height(), g_director->_wm, _ftext, macFont, getForeColor(), g_director->_wm->_colorWhite);
- widget->_focusable = true;
-
- ((Graphics::MacButton *)widget)->setHilite(_hilite);
- ((Graphics::MacButton *)widget)->setCheckBoxType(g_director->getCurrentMovie()->_checkBoxType);
- ((Graphics::MacButton *)widget)->draw();
- break;
-
- default:
- break;
- }
-
- delete macFont;
- return widget;
-}
-
-void TextCastMember::importRTE(byte *text) {
- //assert(rteList.size() == 3);
- //child0 is probably font data.
- //child1 is the raw text.
- _rtext = _ptext = _ftext = Common::String((char*)text);
- //child2 is positional?
-}
-
-void TextCastMember::setRawText(const Common::String &text) {
- // Do nothing if text did not change
- if (_rtext.equals(text))
- return;
-
- _rtext = text;
- _ptext = Common::U32String(text);
-
- // If text has changed, use the cached formatting from first STXT in this castmember.
- Common::U32String formatting = Common::String::format("\001\016%04x%02x%04x%04x%04x%04x", _fontId, _textSlant, _fontSize, _fgpalinfo1, _fgpalinfo2, _fgpalinfo3);
- _ftext = formatting + _ptext;
- _modified = true;
-}
-
-// D4 dictionary book said this is line spacing
-int TextCastMember::getTextHeight() {
- if (_widget)
- return ((Graphics::MacText *)_widget)->getLineSpacing();
- else
- return _lineSpacing;
- return 0;
-}
-
-// this should be amend when we have some where using this function
-int TextCastMember::getTextSize() {
- if (_widget)
- return ((Graphics::MacText *)_widget)->getTextSize();
- else
- return _fontSize;
- return 0;
-}
-
-Common::U32String TextCastMember::getText() {
- return _ptext;
-}
-
-Common::String TextCastMember::getRawText() {
- return _rtext;
-}
-
-void TextCastMember::setTextSize(int textSize) {
- if (_widget) {
- ((Graphics::MacText *)_widget)->setTextSize(textSize);
- ((Graphics::MacText *)_widget)->draw();
- } else {
- _fontSize = textSize;
- _modified = true;
- }
-}
-
-void TextCastMember::updateFromWidget(Graphics::MacWidget *widget) {
- if (widget && _type == kCastText) {
- _ptext = ((Graphics::MacText *)widget)->getEditedString();
- }
-}
-
-Common::String TextCastMember::formatInfo() {
- Common::String format = formatStringForDump(_ptext.encode());
-
- return Common::String::format(
- "initialRect: %dx%d@%d,%d, boundingRect: %dx%d@%d,%d, foreColor: %d, backColor: %d, editable: %d, text: \"%s\"",
- _initialRect.width(), _initialRect.height(),
- _initialRect.left, _initialRect.top,
- _boundingRect.width(), _boundingRect.height(),
- _boundingRect.left, _boundingRect.top,
- getForeColor(), getBackColor(),
- _editable, format.c_str()
- );
-
-}
-
-
-/////////////////////////////////////
-// Shape
-/////////////////////////////////////
-
-ShapeCastMember::ShapeCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version)
- : CastMember(cast, castId, stream) {
- _type = kCastShape;
-
- byte unk1;
-
- _ink = kInkTypeCopy;
-
- if (version < kFileVer400) {
- unk1 = stream.readByte();
- _shapeType = static_cast<ShapeType>(stream.readByte());
- _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 < kFileVer500) {
- unk1 = stream.readByte();
- _shapeType = static_cast<ShapeType>(stream.readByte());
- _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 {
- stream.readByte(); // FIXME: Was this copied from D4 by mistake?
- unk1 = stream.readByte();
-
- _initialRect = Movie::readRect(stream);
- _boundingRect = Movie::readRect(stream);
-
- _shapeType = kShapeRectangle;
- _pattern = 0;
- _fgCol = _bgCol = 0;
- _fillType = 0;
- _lineThickness = 1;
- _lineDirection = 0;
- }
- _modified = false;
-
- debugC(3, kDebugLoading, "ShapeCastMember: unk1: %x type: %d pat: %d fg: %d bg: %d fill: %d thick: %d dir: %d",
- unk1, _shapeType, _pattern, _fgCol, _bgCol, _fillType, _lineThickness, _lineDirection);
-
- if (debugChannelSet(3, kDebugLoading))
- _initialRect.debugPrint(0, "ShapeCastMember: rect:");
-}
-
-void ShapeCastMember::setBackColor(uint32 bgCol) {
- _bgCol = bgCol;
- _modified = true;
-}
-
-void ShapeCastMember::setForeColor(uint32 fgCol) {
- _fgCol = fgCol;
- _modified = true;
-}
-
-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
- );
-}
-
-/////////////////////////////////////
-// Script
-/////////////////////////////////////
-
-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 < kFileVer600) {
- byte unk1 = stream.readByte();
- byte type = stream.readByte();
-
- switch (type) {
- case 1:
- _scriptType = kScoreScript;
- break;
- case 3:
- _scriptType = kMovieScript;
- break;
- default:
- error("ScriptCastMember: Unprocessed script type: %d", type);
- }
-
- debugC(3, kDebugLoading, "CASt: Script type: %s (%d), unk1: %d", scriptType2str(_scriptType), type, unk1);
-
- stream.readByte(); // There should be no more data
- assert(stream.eos());
- }
-}
-
-
-/////////////////////////////////////
-// RTE
-/////////////////////////////////////
-
-RTECastMember::RTECastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version)
- : TextCastMember(cast, castId, stream, version) {
-
- _type = kCastRTE;
-}
-
-void RTECastMember::loadChunks() {
- //TODO: Actually load RTEs correctly, don't just make fake STXT.
-#if 0
- Common::SeekableReadStream *rte1 = _movieArchive->getResource(res->children[child].tag, res->children[child].index);
- byte *buffer = new byte[rte1->size() + 2];
- rte1->read(buffer, rte1->size());
- buffer[rte1->size()] = '\n';
- buffer[rte1->size() + 1] = '\0';
- _loadedText->getVal(id)->importRTE(buffer);
-
- delete rte1;
-#endif
-}
-
-
-/////////////////////////////////////
-// Palette
-/////////////////////////////////////
-
-PaletteCastMember::PaletteCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version)
- : CastMember(cast, castId, stream) {
- _type = kCastPalette;
- _palette = nullptr;
-}
-
-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;
-}
-
-} // End of namespace Director
diff --git a/engines/director/castmember.h b/engines/director/castmember.h
deleted file mode 100644
index 18fd6b99039..00000000000
--- a/engines/director/castmember.h
+++ /dev/null
@@ -1,417 +0,0 @@
-/* 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_H
-#define DIRECTOR_CASTMEMBER_H
-
-#include "graphics/font.h"
-
-#include "director/archive.h"
-#include "director/sprite.h"
-#include "director/stxt.h"
-
-#include "director/lingo/lingo-object.h"
-
-namespace Graphics {
-struct Surface;
-class FloodFill;
-class MacText;
-class MacWindowManager;
-class MacButton;
-class MacWidget;
-}
-
-namespace Common {
-class SeekableReadStream;
-class SeekableReadStreamEndian;
-}
-
-namespace Image {
-class ImageDecoder;
-}
-
-namespace Video {
-class VideoDecoder;
-}
-
-namespace Director {
-
-class AudioDecoder;
-struct CastMemberInfo;
-class Channel;
-struct Picture;
-struct Resource;
-class Sprite;
-class Stxt;
-
-class CastMember : public Object<CastMember> {
-public:
- CastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream);
- CastMember(Cast *cast, uint16 castId);
- virtual ~CastMember() {}
-
- Cast *getCast() { return _cast; }
- uint16 getID() { return _castId; }
- CastMemberInfo *getInfo();
-
- virtual bool isEditable() { return false; }
- virtual void setEditable(bool editable) {}
- virtual bool isModified() { return _modified; }
- 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) {}
- 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;
- bool setProp(const Common::String &propName, const Datum &value) override;
- bool hasField(int field) override;
- Datum getField(int field) override;
- bool 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(); };
-
- CastType _type;
- Common::Rect _initialRect;
- Common::Rect _boundingRect;
- Common::Array<Resource> _children;
-
- bool _hilite;
- bool _erase;
- int _purgePriority;
- uint32 _size;
- uint8 _flags1;
-
-protected:
- Cast *_cast;
- uint16 _castId;
- // a link to the widget we created, we may use it later
- Graphics::MacWidget *_widget;
- bool _modified;
- bool _isChanged;
-};
-
-class BitmapCastMember : public CastMember {
-public:
- 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();
- Graphics::MacWidget *createWidget(Common::Rect &bbox, Channel *channel, SpriteType spriteType) override;
-
- bool isModified() override;
- void createMatte(Common::Rect &bbox);
- Graphics::Surface *getMatte(Common::Rect &bbox);
- void copyStretchImg(Graphics::Surface *surface, const Common::Rect &bbox, const byte *pal = 0);
-
- bool hasField(int field) override;
- Datum getField(int field) override;
- bool setField(int field, const Datum &value) override;
-
- Common::String formatInfo() override;
-
- PictureReference *getPicture() const;
- void setPicture(PictureReference &picture);
- void setPicture(Image::ImageDecoder &image, bool adjustSize);
-
- Picture *_picture = nullptr;
- Graphics::Surface *_ditheredImg;
- Graphics::FloodFill *_matte;
-
- uint16 _pitch;
- uint16 _regX;
- uint16 _regY;
- uint16 _flags2;
- uint16 _bytes;
- int _clut;
- int _ditheredTargetClut;
-
- uint16 _bitsPerPixel;
-
- uint32 _tag;
- bool _noMatte;
- bool _external;
-};
-
-class DigitalVideoCastMember : public CastMember {
-public:
- DigitalVideoCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version);
- ~DigitalVideoCastMember();
-
- bool isModified() override;
- Graphics::MacWidget *createWidget(Common::Rect &bbox, Channel *channel, SpriteType spriteType) override;
-
- bool loadVideo(Common::String path);
- void startVideo(Channel *channel);
- 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;
- bool setField(int field, const Datum &value) override;
-
- Common::String formatInfo() 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;
- FrameRateType _frameRateType;
-
- uint16 _frameRate;
- bool _getFirstFrame;
- int _duration;
-
- Video::VideoDecoder *_video;
- Graphics::Surface *_lastFrame;
-
- Channel *_channel;
-};
-
-
-struct FilmLoopFrame {
- Common::HashMap<int, Sprite> sprites;
-};
-
-class FilmLoopCastMember : public CastMember {
-public:
- FilmLoopCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version);
- ~FilmLoopCastMember();
-
- bool isModified() override;
- //Graphics::MacWidget *createWidget(Common::Rect &bbox, Channel *channel, SpriteType spriteType) override;
-
- Common::Array<Channel> *getSubChannels(Common::Rect &bbox, Channel *channel);
-
- void loadFilmLoopData(Common::SeekableReadStreamEndian &stream);
- void loadFilmLoopDataV4(Common::SeekableReadStreamEndian &stream);
-
- Common::String formatInfo() override;
-
- bool _enableSound;
- bool _looping;
- bool _crop;
- bool _center;
-
- Common::Array<FilmLoopFrame> _frames;
- Common::Array<Channel> _subchannels;
-};
-
-class MovieCastMember : public CastMember {
-public:
- MovieCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version);
-
- Common::String formatInfo() override;
-
- uint32 _flags;
- bool _looping;
- bool _enableScripts;
- bool _enableSound;
- bool _crop;
- bool _center;
-};
-
-class SoundCastMember : public CastMember {
-public:
- SoundCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version);
- ~SoundCastMember();
-
- Common::String formatInfo() override;
-
- bool _looping;
- AudioDecoder *_audio;
-};
-
-class ShapeCastMember : public CastMember {
-public:
- ShapeCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version);
- uint32 getForeColor() override { return _fgCol; }
- uint32 getBackColor() override { return _bgCol; }
- void setBackColor(uint32 bgCol) override;
- void setForeColor(uint32 fgCol) override;
-
- Common::String formatInfo() override;
-
- ShapeType _shapeType;
- uint16 _pattern;
- byte _fillType;
- byte _lineThickness;
- byte _lineDirection;
- InkType _ink;
-
-private:
- uint32 _fgCol;
- uint32 _bgCol;
-};
-
-class TextCastMember : public CastMember {
-public:
- TextCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version, uint8 flags1 = 0, bool asButton = false);
- void setColors(uint32 *fgcolor, uint32 *bgcolor) override;
-
- Graphics::MacWidget *createWidget(Common::Rect &bbox, Channel *channel, SpriteType spriteType) override;
-
- bool isEditable() override { return _editable; }
- void setEditable(bool editable) override { _editable = editable; }
- void updateFromWidget(Graphics::MacWidget *widget) override;
- Graphics::TextAlign getAlignment();
-
- uint32 getBackColor() override { return _bgcolor; }
- void setBackColor(uint32 bgCol) override;
- uint32 getForeColor() override { return _fgcolor; }
- void setForeColor(uint32 fgCol) override;
-
- bool hasField(int field) override;
- Datum getField(int field) override;
- bool 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 getTextHeight();
-
- int getTextSize();
- void setTextSize(int textSize);
-
- Common::String formatInfo() override;
-
- SizeType _borderSize;
- SizeType _gutterSize;
- SizeType _boxShadow;
- uint16 _maxHeight;
- uint16 _textHeight;
-
- uint32 _fontId;
- uint16 _fontSize;
- TextType _textType;
- TextAlignType _textAlign;
- SizeType _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:
- uint32 _bgcolor;
- uint32 _fgcolor;
-};
-
-class ScriptCastMember : public CastMember {
-public:
- ScriptCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version);
-
- ScriptType _scriptType;
-};
-
-class RTECastMember : public TextCastMember {
-public:
- RTECastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version);
-
- void loadChunks();
-};
-
-struct EditInfo {
- Common::Rect rect;
- int32 selStart;
- int32 selEnd;
- byte version;
- byte rulerFlag;
-};
-
-struct CastMemberInfo {
- bool autoHilite;
- uint32 scriptId;
- Common::String script;
- Common::String name;
- Common::String directory;
- Common::String fileName;
- Common::String type;
- EditInfo scriptEditInfo;
- FontStyle scriptStyle;
- EditInfo textEditInfo;
- Common::String modifiedBy;
- Common::String comments;
-
- CastMemberInfo() : autoHilite(false), scriptId(0) {}
-};
-
-struct Label {
- Common::String comment;
- Common::String name;
- uint16 number;
- Label(Common::String name1, uint16 number1, Common::String comment1) { name = name1; number = number1; comment = comment1;}
-};
-
-class PaletteCastMember : public CastMember {
-public:
- PaletteCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version);
- int getPaletteId() { return _palette ? _palette->id : 0; }
- void activatePalette() { if (_palette) g_director->setPalette(_palette->id); }
-
- Common::String formatInfo() override;
-
- PaletteV4 *_palette;
-};
-
-} // End of namespace Director
-
-#endif
diff --git a/engines/director/castmember/bitmap.cpp b/engines/director/castmember/bitmap.cpp
new file mode 100644
index 00000000000..2bae5705fd6
--- /dev/null
+++ b/engines/director/castmember/bitmap.cpp
@@ -0,0 +1,591 @@
+/* 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 "graphics/surface.h"
+#include "graphics/macgui/macwidget.h"
+#include "image/image_decoder.h"
+
+#include "director/director.h"
+#include "director/cast.h"
+#include "director/movie.h"
+#include "director/picture.h"
+#include "director/score.h"
+#include "director/window.h"
+#include "director/castmember/bitmap.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-the.h"
+
+namespace Director {
+
+BitmapCastMember::BitmapCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint32 castTag, uint16 version, uint8 flags1)
+ : CastMember(cast, castId, stream) {
+ _type = kCastBitmap;
+ _picture = nullptr;
+ _ditheredImg = nullptr;
+ _matte = nullptr;
+ _noMatte = false;
+ _bytes = 0;
+ _pitch = 0;
+ _flags2 = 0;
+ _regX = _regY = 0;
+ _clut = 0;
+ _ditheredTargetClut = 0;
+ _bitsPerPixel = 0;
+ _external = false;
+
+ if (version < kFileVer400) {
+ _flags1 = flags1; // region: 0 - auto, 1 - matte, 2 - disabled
+
+ _bytes = stream.readUint16();
+ _initialRect = Movie::readRect(stream);
+ _boundingRect = Movie::readRect(stream);
+ _regY = stream.readUint16();
+ _regX = stream.readUint16();
+
+ if (_bytes & 0x8000) {
+ _bitsPerPixel = stream.readUint16();
+ _clut = stream.readSint16();
+ if (_clut <= 0) // builtin palette
+ _clut -= 1;
+ } else {
+ _bitsPerPixel = 1;
+ _clut = kClutSystemMac;
+ }
+
+ _pitch = _initialRect.width();
+ if (_pitch % 16)
+ _pitch += 16 - (_initialRect.width() % 16);
+
+ _pitch *= _bitsPerPixel;
+ _pitch >>= 3;
+
+ } else if (version >= kFileVer400 && version < kFileVer600) {
+ _flags1 = flags1;
+ _pitch = stream.readUint16();
+ _pitch &= 0x0fff;
+
+ _initialRect = Movie::readRect(stream);
+ _boundingRect = Movie::readRect(stream);
+ _regY = stream.readUint16();
+ _regX = stream.readUint16();
+
+ _bitsPerPixel = stream.readUint16();
+
+ if (stream.eos()) {
+ _bitsPerPixel = 0;
+ } else {
+ if (version >= kFileVer500) {
+ stream.readSint16(); // is this the castlib? was ff ff
+ }
+ _clut = stream.readSint16();
+ if (_clut <= 0) // builtin palette
+ _clut -= 1;
+ stream.readUint16();
+ /* uint16 unk1 = */ stream.readUint16();
+ stream.readUint16();
+
+ stream.readUint32();
+ stream.readUint32();
+
+ _flags2 = stream.readUint16();
+ }
+
+ if (_bitsPerPixel == 0)
+ _bitsPerPixel = 1;
+
+ int tail = 0;
+ byte buf[256];
+
+ while (!stream.eos()) {
+ byte c = stream.readByte();
+ if (tail < 256)
+ buf[tail] = c;
+ tail++;
+ }
+
+ if (tail)
+ warning("BUILDBOT: BitmapCastMember: %d bytes left", tail);
+
+ if (tail && debugChannelSet(2, kDebugLoading)) {
+ debug("BitmapCastMember: tail");
+ Common::hexdump(buf, tail);
+ }
+ }
+
+ _tag = castTag;
+}
+
+BitmapCastMember::BitmapCastMember(Cast *cast, uint16 castId, Image::ImageDecoder *img, uint8 flags1)
+ : CastMember(cast, castId) {
+ _type = kCastBitmap;
+ _matte = nullptr;
+ _noMatte = false;
+ _bytes = 0;
+ if (img != nullptr) {
+ _picture = new Picture(*img);
+ }
+ _ditheredImg = nullptr;
+ _clut = -1;
+ _ditheredTargetClut = 0;
+ _initialRect = Common::Rect(0, 0, img->getSurface()->w, img->getSurface()->h);
+ _pitch = img->getSurface()->pitch;
+ _bitsPerPixel = img->getSurface()->format.bytesPerPixel * 8;
+ _regY = img->getSurface()->h / 2;
+ _regX = img->getSurface()->w / 2;
+ _flags1 = flags1;
+ _flags2 = 0;
+ _tag = 0;
+ _external = false;
+}
+
+BitmapCastMember::~BitmapCastMember() {
+ delete _picture;
+
+ if (_ditheredImg) {
+ _ditheredImg->free();
+ delete _ditheredImg;
+ }
+
+ if (_matte)
+ delete _matte;
+}
+
+Graphics::MacWidget *BitmapCastMember::createWidget(Common::Rect &bbox, Channel *channel, SpriteType spriteType) {
+ if (!_picture) {
+ warning("BitmapCastMember::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;
+ int srcBpp = _picture->_surface.format.bytesPerPixel;
+
+ const byte *pal = _picture->_palette;
+ bool previouslyDithered = _ditheredImg != nullptr;
+ if (_ditheredImg) {
+ _ditheredImg->free();
+ delete _ditheredImg;
+ _ditheredImg = nullptr;
+ _ditheredTargetClut = 0;
+ }
+
+ if (dstBpp == 1) {
+ if (srcBpp > 1
+ // At least early directors were not remapping 8bpp images. But in case it is
+ // needed, here is the code
+#if 0
+ || (srcBpp == 1 &&
+ memcmp(g_director->_wm->getPalette(), _img->_palette, _img->_paletteSize))
+#endif
+ ) {
+
+ _ditheredImg = _picture->_surface.convertTo(g_director->_wm->_pixelformat, _picture->_palette, _picture->_paletteColors, g_director->_wm->getPalette(), g_director->_wm->getPaletteSize());
+
+ pal = g_director->_wm->getPalette();
+ } else {
+ // Convert indexed image to indexed palette
+ Movie *movie = g_director->getCurrentMovie();
+ Cast *cast = movie->getCast();
+ Score *score = movie->getScore();
+ // Get the current score palette. Note that this is the ID of the palette in the list, not the cast member!
+ int currentPaletteId = score->resolvePaletteId(score->getCurrentPalette());
+ if (!currentPaletteId)
+ currentPaletteId = cast->_defaultPalette;
+ PaletteV4 *currentPalette = g_director->getPalette(currentPaletteId);
+ if (!currentPalette) {
+ currentPaletteId = kClutSystemMac;
+ currentPalette = g_director->getPalette(currentPaletteId);
+ }
+ int castPaletteId = score->resolvePaletteId(_clut);
+ // It is possible for Director to have saved an invalid ID in _clut;
+ // if this is the case, do no dithering.
+ if (!castPaletteId)
+ castPaletteId = currentPaletteId;
+
+ // Check if the palette is in the middle of a color fade event
+ bool isColorCycling = score->isPaletteColorCycling();
+
+ // First, check if the palettes are different
+ switch (_bitsPerPixel) {
+ // 1bpp - this is preconverted to 0x00 and 0xff, change nothing.
+ case 1:
+ break;
+ // 2bpp - convert to nearest using the standard 2-bit palette.
+ case 2:
+ {
+ const PaletteV4 &srcPal = g_director->getLoaded4Palette();
+ _ditheredImg = _picture->_surface.convertTo(g_director->_wm->_pixelformat, srcPal.palette, srcPal.length, currentPalette->palette, currentPalette->length, Graphics::kDitherNaive);
+ }
+ break;
+ // 4bpp - if using a builtin palette, use one of the corresponding 4-bit ones.
+ case 4:
+ {
+ const auto pals = g_director->getLoaded16Palettes();
+ // in D4 you aren't allowed to use custom palettes for 4-bit images, so uh...
+ // I guess default to the mac palette?
+ int palIndex = pals.contains(castPaletteId) ? castPaletteId : kClutSystemMac;
+ const PaletteV4 &srcPal = pals.getVal(palIndex);
+ _ditheredImg = _picture->_surface.convertTo(g_director->_wm->_pixelformat, srcPal.palette, srcPal.length, currentPalette->palette, currentPalette->length, Graphics::kDitherNaive);
+ }
+ break;
+ // 8bpp - if using a different palette, and we're not doing a color cycling operation, convert using nearest colour matching
+ case 8:
+ // Only redither 8-bit images if we have the flag set, or it is external
+ if (!movie->_remapPalettesWhenNeeded && !_external)
+ break;
+ if (_external || (castPaletteId != currentPaletteId && !isColorCycling)) {
+ const auto pals = g_director->getLoadedPalettes();
+ int palIndex = pals.contains(castPaletteId) ? castPaletteId : kClutSystemMac;
+ const PaletteV4 &srcPal = pals.getVal(palIndex);
+
+ // If it is an external image, use the included palette.
+ // For BMP images especially, they'll often have the right colors
+ // but in the wrong palette order.
+ const byte *palPtr = _external ? pal : srcPal.palette;
+ int palLength = _external ? _picture->getPaletteSize() : srcPal.length;
+ _ditheredImg = _picture->_surface.convertTo(g_director->_wm->_pixelformat, palPtr, palLength, currentPalette->palette, currentPalette->length, Graphics::kDitherNaive);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (_ditheredImg) {
+ debugC(4, kDebugImages, "BitmapCastMember::createWidget(): Dithering image from source palette %d to target palette %d", _clut, score->getCurrentPalette());
+ // Save the palette ID so we can check if a redraw is required
+ _ditheredTargetClut = currentPaletteId;
+
+ if (!_external) {
+ // Finally, the first and last colours in the palette are special. No matter what the palette remap
+ // does, we need to scrub those to be the same.
+ const Graphics::Surface *src = &_picture->_surface;
+ for (int y = 0; y < src->h; y++) {
+ for (int x = 0; x < src->w; x++) {
+ const int test = *(const byte *)src->getBasePtr(x, y);
+ if (test == 0 || test == (1 << _bitsPerPixel) - 1) {
+ *(byte *)_ditheredImg->getBasePtr(x, y) = test == 0 ? 0x00 : 0xff;
+ }
+ }
+ }
+ }
+ } else if (previouslyDithered) {
+ debugC(4, kDebugImages, "BitmapCastMember::createWidget(): Removed dithered image, score palette %d matches cast member", score->getCurrentPalette());
+ }
+
+ }
+ }
+
+ Graphics::MacWidget *widget = new Graphics::MacWidget(g_director->getCurrentWindow(), bbox.left, bbox.top, bbox.width(), bbox.height(), g_director->_wm, false);
+
+ // scale for drawing a different size sprite
+ copyStretchImg(widget->getSurface()->surfacePtr(), bbox, pal);
+
+ return widget;
+}
+
+void BitmapCastMember::copyStretchImg(Graphics::Surface *surface, const Common::Rect &bbox, const byte *pal) {
+ const Graphics::Surface *srcSurf;
+
+ if (_ditheredImg)
+ srcSurf = _ditheredImg;
+ else
+ srcSurf = &_picture->_surface;
+
+ if (bbox.width() != _initialRect.width() || bbox.height() != _initialRect.height()) {
+
+ int scaleX = SCALE_THRESHOLD * _initialRect.width() / bbox.width();
+ int scaleY = SCALE_THRESHOLD * _initialRect.height() / bbox.height();
+
+ for (int y = 0, scaleYCtr = 0; y < bbox.height(); y++, scaleYCtr += scaleY) {
+ if (g_director->_wm->_pixelformat.bytesPerPixel == 1) {
+ for (int x = 0, scaleXCtr = 0; x < bbox.width(); x++, scaleXCtr += scaleX) {
+ const byte *src = (const byte *)srcSurf->getBasePtr(scaleXCtr / SCALE_THRESHOLD, scaleYCtr / SCALE_THRESHOLD);
+ *(byte *)surface->getBasePtr(x, y) = *src;
+ }
+ } else {
+ for (int x = 0, scaleXCtr = 0; x < bbox.width(); x++, scaleXCtr += scaleX) {
+ const void *ptr = srcSurf->getBasePtr(scaleXCtr / SCALE_THRESHOLD, scaleYCtr / SCALE_THRESHOLD);
+ int32 color;
+
+ switch (srcSurf->format.bytesPerPixel) {
+ case 1:
+ {
+ color = *(const byte *)ptr * 3;
+ color = surface->format.RGBToColor(pal[color], pal[color + 1], pal[color + 2]);
+ }
+ break;
+ case 4:
+ color = *(const int32 *)ptr;
+ break;
+ default:
+ error("Unimplemented src bpp: %d", srcSurf->format.bytesPerPixel);
+ }
+
+ *(int32 *)surface->getBasePtr(x, y) = color;
+ }
+ }
+ }
+ } else {
+ surface->copyFrom(*srcSurf);
+ }
+
+ if (g_director->_debugDraw & kDebugDrawCast) {
+ surface->frameRect(Common::Rect(0, 0, surface->w, surface->h), g_director->_wm->_colorWhite);
+
+ const Graphics::Font *font = FontMan.getFontByUsage(Graphics::FontManager::kConsoleFont);
+ font->drawString(surface, Common::String::format("%d", _castId), 2, 2, 10, g_director->_wm->_colorWhite);
+ }
+}
+
+bool BitmapCastMember::isModified() {
+ if (CastMember::isModified()) {
+ // Let's us use "setChanged" when changing the picture through Lingo
+ return true;
+ }
+ // Check for palette changes.
+ // If a bitmap has a custom palette assigned to it, createWidget()
+ // will dither the image so that it fits within the current palette.
+ // When the score palette changes, we need to flag that the widget needs
+ // to be recreated.
+ if (_clut) {
+ Movie *movie = g_director->getCurrentMovie();
+ Cast *cast = movie->getCast();
+ Score *score = movie->getScore();
+ int currentPaletteId = score->resolvePaletteId(score->getCurrentPalette());
+ if (!currentPaletteId)
+ currentPaletteId = cast->_defaultPalette;
+ PaletteV4 *currentPalette = g_director->getPalette(currentPaletteId);
+ if (!currentPalette) {
+ currentPaletteId = kClutSystemMac;
+ currentPalette = g_director->getPalette(currentPaletteId);
+ }
+ int castPaletteId = score->resolvePaletteId(_clut);
+ if (!castPaletteId)
+ castPaletteId = cast->_defaultPalette;
+
+ if (currentPaletteId == castPaletteId) {
+ return _ditheredTargetClut != 0;
+ } else {
+ return _ditheredTargetClut != currentPaletteId;
+ }
+ }
+ return false;
+}
+
+void BitmapCastMember::createMatte(Common::Rect &bbox) {
+ // Like background trans, but all white pixels NOT ENCLOSED by coloured pixels
+ // are transparent
+ Graphics::Surface tmp;
+ tmp.create(bbox.width(), bbox.height(), g_director->_pixelformat);
+
+ copyStretchImg(&tmp, bbox);
+
+ _noMatte = true;
+
+ // Searching white color in the corners
+ uint32 whiteColor = 0;
+ bool colorFound = false;
+
+ if (g_director->_pixelformat.bytesPerPixel == 1) {
+ for (int y = 0; y < tmp.h; y++) {
+ for (int x = 0; x < tmp.w; x++) {
+ byte color = *(byte *)tmp.getBasePtr(x, y);
+
+ if (g_director->getPalette()[color * 3 + 0] == 0xff &&
+ g_director->getPalette()[color * 3 + 1] == 0xff &&
+ g_director->getPalette()[color * 3 + 2] == 0xff) {
+ whiteColor = color;
+ colorFound = true;
+ break;
+ }
+ }
+ }
+ } else {
+ whiteColor = g_director->_wm->_colorWhite;
+ colorFound = true;
+ }
+
+ if (!colorFound) {
+ debugC(1, kDebugImages, "BitmapCastMember::createMatte(): No white color for matte image");
+ } else {
+ delete _matte;
+
+ _matte = new Graphics::FloodFill(&tmp, whiteColor, 0, true);
+
+ for (int yy = 0; yy < tmp.h; yy++) {
+ _matte->addSeed(0, yy);
+ _matte->addSeed(tmp.w - 1, yy);
+ }
+
+ for (int xx = 0; xx < tmp.w; xx++) {
+ _matte->addSeed(xx, 0);
+ _matte->addSeed(xx, tmp.h - 1);
+ }
+
+ _matte->fillMask();
+ _noMatte = false;
+ }
+
+ tmp.free();
+}
+
+Graphics::Surface *BitmapCastMember::getMatte(Common::Rect &bbox) {
+ // Lazy loading of mattes
+ if (!_matte && !_noMatte) {
+ createMatte(bbox);
+ }
+
+ // check for the scale matte
+ Graphics::Surface *surface = _matte ? _matte->getMask() : nullptr;
+ if (surface && (surface->w != bbox.width() || surface->h != bbox.height())) {
+ createMatte(bbox);
+ }
+
+ return _matte ? _matte->getMask() : nullptr;
+}
+
+Common::String BitmapCastMember::formatInfo() {
+ return Common::String::format(
+ "initialRect: %dx%d@%d,%d, boundingRect: %dx%d@%d,%d, foreColor: %d, backColor: %d, regX: %d, regY: %d, pitch: %d, bitsPerPixel: %d, palette: %d",
+ _initialRect.width(), _initialRect.height(),
+ _initialRect.left, _initialRect.top,
+ _boundingRect.width(), _boundingRect.height(),
+ _boundingRect.left, _boundingRect.top,
+ getForeColor(), getBackColor(),
+ _regX, _regY, _pitch, _bitsPerPixel, _clut
+ );
+}
+
+PictureReference *BitmapCastMember::getPicture() const {
+ auto picture = new PictureReference;
+
+ // Not sure if we can make the assumption that the owning
+ // BitmapCastMember will live as long as any reference,
+ // so we'll make a copy of the Picture.
+ picture->_picture = new Picture(*_picture);
+
+ return picture;
+}
+
+void BitmapCastMember::setPicture(PictureReference &picture) {
+ delete _picture;
+ _picture = new Picture(*picture._picture);
+
+ // Force redither
+ delete _ditheredImg;
+ _ditheredImg = nullptr;
+
+ // Make sure we get redrawn
+ setModified(true);
+ // TODO: Should size be adjusted?
+}
+
+void BitmapCastMember::setPicture(Image::ImageDecoder &image, bool adjustSize) {
+ delete _picture;
+ _picture = new Picture(image);
+ if (adjustSize) {
+ auto surf = image.getSurface();
+ _size = surf->pitch * surf->h + _picture->getPaletteSize();
+ }
+ // Make sure we get redrawn
+ setModified(true);
+}
+
+bool BitmapCastMember::hasField(int field) {
+ switch (field) {
+ case kTheDepth:
+ case kTheRegPoint:
+ case kThePalette:
+ case kThePicture:
+ return true;
+ default:
+ break;
+ }
+ return CastMember::hasField(field);
+}
+
+Datum BitmapCastMember::getField(int field) {
+ Datum d;
+
+ switch (field) {
+ case kTheDepth:
+ d = _bitsPerPixel;
+ break;
+ case kTheRegPoint:
+ d.type = POINT;
+ d.u.farr = new FArray;
+ d.u.farr->arr.push_back(_regX);
+ d.u.farr->arr.push_back(_regY);
+ break;
+ case kThePalette:
+ d = _clut;
+ break;
+ case kThePicture:
+ d.type = PICTUREREF;
+ d.u.picture = getPicture();
+ break;
+ default:
+ d = CastMember::getField(field);
+ }
+
+ return d;
+}
+
+bool BitmapCastMember::setField(int field, const Datum &d) {
+ switch (field) {
+ case kTheDepth:
+ warning("BitmapCastMember::setField(): Attempt to set read-only field %s of cast %d", g_lingo->field2str(field), _castId);
+ return false;
+ case kTheRegPoint:
+ if (d.type == POINT || (d.type == ARRAY && d.u.farr->arr.size() >= 2)) {
+ Score *score = g_director->getCurrentMovie()->getScore();
+ score->invalidateRectsForMember(this);
+ _regX = d.u.farr->arr[0].asInt();
+ _regY = d.u.farr->arr[1].asInt();
+ _modified = true;
+ } else {
+ warning("BitmapCastMember::setField(): Wrong Datum type %d for kTheRegPoint", d.type);
+ return false;
+ }
+ return true;
+ case kThePalette:
+ _clut = d.asInt();
+ return true;
+ case kThePicture:
+ if (d.type == PICTUREREF && d.u.picture != nullptr) {
+ setPicture(*d.u.picture);
+ return true;
+ } else {
+ warning("BitmapCastMember::setField(): Wrong Datum type %d for kThePicture (or nullptr)", d.type);
+ }
+ return false;
+ default:
+ break;
+ }
+
+ return CastMember::setField(field, d);
+}
+
+} // End of namespace Director
diff --git a/engines/director/castmember/bitmap.h b/engines/director/castmember/bitmap.h
new file mode 100644
index 00000000000..ae8808983fe
--- /dev/null
+++ b/engines/director/castmember/bitmap.h
@@ -0,0 +1,76 @@
+/* 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, Common::SeekableReadStreamEndian &stream, uint32 castTag, uint16 version, uint8 flags1 = 0);
+ BitmapCastMember(Cast *cast, uint16 castId, Image::ImageDecoder *img, uint8 flags1 = 0);
+ ~BitmapCastMember();
+ Graphics::MacWidget *createWidget(Common::Rect &bbox, Channel *channel, SpriteType spriteType) override;
+
+ bool isModified() override;
+ void createMatte(Common::Rect &bbox);
+ Graphics::Surface *getMatte(Common::Rect &bbox);
+ void copyStretchImg(Graphics::Surface *surface, const Common::Rect &bbox, const byte *pal = 0);
+
+ bool hasField(int field) override;
+ Datum getField(int field) override;
+ bool setField(int field, const Datum &value) override;
+
+ Common::String formatInfo() override;
+
+ PictureReference *getPicture() const;
+ void setPicture(PictureReference &picture);
+ void setPicture(Image::ImageDecoder &image, bool adjustSize);
+
+ Picture *_picture = nullptr;
+ Graphics::Surface *_ditheredImg;
+ Graphics::FloodFill *_matte;
+
+ uint16 _pitch;
+ uint16 _regX;
+ uint16 _regY;
+ uint16 _flags2;
+ uint16 _bytes;
+ int _clut;
+ int _ditheredTargetClut;
+
+ uint16 _bitsPerPixel;
+
+ uint32 _tag;
+ bool _noMatte;
+ bool _external;
+};
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/castmember/castmember.cpp b/engines/director/castmember/castmember.cpp
new file mode 100644
index 00000000000..0c7692c8bf6
--- /dev/null
+++ b/engines/director/castmember/castmember.cpp
@@ -0,0 +1,241 @@
+/* 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/castmember/castmember.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-the.h"
+
+namespace Director {
+
+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;
+
+ _modified = true;
+ _isChanged = false;
+
+ _objType = kCastMemberObj;
+
+ _widget = nullptr;
+ _erase = false;
+}
+
+CastMember::CastMember(Cast *cast, uint16 castId) : Object<CastMember>("CastMember") {
+ _type = kCastTypeNull;
+ _cast = cast;
+ _castId = castId;
+ _hilite = false;
+ _purgePriority = 3;
+ _size = 0;
+ _flags1 = 0;
+
+ _modified = true;
+ _isChanged = false;
+
+ _objType = kCastMemberObj;
+
+ _widget = nullptr;
+ _erase = false;
+}
+
+void CastMember::setModified(bool modified) {
+ _modified = modified;
+ if (modified)
+ _isChanged = true;
+}
+
+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();
+}
+
+bool CastMember::setProp(const Common::String &propName, const Datum &value) {
+ Common::String fieldName = Common::String::format("%d%s", kTheCast, propName.c_str());
+ if (g_lingo->_theEntityFields.contains(fieldName)) {
+ return setField(g_lingo->_theEntityFields[fieldName]->field, value);
+ }
+
+ warning("CastMember::setProp: unknown property '%s'", propName.c_str());
+ return false;
+}
+
+bool CastMember::hasField(int field) {
+ switch (field) {
+ case kTheBackColor:
+ case kTheCastType:
+ case kTheFileName:
+ case kTheForeColor:
+ case kTheHeight:
+ case kTheLoaded:
+ case kTheModified:
+ case kTheName:
+ case kTheNumber:
+ case kTheRect:
+ case kThePurgePriority:
+ case kTheScriptText:
+ case kTheSize:
+ 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 kTheCastType:
+ d.type = SYMBOL;
+ d.u.s = new Common::String(castType2str(_type));
+ 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 kTheLoaded:
+ d = 1; // Not loaded handled in Lingo::getTheCast
+ break;
+ case kTheModified:
+ d = (int)_isChanged;
+ break;
+ case kTheName:
+ if (castInfo)
+ d = Datum(castInfo->name);
+ break;
+ case kTheNumber:
+ d = _castId;
+ break;
+ case kTheRect:
+ // not sure get the initial rect would be fine to castmember
+ d = Datum(_cast->getCastMember(_castId)->_initialRect);
+ 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;
+}
+
+bool CastMember::setField(int field, const Datum &d) {
+ CastMemberInfo *castInfo = _cast->getCastMemberInfo(_castId);
+
+ switch (field) {
+ case kTheBackColor:
+ _cast->getCastMember(_castId)->setBackColor(d.asInt());
+ return true;
+ case kTheCastType:
+ warning("BUILDBOT: CastMember::setField(): Attempt to set read-only field %s of cast %d", g_lingo->entity2str(field), _castId);
+ return false;
+ case kTheFileName:
+ if (!castInfo) {
+ warning("CastMember::setField(): CastMember info for %d not found", _castId);
+ return false;
+ }
+ castInfo->fileName = d.asString();
+ return true;
+ case kTheForeColor:
+ _cast->getCastMember(_castId)->setForeColor(d.asInt());
+ return true;
+ case kTheHeight:
+ warning("BUILDBOT: CastMember::setField(): Attempt to set read-only field \"%s\" of cast %d", g_lingo->field2str(field), _castId);
+ return false;
+ case kTheName:
+ if (!castInfo) {
+ warning("CastMember::setField(): CastMember info for %d not found", _castId);
+ return false;
+ }
+ castInfo->name = d.asString();
+ return true;
+ case kTheRect:
+ warning("CastMember::setField(): Attempt to set read-only field \"%s\" of cast %d", g_lingo->field2str(field), _castId);
+ return false;
+ case kThePurgePriority:
+ _purgePriority = CLIP<int>(d.asInt(), 0, 3);
+ return true;
+ case kTheScriptText:
+ if (!castInfo) {
+ warning("CastMember::setField(): CastMember info for %d not found", _castId);
+ return false;
+ }
+ _cast->_lingoArchive->replaceCode(*d.u.s, kCastScript, _castId);
+ castInfo->script = d.asString();
+ return true;
+ case kTheWidth:
+ warning("BUILDBOT: CastMember::setField(): Attempt to set read-only field \"%s\" of cast %d", g_lingo->field2str(field), _castId);
+ return false;
+ default:
+ warning("CastMember::setField(): Unprocessed setting field \"%s\" of cast %d", g_lingo->field2str(field), _castId);
+ }
+
+ return false;
+}
+
+CastMemberInfo *CastMember::getInfo() {
+ return _cast->getCastMemberInfo(_castId);
+}
+
+} // End of namespace Director
diff --git a/engines/director/castmember/castmember.h b/engines/director/castmember/castmember.h
new file mode 100644
index 00000000000..f5e8ec22948
--- /dev/null
+++ b/engines/director/castmember/castmember.h
@@ -0,0 +1,132 @@
+/* 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 "graphics/font.h"
+
+#include "director/archive.h"
+#include "director/sprite.h"
+#include "director/stxt.h"
+
+#include "director/lingo/lingo-object.h"
+
+namespace Graphics {
+class MacWidget;
+}
+
+namespace Common {
+class SeekableReadStream;
+class SeekableReadStreamEndian;
+}
+
+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() {}
+
+ Cast *getCast() { return _cast; }
+ uint16 getID() { return _castId; }
+ CastMemberInfo *getInfo();
+
+ virtual bool isEditable() { return false; }
+ virtual void setEditable(bool editable) {}
+ virtual bool isModified() { return _modified; }
+ 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) {}
+ 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;
+ bool setProp(const Common::String &propName, const Datum &value) override;
+ bool hasField(int field) override;
+ Datum getField(int field) override;
+ bool 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(); };
+
+ CastType _type;
+ Common::Rect _initialRect;
+ Common::Rect _boundingRect;
+ Common::Array<Resource> _children;
+
+ bool _hilite;
+ bool _erase;
+ int _purgePriority;
+ uint32 _size;
+ uint8 _flags1;
+
+protected:
+ Cast *_cast;
+ uint16 _castId;
+ // a link to the widget we created, we may use it later
+ Graphics::MacWidget *_widget;
+ bool _modified;
+ bool _isChanged;
+};
+
+struct EditInfo {
+ Common::Rect rect;
+ int32 selStart;
+ int32 selEnd;
+ byte version;
+ byte rulerFlag;
+};
+
+struct CastMemberInfo {
+ bool autoHilite;
+ uint32 scriptId;
+ Common::String script;
+ Common::String name;
+ Common::String directory;
+ Common::String fileName;
+ Common::String type;
+ EditInfo scriptEditInfo;
+ FontStyle scriptStyle;
+ EditInfo textEditInfo;
+ Common::String modifiedBy;
+ Common::String comments;
+
+ CastMemberInfo() : autoHilite(false), scriptId(0) {}
+};
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/castmember/digitalvideo.cpp b/engines/director/castmember/digitalvideo.cpp
new file mode 100644
index 00000000000..d299d87502f
--- /dev/null
+++ b/engines/director/castmember/digitalvideo.cpp
@@ -0,0 +1,442 @@
+/* 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 "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/movie.h"
+#include "director/window.h"
+#include "director/castmember/digitalvideo.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-the.h"
+
+namespace Director {
+
+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;
+ 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;
+
+ 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() {
+ if (_lastFrame) {
+ _lastFrame->free();
+ delete _lastFrame;
+ }
+
+ if (_video)
+ delete _video;
+}
+
+bool DigitalVideoCastMember::loadVideo(Common::String path) {
+ // TODO: detect file type (AVI, QuickTime, FLIC) based on magic number,
+ // insert the right video decoder
+
+ if (_video)
+ delete _video;
+
+ _filename = path;
+ _video = new Video::QuickTimeDecoder();
+
+ Common::String path1 = pathMakeRelative(path);
+
+ debugC(2, kDebugLoading | kDebugImages, "Loading video %s -> %s", path.c_str(), path1.c_str());
+ bool result = _video->loadFile(Common::Path(path1, g_director->_dirSeparator));
+ if (!result) {
+ delete _video;
+ _video = new Video::AVIDecoder();
+ result = _video->loadFile(Common::Path(path1, g_director->_dirSeparator));
+ if (!result) {
+ warning("DigitalVideoCastMember::loadVideo(): format not supported, skipping");
+ delete _video;
+ _video = nullptr;
+ }
+ }
+
+ 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.
+ byte palette[256 * 3];
+ g_system->getPaletteManager()->grabPalette(palette, 0, 256);
+ _video->setDitheringPalette(palette);
+ }
+
+ return result;
+}
+
+bool DigitalVideoCastMember::isModified() {
+ if (!_video || !_video->isVideoLoaded())
+ return true;
+
+ if (_getFirstFrame)
+ return true;
+
+ if (_channel->_movieRate == 0.0)
+ return false;
+
+ return _video->needsUpdate();
+}
+
+void DigitalVideoCastMember::startVideo(Channel *channel) {
+ _channel = channel;
+
+ if (!_video || !_video->isVideoLoaded()) {
+ warning("DigitalVideoCastMember::startVideo: No video %s", !_video ? "decoder" : "loaded");
+ return;
+ }
+
+ if (_pausedAtStart) {
+ _getFirstFrame = true;
+ } else {
+ if (_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->_stopTime == 0)
+ _channel->_stopTime = getMovieTotalTime();
+
+ _duration = getMovieTotalTime();
+}
+
+void DigitalVideoCastMember::stopVideo() {
+ if (!_video || !_video->isVideoLoaded()) {
+ 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()) {
+ 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) {
+ Graphics::MacWidget *widget = new Graphics::MacWidget(g_director->getCurrentWindow(), bbox.left, bbox.top, bbox.width(), bbox.height(), g_director->_wm, false);
+
+ _channel = channel;
+
+ if (!_video || !_video->isVideoLoaded()) {
+ warning("DigitalVideoCastMember::createWidget: No video decoder");
+ delete widget;
+
+ return nullptr;
+ }
+
+ // Do not render stopped videos
+ if (_channel->_movieRate == 0.0 && !_getFirstFrame && _lastFrame) {
+ widget->getSurface()->blitFrom(*_lastFrame);
+
+ return widget;
+ }
+
+ const Graphics::Surface *frame = _video->decodeNextFrame();
+
+ debugC(1, kDebugImages, "Video time: %d rate: %f", _channel->_movieTime, _channel->_movieRate);
+
+ if (frame) {
+ if (_lastFrame) {
+ _lastFrame->free();
+ delete _lastFrame;
+ }
+
+ _lastFrame = frame->convertTo(g_director->_pixelformat, g_director->getPalette());
+ }
+ if (_lastFrame)
+ widget->getSurface()->blitFrom(*_lastFrame);
+
+ if (_getFirstFrame) {
+ _video->stop();
+ _getFirstFrame = false;
+ }
+
+ if (_video->endOfVideo()) {
+ if (_looping) {
+ _video->rewind();
+ } else {
+ _channel->_movieRate = 0.0;
+ }
+ }
+
+ return widget;
+}
+
+uint DigitalVideoCastMember::getDuration() {
+ if (!_video || !_video->isVideoLoaded()) {
+ Common::String path = getCast()->getVideoPath(_castId);
+ if (!path.empty())
+ loadVideo(pathMakeRelative(path));
+
+ _duration = getMovieTotalTime();
+ }
+ return _duration;
+}
+
+uint DigitalVideoCastMember::getMovieCurrentTime() {
+ if (!_video)
+ return 0;
+
+ int stamp = MIN<int>(_video->getTime() * 60 / 1000, getMovieTotalTime());
+
+ return stamp;
+}
+
+uint DigitalVideoCastMember::getMovieTotalTime() {
+ if (!_video)
+ return 0;
+
+ int stamp = _video->getDuration().msecs() * 60 / 1000;
+
+ return stamp;
+}
+
+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()));
+}
+
+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
+ _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
+ );
+}
+
+bool DigitalVideoCastMember::hasField(int field) {
+ switch (field) {
+ case kTheCenter:
+ case kTheController:
+ case kTheCrop:
+ case kTheDirectToStage:
+ case kTheDuration:
+ case kTheFrameRate:
+ case kTheLoop:
+ case kTheMovieRate:
+ case kTheMovieTime:
+ case kThePausedAtStart:
+ case kThePreLoad:
+ case kTheSound:
+ case kTheVideo:
+ case kTheVolume:
+ 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 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 kTheVideo:
+ d = _enableVideo;
+ break;
+ default:
+ d = CastMember::getField(field);
+ }
+
+ return d;
+}
+
+bool DigitalVideoCastMember::setField(int field, const Datum &d) {
+ switch (field) {
+ case kTheCenter:
+ _center = (bool)d.asInt();
+ return true;
+ case kTheController:
+ _showControls = (bool)d.asInt();
+ return true;
+ case kTheCrop:
+ _crop = (bool)d.asInt();
+ return true;
+ case kTheDirectToStage:
+ _directToStage = (bool)d.asInt();
+ return true;
+ case kTheDuration:
+ warning("DigitalVideoCastMember::setField(): Attempt to set read-only field %s of cast %d", g_lingo->entity2str(field), _castId);
+ return false;
+ case kTheFrameRate:
+ _frameRate = d.asInt();
+ setFrameRate(d.asInt());
+ return true;
+ case kTheLoop:
+ _looping = (bool)d.asInt();
+ if (_looping && _channel && _channel->_movieRate == 0.0) {
+ setMovieRate(1.0);
+ }
+ return true;
+ case kThePausedAtStart:
+ _pausedAtStart = (bool)d.asInt();
+ return true;
+ case kThePreLoad:
+ _preload = (bool)d.asInt();
+ return true;
+ case kTheSound:
+ _enableSound = (bool)d.asInt();
+ return true;
+ case kTheVideo:
+ _enableVideo = (bool)d.asInt();
+ return true;
+ default:
+ break;
+ }
+
+ return CastMember::setField(field, d);
+}
+
+} // End of namespace Director
diff --git a/engines/director/castmember/digitalvideo.h b/engines/director/castmember/digitalvideo.h
new file mode 100644
index 00000000000..f2647b735d5
--- /dev/null
+++ b/engines/director/castmember/digitalvideo.h
@@ -0,0 +1,87 @@
+/* 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 {
+
+class DigitalVideoCastMember : public CastMember {
+public:
+ DigitalVideoCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version);
+ ~DigitalVideoCastMember();
+
+ bool isModified() override;
+ Graphics::MacWidget *createWidget(Common::Rect &bbox, Channel *channel, SpriteType spriteType) override;
+
+ bool loadVideo(Common::String path);
+ void startVideo(Channel *channel);
+ 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;
+ bool setField(int field, const Datum &value) override;
+
+ Common::String formatInfo() 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;
+ FrameRateType _frameRateType;
+
+ uint16 _frameRate;
+ bool _getFirstFrame;
+ int _duration;
+
+ Video::VideoDecoder *_video;
+ Graphics::Surface *_lastFrame;
+
+ Channel *_channel;
+};
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/castmember/filmloop.cpp b/engines/director/castmember/filmloop.cpp
new file mode 100644
index 00000000000..5a654eebda9
--- /dev/null
+++ b/engines/director/castmember/filmloop.cpp
@@ -0,0 +1,392 @@
+/* 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 "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/movie.h"
+#include "director/window.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;
+}
+
+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, Channel *channel) {
+ Common::Rect widgetRect(bbox.width() ? bbox.width() : _initialRect.width(), bbox.height() ? bbox.height() : _initialRect.height());
+
+ _subchannels.clear();
+
+ if (channel->_filmLoopFrame >= _frames.size()) {
+ warning("Film loop frame %d requested, only %d available", channel->_filmLoopFrame, _frames.size());
+ return &_subchannels;
+ }
+
+ // get the list of sprite IDs for this frame
+ Common::Array<int> spriteIds;
+ for (Common::HashMap<int, Director::Sprite>::iterator iter = _frames[channel->_filmLoopFrame].sprites.begin(); iter != _frames[channel->_filmLoopFrame].sprites.end(); ++iter) {
+ spriteIds.push_back(iter->_key);
+ }
+ Common::sort(spriteIds.begin(), spriteIds.end());
+
+ // copy the sprites in order to the list
+ for (Common::Array<int>::iterator iter = spriteIds.begin(); iter != spriteIds.end(); ++iter) {
+ Sprite src = _frames[channel->_filmLoopFrame].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();
+
+ Channel chan(&src);
+ chan._currentPoint = Common::Point(absX, absY);
+ chan._width = width;
+ chan._height = height;
+
+ _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;
+}
+
+void FilmLoopCastMember::loadFilmLoopData(Common::SeekableReadStreamEndian &stream) {
+ _initialRect = Common::Rect();
+ _frames.clear();
+
+ uint32 size = stream.readUint32BE();
+ if (debugChannelSet(5, kDebugLoading)) {
+ debugC(5, kDebugLoading, "SCVW body:");
+ uint32 pos = stream.pos();
+ stream.seek(0);
+ stream.hexdump(size);
+ stream.seek(pos);
+ }
+ uint16 channelSize = 16;
+ FilmLoopFrame newFrame;
+
+ while (stream.pos() < size) {
+ uint16 frameSize = stream.readUint16BE() - 2;
+ if (debugChannelSet(5, kDebugLoading)) {
+ debugC(5, kDebugLoading, "Frame entry:");
+ stream.hexdump(frameSize);
+ }
+
+ while (frameSize > 0) {
+ int msgWidth = stream.readByte() * 2;
+ int order = stream.readByte() * 2 - 0x20;
+ frameSize -= 2;
+ debugC(8, kDebugLoading, "Message: msgWidth %d, order %d", msgWidth, order);
+ if (debugChannelSet(8, kDebugLoading)) {
+ stream.hexdump(msgWidth);
+ }
+
+ int fieldPosition = order;
+ int finishPosition = order + msgWidth;
+ while (fieldPosition < finishPosition) {
+ int channel = (fieldPosition / channelSize);
+ int channelOffset = fieldPosition % channelSize;
+
+ Sprite sprite(nullptr);
+ sprite._movie = g_director->getCurrentMovie();
+ if (newFrame.sprites.contains(channel)) {
+ sprite = newFrame.sprites.getVal(channel);
+ }
+ sprite._spriteType = kCastMemberSprite;
+ sprite._puppet = 1;
+ sprite._stretch = 1;
+
+ switch (channelOffset) {
+ case kSpritePositionUnk1:
+ stream.readByte();
+ fieldPosition++;
+ break;
+ case kSpritePositionEnabled:
+ sprite._enabled = stream.readByte() != 0;
+ fieldPosition++;
+ break;
+ case kSpritePositionUnk2:
+ stream.readUint16BE();
+ fieldPosition += 2;
+ break;
+ case kSpritePositionFlags:
+ sprite._thickness = stream.readByte();
+ sprite._inkData = stream.readByte();
+ sprite._ink = static_cast<InkType>(sprite._inkData & 0x3f);
+
+ if (sprite._inkData & 0x40)
+ sprite._trails = 1;
+ else
+ sprite._trails = 0;
+
+ fieldPosition += 2;
+ break;
+ case kSpritePositionCastId:
+ sprite.setCast(CastMemberID(stream.readUint16(), 0));
+ fieldPosition += 2;
+ break;
+ case kSpritePositionY:
+ sprite._startPoint.y = stream.readUint16();
+ fieldPosition += 2;
+ break;
+ case kSpritePositionX:
+ sprite._startPoint.x = stream.readUint16();
+ fieldPosition += 2;
+ break;
+ case kSpritePositionWidth:
+ sprite._width = stream.readUint16();
+ fieldPosition += 2;
+ break;
+ case kSpritePositionHeight:
+ sprite._height = stream.readUint16();
+ fieldPosition += 2;
+ break;
+ default:
+ stream.readUint16BE();
+ fieldPosition += 2;
+ break;
+ }
+ newFrame.sprites.setVal(channel, sprite);
+ }
+
+ frameSize -= msgWidth;
+ }
+
+ for (Common::HashMap<int, Sprite>::iterator s = newFrame.sprites.begin(); s != newFrame.sprites.end(); ++s) {
+ debugC(5, kDebugLoading, "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);
+
+ Common::Point topLeft = s->_value._startPoint + s->_value.getRegistrationOffset();
+ 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, "New bounding box: %d %d %d %d", _initialRect.left, _initialRect.top, _initialRect.width(), _initialRect.height());
+
+ }
+
+ _frames.push_back(newFrame);
+
+ }
+ debugC(5, kDebugLoading, "Full bounding box: %d %d %d %d", _initialRect.left, _initialRect.top, _initialRect.width(), _initialRect.height());
+}
+
+void FilmLoopCastMember::loadFilmLoopDataV4(Common::SeekableReadStreamEndian &stream) {
+ _initialRect = Common::Rect();
+ _frames.clear();
+
+ uint32 size = stream.readUint32BE();
+ if (debugChannelSet(5, kDebugLoading)) {
+ debugC(5, kDebugLoading, "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, "SCVW header:");
+ stream.hexdump(framesOffset - 8);
+ }
+ stream.skip(6);
+ uint16 channelSize = stream.readUint16BE(); // should be 20!
+ stream.skip(framesOffset - 16);
+
+ FilmLoopFrame newFrame;
+
+ while (stream.pos() < size) {
+ uint16 frameSize = stream.readUint16BE() - 2;
+ if (debugChannelSet(5, kDebugLoading)) {
+ debugC(5, kDebugLoading, "Frame entry:");
+ stream.hexdump(frameSize);
+ }
+
+ while (frameSize > 0) {
+ uint16 msgWidth = stream.readUint16BE();
+ uint16 order = stream.readUint16BE();
+ frameSize -= 4;
+
+ int channel = (order / channelSize) - 1;
+ int channelOffset = order % channelSize;
+
+ Sprite sprite(nullptr);
+ sprite._movie = g_director->getCurrentMovie();
+ if (newFrame.sprites.contains(channel)) {
+ sprite = newFrame.sprites.getVal(channel);
+ }
+ debugC(8, kDebugLoading, "Message: msgWidth %d, channel %d, channelOffset %d", msgWidth, channel, channelOffset);
+ if (debugChannelSet(8, kDebugLoading)) {
+ stream.hexdump(msgWidth);
+ }
+ sprite._puppet = 1;
+ sprite._stretch = 1;
+
+ int fieldPosition = channelOffset;
+ int finishPosition = channelOffset + msgWidth;
+ while (fieldPosition < finishPosition) {
+ switch (fieldPosition) {
+ case kSpritePositionUnk1:
+ stream.readByte();
+ fieldPosition++;
+ break;
+ case kSpritePositionEnabled:
+ sprite._enabled = stream.readByte() != 0;
+ fieldPosition++;
+ break;
+ case kSpritePositionUnk2:
+ stream.readUint16BE();
+ fieldPosition += 2;
+ break;
+ case kSpritePositionFlags:
+ sprite._thickness = stream.readByte();
+ sprite._inkData = stream.readByte();
+ sprite._ink = static_cast<InkType>(sprite._inkData & 0x3f);
+
+ if (sprite._inkData & 0x40)
+ sprite._trails = 1;
+ else
+ sprite._trails = 0;
+
+ fieldPosition += 2;
+ break;
+ case kSpritePositionCastId:
+ sprite.setCast(CastMemberID(stream.readUint16(), 0));
+ fieldPosition += 2;
+ break;
+ case kSpritePositionY:
+ sprite._startPoint.y = stream.readUint16();
+ fieldPosition += 2;
+ break;
+ case kSpritePositionX:
+ sprite._startPoint.x = stream.readUint16();
+ fieldPosition += 2;
+ break;
+ case kSpritePositionWidth:
+ sprite._width = stream.readUint16();
+ fieldPosition += 2;
+ break;
+ case kSpritePositionHeight:
+ sprite._height = stream.readUint16();
+ fieldPosition += 2;
+ break;
+ default:
+ stream.readUint16BE();
+ fieldPosition += 2;
+ break;
+ }
+ }
+
+ frameSize -= msgWidth;
+
+ newFrame.sprites.setVal(channel, sprite);
+ }
+
+ for (Common::HashMap<int, Sprite>::iterator s = newFrame.sprites.begin(); s != newFrame.sprites.end(); ++s) {
+ debugC(5, kDebugLoading, "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);
+
+ Common::Point topLeft = s->_value._startPoint + s->_value.getRegistrationOffset();
+ 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, "New bounding box: %d %d %d %d", _initialRect.left, _initialRect.top, _initialRect.width(), _initialRect.height());
+
+ }
+
+ _frames.push_back(newFrame);
+
+ }
+ debugC(5, kDebugLoading, "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
+ );
+}
+
+} // End of namespace Director
diff --git a/engines/director/castmember/filmloop.h b/engines/director/castmember/filmloop.h
new file mode 100644
index 00000000000..297eb2b2775
--- /dev/null
+++ b/engines/director/castmember/filmloop.h
@@ -0,0 +1,61 @@
+/* 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/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();
+
+ bool isModified() override;
+ //Graphics::MacWidget *createWidget(Common::Rect &bbox, Channel *channel, SpriteType spriteType) override;
+
+ Common::Array<Channel> *getSubChannels(Common::Rect &bbox, Channel *channel);
+
+ void loadFilmLoopData(Common::SeekableReadStreamEndian &stream);
+ void loadFilmLoopDataV4(Common::SeekableReadStreamEndian &stream);
+
+ Common::String formatInfo() override;
+
+ bool _enableSound;
+ bool _looping;
+ bool _crop;
+ bool _center;
+
+ Common::Array<FilmLoopFrame> _frames;
+ Common::Array<Channel> _subchannels;
+};
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/castmember/movie.cpp b/engines/director/castmember/movie.cpp
new file mode 100644
index 00000000000..38026310f8d
--- /dev/null
+++ b/engines/director/castmember/movie.cpp
@@ -0,0 +1,61 @@
+/* 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/castmember/movie.h"
+
+namespace Director {
+
+MovieCastMember::MovieCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version)
+ : CastMember(cast, castId, stream) {
+ _type = kCastMovie;
+
+ _initialRect = Movie::readRect(stream);
+ _flags = stream.readUint32();
+
+ _looping = !(_flags & 0x20);
+ _enableScripts = _flags & 0x10;
+ _enableSound = _flags & 0x08;
+ _crop = !(_flags & 0x02);
+ _center = _flags & 0x01;
+
+ 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);
+
+}
+
+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
diff --git a/engines/director/castmember/movie.h b/engines/director/castmember/movie.h
new file mode 100644
index 00000000000..31d3322f1a9
--- /dev/null
+++ b/engines/director/castmember/movie.h
@@ -0,0 +1,45 @@
+/* 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/castmember.h"
+
+namespace Director {
+
+class MovieCastMember : public CastMember {
+public:
+ MovieCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version);
+
+ Common::String formatInfo() override;
+
+ uint32 _flags;
+ bool _looping;
+ bool _enableScripts;
+ bool _enableSound;
+ bool _crop;
+ bool _center;
+};
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/castmember/palette.cpp b/engines/director/castmember/palette.cpp
new file mode 100644
index 00000000000..d15a90f84f3
--- /dev/null
+++ b/engines/director/castmember/palette.cpp
@@ -0,0 +1,44 @@
+/* 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/castmember/palette.h"
+
+namespace Director {
+
+PaletteCastMember::PaletteCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version)
+ : CastMember(cast, castId, stream) {
+ _type = kCastPalette;
+ _palette = nullptr;
+}
+
+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;
+}
+
+}
diff --git a/engines/director/castmember/palette.h b/engines/director/castmember/palette.h
new file mode 100644
index 00000000000..769bf9817db
--- /dev/null
+++ b/engines/director/castmember/palette.h
@@ -0,0 +1,42 @@
+/* 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);
+ int getPaletteId() { return _palette ? _palette->id : 0; }
+ void activatePalette() { if (_palette) g_director->setPalette(_palette->id); }
+
+ Common::String formatInfo() override;
+
+ PaletteV4 *_palette;
+};
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/castmember/script.cpp b/engines/director/castmember/script.cpp
new file mode 100644
index 00000000000..f9250597efd
--- /dev/null
+++ b/engines/director/castmember/script.cpp
@@ -0,0 +1,61 @@
+/* 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/castmember/script.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 < kFileVer600) {
+ byte unk1 = stream.readByte();
+ byte type = stream.readByte();
+
+ switch (type) {
+ case 1:
+ _scriptType = kScoreScript;
+ break;
+ case 3:
+ _scriptType = kMovieScript;
+ break;
+ default:
+ error("ScriptCastMember: Unprocessed script type: %d", type);
+ }
+
+ debugC(3, kDebugLoading, "CASt: Script type: %s (%d), unk1: %d", scriptType2str(_scriptType), type, unk1);
+
+ stream.readByte(); // There should be no more data
+ assert(stream.eos());
+ }
+}
+
+}
diff --git a/engines/director/castmember/script.h b/engines/director/castmember/script.h
new file mode 100644
index 00000000000..a6c1e8610a4
--- /dev/null
+++ b/engines/director/castmember/script.h
@@ -0,0 +1,38 @@
+/* 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);
+
+ ScriptType _scriptType;
+};
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/castmember/shape.cpp b/engines/director/castmember/shape.cpp
new file mode 100644
index 00000000000..4036db4191d
--- /dev/null
+++ b/engines/director/castmember/shape.cpp
@@ -0,0 +1,105 @@
+/* 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/castmember/shape.h"
+
+namespace Director {
+
+ShapeCastMember::ShapeCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version)
+ : CastMember(cast, castId, stream) {
+ _type = kCastShape;
+
+ byte unk1;
+
+ _ink = kInkTypeCopy;
+
+ if (version < kFileVer400) {
+ unk1 = stream.readByte();
+ _shapeType = static_cast<ShapeType>(stream.readByte());
+ _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 < kFileVer500) {
+ unk1 = stream.readByte();
+ _shapeType = static_cast<ShapeType>(stream.readByte());
+ _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 {
+ stream.readByte(); // FIXME: Was this copied from D4 by mistake?
+ unk1 = stream.readByte();
+
+ _initialRect = Movie::readRect(stream);
+ _boundingRect = Movie::readRect(stream);
+
+ _shapeType = kShapeRectangle;
+ _pattern = 0;
+ _fgCol = _bgCol = 0;
+ _fillType = 0;
+ _lineThickness = 1;
+ _lineDirection = 0;
+ }
+ _modified = false;
+
+ debugC(3, kDebugLoading, "ShapeCastMember: unk1: %x type: %d pat: %d fg: %d bg: %d fill: %d thick: %d dir: %d",
+ unk1, _shapeType, _pattern, _fgCol, _bgCol, _fillType, _lineThickness, _lineDirection);
+
+ if (debugChannelSet(3, kDebugLoading))
+ _initialRect.debugPrint(0, "ShapeCastMember: rect:");
+}
+
+void ShapeCastMember::setBackColor(uint32 bgCol) {
+ _bgCol = bgCol;
+ _modified = true;
+}
+
+void ShapeCastMember::setForeColor(uint32 fgCol) {
+ _fgCol = fgCol;
+ _modified = true;
+}
+
+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
+ );
+}
+
+}
diff --git a/engines/director/castmember/shape.h b/engines/director/castmember/shape.h
new file mode 100644
index 00000000000..2f1eb3d9efd
--- /dev/null
+++ b/engines/director/castmember/shape.h
@@ -0,0 +1,53 @@
+/* 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);
+ uint32 getForeColor() override { return _fgCol; }
+ uint32 getBackColor() override { return _bgCol; }
+ void setBackColor(uint32 bgCol) override;
+ void setForeColor(uint32 fgCol) override;
+
+ Common::String formatInfo() override;
+
+ ShapeType _shapeType;
+ uint16 _pattern;
+ byte _fillType;
+ byte _lineThickness;
+ byte _lineDirection;
+ InkType _ink;
+
+private:
+ uint32 _fgCol;
+ uint32 _bgCol;
+};
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/castmember/sound.cpp b/engines/director/castmember/sound.cpp
new file mode 100644
index 00000000000..4d62b1be855
--- /dev/null
+++ b/engines/director/castmember/sound.cpp
@@ -0,0 +1,46 @@
+/* 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/sound.h"
+#include "director/castmember/sound.h"
+
+namespace Director {
+
+SoundCastMember::SoundCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version)
+ : CastMember(cast, castId, stream) {
+ _type = kCastSound;
+ _audio = nullptr;
+ _looping = 0;
+}
+
+SoundCastMember::~SoundCastMember() {
+ if (_audio)
+ delete _audio;
+}
+
+Common::String SoundCastMember::formatInfo() {
+ return Common::String::format(
+ "looping: %d", _looping
+ );
+}
+
+} // End of namespace Director
diff --git a/engines/director/castmember/sound.h b/engines/director/castmember/sound.h
new file mode 100644
index 00000000000..57e9372fef7
--- /dev/null
+++ b/engines/director/castmember/sound.h
@@ -0,0 +1,44 @@
+/* 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, Common::SeekableReadStreamEndian &stream, uint16 version);
+ ~SoundCastMember();
+
+ Common::String formatInfo() override;
+
+ bool _looping;
+ AudioDecoder *_audio;
+};
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/castmember/text.cpp b/engines/director/castmember/text.cpp
new file mode 100644
index 00000000000..1f26b8b6543
--- /dev/null
+++ b/engines/director/castmember/text.cpp
@@ -0,0 +1,652 @@
+/* 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 "graphics/macgui/macbutton.h"
+#include "graphics/macgui/mactext.h"
+
+#include "director/director.h"
+#include "director/channel.h"
+#include "director/movie.h"
+#include "director/score.h"
+#include "director/window.h"
+#include "director/castmember/text.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-the.h"
+
+namespace Director {
+
+TextCastMember::TextCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version, uint8 flags1, bool asButton)
+ : CastMember(cast, castId, stream) {
+ _type = kCastText;
+
+ _borderSize = kSizeNone;
+ _gutterSize = kSizeNone;
+ _boxShadow = kSizeNone;
+ _buttonType = kTypeButton;
+ _editable = false;
+ _maxHeight = _textHeight = 0;
+
+ _bgcolor = 0;
+ _fgcolor = 0xff;
+
+ _textFlags = 0;
+ _scroll = 0;
+ _fontId = 1;
+ _fontSize = 12;
+ _textType = kTextTypeFixed;
+ _textAlign = kTextAlignLeft;
+ _textShadow = kSizeNone;
+ _textSlant = 0;
+ _bgpalinfo1 = _bgpalinfo2 = _bgpalinfo3 = 0;
+ _fgpalinfo1 = _fgpalinfo2 = _fgpalinfo3 = 0xff;
+
+ // seems like the line spacing is default to 1 in D4
+ _lineSpacing = g_director->getVersion() >= 400 ? 1 : 0;
+
+ if (version < kFileVer400) {
+ _flags1 = flags1; // region: 0 - auto, 1 - matte, 2 - disabled
+ _borderSize = static_cast<SizeType>(stream.readByte());
+ _gutterSize = static_cast<SizeType>(stream.readByte());
+ _boxShadow = static_cast<SizeType>(stream.readByte());
+ _textType = static_cast<TextType>(stream.readByte());
+ _textAlign = static_cast<TextAlignType>(stream.readUint16());
+ _bgpalinfo1 = stream.readUint16();
+ _bgpalinfo2 = stream.readUint16();
+ _bgpalinfo3 = stream.readUint16();
+
+ uint32 pad2;
+ uint16 pad3;
+ uint16 pad4 = 0;
+ uint16 totalTextHeight;
+
+ if (version < kFileVer300) {
+ pad2 = stream.readUint16();
+ if (pad2 != 0) { // In D2 there are values
+ warning("TextCastMember: pad2: %x", pad2);
+ }
+
+ _initialRect = Movie::readRect(stream);
+ pad3 = stream.readUint16();
+
+ _textShadow = static_cast<SizeType>(stream.readByte());
+ _textFlags = stream.readByte();
+ if (_textFlags & 0xf8)
+ warning("Unprocessed text cast flags: %x", _textFlags & 0xf8);
+
+ totalTextHeight = stream.readUint16();
+ } else {
+ pad2 = stream.readUint16();
+ _initialRect = Movie::readRect(stream);
+ pad3 = stream.readUint16();
+ _textFlags = stream.readUint16(); // 1: editable, 2: auto tab, 4: don't wrap
+ _editable = _textFlags & 0x1;
+ totalTextHeight = stream.readUint16();
+ }
+
+ debugC(2, kDebugLoading, "TextCastMember(): flags1: %d, border: %d gutter: %d shadow: %d textType: %d align: %04x",
+ _flags1, _borderSize, _gutterSize, _boxShadow, _textType, _textAlign);
+ debugC(2, kDebugLoading, "TextCastMember(): background rgb: 0x%04x 0x%04x 0x%04x, pad2: %x pad3: %d pad4: %d shadow: %d flags: %d totHeight: %d",
+ _bgpalinfo1, _bgpalinfo2, _bgpalinfo3, pad2, pad3, pad4, _textShadow, _textFlags, totalTextHeight);
+ if (debugChannelSet(2, kDebugLoading)) {
+ _initialRect.debugPrint(2, "TextCastMember(): rect:");
+ }
+ } else if (version >= kFileVer400 && version < kFileVer500) {
+ _flags1 = flags1;
+ _borderSize = static_cast<SizeType>(stream.readByte());
+ _gutterSize = static_cast<SizeType>(stream.readByte());
+ _boxShadow = static_cast<SizeType>(stream.readByte());
+ _textType = static_cast<TextType>(stream.readByte());
+ _textAlign = static_cast<TextAlignType>(stream.readSint16()); // this is because 'right' is -1? or should that be 255?
+ _bgpalinfo1 = stream.readUint16();
+ _bgpalinfo2 = stream.readUint16();
+ _bgpalinfo3 = stream.readUint16();
+ _scroll = stream.readUint16();
+
+ _fontId = 1; // this is in STXT
+
+ _initialRect = Movie::readRect(stream);
+ _maxHeight = stream.readUint16();
+ _textShadow = static_cast<SizeType>(stream.readByte());
+ _textFlags = stream.readByte(); // 1: editable, 2: auto tab 4: don't wrap
+ _editable = _textFlags & 0x1;
+
+ _textHeight = stream.readUint16();
+ _textSlant = 0;
+ debugC(2, kDebugLoading, "TextCastMember(): flags1: %d, border: %d gutter: %d shadow: %d textType: %d align: %04x",
+ _flags1, _borderSize, _gutterSize, _boxShadow, _textType, _textAlign);
+ debugC(2, kDebugLoading, "TextCastMember(): background rgb: 0x%04x 0x%04x 0x%04x, shadow: %d flags: %d textHeight: %d",
+ _bgpalinfo1, _bgpalinfo2, _bgpalinfo3, _textShadow, _textFlags, _textHeight);
+ if (debugChannelSet(2, kDebugLoading)) {
+ _initialRect.debugPrint(2, "TextCastMember(): rect:");
+ }
+ } else {
+ _fontId = 1;
+
+ stream.readUint32();
+ stream.readUint32();
+ stream.readUint32();
+ stream.readUint32();
+ uint16 skip = stream.readUint16();
+ for (int i = 0; i < skip; i++)
+ stream.readUint32();
+
+ stream.readUint32();
+ stream.readUint32();
+ stream.readUint32();
+ stream.readUint32();
+ stream.readUint32();
+ stream.readUint32();
+
+ _initialRect = Movie::readRect(stream);
+ _boundingRect = Movie::readRect(stream);
+
+ stream.readUint32();
+ stream.readUint16();
+ stream.readUint16();
+ }
+
+ if (asButton) {
+ _type = kCastButton;
+
+ if (version < kFileVer500) {
+ _buttonType = static_cast<ButtonType>(stream.readUint16BE() - 1);
+ } else {
+ warning("TextCastMember(): Attempting to initialize >D4 button castmember");
+ _buttonType = kTypeButton;
+ }
+ }
+
+ _bgcolor = g_director->_wm->findBestColor(_bgpalinfo1 & 0xff, _bgpalinfo2 & 0xff, _bgpalinfo3 & 0xff);
+
+ _modified = true;
+}
+
+void TextCastMember::setColors(uint32 *fgcolor, uint32 *bgcolor) {
+ if (fgcolor)
+ _fgcolor = *fgcolor;
+
+ if (bgcolor)
+ _bgcolor = *bgcolor;
+
+ // if we want to keep the format unchanged, then we need to modify _ftext as well
+ if (_widget)
+ ((Graphics::MacText *)_widget)->setColors(_fgcolor, _bgcolor);
+ else
+ _modified = true;
+}
+
+Graphics::TextAlign TextCastMember::getAlignment() {
+ switch (_textAlign) {
+ case kTextAlignRight:
+ return Graphics::kTextAlignRight;
+ case kTextAlignCenter:
+ return Graphics::kTextAlignCenter;
+ case kTextAlignLeft:
+ default:
+ return Graphics::kTextAlignLeft;
+ }
+}
+
+void TextCastMember::setBackColor(uint32 bgCol) {
+ _bgcolor = bgCol;
+ _modified = true;
+}
+
+void TextCastMember::setForeColor(uint32 fgCol) {
+ _fgcolor = fgCol;
+ _modified = true;
+}
+
+void TextCastMember::importStxt(const Stxt *stxt) {
+ _fontId = stxt->_style.fontId;
+ _textSlant = stxt->_style.textSlant;
+ _fontSize = stxt->_style.fontSize;
+ _fgpalinfo1 = stxt->_style.r;
+ _fgpalinfo2 = stxt->_style.g;
+ _fgpalinfo3 = stxt->_style.b;
+ _ftext = stxt->_ftext;
+ _ptext = stxt->_ptext;
+ _rtext = stxt->_rtext;
+
+ // Rectifying _fontId in case of a fallback font
+ Graphics::MacFont macFont(_fontId, _fontSize, _textSlant);
+ g_director->_wm->_fontMan->getFont(&macFont);
+ _fontId = macFont.getId();
+}
+
+Graphics::MacWidget *TextCastMember::createWidget(Common::Rect &bbox, Channel *channel, SpriteType spriteType) {
+ Graphics::MacFont *macFont = new Graphics::MacFont(_fontId, _fontSize, _textSlant);
+ Graphics::MacWidget *widget = nullptr;
+ Common::Rect dims(bbox);
+
+ CastType type = _type;
+ ButtonType buttonType = _buttonType;
+
+ // WORKAROUND: In D2/D3 there can be text casts that have button
+ // information set in the sprite.
+ if (type == kCastText && isButtonSprite(spriteType)) {
+ type = kCastButton;
+ buttonType = ButtonType(spriteType - 8);
+ }
+
+ switch (type) {
+ case kCastText:
+ // for mactext, we can expand now, but we can't shrink. so we may pass the small size when we have adjustToFit text style
+ if (_textType == kTextTypeAdjustToFit) {
+ dims.right = MIN<int>(dims.right, dims.left + _initialRect.width());
+ dims.bottom = MIN<int>(dims.bottom, dims.top + _initialRect.height());
+ } else if (_textType == kTextTypeFixed){
+ // use initialRect to create widget for fixed style text, this maybe related to version.
+ dims.right = MAX<int>(dims.right, dims.left + _initialRect.width());
+ dims.bottom = MAX<int>(dims.bottom, dims.top + _initialRect.height());
+ }
+ widget = new Graphics::MacText(g_director->getCurrentWindow(), bbox.left, bbox.top, dims.width(), dims.height(), g_director->_wm, _ftext, macFont, getForeColor(), getBackColor(), _initialRect.width(), getAlignment(), _lineSpacing, _borderSize, _gutterSize, _boxShadow, _textShadow, _textType == kTextTypeFixed);
+ ((Graphics::MacText *)widget)->setSelRange(g_director->getCurrentMovie()->_selStart, g_director->getCurrentMovie()->_selEnd);
+ ((Graphics::MacText *)widget)->setEditable(channel->_sprite->_editable);
+ ((Graphics::MacText *)widget)->draw();
+
+ // since we disable the ability of setActive in setEdtiable, then we need to set active widget manually
+ if (channel->_sprite->_editable) {
+ Graphics::MacWidget *activeWidget = g_director->_wm->getActiveWidget();
+ if (activeWidget == nullptr || !activeWidget->isEditable())
+ g_director->_wm->setActiveWidget(widget);
+ }
+ break;
+
+ case kCastButton:
+ // note that we use _initialRect for the dimensions of the button;
+ // the values provided in the sprite bounding box are ignored
+ widget = new Graphics::MacButton(Graphics::MacButtonType(buttonType), getAlignment(), g_director->getCurrentWindow(), bbox.left, bbox.top, _initialRect.width(), _initialRect.height(), g_director->_wm, _ftext, macFont, getForeColor(), g_director->_wm->_colorWhite);
+ widget->_focusable = true;
+
+ ((Graphics::MacButton *)widget)->setHilite(_hilite);
+ ((Graphics::MacButton *)widget)->setCheckBoxType(g_director->getCurrentMovie()->_checkBoxType);
+ ((Graphics::MacButton *)widget)->draw();
+ break;
+
+ default:
+ break;
+ }
+
+ delete macFont;
+ return widget;
+}
+
+void TextCastMember::importRTE(byte *text) {
+ //assert(rteList.size() == 3);
+ //child0 is probably font data.
+ //child1 is the raw text.
+ _rtext = _ptext = _ftext = Common::String((char*)text);
+ //child2 is positional?
+}
+
+void TextCastMember::setRawText(const Common::String &text) {
+ // Do nothing if text did not change
+ if (_rtext.equals(text))
+ return;
+
+ _rtext = text;
+ _ptext = Common::U32String(text);
+
+ // If text has changed, use the cached formatting from first STXT in this castmember.
+ Common::U32String formatting = Common::String::format("\001\016%04x%02x%04x%04x%04x%04x", _fontId, _textSlant, _fontSize, _fgpalinfo1, _fgpalinfo2, _fgpalinfo3);
+ _ftext = formatting + _ptext;
+ _modified = true;
+}
+
+// D4 dictionary book said this is line spacing
+int TextCastMember::getTextHeight() {
+ if (_widget)
+ return ((Graphics::MacText *)_widget)->getLineSpacing();
+ else
+ return _lineSpacing;
+ return 0;
+}
+
+// this should be amend when we have some where using this function
+int TextCastMember::getTextSize() {
+ if (_widget)
+ return ((Graphics::MacText *)_widget)->getTextSize();
+ else
+ return _fontSize;
+ return 0;
+}
+
+Common::U32String TextCastMember::getText() {
+ return _ptext;
+}
+
+Common::String TextCastMember::getRawText() {
+ return _rtext;
+}
+
+void TextCastMember::setTextSize(int textSize) {
+ if (_widget) {
+ ((Graphics::MacText *)_widget)->setTextSize(textSize);
+ ((Graphics::MacText *)_widget)->draw();
+ } else {
+ _fontSize = textSize;
+ _modified = true;
+ }
+}
+
+void TextCastMember::updateFromWidget(Graphics::MacWidget *widget) {
+ if (widget && _type == kCastText) {
+ _ptext = ((Graphics::MacText *)widget)->getEditedString();
+ }
+}
+
+Common::String TextCastMember::formatInfo() {
+ Common::String format = formatStringForDump(_ptext.encode());
+
+ return Common::String::format(
+ "initialRect: %dx%d@%d,%d, boundingRect: %dx%d@%d,%d, foreColor: %d, backColor: %d, editable: %d, text: \"%s\"",
+ _initialRect.width(), _initialRect.height(),
+ _initialRect.left, _initialRect.top,
+ _boundingRect.width(), _boundingRect.height(),
+ _boundingRect.left, _boundingRect.top,
+ getForeColor(), getBackColor(),
+ _editable, format.c_str()
+ );
+
+}
+
+bool TextCastMember::hasField(int field) {
+ switch (field) {
+ case kTheHilite:
+ case kTheText:
+ case kTheTextAlign:
+ case kTheTextFont:
+ case kTheTextHeight:
+ case kTheTextSize:
+ case kTheTextStyle:
+ return true;
+ default:
+ break;
+ }
+ return CastMember::hasField(field);
+}
+
+Datum TextCastMember::getField(int field) {
+ Datum d;
+
+ switch (field) {
+ case kTheHilite:
+ d = _hilite;
+ break;
+ case kTheText:
+ d = getText().encode(Common::kUtf8);
+ break;
+ case kTheTextAlign:
+ d.type = STRING;
+ switch (_textAlign) {
+ case kTextAlignLeft:
+ d.u.s = new Common::String("left");
+ break;
+ case kTextAlignCenter:
+ d.u.s = new Common::String("center");
+ break;
+ case kTextAlignRight:
+ d.u.s = new Common::String("right");
+ break;
+ default:
+ warning("TextCastMember::getField(): Invalid text align spec");
+ break;
+ }
+ break;
+ case kTheTextFont:
+ d.type = STRING;
+ d.u.s = new Common::String(g_director->_wm->_fontMan->getFontName(_fontId));
+ break;
+ case kTheTextHeight:
+ d = getTextHeight();
+ break;
+ case kTheTextSize:
+ d = getTextSize();
+ break;
+ case kTheTextStyle:
+ d = (int)_textSlant;
+ break;
+ default:
+ d = CastMember::getField(field);
+ }
+
+ return d;
+}
+
+bool TextCastMember::setField(int field, const Datum &d) {
+ Channel *toEdit = nullptr;
+
+ if (field == kTheTextFont || field == kTheTextSize || field == kTheTextStyle) {
+ Common::Array<Channel *> channels = g_director->getCurrentMovie()->getScore()->_channels;
+ for (uint i = 0; i < channels.size(); i++) {
+ if (channels[i]->_sprite->_cast == this) {
+ toEdit = channels[i];
+ break;
+ }
+ }
+ if (toEdit) {
+ Common::Rect bbox = toEdit->getBbox();
+ if (!toEdit->_widget)
+ toEdit->_widget = createWidget(bbox, toEdit, toEdit->_sprite->_spriteType);
+ }
+ }
+
+ switch (field) {
+ case kTheBackColor:
+ {
+ uint32 color = g_director->transformColor(d.asInt());
+ setColors(nullptr, &color);
+ }
+ return true;
+ case kTheForeColor:
+ {
+ uint32 color = g_director->transformColor(d.asInt());
+ setColors(&color, nullptr);
+ }
+ return true;
+ case kTheHilite:
+ // TODO: Understand how texts can be selected programmatically as well.
+ // since hilite won't affect text castmember, and we may have button info in text cast in D2/3. so don't check type here
+ _hilite = (bool)d.asInt();
+ _modified = true;
+ return true;
+ break;
+ case kTheText:
+ setRawText(d.asString());
+ return true;
+ case kTheTextAlign:
+ {
+ Common::String select = d.asString(true);
+ select.toLowercase();
+
+ TextAlignType align;
+ if (select == "\"left\"") {
+ align = kTextAlignLeft;
+ } else if (select == "\"center\"") {
+ align = kTextAlignCenter;
+ } else if (select == "\"right\"") {
+ align = kTextAlignRight;
+ } else {
+ warning("TextCastMember::setField(): Unknown text align spec: %s", d.asString(true).c_str());
+ break;
+ }
+
+ _textAlign = align;
+ _modified = true;
+ }
+ return true;
+ case kTheTextFont:
+ if (!toEdit) {
+ warning("Channel containing this CastMember %d doesn't exist", (int) _castId);
+ return false;
+ }
+ ((Graphics::MacText *)toEdit->_widget)->enforceTextFont((uint16) g_director->_wm->_fontMan->getFontIdByName(d.asString()));
+ _ptext = ((Graphics::MacText *)toEdit->_widget)->getPlainText();
+ _ftext = ((Graphics::MacText *)toEdit->_widget)->getTextChunk(0, 0, -1, -1, true);
+ return true;
+ case kTheTextHeight:
+ _lineSpacing = d.asInt();
+ _modified = true;
+ return false;
+ case kTheTextSize:
+ if (!toEdit) {
+ warning("Channel containing this CastMember %d doesn't exist", (int) _castId);
+ return false;
+ }
+ ((Graphics::MacText *)toEdit->_widget)->setTextSize(d.asInt());
+ _ptext = ((Graphics::MacText *)toEdit->_widget)->getPlainText();
+ _ftext = ((Graphics::MacText *)toEdit->_widget)->getTextChunk(0, 0, -1, -1, true);
+ return true;
+ case kTheTextStyle:
+ if (!toEdit) {
+ warning("Channel containing this CastMember %d doesn't exist", (int) _castId);
+ return false;
+ }
+ {
+ int slant = g_director->_wm->_fontMan->parseSlantFromName(d.asString());
+ ((Graphics::MacText *)toEdit->_widget)->enforceTextSlant(slant);
+ }
+ _ptext = ((Graphics::MacText *)toEdit->_widget)->getPlainText();
+ _ftext = ((Graphics::MacText *)toEdit->_widget)->getTextChunk(0, 0, -1, -1, true);
+ return true;
+ default:
+ break;
+ }
+
+ return CastMember::setField(field, d);
+}
+
+bool TextCastMember::hasChunkField(int field) {
+ switch (field) {
+ case kTheForeColor:
+ case kTheTextFont:
+ case kTheTextHeight:
+ case kTheTextSize:
+ case kTheTextStyle:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+Datum TextCastMember::getChunkField(int field, int start, int end) {
+ Datum d;
+
+ Graphics::MacText *macText = ((Graphics::MacText *)_widget);
+ if (!_widget)
+ warning("TextCastMember::getChunkField getting chunk field when there is no linked widget, returning the default value");
+
+ switch (field) {
+ case kTheForeColor:
+ if (_widget)
+ d.u.i = macText->getTextColor(start, end);
+ else
+ d.u.i = getForeColor();
+ break;
+ case kTheTextFont: {
+ int fontId;
+ if (_widget)
+ fontId = macText->getTextFont(start, end);
+ else
+ fontId = _fontId;
+
+ d.type = STRING;
+ d.u.s = new Common::String(g_director->_wm->_fontMan->getFontName(fontId));
+ break;
+ }
+ case kTheTextHeight:
+ warning("TextCastMember::getChunkField getting text height(line spacing) is not implemented yet, returning the default one");
+ d.u.i = _lineSpacing;
+ break;
+ case kTheTextSize:
+ if (_widget)
+ d.u.i = macText->getTextSize(start, end);
+ else
+ d.u.i = _fontSize;
+ break;
+ case kTheTextStyle:
+ if (_widget)
+ d.u.i = macText->getTextSlant(start, end);
+ else
+ d.u.i = _textSlant;
+ break;
+ default:
+ break;
+ }
+
+ return d;
+}
+
+bool TextCastMember::setChunkField(int field, int start, int end, const Datum &d) {
+ Graphics::MacText *macText = ((Graphics::MacText *)_widget);
+ if (!_widget)
+ warning("TextCastMember::setChunkField setting chunk field when there is no linked widget");
+
+ switch (field) {
+ case kTheForeColor:
+ if (_widget)
+ macText->setTextColor(d.asInt(), start, end);
+ return true;
+ case kTheTextFont:
+ if (_widget)
+ macText->setTextFont(d.asInt(), start, end);
+ return true;
+ case kTheTextHeight:
+ warning("TextCastMember::setChunkField setting text height(line spacing) is not implemented yet");
+ return false;
+ case kTheTextSize:
+ if (_widget)
+ macText->setTextSize(d.asInt(), start, end);
+ return true;
+ case kTheTextStyle:
+ if (_widget)
+ macText->setTextSlant(d.asInt(), start, end);
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+RTECastMember::RTECastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version)
+ : TextCastMember(cast, castId, stream, version) {
+
+ _type = kCastRTE;
+}
+
+void RTECastMember::loadChunks() {
+ //TODO: Actually load RTEs correctly, don't just make fake STXT.
+#if 0
+ Common::SeekableReadStream *rte1 = _movieArchive->getResource(res->children[child].tag, res->children[child].index);
+ byte *buffer = new byte[rte1->size() + 2];
+ rte1->read(buffer, rte1->size());
+ buffer[rte1->size()] = '\n';
+ buffer[rte1->size() + 1] = '\0';
+ _loadedText->getVal(id)->importRTE(buffer);
+
+ delete rte1;
+#endif
+}
+
+}
diff --git a/engines/director/castmember/text.h b/engines/director/castmember/text.h
new file mode 100644
index 00000000000..a579bb51e68
--- /dev/null
+++ b/engines/director/castmember/text.h
@@ -0,0 +1,105 @@
+/* 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 Director {
+
+class TextCastMember : public CastMember {
+public:
+ TextCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version, uint8 flags1 = 0, bool asButton = false);
+ void setColors(uint32 *fgcolor, uint32 *bgcolor) override;
+
+ Graphics::MacWidget *createWidget(Common::Rect &bbox, Channel *channel, SpriteType spriteType) override;
+
+ bool isEditable() override { return _editable; }
+ void setEditable(bool editable) override { _editable = editable; }
+ void updateFromWidget(Graphics::MacWidget *widget) override;
+ Graphics::TextAlign getAlignment();
+
+ uint32 getBackColor() override { return _bgcolor; }
+ void setBackColor(uint32 bgCol) override;
+ uint32 getForeColor() override { return _fgcolor; }
+ void setForeColor(uint32 fgCol) override;
+
+ bool hasField(int field) override;
+ Datum getField(int field) override;
+ bool 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 getTextHeight();
+
+ int getTextSize();
+ void setTextSize(int textSize);
+
+ Common::String formatInfo() override;
+
+ SizeType _borderSize;
+ SizeType _gutterSize;
+ SizeType _boxShadow;
+ uint16 _maxHeight;
+ uint16 _textHeight;
+
+ uint32 _fontId;
+ uint16 _fontSize;
+ TextType _textType;
+ TextAlignType _textAlign;
+ SizeType _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:
+ uint32 _bgcolor;
+ uint32 _fgcolor;
+};
+
+class RTECastMember : public TextCastMember {
+public:
+ RTECastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version);
+
+ void loadChunks();
+};
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/channel.cpp b/engines/director/channel.cpp
index d4a43dc5136..89d25373241 100644
--- a/engines/director/channel.cpp
+++ b/engines/director/channel.cpp
@@ -26,9 +26,12 @@
#include "director/cast.h"
#include "director/channel.h"
#include "director/sprite.h"
-#include "director/castmember.h"
#include "director/types.h"
#include "director/window.h"
+#include "director/castmember/castmember.h"
+#include "director/castmember/bitmap.h"
+#include "director/castmember/digitalvideo.h"
+#include "director/castmember/filmloop.h"
#include "graphics/macgui/mactext.h"
#include "graphics/macgui/macbutton.h"
diff --git a/engines/director/cursor.cpp b/engines/director/cursor.cpp
index f705ce3678e..df7b63e1c44 100644
--- a/engines/director/cursor.cpp
+++ b/engines/director/cursor.cpp
@@ -26,7 +26,7 @@
#include "director/cast.h"
#include "director/cursor.h"
#include "director/movie.h"
-#include "director/castmember.h"
+#include "director/castmember/bitmap.h"
#include "director/picture.h"
namespace Director {
diff --git a/engines/director/debugger.cpp b/engines/director/debugger.cpp
index 17fffd89b03..248e91ddfdc 100644
--- a/engines/director/debugger.cpp
+++ b/engines/director/debugger.cpp
@@ -23,8 +23,8 @@
#include "common/platform.h"
#include "director/director.h"
#include "director/debugger.h"
+#include "director/archive.h"
#include "director/cast.h"
-#include "director/castmember.h"
#include "director/frame.h"
#include "director/movie.h"
#include "director/score.h"
diff --git a/engines/director/events.cpp b/engines/director/events.cpp
index c4c71be273b..e22dc8ce464 100644
--- a/engines/director/events.cpp
+++ b/engines/director/events.cpp
@@ -35,7 +35,7 @@
#include "director/channel.h"
#include "director/sprite.h"
#include "director/window.h"
-#include "director/castmember.h"
+#include "director/castmember/castmember.h"
#include "director/lingo/lingo.h"
namespace Director {
diff --git a/engines/director/graphics.cpp b/engines/director/graphics.cpp
index cf5d065521d..575b79a9577 100644
--- a/engines/director/graphics.cpp
+++ b/engines/director/graphics.cpp
@@ -25,11 +25,11 @@
#include "director/director.h"
#include "director/cast.h"
-#include "director/castmember.h"
#include "director/movie.h"
#include "director/images.h"
#include "director/picture.h"
#include "director/window.h"
+#include "director/castmember/bitmap.h"
namespace Director {
diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index 64dd09278fd..66ecea3ecdb 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -30,7 +30,6 @@
#include "director/director.h"
#include "director/cast.h"
-#include "director/castmember.h"
#include "director/frame.h"
#include "director/movie.h"
#include "director/score.h"
@@ -41,6 +40,10 @@
#include "director/window.h"
#include "director/stxt.h"
#include "director/util.h"
+#include "director/castmember/castmember.h"
+#include "director/castmember/bitmap.h"
+#include "director/castmember/palette.h"
+#include "director/castmember/text.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-builtins.h"
#include "director/lingo/lingo-code.h"
diff --git a/engines/director/lingo/lingo-bytecode.cpp b/engines/director/lingo/lingo-bytecode.cpp
index adb6b30e540..051adf34bff 100644
--- a/engines/director/lingo/lingo-bytecode.cpp
+++ b/engines/director/lingo/lingo-bytecode.cpp
@@ -26,10 +26,11 @@
#include "director/director.h"
#include "director/cast.h"
-#include "director/castmember.h"
#include "director/movie.h"
#include "director/util.h"
#include "director/window.h"
+#include "director/castmember/castmember.h"
+#include "director/castmember/script.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-code.h"
#include "director/lingo/lingo-codegen.h"
diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp
index 7fdaf18e965..21243a174d9 100644
--- a/engines/director/lingo/lingo-code.cpp
+++ b/engines/director/lingo/lingo-code.cpp
@@ -46,7 +46,6 @@
#include "graphics/macgui/mactext.h"
#include "director/director.h"
-#include "director/castmember.h"
#include "director/movie.h"
#include "director/score.h"
#include "director/sprite.h"
@@ -54,6 +53,7 @@
#include "director/cursor.h"
#include "director/channel.h"
#include "director/util.h"
+#include "director/castmember/castmember.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-builtins.h"
#include "director/lingo/lingo-code.h"
diff --git a/engines/director/lingo/lingo-funcs.cpp b/engines/director/lingo/lingo-funcs.cpp
index e0add1a92f0..58e8c883609 100644
--- a/engines/director/lingo/lingo-funcs.cpp
+++ b/engines/director/lingo/lingo-funcs.cpp
@@ -28,7 +28,7 @@
#include "graphics/macgui/macwindowmanager.h"
#include "director/director.h"
-#include "director/castmember.h"
+#include "director/archive.h"
#include "director/cursor.h"
#include "director/movie.h"
#include "director/score.h"
diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index b4394714a79..b925208091c 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -24,11 +24,7 @@
#include "graphics/macgui/mactext.h"
#include "director/director.h"
-#include "director/cast.h"
-#include "director/channel.h"
-#include "director/castmember.h"
#include "director/movie.h"
-#include "director/score.h"
#include "director/window.h"
#include "director/util.h"
#include "director/lingo/lingo.h"
@@ -676,621 +672,4 @@ void LM::m_moveToFront(int nargs) {
g_lingo->dropStack(nargs);
}
-// CastMember
-
-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();
-}
-
-bool CastMember::setProp(const Common::String &propName, const Datum &value) {
- Common::String fieldName = Common::String::format("%d%s", kTheCast, propName.c_str());
- if (g_lingo->_theEntityFields.contains(fieldName)) {
- return setField(g_lingo->_theEntityFields[fieldName]->field, value);
- }
-
- warning("CastMember::setProp: unknown property '%s'", propName.c_str());
- return false;
-}
-
-bool CastMember::hasField(int field) {
- switch (field) {
- case kTheBackColor:
- case kTheCastType:
- case kTheFileName:
- case kTheForeColor:
- case kTheHeight:
- case kTheLoaded:
- case kTheModified:
- case kTheName:
- case kTheNumber:
- case kTheRect:
- case kThePurgePriority:
- case kTheScriptText:
- case kTheSize:
- 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 kTheCastType:
- d.type = SYMBOL;
- d.u.s = new Common::String(castType2str(_type));
- 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 kTheLoaded:
- d = 1; // Not loaded handled in Lingo::getTheCast
- break;
- case kTheModified:
- d = (int)_isChanged;
- break;
- case kTheName:
- if (castInfo)
- d = Datum(castInfo->name);
- break;
- case kTheNumber:
- d = _castId;
- break;
- case kTheRect:
- // not sure get the initial rect would be fine to castmember
- d = Datum(_cast->getCastMember(_castId)->_initialRect);
- 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;
-}
-
-bool CastMember::setField(int field, const Datum &d) {
- CastMemberInfo *castInfo = _cast->getCastMemberInfo(_castId);
-
- switch (field) {
- case kTheBackColor:
- _cast->getCastMember(_castId)->setBackColor(d.asInt());
- return true;
- case kTheCastType:
- warning("BUILDBOT: CastMember::setField(): Attempt to set read-only field %s of cast %d", g_lingo->entity2str(field), _castId);
- return false;
- case kTheFileName:
- if (!castInfo) {
- warning("CastMember::setField(): CastMember info for %d not found", _castId);
- return false;
- }
- castInfo->fileName = d.asString();
- return true;
- case kTheForeColor:
- _cast->getCastMember(_castId)->setForeColor(d.asInt());
- return true;
- case kTheHeight:
- warning("BUILDBOT: CastMember::setField(): Attempt to set read-only field \"%s\" of cast %d", g_lingo->field2str(field), _castId);
- return false;
- case kTheName:
- if (!castInfo) {
- warning("CastMember::setField(): CastMember info for %d not found", _castId);
- return false;
- }
- castInfo->name = d.asString();
- return true;
- case kTheRect:
- warning("CastMember::setField(): Attempt to set read-only field \"%s\" of cast %d", g_lingo->field2str(field), _castId);
- return false;
- case kThePurgePriority:
- _purgePriority = CLIP<int>(d.asInt(), 0, 3);
- return true;
- case kTheScriptText:
- if (!castInfo) {
- warning("CastMember::setField(): CastMember info for %d not found", _castId);
- return false;
- }
- _cast->_lingoArchive->replaceCode(*d.u.s, kCastScript, _castId);
- castInfo->script = d.asString();
- return true;
- case kTheWidth:
- warning("BUILDBOT: CastMember::setField(): Attempt to set read-only field \"%s\" of cast %d", g_lingo->field2str(field), _castId);
- return false;
- default:
- warning("CastMember::setField(): Unprocessed setting field \"%s\" of cast %d", g_lingo->field2str(field), _castId);
- }
-
- return false;
-}
-
-bool DigitalVideoCastMember::hasField(int field) {
- switch (field) {
- case kTheCenter:
- case kTheController:
- case kTheCrop:
- case kTheDirectToStage:
- case kTheDuration:
- case kTheFrameRate:
- case kTheLoop:
- case kTheMovieRate:
- case kTheMovieTime:
- case kThePausedAtStart:
- case kThePreLoad:
- case kTheSound:
- case kTheVideo:
- case kTheVolume:
- 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 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 kTheVideo:
- d = _enableVideo;
- break;
- default:
- d = CastMember::getField(field);
- }
-
- return d;
-}
-
-bool DigitalVideoCastMember::setField(int field, const Datum &d) {
- switch (field) {
- case kTheCenter:
- _center = (bool)d.asInt();
- return true;
- case kTheController:
- _showControls = (bool)d.asInt();
- return true;
- case kTheCrop:
- _crop = (bool)d.asInt();
- return true;
- case kTheDirectToStage:
- _directToStage = (bool)d.asInt();
- return true;
- case kTheDuration:
- warning("DigitalVideoCastMember::setField(): Attempt to set read-only field %s of cast %d", g_lingo->entity2str(field), _castId);
- return false;
- case kTheFrameRate:
- _frameRate = d.asInt();
- setFrameRate(d.asInt());
- return true;
- case kTheLoop:
- _looping = (bool)d.asInt();
- if (_looping && _channel && _channel->_movieRate == 0.0) {
- setMovieRate(1.0);
- }
- return true;
- case kThePausedAtStart:
- _pausedAtStart = (bool)d.asInt();
- return true;
- case kThePreLoad:
- _preload = (bool)d.asInt();
- return true;
- case kTheSound:
- _enableSound = (bool)d.asInt();
- return true;
- case kTheVideo:
- _enableVideo = (bool)d.asInt();
- return true;
- default:
- break;
- }
-
- return CastMember::setField(field, d);
-}
-
-bool BitmapCastMember::hasField(int field) {
- switch (field) {
- case kTheDepth:
- case kTheRegPoint:
- case kThePalette:
- case kThePicture:
- return true;
- default:
- break;
- }
- return CastMember::hasField(field);
-}
-
-Datum BitmapCastMember::getField(int field) {
- Datum d;
-
- switch (field) {
- case kTheDepth:
- d = _bitsPerPixel;
- break;
- case kTheRegPoint:
- d.type = POINT;
- d.u.farr = new FArray;
- d.u.farr->arr.push_back(_regX);
- d.u.farr->arr.push_back(_regY);
- break;
- case kThePalette:
- d = _clut;
- break;
- case kThePicture:
- d.type = PICTUREREF;
- d.u.picture = getPicture();
- break;
- default:
- d = CastMember::getField(field);
- }
-
- return d;
-}
-
-bool BitmapCastMember::setField(int field, const Datum &d) {
- switch (field) {
- case kTheDepth:
- warning("BitmapCastMember::setField(): Attempt to set read-only field %s of cast %d", g_lingo->field2str(field), _castId);
- return false;
- case kTheRegPoint:
- if (d.type == POINT || (d.type == ARRAY && d.u.farr->arr.size() >= 2)) {
- Score *score = g_director->getCurrentMovie()->getScore();
- score->invalidateRectsForMember(this);
- _regX = d.u.farr->arr[0].asInt();
- _regY = d.u.farr->arr[1].asInt();
- _modified = true;
- } else {
- warning("BitmapCastMember::setField(): Wrong Datum type %d for kTheRegPoint", d.type);
- return false;
- }
- return true;
- case kThePalette:
- _clut = d.asInt();
- return true;
- case kThePicture:
- if (d.type == PICTUREREF && d.u.picture != nullptr) {
- setPicture(*d.u.picture);
- return true;
- } else {
- warning("BitmapCastMember::setField(): Wrong Datum type %d for kThePicture (or nullptr)", d.type);
- }
- return false;
- default:
- break;
- }
-
- return CastMember::setField(field, d);
-}
-
-bool TextCastMember::hasField(int field) {
- switch (field) {
- case kTheHilite:
- case kTheText:
- case kTheTextAlign:
- case kTheTextFont:
- case kTheTextHeight:
- case kTheTextSize:
- case kTheTextStyle:
- return true;
- default:
- break;
- }
- return CastMember::hasField(field);
-}
-
-Datum TextCastMember::getField(int field) {
- Datum d;
-
- switch (field) {
- case kTheHilite:
- d = _hilite;
- break;
- case kTheText:
- d = getText().encode(Common::kUtf8);
- break;
- case kTheTextAlign:
- d.type = STRING;
- switch (_textAlign) {
- case kTextAlignLeft:
- d.u.s = new Common::String("left");
- break;
- case kTextAlignCenter:
- d.u.s = new Common::String("center");
- break;
- case kTextAlignRight:
- d.u.s = new Common::String("right");
- break;
- default:
- warning("TextCastMember::getField(): Invalid text align spec");
- break;
- }
- break;
- case kTheTextFont:
- d.type = STRING;
- d.u.s = new Common::String(g_director->_wm->_fontMan->getFontName(_fontId));
- break;
- case kTheTextHeight:
- d = getTextHeight();
- break;
- case kTheTextSize:
- d = getTextSize();
- break;
- case kTheTextStyle:
- d = (int)_textSlant;
- break;
- default:
- d = CastMember::getField(field);
- }
-
- return d;
-}
-
-bool TextCastMember::setField(int field, const Datum &d) {
- Channel *toEdit = nullptr;
-
- if (field == kTheTextFont || field == kTheTextSize || field == kTheTextStyle) {
- Common::Array<Channel *> channels = g_director->getCurrentMovie()->getScore()->_channels;
- for (uint i = 0; i < channels.size(); i++) {
- if (channels[i]->_sprite->_cast == this) {
- toEdit = channels[i];
- break;
- }
- }
- if (toEdit) {
- Common::Rect bbox = toEdit->getBbox();
- if (!toEdit->_widget)
- toEdit->_widget = createWidget(bbox, toEdit, toEdit->_sprite->_spriteType);
- }
- }
-
- switch (field) {
- case kTheBackColor:
- {
- uint32 color = g_director->transformColor(d.asInt());
- setColors(nullptr, &color);
- }
- return true;
- case kTheForeColor:
- {
- uint32 color = g_director->transformColor(d.asInt());
- setColors(&color, nullptr);
- }
- return true;
- case kTheHilite:
- // TODO: Understand how texts can be selected programmatically as well.
- // since hilite won't affect text castmember, and we may have button info in text cast in D2/3. so don't check type here
- _hilite = (bool)d.asInt();
- _modified = true;
- return true;
- break;
- case kTheText:
- setRawText(d.asString());
- return true;
- case kTheTextAlign:
- {
- Common::String select = d.asString(true);
- select.toLowercase();
-
- TextAlignType align;
- if (select == "\"left\"") {
- align = kTextAlignLeft;
- } else if (select == "\"center\"") {
- align = kTextAlignCenter;
- } else if (select == "\"right\"") {
- align = kTextAlignRight;
- } else {
- warning("TextCastMember::setField(): Unknown text align spec: %s", d.asString(true).c_str());
- break;
- }
-
- _textAlign = align;
- _modified = true;
- }
- return true;
- case kTheTextFont:
- if (!toEdit) {
- warning("Channel containing this CastMember %d doesn't exist", (int) _castId);
- return false;
- }
- ((Graphics::MacText *)toEdit->_widget)->enforceTextFont((uint16) g_director->_wm->_fontMan->getFontIdByName(d.asString()));
- _ptext = ((Graphics::MacText *)toEdit->_widget)->getPlainText();
- _ftext = ((Graphics::MacText *)toEdit->_widget)->getTextChunk(0, 0, -1, -1, true);
- return true;
- case kTheTextHeight:
- _lineSpacing = d.asInt();
- _modified = true;
- return false;
- case kTheTextSize:
- if (!toEdit) {
- warning("Channel containing this CastMember %d doesn't exist", (int) _castId);
- return false;
- }
- ((Graphics::MacText *)toEdit->_widget)->setTextSize(d.asInt());
- _ptext = ((Graphics::MacText *)toEdit->_widget)->getPlainText();
- _ftext = ((Graphics::MacText *)toEdit->_widget)->getTextChunk(0, 0, -1, -1, true);
- return true;
- case kTheTextStyle:
- if (!toEdit) {
- warning("Channel containing this CastMember %d doesn't exist", (int) _castId);
- return false;
- }
- {
- int slant = g_director->_wm->_fontMan->parseSlantFromName(d.asString());
- ((Graphics::MacText *)toEdit->_widget)->enforceTextSlant(slant);
- }
- _ptext = ((Graphics::MacText *)toEdit->_widget)->getPlainText();
- _ftext = ((Graphics::MacText *)toEdit->_widget)->getTextChunk(0, 0, -1, -1, true);
- return true;
- default:
- break;
- }
-
- return CastMember::setField(field, d);
-}
-
-bool TextCastMember::hasChunkField(int field) {
- switch (field) {
- case kTheForeColor:
- case kTheTextFont:
- case kTheTextHeight:
- case kTheTextSize:
- case kTheTextStyle:
- return true;
- default:
- break;
- }
- return false;
-}
-
-Datum TextCastMember::getChunkField(int field, int start, int end) {
- Datum d;
-
- Graphics::MacText *macText = ((Graphics::MacText *)_widget);
- if (!_widget)
- warning("TextCastMember::getChunkField getting chunk field when there is no linked widget, returning the default value");
-
- switch (field) {
- case kTheForeColor:
- if (_widget)
- d.u.i = macText->getTextColor(start, end);
- else
- d.u.i = getForeColor();
- break;
- case kTheTextFont: {
- int fontId;
- if (_widget)
- fontId = macText->getTextFont(start, end);
- else
- fontId = _fontId;
-
- d.type = STRING;
- d.u.s = new Common::String(g_director->_wm->_fontMan->getFontName(fontId));
- break;
- }
- case kTheTextHeight:
- warning("TextCastMember::getChunkField getting text height(line spacing) is not implemented yet, returning the default one");
- d.u.i = _lineSpacing;
- break;
- case kTheTextSize:
- if (_widget)
- d.u.i = macText->getTextSize(start, end);
- else
- d.u.i = _fontSize;
- break;
- case kTheTextStyle:
- if (_widget)
- d.u.i = macText->getTextSlant(start, end);
- else
- d.u.i = _textSlant;
- break;
- default:
- break;
- }
-
- return d;
-}
-
-bool TextCastMember::setChunkField(int field, int start, int end, const Datum &d) {
- Graphics::MacText *macText = ((Graphics::MacText *)_widget);
- if (!_widget)
- warning("TextCastMember::setChunkField setting chunk field when there is no linked widget");
-
- switch (field) {
- case kTheForeColor:
- if (_widget)
- macText->setTextColor(d.asInt(), start, end);
- return true;
- case kTheTextFont:
- if (_widget)
- macText->setTextFont(d.asInt(), start, end);
- return true;
- case kTheTextHeight:
- warning("TextCastMember::setChunkField setting text height(line spacing) is not implemented yet");
- return false;
- case kTheTextSize:
- if (_widget)
- macText->setTextSize(d.asInt(), start, end);
- return true;
- case kTheTextStyle:
- if (_widget)
- macText->setTextSlant(d.asInt(), start, end);
- return true;
- default:
- break;
- }
-
- return false;
-}
-
} // End of namespace Director
diff --git a/engines/director/lingo/lingo-the.cpp b/engines/director/lingo/lingo-the.cpp
index cd249334012..922a682fe8c 100644
--- a/engines/director/lingo/lingo-the.cpp
+++ b/engines/director/lingo/lingo-the.cpp
@@ -27,7 +27,6 @@
#include "director/director.h"
#include "director/cast.h"
-#include "director/castmember.h"
#include "director/cursor.h"
#include "director/channel.h"
#include "director/frame.h"
@@ -36,6 +35,9 @@
#include "director/sprite.h"
#include "director/score.h"
#include "director/window.h"
+#include "director/castmember/castmember.h"
+#include "director/castmember/digitalvideo.h"
+#include "director/castmember/text.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-builtins.h"
#include "director/lingo/lingo-code.h"
diff --git a/engines/director/lingo/lingo.cpp b/engines/director/lingo/lingo.cpp
index bea80135316..b645712a606 100644
--- a/engines/director/lingo/lingo.cpp
+++ b/engines/director/lingo/lingo.cpp
@@ -26,7 +26,6 @@
#include "director/director.h"
#include "director/cast.h"
-#include "director/castmember.h"
#include "director/frame.h"
#include "director/movie.h"
#include "director/picture.h"
@@ -34,6 +33,8 @@
#include "director/sprite.h"
#include "director/window.h"
#include "director/util.h"
+#include "director/castmember/castmember.h"
+#include "director/castmember/text.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-ast.h"
diff --git a/engines/director/module.mk b/engines/director/module.mk
index 46f413d11f9..c47a2051064 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -3,7 +3,6 @@ MODULE := engines/director
MODULE_OBJS = \
archive.o \
cast.o \
- castmember.o \
channel.o \
cursor.o \
director.o \
@@ -27,6 +26,16 @@ MODULE_OBJS = \
types.o \
util.o \
window.o \
+ castmember/castmember.o \
+ castmember/bitmap.o \
+ castmember/digitalvideo.o \
+ castmember/filmloop.o \
+ castmember/movie.o \
+ castmember/palette.o \
+ castmember/script.o \
+ castmember/shape.o \
+ castmember/sound.o \
+ castmember/text.o \
lingo/lingo.o \
lingo/lingo-builtins.o \
lingo/lingo-bytecode.o \
diff --git a/engines/director/resource.cpp b/engines/director/resource.cpp
index 85684872086..c218c5547af 100644
--- a/engines/director/resource.cpp
+++ b/engines/director/resource.cpp
@@ -30,8 +30,8 @@
#include "graphics/wincursor.h"
#include "director/director.h"
+#include "director/archive.h"
#include "director/cast.h"
-#include "director/castmember.h"
#include "director/movie.h"
#include "director/window.h"
#include "director/lingo/lingo.h"
diff --git a/engines/director/score.cpp b/engines/director/score.cpp
index 46361104600..7aba55f2c71 100644
--- a/engines/director/score.cpp
+++ b/engines/director/score.cpp
@@ -38,7 +38,6 @@
#include "director/director.h"
#include "director/cast.h"
-#include "director/castmember.h"
#include "director/score.h"
#include "director/frame.h"
#include "director/movie.h"
@@ -48,6 +47,8 @@
#include "director/sprite.h"
#include "director/window.h"
#include "director/util.h"
+#include "director/castmember/castmember.h"
+#include "director/castmember/palette.h"
#include "director/lingo/lingo.h"
namespace Director {
diff --git a/engines/director/score.h b/engines/director/score.h
index 28b556fcd45..f4a508084f5 100644
--- a/engines/director/score.h
+++ b/engines/director/score.h
@@ -46,7 +46,6 @@ class Archive;
class DirectorEngine;
class DirectorSound;
class Frame;
-struct Label;
class Movie;
struct Resource;
class Cursor;
@@ -60,6 +59,13 @@ enum RenderMode {
kRenderForceUpdate
};
+struct Label {
+ Common::String comment;
+ Common::String name;
+ uint16 number;
+ Label(Common::String name1, uint16 number1, Common::String comment1) { name = name1; number = number1; comment = comment1;}
+};
+
class Score {
public:
Score(Movie *movie);
diff --git a/engines/director/sound.cpp b/engines/director/sound.cpp
index 3c5210adea9..3869cfb10a5 100644
--- a/engines/director/sound.cpp
+++ b/engines/director/sound.cpp
@@ -35,10 +35,10 @@
#include "director/director.h"
#include "director/movie.h"
-#include "director/castmember.h"
#include "director/sound.h"
#include "director/util.h"
#include "director/window.h"
+#include "director/castmember/sound.h"
namespace Director {
diff --git a/engines/director/sprite.cpp b/engines/director/sprite.cpp
index 745172ca217..a3c33b5cbcc 100644
--- a/engines/director/sprite.cpp
+++ b/engines/director/sprite.cpp
@@ -22,11 +22,13 @@
#include "graphics/macgui/macwidget.h"
#include "director/director.h"
-#include "director/castmember.h"
#include "director/frame.h"
#include "director/movie.h"
#include "director/score.h"
#include "director/sprite.h"
+#include "director/castmember/castmember.h"
+#include "director/castmember/bitmap.h"
+#include "director/castmember/shape.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
diff --git a/engines/director/window.cpp b/engines/director/window.cpp
index 45d280f137d..bafb880a168 100644
--- a/engines/director/window.cpp
+++ b/engines/director/window.cpp
@@ -27,17 +27,18 @@
#include "graphics/macgui/macwindowmanager.h"
#include "director/director.h"
+#include "director/archive.h"
#include "director/cast.h"
#include "director/lingo/lingo.h"
#include "director/movie.h"
#include "director/window.h"
#include "director/score.h"
-#include "director/castmember.h"
#include "director/cursor.h"
#include "director/channel.h"
#include "director/sound.h"
#include "director/sprite.h"
#include "director/util.h"
+#include "director/castmember/castmember.h"
namespace Director {
Commit: e289ea58962870a719179eb736c7b678eec6d707
https://github.com/scummvm/scummvm/commit/e289ea58962870a719179eb736c7b678eec6d707
Author: Scott Percival (code at moral.net.au)
Date: 2023-04-29T14:20:05+02:00
Commit Message:
DIRECTOR: Remove shared cast check from Cast::loadBitmapData()
This is a holdover from the original code, where the cast members
were loaded as part of the Score. Cast is now per-movie, so all the
resources should be in the one place.
Changed paths:
engines/director/cast.cpp
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index 301b26061fc..65bd24a9ce6 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -691,7 +691,6 @@ void Cast::loadBitmapData(int key, BitmapCastMember *bitmapCast) {
uint32 tag = bitmapCast->_tag;
uint16 imgId = key;
uint16 realId = 0;
- Cast *sharedCast = _movie ? _movie->getSharedCast() : nullptr;
Image::ImageDecoder *img = nullptr;
Common::SeekableReadStream *pic = nullptr;
@@ -703,8 +702,6 @@ void Cast::loadBitmapData(int key, BitmapCastMember *bitmapCast) {
if (_castArchive->hasResource(tag, imgId))
pic = _castArchive->getResource(tag, imgId);
- else if (sharedCast && sharedCast->getArchive()->hasResource(tag, imgId))
- pic = sharedCast->getArchive()->getResource(tag, imgId);
}
if ((pic == nullptr || pic->size() == 0)
@@ -766,10 +763,6 @@ void Cast::loadBitmapData(int key, BitmapCastMember *bitmapCast) {
bitmapCast->_tag = tag = ((BitmapCastMember *)_loadedCast->getVal(imgId))->_tag;
realId = imgId + _castIDoffset;
pic = _castArchive->getResource(tag, realId);
- } else if (sharedCast && sharedCast->_loadedCast && sharedCast->_loadedCast->contains(imgId)) {
- bitmapCast->_tag = tag = ((BitmapCastMember *)sharedCast->_loadedCast->getVal(imgId))->_tag;
- realId = imgId + sharedCast->_castIDoffset;
- pic = sharedCast->getArchive()->getResource(tag, realId);
}
}
Commit: 1fd0692f6b00b61daa435ce5b30969b8b7a25b17
https://github.com/scummvm/scummvm/commit/1fd0692f6b00b61daa435ce5b30969b8b7a25b17
Author: Scott Percival (code at moral.net.au)
Date: 2023-04-29T14:20:05+02:00
Commit Message:
DIRECTOR: Move load operations inside cast member
Changed paths:
engines/director/cast.cpp
engines/director/cast.h
engines/director/castmember/bitmap.cpp
engines/director/castmember/bitmap.h
engines/director/castmember/castmember.cpp
engines/director/castmember/castmember.h
engines/director/castmember/filmloop.cpp
engines/director/castmember/filmloop.h
engines/director/castmember/palette.cpp
engines/director/castmember/palette.h
engines/director/castmember/sound.cpp
engines/director/castmember/sound.h
engines/director/castmember/text.cpp
engines/director/castmember/text.h
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index 65bd24a9ce6..e0e6ebafcf7 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -23,17 +23,14 @@
#include "common/file.h"
#include "common/macresman.h"
#include "common/memstream.h"
+#include "common/stream.h"
#include "common/substream.h"
#include "graphics/macgui/macfontmanager.h"
#include "graphics/macgui/macwindowmanager.h"
-#include "image/bmp.h"
-#include "image/jpeg.h"
-#include "image/pict.h"
#include "director/director.h"
#include "director/cast.h"
-#include "director/images.h"
#include "director/movie.h"
#include "director/score.h"
#include "director/sound.h"
@@ -625,240 +622,6 @@ void Cast::loadCast() {
loadCastMemberData();
}
-void Cast::loadStxtData(int key, TextCastMember *member) {
- uint stxtid;
- if (_version >= kFileVer400 && member->_children.size() > 0)
- stxtid = member->_children[0].index;
- else
- stxtid = key;
-
- if (_loadedStxts->contains(stxtid)) {
- const Stxt *stxt = _loadedStxts->getVal(stxtid);
- member->importStxt(stxt);
- member->_size = stxt->_size;
- } else {
- warning("Cast::loadStxtData: stxtid %i isn't loaded", stxtid);
- }
-}
-
-void Cast::loadPaletteData(int key, PaletteCastMember *member) {
- // TODO: Verify how palettes work in >D4 versions
- int paletteId = 0;
- if (_version >= kFileVer400 && _version < kFileVer500 && member->_children.size() == 1) {
- paletteId = member->_children[0].index;
- } else if (_version < kFileVer400) {
- // For D3 and below, palette IDs are stored in the CLUT resource as cast ID + 1024
- paletteId = key + _castIDoffset;
- } else {
- warning("Cast::loadPaletteData(): Expected 1 child for palette cast, got %d", member->_children.size());
- }
- if (paletteId) {
- debugC(2, kDebugImages, "Cast::loadPaletteData(): linking palette id %d to cast index %d", paletteId, key);
- member->_palette = g_director->getPalette(paletteId);
- }
-}
-
-void Cast::loadFilmLoopData(int key, FilmLoopCastMember *member) {
- if (_version < kFileVer400) {
- // Director 3 and below should have a SCVW resource
- uint16 filmLoopId = key + _castIDoffset;
- uint32 tag = MKTAG('S', 'C', 'V', 'W');
- Common::SeekableReadStreamEndian *loop = _castArchive->getResource(tag, filmLoopId);
- debugC(2, kDebugLoading, "****** Loading '%s' id: %d, %d bytes", tag2str(tag), filmLoopId, (int)loop->size());
- member->loadFilmLoopData(*loop);
- delete loop;
- } else if (_version >= kFileVer400 && _version < kFileVer500) {
- if (member->_children.size() == 1) {
- uint16 filmLoopId = member->_children[0].index;
- uint32 tag = member->_children[0].tag;
- if (_castArchive->hasResource(tag, filmLoopId)) {
- Common::SeekableReadStreamEndian *loop = _castArchive->getResource(tag, filmLoopId);
- debugC(2, kDebugLoading, "****** Loading '%s' id: %d, %d bytes", tag2str(tag), filmLoopId, (int)loop->size());
- member->loadFilmLoopDataV4(*loop);
- delete loop;
- } else {
- warning("Cast::loadFilmLoopData(): Film loop not found");
- }
- } else {
- warning("Cast::loadFilmLoopData(): Expected 1 child for film loop cast, got %d", member->_children.size());
- }
- } else {
- warning("STUB: Cast::loadFilmLoopData(): Film loops not supported for version %d", _version);
- }
-}
-
-void Cast::loadBitmapData(int key, BitmapCastMember *bitmapCast) {
- uint32 tag = bitmapCast->_tag;
- uint16 imgId = key;
- uint16 realId = 0;
-
- Image::ImageDecoder *img = nullptr;
- Common::SeekableReadStream *pic = nullptr;
-
- if (_version >= kFileVer400) {
- if (bitmapCast->_children.size() > 0) {
- imgId = bitmapCast->_children[0].index;
- tag = bitmapCast->_children[0].tag;
-
- if (_castArchive->hasResource(tag, imgId))
- pic = _castArchive->getResource(tag, imgId);
- }
-
- if ((pic == nullptr || pic->size() == 0)
- && _castsInfo.contains(key) && !_castsInfo[key]->fileName.empty()) {
- // image file is linked, load from the filesystem
- Common::String filename = _castsInfo[key]->fileName;
- Common::String directory = _castsInfo[key]->directory;
-
- Common::String imageFilename = directory + g_director->_dirSeparator + filename;
-
- Common::Path path = Common::Path(pathMakeRelative(imageFilename), g_director->_dirSeparator);
-
- Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(path);
- if (file) {
- // Detect the filetype. Director will ignore file extensions, as do we.
- Image::ImageDecoder *decoder = nullptr;
- uint32 fileType = file->readUint32BE();
- file->seek(0);
-
- if ((fileType >> 16) == MKTAG16('B', 'M')) {
- // Windows Bitmap file
- decoder = new Image::BitmapDecoder();
- } else if ((fileType == 0xffd8ffe0) || (fileType == 0xffd8ffe1) || (fileType == 0xffd8ffe2)) {
- // JPEG file
- decoder = new Image::JPEGDecoder();
- } else {
- // Well... Director allowed someone to add it, so it must be a PICT. No further questions!
- decoder = new Image::PICTDecoder();
- }
-
- bool res = decoder->loadStream(*file);
- delete file;
-
- if (res) {
- bitmapCast->setPicture(*decoder, decoder->hasPalette());
- bitmapCast->_external = true;
-
- const Graphics::Surface *surf = decoder->getSurface();
- if (decoder->hasPalette()) {
- // For BMPs this sometimes gets set to 16 in the cast record,
- // we should go with what the target image has.
- bitmapCast->_bitsPerPixel = 8;
- }
-
- debugC(5, kDebugImages, "Cast::loadBitmapData(): Bitmap: id: %d, w: %d, h: %d, flags1: %x, flags2: %x bytes: %x, bpp: %d clut: %x", imgId, surf->w, surf->h, bitmapCast->_flags1, bitmapCast->_flags2, bitmapCast->_bytes, bitmapCast->_bitsPerPixel, bitmapCast->_clut);
- delete pic;
- delete decoder;
- return;
- } else {
- delete decoder;
- warning("BUILDBOT: Cast::loadBitmapData(): wrong format for external picture '%s'", path.toString().c_str());
- }
- } else {
- warning("Cast::loadBitmapData(): cannot open external picture '%s'", path.toString().c_str());
- }
- }
- } else {
- if (_loadedCast->contains(imgId)) {
- bitmapCast->_tag = tag = ((BitmapCastMember *)_loadedCast->getVal(imgId))->_tag;
- realId = imgId + _castIDoffset;
- pic = _castArchive->getResource(tag, realId);
- }
- }
-
- if (pic == nullptr) {
- warning("Cast::loadBitmapData(): Bitmap image %d not found", imgId);
- return;
- }
-
- int w = bitmapCast->_initialRect.width();
- int h = bitmapCast->_initialRect.height();
-
- switch (tag) {
- case MKTAG('D', 'I', 'B', ' '):
- debugC(2, kDebugLoading, "****** Loading 'DIB ' id: %d (%d), %d bytes", imgId, realId, (int)pic->size());
- img = new DIBDecoder();
- break;
-
- case MKTAG('B', 'I', 'T', 'D'):
- debugC(2, kDebugLoading, "****** Loading 'BITD' id: %d (%d), %d bytes", imgId, realId, (int)pic->size());
-
- if (w > 0 && h > 0) {
- if (_version < kFileVer600) {
- img = new BITDDecoder(w, h, bitmapCast->_bitsPerPixel, bitmapCast->_pitch, _vm->getPalette(), _version);
- } else {
- img = new Image::BitmapDecoder();
- }
- } else {
- warning("Cast::loadBitmapData(): Bitmap image %d not found", imgId);
- }
-
- break;
-
- default:
- warning("Cast::loadBitmapData(): Unknown Bitmap CastMember Tag: [%d] %s", tag, tag2str(tag));
- break;
- }
-
- if (!img || !img->loadStream(*pic)) {
- warning("Cast::loadBitmapData(): Unable to load id: %d", imgId);
- delete pic;
- delete img;
- return;
- }
-
- bitmapCast->setPicture(*img, true);
-
- delete img;
- delete pic;
-
- debugC(5, kDebugImages, "Cast::loadBitmapData(): Bitmap: id: %d, w: %d, h: %d, flags1: %x, flags2: %x bytes: %x, bpp: %d clut: %x", imgId, w, h, bitmapCast->_flags1, bitmapCast->_flags2, bitmapCast->_bytes, bitmapCast->_bitsPerPixel, bitmapCast->_clut);
-}
-
-void Cast::loadSoundData(int key, SoundCastMember *soundCast) {
- uint32 tag = MKTAG('S', 'N', 'D', ' ');
- uint16 sndId = (uint16)(key + _castIDoffset);
-
- if (_version >= kFileVer400 && soundCast->_children.size() > 0) {
- sndId = soundCast->_children[0].index;
- tag = soundCast->_children[0].tag;
- }
-
- Common::SeekableReadStreamEndian *sndData = nullptr;
-
- if (!_castArchive->hasResource(tag, sndId)) {
- if (_castArchive->hasResource(MKTAG('s', 'n', 'd', ' '), sndId))
- tag = MKTAG('s', 'n', 'd', ' ');
- }
-
- if (_castArchive->hasResource(tag, sndId)) {
- debugC(2, kDebugLoading, "****** Loading '%s' id: %d", tag2str(tag), sndId);
- sndData = _castArchive->getResource(tag, sndId);
- }
-
- if (sndData == nullptr || sndData->size() == 0) {
- // audio file is linked, load from the filesystem
- Common::String filename = _castsInfo[key]->fileName;
-
- if (!_castsInfo[key]->directory.empty())
- filename = _castsInfo[key]->directory + g_director->_dirSeparator + _castsInfo[key]->fileName;
-
- AudioFileDecoder *audio = new AudioFileDecoder(filename);
- soundCast->_audio = audio;
- } else {
- SNDDecoder *audio = new SNDDecoder();
- audio->loadStream(*sndData);
- soundCast->_audio = audio;
- soundCast->_size = sndData->size();
- if (_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.
- soundCast->_looping = audio->hasLoopBounds();
- }
- }
- delete sndData;
-}
-
void Cast::loadCastMemberData() {
debugC(1, kDebugLoading, "****** Loading casts data: sprite palettes, images, filmloops, sounds and texts.");
@@ -867,26 +630,7 @@ void Cast::loadCastMemberData() {
for (Common::HashMap<int, CastMember *>::iterator c = _loadedCast->begin(); c != _loadedCast->end(); ++c) {
if (!c->_value)
continue;
-
- switch (c->_value->_type){
- case kCastPalette:
- loadPaletteData(c->_key, (PaletteCastMember *)c->_value);
- break;
- case kCastFilmLoop:
- loadFilmLoopData(c->_key, (FilmLoopCastMember *)c->_value);
- break;
- case kCastBitmap:
- loadBitmapData(c->_key, (BitmapCastMember *)c->_value);
- break;
- case kCastSound:
- loadSoundData(c->_key, (SoundCastMember *)c->_value);
- break;
- case kCastText:
- case kCastButton:
- loadStxtData(c->_key, (TextCastMember *)c->_value);
- default:
- break;
- }
+ c->_value->load();
if (debugChannelSet(-1, kDebugFewFramesOnly) && idx++ > 0 && !(idx % 200))
debug("Loaded %d casts data", idx);
@@ -936,6 +680,13 @@ Common::String Cast::getVideoPath(int castId) {
return res;
}
+Common::SeekableReadStreamEndian *Cast::getResource(uint32 tag, uint16 id) {
+ if (!_castArchive || !_castArchive->hasResource(tag, id))
+ return nullptr;
+
+ return _castArchive->getResource(tag, id);
+}
+
PaletteV4 Cast::loadPalette(Common::SeekableReadStreamEndian &stream) {
int size = stream.size();
debugC(3, kDebugLoading, "Cast::loadPalette(): %d bytes", size);
diff --git a/engines/director/cast.h b/engines/director/cast.h
index 7de16e2b8fb..29b64c7a50c 100644
--- a/engines/director/cast.h
+++ b/engines/director/cast.h
@@ -96,11 +96,6 @@ public:
void loadSord(Common::SeekableReadStreamEndian &stream);
void loadCastMemberData();
- void loadStxtData(int key, TextCastMember *member);
- void loadPaletteData(int key, PaletteCastMember *member);
- void loadFilmLoopData(int key, FilmLoopCastMember *member);
- void loadBitmapData(int key, BitmapCastMember *bitmapCast);
- void loadSoundData(int key, SoundCastMember *soundCast);
int getCastSize();
Common::Rect getCastMemberInitialRect(int castId);
@@ -113,6 +108,7 @@ public:
CastMemberInfo *getCastMemberInfo(int castId);
const Stxt *getStxt(int castId);
Common::String getVideoPath(int castId);
+ Common::SeekableReadStreamEndian *getResource(uint32 tag, uint16 id);
// release all castmember's widget, should be called when we are changing movie.
// because widget is handled by channel, thus we should clear all of those run-time info when we are switching the movie. (because we will create new widgets for cast)
diff --git a/engines/director/castmember/bitmap.cpp b/engines/director/castmember/bitmap.cpp
index 2bae5705fd6..2708f75a9e0 100644
--- a/engines/director/castmember/bitmap.cpp
+++ b/engines/director/castmember/bitmap.cpp
@@ -19,12 +19,17 @@
*
*/
+#include "common/macresman.h"
#include "graphics/surface.h"
#include "graphics/macgui/macwidget.h"
+#include "image/bmp.h"
#include "image/image_decoder.h"
+#include "image/jpeg.h"
+#include "image/pict.h"
#include "director/director.h"
#include "director/cast.h"
+#include "director/images.h"
#include "director/movie.h"
#include "director/picture.h"
#include "director/score.h"
@@ -478,6 +483,151 @@ Common::String BitmapCastMember::formatInfo() {
);
}
+void BitmapCastMember::load() {
+ if (_loaded)
+ return;
+
+ uint32 tag = _tag;
+ uint16 imgId = _castId;
+ uint16 realId = 0;
+
+ Image::ImageDecoder *img = nullptr;
+ Common::SeekableReadStream *pic = nullptr;
+
+ if (_cast->_version >= kFileVer400) {
+ if (_children.size() > 0) {
+ imgId = _children[0].index;
+ tag = _children[0].tag;
+
+ pic = _cast->getResource(tag, imgId);
+ }
+
+ CastMemberInfo *ci = _cast->getCastMemberInfo(_castId);
+
+ if ((pic == nullptr || pic->size() == 0)
+ && ci && !ci->fileName.empty()) {
+ // image file is linked, load from the filesystem
+ Common::String filename = ci->fileName;
+ Common::String directory = ci->directory;
+
+ Common::String imageFilename = directory + g_director->_dirSeparator + filename;
+
+ Common::Path path = Common::Path(pathMakeRelative(imageFilename), g_director->_dirSeparator);
+
+ Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(path);
+ if (file) {
+ // Detect the filetype. Director will ignore file extensions, as do we.
+ Image::ImageDecoder *decoder = nullptr;
+ uint32 fileType = file->readUint32BE();
+ file->seek(0);
+
+ if ((fileType >> 16) == MKTAG16('B', 'M')) {
+ // Windows Bitmap file
+ decoder = new Image::BitmapDecoder();
+ } else if ((fileType == 0xffd8ffe0) || (fileType == 0xffd8ffe1) || (fileType == 0xffd8ffe2)) {
+ // JPEG file
+ decoder = new Image::JPEGDecoder();
+ } else {
+ // Well... Director allowed someone to add it, so it must be a PICT. No further questions!
+ decoder = new Image::PICTDecoder();
+ }
+
+ bool res = decoder->loadStream(*file);
+ delete file;
+
+ if (res) {
+ setPicture(*decoder, decoder->hasPalette());
+ _external = true;
+
+ const Graphics::Surface *surf = decoder->getSurface();
+ if (decoder->hasPalette()) {
+ // For BMPs this sometimes gets set to 16 in the cast record,
+ // we should go with what the target image has.
+ _bitsPerPixel = 8;
+ }
+
+ debugC(5, kDebugImages, "BitmapCastMember::load(): Bitmap: id: %d, w: %d, h: %d, flags1: %x, flags2: %x bytes: %x, bpp: %d clut: %x", imgId, surf->w, surf->h, _flags1, _flags2, _bytes, _bitsPerPixel, _clut);
+ delete pic;
+ delete decoder;
+ _loaded = true;
+ return;
+ } else {
+ delete decoder;
+ warning("BUILDBOT: BitmapCastMember::load(): wrong format for external picture '%s'", path.toString().c_str());
+ }
+ } else {
+ warning("BitmapCastMember::load(): cannot open external picture '%s'", path.toString().c_str());
+ }
+ }
+ } else {
+ realId = imgId + _cast->_castIDoffset;
+ pic = _cast->getResource(tag, realId);
+ }
+
+ if (pic == nullptr) {
+ warning("BitmapCastMember::load(): Bitmap image %d not found", imgId);
+ return;
+ }
+
+ int w = _initialRect.width();
+ int h = _initialRect.height();
+
+ switch (tag) {
+ case MKTAG('D', 'I', 'B', ' '):
+ debugC(2, kDebugLoading, "****** Loading 'DIB ' id: %d (%d), %d bytes", imgId, realId, (int)pic->size());
+ img = new DIBDecoder();
+ break;
+
+ case MKTAG('B', 'I', 'T', 'D'):
+ debugC(2, kDebugLoading, "****** Loading 'BITD' id: %d (%d), %d bytes", imgId, realId, (int)pic->size());
+
+ if (w > 0 && h > 0) {
+ if (_cast->_version < kFileVer600) {
+ img = new BITDDecoder(w, h, _bitsPerPixel, _pitch, g_director->getPalette(), _cast->_version);
+ } else {
+ img = new Image::BitmapDecoder();
+ }
+ } else {
+ warning("BitmapCastMember::load(): Bitmap image %d not found", imgId);
+ }
+
+ break;
+
+ default:
+ warning("BitmapCastMember::load(): Unknown Bitmap CastMember Tag: [%d] %s", tag, tag2str(tag));
+ break;
+ }
+
+ if (!img || !img->loadStream(*pic)) {
+ warning("BitmapCastMember::load(): Unable to load id: %d", imgId);
+ delete pic;
+ delete img;
+ return;
+ }
+
+ setPicture(*img, true);
+
+ delete img;
+ delete pic;
+
+ debugC(5, kDebugImages, "BitmapCastMember::load(): Bitmap: id: %d, w: %d, h: %d, flags1: %x, flags2: %x bytes: %x, bpp: %d clut: %x", imgId, w, h, _flags1, _flags2, _bytes, _bitsPerPixel, _clut);
+
+ _loaded = true;
+}
+
+void BitmapCastMember::unload() {
+ if (!_loaded)
+ return;
+
+ delete _picture;
+ _picture = nullptr;
+
+ delete _ditheredImg;
+ _ditheredImg = nullptr;
+
+ _loaded = false;
+}
+
PictureReference *BitmapCastMember::getPicture() const {
auto picture = new PictureReference;
diff --git a/engines/director/castmember/bitmap.h b/engines/director/castmember/bitmap.h
index ae8808983fe..bdacde3f14a 100644
--- a/engines/director/castmember/bitmap.h
+++ b/engines/director/castmember/bitmap.h
@@ -48,6 +48,9 @@ public:
Common::String formatInfo() override;
+ void load() override;
+ void unload() override;
+
PictureReference *getPicture() const;
void setPicture(PictureReference &picture);
void setPicture(Image::ImageDecoder &image, bool adjustSize);
diff --git a/engines/director/castmember/castmember.cpp b/engines/director/castmember/castmember.cpp
index 0c7692c8bf6..2e1c4c29f9d 100644
--- a/engines/director/castmember/castmember.cpp
+++ b/engines/director/castmember/castmember.cpp
@@ -19,6 +19,7 @@
*
*/
+#include "common/events.h"
#include "director/director.h"
#include "director/cast.h"
#include "director/castmember/castmember.h"
@@ -36,6 +37,7 @@ CastMember::CastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndi
_size = stream.size();
_flags1 = 0;
+ _loaded = false;
_modified = true;
_isChanged = false;
@@ -54,6 +56,7 @@ CastMember::CastMember(Cast *cast, uint16 castId) : Object<CastMember>("CastMemb
_size = 0;
_flags1 = 0;
+ _loaded = false;
_modified = true;
_isChanged = false;
@@ -238,4 +241,18 @@ CastMemberInfo *CastMember::getInfo() {
return _cast->getCastMemberInfo(_castId);
}
+void CastMember::load() {
+ if (_loaded)
+ return;
+
+ _loaded = true;
+}
+
+void CastMember::unload() {
+ if (!_loaded)
+ return;
+
+ _loaded = false;
+}
+
} // End of namespace Director
diff --git a/engines/director/castmember/castmember.h b/engines/director/castmember/castmember.h
index f5e8ec22948..1ac3a103b67 100644
--- a/engines/director/castmember/castmember.h
+++ b/engines/director/castmember/castmember.h
@@ -55,6 +55,10 @@ public:
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; }
@@ -98,6 +102,7 @@ protected:
uint16 _castId;
// a link to the widget we created, we may use it later
Graphics::MacWidget *_widget;
+ bool _loaded;
bool _modified;
bool _isChanged;
};
diff --git a/engines/director/castmember/filmloop.cpp b/engines/director/castmember/filmloop.cpp
index 5a654eebda9..c860f2aa984 100644
--- a/engines/director/castmember/filmloop.cpp
+++ b/engines/director/castmember/filmloop.cpp
@@ -19,6 +19,7 @@
*
*/
+#include "common/stream.h"
#include "graphics/surface.h"
#include "graphics/macgui/macwidget.h"
@@ -389,4 +390,46 @@ Common::String FilmLoopCastMember::formatInfo() {
);
}
+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());
+ loadFilmLoopData(*loop);
+ delete loop;
+ } else {
+ warning("FilmLoopCastMember::load(): Film loop not found");
+ }
+ } else if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
+ if (_children.size() == 1) {
+ uint16 filmLoopId = _children[0].index;
+ uint32 tag = _children[0].tag;
+ Common::SeekableReadStreamEndian *loop = _cast->getResource(tag, filmLoopId);
+ if (loop) {
+ debugC(2, kDebugLoading, "****** Loading '%s' id: %d, %d bytes", tag2str(tag), filmLoopId, (int)loop->size());
+ loadFilmLoopDataV4(*loop);
+ delete loop;
+ } else {
+ warning("FilmLoopCastMember::load(): Film loop not found");
+ }
+ } else {
+ warning("FilmLoopCastMember::load(): Expected 1 child for film loop cast, got %d", _children.size());
+ }
+ } else {
+ warning("STUB: FilmLoopCastMember::load(): Film loops not supported for version %d", _cast->_version);
+ }
+
+ _loaded = true;
+}
+
+void FilmLoopCastMember::unload() {
+ // No unload necessary.
+}
+
} // End of namespace Director
diff --git a/engines/director/castmember/filmloop.h b/engines/director/castmember/filmloop.h
index 297eb2b2775..1085990dc15 100644
--- a/engines/director/castmember/filmloop.h
+++ b/engines/director/castmember/filmloop.h
@@ -47,6 +47,9 @@ public:
Common::String formatInfo() override;
+ void load() override;
+ void unload() override;
+
bool _enableSound;
bool _looping;
bool _crop;
diff --git a/engines/director/castmember/palette.cpp b/engines/director/castmember/palette.cpp
index d15a90f84f3..15d2866b223 100644
--- a/engines/director/castmember/palette.cpp
+++ b/engines/director/castmember/palette.cpp
@@ -20,6 +20,7 @@
*/
#include "director/director.h"
+#include "director/cast.h"
#include "director/castmember/palette.h"
namespace Director {
@@ -41,4 +42,30 @@ Common::String PaletteCastMember::formatInfo() {
return result;
}
+void PaletteCastMember::load() {
+ if (_loaded)
+ return;
+
+ // TODO: Verify how palettes work in >D4 versions
+ int paletteId = 0;
+ if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500 && _children.size() == 1) {
+ paletteId = _children[0].index;
+ } else 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 {
+ warning("PaletteCastMember::load(): Expected 1 child for palette cast, got %d", _children.size());
+ }
+ if (paletteId) {
+ debugC(2, kDebugImages, "PaletteCastMember::load(): linking palette id %d to cast index %d", paletteId, _castId);
+ _palette = g_director->getPalette(paletteId);
+ }
+
+ _loaded = true;
+}
+
+void PaletteCastMember::unload() {
+ // No unload necessary.
+}
+
}
diff --git a/engines/director/castmember/palette.h b/engines/director/castmember/palette.h
index 769bf9817db..84a4f25af7b 100644
--- a/engines/director/castmember/palette.h
+++ b/engines/director/castmember/palette.h
@@ -34,6 +34,9 @@ public:
Common::String formatInfo() override;
+ void load() override;
+ void unload() override;
+
PaletteV4 *_palette;
};
diff --git a/engines/director/castmember/sound.cpp b/engines/director/castmember/sound.cpp
index 4d62b1be855..838c3851545 100644
--- a/engines/director/castmember/sound.cpp
+++ b/engines/director/castmember/sound.cpp
@@ -20,6 +20,7 @@
*/
#include "director/director.h"
+#include "director/cast.h"
#include "director/sound.h"
#include "director/castmember/sound.h"
@@ -43,4 +44,65 @@ Common::String SoundCastMember::formatInfo() {
);
}
+void SoundCastMember::load() {
+ if (_loaded)
+ return;
+
+ uint32 tag = MKTAG('S', 'N', 'D', ' ');
+ uint16 sndId = (uint16)(_castId + _cast->_castIDoffset);
+
+ if (_cast->_version >= kFileVer400 && _children.size() > 0) {
+ sndId = _children[0].index;
+ tag = _children[0].tag;
+ }
+
+ 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
+ CastMemberInfo *ci = _cast->getCastMemberInfo(_castId);
+ if (ci) {
+ Common::String filename = ci->fileName;
+
+ if (!ci->directory.empty())
+ filename = ci->directory + g_director->_dirSeparator + ci->fileName;
+
+ AudioFileDecoder *audio = new AudioFileDecoder(filename);
+ _audio = audio;
+ } else {
+ warning("Sound::load(): no resource or info found for cast member %d, skipping", _castId);
+ }
+ } else {
+ 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();
+ }
+ }
+ if (sndData)
+ delete sndData;
+
+ _loaded = true;
+}
+
+void SoundCastMember::unload() {
+ if (!_loaded)
+ return;
+
+ delete _audio;
+ _audio = nullptr;
+ _size = 0;
+ _looping = false;
+
+ _loaded = false;
+}
+
} // End of namespace Director
diff --git a/engines/director/castmember/sound.h b/engines/director/castmember/sound.h
index 57e9372fef7..f4b7527b493 100644
--- a/engines/director/castmember/sound.h
+++ b/engines/director/castmember/sound.h
@@ -33,6 +33,8 @@ public:
SoundCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version);
~SoundCastMember();
+ void load() override;
+ void unload() override;
Common::String formatInfo() override;
bool _looping;
diff --git a/engines/director/castmember/text.cpp b/engines/director/castmember/text.cpp
index 1f26b8b6543..eb83e61c162 100644
--- a/engines/director/castmember/text.cpp
+++ b/engines/director/castmember/text.cpp
@@ -23,6 +23,7 @@
#include "graphics/macgui/mactext.h"
#include "director/director.h"
+#include "director/cast.h"
#include "director/channel.h"
#include "director/movie.h"
#include "director/score.h"
@@ -366,7 +367,31 @@ Common::String TextCastMember::formatInfo() {
getForeColor(), getBackColor(),
_editable, format.c_str()
);
+}
+
+void TextCastMember::load() {
+ if (_loaded)
+ return;
+
+ uint stxtid;
+ if (_cast->_version >= kFileVer400 && _children.size() > 0)
+ stxtid = _children[0].index;
+ else
+ stxtid = _castId;
+
+ if (_cast->_loadedStxts->contains(stxtid)) {
+ const Stxt *stxt = _cast->_loadedStxts->getVal(stxtid);
+ importStxt(stxt);
+ _size = stxt->_size;
+ } else {
+ warning("TextCastMember::load(): stxtid %i isn't loaded", stxtid);
+ }
+
+ _loaded = true;
+}
+void TextCastMember::unload() {
+ // No unload necessary.
}
bool TextCastMember::hasField(int field) {
diff --git a/engines/director/castmember/text.h b/engines/director/castmember/text.h
index a579bb51e68..4db38085f69 100644
--- a/engines/director/castmember/text.h
+++ b/engines/director/castmember/text.h
@@ -58,6 +58,9 @@ public:
Common::String formatInfo() override;
+ void load() override;
+ void unload() override;
+
SizeType _borderSize;
SizeType _gutterSize;
SizeType _boxShadow;
Commit: 4557c5225813d5389bcf5fcf2733658e170c797f
https://github.com/scummvm/scummvm/commit/4557c5225813d5389bcf5fcf2733658e170c797f
Author: Scott Percival (code at moral.net.au)
Date: 2023-04-29T14:20:05+02:00
Commit Message:
DIRECTOR: Lazily load cast member data from archive
Required for e.g. Inscape games, where movie files contain more data
than could (at the time) fit into RAM. Removes a noticable wait when
switching movie files.
Changed paths:
engines/director/cast.cpp
engines/director/cast.h
engines/director/castmember/bitmap.cpp
engines/director/castmember/sound.cpp
engines/director/castmember/text.cpp
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index e0e6ebafcf7..26f3df8f314 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -58,6 +58,7 @@ Cast::Cast(Movie *movie, uint16 castLibID, bool isShared) {
_castLibID = castLibID;
_isShared = isShared;
+ _loadMutex = true;
_lingoArchive = new LingoArchive(this);
@@ -117,6 +118,13 @@ CastMember *Cast::getCastMember(int castId) {
if (_loadedCast && _loadedCast->contains(castId)) {
result = _loadedCast->getVal(castId);
}
+ if (result && _loadMutex) {
+ // Archives only support having one stream open at a time,
+ // prevent recursive calls to CastMember::load()
+ _loadMutex = false;
+ result->load();
+ _loadMutex = true;
+ }
return result;
}
@@ -127,26 +135,23 @@ void Cast::releaseCastMemberWidget() {
}
CastMember *Cast::getCastMemberByNameAndType(const Common::String &name, CastType type) {
- CastMember *result = nullptr;
-
if (type == kCastTypeAny) {
if (_castsNames.contains(name)) {
- result = _loadedCast->getVal(_castsNames[name]);
+ return getCastMember(_castsNames[name]);
}
} else {
Common::String cname = Common::String::format("%s:%d", name.c_str(), type);
if (_castsNames.contains(cname))
- result = _loadedCast->getVal(_castsNames[cname]);
+ return getCastMember(_castsNames[cname]);
}
- return result;
+ return nullptr;
}
CastMember *Cast::getCastMemberByScriptId(int scriptId) {
- CastMember *result = nullptr;
if (_castsScriptIds.contains(scriptId))
- result = _loadedCast->getVal(_castsScriptIds[scriptId]);
- return result;
+ return getCastMember(_castsScriptIds[scriptId]);
+ return nullptr;
}
CastMemberInfo *Cast::getCastMemberInfo(int castId) {
@@ -618,23 +623,6 @@ void Cast::loadCast() {
}
}
-
- loadCastMemberData();
-}
-
-void Cast::loadCastMemberData() {
- debugC(1, kDebugLoading, "****** Loading casts data: sprite palettes, images, filmloops, sounds and texts.");
-
- int idx = 0;
-
- for (Common::HashMap<int, CastMember *>::iterator c = _loadedCast->begin(); c != _loadedCast->end(); ++c) {
- if (!c->_value)
- continue;
- c->_value->load();
-
- if (debugChannelSet(-1, kDebugFewFramesOnly) && idx++ > 0 && !(idx % 200))
- debug("Loaded %d casts data", idx);
- }
}
Common::String Cast::getVideoPath(int castId) {
diff --git a/engines/director/cast.h b/engines/director/cast.h
index 29b64c7a50c..b8492132107 100644
--- a/engines/director/cast.h
+++ b/engines/director/cast.h
@@ -95,8 +95,6 @@ public:
void loadExternalSound(Common::SeekableReadStreamEndian &stream);
void loadSord(Common::SeekableReadStreamEndian &stream);
- void loadCastMemberData();
-
int getCastSize();
Common::Rect getCastMemberInitialRect(int castId);
void setCastMemberModified(int castId);
@@ -162,6 +160,7 @@ private:
Movie *_movie;
bool _isShared;
+ bool _loadMutex;
Common::String _macName;
diff --git a/engines/director/castmember/bitmap.cpp b/engines/director/castmember/bitmap.cpp
index 2708f75a9e0..084d9cf1221 100644
--- a/engines/director/castmember/bitmap.cpp
+++ b/engines/director/castmember/bitmap.cpp
@@ -516,6 +516,7 @@ void BitmapCastMember::load() {
Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(path);
if (file) {
+ debugC(2, kDebugLoading, "****** Loading file '%s', cast id: %d", imageFilename.c_str(), imgId);
// Detect the filetype. Director will ignore file extensions, as do we.
Image::ImageDecoder *decoder = nullptr;
uint32 fileType = file->readUint32BE();
diff --git a/engines/director/castmember/sound.cpp b/engines/director/castmember/sound.cpp
index 838c3851545..ed6dd45e974 100644
--- a/engines/director/castmember/sound.cpp
+++ b/engines/director/castmember/sound.cpp
@@ -71,12 +71,14 @@ void SoundCastMember::load() {
if (!ci->directory.empty())
filename = ci->directory + g_director->_dirSeparator + ci->fileName;
+ debugC(2, kDebugLoading, "****** Loading file '%s', cast id: %d", filename.c_str(), sndId);
AudioFileDecoder *audio = new AudioFileDecoder(filename);
_audio = audio;
} 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;
diff --git a/engines/director/castmember/text.cpp b/engines/director/castmember/text.cpp
index eb83e61c162..a5c451ded60 100644
--- a/engines/director/castmember/text.cpp
+++ b/engines/director/castmember/text.cpp
@@ -373,6 +373,9 @@ void TextCastMember::load() {
if (_loaded)
return;
+ if (!_cast->_loadedStxts)
+ return;
+
uint stxtid;
if (_cast->_version >= kFileVer400 && _children.size() > 0)
stxtid = _children[0].index;
Commit: 14af0945150d4cae2e7add77b502c70dc9be131e
https://github.com/scummvm/scummvm/commit/14af0945150d4cae2e7add77b502c70dc9be131e
Author: Scott Percival (code at moral.net.au)
Date: 2023-04-29T14:20:05+02:00
Commit Message:
DIRECTOR: XOBJ: Replace stub boilerplate with macro
Changed paths:
engines/director/detection_tables.h
engines/director/lingo/lingo-utils.h
engines/director/lingo/tests/equality.lingo
engines/director/lingo/xlibs/barakeobj.cpp
engines/director/lingo/xlibs/batqt.cpp
engines/director/lingo/xlibs/cdromxobj.cpp
engines/director/lingo/xlibs/darkenscreen.cpp
engines/director/lingo/xlibs/dpwavi.cpp
engines/director/lingo/xlibs/draw.cpp
engines/director/lingo/xlibs/ednox.cpp
engines/director/lingo/xlibs/fileio.cpp
engines/director/lingo/xlibs/fplayxobj.cpp
engines/director/lingo/xlibs/gpid.cpp
engines/director/lingo/xlibs/gpid.h
engines/director/lingo/xlibs/jitdraw3.cpp
engines/director/lingo/xlibs/jwxini.cpp
engines/director/lingo/xlibs/moovxobj.cpp
engines/director/lingo/xlibs/orthoplayxobj.cpp
engines/director/lingo/xlibs/popupmenuxobj.cpp
engines/director/lingo/xlibs/printomatic.cpp
engines/director/lingo/xlibs/qtmovie.cpp
engines/director/lingo/xlibs/serialportxobj.cpp
engines/director/lingo/xlibs/soundjam.cpp
engines/director/lingo/xlibs/spacemgr.cpp
engines/director/lingo/xlibs/valkyrie.cpp
engines/director/lingo/xlibs/videodiscxobj.cpp
engines/director/lingo/xlibs/widgetxobj.cpp
engines/director/lingo/xlibs/xio.cpp
diff --git a/engines/director/detection_tables.h b/engines/director/detection_tables.h
index 024faf0a7e9..1d4611afab3 100644
--- a/engines/director/detection_tables.h
+++ b/engines/director/detection_tables.h
@@ -5300,7 +5300,7 @@ static const DirectorGameDescription gameDescriptions[] = {
WINGAME1("trekguideds9", "v1.0", "OMNI_DS9.EXE", "0058390ff77e527c7bb413081004c304", 805517, 400),
MACGAME1("trekguidetng", "v1.1", "TNG Episodes", "1c2e5371b835680e7c1ca8bcea008bef", 520149, 400),
- WINGAME1("trekguidetng", "v1.1", "OMNI_TNG.EXE", "0049d72e2d5869408fc33860ec4b5c1f", 794201, 400),
+ WINGAME1("trekguidetng", "v1.1", "OMNI2/OMNI_TNG.EXE", "0049d72e2d5869408fc33860ec4b5c1f", 794201, 400),
MACGAME1("trekomni", "v1.00", "Omnipedia", "b7e69c37b7355022d400c14aa97c5d54", 516791, 400),
MACGAME1("trekomni", "v1.1.2 Upgrade", "Omnipedia 2", "b7e69c37b7355022d400c14aa97c5d54", 613253, 400),
diff --git a/engines/director/lingo/lingo-utils.h b/engines/director/lingo/lingo-utils.h
index 195d4118be9..567a84624f3 100644
--- a/engines/director/lingo/lingo-utils.h
+++ b/engines/director/lingo/lingo-utils.h
@@ -53,4 +53,24 @@
return; \
}
+#define XOBJSTUB(methname,retval) \
+ void methname(int nargs) { \
+ g_lingo->printSTUBWithArglist(#methname, nargs); \
+ g_lingo->dropStack(nargs); \
+ g_lingo->push(Datum(retval)); \
+ }
+
+#define XOBJSTUBV(methname) \
+ void methname(int nargs) { \
+ g_lingo->printSTUBWithArglist(#methname, nargs); \
+ g_lingo->dropStack(nargs); \
+ g_lingo->push(Datum()); \
+ }
+
+#define XOBJSTUBNR(methname) \
+ void methname(int nargs) { \
+ g_lingo->printSTUBWithArglist(#methname, nargs); \
+ g_lingo->dropStack(nargs); \
+ }
+
#endif
diff --git a/engines/director/lingo/tests/equality.lingo b/engines/director/lingo/tests/equality.lingo
index 06f39ebf4c9..60c4b45dafe 100644
--- a/engines/director/lingo/tests/equality.lingo
+++ b/engines/director/lingo/tests/equality.lingo
@@ -25,7 +25,6 @@ set a to the picture of cast 1
scummvmAssert(a <> a)
scummvmAssert(a <> the picture of cast 1) -- always false
-
-- String comparison
scummvmAssert("a" > "A")
scummvmAssert("a" <= "Z")
@@ -34,8 +33,8 @@ scummvmAssert("z" > "Z")
scummvmAssert("abba" > "Abba")
-- This behaviour was fixed by 8.5
--- set save to the scummvmVersion
--- set the scummvmVersion to 850
--- scummvmAssert("a" <= "Z")
--- scummvmAssert("a" <= "A")
--- set the scummvmVersion to save
+set save to the scummvmVersion
+set the scummvmVersion to 850
+scummvmAssert("a" <= "Z")
+scummvmAssert("a" <= "A")
+set the scummvmVersion to save
diff --git a/engines/director/lingo/xlibs/barakeobj.cpp b/engines/director/lingo/xlibs/barakeobj.cpp
index a596cea8164..e3f72cee072 100644
--- a/engines/director/lingo/xlibs/barakeobj.cpp
+++ b/engines/director/lingo/xlibs/barakeobj.cpp
@@ -37,6 +37,7 @@
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/barakeobj.h"
@@ -83,14 +84,7 @@ void BarakeObj::m_new(int nargs) {
void BarakeObj::m_clear(int nargs) {
}
-void BarakeObj::m_gpal(int nargs) {
- g_lingo->printSTUBWithArglist("BarakeObj::Gpal", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void BarakeObj::m_line(int nargs) {
- g_lingo->printSTUBWithArglist("BarakeObj::Line", nargs);
- g_lingo->dropStack(nargs);
-}
+XOBJSTUBNR(BarakeObj::m_gpal)
+XOBJSTUBNR(BarakeObj::m_line)
} // End of namespace Director
diff --git a/engines/director/lingo/xlibs/batqt.cpp b/engines/director/lingo/xlibs/batqt.cpp
index 4e28933ffdf..5858a92e875 100644
--- a/engines/director/lingo/xlibs/batqt.cpp
+++ b/engines/director/lingo/xlibs/batqt.cpp
@@ -55,6 +55,7 @@
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/batqt.h"
@@ -116,123 +117,25 @@ void BatQT::m_new(int nargs) {
g_lingo->push(g_lingo->_state->me);
}
-void BatQT::m_dispose(int nargs) {
- g_lingo->printSTUBWithArglist("BatQT::m_dispose", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void BatQT::m_name(int nargs) {
- g_lingo->printSTUBWithArglist("BatQT::m_name", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(""));
-}
-
-void BatQT::m_status(int nargs) {
- g_lingo->printSTUBWithArglist("BatQT::m_status", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
-
-void BatQT::m_error(int nargs) {
- g_lingo->printSTUBWithArglist("BatQT::m_error", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(""));
-}
-
-void BatQT::m_lastError(int nargs) {
- g_lingo->printSTUBWithArglist("BatQT::m_lastError", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(""));
-}
-
-void BatQT::m_open(int nargs) {
- g_lingo->printSTUBWithArglist("BatQT::m_open", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
-
-void BatQT::m_play(int nargs) {
- g_lingo->printSTUBWithArglist("BatQT::m_play", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
-
-void BatQT::m_stop(int nargs) {
- g_lingo->printSTUBWithArglist("BatQT::m_stop", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
-
-void BatQT::m_getTimeRange(int nargs) {
- g_lingo->printSTUBWithArglist("BatQT::m_getTimeRange", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(""));
-}
-
-void BatQT::m_getMovieBox(int nargs) {
- g_lingo->printSTUBWithArglist("BatQT::m_getMovieBox", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum("0,0,320,240"));
-}
-
-void BatQT::m_getTime(int nargs) {
- g_lingo->printSTUBWithArglist("BatQT::m_getTime", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
-
-void BatQT::m_setTime(int nargs) {
- g_lingo->printSTUBWithArglist("BatQT::m_setTime", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(""));
-}
-
-void BatQT::m_setVolume(int nargs) {
- g_lingo->printSTUBWithArglist("BatQT::m_setVolume", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(""));
-}
-
-void BatQT::m_length(int nargs) {
- g_lingo->printSTUBWithArglist("BatQT::m_length", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
-
-void BatQT::m_setMovieBox(int nargs) {
- g_lingo->printSTUBWithArglist("BatQT::m_setMovieBox", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
-
-void BatQT::m_setTimeRange(int nargs) {
- g_lingo->printSTUBWithArglist("BatQT::m_setTimeRange", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
-
-void BatQT::m_addCallback(int nargs) {
- g_lingo->printSTUBWithArglist("BatQT::m_addCallback", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
-
-void BatQT::m_removeCallback(int nargs) {
- g_lingo->printSTUBWithArglist("BatQT::m_removeCallback", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
-
-void BatQT::m_resetCallbacks(int nargs) {
- g_lingo->printSTUBWithArglist("BatQT::m_resetCallbacks", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
-
-void BatQT::m_setBatch(int nargs) {
- g_lingo->printSTUBWithArglist("BatQT::m_setBatch", nargs);
- g_lingo->dropStack(nargs);
-}
-
+XOBJSTUBNR(BatQT::m_dispose)
+XOBJSTUB(BatQT::m_name, "")
+XOBJSTUB(BatQT::m_status, 0)
+XOBJSTUB(BatQT::m_error, "")
+XOBJSTUB(BatQT::m_lastError, "")
+XOBJSTUB(BatQT::m_open, 0)
+XOBJSTUB(BatQT::m_play, 0)
+XOBJSTUB(BatQT::m_stop, 0)
+XOBJSTUB(BatQT::m_getTimeRange, "")
+XOBJSTUB(BatQT::m_getMovieBox, "0,0,320,240")
+XOBJSTUB(BatQT::m_getTime, 0)
+XOBJSTUB(BatQT::m_setTime, "")
+XOBJSTUB(BatQT::m_setVolume, "")
+XOBJSTUB(BatQT::m_length, 0)
+XOBJSTUB(BatQT::m_setMovieBox, 0)
+XOBJSTUB(BatQT::m_setTimeRange, 0)
+XOBJSTUB(BatQT::m_addCallback, 0)
+XOBJSTUB(BatQT::m_removeCallback, 0)
+XOBJSTUB(BatQT::m_resetCallbacks, 0)
+XOBJSTUBNR(BatQT::m_setBatch)
} // End of namespace Director
diff --git a/engines/director/lingo/xlibs/cdromxobj.cpp b/engines/director/lingo/xlibs/cdromxobj.cpp
index 4ab66d58d0b..93a976ec613 100644
--- a/engines/director/lingo/xlibs/cdromxobj.cpp
+++ b/engines/director/lingo/xlibs/cdromxobj.cpp
@@ -165,6 +165,7 @@
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/cdromxobj.h"
namespace Director {
@@ -315,11 +316,7 @@ void CDROMXObj::m_playSegment(int nargs) {
g_lingo->push(Datum());
}
-void CDROMXObj::m_askPlay(int nargs) {
- g_lingo->printSTUBWithArglist("CDROMXObj::m_askPlay", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
+XOBJSTUBV(CDROMXObj::m_askPlay)
void CDROMXObj::m_stepFwd(int nargs) {
CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_state->me.u.obj);
@@ -471,51 +468,18 @@ void CDROMXObj::m_currentTrack(int nargs) {
g_lingo->push(Datum(me->_cdda_status.track));
}
-void CDROMXObj::m_currentTime(int nargs) {
- g_lingo->printSTUBWithArglist("CDROMXObj::m_currentTime", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
+XOBJSTUBV(CDROMXObj::m_currentTime)
// The next few methods depend on full TOC implementation, so they
// can't be implemented right now.
-void CDROMXObj::m_firstTrack(int nargs) {
- g_lingo->printSTUBWithArglist("CDROMXObj::m_firstTrack", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void CDROMXObj::m_lastTrack(int nargs) {
- g_lingo->printSTUBWithArglist("CDROMXObj::m_lastTrack", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void CDROMXObj::m_totalTime(int nargs) {
- g_lingo->printSTUBWithArglist("CDROMXObj::m_totalTime", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
+XOBJSTUBV(CDROMXObj::m_firstTrack)
+XOBJSTUBV(CDROMXObj::m_lastTrack)
+XOBJSTUBV(CDROMXObj::m_totalTime)
// The scan methods depend on absolute timing, so they also require
// a full TOC.
-void CDROMXObj::m_scanFwd(int nargs) {
- g_lingo->printSTUBWithArglist("CDROMXObj::m_scanFwd", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void CDROMXObj::m_scanBwd(int nargs) {
- g_lingo->printSTUBWithArglist("CDROMXObj::m_scanBwd", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void CDROMXObj::m_stopScan(int nargs) {
- g_lingo->printSTUBWithArglist("CDROMXObj::m_stopScan", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
+XOBJSTUBV(CDROMXObj::m_scanFwd)
+XOBJSTUBV(CDROMXObj::m_scanBwd)
+XOBJSTUBV(CDROMXObj::m_stopScan)
} // End of namespace Director
diff --git a/engines/director/lingo/xlibs/darkenscreen.cpp b/engines/director/lingo/xlibs/darkenscreen.cpp
index e1ff14c739e..204a794079f 100644
--- a/engines/director/lingo/xlibs/darkenscreen.cpp
+++ b/engines/director/lingo/xlibs/darkenscreen.cpp
@@ -33,6 +33,7 @@
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/darkenscreen.h"
@@ -57,8 +58,6 @@ void DarkenScreen::close(int type) {
g_lingo->cleanupBuiltIns(builtins);
}
-void DarkenScreen::m_darkenscreen(int nargs) {
- g_lingo->printSTUBWithArglist("DarkenScreen::m_darkenscreen", nargs);
-}
+XOBJSTUBNR(DarkenScreen::m_darkenscreen)
} // End of namespace Director
diff --git a/engines/director/lingo/xlibs/dpwavi.cpp b/engines/director/lingo/xlibs/dpwavi.cpp
index f67b7a76252..442da24efeb 100644
--- a/engines/director/lingo/xlibs/dpwavi.cpp
+++ b/engines/director/lingo/xlibs/dpwavi.cpp
@@ -39,6 +39,7 @@
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/dpwavi.h"
diff --git a/engines/director/lingo/xlibs/draw.cpp b/engines/director/lingo/xlibs/draw.cpp
index 68c10f2c0b2..85eefb6be78 100644
--- a/engines/director/lingo/xlibs/draw.cpp
+++ b/engines/director/lingo/xlibs/draw.cpp
@@ -83,6 +83,7 @@
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/draw.h"
@@ -139,84 +140,21 @@ void DrawXObj::m_new(int nargs) {
g_lingo->push(g_lingo->_state->me);
}
-void DrawXObj::m_dispose(int nargs) {
- g_lingo->printSTUBWithArglist("DrawXObj::m_dispose", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void DrawXObj::m_line(int nargs) {
- g_lingo->printSTUBWithArglist("DrawXObj::m_line", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void DrawXObj::m_lineBrush(int nargs) {
- g_lingo->printSTUBWithArglist("DrawXObj::m_lineBrush", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void DrawXObj::m_lineBrushTrans(int nargs) {
- g_lingo->printSTUBWithArglist("DrawXObj::m_lineBrushTrans", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void DrawXObj::m_lineBrushCol(int nargs) {
- g_lingo->printSTUBWithArglist("DrawXObj::m_lineBrushCol", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void DrawXObj::m_filterBMP(int nargs) {
- g_lingo->printSTUBWithArglist("DrawXObj::m_filterBMP", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void DrawXObj::m_filterDIB(int nargs) {
- g_lingo->printSTUBWithArglist("DrawXObj::m_filterDIB", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void DrawXObj::m_filterBMP128(int nargs) {
- g_lingo->printSTUBWithArglist("DrawXObj::m_filterBMP128", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void DrawXObj::m_filterDIB128(int nargs) {
- g_lingo->printSTUBWithArglist("DrawXObj::m_filterDIB128", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void DrawXObj::m_filterBMPMakeGhostImage(int nargs) {
- g_lingo->printSTUBWithArglist("DrawXObj::m_filterBMPMakeGhostImage", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void DrawXObj::m_filterDIBMakeGhostImage(int nargs) {
- g_lingo->printSTUBWithArglist("DrawXObj::m_filterDIBMakeGhostImage", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void DrawXObj::m_emptyClipboard(int nargs) {
- g_lingo->printSTUBWithArglist("DrawXObj::m_emptyClipboard", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void DrawXObj::m_fill(int nargs) {
- g_lingo->printSTUBWithArglist("DrawXObj::m_fill", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void DrawXObj::m_getColor(int nargs) {
- g_lingo->printSTUBWithArglist("DrawXObj::m_getColor", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void DrawXObj::m_drawRect(int nargs) {
- g_lingo->printSTUBWithArglist("DrawXObj::m_drawRect", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void DrawXObj::m_drawFrame(int nargs) {
- g_lingo->printSTUBWithArglist("DrawXObj::m_drawFrame", nargs);
- g_lingo->dropStack(nargs);
-}
+XOBJSTUB(DrawXObj::m_dispose, 0)
+XOBJSTUB(DrawXObj::m_line, 0)
+XOBJSTUB(DrawXObj::m_lineBrush, 0)
+XOBJSTUB(DrawXObj::m_lineBrushTrans, 0)
+XOBJSTUB(DrawXObj::m_lineBrushCol, 0)
+XOBJSTUB(DrawXObj::m_filterBMP, 0)
+XOBJSTUB(DrawXObj::m_filterDIB, 0)
+XOBJSTUB(DrawXObj::m_filterBMP128, 0)
+XOBJSTUB(DrawXObj::m_filterDIB128, 0)
+XOBJSTUB(DrawXObj::m_filterBMPMakeGhostImage, 0)
+XOBJSTUB(DrawXObj::m_filterDIBMakeGhostImage, 0)
+XOBJSTUB(DrawXObj::m_emptyClipboard, 0)
+XOBJSTUB(DrawXObj::m_fill, 0)
+XOBJSTUB(DrawXObj::m_getColor, 0)
+XOBJSTUB(DrawXObj::m_drawRect, 0)
+XOBJSTUB(DrawXObj::m_drawFrame, 0)
} // End of namespace Director
diff --git a/engines/director/lingo/xlibs/ednox.cpp b/engines/director/lingo/xlibs/ednox.cpp
index b555c217b73..ab5544e896d 100644
--- a/engines/director/lingo/xlibs/ednox.cpp
+++ b/engines/director/lingo/xlibs/ednox.cpp
@@ -58,6 +58,7 @@
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/ednox.h"
@@ -118,9 +119,7 @@ void Ednox::m_new(int nargs) {
g_lingo->push(g_lingo->_state->me);
}
-void Ednox::m_dispose(int nargs) {
- g_lingo->printSTUBWithArglist("Ednox::m_dispose", nargs);
-}
+XOBJSTUBNR(Ednox::m_dispose)
void Ednox::m_getdocumentfile(int nargs) {
// Common::U32String hFile = g_lingo->pop().asString();
@@ -167,13 +166,8 @@ void Ednox::m_setdrivex(int nargs) {
g_lingo->push(Datum(0));
}
-void Ednox::m_checksoundx(int nargs) {
- g_lingo->printSTUBWithArglist("Ednox::m_checksoundx", nargs);
-}
-
-void Ednox::m_clearsoundx(int nargs) {
- g_lingo->printSTUBWithArglist("Ednox::m_clearsoundx", nargs);
-}
+XOBJSTUB(Ednox::m_checksoundx, "")
+XOBJSTUB(Ednox::m_clearsoundx, "")
void Ednox::m_deletedocumentfile(int nargs) {
// Common::U32String hFile = g_lingo->pop().asString();
@@ -182,19 +176,14 @@ void Ednox::m_deletedocumentfile(int nargs) {
g_lingo->dropStack(nargs);
}
-void Ednox::m_disabletaskswitch(int nargs) {
- g_lingo->printSTUBWithArglist("Ednox::m_disabletaskswitch", nargs);
-}
+XOBJSTUB(Ednox::m_enabletaskswitch, "")
+XOBJSTUB(Ednox::m_disabletaskswitch, "")
void Ednox::m_drawbkgndx(int nargs) {
// Common::U32String hBkgnd = g_lingo->pop().asString();
g_lingo->printSTUBWithArglist("Ednox::m_drawbkgndx", nargs);
}
-void Ednox::m_enabletaskswitch(int nargs) {
- g_lingo->printSTUBWithArglist("Ednox::m_enabletaskswitch", nargs);
-}
-
void Ednox::m_getdocumentname(int nargs) {
// Common::U32String hExt = g_lingo->pop().asString();
// Common::U32String hDir = g_lingo->pop().asString();
@@ -208,25 +197,15 @@ void Ednox::m_error(int nargs) {
g_lingo->dropStack(nargs);
}
-void Ednox::m_lasterror(int nargs) {
- g_lingo->printSTUBWithArglist("Ednox::m_lasterror", nargs);
-}
+XOBJSTUB(Ednox::m_lasterror, "")
void Ednox::m_name(int nargs) {
g_lingo->push(Datum("ednox"));
}
-void Ednox::m_status(int nargs) {
- g_lingo->printSTUBWithArglist("Ednox::m_status", nargs);
-}
-
-void Ednox::m_playsoundx(int nargs) {
- g_lingo->printSTUBWithArglist("Ednox::m_playsoundx", nargs);
-}
-
-void Ednox::m_restorex(int nargs) {
- g_lingo->printSTUBWithArglist("Ednox::m_restorex", nargs);
-}
+XOBJSTUB(Ednox::m_status, 0)
+XOBJSTUB(Ednox::m_playsoundx, "")
+XOBJSTUB(Ednox::m_restorex, "")
void Ednox::m_savex(int nargs) {
// Common::U32String hStrIn = g_lingo->pop().asString();
diff --git a/engines/director/lingo/xlibs/fileio.cpp b/engines/director/lingo/xlibs/fileio.cpp
index 1462469c9f2..51bf435642d 100644
--- a/engines/director/lingo/xlibs/fileio.cpp
+++ b/engines/director/lingo/xlibs/fileio.cpp
@@ -95,6 +95,7 @@
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/fileio.h"
namespace Director {
@@ -332,10 +333,7 @@ void FileIO::m_readWord(int nargs) {
FileIO::m_readToken(2);
}
-void FileIO::m_readPict(int nargs) {
- g_lingo->printSTUBWithArglist("FileIO::m_readPict", nargs);
- g_lingo->push(Datum(""));
-}
+XOBJSTUB(FileIO::m_readPict, "")
bool FileIO::charInMatchString(char ch, const Common::String &matchString) {
return matchString.contains(ch);
@@ -433,17 +431,8 @@ void FileIO::m_writeString(int nargs) {
// Getters/Setters
-void FileIO::m_getFinderInfo(int nargs) {
- g_lingo->printSTUBWithArglist("FileIO::m_getFinderInfo", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void FileIO::m_setFinderInfo(int nargs) {
- g_lingo->printSTUBWithArglist("FileIO::m_setFinderInfo", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
+XOBJSTUB(FileIO::m_getFinderInfo, "")
+XOBJSTUB(FileIO::m_setFinderInfo, 0)
void FileIO::m_getPosition(int nargs) {
FileObject *me = static_cast<FileObject *>(g_lingo->_state->me.u.obj);
@@ -515,17 +504,8 @@ void FileIO::m_fileName(int nargs) {
}
}
-void FileIO::m_error(int nargs) {
- g_lingo->printSTUBWithArglist("FileIO::m_error", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void FileIO::m_status(int nargs) {
- g_lingo->printSTUBWithArglist("FileIO::m_status", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
+XOBJSTUB(FileIO::m_error, "")
+XOBJSTUB(FileIO::m_status, 0)
// Other
diff --git a/engines/director/lingo/xlibs/fplayxobj.cpp b/engines/director/lingo/xlibs/fplayxobj.cpp
index 085994f7b45..260b9881c99 100644
--- a/engines/director/lingo/xlibs/fplayxobj.cpp
+++ b/engines/director/lingo/xlibs/fplayxobj.cpp
@@ -34,6 +34,7 @@
#include "director/sound.h"
#include "director/window.h"
#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/fplayxobj.h"
#include "audio/audiostream.h"
@@ -86,65 +87,16 @@ void FPlayXObj::b_fplay(int nargs) {
sound->playFPlaySound(arr);
}
-void FPlayXObj::b_copysnd(int nargs) {
- g_lingo->printSTUBWithArglist("FPlayXObj::b_copysnd", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void FPlayXObj::b_erasesnd(int nargs) {
- g_lingo->printSTUBWithArglist("FPlayXObj::b_erasesnd", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void FPlayXObj::b_pastesnd(int nargs) {
- g_lingo->printSTUBWithArglist("FPlayXObj::b_pastesnd", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void FPlayXObj::b_renamesnd(int nargs) {
- g_lingo->printSTUBWithArglist("FPlayXObj::b_renamesnd", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void FPlayXObj::b_duplicatesnd(int nargs) {
- g_lingo->printSTUBWithArglist("FPlayXObj::b_duplicatesnd", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void FPlayXObj::b_sndinfo(int nargs) {
- g_lingo->printSTUBWithArglist("FPlayXObj::b_sndinfo", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void FPlayXObj::b_sndlist(int nargs) {
- g_lingo->printSTUBWithArglist("FPlayXObj::b_sndlist", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void FPlayXObj::b_volume(int nargs) {
- g_lingo->printSTUBWithArglist("FPlayXObj::b_volume", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void FPlayXObj::b_filename(int nargs) {
- g_lingo->printSTUBWithArglist("FPlayXObj::b_filename", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void FPlayXObj::b_inputlevel(int nargs) {
- g_lingo->printSTUBWithArglist("FPlayXObj::b_inputlevel", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
+XOBJSTUBV(FPlayXObj::b_copysnd)
+XOBJSTUBV(FPlayXObj::b_erasesnd)
+XOBJSTUBV(FPlayXObj::b_pastesnd)
+XOBJSTUBV(FPlayXObj::b_renamesnd)
+XOBJSTUBV(FPlayXObj::b_duplicatesnd)
+XOBJSTUBV(FPlayXObj::b_sndinfo)
+XOBJSTUBV(FPlayXObj::b_sndlist)
+XOBJSTUBV(FPlayXObj::b_volume)
+XOBJSTUBV(FPlayXObj::b_filename)
+XOBJSTUBV(FPlayXObj::b_inputlevel)
void FPlayXObj::b_fsound(int nargs) {
if (nargs != 0) {
diff --git a/engines/director/lingo/xlibs/gpid.cpp b/engines/director/lingo/xlibs/gpid.cpp
index 55c9730d122..ad825650dc4 100644
--- a/engines/director/lingo/xlibs/gpid.cpp
+++ b/engines/director/lingo/xlibs/gpid.cpp
@@ -46,6 +46,7 @@
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/gpid.h"
@@ -58,7 +59,13 @@ const char *GpidXObj::fileNames[] = {
};
static MethodProto xlibMethods[] = {
- { "new", GpidXObj::m_new, 0, 0, 400 }, // D4
+ { "new", GpidXObj::m_new, 0, 0, 400 }, // D4
+ { "dispose", GpidXObj::m_dispose, 0, 0, 400 }, // D4
+ { "name", GpidXObj::m_name, 0, 0, 400 }, // D4
+ { "status", GpidXObj::m_status, 0, 0, 400 }, // D4
+ { "error", GpidXObj::m_error, 1, 1, 400 }, // D4
+ { "lastError", GpidXObj::m_lastError, 0, 0, 400 }, // D4
+ { "getPid", GpidXObj::m_getPid, 0, 0, 400 }, // D4
{ nullptr, nullptr, 0, 0, 0 }
};
@@ -87,4 +94,11 @@ void GpidXObj::m_new(int nargs) {
g_lingo->push(g_lingo->_state->me);
}
+XOBJSTUBNR(GpidXObj::m_dispose)
+XOBJSTUB(GpidXObj::m_name, "")
+XOBJSTUB(GpidXObj::m_status, 0)
+XOBJSTUB(GpidXObj::m_error, "")
+XOBJSTUB(GpidXObj::m_lastError, "")
+XOBJSTUB(GpidXObj::m_getPid, 0)
+
} // End of namespace Director
diff --git a/engines/director/lingo/xlibs/gpid.h b/engines/director/lingo/xlibs/gpid.h
index 077309c709d..6186959fdae 100644
--- a/engines/director/lingo/xlibs/gpid.h
+++ b/engines/director/lingo/xlibs/gpid.h
@@ -39,6 +39,12 @@ void open(int type);
void close(int type);
void m_new(int nargs);
+void m_dispose(int nargs);
+void m_name(int nargs);
+void m_status(int nargs);
+void m_error(int nargs);
+void m_lastError(int nargs);
+void m_getPid(int nargs);
} // End of namespace GpidXObj
diff --git a/engines/director/lingo/xlibs/jitdraw3.cpp b/engines/director/lingo/xlibs/jitdraw3.cpp
index 4ea7867a7aa..93494fe45b9 100644
--- a/engines/director/lingo/xlibs/jitdraw3.cpp
+++ b/engines/director/lingo/xlibs/jitdraw3.cpp
@@ -65,6 +65,7 @@
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/jitdraw3.h"
@@ -113,15 +114,8 @@ void JITDraw3XObj::m_new(int nargs) {
g_lingo->push(g_lingo->_state->me);
}
-void JITDraw3XObj::m_dispose(int nargs) {
- g_lingo->printSTUBWithArglist("JITDraw3XObj::m_dispose", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void JITDraw3XObj::m_checkifcdrom(int nargs) {
- g_lingo->printSTUBWithArglist("JITDraw3XObj::m_checkifcdrom", nargs);
- g_lingo->push(Datum(0));
-}
+XOBJSTUBNR(JITDraw3XObj::m_dispose)
+XOBJSTUB(JITDraw3XObj::m_checkifcdrom, 0)
void JITDraw3XObj::m_msgokcancel(int nargs) {
Common::U32String caption = g_lingo->pop().asString(); // Title of the message box
@@ -147,20 +141,8 @@ void JITDraw3XObj::m_msgyesno(int nargs) {
g_lingo->push(Datum(result == GUI::kMessageOK ? 1 : 0));
}
-void JITDraw3XObj::m_gotodraw(int nargs) {
- g_lingo->printSTUBWithArglist("JITDraw3XObj::m_gotodraw", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(3));
-}
-
-void JITDraw3XObj::m_adddrawbutton(int nargs) {
- g_lingo->printSTUBWithArglist("JITDraw3XObj::m_adddrawbutton", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void JITDraw3XObj::m_removedrawbutton(int nargs) {
- g_lingo->printSTUBWithArglist("JITDraw3XObj::m_removedrawbutton", nargs);
- g_lingo->dropStack(nargs);
-}
+XOBJSTUB(JITDraw3XObj::m_gotodraw, 3)
+XOBJSTUB(JITDraw3XObj::m_adddrawbutton, 0)
+XOBJSTUB(JITDraw3XObj::m_removedrawbutton, 0)
} // End of namespace Director
diff --git a/engines/director/lingo/xlibs/jwxini.cpp b/engines/director/lingo/xlibs/jwxini.cpp
index c8fdb69f49e..b83beb2c98b 100644
--- a/engines/director/lingo/xlibs/jwxini.cpp
+++ b/engines/director/lingo/xlibs/jwxini.cpp
@@ -43,6 +43,7 @@
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/jwxini.h"
@@ -126,17 +127,7 @@ void JourneyWareXINIXObj::m_GetProfileString(int nargs) {
g_lingo->push(Datum(defaultValue)); // TODO: We only return the default for now
}
-void JourneyWareXINIXObj::m_WritePrivateProfileString(int nargs) {
- g_lingo->printSTUBWithArglist("JWXIni::WritePrivateProfileString", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0)); // TODO
-}
-
-void JourneyWareXINIXObj::m_WriteProfileString(int nargs) {
- g_lingo->printSTUBWithArglist("JWXIni::WriteProfileString", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0)); // TODO
-}
-
+XOBJSTUB(JourneyWareXINIXObj::m_WritePrivateProfileString, 0)
+XOBJSTUB(JourneyWareXINIXObj::m_WriteProfileString, 0)
} // End of namespace Director
diff --git a/engines/director/lingo/xlibs/moovxobj.cpp b/engines/director/lingo/xlibs/moovxobj.cpp
index 45e9e9155d7..4a0b7f995d6 100644
--- a/engines/director/lingo/xlibs/moovxobj.cpp
+++ b/engines/director/lingo/xlibs/moovxobj.cpp
@@ -50,6 +50,7 @@
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/moovxobj.h"
@@ -118,15 +119,10 @@ void MoovXObj::m_dispose(int nargs) {
}
}
-void MoovXObj::m_name(int nargs) {
- // unused in C.H.A.O.S.
- g_lingo->printSTUBWithArglist("MoovXObj::m_name", nargs);
-}
-
-void MoovXObj::m_movieInit(int nargs) {
- // called in C.H.A.O.S. ScummVMs setup happens in playMovie
- g_lingo->printSTUBWithArglist("MoovXObj::m_movieInit", nargs);
-}
+// unused in C.H.A.O.S.
+XOBJSTUB(MoovXObj::m_name, "MoovXObj")
+// called in C.H.A.O.S. ScummVMs setup happens in playMovie
+XOBJSTUB(MoovXObj::m_movieInit, 0)
void MoovXObj::m_movieKill(int nargs) {
debug(5, "MoovXObj::m_movieKill");
@@ -172,21 +168,12 @@ void MoovXObj::m_playMovie(int nargs) {
me->_video->start();
}
-void MoovXObj::m_pauseMovie(int nargs) {
- // unused in C.H.A.O.S.
- g_lingo->printSTUBWithArglist("MoovXObj::m_pauseMovie", nargs);
-}
-
-void MoovXObj::m_soundMovie(int nargs) {
- // unused in C.H.A.O.S.
- g_lingo->printSTUBWithArglist("MoovXObj::m_soundMovie", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void MoovXObj::m_stopMovie(int nargs) {
- // unused in C.H.A.O.S.
- g_lingo->printSTUBWithArglist("MoovXObj::m_stopMovie", nargs);
-}
+// unused in C.H.A.O.S.
+XOBJSTUB(MoovXObj::m_pauseMovie, 0)
+// unused in C.H.A.O.S.
+XOBJSTUB(MoovXObj::m_soundMovie, 0)
+// unused in C.H.A.O.S.
+XOBJSTUB(MoovXObj::m_stopMovie, 0)
void MoovXObj::m_movieDone(int nargs) {
MoovXObject *me = static_cast<MoovXObject *>(g_lingo->_state->me.u.obj);
diff --git a/engines/director/lingo/xlibs/orthoplayxobj.cpp b/engines/director/lingo/xlibs/orthoplayxobj.cpp
index 3e2ef4e135b..ef3005653df 100644
--- a/engines/director/lingo/xlibs/orthoplayxobj.cpp
+++ b/engines/director/lingo/xlibs/orthoplayxobj.cpp
@@ -41,6 +41,7 @@
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/orthoplayxobj.h"
@@ -139,7 +140,6 @@ void OrthoPlayXObj::close(int type) {
}
}
-
OrthoPlayXObject::OrthoPlayXObject(ObjectType ObjectType) :Object<OrthoPlayXObject>("OrthoPlayXObj") {
_objType = ObjectType;
}
@@ -148,375 +148,72 @@ void OrthoPlayXObj::m_new(int nargs) {
g_lingo->push(g_lingo->_state->me);
}
-void OrthoPlayXObj::m_setSerialPort(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_setSerialPort", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_setInitViaDlog(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_setInitViaDlog", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_getInitInfo(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_getInitInfo", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_setInitInfo(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_setInitInfo", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_getMaxDevices(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_getMaxDevices", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_getDeviceTitle(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_getDeviceTitle", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_setDevice(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_setDevice", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_selectDevice(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_selectDevice", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_getDevice(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_getDevice", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_service(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_service", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_getValue(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_getValue", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_cancel(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_cancel", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_explain(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_explain", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_idle(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_idle", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_readStatus(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_readStatus", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_readPos(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_readPos", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_searchTo(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_searchTo", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_play(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_play", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_still(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_still", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_stop(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_stop", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_scanForward(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_scanForward", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_scanReverse(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_scanReverse", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_playReverse(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_playReverse", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_fastForward(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_fastForward", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_rewind(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_rewind", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_stepForward(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_stepForward", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_stepReverse(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_stepReverse", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_shuttle(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_shuttle", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_record(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_record", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_eject(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_eject", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_prepareMedium(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_prepareMedium", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_getFirstTrack(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_getFirstTrack", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_getLastTrack(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_getLastTrack", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_getFirstFrame(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_getFirstFrame", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_getLastFrame(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_getLastFrame", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_getTrack(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_getTrack", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_resetCounter(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_resetCounter", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_audioEnable(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_audioEnable", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_audioMute(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_audioMute", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_videoEnable(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_videoEnable", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_showFrame(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_showFrame", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_getFrameResolution(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_getFrameResolution", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_setFrameResolution(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_setFrameResolution", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_hasDropFrames(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_hasDropFrames", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_sendRaw(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_sendRaw", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_readRaw(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_readRaw", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_setInPoint(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_setInPoint", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_setOutPoint(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_setOutPoint", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_setDuration(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_setDuration", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_getMinDuration(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_getMinDuration", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_setPreroll(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_setPreroll", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_getPreroll(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_getPreroll", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_setPostroll(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_setPostroll", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_getPostroll(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_getPostroll", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_setFieldDominance(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_setFieldDominance", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_playCue(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_playCue", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_playSegment(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_playSegment", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_recordCue(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_recordCue", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_recordSegment(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_recordSegment", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_recordVideoEnable(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_recordVideoEnable", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_recordAudioEnable(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_recordAudioEnable", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_assembleRecord(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_assembleRecord", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_previewRecord(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_previewRecord", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void OrthoPlayXObj::m_gotoInPoint(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_gotoInPoint", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_gotoOutPoint(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_gotoOutPoint", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_gotoPrerollPoint(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_gotoPrerollPoint", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void OrthoPlayXObj::m_gotoPostrollPoint(int nargs) {
- g_lingo->printSTUBWithArglist("OrthoPlayXObj::m_gotoPostrollPoint", nargs);
- g_lingo->dropStack(nargs);
-}
-
-
+XOBJSTUBV(OrthoPlayXObj::m_setSerialPort)
+XOBJSTUBV(OrthoPlayXObj::m_setInitViaDlog)
+XOBJSTUBV(OrthoPlayXObj::m_getInitInfo)
+XOBJSTUBV(OrthoPlayXObj::m_setInitInfo)
+XOBJSTUBV(OrthoPlayXObj::m_getMaxDevices)
+XOBJSTUBV(OrthoPlayXObj::m_getDeviceTitle)
+XOBJSTUBV(OrthoPlayXObj::m_setDevice)
+XOBJSTUBV(OrthoPlayXObj::m_selectDevice)
+XOBJSTUBV(OrthoPlayXObj::m_getDevice)
+XOBJSTUBV(OrthoPlayXObj::m_service)
+XOBJSTUBV(OrthoPlayXObj::m_getValue)
+XOBJSTUBV(OrthoPlayXObj::m_cancel)
+XOBJSTUBV(OrthoPlayXObj::m_explain)
+XOBJSTUBV(OrthoPlayXObj::m_idle)
+XOBJSTUBNR(OrthoPlayXObj::m_readStatus)
+XOBJSTUBNR(OrthoPlayXObj::m_readPos)
+XOBJSTUBNR(OrthoPlayXObj::m_searchTo)
+XOBJSTUBNR(OrthoPlayXObj::m_play)
+XOBJSTUBNR(OrthoPlayXObj::m_still)
+XOBJSTUBNR(OrthoPlayXObj::m_stop)
+XOBJSTUBNR(OrthoPlayXObj::m_scanForward)
+XOBJSTUBNR(OrthoPlayXObj::m_scanReverse)
+XOBJSTUBNR(OrthoPlayXObj::m_playReverse)
+XOBJSTUBNR(OrthoPlayXObj::m_fastForward)
+XOBJSTUBNR(OrthoPlayXObj::m_rewind)
+XOBJSTUBNR(OrthoPlayXObj::m_stepForward)
+XOBJSTUBNR(OrthoPlayXObj::m_stepReverse)
+XOBJSTUBNR(OrthoPlayXObj::m_shuttle)
+XOBJSTUBNR(OrthoPlayXObj::m_record)
+XOBJSTUBNR(OrthoPlayXObj::m_eject)
+XOBJSTUBNR(OrthoPlayXObj::m_prepareMedium)
+XOBJSTUBV(OrthoPlayXObj::m_getFirstTrack)
+XOBJSTUBV(OrthoPlayXObj::m_getLastTrack)
+XOBJSTUBV(OrthoPlayXObj::m_getFirstFrame)
+XOBJSTUBV(OrthoPlayXObj::m_getLastFrame)
+XOBJSTUBV(OrthoPlayXObj::m_getTrack)
+XOBJSTUBNR(OrthoPlayXObj::m_resetCounter)
+XOBJSTUBNR(OrthoPlayXObj::m_audioEnable)
+XOBJSTUBNR(OrthoPlayXObj::m_audioMute)
+XOBJSTUBNR(OrthoPlayXObj::m_videoEnable)
+XOBJSTUBNR(OrthoPlayXObj::m_showFrame)
+XOBJSTUBV(OrthoPlayXObj::m_getFrameResolution)
+XOBJSTUBV(OrthoPlayXObj::m_setFrameResolution)
+XOBJSTUBV(OrthoPlayXObj::m_hasDropFrames)
+XOBJSTUBNR(OrthoPlayXObj::m_sendRaw)
+XOBJSTUBV(OrthoPlayXObj::m_readRaw)
+XOBJSTUBV(OrthoPlayXObj::m_setInPoint)
+XOBJSTUBV(OrthoPlayXObj::m_setOutPoint)
+XOBJSTUBV(OrthoPlayXObj::m_setDuration)
+XOBJSTUBV(OrthoPlayXObj::m_getMinDuration)
+XOBJSTUBV(OrthoPlayXObj::m_setPreroll)
+XOBJSTUBV(OrthoPlayXObj::m_getPreroll)
+XOBJSTUBV(OrthoPlayXObj::m_setPostroll)
+XOBJSTUBV(OrthoPlayXObj::m_getPostroll)
+XOBJSTUBV(OrthoPlayXObj::m_setFieldDominance)
+XOBJSTUBNR(OrthoPlayXObj::m_playCue)
+XOBJSTUBNR(OrthoPlayXObj::m_playSegment)
+XOBJSTUBNR(OrthoPlayXObj::m_recordCue)
+XOBJSTUBNR(OrthoPlayXObj::m_recordSegment)
+XOBJSTUBNR(OrthoPlayXObj::m_recordVideoEnable)
+XOBJSTUBNR(OrthoPlayXObj::m_recordAudioEnable)
+XOBJSTUBV(OrthoPlayXObj::m_assembleRecord)
+XOBJSTUBV(OrthoPlayXObj::m_previewRecord)
+XOBJSTUBNR(OrthoPlayXObj::m_gotoInPoint)
+XOBJSTUBNR(OrthoPlayXObj::m_gotoOutPoint)
+XOBJSTUBNR(OrthoPlayXObj::m_gotoPrerollPoint)
+XOBJSTUBNR(OrthoPlayXObj::m_gotoPostrollPoint)
} // End of namespace Director
diff --git a/engines/director/lingo/xlibs/popupmenuxobj.cpp b/engines/director/lingo/xlibs/popupmenuxobj.cpp
index 0e975281b4b..8585b8f2848 100644
--- a/engines/director/lingo/xlibs/popupmenuxobj.cpp
+++ b/engines/director/lingo/xlibs/popupmenuxobj.cpp
@@ -104,6 +104,7 @@
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/popupmenuxobj.h"
@@ -158,63 +159,16 @@ void PopUpMenuXObj::m_new(int nargs) {
g_lingo->push(g_lingo->_state->me);
}
-void PopUpMenuXObj::m_appendMenu(int nargs) {
- g_lingo->printSTUBWithArglist("PopUpMenuXObj::m_appendMenu", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void PopUpMenuXObj::m_disableItem(int nargs) {
- g_lingo->printSTUBWithArglist("PopUpMenuXObj::m_disableItem", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void PopUpMenuXObj::m_enableItem(int nargs) {
- g_lingo->printSTUBWithArglist("PopUpMenuXObj::m_enableItem", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void PopUpMenuXObj::m_getItem(int nargs) {
- g_lingo->printSTUBWithArglist("PopUpMenuXObj::m_getItem", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void PopUpMenuXObj::m_getMenuID(int nargs) {
- g_lingo->printSTUBWithArglist("PopUpMenuXObj::m_getMenuID", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void PopUpMenuXObj::m_popNum(int nargs) {
- g_lingo->printSTUBWithArglist("PopUpMenuXObj::m_popNum", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void PopUpMenuXObj::m_popText(int nargs) {
- g_lingo->printSTUBWithArglist("PopUpMenuXObj::m_popText", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void PopUpMenuXObj::m_setItem(int nargs) {
- g_lingo->printSTUBWithArglist("PopUpMenuXObj::m_setItem", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void PopUpMenuXObj::m_setItemMark(int nargs) {
- g_lingo->printSTUBWithArglist("PopUpMenuXObj::m_setItemMark", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void PopUpMenuXObj::m_smart(int nargs) {
- g_lingo->printSTUBWithArglist("PopUpMenuXObj::m_smart", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void PopUpMenuXObj::m_setItemIcon(int nargs) {
- g_lingo->printSTUBWithArglist("PopUpMenuXObj::m_setItemIcon", nargs);
- g_lingo->dropStack(nargs);
-}
+XOBJSTUBNR(PopUpMenuXObj::m_appendMenu)
+XOBJSTUBNR(PopUpMenuXObj::m_disableItem)
+XOBJSTUBNR(PopUpMenuXObj::m_enableItem)
+XOBJSTUB(PopUpMenuXObj::m_getItem, "")
+XOBJSTUB(PopUpMenuXObj::m_getMenuID, 0)
+XOBJSTUB(PopUpMenuXObj::m_popNum, 0)
+XOBJSTUB(PopUpMenuXObj::m_popText, "")
+XOBJSTUBNR(PopUpMenuXObj::m_setItem)
+XOBJSTUBNR(PopUpMenuXObj::m_setItemMark)
+XOBJSTUBNR(PopUpMenuXObj::m_smart)
+XOBJSTUBNR(PopUpMenuXObj::m_setItemIcon)
} // End of namespace Director
diff --git a/engines/director/lingo/xlibs/printomatic.cpp b/engines/director/lingo/xlibs/printomatic.cpp
index 9ab947c8c2b..7060e6d2776 100644
--- a/engines/director/lingo/xlibs/printomatic.cpp
+++ b/engines/director/lingo/xlibs/printomatic.cpp
@@ -106,6 +106,7 @@ IS mRegister, serialNumber
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/printomatic.h"
@@ -164,57 +165,21 @@ void PrintOMaticXObj::m_new(int nargs) {
g_lingo->push(g_lingo->_state->me);
}
-void PrintOMaticXObj::m_dispose(int nargs) {
- g_lingo->printSTUBWithArglist("PrintOMaticXObj::m_dispose", nargs);
- g_lingo->dropStack(nargs);
-}
+XOBJSTUBNR(PrintOMaticXObj::m_dispose)
void PrintOMaticXObj::m_register(int nargs) {
Common::String serialNumber = g_lingo->pop().asString();
warning("PrintOMaticXObj::m_register: Registered with serial \"%s\"", serialNumber.c_str());
}
-void PrintOMaticXObj::m_reset(int nargs) {
- g_lingo->printSTUBWithArglist("PrintOMaticXObj::m_reset", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void PrintOMaticXObj::m_newPage(int nargs) {
- g_lingo->printSTUBWithArglist("PrintOMaticXObj::m_newPage", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void PrintOMaticXObj::m_setPrintableMargins(int nargs) {
- g_lingo->printSTUBWithArglist("PrintOMaticXObj::m_setPrintableMargins", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void PrintOMaticXObj::m_getPageWidth(int nargs) {
- g_lingo->printSTUBWithArglist("PrintOMaticXObj::m_getPageWidth", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(-1));
-}
-
-void PrintOMaticXObj::m_getPageHeight(int nargs) {
- g_lingo->printSTUBWithArglist("PrintOMaticXObj::m_getPageHeight", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(-1));
-}
-
-void PrintOMaticXObj::m_picture(int nargs) {
- g_lingo->printSTUBWithArglist("PrintOMaticXObj::m_picture", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void PrintOMaticXObj::m_stagePicture(int nargs) {
- g_lingo->printSTUBWithArglist("PrintOMaticXObj::m_stagePicture", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void PrintOMaticXObj::m_1bitStagePicture(int nargs) {
- g_lingo->printSTUBWithArglist("PrintOMaticXObj::m_1bitStagePicture", nargs);
- g_lingo->dropStack(nargs);
-}
+XOBJSTUBNR(PrintOMaticXObj::m_reset)
+XOBJSTUB(PrintOMaticXObj::m_newPage, 0)
+XOBJSTUBNR(PrintOMaticXObj::m_setPrintableMargins)
+XOBJSTUB(PrintOMaticXObj::m_getPageWidth, -1)
+XOBJSTUB(PrintOMaticXObj::m_getPageHeight, -1)
+XOBJSTUBV(PrintOMaticXObj::m_picture)
+XOBJSTUBV(PrintOMaticXObj::m_stagePicture)
+XOBJSTUBV(PrintOMaticXObj::m_1bitStagePicture)
void PrintOMaticXObj::m_setLandscapeMode(int nargs) {
// int trueOrFalse = g_lingo->pop.asInt()
@@ -222,36 +187,11 @@ void PrintOMaticXObj::m_setLandscapeMode(int nargs) {
g_lingo->dropStack(nargs);
}
-void PrintOMaticXObj::m_doPageSetup(int nargs) {
- g_lingo->printSTUBWithArglist("PrintOMaticXObj::m_doPageSetup", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(1));
-}
-
-void PrintOMaticXObj::m_doJobSetup(int nargs) {
- g_lingo->printSTUBWithArglist("PrintOMaticXObj::m_doJobSetup", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(1));
-}
-
-void PrintOMaticXObj::m_setProgressMsg(int nargs) {
- g_lingo->printSTUBWithArglist("PrintOMaticXObj::m_setProgressMsg", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void PrintOMaticXObj::m_printPreview(int nargs) {
- g_lingo->printSTUBWithArglist("PrintOMaticXObj::m_printPreview", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void PrintOMaticXObj::m_printPicts(int nargs) {
- g_lingo->printSTUBWithArglist("PrintOMaticXObj::m_printPicts", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void PrintOMaticXObj::m_print(int nargs) {
- g_lingo->printSTUBWithArglist("PrintOMaticXObj::m_print", nargs);
- g_lingo->dropStack(nargs);
-}
+XOBJSTUB(PrintOMaticXObj::m_doPageSetup, 1)
+XOBJSTUB(PrintOMaticXObj::m_doJobSetup, 1)
+XOBJSTUBNR(PrintOMaticXObj::m_setProgressMsg)
+XOBJSTUB(PrintOMaticXObj::m_printPreview, 0)
+XOBJSTUBV(PrintOMaticXObj::m_printPicts)
+XOBJSTUBNR(PrintOMaticXObj::m_print)
} // End of namespace Director
diff --git a/engines/director/lingo/xlibs/qtmovie.cpp b/engines/director/lingo/xlibs/qtmovie.cpp
index 19bf4e9e2b8..da0b64c1cc1 100644
--- a/engines/director/lingo/xlibs/qtmovie.cpp
+++ b/engines/director/lingo/xlibs/qtmovie.cpp
@@ -33,6 +33,7 @@
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/qtmovie.h"
@@ -57,9 +58,6 @@ void QTMovie::close(int type) {
g_lingo->cleanupBuiltIns(builtins);
}
-void QTMovie::m_qtmovie(int nargs) {
- g_lingo->printSTUBWithArglist("QTMovie::m_qtmovie", nargs);
- g_lingo->dropStack(nargs);
-}
+XOBJSTUBNR(QTMovie::m_qtmovie)
} // End of namespace Director
diff --git a/engines/director/lingo/xlibs/serialportxobj.cpp b/engines/director/lingo/xlibs/serialportxobj.cpp
index e7ad7d2c542..7e75f2ae0c3 100644
--- a/engines/director/lingo/xlibs/serialportxobj.cpp
+++ b/engines/director/lingo/xlibs/serialportxobj.cpp
@@ -38,6 +38,7 @@
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/serialportxobj.h"
namespace Director {
@@ -89,66 +90,15 @@ void SerialPortXObj::m_new(int nargs) {
g_lingo->push(g_lingo->_state->me);
}
-void SerialPortXObj::m_getPortNum(int nargs) {
- g_lingo->printSTUBWithArglist("SerialPortXObj::m_getPortNum", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void SerialPortXObj::m_writeString(int nargs) {
- g_lingo->printSTUBWithArglist("SerialPortXObj::m_writeString", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void SerialPortXObj::m_writeChar(int nargs) {
- g_lingo->printSTUBWithArglist("SerialPortXObj::m_writeChar", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void SerialPortXObj::m_readString(int nargs) {
- g_lingo->printSTUBWithArglist("SerialPortXObj::m_readString", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void SerialPortXObj::m_readChar(int nargs) {
- g_lingo->printSTUBWithArglist("SerialPortXObj::m_readChar", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void SerialPortXObj::m_readCount(int nargs) {
- g_lingo->printSTUBWithArglist("SerialPortXObj::m_readCount", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void SerialPortXObj::m_readFlush(int nargs) {
- g_lingo->printSTUBWithArglist("SerialPortXObj::m_readFlush", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void SerialPortXObj::m_configChan(int nargs) {
- g_lingo->printSTUBWithArglist("SerialPortXObj::m_configChan", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void SerialPortXObj::m_hShakeChan(int nargs) {
- g_lingo->printSTUBWithArglist("SerialPortXObj::m_hShakeChan", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void SerialPortXObj::m_setUp(int nargs) {
- g_lingo->printSTUBWithArglist("SerialPortXObj::m_setUp", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-
+XOBJSTUBV(SerialPortXObj::m_getPortNum)
+XOBJSTUBV(SerialPortXObj::m_writeString)
+XOBJSTUBV(SerialPortXObj::m_writeChar)
+XOBJSTUBV(SerialPortXObj::m_readString)
+XOBJSTUBV(SerialPortXObj::m_readChar)
+XOBJSTUBV(SerialPortXObj::m_readCount)
+XOBJSTUBV(SerialPortXObj::m_readFlush)
+XOBJSTUBV(SerialPortXObj::m_configChan)
+XOBJSTUBV(SerialPortXObj::m_hShakeChan)
+XOBJSTUBV(SerialPortXObj::m_setUp)
} // End of namespace Director
diff --git a/engines/director/lingo/xlibs/soundjam.cpp b/engines/director/lingo/xlibs/soundjam.cpp
index 7cf8c1c7508..e7f20996ea5 100644
--- a/engines/director/lingo/xlibs/soundjam.cpp
+++ b/engines/director/lingo/xlibs/soundjam.cpp
@@ -107,11 +107,7 @@ void SoundJam::m_new(int nargs) {
g_lingo->push(g_lingo->_state->me);
}
-void SoundJam::m_defineFileSound(int nargs) {
- g_lingo->printSTUBWithArglist("SoundJam::m_defineFileSound", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
+XOBJSTUB(SoundJam::m_defineFileSound, 0)
void SoundJam::m_defineCastSound(int nargs) {
SoundJamObject *me = static_cast<SoundJamObject *>(g_lingo->_state->me.u.obj);
@@ -147,17 +143,8 @@ void SoundJam::m_undefineSound(int nargs) {
g_lingo->push(0); // success
}
-void SoundJam::m_readSome(int nargs) {
- g_lingo->printSTUBWithArglist("SoundJam::m_readSome", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void SoundJam::m_startSound(int nargs) {
- g_lingo->printSTUBWithArglist("SoundJam::m_startSound", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
+XOBJSTUB(SoundJam::m_readSome, 0)
+XOBJSTUB(SoundJam::m_startSound, 0)
void SoundJam::m_switchNew(int nargs) {
SoundJamObject *me = static_cast<SoundJamObject *>(g_lingo->_state->me.u.obj);
@@ -175,22 +162,9 @@ void SoundJam::m_switchNew(int nargs) {
g_lingo->push(0); // success
}
-void SoundJam::m_switchParallel(int nargs) {
- g_lingo->printSTUBWithArglist("SoundJam::m_switchParallel", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void SoundJam::m_hasSwitchHappened(int nargs) {
- g_lingo->printSTUBWithArglist("SoundJam::m_hasSwitchHappened", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void SoundJam::m_toggleMute(int nargs) {
- g_lingo->printSTUBWithArglist("SoundJam::m_toggleMute", nargs);
- g_lingo->dropStack(nargs);
-}
+XOBJSTUB(SoundJam::m_switchParallel, 0)
+XOBJSTUB(SoundJam::m_hasSwitchHappened, 0)
+XOBJSTUBNR(SoundJam::m_toggleMute)
void SoundJam::m_stop(int nargs) {
DirectorSound *sound = g_director->getCurrentWindow()->getSoundManager();
diff --git a/engines/director/lingo/xlibs/spacemgr.cpp b/engines/director/lingo/xlibs/spacemgr.cpp
index c149511ee58..2e4782dd982 100644
--- a/engines/director/lingo/xlibs/spacemgr.cpp
+++ b/engines/director/lingo/xlibs/spacemgr.cpp
@@ -76,6 +76,7 @@
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/spacemgr.h"
#include "director/util.h"
@@ -162,34 +163,11 @@ void SpaceMgr::m_new(int nargs) {
g_lingo->push(g_lingo->_state->me);
}
-void SpaceMgr::m_dispose(int nargs) {
- g_lingo->printSTUBWithArglist("SpaceMgr::m_dispose", nargs);
- g_lingo->dropStack(nargs);
-}
-
-void SpaceMgr::m_lastError(int nargs) {
- g_lingo->printSTUBWithArglist("SpaceMgr::m_lastError", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
-
-void SpaceMgr::m_memUsed(int nargs) {
- g_lingo->printSTUBWithArglist("SpaceMgr::m_memUsed", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
-
-void SpaceMgr::m_listData(int nargs) {
- g_lingo->printSTUBWithArglist("SpaceMgr::m_listData", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(""));
-}
-
-void SpaceMgr::m_sortAll(int nargs) {
- g_lingo->printSTUBWithArglist("SpaceMgr::m_sortAll", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
+XOBJSTUBNR(SpaceMgr::m_dispose)
+XOBJSTUB(SpaceMgr::m_lastError, 0)
+XOBJSTUB(SpaceMgr::m_memUsed, 0)
+XOBJSTUB(SpaceMgr::m_listData, "")
+XOBJSTUB(SpaceMgr::m_sortAll, 0)
void SpaceMgr::m_checkForDups(int nargs) {
if (nargs != 1) {
@@ -354,11 +332,7 @@ void SpaceMgr::m_setCurData(int nargs) {
g_lingo->push(Datum(0));
}
-void SpaceMgr::m_addSpaceCollection(int nargs) {
- g_lingo->printSTUBWithArglist("SpaceMgr::m_addSpaceCollection", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
+XOBJSTUB(SpaceMgr::m_addSpaceCollection, 0)
void SpaceMgr::m_removeSpaceCollection(int nargs) {
if (nargs != 1) {
@@ -433,17 +407,8 @@ void SpaceMgr::m_getSpaceCollection(int nargs) {
g_lingo->push(Datum(result));
}
-void SpaceMgr::m_addSpace(int nargs) {
- g_lingo->printSTUBWithArglist("SpaceMgr::m_addSpace", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
-
-void SpaceMgr::m_removeSpace(int nargs) {
- g_lingo->printSTUBWithArglist("SpaceMgr::m_removeSpace", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
+XOBJSTUB(SpaceMgr::m_addSpace, 0)
+XOBJSTUB(SpaceMgr::m_removeSpace, 0)
void SpaceMgr::m_setCurSpace(int nargs) {
if (nargs != 1) {
@@ -507,17 +472,8 @@ void SpaceMgr::m_getSpace(int nargs) {
g_lingo->push(Datum(result));
}
-void SpaceMgr::m_addNode(int nargs) {
- g_lingo->printSTUBWithArglist("SpaceMgr::m_addNode", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
-
-void SpaceMgr::m_removeNode(int nargs) {
- g_lingo->printSTUBWithArglist("SpaceMgr::m_removeNode", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
+XOBJSTUB(SpaceMgr::m_addNode, 0)
+XOBJSTUB(SpaceMgr::m_removeNode, 0)
void SpaceMgr::m_setCurNode(int nargs) {
if (nargs != 1) {
@@ -587,17 +543,8 @@ void SpaceMgr::m_getNode(int nargs) {
g_lingo->push(Datum(result));
}
-void SpaceMgr::m_addView(int nargs) {
- g_lingo->printSTUBWithArglist("SpaceMgr::m_addView", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
-
-void SpaceMgr::m_removeView(int nargs) {
- g_lingo->printSTUBWithArglist("SpaceMgr::m_removeView", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
+XOBJSTUB(SpaceMgr::m_addView, 0)
+XOBJSTUB(SpaceMgr::m_removeView, 0)
void SpaceMgr::m_setCurView(int nargs) {
if (nargs != 1) {
@@ -673,23 +620,9 @@ void SpaceMgr::m_getView(int nargs) {
g_lingo->push(Datum(result));
}
-void SpaceMgr::m_addLocalLink(int nargs) {
- g_lingo->printSTUBWithArglist("SpaceMgr::m_addLocalLink", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
-
-void SpaceMgr::m_removeLocalLink(int nargs) {
- g_lingo->printSTUBWithArglist("SpaceMgr::m_removeLocalLink", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
-
-void SpaceMgr::m_removeLocalLinks(int nargs) {
- g_lingo->printSTUBWithArglist("SpaceMgr::m_removeLocalLinks", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
+XOBJSTUB(SpaceMgr::m_addLocalLink, 0)
+XOBJSTUB(SpaceMgr::m_removeLocalLink, 0)
+XOBJSTUB(SpaceMgr::m_removeLocalLinks, 0)
void SpaceMgr::m_getLocalLink(int nargs) {
if (nargs != 1) {
@@ -721,34 +654,10 @@ void SpaceMgr::m_getLocalLink(int nargs) {
g_lingo->push(Datum(result));
}
-void SpaceMgr::m_getLocalLinks(int nargs) {
- g_lingo->printSTUBWithArglist("SpaceMgr::m_getLocalLinks", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(""));
-}
-
-void SpaceMgr::m_addGlobalLink(int nargs) {
- g_lingo->printSTUBWithArglist("SpaceMgr::m_addGlobalLink", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(""));
-}
-
-void SpaceMgr::m_removeGlobalLink(int nargs) {
- g_lingo->printSTUBWithArglist("SpaceMgr::m_removeGlobalLink", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(0));
-}
-
-void SpaceMgr::m_getGlobalLink(int nargs) {
- g_lingo->printSTUBWithArglist("SpaceMgr::m_getGlobalLink", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(""));
-}
-
-void SpaceMgr::m_getGlobalLinks(int nargs) {
- g_lingo->printSTUBWithArglist("SpaceMgr::m_getGlobalLinks", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(""));
-}
+XOBJSTUB(SpaceMgr::m_getLocalLinks, "")
+XOBJSTUB(SpaceMgr::m_addGlobalLink, "")
+XOBJSTUB(SpaceMgr::m_removeGlobalLink, 0)
+XOBJSTUB(SpaceMgr::m_getGlobalLink, "")
+XOBJSTUB(SpaceMgr::m_getGlobalLinks, "")
} // End of namespace Director
diff --git a/engines/director/lingo/xlibs/valkyrie.cpp b/engines/director/lingo/xlibs/valkyrie.cpp
index d6bc50b7d5d..9a563eb4acf 100644
--- a/engines/director/lingo/xlibs/valkyrie.cpp
+++ b/engines/director/lingo/xlibs/valkyrie.cpp
@@ -41,6 +41,7 @@
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/valkyrie.h"
@@ -88,19 +89,13 @@ void ValkyrieXObj::m_new(int nargs) {
g_lingo->push(g_lingo->_state->me);
}
-void ValkyrieXObj::m_dispose(int nargs) {
- g_lingo->printSTUBWithArglist("ValkyrieXObj::m_dispose", nargs);
- g_lingo->dropStack(nargs);
-}
+XOBJSTUBNR(ValkyrieXObj::m_dispose)
void ValkyrieXObj::m_name(int nargs) {
g_lingo->push(Datum("Valkyrie"));
}
-void ValkyrieXObj::m_status(int nargs) {
- g_lingo->printSTUBWithArglist("ValkyrieXObj::m_status", nargs);
- g_lingo->dropStack(nargs);
-}
+XOBJSTUB(ValkyrieXObj::m_status, 0)
void ValkyrieXObj::m_error(int nargs) {
// TODO: Save error code for m_lastError?
@@ -108,10 +103,7 @@ void ValkyrieXObj::m_error(int nargs) {
warning("ValkyrieXObj::m_error: Got error %d", errorCode);
}
-void ValkyrieXObj::m_lastError(int nargs) {
- g_lingo->printSTUBWithArglist("ValkyrieXObj::m_lastError", nargs);
- g_lingo->dropStack(nargs);
-}
+XOBJSTUB(ValkyrieXObj::m_lastError, "")
void ValkyrieXObj::m_save(int nargs) {
// should write to namco.ini > Valkyrie > Data
diff --git a/engines/director/lingo/xlibs/videodiscxobj.cpp b/engines/director/lingo/xlibs/videodiscxobj.cpp
index 7b838792e9b..c7ebe9d618d 100644
--- a/engines/director/lingo/xlibs/videodiscxobj.cpp
+++ b/engines/director/lingo/xlibs/videodiscxobj.cpp
@@ -110,6 +110,7 @@
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/videodiscxobj.h"
namespace Director {
@@ -176,149 +177,29 @@ void VideodiscXObj::m_new(int nargs) {
g_lingo->push(g_lingo->_state->me);
}
-void VideodiscXObj::m_name(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_name", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_player(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_player", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_play(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_play", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_playRev(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_playRev", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_fastFwd(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_fastFwd", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_fastRev(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_fastRev", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_slowFwd(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_slowFwd", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_slowRev(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_slowRev", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_stepFwd(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_stepFwd", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_stepRev(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_stepRev", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_playJog(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_playJog", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_playSpeed(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_playSpeed", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_playSegment(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_playSegment", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_pause(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_pause", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_stop(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_stop", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_eject(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_eject", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_stopAtFrame(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_stopAtFrame", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_searchWait(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_searchWait", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_readPos(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_readPos", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_showDisplay(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_showDisplay", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_clear(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_clear", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_videoControl(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_videoControl", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_audioControl(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_audioControl", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
-void VideodiscXObj::m_status(int nargs) {
- g_lingo->printSTUBWithArglist("VideodiscXObj::m_status", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum());
-}
-
+XOBJSTUBV(VideodiscXObj::m_name)
+XOBJSTUBV(VideodiscXObj::m_player)
+XOBJSTUBV(VideodiscXObj::m_play)
+XOBJSTUBV(VideodiscXObj::m_playRev)
+XOBJSTUBV(VideodiscXObj::m_fastFwd)
+XOBJSTUBV(VideodiscXObj::m_fastRev)
+XOBJSTUBV(VideodiscXObj::m_slowFwd)
+XOBJSTUBV(VideodiscXObj::m_slowRev)
+XOBJSTUBV(VideodiscXObj::m_stepFwd)
+XOBJSTUBV(VideodiscXObj::m_stepRev)
+XOBJSTUBV(VideodiscXObj::m_playJog)
+XOBJSTUBV(VideodiscXObj::m_playSpeed)
+XOBJSTUBV(VideodiscXObj::m_playSegment)
+XOBJSTUBV(VideodiscXObj::m_pause)
+XOBJSTUBV(VideodiscXObj::m_stop)
+XOBJSTUBV(VideodiscXObj::m_eject)
+XOBJSTUBV(VideodiscXObj::m_stopAtFrame)
+XOBJSTUBV(VideodiscXObj::m_searchWait)
+XOBJSTUBV(VideodiscXObj::m_readPos)
+XOBJSTUBV(VideodiscXObj::m_showDisplay)
+XOBJSTUBV(VideodiscXObj::m_clear)
+XOBJSTUBV(VideodiscXObj::m_videoControl)
+XOBJSTUBV(VideodiscXObj::m_audioControl)
+XOBJSTUBV(VideodiscXObj::m_status)
} // End of namespace Director
diff --git a/engines/director/lingo/xlibs/widgetxobj.cpp b/engines/director/lingo/xlibs/widgetxobj.cpp
index 13d5a4c4db0..aa73ac6ee4d 100644
--- a/engines/director/lingo/xlibs/widgetxobj.cpp
+++ b/engines/director/lingo/xlibs/widgetxobj.cpp
@@ -39,6 +39,7 @@
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/widgetxobj.h"
namespace Director {
@@ -91,9 +92,6 @@ void WidgetXObj::m_getPro(int nargs) {
g_lingo->push(Datum("D"));
}
-void WidgetXObj::m_askQuit(int nargs) {
- g_lingo->printSTUBWithArglist("WidgetXObj::m_askQuit", nargs);
- g_lingo->dropStack(nargs);
-}
+XOBJSTUB(WidgetXObj::m_askQuit, 0)
} // End of namespace Director
diff --git a/engines/director/lingo/xlibs/xio.cpp b/engines/director/lingo/xlibs/xio.cpp
index bb021310a50..7db98f03dd4 100644
--- a/engines/director/lingo/xlibs/xio.cpp
+++ b/engines/director/lingo/xlibs/xio.cpp
@@ -39,6 +39,7 @@
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/xio.h"
@@ -94,19 +95,7 @@ void XioXObj::m_unlock(int nargs) {
g_lingo->push(Datum(1));
}
-void XioXObj::m_deleteFile(int nargs) {
- // Common::String filename = g_lingo->pop().asString();
- g_lingo->printSTUBWithArglist("XioXObj::m_deleteFile", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(1));
-}
-
-void XioXObj::m_copyFile(int nargs) {
- // Common::String source = g_lingo->pop().asString();
- // Common::String destination = g_lingo->pop().asString();
- g_lingo->printSTUBWithArglist("XioXObj::m_copyFile", nargs);
- g_lingo->dropStack(nargs);
- g_lingo->push(Datum(1));
-}
+XOBJSTUB(XioXObj::m_deleteFile, 1)
+XOBJSTUB(XioXObj::m_copyFile, 1)
} // End of namespace Director
Commit: 2730ccc7edda477afb5ede91a021b98e8f0eb59c
https://github.com/scummvm/scummvm/commit/2730ccc7edda477afb5ede91a021b98e8f0eb59c
Author: Scott Percival (code at moral.net.au)
Date: 2023-04-29T14:20:05+02:00
Commit Message:
DIRECTOR: LINGO: Make arithmetic issues invoke lingoError()
Changed paths:
engines/director/lingo/lingo-code.cpp
diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp
index 21243a174d9..9009476e3c9 100644
--- a/engines/director/lingo/lingo-code.cpp
+++ b/engines/director/lingo/lingo-code.cpp
@@ -727,7 +727,7 @@ Datum LC::addData(Datum &d1, Datum &d2) {
} else if (alignedType == INT) {
res = Datum(d1.asInt() + d2.asInt());
} else {
- warning("LC::addData(): not supported between types %s and %s", d1.type2str(), d2.type2str());
+ g_lingo->lingoError("LC::addData(): not supported between types %s and %s", d1.type2str(), d2.type2str());
}
return res;
}
@@ -751,7 +751,7 @@ Datum LC::subData(Datum &d1, Datum &d2) {
} else if (alignedType == INT) {
res = Datum(d1.asInt() - d2.asInt());
} else {
- warning("LC::subData(): not supported between types %s and %s", d1.type2str(), d2.type2str());
+ g_lingo->lingoError("LC::subData(): not supported between types %s and %s", d1.type2str(), d2.type2str());
}
return res;
}
@@ -775,7 +775,7 @@ Datum LC::mulData(Datum &d1, Datum &d2) {
} else if (alignedType == INT) {
res = Datum(d1.asInt() * d2.asInt());
} else {
- warning("LC::mulData(): not supported between types %s and %s", d1.type2str(), d2.type2str());
+ g_lingo->lingoError("LC::mulData(): not supported between types %s and %s", d1.type2str(), d2.type2str());
}
return res;
}
@@ -808,7 +808,7 @@ Datum LC::divData(Datum &d1, Datum &d2) {
} else if (alignedType == INT) {
res = Datum(d1.asInt() / d2.asInt());
} else {
- warning("LC::divData(): not supported between types %s and %s", d1.type2str(), d2.type2str());
+ g_lingo->lingoError("LC::divData(): not supported between types %s and %s", d1.type2str(), d2.type2str());
}
return res;
@@ -828,7 +828,7 @@ Datum LC::modData(Datum &d1, Datum &d2) {
int i1 = d1.asInt();
int i2 = d2.asInt();
if (i2 == 0) {
- warning("LC::modData(): division by zero");
+ g_lingo->lingoError("LC::modData(): division by zero");
i2 = 1;
}
@@ -860,7 +860,7 @@ Datum LC::negateData(Datum &d) {
} else if (d.type == FLOAT) {
res = Datum(-d.asFloat());
} else {
- warning("LC::negateData(): not supported for type %s", d.type2str());
+ g_lingo->lingoError("LC::negateData(): not supported for type %s", d.type2str());
}
return res;
Commit: 8cb29bd921b65e4495a07e8c46eff3e616d3fca3
https://github.com/scummvm/scummvm/commit/8cb29bd921b65e4495a07e8c46eff3e616d3fca3
Author: Scott Percival (code at moral.net.au)
Date: 2023-04-29T14:20:05+02:00
Commit Message:
DIRECTOR: LINGO: Add special override mechanics for List builtins
Fixes the loader in Star Trek TNG Episode Guide
Changed paths:
A engines/director/lingo/tests/listoverride.lingo
engines/director/lingo/lingo-builtins.cpp
engines/director/lingo/lingo-code.cpp
engines/director/lingo/lingo.h
engines/director/types.h
diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index 66ecea3ecdb..98cb66d656e 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -79,31 +79,31 @@ static BuiltinProto builtins[] = {
{ "string", LB::b_string, 1, 1, 200, FBLTIN }, // D2 f
{ "value", LB::b_value, 1, 1, 200, FBLTIN }, // D2 f
// Lists
- { "add", LB::b_add, 2, 2, 400, HBLTIN }, // D4 handler
- { "addAt", LB::b_addAt, 3, 3, 400, HBLTIN }, // D4 h
- { "addProp", LB::b_addProp, 3, 3, 400, HBLTIN }, // D4 h
- { "append", LB::b_append, 2, 2, 400, HBLTIN }, // D4 h
- { "count", LB::b_count, 1, 1, 400, FBLTIN }, // D4 f
- { "deleteAt", LB::b_deleteAt, 2, 2, 400, HBLTIN }, // D4 h
- { "deleteOne", LB::b_deleteOne, 2, 2, 400, HBLTIN }, // D4 h, undocumented?
- { "deleteProp", LB::b_deleteProp, 2, 2, 400, HBLTIN }, // D4 h
- { "findPos", LB::b_findPos, 2, 2, 400, FBLTIN }, // D4 f
- { "findPosNear", LB::b_findPosNear, 2, 2, 400, FBLTIN }, // D4 f
- { "getaProp", LB::b_getaProp, 2, 2, 400, FBLTIN }, // D4 f
- { "getAt", LB::b_getAt, 2, 2, 400, FBLTIN }, // D4 f
- { "getLast", LB::b_getLast, 1, 1, 400, FBLTIN }, // D4 f
- { "getOne", LB::b_getOne, 2, 2, 400, FBLTIN }, // D4 f
- { "getPos", LB::b_getPos, 2, 2, 400, FBLTIN }, // D4 f
- { "getProp", LB::b_getProp, 2, 2, 400, FBLTIN }, // D4 f
- { "getPropAt", LB::b_getPropAt, 2, 2, 400, FBLTIN }, // D4 f
- { "list", LB::b_list, -1, 0, 400, FBLTIN }, // D4 f
- { "listP", LB::b_listP, 1, 1, 400, FBLTIN }, // D4 f
- { "max", LB::b_max, -1,0, 400, FBLTIN }, // D4 f
- { "min", LB::b_min, -1,0, 400, FBLTIN }, // D4 f
- { "setaProp", LB::b_setaProp, 3, 3, 400, HBLTIN }, // D4 h
- { "setAt", LB::b_setAt, 3, 3, 400, HBLTIN }, // D4 h
- { "setProp", LB::b_setProp, 3, 3, 400, HBLTIN }, // D4 h
- { "sort", LB::b_sort, 1, 1, 400, HBLTIN }, // D4 h
+ { "add", LB::b_add, 2, 2, 400, HBLTIN_LIST }, // D4 handler
+ { "addAt", LB::b_addAt, 3, 3, 400, HBLTIN_LIST }, // D4 h
+ { "addProp", LB::b_addProp, 3, 3, 400, HBLTIN_LIST }, // D4 h
+ { "append", LB::b_append, 2, 2, 400, HBLTIN_LIST }, // D4 h
+ { "count", LB::b_count, 1, 1, 400, FBLTIN_LIST }, // D4 f
+ { "deleteAt", LB::b_deleteAt, 2, 2, 400, HBLTIN_LIST }, // D4 h
+ { "deleteOne", LB::b_deleteOne, 2, 2, 400, HBLTIN_LIST }, // D4 h, undocumented?
+ { "deleteProp", LB::b_deleteProp, 2, 2, 400, HBLTIN_LIST }, // D4 h
+ { "findPos", LB::b_findPos, 2, 2, 400, FBLTIN_LIST }, // D4 f
+ { "findPosNear", LB::b_findPosNear, 2, 2, 400, FBLTIN_LIST }, // D4 f
+ { "getaProp", LB::b_getaProp, 2, 2, 400, FBLTIN_LIST }, // D4 f
+ { "getAt", LB::b_getAt, 2, 2, 400, FBLTIN_LIST }, // D4 f
+ { "getLast", LB::b_getLast, 1, 1, 400, FBLTIN_LIST }, // D4 f
+ { "getOne", LB::b_getOne, 2, 2, 400, FBLTIN_LIST }, // D4 f
+ { "getPos", LB::b_getPos, 2, 2, 400, FBLTIN_LIST }, // D4 f
+ { "getProp", LB::b_getProp, 2, 2, 400, FBLTIN_LIST }, // D4 f
+ { "getPropAt", LB::b_getPropAt, 2, 2, 400, FBLTIN_LIST }, // D4 f
+ { "list", LB::b_list, -1, 0, 400, FBLTIN_LIST }, // D4 f
+ { "listP", LB::b_listP, 1, 1, 400, FBLTIN_LIST }, // D4 f
+ { "max", LB::b_max, -1,0, 400, FBLTIN_LIST }, // D4 f
+ { "min", LB::b_min, -1,0, 400, FBLTIN_LIST }, // D4 f
+ { "setaProp", LB::b_setaProp, 3, 3, 400, HBLTIN_LIST }, // D4 h
+ { "setAt", LB::b_setAt, 3, 3, 400, HBLTIN_LIST }, // D4 h
+ { "setProp", LB::b_setProp, 3, 3, 400, HBLTIN_LIST }, // D4 h
+ { "sort", LB::b_sort, 1, 1, 400, HBLTIN_LIST }, // D4 h
// Files
{ "closeDA", LB::b_closeDA, 0, 0, 200, CBLTIN }, // D2 c
{ "closeResFile", LB::b_closeResFile, 0, 1, 200, CBLTIN }, // D2 c
@@ -260,11 +260,15 @@ void Lingo::initBuiltIns(BuiltinProto protos[]) {
_builtinCmds[blt->name] = sym;
break;
case FBLTIN:
+ case FBLTIN_LIST:
_builtinFuncs[blt->name] = sym;
+ _builtinListHandlers[blt->name] = sym;
break;
case HBLTIN:
+ case HBLTIN_LIST:
_builtinCmds[blt->name] = sym;
_builtinFuncs[blt->name] = sym;
+ _builtinListHandlers[blt->name] = sym;
break;
case KBLTIN:
_builtinConsts[blt->name] = sym;
diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp
index 9009476e3c9..5daddac0d89 100644
--- a/engines/director/lingo/lingo-code.cpp
+++ b/engines/director/lingo/lingo-code.cpp
@@ -1533,6 +1533,16 @@ void LC::call(const Common::String &name, int nargs, bool allowRetVal) {
// Handler
funcSym = g_lingo->getHandler(name);
+ if (g_lingo->_builtinListHandlers.contains(name) && nargs >= 1) {
+ // Lingo builtin functions in the "List" category have very strange override mechanics.
+ // If the first argument is an ARRAY or PARRAY, it will use the builtin.
+ // Otherwise, it will fall back to whatever handler is defined globally.
+ Datum firstArg = g_lingo->peek(nargs - 1);
+ if (firstArg.type == ARRAY || firstArg.type == PARRAY) {
+ funcSym = g_lingo->_builtinListHandlers[name];
+ }
+ }
+
if (funcSym.type == VOIDSYM) { // The built-ins could be overridden
// Builtin
if (allowRetVal) {
diff --git a/engines/director/lingo/lingo.h b/engines/director/lingo/lingo.h
index 2edebbc7578..46a198ac349 100644
--- a/engines/director/lingo/lingo.h
+++ b/engines/director/lingo/lingo.h
@@ -482,6 +482,7 @@ public:
SymbolHash _builtinCmds;
SymbolHash _builtinFuncs;
SymbolHash _builtinConsts;
+ SymbolHash _builtinListHandlers;
SymbolHash _methods;
XLibFuncHash _xlibOpeners;
XLibFuncHash _xlibClosers;
diff --git a/engines/director/lingo/tests/listoverride.lingo b/engines/director/lingo/tests/listoverride.lingo
new file mode 100644
index 00000000000..6b3c22aa114
--- /dev/null
+++ b/engines/director/lingo/tests/listoverride.lingo
@@ -0,0 +1,28 @@
+
+on count l
+ return "what is this"
+end
+
+on add l, x
+ return "this is the worst"
+end
+
+set result = count([1, 2, 3])
+scummvmAssertEqual(result, 3)
+
+set result = count([1: 2, 3: 4, 5: 6])
+scummvmAssertEqual(result, 3)
+
+set result = count("not an array")
+scummvmAssertEqual(result, "what is this")
+
+set result = add("still not an array")
+scummvmAssertEqual(result, "this is the worst")
+
+set result = add("even less of an array", 8)
+scummvmAssertEqual(result, "this is the worst")
+
+set target = [1, 2, 3]
+add(target, 4)
+scummvmAssertEqual(count(target), 4)
+
diff --git a/engines/director/types.h b/engines/director/types.h
index 5d9ddefc5aa..bd86e562695 100644
--- a/engines/director/types.h
+++ b/engines/director/types.h
@@ -317,6 +317,8 @@ enum SymbolType {
FBLTIN, // builtin function
HBLTIN, // builtin handler (can be called as either command or func)
KBLTIN, // builtin constant
+ FBLTIN_LIST, // builtin function w/list override check
+ HBLTIN_LIST, // builtin handler w/list override check
HANDLER // user-defined handler
};
More information about the Scummvm-git-logs
mailing list