[Scummvm-git-logs] scummvm master -> d0e5c19bd71bf0a1206da31f605495a5f611b232
sev-
noreply at scummvm.org
Sat Aug 9 16:30:39 UTC 2025
This automated email contains information about 31 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
c09885fb2c DIRECTOR: Write 'CLUt' and 'STXT' Resources
eb8fb36eb2 DIRECTOR: Saving 'STXT' and 'BITD' resources
0a7dd134f2 DIRECTOR: Write filmloop data 'SCVW' Resource
3691cd382b DIRECTOR: Recalculate the size and offset of resources while writing
3ab3204b09 DIRECTOR: Remove `saveMovie` lingo command STUB
2a9a582096 DIRECTOR: Bunch of fixes for the `writeToFile()` function
c495df4e33 DIRECTOR: Fixes to lingo command `saveMovie`
a932fcad87 DIRECTOR: Fix Saving Bitmap and Filmloop
faafe78d54 DIRECTOR: Fix pascal string reading/writing of CastInfo
40ace668ad DIRECTOR: Fix `readHex` in STXT writing
d611befc5a DIRECTOR: Fix bitmap writing for 1/2/4bpp
a791282372 DIRECTOR: Fix writing duplicated 'CASt' resources
5174de231c DIRECTOR: Save D5 Filmloop Sprite Data
ccc93bd0f0 DIRECTOR: Write D4/D5 score main channels
650c28df21 DIRECTOR: Write Score ('VWSC' resource) while saving movies
9d07259b02 DIRECTOR: Erase overwritten cast members while saving
9c77c06e5f DIRECTOR: Fix writing STXT formatting
3a95842ee0 DIRECTOR: Fix STXT formatting writing
77c712200e DIRECTOR: Move the saving functionality from archive.cpp to
5c4b4e787b DIRECTOR: Minor formatting fixes
a5887eb10d DIRECTOR: Save current movie rather than initial movie
4228fd0026 DIRECTOR: Convert all `MemoryWriteStream` to `SeekableWriteStream` while
1d7e0589f1 DIRECTOR: Add saved files to the search set while loading
463bdaf4b6 DIRECTOR: Write _platformID instead of _platform
e0fa481694 DIRECTOR: Save the correct movie in `saveMovie`
25788134e0 DIRECTOR: Remove unnecessary header files and indentation fixes
c866bd6b88 DIRECTOR: Implement Stubbed `Cast::computeChecksum()`
84fec1adee DIRECTOR: Delete rebuilt resources after saving file
c772010014 DIRECTOR: Write movies with multiple casts
5c2cf9dd26 DIRECTOR: Write zero size for external bitmaps
d0e5c19bd7 DIRECTOR: Simplify bitmap saving using `Graphics::Surface`
Commit: c09885fb2cbadd3050790a3055b639b8d244abad
https://github.com/scummvm/scummvm/commit/c09885fb2cbadd3050790a3055b639b8d244abad
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Write 'CLUt' and 'STXT' Resources
Changed paths:
engines/director/castmember/palette.cpp
engines/director/castmember/palette.h
engines/director/castmember/text.cpp
engines/director/castmember/text.h
diff --git a/engines/director/castmember/palette.cpp b/engines/director/castmember/palette.cpp
index 8b9a9a69ee3..11dbb4c5743 100644
--- a/engines/director/castmember/palette.cpp
+++ b/engines/director/castmember/palette.cpp
@@ -155,4 +155,41 @@ void PaletteCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
// Since there is no data to write
}
+uint32 PaletteCastMember::getPaletteDataSize() {
+ // This is the actual Palette data, in the 'CLUT' resource
+ // PaletteCastMembers data stored in the 'CLUT' resource does not change in size (may change in content) (need to verify)
+ // Hence their original size can be written
+ // This is the length of the 'CLUT' resource without the header and size
+ return _palette->length * 6;
+}
+void PaletteCastMember::writePaletteData(Common::MemoryWriteStream *writeStream, uint32 offset) {
+ uint32 castSize = getPaletteDataSize() + 8;
+
+ /**/
+ byte *dumpData = nullptr;
+ dumpData = (byte *)calloc(castSize, sizeof(byte));
+ writeStream = new Common::SeekableMemoryWriteStream(dumpData, castSize);
+ /**/
+
+ // writeStream->seek(offset);
+ writeStream->writeUint32LE(MKTAG('C', 'L', 'U', 'T'));
+ writeStream->writeUint32LE(getPaletteDataSize());
+
+ const byte *pal = _palette->palette;
+
+ for (int i = 0; i < _palette->length; i++) {
+ writeStream->writeByte(pal[3 * i]);
+ writeStream->writeByte(pal[3 * i]);
+
+ writeStream->writeByte(pal[3 * i + 1]);
+ writeStream->writeByte(pal[3 * i + 1]);
+
+ writeStream->writeByte(pal[3 * i + 2]);
+ writeStream->writeByte(pal[3 * i + 2]);
+ }
+
+ dumpFile("PaletteData", _castId, MKTAG('C', 'L', 'U', 'T'), dumpData, castSize);
+ delete writeStream;
+}
+
} // End of namespace Director
diff --git a/engines/director/castmember/palette.h b/engines/director/castmember/palette.h
index 3e25f4e26ce..a9d13b5f97a 100644
--- a/engines/director/castmember/palette.h
+++ b/engines/director/castmember/palette.h
@@ -47,6 +47,9 @@ public:
uint32 getCastDataSize() override; // This is the size of the data in the 'CASt' resource
void writeCastData(Common::MemoryWriteStream *writeStream) override;
+ void writePaletteData(Common::MemoryWriteStream *writeStream, uint32 offset);
+ uint32 getPaletteDataSize();
+
PaletteV4 *_palette;
};
diff --git a/engines/director/castmember/text.cpp b/engines/director/castmember/text.cpp
index 460c579c293..0ae8799c539 100644
--- a/engines/director/castmember/text.cpp
+++ b/engines/director/castmember/text.cpp
@@ -977,4 +977,63 @@ uint32 TextCastMember::getCastDataSize() {
return (_type == kCastButton) ? 30 : 28;
}
+uint32 TextCastMember::writeSTXTResource(Common::MemoryWriteStream *writeStream, uint32 offset) {
+ uint32 castSize = getSTXTResourceSize() + 10;
+ byte *dumpData = nullptr;
+ dumpData = (byte *)calloc(castSize, sizeof(byte));
+
+ writeStream = new Common::SeekableMemoryWriteStream(dumpData, castSize);
+
+ writeStream->seek(offset);
+
+ writeStream->writeUint32BE(MKTAG('S', 'T', 'X', 'T'));
+ writeStream->writeUint32BE(getSTXTResourceSize() + 8); // Size of the STXT resource without the header and size
+
+ writeStream->writeUint32BE(12); // This is the offset, if it's not 12, we throw an error and exit ScummVM
+ writeStream->writeUint32BE(_ptext.size());
+ // Encode only in one format, original may be encoded in multiple formats
+ // Size of one Font Style is 20 + The number of encodings takes 2 bytes
+ writeStream->writeUint32BE(20 + 2);
+
+ Common::CodePage encoding = detectFontEncoding(_cast->_platform, _fontId);
+ writeStream->writeString(_ptext.encode(encoding)); // Currently using MacRoman encoding only, may change later to include others
+
+ writeStream->writeUint16BE(1); // Only one formatting: MacRoman
+
+ writeStream->writeUint32BE(0); // FontStyle::formatStartOffset
+ writeStream->writeUint16BE(_height); // FontStyle::height // Apparently height doesn't matter
+ writeStream->writeUint16BE(_ascent); // FontStyle::ascent // And neither does ascent
+
+ writeStream->writeUint16BE(_fontId); // FontStyle::fontId
+ writeStream->writeByte(_textSlant); // FontStyle::textSlant
+ writeStream->writeByte(0); // padding
+ writeStream->writeUint16BE(_fontSize); // FontStyle::fontSize
+
+ writeStream->writeUint16BE(_fgpalinfo1); // FontStyle:r
+ writeStream->writeUint16BE(_fgpalinfo2); // FontStyle:g
+ writeStream->writeUint16BE(_fgpalinfo3); // FontStyle:b
+
+ Common::DumpFile out;
+ char buf[256];
+ Common::sprintf_s(buf, "./dumps/%d-%s-%s", _castId, tag2str(MKTAG('S', 'T', 'X', 'T')), "WrittenSTXTResource");
+
+ // Write the movie out, stored in dumpData
+ if (out.open(buf, true)) {
+ out.write(dumpData, castSize);
+ out.flush();
+ out.close();
+ debugC(3, kDebugLoading, "RIFXArchive::writeStream:Saved the movie as file %s", buf);
+ } else {
+ warning("RIFXArchive::writeStream: Error saving the file %s", buf);
+ }
+ free(dumpData);
+ delete writeStream;
+ return getSTXTResourceSize() + 8;
+}
+
+uint32 TextCastMember::getSTXTResourceSize() {
+ // Header (offset, string length, data length) + text string + data (FontStyle)
+ return 12 + _ptext.size() + 22;
+}
+
} // End of namespace Director
diff --git a/engines/director/castmember/text.h b/engines/director/castmember/text.h
index d81f3f9882c..d8973280d94 100644
--- a/engines/director/castmember/text.h
+++ b/engines/director/castmember/text.h
@@ -95,6 +95,9 @@ public:
void writeCastData(Common::MemoryWriteStream *writeStream) override;
uint32 getCastDataSize() override; // This is the size of the data in the 'CASt' resource
+ uint32 getSTXTResourceSize();
+ uint32 writeSTXTResource(Common::MemoryWriteStream *writeStream, uint32 offset);
+
uint8 _borderSize;
uint8 _gutterSize;
uint8 _boxShadow;
Commit: eb8fb36eb2f2ea08b426d375747d4e6bc95f23b7
https://github.com/scummvm/scummvm/commit/eb8fb36eb2f2ea08b426d375747d4e6bc95f23b7
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Saving 'STXT' and 'BITD' resources
Saving the Text and Bitmap data
Changed paths:
engines/director/archive.cpp
engines/director/cast.cpp
engines/director/castmember/bitmap.cpp
engines/director/castmember/bitmap.h
engines/director/castmember/palette.cpp
engines/director/castmember/text.cpp
engines/director/castmember/text.h
engines/director/images.cpp
engines/director/stxt.cpp
diff --git a/engines/director/archive.cpp b/engines/director/archive.cpp
index a65f9e89770..b34caa7ce75 100644
--- a/engines/director/archive.cpp
+++ b/engines/director/archive.cpp
@@ -1231,7 +1231,8 @@ bool RIFXArchive::writeToFile(Common::Path path) {
it->tag != SWAP_BYTES_32(_metaTag) &&
it->tag != MKTAG('i', 'm', 'a', 'p') &&
it->tag != MKTAG('m', 'm', 'a', 'p') &&
- it->tag != MKTAG('K', 'E', 'Y', '*')
+ it->tag != MKTAG('K', 'E', 'Y', '*') &&
+ it->tag != MKTAG('f', 'r', 'e', 'e')
) {
writeStream->seek(it->offset);
writeStream->writeUint32LE(it->tag);
@@ -1381,7 +1382,6 @@ void dumpFile(Common::String fileName, uint32 id, uint32 tag, byte *dumpData, ui
} else {
warning("RIFXArchive::writeStream: Error saving the file %s", fname.c_str());
}
- free(dumpData);
}
} // End of namespace Director
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index a68ecbb35e3..4aa73afef59 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -1169,6 +1169,7 @@ PaletteV4 Cast::loadPalette(Common::SeekableReadStreamEndian &stream, int id) {
break;
}
+ // Ignoring the lower 8 bits of a 16-bit data
palette[3 * colorIndex] = stream.readByte();
stream.readByte();
diff --git a/engines/director/castmember/bitmap.cpp b/engines/director/castmember/bitmap.cpp
index bb869190d10..29248cf8d16 100644
--- a/engines/director/castmember/bitmap.cpp
+++ b/engines/director/castmember/bitmap.cpp
@@ -687,7 +687,7 @@ void BitmapCastMember::load() {
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;
@@ -715,6 +715,7 @@ void BitmapCastMember::load() {
debugC(5, kDebugImages, "BitmapCastMember::load(): Bitmap: id: %d, w: %d, h: %d, flags1: %x, flags2: %x bytes: %x, bpp: %d clut: %s", imgId, w, h, _flags1, _flags2, _bytes, _bitsPerPixel, _clut.asString().c_str());
_loaded = true;
+ writeBITDResource(nullptr, 0);
}
void BitmapCastMember::unload() {
@@ -1036,4 +1037,83 @@ void BitmapCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
// Ignoring the tail during loading as well as saving
}
+uint32 BitmapCastMember::writeBITDResource(Common::MemoryWriteStream *writeStream, uint32 offset) {
+ // writeStream->seek(offset);
+
+ // writeStream->writeUint32LE(MKTAG('B', 'I', 'T', 'D'));
+ // writeStream->writeUint32LE(_size);
+
+ if (_external) {
+ warning("BitmapCastMember::writeBITDResource: the bitmap is external, ignoring for now");
+ return 8; // 8 for the tag and size
+ }
+
+ // No compression for now
+ // pixels.size() == bytes needed
+ Common::Array<byte> pixels;
+ pixels.resize(_pitch * _picture->_surface.h); // for <= 8bpp
+ offset = 0;
+
+ if (_bitsPerPixel == 8 && _picture->_surface.w < (int)(pixels.size() / _picture->_surface.h)) {
+ offset = _picture->_surface.w % 2;
+ }
+
+ for (int y = 0; y < _picture->_surface.h; y++) {
+ for (int x = 0; x < _picture->_surface.w;) {
+ uint32 color = 0;
+
+ switch (_bitsPerPixel) {
+ case 1:
+ for (int c = 0; c < 8 && x < _picture->_surface.w; c++, x++) {
+ color += *((byte *)_picture->_surface.getBasePtr(x, y)) & (1 << (7 - c));
+ }
+ pixels[(y * _pitch) + (x >> 3)] = color;
+ break;
+
+ case 2:
+ for (int c = 0; c < 4 && x < _picture->_surface.w; c++, x++) {
+ color += (*((byte *)_picture->_surface.getBasePtr(x, y)) & 0x3) << (2 * (3 - c));
+ }
+ pixels[(y * _pitch) + (x >> 2)] = color;
+ break;
+
+ case 4:
+ for (int c = 0; c < 2 && x < _picture->_surface.w; c++, x++) {
+ color = (*((byte *)_picture->_surface.getBasePtr(x, y)) & 0xF) << (4 * (1 - c));
+ }
+ pixels[(y * _pitch) + (x >> 1)] = color;
+ break;
+
+ case 8:
+ pixels[(y * _picture->_surface.w) + x + (y * offset)] = *((byte *)_picture->_surface.getBasePtr(x, y));
+ x++;
+ break;
+
+ case 16:
+ color = *((uint16 *)_picture->_surface.getBasePtr(x, y));
+ pixels[(y * _picture->_surface.w * 2) + x * 2] = color >> 8;
+ pixels[(y * _picture->_surface.w * 2) + x * 2 + 1] = color & 0xFF;
+ x++;
+ break;
+
+ case 32:
+ color = *((uint32 *)(_picture->_surface.getBasePtr(x, y)));
+ // only storing RGB, no alpha
+ pixels[(y * _picture->_surface.w * 4) + x * 4 + 1] = (color >> 16) & 0xFF;
+ pixels[(y * _picture->_surface.w * 4) + x * 4 + 2] = (color >> 8) & 0xFF;
+ pixels[(y * _picture->_surface.w * 4) + x * 4 + 3] = color & 0xFF;
+ x++;
+ break;
+
+ default:
+ x++;
+ break;
+ }
+ }
+ }
+
+ dumpFile("BitmapData", _castId, MKTAG('B', 'I', 'T', 'D'), pixels.data(), pixels.size());
+ return 0;
+}
+
} // End of namespace Director
diff --git a/engines/director/castmember/bitmap.h b/engines/director/castmember/bitmap.h
index 7b75c87926d..257b6ed4324 100644
--- a/engines/director/castmember/bitmap.h
+++ b/engines/director/castmember/bitmap.h
@@ -65,6 +65,7 @@ public:
CollisionTest isWithin(const Common::Rect &bbox, const Common::Point &pos, InkType ink) override;
void writeCastData(Common::MemoryWriteStream *writeStream) override;
+ uint32 writeBITDResource(Common::MemoryWriteStream *writeStream, uint32 offset);
uint32 getCastDataSize() override; // This is the size of the data in the 'CASt' resource
diff --git a/engines/director/castmember/palette.cpp b/engines/director/castmember/palette.cpp
index 11dbb4c5743..9522e647e2f 100644
--- a/engines/director/castmember/palette.cpp
+++ b/engines/director/castmember/palette.cpp
@@ -131,6 +131,10 @@ void PaletteCastMember::load() {
}
_loaded = true;
+
+ if (debugChannelSet(5, kDebugSaving)) {
+ writePaletteData(nullptr, 0);
+ }
}
void PaletteCastMember::unload() {
@@ -162,6 +166,7 @@ uint32 PaletteCastMember::getPaletteDataSize() {
// This is the length of the 'CLUT' resource without the header and size
return _palette->length * 6;
}
+
void PaletteCastMember::writePaletteData(Common::MemoryWriteStream *writeStream, uint32 offset) {
uint32 castSize = getPaletteDataSize() + 8;
@@ -178,8 +183,8 @@ void PaletteCastMember::writePaletteData(Common::MemoryWriteStream *writeStream,
const byte *pal = _palette->palette;
for (int i = 0; i < _palette->length; i++) {
- writeStream->writeByte(pal[3 * i]);
- writeStream->writeByte(pal[3 * i]);
+ writeStream->writeByte(pal[3 * i]); // The palette data is converted to 8-bit at the time of loading
+ writeStream->writeByte(pal[3 * i]); // Duplicating the data to convert to 16-bit
writeStream->writeByte(pal[3 * i + 1]);
writeStream->writeByte(pal[3 * i + 1]);
diff --git a/engines/director/castmember/text.cpp b/engines/director/castmember/text.cpp
index 0ae8799c539..07fe27c4060 100644
--- a/engines/director/castmember/text.cpp
+++ b/engines/director/castmember/text.cpp
@@ -26,6 +26,7 @@
#include "graphics/macgui/macbutton.h"
#include "graphics/macgui/macwindow.h"
+#include "graphics/macgui/macwindowmanager.h"
#include "director/director.h"
#include "director/cast.h"
@@ -635,6 +636,7 @@ void TextCastMember::load() {
}
_loaded = true;
+ writeSTXTResource(nullptr, 0);
}
void TextCastMember::unload() {
@@ -978,62 +980,82 @@ uint32 TextCastMember::getCastDataSize() {
}
uint32 TextCastMember::writeSTXTResource(Common::MemoryWriteStream *writeStream, uint32 offset) {
- uint32 castSize = getSTXTResourceSize() + 10;
+ uint32 castSize = getSTXTResourceSize() + 8;
+
byte *dumpData = nullptr;
dumpData = (byte *)calloc(castSize, sizeof(byte));
-
writeStream = new Common::SeekableMemoryWriteStream(dumpData, castSize);
writeStream->seek(offset);
- writeStream->writeUint32BE(MKTAG('S', 'T', 'X', 'T'));
- writeStream->writeUint32BE(getSTXTResourceSize() + 8); // Size of the STXT resource without the header and size
+ writeStream->writeUint32LE(MKTAG('S', 'T', 'X', 'T'));
+ writeStream->writeUint32LE(getSTXTResourceSize()); // Size of the STXT resource without the header and size
- writeStream->writeUint32BE(12); // This is the offset, if it's not 12, we throw an error and exit ScummVM
- writeStream->writeUint32BE(_ptext.size());
+ writeStream->writeUint32BE(12); // This is the offset, if it's not 12, we throw an error, other offsets are not handled
+
+ int8 formatting = getFormattingCount();
+ writeStream->writeUint32BE(_rtext.size()); // Length of the string
// Encode only in one format, original may be encoded in multiple formats
// Size of one Font Style is 20 + The number of encodings takes 2 bytes
- writeStream->writeUint32BE(20 + 2);
-
- Common::CodePage encoding = detectFontEncoding(_cast->_platform, _fontId);
- writeStream->writeString(_ptext.encode(encoding)); // Currently using MacRoman encoding only, may change later to include others
+ writeStream->writeUint32BE(20 * formatting + 2); // Data Length
- writeStream->writeUint16BE(1); // Only one formatting: MacRoman
+ FontStyle style;
+ writeStream->writeString(_rtext);
+ debug("what is the size of _rtext = %d", _rtext.size());
- writeStream->writeUint32BE(0); // FontStyle::formatStartOffset
- writeStream->writeUint16BE(_height); // FontStyle::height // Apparently height doesn't matter
- writeStream->writeUint16BE(_ascent); // FontStyle::ascent // And neither does ascent
+ writeStream->writeUint16BE(formatting);
- writeStream->writeUint16BE(_fontId); // FontStyle::fontId
- writeStream->writeByte(_textSlant); // FontStyle::textSlant
- writeStream->writeByte(0); // padding
- writeStream->writeUint16BE(_fontSize); // FontStyle::fontSize
+ uint32 it = 0;
+ while (it < _ftext.size() - 1) {
+ uint32 rIndex = 0;
+ if (_ftext[it] == '\001' && _ftext[it + 1] == '\016') {
+ debug("Found Styling header at position: %d", it);
+ // Styling header found
+ it += 2;
- writeStream->writeUint16BE(_fgpalinfo1); // FontStyle:r
- writeStream->writeUint16BE(_fgpalinfo2); // FontStyle:g
- writeStream->writeUint16BE(_fgpalinfo3); // FontStyle:b
+ if (it + 22 > _ftext.size()) {
+ warning("TextCastMember::writeSTXTResource: incorrect format sequence");
+ break;
+ }
- Common::DumpFile out;
- char buf[256];
- Common::sprintf_s(buf, "./dumps/%d-%s-%s", _castId, tag2str(MKTAG('S', 'T', 'X', 'T')), "WrittenSTXTResource");
+ // Ignoring height and ascent for now from FontStyle
+ uint16 temp;
+ style.formatStartOffset = rIndex;
+ Graphics::readHex(&style.fontId, _ftext.substr(it, 4).c_str(), 4);
+ Graphics::readHex(&temp, _ftext.substr(it, 2).c_str(), 2);
+ Graphics::readHex(&style.fontSize, _ftext.substr(it + 6, 4).c_str(), 4);
+ Graphics::readHex(&style.r, _ftext.substr(it + 10, 4).c_str(), 4);
+ Graphics::readHex(&style.g, _ftext.substr(it + 14, 4).c_str(), 4);
+ Graphics::readHex(&style.b, _ftext.substr(it + 18, 4).c_str(), 4);
+ style.textSlant = temp;
+
+ style.write(writeStream);
+ it += 22;
+ continue;
+ }
- // Write the movie out, stored in dumpData
- if (out.open(buf, true)) {
- out.write(dumpData, castSize);
- out.flush();
- out.close();
- debugC(3, kDebugLoading, "RIFXArchive::writeStream:Saved the movie as file %s", buf);
- } else {
- warning("RIFXArchive::writeStream: Error saving the file %s", buf);
+ rIndex += 1;
+ it++;
}
- free(dumpData);
+
+ dumpFile("TextData", _castId, MKTAG('S', 'T', 'X', 'T'), dumpData, getSTXTResourceSize() + 8);
delete writeStream;
return getSTXTResourceSize() + 8;
}
uint32 TextCastMember::getSTXTResourceSize() {
- // Header (offset, string length, data length) + text string + data (FontStyle)
- return 12 + _ptext.size() + 22;
+ // Header (offset, string length, data length) + text string + data (FontStyle)
+ return 12 + _rtext.size() + getFormattingCount() * 20 + 2;
+}
+
+uint8 TextCastMember::getFormattingCount() {
+ uint8 count = 0;
+ for (uint32 i = 0; i < _ftext.size() - 1; i++) {
+ if (_ftext[i] == '\001' && _ftext[i + 1] == '\016') {
+ count++;
+ }
+ }
+ return count;
}
} // End of namespace Director
diff --git a/engines/director/castmember/text.h b/engines/director/castmember/text.h
index d8973280d94..f1c2fcb193b 100644
--- a/engines/director/castmember/text.h
+++ b/engines/director/castmember/text.h
@@ -97,6 +97,7 @@ public:
uint32 getSTXTResourceSize();
uint32 writeSTXTResource(Common::MemoryWriteStream *writeStream, uint32 offset);
+ uint8 getFormattingCount();
uint8 _borderSize;
uint8 _gutterSize;
diff --git a/engines/director/images.cpp b/engines/director/images.cpp
index 804d02ed2b2..7bb95e7c12e 100644
--- a/engines/director/images.cpp
+++ b/engines/director/images.cpp
@@ -20,6 +20,9 @@
*/
#include "common/substream.h"
+#include "common/macresman.h"
+#include "common/memstream.h"
+#include "common/file.h"
#include "graphics/macgui/macwindowmanager.h"
#include "graphics/pixelformat.h"
#include "image/codecs/bmp_raw.h"
diff --git a/engines/director/stxt.cpp b/engines/director/stxt.cpp
index ce9339825d0..45646c467dd 100644
--- a/engines/director/stxt.cpp
+++ b/engines/director/stxt.cpp
@@ -45,7 +45,8 @@ Stxt::Stxt(Cast *cast, Common::SeekableReadStreamEndian &textStream) : _cast(cas
uint32 offset = textStream.readUint32();
if (offset != 12) {
- error("Stxt init: unhandled offset");
+ textStream.hexdump(textStream.size());
+ error("Stxt init: unhandled offset, %d", offset);
return;
}
uint32 strLen = textStream.readUint32();
Commit: 0a7dd134f272b2ca8090fd69de3c7510e63117fd
https://github.com/scummvm/scummvm/commit/0a7dd134f272b2ca8090fd69de3c7510e63117fd
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Write filmloop data 'SCVW' Resource
Changed paths:
engines/director/archive.cpp
engines/director/castmember/bitmap.cpp
engines/director/castmember/filmloop.cpp
engines/director/castmember/filmloop.h
engines/director/castmember/palette.cpp
engines/director/castmember/shape.cpp
engines/director/frame.cpp
engines/director/frame.h
diff --git a/engines/director/archive.cpp b/engines/director/archive.cpp
index b34caa7ce75..37a37761d5c 100644
--- a/engines/director/archive.cpp
+++ b/engines/director/archive.cpp
@@ -1232,7 +1232,7 @@ bool RIFXArchive::writeToFile(Common::Path path) {
it->tag != MKTAG('i', 'm', 'a', 'p') &&
it->tag != MKTAG('m', 'm', 'a', 'p') &&
it->tag != MKTAG('K', 'E', 'Y', '*') &&
- it->tag != MKTAG('f', 'r', 'e', 'e')
+ it->tag != MKTAG('f', 'r', 'e', 'e') // Sometimes 'free' resources have garbage values, they might overwrite other data
) {
writeStream->seek(it->offset);
writeStream->writeUint32LE(it->tag);
diff --git a/engines/director/castmember/bitmap.cpp b/engines/director/castmember/bitmap.cpp
index 29248cf8d16..78352ca1521 100644
--- a/engines/director/castmember/bitmap.cpp
+++ b/engines/director/castmember/bitmap.cpp
@@ -1058,6 +1058,7 @@ uint32 BitmapCastMember::writeBITDResource(Common::MemoryWriteStream *writeStrea
offset = _picture->_surface.w % 2;
}
+ debug("What is _bitsPerPixel = %d", _bitsPerPixel);
for (int y = 0; y < _picture->_surface.h; y++) {
for (int x = 0; x < _picture->_surface.w;) {
uint32 color = 0;
diff --git a/engines/director/castmember/filmloop.cpp b/engines/director/castmember/filmloop.cpp
index 723e29840d6..c2d364f7840 100644
--- a/engines/director/castmember/filmloop.cpp
+++ b/engines/director/castmember/filmloop.cpp
@@ -315,7 +315,7 @@ void FilmLoopCastMember::loadFilmLoopDataD4(Common::SeekableReadStreamEndian &st
int channelOffset = order % channelSize;
int offset = order;
- debugC(8, kDebugLoading, "loadFilmLoopDataD4: Message: msgWidth %d, channel %d, channelOffset %d", msgWidth, channel, channelOffset);
+ debugC(2, kDebugLoading, "loadFilmLoopDataD4: Message: msgWidth %d, order: %d, channel %d, channelOffset %d", msgWidth, order, channel, channelOffset);
if (debugChannelSet(8, kDebugLoading)) {
stream.hexdump(msgWidth);
}
@@ -748,4 +748,88 @@ void FilmLoopCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
writeStream->writeUint16LE(0); // May need to save proper value in the future, currently ignored
}
+void FilmLoopCastMember::writeSCVWResource(Common::MemoryWriteStream *writeStream, uint32 offset) {
+ uint32 channelSize = 0;
+ switch (_cast->_version) {
+ case kFileVer400:
+ channelSize = kSprChannelSizeD4;
+ break;
+
+ case kFileVer500:
+ channelSize = kSprChannelSizeD5;
+ break;
+
+ default:
+ warning("FilmLoopCastMember::writeSCVWResource: Writing Director Version 6 not supported yet");
+ return;
+ }
+
+ // Go to the desired offset put in the memory map
+ writeStream->seek(offset);
+
+ writeStream->writeUint32LE(MKTAG('S', 'C', 'V', 'W'));
+ writeStream->writeUint32LE(getSCVWResourceSize(channelSize)); // Size of the resource
+
+ uint32 sizePos = writeStream->pos();
+ writeStream->writeUint32BE(0); // Putting zeroes instead of size currently
+
+ uint32 frameOffset = 16; // Should be greater than 16
+ writeStream->writeUint32BE(frameOffset); // framesOffset
+ writeStream->seek(6, SEEK_CUR); // Ignored data
+ writeStream->writeUint16BE(kSprChannelSizeD4);
+ writeStream->seek(frameOffset - 16, SEEK_CUR); // Ignored data
+
+ // The structure of the filmloop 'SCVW' data is as follows
+ // The 'SCVW' tag -> the size of the resource ->
+ // frameoffset (This offset is where the frame date actually starts) ->
+ // Some headers which we ignore except the Sprite Channel Size (which we also ignore during loading) ->
+
+ // until there are no more frames
+ // size of the frame ->
+
+ // until there are no more channels in the frame
+ // width of message (One chunk of data) (This is the size of data for the sprite that needs to be read) ->
+ // order of message (this order tells us the channel we're reading) ->
+ // 1-20 bytes of Sprite data
+
+ for (FilmLoopFrame frame : _frames) {
+ writeStream->writeUint16BE(0); // Frame Size
+
+ for (auto it : frame.sprites) {
+ int channel = it._key;
+ // For now writing the order considering that each sprite will have 20 bytes of data
+ // In the future, for optimization, we can actually calculate the data of each sprite
+ // And write the order accordingly
+ // But for this we'll need a way to find how many data values (out of 20) of a sprite are valid, i.e. determine message width
+ // this means while loading, the channelOffset will always be 0, order will always be multiple of 20
+ // And message width will always be 20
+ // Channel indexes start with 0
+ writeStream->writeUint16BE(kSprChannelSizeD4); // message width
+ writeStream->writeUint16BE(channel * kSprChannelSizeD4);
+
+ Sprite sprite = it._value;
+
+ writeSpriteDataD4(writeStream, sprite);
+ }
+
+ }
+}
+
+uint32 FilmLoopCastMember::getSCVWResourceSize(uint32 channelSize) {
+ // Size of the filmloop data: 4 bytes
+ // frameoffset: 4 bytes
+ // Header (Ignored data): 12 bytes
+ //
+ uint32 framesSize = 0;
+ for (FilmLoopFrame frame : _frames) {
+ for (auto it : frame.sprites) {
+ // message width: 2 bytes
+ // order: 2 bytes
+ // Sprite data: 20 bytes
+ framesSize += 2 + 2 + channelSize;
+ }
+ }
+ return 4 + 4 + 12 + framesSize;
+}
+
} // End of namespace Director
diff --git a/engines/director/castmember/filmloop.h b/engines/director/castmember/filmloop.h
index dd682ebb14d..577ed91047b 100644
--- a/engines/director/castmember/filmloop.h
+++ b/engines/director/castmember/filmloop.h
@@ -61,6 +61,9 @@ public:
uint32 getCastDataSize() override;
void writeCastData(Common::MemoryWriteStream *writeStream) override;
+ void writeSCVWResource(Common::MemoryWriteStream *writeStream, uint32 offset);
+ uint32 getSCVWResourceSize(uint32 channelSize);
+
bool _enableSound;
bool _looping;
bool _crop;
diff --git a/engines/director/castmember/palette.cpp b/engines/director/castmember/palette.cpp
index 9522e647e2f..54d22d757d8 100644
--- a/engines/director/castmember/palette.cpp
+++ b/engines/director/castmember/palette.cpp
@@ -183,14 +183,11 @@ void PaletteCastMember::writePaletteData(Common::MemoryWriteStream *writeStream,
const byte *pal = _palette->palette;
for (int i = 0; i < _palette->length; i++) {
- writeStream->writeByte(pal[3 * i]); // The palette data is converted to 8-bit at the time of loading
- writeStream->writeByte(pal[3 * i]); // Duplicating the data to convert to 16-bit
-
- writeStream->writeByte(pal[3 * i + 1]);
- writeStream->writeByte(pal[3 * i + 1]);
-
- writeStream->writeByte(pal[3 * i + 2]);
- writeStream->writeByte(pal[3 * i + 2]);
+ // Duplicating the data to convert to 16-bit
+ // The palette data is converted to 8-bit at the time of loading
+ writeStream->writeUint16BE(pal[3 * i] << 8);
+ writeStream->writeUint16BE(pal[3 * i + 1] << 8);
+ writeStream->writeUint16BE(pal[3 * i + 2] << 8);
}
dumpFile("PaletteData", _castId, MKTAG('C', 'L', 'U', 'T'), dumpData, castSize);
diff --git a/engines/director/castmember/shape.cpp b/engines/director/castmember/shape.cpp
index 4d27d18996f..45bccae24d3 100644
--- a/engines/director/castmember/shape.cpp
+++ b/engines/director/castmember/shape.cpp
@@ -244,7 +244,7 @@ void ShapeCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
writeStream->writeUint16LE(_pattern);
// The foreground and background colors are transformed
- // Need to retreive the original colors for saving
+ // Need to retrieve the original colors for saving
writeStream->writeByte(_fgCol);
writeStream->writeByte(_bgCol);
diff --git a/engines/director/frame.cpp b/engines/director/frame.cpp
index 8277114cc7a..94fedda3deb 100644
--- a/engines/director/frame.cpp
+++ b/engines/director/frame.cpp
@@ -594,6 +594,7 @@ void Frame::readSpriteD4(Common::MemoryReadStreamEndian &stream, uint16 offset,
}
void readSpriteDataD4(Common::SeekableReadStreamEndian &stream, Sprite &sprite, uint32 startPosition, uint32 finishPosition) {
+ debug("I'm here to read: %d bytes, I'm at: %ld, starting at: %d, ending at %d", finishPosition - startPosition, stream.pos(), startPosition, finishPosition);
while (stream.pos() < finishPosition) {
switch (stream.pos() - startPosition) {
case 0:
@@ -716,6 +717,38 @@ void readSpriteDataD4(Common::SeekableReadStreamEndian &stream, Sprite &sprite,
sprite._width = sprite._height = 0;
}
+void writeSpriteDataD4(Common::MemoryWriteStream *writeStream, Sprite &sprite) {
+ // Writing 20 bytes of sprite data
+ // The original data for a certain sprite might be less
+ writeStream->writeByte(sprite._scriptId.member); // 0
+
+ // If the sprite is a puppet (controlled by lingo scripting)
+ // The rest of the data isn't read
+ if (sprite._puppet) {
+ writeStream->write(0, 19);
+ } else {
+ writeStream->writeByte((byte) sprite._spriteType); // 1
+ writeStream->writeByte(sprite._foreColor); // 2
+ writeStream->writeByte(sprite._backColor); // 3
+ writeStream->writeByte(sprite._thickness); // 4
+ writeStream->writeByte(sprite._inkData); // 5
+
+ if (sprite.isQDShape()) {
+ writeStream->writeUint16BE(sprite._pattern); // 6, 7
+ } else {
+ writeStream->writeUint16BE(sprite._castId.member); // 6, 7
+ }
+
+ writeStream->writeUint16BE(sprite._startPoint.y); // 8, 9
+ writeStream->writeUint16BE(sprite._startPoint.x); // 10, 11
+ writeStream->writeUint16BE(sprite._height); // 12, 13
+ writeStream->writeUint16BE(sprite._width); // 14, 15
+ writeStream->writeUint16BE(sprite._scriptId.member); // 16, 17
+ writeStream->writeByte(sprite._colorcode); // 18
+ writeStream->writeByte(sprite._blendAmount); // 19
+ }
+}
+
/**************************
*
* D5 Loading
diff --git a/engines/director/frame.h b/engines/director/frame.h
index 71b9182f077..b9d9632f376 100644
--- a/engines/director/frame.h
+++ b/engines/director/frame.h
@@ -34,6 +34,7 @@ struct Surface;
namespace Common {
class ReadStreamEndian;
class MemoryReadStreamEndian;
+class MemoryWriteStream;
}
namespace Director {
@@ -199,6 +200,8 @@ void readSpriteDataD4(Common::SeekableReadStreamEndian &stream, Sprite &sprite,
void readSpriteDataD5(Common::SeekableReadStreamEndian &stream, Sprite &sprite, uint32 startPosition, uint32 finishPosition);
void readSpriteDataD6(Common::SeekableReadStreamEndian &stream, Sprite &sprite, uint32 startPosition, uint32 finishPosition);
+void writeSpriteDataD4(Common::MemoryWriteStream *writeStream, Sprite &sprite);
+
} // End of namespace Director
#endif
Commit: 3691cd382b4dacd1c422e67eb160f9b09381af7f
https://github.com/scummvm/scummvm/commit/3691cd382b4dacd1c422e67eb160f9b09381af7f
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Recalculate the size and offset of resources while writing
Before writing down the resources, if there are any changes in size
and offset, they should be recalculated accordingly
Changed paths:
engines/director/archive.cpp
engines/director/archive.h
engines/director/cast.cpp
engines/director/cast.h
engines/director/castmember/bitmap.cpp
engines/director/castmember/bitmap.h
engines/director/castmember/castmember.h
engines/director/castmember/filmloop.cpp
engines/director/castmember/filmloop.h
engines/director/director.cpp
engines/director/frame.cpp
engines/director/lingo/lingo-builtins.cpp
diff --git a/engines/director/archive.cpp b/engines/director/archive.cpp
index 37a37761d5c..bc8500ad187 100644
--- a/engines/director/archive.cpp
+++ b/engines/director/archive.cpp
@@ -20,6 +20,7 @@
*/
#include "common/config-manager.h"
+#include "common/hashmap.h"
#include "common/file.h"
#include "common/substream.h"
#include "common/macresman.h"
@@ -28,7 +29,15 @@
#include "director/director.h"
#include "director/archive.h"
#include "director/movie.h"
+#include "director/cast.h"
#include "director/window.h"
+#include "director/sprite.h"
+
+#include "director/castmember/castmember.h"
+#include "director/castmember/bitmap.h"
+#include "director/castmember/text.h"
+#include "director/castmember/palette.h"
+#include "director/castmember/filmloop.h"
namespace Director {
@@ -790,8 +799,6 @@ bool RIFXArchive::readMemoryMap(Common::SeekableReadStreamEndian &stream, uint32
if (stream.readUint32() != MKTAG('i', 'm', 'a', 'p'))
return false;
- _types[MKTAG('i', 'm', 'a', 'p')][0].accessed = true; // Mark it as accessed
-
_imapLength = stream.readUint32(); // imap length
_mapversion = stream.readUint32(); // version, seen 0 or 1
uint32 mmapOffsetPos = stream.pos();
@@ -814,8 +821,6 @@ bool RIFXArchive::readMemoryMap(Common::SeekableReadStreamEndian &stream, uint32
return false;
}
- _types[MKTAG('m', 'm', 'a', 'p')][0].accessed = true; // Mark it as accessed
-
_mmapLength = stream.readUint32(); // mmap length
_mmapHeaderSize = stream.readUint16(); // header size
_mmapEntrySize = stream.readUint16(); // size of map entry
@@ -857,6 +862,14 @@ bool RIFXArchive::readMemoryMap(Common::SeekableReadStreamEndian &stream, uint32
_resources.push_back(&res);
}
+ for (auto &it : _types[MKTAG('m', 'm', 'a', 'p')]) {
+ _types[MKTAG('m', 'm', 'a', 'p')][it._key].accessed = true; // Mark it as accessed
+ }
+
+ for (auto &it : _types[MKTAG('i', 'm', 'a', 'p')]) {
+ _types[MKTAG('i', 'm', 'a', 'p')][it._key].accessed = true; // Mark it as accessed
+ }
+
if (debugChannelSet(5, kDebugLoading)) {
debugC(5, kDebugLoading, "RIFXArchive::readMemoryMap: Resources found:");
for (const auto &it : _types) {
@@ -1163,16 +1176,18 @@ Common::String RIFXArchive::formatArchiveInfo() {
return result;
}
-bool RIFXArchive::writeToFile(Common::Path path) {
+bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
+ rebuildTypes(movie);
// The offsets used in this function should be not the same as original
// That defeats the whole purpose of writing movies
// ignoring the startOffset
- // For RIFX stream, moreoffset = 0
+ // For RIFX stream, moreoffset = 0, we won't be writing macbinary
byte *dumpData = nullptr;
// Don't need to allocate this much size in case 'junk' and 'free' resources are ignored
// Or might need to allocate even more size if extra chunks are written
+ _size = getArchiveSize();
dumpData = (byte *)calloc(_size, sizeof(byte));
Common::SeekableMemoryWriteStream *writeStream = new Common::SeekableMemoryWriteStream(dumpData, _size);
@@ -1221,23 +1236,26 @@ bool RIFXArchive::writeToFile(Common::Path path) {
for (auto &it : _keyData[casTag]) {
for (auto &jt : it._value) {
uint32 offset = getOffset(casTag, jt) + 8;
- writeCast(writeStream, offset);
+ writeCast(writeStream, offset, it._key);
}
}
}
+ int i = 0;
+ debug("What is the size of the archive = %d, what is the size of stream = %ld", getArchiveSize(), writeStream->size());
for (auto &it : _resources) {
if (it->tag != _metaTag &&
it->tag != SWAP_BYTES_32(_metaTag) &&
it->tag != MKTAG('i', 'm', 'a', 'p') &&
it->tag != MKTAG('m', 'm', 'a', 'p') &&
- it->tag != MKTAG('K', 'E', 'Y', '*') &&
- it->tag != MKTAG('f', 'r', 'e', 'e') // Sometimes 'free' resources have garbage values, they might overwrite other data
+ it->tag != MKTAG('K', 'E', 'Y', '*')
+ // it->tag != MKTAG('f', 'r', 'e', 'e') // Sometimes 'free' resources have garbage values, they might overwrite other data
) {
writeStream->seek(it->offset);
+ debug("position: %ld, i: %d and it->size: %d, tag = %s, offset = %d", writeStream->pos(), i, it->size, tag2str(it->tag), it->offset);
writeStream->writeUint32LE(it->tag);
writeStream->writeUint32LE(it->size);
- writeStream->writeStream(getResource(it->tag, it->index));
+ i++;
}
}
@@ -1272,7 +1290,7 @@ bool RIFXArchive::writeMemoryMap(Common::SeekableMemoryWriteStream *writeStream)
// Need to recalculate the following things
// Similarly to the RIFX resource, we'll need to update the size of the mmap resource whenever there is some change
- writeStream->writeUint32LE(getResourceSize(MKTAG('m', 'm', 'a', 'p'), 0));
+ writeStream->writeUint32LE(getResourceSize(MKTAG('m', 'm', 'a', 'p'), 2));
writeStream->writeUint16LE(_mmapHeaderSize);
writeStream->writeUint16LE(_mmapEntrySize);
@@ -1355,13 +1373,13 @@ bool RIFXArchive::writeKeyTable(Common::SeekableMemoryWriteStream *writeStream,
return true;
}
-bool RIFXArchive::writeCast(Common::SeekableWriteStream *writeStream, uint32 offset) {
+bool RIFXArchive::writeCast(Common::SeekableWriteStream *writeStream, uint32 offset, uint32 castLib) {
writeStream->seek(offset);
uint castTag = MKTAG('C', 'A', 'S', 't');
for (auto &it : _types[castTag]) {
- if (it._value.libResourceId) {
+ if (it._value.libResourceId == castLib) {
writeStream->writeUint32LE(it._key);
}
}
@@ -1369,6 +1387,300 @@ bool RIFXArchive::writeCast(Common::SeekableWriteStream *writeStream, uint32 off
return true;
}
+void RIFXArchive::rebuildTypes(Movie *movie) {
+ // Currently I'm modifying the original _resources, _types and _keydata structures, this is what happens in the original Director
+ // However, if we don't want that, we could make copies of them and then modify them
+
+ // Currently handled Resource types:
+ // imap // BITD
+ // mmap // CLUT
+ // RIFX // STXT
+ // KEY* // SCVW (filmloop)
+ // CAS* // VWCF
+ // CASt //
+
+ // Resources yet to be handled
+ // Any external file
+ // Script 'Lscr'
+ // Script Names 'Lnam'
+ // Lingo Context 'Lctx'
+ // Score 'SCVW'
+ // Rich Text 'RTE0', 'RTE1', 'RTE2'
+ // Sound ('snd ')
+ // 'SCVW' External Movies
+
+ // Transitions and OLE are Director 5+, not yet loaded in ScummVM Director
+
+ // Resources that are loaded but probably need not be handled
+ // 'VWFM' Font Mapping
+ // 'FXmp' Corss Platform Font Mapping
+ // 'VWTL' Pattern Tiles
+ // 'STR ' External Sound files
+ // 'MooV' External Digital Video
+ // 'XCOD' XObjects
+ // 'Cinf' Cast Lib Info
+ // 'VWCI' Cast Info (For what cast though?)
+ // 'Sord' Score Order List Resource
+ // 'PICT' Picture data
+ // 'SCRF' External Cast Reference
+ // 'Xtra' Xtras
+ // 'THUM' Thumbnail
+
+ // First we'll have to update the _types table to include all the newly added
+ // cast members, and their 'CASt' resources
+ // Only handling movies with a single cast for now
+ Cast *cast = movie->getCast();
+ ResourceMap &castResMap = _types[MKTAG('C', 'A', 'S', 't')];
+
+ for (auto it : *(cast->_loadedCast)) {
+ if (!castResMap.contains(it._value->_index)) {
+ Resource *res = &castResMap[it._value->_index - cast->_castArrayStart];
+ res->tag = MKTAG('C', 'A', 'S', 't');
+ res->accessed = true;
+ res->libResourceId = cast->_castLibID;
+ res->children = it._value->_children;
+ res->index = _resources.size() + 1;
+
+ for (auto child : it._value->_children) {
+ _keyData[child.tag][res->index].push_back(child.index); // Indices are a huge problem, need to figure out them first
+ }
+ _resources.push_back(res);
+
+ debugC(5, kDebugSaving, "RIFXArchive::rebuildTypes(): new 'CASt' resource added");
+ }
+ }
+
+ // TODO: Then we'll need to see if there are any other newly added resources
+ // Now when you duplicate a cast member, say BitmapCastMember, the cast member is duplicated
+ // but its children resources are not, meaning the duplicated BitmapCastMember is also loaded from the same 'BITD' resource
+ // So it is not necessary to duplicate the 'BITD' resource
+ // However, in case an entirely new cast member is added, say a filmloop is recorded, then that requires a new 'SCVW' resource
+ // Same goes for if a new cast is added to the movie
+ // Ignoring that for now
+
+ // Next step is to recalculate the sizes and the offsets of all the resources
+ // Handle writing KEY* and CAS* last, because they need to know the indices before hand
+ // Also their indexes will be 0 through 4 (fixed), RIFX and imap need to be the very first two indices 0 and 1
+ // The rest can be rearranged to any index, but for simplicity, we'll assign them a fixed index
+
+ // This might become easier if ResourceMap was an Array rather than a HasMap, can consider refactoring
+ // Anyways its key is not used anywhere
+
+ uint16 newIndex = 3;
+
+ // Since the first 5 resources are determined (for simplicity)
+ // We'll start writing after that RIFX header, mmap and imap resources
+ uint32 currentSize = 12 + getImapSize() + getMmapSize();
+
+ // This switch statement can be simplified by keeping a pointer to the write function of the resrouce in the Resource
+ // But that will require accessing _resource every time so not doing that right now
+ uint32 resSize = 0;
+ for (auto &it : _resources) {
+ switch (it->tag) {
+ case MKTAG('R', 'I', 'F', 'X'):
+ case MKTAG('X', 'F', 'I', 'R'):
+ // only one resource only
+ // Size will be determined after all other sizes have been calculated
+ it->index = 0;
+ it->offset = 0;
+ break;
+
+ case MKTAG('i', 'm', 'a', 'p'):
+ // one resource only
+ it->index = 1;
+ it->size = getImapSize();
+ it->offset = 12;
+ break;
+
+ case MKTAG('m', 'm', 'a', 'p'):
+ // one resource only
+ it->index = 2;
+ it->size = getMmapSize();
+ it->offset = 12 + getImapSize();
+ break;
+
+ case MKTAG('C', 'A', 'S', 't'):
+ {
+ // The castIds of cast members start from _castArrayStart
+ it->index = newIndex;
+ newIndex += 1;
+
+ CastMember *target = cast->getCastMember(it->castId - cast->_castArrayStart);
+
+ if (target) {
+ resSize = target->getCastResourceSize();
+ it->size = resSize; // getCastResourceSize returns size without header and size
+ } else {
+ resSize = it->size;
+ }
+ it->offset = currentSize;
+ currentSize += resSize + 8;
+ break;
+ }
+
+ case MKTAG('C', 'A', 'S', '*'):
+ // Currently handling only movies with one 'CAS*' resource, i.e. only one cast
+ it->index = newIndex;
+ newIndex += 1;
+
+ resSize = getCASResourceSize(); // getCASResourceSize() returns size without header and size
+ it->size = resSize;
+ it->offset = currentSize;
+ currentSize += resSize + 8;
+ break;
+
+ case MKTAG('V', 'W', 'C', 'F'):
+ {
+ it->index = newIndex;
+ newIndex += 1;
+ // Cast config, as many resources as Casts
+ // No need to update the key mapping
+ resSize = cast->getConfigSize();
+
+ it->offset = currentSize;
+
+ currentSize += resSize + 8; // getConfigSize() doesn't include header and size
+ it->size = resSize;
+ break;
+ }
+ case MKTAG('S', 'T', 'X', 'T'):
+ {
+ uint32 parentIndex = _keyData[MKTAG('S', 'T', 'X', 'T')].begin()->_key;
+ Resource parent = castResMap[parentIndex];
+
+ it->index = newIndex;
+ newIndex += 1;
+
+ TextCastMember *target = (TextCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
+ resSize = target->getSTXTResourceSize();
+
+ it->offset = currentSize;
+ it->size = resSize;
+
+ currentSize += resSize + 8;
+ break;
+ }
+
+ case MKTAG('C', 'L', 'U', 'T'):
+ {
+ uint32 parentIndex = _keyData[MKTAG('C', 'L', 'U', 'T')].begin()->_key;
+ Resource parent = castResMap[parentIndex];
+
+ it->index = newIndex;
+ newIndex += 1;
+
+ PaletteCastMember *target = (PaletteCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
+ resSize = target->getPaletteDataSize();
+
+ it->offset = currentSize;
+ it->size = resSize;
+
+ currentSize += resSize + 8;
+ break;
+ }
+
+ case MKTAG('B', 'I', 'T', 'D'):
+ {
+ // One child can have multiple parents, so the question is whether or not to choose parent arbitrarily
+ // and whether a single parent can have multiple 'BITD' children, BitmapCastMember:load() suggests that it doesn't
+ uint32 parentIndex = _keyData[MKTAG('B', 'I', 'T', 'D')].begin()->_key;
+
+ // This begs the question whether 'BITD' resources can be children to resources other than 'CASt'
+ Resource parent = castResMap[parentIndex];
+
+ it->index = newIndex;
+ newIndex += 1;
+
+ BitmapCastMember *target = (BitmapCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
+ resSize = target->getBITDResourceSize();
+
+ it->offset = currentSize;
+ it->size = resSize;
+
+ currentSize += resSize + 8;
+ break;
+ }
+
+ case MKTAG('S', 'C', 'V', 'W'):
+ {
+ uint32 parentIndex = _keyData[MKTAG('S', 'C', 'V', 'W')].begin()->_key;
+ Resource parent = castResMap[parentIndex];
+
+ it->index = newIndex;
+ newIndex += 1;
+
+ FilmLoopCastMember *target = (FilmLoopCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
+ resSize = target->getSCVWResourceSize();
+
+ it->offset = currentSize;
+ it->size = resSize;
+
+ currentSize += resSize + 8;
+ break;
+ }
+ break;
+
+ case MKTAG('f', 'r', 'e', 'e'):
+ case MKTAG('j', 'u', 'n', 'k'):
+ // These resource do not hold any data
+ it->size = 0;
+
+ // We could just ignore these and not write them at all
+ it->offset = currentSize;
+ currentSize += 8;
+ it->index = newIndex;
+ newIndex += 1;
+ break;
+
+ default:
+ it->index = newIndex;
+ it->offset = currentSize;
+ currentSize += it->size + 8; // This size doesn't include the header and size entry
+ newIndex += 1;
+ break;
+
+ }
+ debugC(3, kDebugSaving, "Rebuild RIFX resource index %d: '%s', %d bytes @ 0x%08x (%d), flags: %x unk1: %x nextFreeResourceId: %d",
+ it->index, tag2str(it->tag), it->size, it->offset, it->offset, it->flags, it->unk1, it->nextFreeResourceID);
+ }
+
+ // Now that all sizes have been updated, we can safely calculate the overall archive size
+ for (auto &it : _resources) {
+ if (it->tag == MKTAG('R', 'I', 'F', 'X') || it->tag == MKTAG('X', 'F', 'I', 'R')) {
+ it->size = getArchiveSize() + 8;
+ }
+ }
+
+ // Need to rebuild the key map as well
+}
+
+uint32 RIFXArchive::getImapSize() {
+ // The length of imap doesn't change
+ // This is the length without
+ return 12;
+}
+
+uint32 RIFXArchive::getMmapSize() {
+ // The headers: 24 bytes and and 8 bytes per resources
+ return 24 + 8 * _resources.size();
+}
+
+uint32 RIFXArchive::getArchiveSize() {
+ // This will be called after updating the size of all the resources
+ uint32 size = 0;
+
+ for (auto it : _resources) {
+ if (it->tag != MKTAG('R', 'I', 'F', 'X') && it->tag != MKTAG('X', 'F', 'I', 'R')) {
+ size += it->size + 8; // The 8 is to account for the header and size
+ }
+ }
+ return size;
+}
+
+uint32 RIFXArchive::getCASResourceSize() {
+ return _types[MKTAG('C', 'A', 'S', 't')].size() * 4;
+}
+
void dumpFile(Common::String fileName, uint32 id, uint32 tag, byte *dumpData, uint32 dumpSize) {
debug("dumpFile():: dumping file %s, resource: %s (id: %d)", fileName.c_str(), tag2str(tag), id);
Common::DumpFile out;
diff --git a/engines/director/archive.h b/engines/director/archive.h
index 6463c211529..e4730d6ecba 100644
--- a/engines/director/archive.h
+++ b/engines/director/archive.h
@@ -61,7 +61,7 @@ public:
virtual bool openFile(const Common::Path &path);
virtual bool openStream(Common::SeekableReadStream *stream, uint32 offset = 0) = 0;
- virtual bool writeToFile(Common::Path path) {
+ virtual bool writeToFile(Common::Path path, Movie *movie) {
// Saving Director movies was introduced in Director 4
// However, from DirectorEngine::createArchive, it is evident that after Director 4 only RIFX Archives were written
error("Archive::writeToFile was called on a non-RIFX Archive, which is not allowed");
@@ -148,7 +148,7 @@ public:
~RIFXArchive() override;
bool openStream(Common::SeekableReadStream *stream, uint32 startOffset = 0) override;
- bool writeToFile(Common::Path writePath) override;
+ bool writeToFile(Common::Path writePath, Movie *movie) override;
Common::SeekableReadStreamEndian *getFirstResource(uint32 tag) override;
virtual Common::SeekableReadStreamEndian *getFirstResource(uint32 tag, bool fileEndianness);
@@ -163,7 +163,13 @@ private:
bool writeMemoryMap(Common::SeekableMemoryWriteStream *writeStream); // Parallel to readMemoryMap
bool writeAfterBurnerMap(Common::SeekableMemoryWriteStream *writeStreaa); // Parallel to readAfterBurnerMap
bool writeKeyTable(Common::SeekableMemoryWriteStream *writeStream, uint32 offset); // Parallel to readKeyTable
- bool writeCast(Common::SeekableWriteStream *writeStream, uint32 offset); // Parallel to readCast
+ bool writeCast(Common::SeekableWriteStream *writeStream, uint32 offset, uint32 castLib); // Parallel to readCast
+
+ uint32 getArchiveSize();
+ uint32 getMmapSize();
+ uint32 getImapSize();
+ uint32 getCASResourceSize();
+ void rebuildTypes(Movie *movie);
bool readMemoryMap(Common::SeekableReadStreamEndian &stream, uint32 moreOffset, Common::SeekableMemoryWriteStream *dumpStream, uint32 movieStartOffset);
bool readAfterburnerMap(Common::SeekableReadStreamEndian &stream, uint32 moreOffset);
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index 4aa73afef59..02456977e9c 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -540,6 +540,10 @@ void Cast::saveConfig(Common::MemoryWriteStream *writeStream, uint32 offset) {
}
writeStream->seek(offset); // This will allow us to write cast config at any offset
+
+ writeStream->writeUint32LE(MKTAG('V', 'W', 'C', 'F'));
+ writeStream->writeUint32LE(getConfigSize());
+
// These offsets are only for Director Version 4 to Director version 6
// offsets
writeStream->writeUint16LE(_len); // 0 // This will change
@@ -548,7 +552,8 @@ void Cast::saveConfig(Common::MemoryWriteStream *writeStream, uint32 offset) {
Movie::writeRect(writeStream, _checkRect); // 4, 6, 8, 10
writeStream->writeUint16LE(_castArrayStart); // 12
- writeStream->writeUint16LE(_castArrayEnd); // 14 // This will change
+ // This will change
+ writeStream->writeUint16LE(_castArrayStart + _loadedCast->size()); // 14
writeStream->writeByte(_readRate); // 16
writeStream->writeByte(_lightswitch); // 17
@@ -598,11 +603,22 @@ void Cast::saveConfig(Common::MemoryWriteStream *writeStream, uint32 offset) {
}
writeStream->writeSint16LE(_defaultPalette.castLib); // 76
- writeStream->writeSint16LE(_defaultPalette.member); // 77
+ writeStream->writeSint16LE(_defaultPalette.member); // 78
}
}
+uint32 Cast::getConfigSize() {
+ if (_version >= kFileVer400 && _version < kFileVer500) {
+ return 78; // 78 bytes of data in castConfig
+ } else if (_version >= kFileVer500 && _version < kFileVer600) {
+ return 80; // 80 bytes of data in castConfig
+ }
+
+ warning("Cast::getConfigSize: Director version 6+ is not handled");
+ return 0;
+}
+
void Cast::loadCast() {
Common::SeekableReadStreamEndian *r = nullptr;
@@ -1432,6 +1448,7 @@ void Cast::loadCastData(Common::SeekableReadStreamEndian &stream, uint16 id, Res
if (target) {
target->_castDataSize = castDataSize;
target->_flags1 = flags1;
+ target->_index = res->index;
setCastMember(id, target);
}
if (castStream.eos()) {
diff --git a/engines/director/cast.h b/engines/director/cast.h
index de88ef24853..63e21c49197 100644
--- a/engines/director/cast.h
+++ b/engines/director/cast.h
@@ -113,6 +113,8 @@ public:
uint32 getCastInfoSize(uint32 castId);
uint32 getCastInfoStringLength(uint32 stringIndex, CastMemberInfo *ci);
+ uint32 getConfigSize();
+
int getCastSize();
int getCastMaxID();
int getNextUnusedID();
@@ -183,7 +185,7 @@ public:
LingoDec::ScriptContext *_lingodec = nullptr;
LingoDec::ChunkResolver *_chunkResolver = nullptr;
- /* Data to be saved */
+ /* Config Data to be saved */
/* 0 */ uint16 _len;
/* 2 */ uint16 _fileVersion;
/* 4, 6, 8, 10 */ Common::Rect _checkRect;
diff --git a/engines/director/castmember/bitmap.cpp b/engines/director/castmember/bitmap.cpp
index 78352ca1521..32c5ebda03d 100644
--- a/engines/director/castmember/bitmap.cpp
+++ b/engines/director/castmember/bitmap.cpp
@@ -1058,7 +1058,6 @@ uint32 BitmapCastMember::writeBITDResource(Common::MemoryWriteStream *writeStrea
offset = _picture->_surface.w % 2;
}
- debug("What is _bitsPerPixel = %d", _bitsPerPixel);
for (int y = 0; y < _picture->_surface.h; y++) {
for (int x = 0; x < _picture->_surface.w;) {
uint32 color = 0;
@@ -1117,4 +1116,9 @@ uint32 BitmapCastMember::writeBITDResource(Common::MemoryWriteStream *writeStrea
return 0;
}
+uint32 BitmapCastMember::getBITDResourceSize() {
+ // No compression for now
+ return _pitch * _picture->_surface.h;
+}
+
} // End of namespace Director
diff --git a/engines/director/castmember/bitmap.h b/engines/director/castmember/bitmap.h
index 257b6ed4324..210bcf7373a 100644
--- a/engines/director/castmember/bitmap.h
+++ b/engines/director/castmember/bitmap.h
@@ -68,6 +68,7 @@ public:
uint32 writeBITDResource(Common::MemoryWriteStream *writeStream, uint32 offset);
uint32 getCastDataSize() override; // This is the size of the data in the 'CASt' resource
+ uint32 getBITDResourceSize();
Picture *_picture = nullptr;
Graphics::Surface *_ditheredImg;
diff --git a/engines/director/castmember/castmember.h b/engines/director/castmember/castmember.h
index bd300b20b5e..29725b8d9f0 100644
--- a/engines/director/castmember/castmember.h
+++ b/engines/director/castmember/castmember.h
@@ -124,10 +124,15 @@ public:
/* Data fields used when saving the Cast Member */
uint32 _castDataSize;
uint8 _flags1;
+ // This index is the index that it appears in the original movie archive
+ int16 _index;
protected:
Cast *_cast;
- uint16 _castId;
+ // This is the id of the cast member, this id is unique to only cast members
+ // Basically the cast members are given ids starting from _castArrayStart to _castArrayEnd
+ // e.g. 0, 1, 2, 3, 4
+ uint16 _castId;
// a link to the widget we created, we may use it later
Graphics::MacWidget *_widget;
bool _loaded;
diff --git a/engines/director/castmember/filmloop.cpp b/engines/director/castmember/filmloop.cpp
index c2d364f7840..c461a724f4e 100644
--- a/engines/director/castmember/filmloop.cpp
+++ b/engines/director/castmember/filmloop.cpp
@@ -37,6 +37,7 @@
#include "director/castmember/bitmap.h"
#include "director/castmember/filmloop.h"
+
namespace Director {
FilmLoopCastMember::FilmLoopCastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndian &stream, uint16 version)
@@ -46,6 +47,7 @@ FilmLoopCastMember::FilmLoopCastMember(Cast *cast, uint16 castId, Common::Seekab
_enableSound = true;
_crop = false;
_center = false;
+ _index = -1;
// We are ignoring some of the bits in the flags
if (cast->_version >= kFileVer400 && cast->_version < kFileVer500) {
@@ -86,6 +88,7 @@ FilmLoopCastMember::FilmLoopCastMember(Cast *cast, uint16 castId, FilmLoopCastMe
_center = source._center;
_frames = source._frames;
_subchannels = source._subchannels;
+ _index = -1;
}
FilmLoopCastMember::~FilmLoopCastMember() {
@@ -315,7 +318,7 @@ void FilmLoopCastMember::loadFilmLoopDataD4(Common::SeekableReadStreamEndian &st
int channelOffset = order % channelSize;
int offset = order;
- debugC(2, kDebugLoading, "loadFilmLoopDataD4: Message: msgWidth %d, order: %d, channel %d, channelOffset %d", msgWidth, order, channel, channelOffset);
+ debugC(8, kDebugLoading, "loadFilmLoopDataD4: Message: msgWidth %d, order: %d, channel %d, channelOffset %d", msgWidth, order, channel, channelOffset);
if (debugChannelSet(8, kDebugLoading)) {
stream.hexdump(msgWidth);
}
@@ -768,7 +771,7 @@ void FilmLoopCastMember::writeSCVWResource(Common::MemoryWriteStream *writeStrea
writeStream->seek(offset);
writeStream->writeUint32LE(MKTAG('S', 'C', 'V', 'W'));
- writeStream->writeUint32LE(getSCVWResourceSize(channelSize)); // Size of the resource
+ writeStream->writeUint32LE(getSCVWResourceSize()); // Size of the resource
uint32 sizePos = writeStream->pos();
writeStream->writeUint32BE(0); // Putting zeroes instead of size currently
@@ -815,11 +818,20 @@ void FilmLoopCastMember::writeSCVWResource(Common::MemoryWriteStream *writeStrea
}
}
-uint32 FilmLoopCastMember::getSCVWResourceSize(uint32 channelSize) {
+uint32 FilmLoopCastMember::getSCVWResourceSize() {
// Size of the filmloop data: 4 bytes
// frameoffset: 4 bytes
// Header (Ignored data): 12 bytes
- //
+
+ uint32 channelSize = 0;
+ if (_cast->_version == kFileVer400) {
+ channelSize = kSprChannelSizeD4;
+ } else if (_cast->_version == kFileVer400) {
+ channelSize = kSprChannelSizeD5;
+ } else {
+ warning("FilmLoopCastMember::getSCVWResourceSize: Director version unsupported");
+ }
+
uint32 framesSize = 0;
for (FilmLoopFrame frame : _frames) {
for (auto it : frame.sprites) {
diff --git a/engines/director/castmember/filmloop.h b/engines/director/castmember/filmloop.h
index 577ed91047b..c65b6f9f1ec 100644
--- a/engines/director/castmember/filmloop.h
+++ b/engines/director/castmember/filmloop.h
@@ -62,7 +62,7 @@ public:
void writeCastData(Common::MemoryWriteStream *writeStream) override;
void writeSCVWResource(Common::MemoryWriteStream *writeStream, uint32 offset);
- uint32 getSCVWResourceSize(uint32 channelSize);
+ uint32 getSCVWResourceSize();
bool _enableSound;
bool _looping;
diff --git a/engines/director/director.cpp b/engines/director/director.cpp
index c200111d94b..dedf98f8d83 100644
--- a/engines/director/director.cpp
+++ b/engines/director/director.cpp
@@ -385,7 +385,7 @@ Common::Error DirectorEngine::run() {
if (debugChannelSet(-1, kDebugSaving)) {
Common::Path writePath("./dumps/writtenMovie.dir");
- _mainArchive->writeToFile(writePath);
+ _mainArchive->writeToFile(writePath, getCurrentMovie());
getCurrentMovie()->getCast()->saveCast();
}
diff --git a/engines/director/frame.cpp b/engines/director/frame.cpp
index 94fedda3deb..35073a21b3b 100644
--- a/engines/director/frame.cpp
+++ b/engines/director/frame.cpp
@@ -594,7 +594,6 @@ void Frame::readSpriteD4(Common::MemoryReadStreamEndian &stream, uint16 offset,
}
void readSpriteDataD4(Common::SeekableReadStreamEndian &stream, Sprite &sprite, uint32 startPosition, uint32 finishPosition) {
- debug("I'm here to read: %d bytes, I'm at: %ld, starting at: %d, ending at %d", finishPosition - startPosition, stream.pos(), startPosition, finishPosition);
while (stream.pos() < finishPosition) {
switch (stream.pos() - startPosition) {
case 0:
diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index 165addecc08..3db5e377148 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -1575,9 +1575,11 @@ void LB::b_save(int nargs) {
}
void LB::b_saveMovie(int nargs) {
- g_lingo->printSTUBWithArglist("b_saveMovie", nargs);
-
- g_lingo->dropStack(nargs);
+ Common::Path path;
+ if (nargs) {
+ path = Common::Path(g_lingo->pop().asString());
+ }
+ g_director->getMainArchive()->writeToFile(path, g_director->getCurrentMovie());
}
void LB::b_setCallBack(int nargs) {
Commit: 3ab3204b09c5677214e84e4611de89f86185d70a
https://github.com/scummvm/scummvm/commit/3ab3204b09c5677214e84e4611de89f86185d70a
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Remove `saveMovie` lingo command STUB
Start writing all the chunks into one file
Changed paths:
engines/director/archive.cpp
engines/director/archive.h
engines/director/cast.cpp
engines/director/cast.h
engines/director/castmember/filmloop.cpp
engines/director/castmember/palette.cpp
engines/director/castmember/text.cpp
engines/director/director.cpp
diff --git a/engines/director/archive.cpp b/engines/director/archive.cpp
index bc8500ad187..0e8293e6087 100644
--- a/engines/director/archive.cpp
+++ b/engines/director/archive.cpp
@@ -1177,7 +1177,7 @@ Common::String RIFXArchive::formatArchiveInfo() {
}
bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
- rebuildTypes(movie);
+ rebuildResources(movie);
// The offsets used in this function should be not the same as original
// That defeats the whole purpose of writing movies
@@ -1222,40 +1222,87 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
break;
}
- int32 keyTag = MKTAG('K', 'E', 'Y', '*');
- if (hasResource(keyTag, -1)) {
- uint16 firstID = getResourceIDList(keyTag)[0];
- // The +8 is consistent with RIFXArchive::getResource()
- // The +8 is to ignore the resource tag (e.g. 'KEY*') and the size entry
- uint32 offset = getOffset(keyTag, firstID) + 8;
- writeKeyTable(writeStream, offset);
- }
+ Cast *cast = movie->getCast();
+ ResourceMap castResMap = _types[MKTAG('C', 'A', 'S', 't')];
- int32 casTag = MKTAG('C', 'A', 'S', '*');
- if (_keyData.contains(casTag)) {
- for (auto &it : _keyData[casTag]) {
- for (auto &jt : it._value) {
- uint32 offset = getOffset(casTag, jt) + 8;
- writeCast(writeStream, offset, it._key);
+ for (auto &it : _resources) {
+ debugC(5, kDebugSaving, "RIFXArchive::writeToFile: writing resource: index: %d, size: %d, tag = %s, offset = %d", it->index, it->size, tag2str(it->tag), it->offset);
+
+ switch (it->tag) {
+ case MKTAG('R', 'I', 'F', 'X'):
+ case MKTAG('X', 'F', 'I', 'R'):
+ // meta resource
+ break;
+
+ case MKTAG('i', 'm', 'a', 'p'):
+ case MKTAG('m', 'm', 'a', 'p'):
+ // Already written
+ break;
+
+ case MKTAG('K', 'E', 'Y', '*'):
+ writeKeyTable(writeStream, it->offset);
+ break;
+
+ case MKTAG('C', 'A', 'S', '*'):
+ writeCast(writeStream, it->offset, it->castId);
+ break;
+
+ case MKTAG('C', 'A', 'S', 't'):
+ cast->saveCast(writeStream, it);
+ break;
+
+ case MKTAG('V', 'W', 'C', 'F'):
+ cast->saveConfig(writeStream, it->offset);
+ break;
+
+ case MKTAG('B', 'I', 'T', 'D'):
+ {
+ // Get one of the parents of the 'BITD' resource stored in the _keyData
+ // Ask the parent to write the 'BITD' resource, assuming the parent only has only one 'BITD' resource
+ uint32 parentIndex = _keyData[MKTAG('B', 'I', 'T', 'D')].begin()->_key;
+ Resource parent = castResMap[parentIndex];
+
+ BitmapCastMember *target = (BitmapCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
+ target->writeBITDResource(writeStream, it->offset);
}
- }
- }
+ break;
- int i = 0;
- debug("What is the size of the archive = %d, what is the size of stream = %ld", getArchiveSize(), writeStream->size());
- for (auto &it : _resources) {
- if (it->tag != _metaTag &&
- it->tag != SWAP_BYTES_32(_metaTag) &&
- it->tag != MKTAG('i', 'm', 'a', 'p') &&
- it->tag != MKTAG('m', 'm', 'a', 'p') &&
- it->tag != MKTAG('K', 'E', 'Y', '*')
- // it->tag != MKTAG('f', 'r', 'e', 'e') // Sometimes 'free' resources have garbage values, they might overwrite other data
- ) {
+ case MKTAG('S', 'T', 'X', 'T'):
+ {
+ uint32 parentIndex = _keyData[MKTAG('S', 'T', 'X', 'T')].begin()->_key;
+ Resource parent = castResMap[parentIndex];
+
+ TextCastMember *target = (TextCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
+ target->writeSTXTResource(writeStream, it->offset);
+ }
+ break;
+
+ case MKTAG('C', 'L', 'U', 'T'):
+ {
+ uint32 parentIndex = _keyData[MKTAG('C', 'L', 'U', 'T')].begin()->_key;
+ Resource parent = castResMap[parentIndex];
+
+ PaletteCastMember *target = (PaletteCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
+ target->writePaletteData(writeStream, it->offset);
+ }
+ break;
+
+ case MKTAG('S', 'C', 'V', 'W'):
+ {
+ uint32 parentIndex = _keyData[MKTAG('S', 'C', 'V', 'W')].begin()->_key;
+ Resource parent = castResMap[parentIndex];
+
+ FilmLoopCastMember *target = (FilmLoopCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
+ target->writeSCVWResource(writeStream, it->offset);
+ }
+ break;
+
+ default:
writeStream->seek(it->offset);
- debug("position: %ld, i: %d and it->size: %d, tag = %s, offset = %d", writeStream->pos(), i, it->size, tag2str(it->tag), it->offset);
writeStream->writeUint32LE(it->tag);
writeStream->writeUint32LE(it->size);
- i++;
+ writeStream->writeStream(getResource(it->tag, it->index));
+ break;
}
}
@@ -1350,6 +1397,8 @@ bool RIFXArchive::writeAfterBurnerMap(Common::SeekableMemoryWriteStream *writeSt
bool RIFXArchive::writeKeyTable(Common::SeekableMemoryWriteStream *writeStream, uint32 offset) {
writeStream->seek(offset);
+ writeStream->writeUint32LE(MKTAG('K', 'E', 'Y', '*'));
+
writeStream->writeUint16LE(_keyTableEntrySize);
writeStream->writeUint16LE(_keyTableEntrySize2);
writeStream->writeUint32LE(_keyTableEntryCount);
@@ -1387,7 +1436,7 @@ bool RIFXArchive::writeCast(Common::SeekableWriteStream *writeStream, uint32 off
return true;
}
-void RIFXArchive::rebuildTypes(Movie *movie) {
+void RIFXArchive::rebuildResources(Movie *movie) {
// Currently I'm modifying the original _resources, _types and _keydata structures, this is what happens in the original Director
// However, if we don't want that, we could make copies of them and then modify them
@@ -1446,7 +1495,7 @@ void RIFXArchive::rebuildTypes(Movie *movie) {
}
_resources.push_back(res);
- debugC(5, kDebugSaving, "RIFXArchive::rebuildTypes(): new 'CASt' resource added");
+ debugC(5, kDebugSaving, "RIFXArchive::rebuildResources(): new 'CASt' resource added");
}
}
@@ -1466,8 +1515,6 @@ void RIFXArchive::rebuildTypes(Movie *movie) {
// This might become easier if ResourceMap was an Array rather than a HasMap, can consider refactoring
// Anyways its key is not used anywhere
- uint16 newIndex = 3;
-
// Since the first 5 resources are determined (for simplicity)
// We'll start writing after that RIFX header, mmap and imap resources
uint32 currentSize = 12 + getImapSize() + getMmapSize();
@@ -1481,20 +1528,17 @@ void RIFXArchive::rebuildTypes(Movie *movie) {
case MKTAG('X', 'F', 'I', 'R'):
// only one resource only
// Size will be determined after all other sizes have been calculated
- it->index = 0;
it->offset = 0;
break;
case MKTAG('i', 'm', 'a', 'p'):
// one resource only
- it->index = 1;
it->size = getImapSize();
it->offset = 12;
break;
case MKTAG('m', 'm', 'a', 'p'):
// one resource only
- it->index = 2;
it->size = getMmapSize();
it->offset = 12 + getImapSize();
break;
@@ -1502,9 +1546,6 @@ void RIFXArchive::rebuildTypes(Movie *movie) {
case MKTAG('C', 'A', 'S', 't'):
{
// The castIds of cast members start from _castArrayStart
- it->index = newIndex;
- newIndex += 1;
-
CastMember *target = cast->getCastMember(it->castId - cast->_castArrayStart);
if (target) {
@@ -1515,14 +1556,11 @@ void RIFXArchive::rebuildTypes(Movie *movie) {
}
it->offset = currentSize;
currentSize += resSize + 8;
- break;
}
+ break;
case MKTAG('C', 'A', 'S', '*'):
// Currently handling only movies with one 'CAS*' resource, i.e. only one cast
- it->index = newIndex;
- newIndex += 1;
-
resSize = getCASResourceSize(); // getCASResourceSize() returns size without header and size
it->size = resSize;
it->offset = currentSize;
@@ -1531,8 +1569,6 @@ void RIFXArchive::rebuildTypes(Movie *movie) {
case MKTAG('V', 'W', 'C', 'F'):
{
- it->index = newIndex;
- newIndex += 1;
// Cast config, as many resources as Casts
// No need to update the key mapping
resSize = cast->getConfigSize();
@@ -1541,15 +1577,13 @@ void RIFXArchive::rebuildTypes(Movie *movie) {
currentSize += resSize + 8; // getConfigSize() doesn't include header and size
it->size = resSize;
- break;
}
+ break;
+
case MKTAG('S', 'T', 'X', 'T'):
{
uint32 parentIndex = _keyData[MKTAG('S', 'T', 'X', 'T')].begin()->_key;
Resource parent = castResMap[parentIndex];
-
- it->index = newIndex;
- newIndex += 1;
TextCastMember *target = (TextCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
resSize = target->getSTXTResourceSize();
@@ -1558,17 +1592,14 @@ void RIFXArchive::rebuildTypes(Movie *movie) {
it->size = resSize;
currentSize += resSize + 8;
- break;
}
+ break;
case MKTAG('C', 'L', 'U', 'T'):
{
uint32 parentIndex = _keyData[MKTAG('C', 'L', 'U', 'T')].begin()->_key;
Resource parent = castResMap[parentIndex];
- it->index = newIndex;
- newIndex += 1;
-
PaletteCastMember *target = (PaletteCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
resSize = target->getPaletteDataSize();
@@ -1576,8 +1607,8 @@ void RIFXArchive::rebuildTypes(Movie *movie) {
it->size = resSize;
currentSize += resSize + 8;
- break;
}
+ break;
case MKTAG('B', 'I', 'T', 'D'):
{
@@ -1588,9 +1619,6 @@ void RIFXArchive::rebuildTypes(Movie *movie) {
// This begs the question whether 'BITD' resources can be children to resources other than 'CASt'
Resource parent = castResMap[parentIndex];
- it->index = newIndex;
- newIndex += 1;
-
BitmapCastMember *target = (BitmapCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
resSize = target->getBITDResourceSize();
@@ -1598,17 +1626,14 @@ void RIFXArchive::rebuildTypes(Movie *movie) {
it->size = resSize;
currentSize += resSize + 8;
- break;
}
+ break;
case MKTAG('S', 'C', 'V', 'W'):
{
uint32 parentIndex = _keyData[MKTAG('S', 'C', 'V', 'W')].begin()->_key;
Resource parent = castResMap[parentIndex];
- it->index = newIndex;
- newIndex += 1;
-
FilmLoopCastMember *target = (FilmLoopCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
resSize = target->getSCVWResourceSize();
@@ -1616,9 +1641,9 @@ void RIFXArchive::rebuildTypes(Movie *movie) {
it->size = resSize;
currentSize += resSize + 8;
- break;
}
break;
+ break;
case MKTAG('f', 'r', 'e', 'e'):
case MKTAG('j', 'u', 'n', 'k'):
@@ -1628,15 +1653,11 @@ void RIFXArchive::rebuildTypes(Movie *movie) {
// We could just ignore these and not write them at all
it->offset = currentSize;
currentSize += 8;
- it->index = newIndex;
- newIndex += 1;
break;
default:
- it->index = newIndex;
it->offset = currentSize;
currentSize += it->size + 8; // This size doesn't include the header and size entry
- newIndex += 1;
break;
}
@@ -1650,8 +1671,6 @@ void RIFXArchive::rebuildTypes(Movie *movie) {
it->size = getArchiveSize() + 8;
}
}
-
- // Need to rebuild the key map as well
}
uint32 RIFXArchive::getImapSize() {
diff --git a/engines/director/archive.h b/engines/director/archive.h
index e4730d6ecba..64c2a4abb59 100644
--- a/engines/director/archive.h
+++ b/engines/director/archive.h
@@ -169,7 +169,7 @@ private:
uint32 getMmapSize();
uint32 getImapSize();
uint32 getCASResourceSize();
- void rebuildTypes(Movie *movie);
+ void rebuildResources(Movie *movie);
bool readMemoryMap(Common::SeekableReadStreamEndian &stream, uint32 moreOffset, Common::SeekableMemoryWriteStream *dumpStream, uint32 movieStartOffset);
bool readAfterburnerMap(Common::SeekableReadStreamEndian &stream, uint32 moreOffset);
@@ -208,11 +208,17 @@ private:
protected:
uint32 _rifxType;
+
+ // Each resource is independent
Common::Array<Resource *> _resources;
Common::HashMap<uint32, byte *> _ilsData;
uint32 _ilsBodyOffset;
typedef Common::Array<uint32> KeyArray;
typedef Common::HashMap<uint32, KeyArray> KeyMap;
+
+ // In the RIFX container type, some resources are related of other resources in a child-parent relation
+ // If you want to check the relation between a 'BITD' resource and some other resource (e.g. 'CASt')
+ // Check if index of 'BITD' resource is present in _keyData['BITD'][index of 'CASt' resource]
Common::HashMap<uint32, KeyMap> _keyData;
};
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index 02456977e9c..1293e5db477 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -518,18 +518,6 @@ bool Cast::loadConfig() {
_vm->setVersion(humanVer);
}
- if (debugChannelSet(7, kDebugSaving)) {
- // Adding +8 because the stream doesn't include the header and the entry for the size itself
- uint32 configSize = stream->size() + 8;
- byte *dumpData = (byte *)calloc(configSize, sizeof(byte));
-
- Common::SeekableMemoryWriteStream *writeStream = new Common::SeekableMemoryWriteStream(dumpData, configSize);
-
- saveConfig(writeStream, 0);
- dumpFile("ConfigData", 0, MKTAG('V', 'W', 'C', 'F'), dumpData, configSize);
- delete writeStream;
- }
-
delete stream;
return true;
}
@@ -541,8 +529,10 @@ void Cast::saveConfig(Common::MemoryWriteStream *writeStream, uint32 offset) {
writeStream->seek(offset); // This will allow us to write cast config at any offset
+ uint32 configSize = getConfigSize();
+
writeStream->writeUint32LE(MKTAG('V', 'W', 'C', 'F'));
- writeStream->writeUint32LE(getConfigSize());
+ writeStream->writeUint32LE(configSize);
// These offsets are only for Director Version 4 to Director version 6
// offsets
@@ -606,6 +596,21 @@ void Cast::saveConfig(Common::MemoryWriteStream *writeStream, uint32 offset) {
writeStream->writeSint16LE(_defaultPalette.member); // 78
}
+ if (debugChannelSet(7, kDebugSaving)) {
+ // Adding +8 because the stream doesn't include the header and the entry for the size itself
+ byte *dumpData = (byte *)calloc(configSize + 8, sizeof(byte));
+
+ Common::SeekableMemoryWriteStream *dumpStream = new Common::SeekableMemoryWriteStream(dumpData, configSize + 8);
+
+ uint32 currentPos = writeStream->pos();
+ writeStream->seek(offset);
+ dumpStream->write(writeStream, configSize + 8);
+ writeStream->seek(currentPos);
+
+ dumpFile("ConfigData", 0, MKTAG('V', 'W', 'C', 'F'), dumpData, configSize + 8);
+ delete writeStream;
+ }
+
}
uint32 Cast::getConfigSize() {
@@ -807,62 +812,59 @@ void Cast::loadCast() {
}
}
-void Cast::saveCast() {
+void Cast::saveCast(Common::MemoryWriteStream *writeStream, Resource *res) {
// This offset is at which we will start writing our 'CASt' resources
// In the original file, all the 'CASt' resources don't necessarily appear side by side
- // writeStream->seek(offset);
-
- Common::Array<uint16> cast = _castArchive->getResourceIDList(MKTAG('C', 'A', 'S', 't'));
+ uint32 offset = res->offset;
+ writeStream->seek(offset);
// Okay, this is going to cause confusion
- // In the director movie archive, each CASt resource is given an ID, Not sure how this ID is assigned
- // but it is present in the keytable and is loaded while reading the keytable in RIFXArchive::readKeyTable();
- // The castId on the other hand present in the Resource struct is assigned by ScummVM Director in RIFXArchive::readCast()
+ // In the director movie archive, each CASt resource is given an index, which is the number at which it appears in the mmap
+ // The _castId_ on the other hand present in the Resource struct is assigned by in RIFXArchive::readCast()
// It is basically the index at which it occurs in the CAS* resource
- // So, RIFXArchive::getResourceDetail will return the list of IDs present in the keytable
- // Whereas, in the _loadedCast, the key of these Cast members is given by castId
- for (auto &it : cast) {
+ // So, RIFXArchive::getResourceDetail will return the list of indexes of the 'CASt' resources in the mmap (read in the 'CAS*' resource)
+ // Whereas, in the _loadedCast, the key of these Cast members is given by _castId_
// I need to call this just because I need the id
- Resource res = _castArchive->getResourceDetail(MKTAG('C', 'A', 'S', 't'), it);
- uint16 id = res.castId + _castArrayStart; // Never encountered _castArrayStart != 0 till now
- uint32 castSize = 0;
-
- if (_loadedCast->contains(id)) {
- CastMember *target = _loadedCast->getVal(id);
- // To make it consistent with how the data is stored originally, getResourceSize returns
- // the size excluding 'CASt' header and the entry for size itself. Adding 8 to compensate for that
- castSize = target->getCastResourceSize() + 8;
- } else {
- // The size stored in the memory map (_resources array), as well as the resource itself is the size
- // excluding the 'CASt' header and the entry of size itself. Adding 8 to compensate for that
- castSize = _castArchive->getResourceSize(MKTAG('C', 'A', 'S', 't'), it) + 8;
- }
+ uint32 castSize = 0;
+ uint16 id = res->castId + _castArrayStart;
- debugC(5, kDebugSaving, "Cast::saveCast()::Saving 'CASt' resource, id: %d, size: %d", id, castSize);
- byte *dumpData = (byte *)calloc(castSize, sizeof(byte));
-
- Common::SeekableMemoryWriteStream *writeStream = new Common::SeekableMemoryWriteStream(dumpData, castSize);
- CastType type = kCastTypeAny;
- if (!_loadedCast->contains(id)) {
- writeStream->writeUint32LE(MKTAG('C', 'A', 'S', 't'));
- Common::SeekableReadStreamEndian *stream = getResource(MKTAG('C', 'A', 'S', 't'), it);
- uint32 size = stream->size(); // This is the size of the Resource without header and size entry itself
- writeStream->writeUint32LE(size);
- writeStream->writeStream(stream);
+ CastType type = kCastTypeAny;
- delete stream;
- } else {
- CastMember *target = _loadedCast->getVal(id);
- type = target->_type;
- target->writeCAStResource(writeStream, 0);
- break;
- }
+ if (_loadedCast->contains(id)) {
+ CastMember *target = _loadedCast->getVal(id);
+ // To make it consistent with how the data is stored originally, getResourceSize returns
+ // the size excluding 'CASt' header and the entry for size itself. Adding 8 to compensate for that
+ castSize = target->getCastResourceSize() + 8;
+ type = target->_type;
+ target->writeCAStResource(writeStream, 0);
+ } else {
+ // The size stored in the memory map (_resources array), as well as the resource itself is the size
+ // excluding the 'CASt' header and the entry of size itself. Adding 8 to compensate for that
+ castSize = _castArchive->getResourceSize(MKTAG('C', 'A', 'S', 't'), res->index) + 8;
+ writeStream->writeUint32LE(MKTAG('C', 'A', 'S', 't'));
+ Common::SeekableReadStreamEndian *stream = getResource(MKTAG('C', 'A', 'S', 't'), res->index);
+ uint32 size = stream->size(); // This is the size of the Resource without header and size entry itself
+ writeStream->writeUint32LE(size);
+ writeStream->writeStream(stream);
- dumpFile(castType2str(type), res.index, MKTAG('C', 'A', 'S', 't'), dumpData, castSize);
- delete writeStream;
+ delete stream;
}
+ debugC(5, kDebugSaving, "Cast::saveCast()::Saving 'CASt' resource, id: %d, size: %d, type: %s", id, castSize, castType2str(type));
+
+ if (debugChannelSet(7, kDebugSaving)) {
+ byte *dumpData = (byte *)calloc(castSize, sizeof(byte));
+ Common::SeekableMemoryWriteStream *dumpStream = new Common::SeekableMemoryWriteStream(dumpData, castSize);
+
+ uint32 currentPos = writeStream->pos();
+ writeStream->seek(offset);
+ dumpStream->write(writeStream, castSize);
+ writeStream->seek(currentPos);
+
+ dumpFile(castType2str(type), res->index, MKTAG('C', 'A', 'S', 't'), dumpData, castSize);
+ delete dumpStream;
+ }
}
void Cast::writeCastInfo(Common::MemoryWriteStream *writeStream, uint32 castId) {
diff --git a/engines/director/cast.h b/engines/director/cast.h
index 63e21c49197..5d842712205 100644
--- a/engines/director/cast.h
+++ b/engines/director/cast.h
@@ -107,7 +107,7 @@ public:
void loadSord(Common::SeekableReadStreamEndian &stream);
void saveConfig(Common::MemoryWriteStream *writeStream, uint32 offset);
- void saveCast();
+ void saveCast(Common::MemoryWriteStream *writeStream, Resource *res);
void saveCastData();
void writeCastInfo(Common::MemoryWriteStream *writeStream, uint32 castId);
uint32 getCastInfoSize(uint32 castId);
diff --git a/engines/director/castmember/filmloop.cpp b/engines/director/castmember/filmloop.cpp
index c461a724f4e..d847ac8c836 100644
--- a/engines/director/castmember/filmloop.cpp
+++ b/engines/director/castmember/filmloop.cpp
@@ -770,8 +770,9 @@ void FilmLoopCastMember::writeSCVWResource(Common::MemoryWriteStream *writeStrea
// Go to the desired offset put in the memory map
writeStream->seek(offset);
+ uint32 filmloopSize = getSCVWResourceSize();
writeStream->writeUint32LE(MKTAG('S', 'C', 'V', 'W'));
- writeStream->writeUint32LE(getSCVWResourceSize()); // Size of the resource
+ writeStream->writeUint32LE(filmloopSize); // Size of the resource
uint32 sizePos = writeStream->pos();
writeStream->writeUint32BE(0); // Putting zeroes instead of size currently
@@ -779,7 +780,7 @@ void FilmLoopCastMember::writeSCVWResource(Common::MemoryWriteStream *writeStrea
uint32 frameOffset = 16; // Should be greater than 16
writeStream->writeUint32BE(frameOffset); // framesOffset
writeStream->seek(6, SEEK_CUR); // Ignored data
- writeStream->writeUint16BE(kSprChannelSizeD4);
+ writeStream->writeUint16BE(channelSize);
writeStream->seek(frameOffset - 16, SEEK_CUR); // Ignored data
// The structure of the filmloop 'SCVW' data is as follows
@@ -788,27 +789,26 @@ void FilmLoopCastMember::writeSCVWResource(Common::MemoryWriteStream *writeStrea
// Some headers which we ignore except the Sprite Channel Size (which we also ignore during loading) ->
// until there are no more frames
- // size of the frame ->
-
- // until there are no more channels in the frame
- // width of message (One chunk of data) (This is the size of data for the sprite that needs to be read) ->
- // order of message (this order tells us the channel we're reading) ->
- // 1-20 bytes of Sprite data
+ // size of the frame ->
+ // until there are no more channels in the frame
+ // width of message (One chunk of data) (This is the size of data for the sprite that needs to be read) ->
+ // order of message (this order tells us the channel we're reading) ->
+ // 1-20 bytes of Sprite data
for (FilmLoopFrame frame : _frames) {
writeStream->writeUint16BE(0); // Frame Size
for (auto it : frame.sprites) {
int channel = it._key;
- // For now writing the order considering that each sprite will have 20 bytes of data
+ // TODO: For now writing the order considering that each sprite will have 20 bytes of data
// In the future, for optimization, we can actually calculate the data of each sprite
// And write the order accordingly
// But for this we'll need a way to find how many data values (out of 20) of a sprite are valid, i.e. determine message width
// this means while loading, the channelOffset will always be 0, order will always be multiple of 20
// And message width will always be 20
// Channel indexes start with 0
- writeStream->writeUint16BE(kSprChannelSizeD4); // message width
- writeStream->writeUint16BE(channel * kSprChannelSizeD4);
+ writeStream->writeUint16BE(channelSize); // message width
+ writeStream->writeUint16BE(channel * channelSize);
Sprite sprite = it._value;
@@ -816,6 +816,21 @@ void FilmLoopCastMember::writeSCVWResource(Common::MemoryWriteStream *writeStrea
}
}
+
+ if (debugChannelSet(7, kDebugSaving)) {
+ // Adding +8 because the stream doesn't include the header and the entry for the size itself
+ byte *dumpData = (byte *)calloc(filmloopSize + 8, sizeof(byte));
+
+ Common::SeekableMemoryWriteStream *dumpStream = new Common::SeekableMemoryWriteStream(dumpData, filmloopSize + 8);
+
+ uint32 currentPos = writeStream->pos();
+ writeStream->seek(offset);
+ dumpStream->write(writeStream, filmloopSize + 8);
+ writeStream->seek(currentPos);
+
+ dumpFile("ConfigData", 0, MKTAG('V', 'W', 'C', 'F'), dumpData, filmloopSize);
+ delete writeStream;
+ }
}
uint32 FilmLoopCastMember::getSCVWResourceSize() {
diff --git a/engines/director/castmember/palette.cpp b/engines/director/castmember/palette.cpp
index 54d22d757d8..83c155902cf 100644
--- a/engines/director/castmember/palette.cpp
+++ b/engines/director/castmember/palette.cpp
@@ -168,17 +168,11 @@ uint32 PaletteCastMember::getPaletteDataSize() {
}
void PaletteCastMember::writePaletteData(Common::MemoryWriteStream *writeStream, uint32 offset) {
- uint32 castSize = getPaletteDataSize() + 8;
+ uint32 castSize = getPaletteDataSize();
- /**/
- byte *dumpData = nullptr;
- dumpData = (byte *)calloc(castSize, sizeof(byte));
- writeStream = new Common::SeekableMemoryWriteStream(dumpData, castSize);
- /**/
-
- // writeStream->seek(offset);
+ writeStream->seek(offset);
writeStream->writeUint32LE(MKTAG('C', 'L', 'U', 'T'));
- writeStream->writeUint32LE(getPaletteDataSize());
+ writeStream->writeUint32LE(castSize);
const byte *pal = _palette->palette;
@@ -190,8 +184,19 @@ void PaletteCastMember::writePaletteData(Common::MemoryWriteStream *writeStream,
writeStream->writeUint16BE(pal[3 * i + 2] << 8);
}
- dumpFile("PaletteData", _castId, MKTAG('C', 'L', 'U', 'T'), dumpData, castSize);
- delete writeStream;
+ if (debugChannelSet(7, kDebugSaving)) {
+ byte *dumpData = nullptr;
+ dumpData = (byte *)calloc(castSize, sizeof(byte));
+ auto dumpStream = new Common::SeekableMemoryWriteStream(dumpData, castSize + 8);
+
+ uint32 currentPos = writeStream->pos();
+ writeStream->seek(offset);
+ dumpStream->write(writeStream, castSize);
+ writeStream->seek(currentPos);
+
+ dumpFile("PaletteData", _castId, MKTAG('C', 'L', 'U', 'T'), dumpData, castSize);
+ delete dumpStream;
+ }
}
} // End of namespace Director
diff --git a/engines/director/castmember/text.cpp b/engines/director/castmember/text.cpp
index 07fe27c4060..f512e3bc45a 100644
--- a/engines/director/castmember/text.cpp
+++ b/engines/director/castmember/text.cpp
@@ -1001,7 +1001,6 @@ uint32 TextCastMember::writeSTXTResource(Common::MemoryWriteStream *writeStream,
FontStyle style;
writeStream->writeString(_rtext);
- debug("what is the size of _rtext = %d", _rtext.size());
writeStream->writeUint16BE(formatting);
@@ -1009,7 +1008,6 @@ uint32 TextCastMember::writeSTXTResource(Common::MemoryWriteStream *writeStream,
while (it < _ftext.size() - 1) {
uint32 rIndex = 0;
if (_ftext[it] == '\001' && _ftext[it + 1] == '\016') {
- debug("Found Styling header at position: %d", it);
// Styling header found
it += 2;
diff --git a/engines/director/director.cpp b/engines/director/director.cpp
index dedf98f8d83..90077adddc9 100644
--- a/engines/director/director.cpp
+++ b/engines/director/director.cpp
@@ -386,7 +386,6 @@ Common::Error DirectorEngine::run() {
if (debugChannelSet(-1, kDebugSaving)) {
Common::Path writePath("./dumps/writtenMovie.dir");
_mainArchive->writeToFile(writePath, getCurrentMovie());
- getCurrentMovie()->getCast()->saveCast();
}
return Common::kNoError;
Commit: 2a9a582096ad6b8f7bda19014f34dd56511cf04b
https://github.com/scummvm/scummvm/commit/2a9a582096ad6b8f7bda19014f34dd56511cf04b
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Bunch of fixes for the `writeToFile()` function
- Instead of rebuilding the _resources, we have to make a new array
with the same resources and update the size and offsets there
- Writing 'CAS*' is not as easy as it sounds, we have to sort the
list of 'CASt' castIds and then write their indices in order
- Smaller fixes like missed tags, low endian to big endian, etc
- Removed previous debugging
Changed paths:
engines/director/archive.cpp
engines/director/archive.h
engines/director/cast.cpp
engines/director/castmember/filmloop.cpp
engines/director/castmember/palette.cpp
engines/director/castmember/text.cpp
engines/director/lingo/lingo-bytecode.cpp
diff --git a/engines/director/archive.cpp b/engines/director/archive.cpp
index 0e8293e6087..b8708a7d87d 100644
--- a/engines/director/archive.cpp
+++ b/engines/director/archive.cpp
@@ -802,26 +802,26 @@ bool RIFXArchive::readMemoryMap(Common::SeekableReadStreamEndian &stream, uint32
_imapLength = stream.readUint32(); // imap length
_mapversion = stream.readUint32(); // version, seen 0 or 1
uint32 mmapOffsetPos = stream.pos();
- _mmapOffset = stream.readUint32() + moreOffset;
+ uint32 mmapOffset = stream.readUint32() + moreOffset;
if (dumpStream) {
// If we're dumping the movie, patch this offset in the dump data.
dumpStream->seek(mmapOffsetPos - movieStartOffset);
if (stream.isBE())
- dumpStream->writeUint32BE(_mmapOffset - movieStartOffset);
+ dumpStream->writeUint32BE(mmapOffset - movieStartOffset);
else
- dumpStream->writeUint32LE(_mmapOffset - movieStartOffset);
+ dumpStream->writeUint32LE(mmapOffset - movieStartOffset);
}
_version = stream.readUint32(); // 0 for 4.0, 0x4c1 for 5.0, 0x4c7 for 6.0, 0x708 for 8.5, 0x742 for 10.0
- debugC(2, kDebugLoading, "RIFXArchive::readMemoryMap: _mapversion: %d version: %x offset: 0x%x (%d)", _mapversion, _version, _mmapOffset, _mmapOffset);
+ debugC(2, kDebugLoading, "RIFXArchive::readMemoryMap: _mapversion: %d version: %x offset: 0x%x (%d)", _mapversion, _version, mmapOffset, mmapOffset);
- stream.seek(_mmapOffset);
+ stream.seek(mmapOffset);
if (stream.readUint32() != MKTAG('m', 'm', 'a', 'p')) {
warning("RIFXArchive::readMemoryMap: mmap expected but not found");
return false;
}
- _mmapLength = stream.readUint32(); // mmap length
+ /* uint32 mmapLength = */ stream.readUint32(); // mmap length
_mmapHeaderSize = stream.readUint16(); // header size
_mmapEntrySize = stream.readUint16(); // size of map entry
_totalCount = stream.readUint32(); // resCount + empty entries
@@ -1074,6 +1074,13 @@ void RIFXArchive::readKeyTable(Common::SeekableReadStreamEndian &keyStream) {
}
_keyData[childTag][parentIndex].push_back(childIndex);
+ if (childTag == MKTAG('C', 'A', 'S', '*')) {
+ // A 'CAS*' resource contains indexes of 'CASt' resources that are linked to the
+ // Resource at parentIndex, hence, mark the 'CAS*' libResourceId to parentIndex
+ // This means all the 'CASt' resources are linked to the parent with index equal to 'CAS*' libResourceId
+ _types[childTag][childIndex].libResourceId = parentIndex;
+ }
+
// Link cast members to their resources.
if (castResMap.contains(parentIndex)) {
castResMap[parentIndex].children.push_back(_types[childTag][childIndex]);
@@ -1177,9 +1184,8 @@ Common::String RIFXArchive::formatArchiveInfo() {
}
bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
- rebuildResources(movie);
- // The offsets used in this function should be not the same as original
- // That defeats the whole purpose of writing movies
+ // Update the resources, their sizes and offsets
+ Common::Array<Resource *> builtResources = rebuildResources(movie);
// ignoring the startOffset
// For RIFX stream, moreoffset = 0, we won't be writing macbinary
@@ -1187,31 +1193,21 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
// Don't need to allocate this much size in case 'junk' and 'free' resources are ignored
// Or might need to allocate even more size if extra chunks are written
- _size = getArchiveSize();
+ _size = getArchiveSize(builtResources);
dumpData = (byte *)calloc(_size, sizeof(byte));
Common::SeekableMemoryWriteStream *writeStream = new Common::SeekableMemoryWriteStream(dumpData, _size);
- writeStream->writeUint32LE(_metaTag); // The _metaTag is "RIFX" or "XFIR" for this case
-
- // This includes the size of 'RIFX', 'mmap' and 'imap' resources
- // This method of calculating new size is inefficient and prone to error
- /* uint32 newSize = 0;
- * for (auto &it: _resources) {
- * newSize += it->size;
- * }
- */
+ writeStream->writeUint32LE(_metaTag); // The _metaTag is "RIFX" or "XFIR"
- // We'll need to update the size of the RIFX resource whenever there is a modification
- // The -8 is consistent with RIFXArchive::openStream()
- writeStream->writeUint32LE(getResourceSize(_metaTag, 0) - 8); // The size of the RIFX archive
+ writeStream->writeUint32LE(getResourceSize(_metaTag, 0) - 8); // The size of the RIFX archive, except header and size
writeStream->writeUint32LE(_rifxType); // e.g. "MV93", "MV95"
switch (_rifxType) {
case MKTAG('M', 'V', '9', '3'):
case MKTAG('M', 'C', '9', '5'):
case MKTAG('A', 'P', 'P', 'L'):
- writeMemoryMap(writeStream);
+ writeMemoryMap(writeStream, builtResources);
break;
case MKTAG('F', 'G', 'D', 'M'):
@@ -1225,7 +1221,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
Cast *cast = movie->getCast();
ResourceMap castResMap = _types[MKTAG('C', 'A', 'S', 't')];
- for (auto &it : _resources) {
+ for (auto &it : builtResources) {
debugC(5, kDebugSaving, "RIFXArchive::writeToFile: writing resource: index: %d, size: %d, tag = %s, offset = %d", it->index, it->size, tag2str(it->tag), it->offset);
switch (it->tag) {
@@ -1244,7 +1240,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
break;
case MKTAG('C', 'A', 'S', '*'):
- writeCast(writeStream, it->offset, it->castId);
+ writeCast(writeStream, it->offset, it->libResourceId);
break;
case MKTAG('C', 'A', 'S', 't'):
@@ -1254,7 +1250,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
case MKTAG('V', 'W', 'C', 'F'):
cast->saveConfig(writeStream, it->offset);
break;
-
+
case MKTAG('B', 'I', 'T', 'D'):
{
// Get one of the parents of the 'BITD' resource stored in the _keyData
@@ -1263,7 +1259,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
Resource parent = castResMap[parentIndex];
BitmapCastMember *target = (BitmapCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
- target->writeBITDResource(writeStream, it->offset);
+ target->writeBITDResource(writeStream, it->offset);
}
break;
@@ -1273,7 +1269,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
Resource parent = castResMap[parentIndex];
TextCastMember *target = (TextCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
- target->writeSTXTResource(writeStream, it->offset);
+ target->writeSTXTResource(writeStream, it->offset);
}
break;
@@ -1283,25 +1279,35 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
Resource parent = castResMap[parentIndex];
PaletteCastMember *target = (PaletteCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
- target->writePaletteData(writeStream, it->offset);
- }
+ target->writePaletteData(writeStream, it->offset);
+ }
break;
case MKTAG('S', 'C', 'V', 'W'):
{
uint32 parentIndex = _keyData[MKTAG('S', 'C', 'V', 'W')].begin()->_key;
+ // If the parentIndex is 1024 that means this 'SCVW' is a score or a movie
+ if (parentIndex == 1024) {
+ writeStream->seek(it->offset);
+ writeStream->writeUint32LE(it->tag);
+ writeStream->writeUint32LE(it->size);
+ writeStream->writeStream(getResource(it->tag, it->index));
+ break;
+ }
Resource parent = castResMap[parentIndex];
FilmLoopCastMember *target = (FilmLoopCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
- target->writeSCVWResource(writeStream, it->offset);
- }
+ target->writeSCVWResource(writeStream, it->offset);
+ }
break;
default:
writeStream->seek(it->offset);
writeStream->writeUint32LE(it->tag);
writeStream->writeUint32LE(it->size);
- writeStream->writeStream(getResource(it->tag, it->index));
+ Common::SeekableReadStreamEndian *r = getResource(it->tag, it->index);
+ r->hexdump(r->size());
+ writeStream->writeStream(r);
break;
}
}
@@ -1323,32 +1329,37 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
return true;
}
-bool RIFXArchive::writeMemoryMap(Common::SeekableMemoryWriteStream *writeStream) {
+bool RIFXArchive::writeMemoryMap(Common::SeekableMemoryWriteStream *writeStream, Common::Array<Resource *> resources) {
+ Resource mmap;
+
+ for (auto it : resources) {
+ if (it->tag == MKTAG('m', 'm', 'a', 'p')) {
+ mmap = *it;
+ }
+ }
+
writeStream->writeUint32LE(MKTAG('i', 'm', 'a', 'p')); // The "imap" resource
writeStream->writeUint32LE(_imapLength); // length of "imap" resource
writeStream->writeUint32LE(_mapversion); // "imap" version
- writeStream->writeUint32LE(_mmapOffset); // offset of the "mmap" resource
+ writeStream->writeUint32LE(mmap.offset); // offset of the "mmap" resource
writeStream->writeUint32LE(_version);
- writeStream->seek(_mmapOffset);
+ writeStream->seek(mmap.offset);
writeStream->writeUint32LE(MKTAG('m', 'm', 'a', 'p'));
+ writeStream->writeUint32LE(mmap.size);
- uint32 newResCount = _resources.size();
-
- // Need to recalculate the following things
- // Similarly to the RIFX resource, we'll need to update the size of the mmap resource whenever there is some change
- writeStream->writeUint32LE(getResourceSize(MKTAG('m', 'm', 'a', 'p'), 2));
writeStream->writeUint16LE(_mmapHeaderSize);
writeStream->writeUint16LE(_mmapEntrySize);
-
+
+ uint32 newResCount = resources.size();
writeStream->writeUint32LE(newResCount + _totalCount - _resCount); // _totalCount - _resCount is the number of empty entries
writeStream->writeUint32LE(newResCount);
writeStream->seek(8, SEEK_CUR); // In the original file, these 8 bytes are all 0xFF, so this will produce a diff
- // ID of the first 'free' resource
+ // ID of the first 'free' resource, we don't make use of it
writeStream->writeUint32LE(0);
- for (auto &it : _resources) {
+ for (auto &it : resources) {
debugC(3, kDebugSaving, "Writing RIFX Resource: tag: %s, size: %d, offset: %08x, flags: %x, unk1: %x, nextFreeResourceID: %d",
tag2str(it->tag), it->size, it->offset, it->flags, it->unk1, it->nextFreeResourceID);
@@ -1398,12 +1409,15 @@ bool RIFXArchive::writeKeyTable(Common::SeekableMemoryWriteStream *writeStream,
writeStream->seek(offset);
writeStream->writeUint32LE(MKTAG('K', 'E', 'Y', '*'));
+ writeStream->writeUint32LE(getResourceSize(MKTAG('K', 'E', 'Y', '*'), getResourceIDList(MKTAG('K', 'E', 'Y', '*'))[0]));
writeStream->writeUint16LE(_keyTableEntrySize);
writeStream->writeUint16LE(_keyTableEntrySize2);
writeStream->writeUint32LE(_keyTableEntryCount);
writeStream->writeUint32LE(_keyTableUsedCount);
+ debugC(3, kDebugSaving, "RIFXArchive::writeKeyTable: writing key table:");
+
for (auto &childTag : _keyData) {
KeyMap keyMap = childTag._value;
@@ -1411,7 +1425,7 @@ bool RIFXArchive::writeKeyTable(Common::SeekableMemoryWriteStream *writeStream,
KeyArray keyArray = parentIndex._value;
for (auto childIndex : keyArray) {
- debugC(3, kDebugSaving, "RIFXArchive::writeKeyTable: _keyData contains tag: %s, parentIndex: %d, childIndex: %d", tag2str(childTag._key), parentIndex._key, childIndex);
+ debugC(3, kDebugSaving, "_keyData contains tag: %s, parentIndex: %d, childIndex: %d", tag2str(childTag._key), parentIndex._key, childIndex);
writeStream->writeUint32LE(childIndex);
writeStream->writeUint32LE(parentIndex._key);
writeStream->writeUint32LE(childTag._key);
@@ -1426,17 +1440,29 @@ bool RIFXArchive::writeCast(Common::SeekableWriteStream *writeStream, uint32 off
writeStream->seek(offset);
uint castTag = MKTAG('C', 'A', 'S', 't');
+ writeStream->writeUint32LE(MKTAG('C', 'A', 'S', '*'));
+ writeStream->writeUint32LE(getCASResourceSize(castLib));
+ Common::HashMap<uint16, uint16> castIndexes;
+
+ // We can't just write all the 'CASt' indices randomly, we have to sort them by castId
+ // Since the order they appear matters, they are given castIds accordingly
+ uint32 maxCastId = 0;
for (auto &it : _types[castTag]) {
if (it._value.libResourceId == castLib) {
- writeStream->writeUint32LE(it._key);
+ castIndexes[it._value.castId] = it._value.index;
+ maxCastId = MAX(maxCastId, it._value.castId);
}
- }
+ }
+ for (uint32 i = 0; i <= maxCastId; i++) {
+ uint32 castIndex = castIndexes.getValOrDefault(i, 0);
+ writeStream->writeUint32BE(castIndex);
+ }
return true;
}
-void RIFXArchive::rebuildResources(Movie *movie) {
+Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
// Currently I'm modifying the original _resources, _types and _keydata structures, this is what happens in the original Director
// However, if we don't want that, we could make copies of them and then modify them
@@ -1446,12 +1472,12 @@ void RIFXArchive::rebuildResources(Movie *movie) {
// RIFX // STXT
// KEY* // SCVW (filmloop)
// CAS* // VWCF
- // CASt //
+ // CASt //
// Resources yet to be handled
// Any external file
// Script 'Lscr'
- // Script Names 'Lnam'
+ // Script Names 'Lnam'
// Lingo Context 'Lctx'
// Score 'SCVW'
// Rich Text 'RTE0', 'RTE1', 'RTE2'
@@ -1459,13 +1485,13 @@ void RIFXArchive::rebuildResources(Movie *movie) {
// 'SCVW' External Movies
// Transitions and OLE are Director 5+, not yet loaded in ScummVM Director
-
+
// Resources that are loaded but probably need not be handled
// 'VWFM' Font Mapping
// 'FXmp' Corss Platform Font Mapping
// 'VWTL' Pattern Tiles
// 'STR ' External Sound files
- // 'MooV' External Digital Video
+ // 'MooV' External Digital Video
// 'XCOD' XObjects
// 'Cinf' Cast Lib Info
// 'VWCI' Cast Info (For what cast though?)
@@ -1489,9 +1515,11 @@ void RIFXArchive::rebuildResources(Movie *movie) {
res->libResourceId = cast->_castLibID;
res->children = it._value->_children;
res->index = _resources.size() + 1;
-
+
for (auto child : it._value->_children) {
_keyData[child.tag][res->index].push_back(child.index); // Indices are a huge problem, need to figure out them first
+ _keyTableUsedCount += 1;
+ _keyTableEntryCount += 1;
}
_resources.push_back(res);
@@ -1508,46 +1536,51 @@ void RIFXArchive::rebuildResources(Movie *movie) {
// Ignoring that for now
// Next step is to recalculate the sizes and the offsets of all the resources
- // Handle writing KEY* and CAS* last, because they need to know the indices before hand
- // Also their indexes will be 0 through 4 (fixed), RIFX and imap need to be the very first two indices 0 and 1
- // The rest can be rearranged to any index, but for simplicity, we'll assign them a fixed index
-
- // This might become easier if ResourceMap was an Array rather than a HasMap, can consider refactoring
- // Anyways its key is not used anywhere
- // Since the first 5 resources are determined (for simplicity)
+ // Since the first 3 resources are determined (RIFX, imap and mmap)
+ // (the mmap doesn't need to be the third resource, but for simplicity, it's better there)
// We'll start writing after that RIFX header, mmap and imap resources
- uint32 currentSize = 12 + getImapSize() + getMmapSize();
+ // The first 12 bytes are metaTag ('RIFX'), size of file, and RIFX type ('MV93', 'MV95', etc.)
+ // The +8 bytes are to account for the header and size
+ uint32 currentSize = 12 + (getImapSize() + 8) + (getMmapSize() + 8);
// This switch statement can be simplified by keeping a pointer to the write function of the resrouce in the Resource
// But that will require accessing _resource every time so not doing that right now
+
+ // need to make a new resources array, because we need the old offsets as well as new ones
+ Common::Array<Resource *> builtResources;
+
+ for (auto it: _resources) {
+ builtResources.push_back(new Resource(it));
+ }
+
uint32 resSize = 0;
- for (auto &it : _resources) {
- switch (it->tag) {
+ for (auto &it : builtResources) {
+ switch (it->tag) {
case MKTAG('R', 'I', 'F', 'X'):
- case MKTAG('X', 'F', 'I', 'R'):
+ case MKTAG('X', 'F', 'I', 'R'):
// only one resource only
// Size will be determined after all other sizes have been calculated
it->offset = 0;
break;
-
- case MKTAG('i', 'm', 'a', 'p'):
+
+ case MKTAG('i', 'm', 'a', 'p'):
// one resource only
it->size = getImapSize();
- it->offset = 12;
+ it->offset = 12; // First 12 bytes are reserved for metaTag ('RIFX'), size of file, and RIFX type ('MV93', 'MV95', etc.)
break;
- case MKTAG('m', 'm', 'a', 'p'):
+ case MKTAG('m', 'm', 'a', 'p'):
// one resource only
it->size = getMmapSize();
- it->offset = 12 + getImapSize();
+ it->offset = 12 + (getImapSize() + 8); // The +8 is to account for header and size
break;
case MKTAG('C', 'A', 'S', 't'):
{
// The castIds of cast members start from _castArrayStart
CastMember *target = cast->getCastMember(it->castId - cast->_castArrayStart);
-
+
if (target) {
resSize = target->getCastResourceSize();
it->size = resSize; // getCastResourceSize returns size without header and size
@@ -1561,10 +1594,17 @@ void RIFXArchive::rebuildResources(Movie *movie) {
case MKTAG('C', 'A', 'S', '*'):
// Currently handling only movies with one 'CAS*' resource, i.e. only one cast
- resSize = getCASResourceSize(); // getCASResourceSize() returns size without header and size
+ resSize = getCASResourceSize(it->libResourceId); // getCASResourceSize() returns size without header and size
+ it->size = resSize;
+ it->offset = currentSize;
+ currentSize += resSize + 8;
+ break;
+
+ case MKTAG('K', 'E', 'Y', '*'):
+ resSize = getKeyTableResourceSize();
it->size = resSize;
it->offset = currentSize;
- currentSize += resSize + 8;
+ currentSize += resSize + 8;
break;
case MKTAG('V', 'W', 'C', 'F'):
@@ -1572,17 +1612,29 @@ void RIFXArchive::rebuildResources(Movie *movie) {
// Cast config, as many resources as Casts
// No need to update the key mapping
resSize = cast->getConfigSize();
-
+
it->offset = currentSize;
currentSize += resSize + 8; // getConfigSize() doesn't include header and size
- it->size = resSize;
+ it->size = resSize;
}
break;
case MKTAG('S', 'T', 'X', 'T'):
{
- uint32 parentIndex = _keyData[MKTAG('S', 'T', 'X', 'T')].begin()->_key;
+ uint32 parentIndex = 0;
+ for (auto &jt : _keyData[MKTAG('S', 'T', 'X', 'T')]) {
+ for (auto &kt : jt._value) {
+ if (kt == it->index) {
+ parentIndex = jt._key;
+ break;
+ }
+ }
+ if (parentIndex) {
+ break;
+ }
+ }
+
Resource parent = castResMap[parentIndex];
TextCastMember *target = (TextCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
@@ -1592,12 +1644,27 @@ void RIFXArchive::rebuildResources(Movie *movie) {
it->size = resSize;
currentSize += resSize + 8;
- }
+ }
break;
-
+
case MKTAG('C', 'L', 'U', 'T'):
{
- uint32 parentIndex = _keyData[MKTAG('C', 'L', 'U', 'T')].begin()->_key;
+ // We have to find the parent
+ // Look into the keyData, for all parents of 'CLUT' resource
+ // If the parent contains this 'CLUT' resource's index, that's our parent
+ uint32 parentIndex = 0;
+ for (auto &jt : _keyData[MKTAG('C', 'L', 'U', 'T')]) {
+ for (auto &kt : jt._value) {
+ if (kt == it->index) {
+ parentIndex = jt._key;
+ break;
+ }
+ }
+ if (parentIndex) {
+ break;
+ }
+ }
+
Resource parent = castResMap[parentIndex];
PaletteCastMember *target = (PaletteCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
@@ -1607,16 +1674,25 @@ void RIFXArchive::rebuildResources(Movie *movie) {
it->size = resSize;
currentSize += resSize + 8;
- }
+ }
break;
case MKTAG('B', 'I', 'T', 'D'):
{
- // One child can have multiple parents, so the question is whether or not to choose parent arbitrarily
- // and whether a single parent can have multiple 'BITD' children, BitmapCastMember:load() suggests that it doesn't
- uint32 parentIndex = _keyData[MKTAG('B', 'I', 'T', 'D')].begin()->_key;
-
- // This begs the question whether 'BITD' resources can be children to resources other than 'CASt'
+ uint32 parentIndex = 0;
+ for (auto &jt : _keyData[MKTAG('B', 'I', 'T', 'D')]) {
+ for (auto &kt : jt._value) {
+ if (kt == it->index) {
+ parentIndex = jt._key;
+ break;
+ }
+ }
+ if (parentIndex) {
+ break;
+ }
+ }
+
+ debug("What is the parentIndex = %d, cast->_castArraySize = %d", parentIndex, cast->_castArrayStart);
Resource parent = castResMap[parentIndex];
BitmapCastMember *target = (BitmapCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
@@ -1626,12 +1702,31 @@ void RIFXArchive::rebuildResources(Movie *movie) {
it->size = resSize;
currentSize += resSize + 8;
- }
+ }
break;
case MKTAG('S', 'C', 'V', 'W'):
{
- uint32 parentIndex = _keyData[MKTAG('S', 'C', 'V', 'W')].begin()->_key;
+ uint32 parentIndex = 0;
+ for (auto &jt : _keyData[MKTAG('B', 'I', 'T', 'D')]) {
+ for (auto &kt : jt._value) {
+ if (kt == it->index) {
+ parentIndex = jt._key;
+ break;
+ }
+ }
+ if (parentIndex) {
+ break;
+ }
+ }
+
+ // If parent index is 1024, that means this 'SCVW' resource is a score or a movie
+ if (parentIndex == 1024) {
+ it->offset = currentSize;
+ currentSize += it->size + 8; // This size doesn't include the header and size entry
+ break;
+ }
+
Resource parent = castResMap[parentIndex];
FilmLoopCastMember *target = (FilmLoopCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
@@ -1641,15 +1736,14 @@ void RIFXArchive::rebuildResources(Movie *movie) {
it->size = resSize;
currentSize += resSize + 8;
- }
- break;
+ }
break;
-
+
case MKTAG('f', 'r', 'e', 'e'):
case MKTAG('j', 'u', 'n', 'k'):
// These resource do not hold any data
it->size = 0;
-
+
// We could just ignore these and not write them at all
it->offset = currentSize;
currentSize += 8;
@@ -1666,29 +1760,31 @@ void RIFXArchive::rebuildResources(Movie *movie) {
}
// Now that all sizes have been updated, we can safely calculate the overall archive size
- for (auto &it : _resources) {
+ for (auto &it : builtResources) {
if (it->tag == MKTAG('R', 'I', 'F', 'X') || it->tag == MKTAG('X', 'F', 'I', 'R')) {
- it->size = getArchiveSize() + 8;
+ it->size = getArchiveSize(builtResources) + 8;
}
}
+
+ return builtResources;
}
uint32 RIFXArchive::getImapSize() {
// The length of imap doesn't change
- // This is the length without
- return 12;
+ // This is the length without header and size
+ return _imapLength;
}
uint32 RIFXArchive::getMmapSize() {
- // The headers: 24 bytes and and 8 bytes per resources
- return 24 + 8 * _resources.size();
+ // The headers: 24 bytes and and 20 bytes per resources
+ return 24 + 20 * _resources.size();
}
-uint32 RIFXArchive::getArchiveSize() {
+uint32 RIFXArchive::getArchiveSize(Common::Array<Resource *> resources) {
// This will be called after updating the size of all the resources
uint32 size = 0;
- for (auto it : _resources) {
+ for (auto it : resources) {
if (it->tag != MKTAG('R', 'I', 'F', 'X') && it->tag != MKTAG('X', 'F', 'I', 'R')) {
size += it->size + 8; // The 8 is to account for the header and size
}
@@ -1696,8 +1792,24 @@ uint32 RIFXArchive::getArchiveSize() {
return size;
}
-uint32 RIFXArchive::getCASResourceSize() {
- return _types[MKTAG('C', 'A', 'S', 't')].size() * 4;
+uint32 RIFXArchive::getCASResourceSize(uint32 castLib) {
+ uint castTag = MKTAG('C', 'A', 'S', 't');
+ uint32 maxCastId = 0;
+
+ // maxCastId is the basically the number of cast members present in the cast
+ // This is the number of entries present in the 'CAS*' resource
+ for (auto &it : _types[castTag]) {
+ if (it._value.libResourceId == castLib) {
+ maxCastId = MAX(maxCastId, it._value.castId);
+ }
+ }
+
+ return (maxCastId + 1) * 4;
+}
+
+uint32 RIFXArchive::getKeyTableResourceSize() {
+ // 12 bytes of header + 12 * number of entries
+ return 12 + _keyTableUsedCount * 12;
}
void dumpFile(Common::String fileName, uint32 id, uint32 tag, byte *dumpData, uint32 dumpSize) {
diff --git a/engines/director/archive.h b/engines/director/archive.h
index 64c2a4abb59..f8e6edab35c 100644
--- a/engines/director/archive.h
+++ b/engines/director/archive.h
@@ -52,6 +52,24 @@ struct Resource {
Common::String name;
Common::Array<Resource> children;
bool accessed;
+
+ Resource() = default;
+ Resource(Resource *original) {
+ index = original->index;
+ offset = original->offset;
+ size = original->size;
+ uncompSize = original->uncompSize;
+ compressionType = original->compressionType;
+ castId = original->castId;
+ libResourceId = original->libResourceId;
+ tag = original->tag;
+ flags = original->flags;
+ unk1 = original->unk1;
+ nextFreeResourceID = original->nextFreeResourceID;
+ name = Common::String(original->name);
+ children = Common::Array<Resource>(original->children);
+ accessed = original->accessed;
+ }
};
class Archive {
@@ -160,16 +178,17 @@ public:
private:
/* These functions are for writing movies */
- bool writeMemoryMap(Common::SeekableMemoryWriteStream *writeStream); // Parallel to readMemoryMap
+ bool writeMemoryMap(Common::SeekableMemoryWriteStream *writeStream, Common::Array<Resource *> resource); // Parallel to readMemoryMap
bool writeAfterBurnerMap(Common::SeekableMemoryWriteStream *writeStreaa); // Parallel to readAfterBurnerMap
bool writeKeyTable(Common::SeekableMemoryWriteStream *writeStream, uint32 offset); // Parallel to readKeyTable
bool writeCast(Common::SeekableWriteStream *writeStream, uint32 offset, uint32 castLib); // Parallel to readCast
- uint32 getArchiveSize();
+ uint32 getArchiveSize(Common::Array<Resource *> resources);
uint32 getMmapSize();
uint32 getImapSize();
- uint32 getCASResourceSize();
- void rebuildResources(Movie *movie);
+ uint32 getCASResourceSize(uint32 castLib);
+ uint32 getKeyTableResourceSize();
+ Common::Array<Resource *> rebuildResources(Movie *movie);
bool readMemoryMap(Common::SeekableReadStreamEndian &stream, uint32 moreOffset, Common::SeekableMemoryWriteStream *dumpStream, uint32 movieStartOffset);
bool readAfterburnerMap(Common::SeekableReadStreamEndian &stream, uint32 moreOffset);
@@ -180,8 +199,6 @@ private:
uint32 _metaTag;
uint32 _moreOffset;
uint32 _mapversion;
- uint32 _mmapOffset;
- uint32 _mmapLength;
uint32 _mmapHeaderSize;
uint32 _mmapEntrySize;
uint32 _totalCount;
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index 1293e5db477..fe9422bcac0 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -131,6 +131,10 @@ Cast::~Cast() {
CastMember *Cast::getCastMember(int castId, bool load) {
CastMember *result = nullptr;
+ debug("What is the loadedcast:");
+ for (auto it : *_loadedCast) {
+ debug("%d: %s/load", it._key, castType2str(it._value->_type));
+ }
if (_loadedCast && _loadedCast->contains(castId)) {
result = _loadedCast->getVal(castId);
}
@@ -536,51 +540,51 @@ void Cast::saveConfig(Common::MemoryWriteStream *writeStream, uint32 offset) {
// These offsets are only for Director Version 4 to Director version 6
// offsets
- writeStream->writeUint16LE(_len); // 0 // This will change
- writeStream->writeUint16LE(_fileVersion); // 2
+ writeStream->writeUint16BE(configSize); // 0 // This will change
+ writeStream->writeUint16BE(_fileVersion); // 2
Movie::writeRect(writeStream, _checkRect); // 4, 6, 8, 10
- writeStream->writeUint16LE(_castArrayStart); // 12
+ writeStream->writeUint16BE(_castArrayStart); // 12
// This will change
- writeStream->writeUint16LE(_castArrayStart + _loadedCast->size()); // 14
+ writeStream->writeUint16BE(_castArrayStart + _castArchive->getResourceIDList(MKTAG('C', 'A', 'S', 't')).size()); // 14
writeStream->writeByte(_readRate); // 16
writeStream->writeByte(_lightswitch); // 17
- writeStream->writeSint16LE(_unk1); // 18
+ writeStream->writeSint16BE(_unk1); // 18
- writeStream->writeUint16LE(_commentFont); // 20
- writeStream->writeUint16LE(_commentSize); // 22
- writeStream->writeUint16LE(_commentStyle); // 24
- writeStream->writeUint16LE(_stageColor); // 26
+ writeStream->writeUint16BE(_commentFont); // 20
+ writeStream->writeUint16BE(_commentSize); // 22
+ writeStream->writeUint16BE(_commentStyle); // 24
+ writeStream->writeUint16BE(_stageColor); // 26
- writeStream->writeUint16LE(_bitdepth); // 28
+ writeStream->writeUint16BE(_bitdepth); // 28
writeStream->writeByte(_field17); // 29
writeStream->writeByte(_field18); // 30
- writeStream->writeSint32LE(_field19); // 34
+ writeStream->writeSint32BE(_field19); // 34
- writeStream->writeUint16LE(_version); // 36
+ writeStream->writeUint16BE(_version); // 36
- writeStream->writeUint16LE(_field21); // 38
- writeStream->writeUint32LE(_field22); // 40
- writeStream->writeUint32LE(_field23); // 44
+ writeStream->writeUint16BE(_field21); // 38
+ writeStream->writeUint32BE(_field22); // 40
+ writeStream->writeUint32BE(_field23); // 44
- writeStream->writeSint32LE(_field24); // 48
+ writeStream->writeSint32BE(_field24); // 48
writeStream->writeSByte(_field25); // 52
writeStream->writeSByte(_field26); // 53
- writeStream->writeSint16LE(_frameRate); // 54
- writeStream->writeUint16LE(_platform); // 56
- writeStream->writeSint16LE(_protection); // 58
- writeStream->writeSint32LE(_field29); // 60
+ writeStream->writeSint16BE(_frameRate); // 54
+ writeStream->writeUint16BE(_platform); // 56
+ writeStream->writeSint16BE(_protection); // 58
+ writeStream->writeSint32BE(_field29); // 60
// Currently a stub
uint32 checksum = computeChecksum();
- writeStream->writeUint32LE(checksum); // 64
+ writeStream->writeUint32BE(checksum); // 64
if (_version >= kFileVer400 && _version < kFileVer500) {
- writeStream->writeSint16LE(_field30); // 68
+ writeStream->writeSint16BE(_field30); // 68
// This loop isn't writing meaningful data currently
// But it is possible that this data might be needed
@@ -592,8 +596,8 @@ void Cast::saveConfig(Common::MemoryWriteStream *writeStream, uint32 offset) {
writeStream->writeByte(0); // 68, 69, 70, 71, 72, 73, 74, 75
}
- writeStream->writeSint16LE(_defaultPalette.castLib); // 76
- writeStream->writeSint16LE(_defaultPalette.member); // 78
+ writeStream->writeSint16BE(_defaultPalette.castLib); // 76
+ writeStream->writeSint16BE(_defaultPalette.member); // 78
}
if (debugChannelSet(7, kDebugSaving)) {
@@ -608,7 +612,7 @@ void Cast::saveConfig(Common::MemoryWriteStream *writeStream, uint32 offset) {
writeStream->seek(currentPos);
dumpFile("ConfigData", 0, MKTAG('V', 'W', 'C', 'F'), dumpData, configSize + 8);
- delete writeStream;
+ delete dumpStream;
}
}
diff --git a/engines/director/castmember/filmloop.cpp b/engines/director/castmember/filmloop.cpp
index d847ac8c836..dccf2a28973 100644
--- a/engines/director/castmember/filmloop.cpp
+++ b/engines/director/castmember/filmloop.cpp
@@ -279,16 +279,16 @@ void FilmLoopCastMember::loadFilmLoopDataD4(Common::SeekableReadStreamEndian &st
_frames.clear();
uint32 size = stream.readUint32BE();
- if (debugChannelSet(5, kDebugLoading)) {
- debugC(5, kDebugLoading, "loadFilmLoopDataD4: SCVW body:");
+ if (debugChannelSet(8, kDebugLoading)) {
+ debugC(8, kDebugLoading, "loadFilmLoopDataD4: 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, "loadFilmLoopDataD4: SCVW header:");
+ if (debugChannelSet(8, kDebugLoading)) {
+ debugC(8, kDebugLoading, "loadFilmLoopDataD4: SCVW header:");
stream.hexdump(framesOffset - 8);
}
stream.skip(6);
@@ -304,8 +304,8 @@ void FilmLoopCastMember::loadFilmLoopDataD4(Common::SeekableReadStreamEndian &st
continue;
}
frameSize -= 2;
- if (debugChannelSet(5, kDebugLoading)) {
- debugC(5, kDebugLoading, "loadFilmLoopDataD4: Frame entry:");
+ if (debugChannelSet(8, kDebugLoading)) {
+ debugC(8, kDebugLoading, "loadFilmLoopDataD4: Frame entry:");
stream.hexdump(frameSize);
}
@@ -351,12 +351,12 @@ void FilmLoopCastMember::loadFilmLoopDataD4(Common::SeekableReadStreamEndian &st
}
for (auto &s : newFrame.sprites) {
- debugC(5, kDebugLoading, "loadFilmLoopDataD4: Sprite: channel %d, castId %s, bbox %d %d %d %d", s._key,
+ debugC(8, kDebugLoading, "loadFilmLoopDataD4: Sprite: channel %d, castId %s, bbox %d %d %d %d", s._key,
s._value._castId.asString().c_str(), s._value._startPoint.x, s._value._startPoint.y,
s._value._width, s._value._height);
if (s._key == -1) {
- debugC(5, kDebugLoading, "loadFilmLoopDataD4: Skipping channel -1");
+ debugC(8, kDebugLoading, "loadFilmLoopDataD4: Skipping channel -1");
if (s._value._startPoint.x != 0 || s._value._startPoint.y != 0 || s._value._width != 0 ||
(s._value._height != -256 && s._value._height != 0))
warning("BUILDBOT: loadFilmLoopDataD4: Malformed VWSC resource: Sprite: channel %d, castId %s, bbox %d %d %d %d", s._key,
@@ -774,7 +774,6 @@ void FilmLoopCastMember::writeSCVWResource(Common::MemoryWriteStream *writeStrea
writeStream->writeUint32LE(MKTAG('S', 'C', 'V', 'W'));
writeStream->writeUint32LE(filmloopSize); // Size of the resource
- uint32 sizePos = writeStream->pos();
writeStream->writeUint32BE(0); // Putting zeroes instead of size currently
uint32 frameOffset = 16; // Should be greater than 16
@@ -839,9 +838,9 @@ uint32 FilmLoopCastMember::getSCVWResourceSize() {
// Header (Ignored data): 12 bytes
uint32 channelSize = 0;
- if (_cast->_version == kFileVer400) {
+ if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
channelSize = kSprChannelSizeD4;
- } else if (_cast->_version == kFileVer400) {
+ } else if (_cast->_version >= kFileVer500) {
channelSize = kSprChannelSizeD5;
} else {
warning("FilmLoopCastMember::getSCVWResourceSize: Director version unsupported");
diff --git a/engines/director/castmember/palette.cpp b/engines/director/castmember/palette.cpp
index 83c155902cf..f0d1da21fe8 100644
--- a/engines/director/castmember/palette.cpp
+++ b/engines/director/castmember/palette.cpp
@@ -131,10 +131,6 @@ void PaletteCastMember::load() {
}
_loaded = true;
-
- if (debugChannelSet(5, kDebugSaving)) {
- writePaletteData(nullptr, 0);
- }
}
void PaletteCastMember::unload() {
diff --git a/engines/director/castmember/text.cpp b/engines/director/castmember/text.cpp
index f512e3bc45a..2bc1b09c3ca 100644
--- a/engines/director/castmember/text.cpp
+++ b/engines/director/castmember/text.cpp
@@ -636,7 +636,6 @@ void TextCastMember::load() {
}
_loaded = true;
- writeSTXTResource(nullptr, 0);
}
void TextCastMember::unload() {
@@ -980,11 +979,7 @@ uint32 TextCastMember::getCastDataSize() {
}
uint32 TextCastMember::writeSTXTResource(Common::MemoryWriteStream *writeStream, uint32 offset) {
- uint32 castSize = getSTXTResourceSize() + 8;
-
- byte *dumpData = nullptr;
- dumpData = (byte *)calloc(castSize, sizeof(byte));
- writeStream = new Common::SeekableMemoryWriteStream(dumpData, castSize);
+ uint32 stxtSize = getSTXTResourceSize() + 8;
writeStream->seek(offset);
@@ -1036,9 +1031,21 @@ uint32 TextCastMember::writeSTXTResource(Common::MemoryWriteStream *writeStream,
it++;
}
- dumpFile("TextData", _castId, MKTAG('S', 'T', 'X', 'T'), dumpData, getSTXTResourceSize() + 8);
- delete writeStream;
- return getSTXTResourceSize() + 8;
+ if (debugChannelSet(7, kDebugSaving)) {
+ byte *dumpData = nullptr;
+ dumpData = (byte *)calloc(stxtSize, sizeof(byte));
+ Common::MemoryWriteStream *dumpStream = new Common::SeekableMemoryWriteStream(dumpData, stxtSize);
+
+ int32 currentPos = writeStream->pos();
+ writeStream->seek(offset);
+ dumpStream->write(writeStream, stxtSize);
+ writeStream->seek(currentPos);
+
+ dumpFile("TextData", _castId, MKTAG('S', 'T', 'X', 'T'), dumpData, getSTXTResourceSize() + 8);
+ delete dumpStream;
+ }
+
+ return stxtSize + 8;
}
uint32 TextCastMember::getSTXTResourceSize() {
diff --git a/engines/director/lingo/lingo-bytecode.cpp b/engines/director/lingo/lingo-bytecode.cpp
index e5eef56b38c..adc6c7758c9 100644
--- a/engines/director/lingo/lingo-bytecode.cpp
+++ b/engines/director/lingo/lingo-bytecode.cpp
@@ -1203,6 +1203,7 @@ ScriptContext *LingoCompiler::compileLingoV4(Common::SeekableReadStreamEndian &s
for (uint16 i = 0; i < constsCount; i++) {
Datum constant;
uint32 constType = 0;
+ debug("what is the version %d", version);
if (version >= kFileVer500) {
constType = stream.readUint32();
} else {
Commit: c495df4e339e249cfe6d72336c93c17ad81fb90c
https://github.com/scummvm/scummvm/commit/c495df4e339e249cfe6d72336c93c17ad81fb90c
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Fixes to lingo command `saveMovie`
If the path in saveMovie command is empty, add a default path
Smaller fixes
Changed paths:
engines/director/archive.cpp
engines/director/cast.cpp
engines/director/castmember/castmember.cpp
engines/director/castmember/castmember.h
engines/director/castmember/script.cpp
engines/director/castmember/text.cpp
engines/director/movie.cpp
engines/director/movie.h
diff --git a/engines/director/archive.cpp b/engines/director/archive.cpp
index b8708a7d87d..47635175755 100644
--- a/engines/director/archive.cpp
+++ b/engines/director/archive.cpp
@@ -1253,9 +1253,20 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
case MKTAG('B', 'I', 'T', 'D'):
{
- // Get one of the parents of the 'BITD' resource stored in the _keyData
- // Ask the parent to write the 'BITD' resource, assuming the parent only has only one 'BITD' resource
- uint32 parentIndex = _keyData[MKTAG('B', 'I', 'T', 'D')].begin()->_key;
+ uint32 parentIndex = 0;
+ for (auto &jt : _keyData[MKTAG('B', 'I', 'T', 'D')]) {
+ for (auto &kt : jt._value) {
+ if (kt == it->index) {
+ parentIndex = jt._key;
+ break;
+ }
+ }
+ if (parentIndex) {
+ break;
+ }
+ }
+
+ debug("What is the parentIndex = %d, cast->_castArraySize = %d", parentIndex, cast->_castArrayStart);
Resource parent = castResMap[parentIndex];
BitmapCastMember *target = (BitmapCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
@@ -1265,7 +1276,19 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
case MKTAG('S', 'T', 'X', 'T'):
{
- uint32 parentIndex = _keyData[MKTAG('S', 'T', 'X', 'T')].begin()->_key;
+ uint32 parentIndex = 0;
+ for (auto &jt : _keyData[MKTAG('S', 'T', 'X', 'T')]) {
+ for (auto &kt : jt._value) {
+ if (kt == it->index) {
+ parentIndex = jt._key;
+ break;
+ }
+ }
+ if (parentIndex) {
+ break;
+ }
+ }
+
Resource parent = castResMap[parentIndex];
TextCastMember *target = (TextCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
@@ -1275,7 +1298,19 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
case MKTAG('C', 'L', 'U', 'T'):
{
- uint32 parentIndex = _keyData[MKTAG('C', 'L', 'U', 'T')].begin()->_key;
+ uint32 parentIndex = 0;
+ for (auto &jt : _keyData[MKTAG('C', 'L', 'U', 'T')]) {
+ for (auto &kt : jt._value) {
+ if (kt == it->index) {
+ parentIndex = jt._key;
+ break;
+ }
+ }
+ if (parentIndex) {
+ break;
+ }
+ }
+
Resource parent = castResMap[parentIndex];
PaletteCastMember *target = (PaletteCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
@@ -1312,6 +1347,10 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
}
}
+ if (path.empty()) {
+ path = Common::Path("./dumps/writtenMovie.dir");
+ }
+
Common::DumpFile out;
// Write the movie out, stored in dumpData
@@ -1579,7 +1618,7 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
case MKTAG('C', 'A', 'S', 't'):
{
// The castIds of cast members start from _castArrayStart
- CastMember *target = cast->getCastMember(it->castId - cast->_castArrayStart);
+ CastMember *target = cast->getCastMember(it->castId + cast->_castArrayStart);
if (target) {
resSize = target->getCastResourceSize();
@@ -1692,7 +1731,6 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
}
}
- debug("What is the parentIndex = %d, cast->_castArraySize = %d", parentIndex, cast->_castArrayStart);
Resource parent = castResMap[parentIndex];
BitmapCastMember *target = (BitmapCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
@@ -1708,7 +1746,7 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
case MKTAG('S', 'C', 'V', 'W'):
{
uint32 parentIndex = 0;
- for (auto &jt : _keyData[MKTAG('B', 'I', 'T', 'D')]) {
+ for (auto &jt : _keyData[MKTAG('S', 'C', 'V', 'W')]) {
for (auto &kt : jt._value) {
if (kt == it->index) {
parentIndex = jt._key;
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index fe9422bcac0..ce41d0138a3 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -839,13 +839,13 @@ void Cast::saveCast(Common::MemoryWriteStream *writeStream, Resource *res) {
CastMember *target = _loadedCast->getVal(id);
// To make it consistent with how the data is stored originally, getResourceSize returns
// the size excluding 'CASt' header and the entry for size itself. Adding 8 to compensate for that
- castSize = target->getCastResourceSize() + 8;
+ castSize = target->getCastResourceSize();
type = target->_type;
- target->writeCAStResource(writeStream, 0);
+ target->writeCAStResource(writeStream);
} else {
// The size stored in the memory map (_resources array), as well as the resource itself is the size
// excluding the 'CASt' header and the entry of size itself. Adding 8 to compensate for that
- castSize = _castArchive->getResourceSize(MKTAG('C', 'A', 'S', 't'), res->index) + 8;
+ castSize = _castArchive->getResourceSize(MKTAG('C', 'A', 'S', 't'), res->index);
writeStream->writeUint32LE(MKTAG('C', 'A', 'S', 't'));
Common::SeekableReadStreamEndian *stream = getResource(MKTAG('C', 'A', 'S', 't'), res->index);
uint32 size = stream->size(); // This is the size of the Resource without header and size entry itself
@@ -858,15 +858,15 @@ void Cast::saveCast(Common::MemoryWriteStream *writeStream, Resource *res) {
debugC(5, kDebugSaving, "Cast::saveCast()::Saving 'CASt' resource, id: %d, size: %d, type: %s", id, castSize, castType2str(type));
if (debugChannelSet(7, kDebugSaving)) {
- byte *dumpData = (byte *)calloc(castSize, sizeof(byte));
- Common::SeekableMemoryWriteStream *dumpStream = new Common::SeekableMemoryWriteStream(dumpData, castSize);
+ byte *dumpData = (byte *)calloc(castSize + 8, sizeof(byte));
+ Common::SeekableMemoryWriteStream *dumpStream = new Common::SeekableMemoryWriteStream(dumpData, castSize + 8);
uint32 currentPos = writeStream->pos();
writeStream->seek(offset);
dumpStream->write(writeStream, castSize);
writeStream->seek(currentPos);
- dumpFile(castType2str(type), res->index, MKTAG('C', 'A', 'S', 't'), dumpData, castSize);
+ dumpFile(castType2str(type), res->index, MKTAG('C', 'A', 'S', 't'), dumpData, castSize + 8);
delete dumpStream;
}
}
@@ -907,28 +907,23 @@ void Cast::writeCastInfo(Common::MemoryWriteStream *writeStream, uint32 castId)
break;
case 1:
- castInfo.strings[0].data = (byte *)malloc(castInfo.strings[0].len);
- memcpy(castInfo.strings[0].data, ci->script.c_str(), castInfo.strings[0].len);
+ castInfo.strings[0].writeString(ci->script, false);
break;
case 2:
- castInfo.strings[1].data = (byte *)malloc(castInfo.strings[1].len);
- memcpy(castInfo.strings[1].data, ci->name.c_str(), castInfo.strings[1].len);
+ castInfo.strings[1].writeString(ci->name, false);
break;
case 3:
- castInfo.strings[2].data = (byte *)malloc(castInfo.strings[2].len);
- memcpy(castInfo.strings[2].data, ci->directory.c_str(), castInfo.strings[2].len);
+ castInfo.strings[2].writeString(ci->directory, false);
break;
case 4:
- castInfo.strings[3].data = (byte *)malloc(castInfo.strings[3].len);
- memcpy(castInfo.strings[3].data, ci->fileName.c_str(), castInfo.strings[3].len);
+ castInfo.strings[3].writeString(ci->fileName);
break;
case 5:
- castInfo.strings[4].data = (byte *)malloc(castInfo.strings[4].len);
- memcpy(castInfo.strings[4].data, ci->type.c_str(), castInfo.strings[4].len);
+ castInfo.strings[4].writeString(ci->type);
break;
case 6:
@@ -1727,6 +1722,10 @@ void Cast::loadCastInfo(Common::SeekableReadStreamEndian &stream, uint16 id) {
if (!_loadedCast->contains(id))
return;
+ if (debugChannelSet(7, kDebugLoading)) {
+ debug("Cast::loadingCastInfo: Loading cast info for castId: %d", id);
+ stream.hexdump(stream.size());
+ }
InfoEntries castInfo = Movie::loadInfoEntries(stream, _version);
CastMemberInfo *ci = new CastMemberInfo();
@@ -1823,16 +1822,16 @@ void Cast::loadCastInfo(Common::SeekableReadStreamEndian &stream, uint16 id) {
}
// fallthrough
case 5:
- ci->type = castInfo.strings[4].readString();
+ ci->type = castInfo.strings[4].readString(false);
// fallthrough
case 4:
- ci->fileName = castInfo.strings[3].readString();
+ ci->fileName = castInfo.strings[3].readString(false);
// fallthrough
case 3:
- ci->directory = castInfo.strings[2].readString();
+ ci->directory = castInfo.strings[2].readString(false);
// fallthrough
case 2:
- ci->name = castInfo.strings[1].readString();
+ ci->name = castInfo.strings[1].readString(false);
// fallthrough
case 1:
ci->script = castInfo.strings[0].readString(false);
diff --git a/engines/director/castmember/castmember.cpp b/engines/director/castmember/castmember.cpp
index de457ff8c49..d34ed50940f 100644
--- a/engines/director/castmember/castmember.cpp
+++ b/engines/director/castmember/castmember.cpp
@@ -313,7 +313,7 @@ void CastMember::unload() {
// Whereas _info_ is metadata (size, name, flags, etc.)
// Some cast members have their _data_ as well as _info_ in this very 'CASt' resource, e.g. TextCastMember
// Whereas some other have their _info_ in a 'CASt' resource and _data_ in a dedicated resource (e.g. PaletteCastMember has 'CLUT' resource)
-uint32 CastMember::writeCAStResource(Common::MemoryWriteStream *writeStream, uint32 offset) {
+uint32 CastMember::writeCAStResource(Common::MemoryWriteStream *writeStream) {
uint32 castResourceSize = getCastResourceSize();
writeStream->writeUint32LE(MKTAG('C', 'A', 'S', 't'));
diff --git a/engines/director/castmember/castmember.h b/engines/director/castmember/castmember.h
index 29725b8d9f0..2152010c2f4 100644
--- a/engines/director/castmember/castmember.h
+++ b/engines/director/castmember/castmember.h
@@ -105,7 +105,7 @@ public:
// Three parts to a 'CASt' resource (header + _info_, _data_)
// The headers, are common, the _info_ writing is handled by the Cast class, so no worries there
// So, the only thing that differs is the _data_, for which we'll have separate implementations for each CastMember
- uint32 writeCAStResource(Common::MemoryWriteStream *writeStream, uint32 offset);
+ uint32 writeCAStResource(Common::MemoryWriteStream *writeStream);
uint32 getCastInfoSize();
uint32 getCastResourceSize();
virtual void writeCastData(Common::MemoryWriteStream *writeStream);
diff --git a/engines/director/castmember/script.cpp b/engines/director/castmember/script.cpp
index 8023986c79c..bb26707b305 100644
--- a/engines/director/castmember/script.cpp
+++ b/engines/director/castmember/script.cpp
@@ -143,8 +143,8 @@ Common::String ScriptCastMember::formatInfo() {
uint32 ScriptCastMember::getCastDataSize() {
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
- // 2 bytes for type and unk1 + 1 byte for castType (see Cast::loadCastData() for Director 4 only
- return 2 + 1;
+ // 2 bytes for type and unk1 + 2 byte for castType and flags ma(see Cast::loadCastData() for Director 4 only
+ return 2 + 2;
} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
// type and unk1: 2 bytes
return 2;
@@ -155,8 +155,11 @@ uint32 ScriptCastMember::getCastDataSize() {
}
void ScriptCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
+ debug("************* I NEED TO CHECK HERE *************");
+ debug("what is the version: %d", _cast->_version);
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer600) {
- writeStream->writeByte(0);
+ debug("I'm here");
+ writeStream->writeByte(0); // unknown
switch (_scriptType) {
case kScoreScript:
diff --git a/engines/director/castmember/text.cpp b/engines/director/castmember/text.cpp
index 2bc1b09c3ca..86dd8ce11fe 100644
--- a/engines/director/castmember/text.cpp
+++ b/engines/director/castmember/text.cpp
@@ -955,27 +955,31 @@ void TextCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
writeStream->writeByte(_gutterSize); // 2 bytes
writeStream->writeByte(_boxShadow); // 3 bytes
writeStream->writeByte(_textType); // 4 bytes
- writeStream->writeSint16LE(_textAlign); // 6 bytes
- writeStream->writeUint16LE(_bgpalinfo1); // 8 bytes
- writeStream->writeUint16LE(_bgpalinfo2); // 10 bytes
- writeStream->writeUint16LE(_bgpalinfo3); // 12 bytes
- writeStream->writeUint16LE(_scroll); // 14 bytes
+ writeStream->writeSint16BE(_textAlign); // 6 bytes
+ writeStream->writeUint16BE(_bgpalinfo1); // 8 bytes
+ writeStream->writeUint16BE(_bgpalinfo2); // 10 bytes
+ writeStream->writeUint16BE(_bgpalinfo3); // 12 bytes
+ writeStream->writeUint16BE(_scroll); // 14 bytes
Movie::writeRect(writeStream, _initialRect); // (+8) 22 bytes
- writeStream->writeUint16LE(_maxHeight); // 24 bytes
+ writeStream->writeUint16BE(_maxHeight); // 24 bytes
writeStream->writeByte(_textShadow); // 25 bytes
writeStream->writeByte(_textFlags); // 26 bytes
- writeStream->writeUint16LE(_textHeight); // 28 bytes
+ writeStream->writeUint16BE(_textHeight); // 28 bytes
if (_type == kCastButton) {
- writeStream->writeUint16LE(_buttonType + 1); // 30 bytes
+ writeStream->writeUint16BE(_buttonType + 1); // 30 bytes
}
}
uint32 TextCastMember::getCastDataSize() {
// In total 30 bytes for text and 28 for button
- return (_type == kCastButton) ? 30 : 28;
+ uint32 size = (_type == kCastButton) ? 30 : 28;
+
+ // See Cast::loadCastData
+ size += (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) ? 2 : 0;
+ return size;
}
uint32 TextCastMember::writeSTXTResource(Common::MemoryWriteStream *writeStream, uint32 offset) {
diff --git a/engines/director/movie.cpp b/engines/director/movie.cpp
index 4da8a9ef40f..1c456ba5002 100644
--- a/engines/director/movie.cpp
+++ b/engines/director/movie.cpp
@@ -738,4 +738,19 @@ Common::String InfoEntry::readString(bool pascal) {
return g_director->getCurrentMovie()->getCast()->decodeString(encodedStr).encode(Common::kUtf8);
}
+void InfoEntry::writeString(Common::String string, bool pascal) {
+ if (string.size() == 0) {
+ return;
+ }
+
+ data = (byte *)malloc(len);
+
+ uint16 start = pascal ? 1 : 0;
+
+ for (uint16 i = start; i < len && string.size(); i++) {
+ data[i] = string.firstChar();
+ string.deleteChar(0);
+ }
+}
+
} // End of namespace Director
diff --git a/engines/director/movie.h b/engines/director/movie.h
index 32a5ed5cb0e..0933da3197e 100644
--- a/engines/director/movie.h
+++ b/engines/director/movie.h
@@ -72,6 +72,7 @@ struct InfoEntry {
}
Common::String readString(bool pascal = true);
+ void writeString(Common::String string, bool pascal = true);
};
struct InfoEntries {
Commit: a932fcad870930d99cec97a5f429fa6b5bb86518
https://github.com/scummvm/scummvm/commit/a932fcad870930d99cec97a5f429fa6b5bb86518
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Fix Saving Bitmap and Filmloop
Changed paths:
engines/director/archive.cpp
engines/director/cast.cpp
engines/director/cast.h
engines/director/castmember/bitmap.cpp
engines/director/castmember/filmloop.cpp
engines/director/castmember/palette.cpp
engines/director/castmember/text.cpp
engines/director/frame.cpp
diff --git a/engines/director/archive.cpp b/engines/director/archive.cpp
index 47635175755..db057225e2c 100644
--- a/engines/director/archive.cpp
+++ b/engines/director/archive.cpp
@@ -1075,7 +1075,7 @@ void RIFXArchive::readKeyTable(Common::SeekableReadStreamEndian &keyStream) {
_keyData[childTag][parentIndex].push_back(childIndex);
if (childTag == MKTAG('C', 'A', 'S', '*')) {
- // A 'CAS*' resource contains indexes of 'CASt' resources that are linked to the
+ // A 'CAS*' resource contains indexes of 'CASt' resources that are linked to the
// Resource at parentIndex, hence, mark the 'CAS*' libResourceId to parentIndex
// This means all the 'CASt' resources are linked to the parent with index equal to 'CAS*' libResourceId
_types[childTag][childIndex].libResourceId = parentIndex;
@@ -1244,7 +1244,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
break;
case MKTAG('C', 'A', 'S', 't'):
- cast->saveCast(writeStream, it);
+ cast->saveCastData(writeStream, it);
break;
case MKTAG('V', 'W', 'C', 'F'):
@@ -1255,7 +1255,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
{
uint32 parentIndex = 0;
for (auto &jt : _keyData[MKTAG('B', 'I', 'T', 'D')]) {
- for (auto &kt : jt._value) {
+ for (auto &kt : jt._value) {
if (kt == it->index) {
parentIndex = jt._key;
break;
@@ -1278,7 +1278,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
{
uint32 parentIndex = 0;
for (auto &jt : _keyData[MKTAG('S', 'T', 'X', 'T')]) {
- for (auto &kt : jt._value) {
+ for (auto &kt : jt._value) {
if (kt == it->index) {
parentIndex = jt._key;
break;
@@ -1300,7 +1300,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
{
uint32 parentIndex = 0;
for (auto &jt : _keyData[MKTAG('C', 'L', 'U', 'T')]) {
- for (auto &kt : jt._value) {
+ for (auto &kt : jt._value) {
if (kt == it->index) {
parentIndex = jt._key;
break;
@@ -1320,15 +1320,19 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
case MKTAG('S', 'C', 'V', 'W'):
{
- uint32 parentIndex = _keyData[MKTAG('S', 'C', 'V', 'W')].begin()->_key;
- // If the parentIndex is 1024 that means this 'SCVW' is a score or a movie
- if (parentIndex == 1024) {
- writeStream->seek(it->offset);
- writeStream->writeUint32LE(it->tag);
- writeStream->writeUint32LE(it->size);
- writeStream->writeStream(getResource(it->tag, it->index));
- break;
+ uint32 parentIndex = 0;
+ for (auto &jt : _keyData[MKTAG('S', 'C', 'V', 'W')]) {
+ for (auto &kt : jt._value) {
+ if (kt == it->index) {
+ parentIndex = jt._key;
+ break;
+ }
+ }
+ if (parentIndex) {
+ break;
+ }
}
+
Resource parent = castResMap[parentIndex];
FilmLoopCastMember *target = (FilmLoopCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
@@ -1389,7 +1393,7 @@ bool RIFXArchive::writeMemoryMap(Common::SeekableMemoryWriteStream *writeStream,
writeStream->writeUint16LE(_mmapHeaderSize);
writeStream->writeUint16LE(_mmapEntrySize);
-
+
uint32 newResCount = resources.size();
writeStream->writeUint32LE(newResCount + _totalCount - _resCount); // _totalCount - _resCount is the number of empty entries
writeStream->writeUint32LE(newResCount);
@@ -1492,10 +1496,10 @@ bool RIFXArchive::writeCast(Common::SeekableWriteStream *writeStream, uint32 off
castIndexes[it._value.castId] = it._value.index;
maxCastId = MAX(maxCastId, it._value.castId);
}
- }
+ }
for (uint32 i = 0; i <= maxCastId; i++) {
- uint32 castIndex = castIndexes.getValOrDefault(i, 0);
+ uint32 castIndex = castIndexes.getValOrDefault(i, 0);
writeStream->writeUint32BE(castIndex);
}
return true;
@@ -1592,7 +1596,7 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
for (auto it: _resources) {
builtResources.push_back(new Resource(it));
}
-
+
uint32 resSize = 0;
for (auto &it : builtResources) {
switch (it->tag) {
@@ -1612,7 +1616,7 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
case MKTAG('m', 'm', 'a', 'p'):
// one resource only
it->size = getMmapSize();
- it->offset = 12 + (getImapSize() + 8); // The +8 is to account for header and size
+ it->offset = 12 + (getImapSize() + 8); // The +8 is to account for header and size
break;
case MKTAG('C', 'A', 'S', 't'):
@@ -1663,7 +1667,7 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
{
uint32 parentIndex = 0;
for (auto &jt : _keyData[MKTAG('S', 'T', 'X', 'T')]) {
- for (auto &kt : jt._value) {
+ for (auto &kt : jt._value) {
if (kt == it->index) {
parentIndex = jt._key;
break;
@@ -1689,11 +1693,11 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
case MKTAG('C', 'L', 'U', 'T'):
{
// We have to find the parent
- // Look into the keyData, for all parents of 'CLUT' resource
- // If the parent contains this 'CLUT' resource's index, that's our parent
+ // Look into the keyData, for all parents of 'CLUT' resource
+ // If the parent contains this 'CLUT' resource's index, that's our parent
uint32 parentIndex = 0;
for (auto &jt : _keyData[MKTAG('C', 'L', 'U', 'T')]) {
- for (auto &kt : jt._value) {
+ for (auto &kt : jt._value) {
if (kt == it->index) {
parentIndex = jt._key;
break;
@@ -1720,7 +1724,7 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
{
uint32 parentIndex = 0;
for (auto &jt : _keyData[MKTAG('B', 'I', 'T', 'D')]) {
- for (auto &kt : jt._value) {
+ for (auto &kt : jt._value) {
if (kt == it->index) {
parentIndex = jt._key;
break;
@@ -1747,7 +1751,7 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
{
uint32 parentIndex = 0;
for (auto &jt : _keyData[MKTAG('S', 'C', 'V', 'W')]) {
- for (auto &kt : jt._value) {
+ for (auto &kt : jt._value) {
if (kt == it->index) {
parentIndex = jt._key;
break;
@@ -1758,13 +1762,6 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
}
}
- // If parent index is 1024, that means this 'SCVW' resource is a score or a movie
- if (parentIndex == 1024) {
- it->offset = currentSize;
- currentSize += it->size + 8; // This size doesn't include the header and size entry
- break;
- }
-
Resource parent = castResMap[parentIndex];
FilmLoopCastMember *target = (FilmLoopCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
@@ -1779,7 +1776,7 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
case MKTAG('f', 'r', 'e', 'e'):
case MKTAG('j', 'u', 'n', 'k'):
- // These resource do not hold any data
+ // These resources do not hold any data
it->size = 0;
// We could just ignore these and not write them at all
@@ -1840,7 +1837,7 @@ uint32 RIFXArchive::getCASResourceSize(uint32 castLib) {
if (it._value.libResourceId == castLib) {
maxCastId = MAX(maxCastId, it._value.castId);
}
- }
+ }
return (maxCastId + 1) * 4;
}
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index ce41d0138a3..7843f1c335f 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -131,10 +131,6 @@ Cast::~Cast() {
CastMember *Cast::getCastMember(int castId, bool load) {
CastMember *result = nullptr;
- debug("What is the loadedcast:");
- for (auto it : *_loadedCast) {
- debug("%d: %s/load", it._key, castType2str(it._value->_type));
- }
if (_loadedCast && _loadedCast->contains(castId)) {
result = _loadedCast->getVal(castId);
}
@@ -612,6 +608,7 @@ void Cast::saveConfig(Common::MemoryWriteStream *writeStream, uint32 offset) {
writeStream->seek(currentPos);
dumpFile("ConfigData", 0, MKTAG('V', 'W', 'C', 'F'), dumpData, configSize + 8);
+ free(dumpData);
delete dumpStream;
}
@@ -816,7 +813,7 @@ void Cast::loadCast() {
}
}
-void Cast::saveCast(Common::MemoryWriteStream *writeStream, Resource *res) {
+void Cast::saveCastData(Common::MemoryWriteStream *writeStream, Resource *res) {
// This offset is at which we will start writing our 'CASt' resources
// In the original file, all the 'CASt' resources don't necessarily appear side by side
uint32 offset = res->offset;
@@ -855,7 +852,7 @@ void Cast::saveCast(Common::MemoryWriteStream *writeStream, Resource *res) {
delete stream;
}
- debugC(5, kDebugSaving, "Cast::saveCast()::Saving 'CASt' resource, id: %d, size: %d, type: %s", id, castSize, castType2str(type));
+ debugC(5, kDebugSaving, "Cast::saveCastData()::Saving 'CASt' resource, id: %d, size: %d, type: %s", id, castSize, castType2str(type));
if (debugChannelSet(7, kDebugSaving)) {
byte *dumpData = (byte *)calloc(castSize + 8, sizeof(byte));
@@ -867,6 +864,7 @@ void Cast::saveCast(Common::MemoryWriteStream *writeStream, Resource *res) {
writeStream->seek(currentPos);
dumpFile(castType2str(type), res->index, MKTAG('C', 'A', 'S', 't'), dumpData, castSize + 8);
+ free(dumpData);
delete dumpStream;
}
}
diff --git a/engines/director/cast.h b/engines/director/cast.h
index 5d842712205..41c8ccce1c7 100644
--- a/engines/director/cast.h
+++ b/engines/director/cast.h
@@ -107,7 +107,7 @@ public:
void loadSord(Common::SeekableReadStreamEndian &stream);
void saveConfig(Common::MemoryWriteStream *writeStream, uint32 offset);
- void saveCast(Common::MemoryWriteStream *writeStream, Resource *res);
+ void saveCastData(Common::MemoryWriteStream *writeStream, Resource *res);
void saveCastData();
void writeCastInfo(Common::MemoryWriteStream *writeStream, uint32 castId);
uint32 getCastInfoSize(uint32 castId);
diff --git a/engines/director/castmember/bitmap.cpp b/engines/director/castmember/bitmap.cpp
index 32c5ebda03d..1b58d978647 100644
--- a/engines/director/castmember/bitmap.cpp
+++ b/engines/director/castmember/bitmap.cpp
@@ -688,6 +688,11 @@ void BitmapCastMember::load() {
break;
}
+ if (debugChannelSet(7, kDebugLoading)) {
+ debug("BitmapCastMember::load(): Bitmap data:");
+ pic->hexdump(pic->size());
+ }
+
if (!img || !img->loadStream(*pic)) {
warning("BitmapCastMember::load(): Unable to load id: %d", imgId);
delete pic;
@@ -715,7 +720,6 @@ void BitmapCastMember::load() {
debugC(5, kDebugImages, "BitmapCastMember::load(): Bitmap: id: %d, w: %d, h: %d, flags1: %x, flags2: %x bytes: %x, bpp: %d clut: %s", imgId, w, h, _flags1, _flags2, _bytes, _bitsPerPixel, _clut.asString().c_str());
_loaded = true;
- writeBITDResource(nullptr, 0);
}
void BitmapCastMember::unload() {
@@ -1030,7 +1034,7 @@ void BitmapCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
if (_flags2 != 0) {
// Skipping 14 bytes because they are not stored in ScummVM Director
// May need to save in the future, see BitCastMember::BitCastMember constructor
- writeStream->write(0, 14);
+ writeStream->seek(14, SEEK_CUR);
writeStream->writeUint16BE(_flags2);
}
}
@@ -1038,10 +1042,10 @@ void BitmapCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
}
uint32 BitmapCastMember::writeBITDResource(Common::MemoryWriteStream *writeStream, uint32 offset) {
- // writeStream->seek(offset);
+ writeStream->seek(offset);
- // writeStream->writeUint32LE(MKTAG('B', 'I', 'T', 'D'));
- // writeStream->writeUint32LE(_size);
+ writeStream->writeUint32LE(MKTAG('B', 'I', 'T', 'D'));
+ writeStream->writeUint32LE(getBITDResourceSize());
if (_external) {
warning("BitmapCastMember::writeBITDResource: the bitmap is external, ignoring for now");
@@ -1112,7 +1116,11 @@ uint32 BitmapCastMember::writeBITDResource(Common::MemoryWriteStream *writeStrea
}
}
- dumpFile("BitmapData", _castId, MKTAG('B', 'I', 'T', 'D'), pixels.data(), pixels.size());
+ writeStream->write(pixels.data(), pixels.size());
+
+ if (debugChannelSet(7, kDebugSaving)) {
+ dumpFile("BitmapData", _castId, MKTAG('B', 'I', 'T', 'D'), pixels.data(), pixels.size());
+ }
return 0;
}
diff --git a/engines/director/castmember/filmloop.cpp b/engines/director/castmember/filmloop.cpp
index dccf2a28973..163ab37b564 100644
--- a/engines/director/castmember/filmloop.cpp
+++ b/engines/director/castmember/filmloop.cpp
@@ -280,7 +280,7 @@ void FilmLoopCastMember::loadFilmLoopDataD4(Common::SeekableReadStreamEndian &st
uint32 size = stream.readUint32BE();
if (debugChannelSet(8, kDebugLoading)) {
- debugC(8, kDebugLoading, "loadFilmLoopDataD4: SCVW body:");
+ debugC(8, kDebugLoading, "loadFilmLoopDataD4: SCVW body of size: %d", size);
uint32 pos = stream.pos();
stream.seek(0);
stream.hexdump(size);
@@ -288,7 +288,7 @@ void FilmLoopCastMember::loadFilmLoopDataD4(Common::SeekableReadStreamEndian &st
}
uint32 framesOffset = stream.readUint32BE();
if (debugChannelSet(8, kDebugLoading)) {
- debugC(8, kDebugLoading, "loadFilmLoopDataD4: SCVW header:");
+ debugC(8, kDebugLoading, "loadFilmLoopDataD4: SCVW header of size: %d", framesOffset - 8);
stream.hexdump(framesOffset - 8);
}
stream.skip(6);
@@ -304,8 +304,8 @@ void FilmLoopCastMember::loadFilmLoopDataD4(Common::SeekableReadStreamEndian &st
continue;
}
frameSize -= 2;
+ debugC(2, kDebugLoading, "loadFilmLoopDataD4: Frame entry: %d", frameSize);
if (debugChannelSet(8, kDebugLoading)) {
- debugC(8, kDebugLoading, "loadFilmLoopDataD4: Frame entry:");
stream.hexdump(frameSize);
}
@@ -318,7 +318,7 @@ void FilmLoopCastMember::loadFilmLoopDataD4(Common::SeekableReadStreamEndian &st
int channelOffset = order % channelSize;
int offset = order;
- debugC(8, kDebugLoading, "loadFilmLoopDataD4: Message: msgWidth %d, order: %d, channel %d, channelOffset %d", msgWidth, order, channel, channelOffset);
+ debugC(2, kDebugLoading, "loadFilmLoopDataD4: Message: msgWidth %d, order: %d, channel %d, channelOffset %d", msgWidth, order, channel, channelOffset);
if (debugChannelSet(8, kDebugLoading)) {
stream.hexdump(msgWidth);
}
@@ -351,7 +351,7 @@ void FilmLoopCastMember::loadFilmLoopDataD4(Common::SeekableReadStreamEndian &st
}
for (auto &s : newFrame.sprites) {
- debugC(8, kDebugLoading, "loadFilmLoopDataD4: Sprite: channel %d, castId %s, bbox %d %d %d %d", s._key,
+ debugC(2, kDebugLoading, "loadFilmLoopDataD4: Sprite: channel %d, castId %s, bbox %d %d %d %d", s._key,
s._value._castId.asString().c_str(), s._value._startPoint.x, s._value._startPoint.y,
s._value._width, s._value._height);
@@ -753,30 +753,27 @@ void FilmLoopCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
void FilmLoopCastMember::writeSCVWResource(Common::MemoryWriteStream *writeStream, uint32 offset) {
uint32 channelSize = 0;
- switch (_cast->_version) {
- case kFileVer400:
+ if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
channelSize = kSprChannelSizeD4;
- break;
-
- case kFileVer500:
+ } else if (_cast->_version <= kFileVer500 && _cast->_version < kFileVer600) {
channelSize = kSprChannelSizeD5;
- break;
-
- default:
- warning("FilmLoopCastMember::writeSCVWResource: Writing Director Version 6 not supported yet");
+ } else {
+ warning("FilmLoopCastMember::writeSCVWResource: Writing Director Version 6+ not supported yet");
return;
}
// Go to the desired offset put in the memory map
writeStream->seek(offset);
+ debugC(5, kDebugSaving, "FilmLoopCastmember::writeSCVWResource: Saving FilmLoop 'SCVW' data at offset %d", offset);
+
uint32 filmloopSize = getSCVWResourceSize();
writeStream->writeUint32LE(MKTAG('S', 'C', 'V', 'W'));
writeStream->writeUint32LE(filmloopSize); // Size of the resource
- writeStream->writeUint32BE(0); // Putting zeroes instead of size currently
+ writeStream->writeUint32BE(filmloopSize);
- uint32 frameOffset = 16; // Should be greater than 16
+ uint32 frameOffset = 20; // Should be greater than 20
writeStream->writeUint32BE(frameOffset); // framesOffset
writeStream->seek(6, SEEK_CUR); // Ignored data
writeStream->writeUint16BE(channelSize);
@@ -795,7 +792,7 @@ void FilmLoopCastMember::writeSCVWResource(Common::MemoryWriteStream *writeStrea
// 1-20 bytes of Sprite data
for (FilmLoopFrame frame : _frames) {
- writeStream->writeUint16BE(0); // Frame Size
+ writeStream->writeUint16BE(frame.sprites.size() * (channelSize + 4) + 2); // Frame Size
for (auto it : frame.sprites) {
int channel = it._key;
@@ -827,15 +824,16 @@ void FilmLoopCastMember::writeSCVWResource(Common::MemoryWriteStream *writeStrea
dumpStream->write(writeStream, filmloopSize + 8);
writeStream->seek(currentPos);
- dumpFile("ConfigData", 0, MKTAG('V', 'W', 'C', 'F'), dumpData, filmloopSize);
- delete writeStream;
+ dumpFile("FilmLoopData", 0, MKTAG('V', 'W', 'C', 'F'), dumpData, filmloopSize);
+ free(dumpData);
+ delete dumpStream;
}
}
uint32 FilmLoopCastMember::getSCVWResourceSize() {
- // Size of the filmloop data: 4 bytes
+ // Size: 4 bytes
// frameoffset: 4 bytes
- // Header (Ignored data): 12 bytes
+ // Header (Ignored data): 16 bytes
uint32 channelSize = 0;
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
@@ -848,6 +846,8 @@ uint32 FilmLoopCastMember::getSCVWResourceSize() {
uint32 framesSize = 0;
for (FilmLoopFrame frame : _frames) {
+ // Frame size
+ framesSize += 2;
for (auto it : frame.sprites) {
// message width: 2 bytes
// order: 2 bytes
@@ -855,7 +855,7 @@ uint32 FilmLoopCastMember::getSCVWResourceSize() {
framesSize += 2 + 2 + channelSize;
}
}
- return 4 + 4 + 12 + framesSize;
+ return 4 + 4 + 16 + framesSize;
}
} // End of namespace Director
diff --git a/engines/director/castmember/palette.cpp b/engines/director/castmember/palette.cpp
index f0d1da21fe8..8aa8a364e32 100644
--- a/engines/director/castmember/palette.cpp
+++ b/engines/director/castmember/palette.cpp
@@ -191,6 +191,7 @@ void PaletteCastMember::writePaletteData(Common::MemoryWriteStream *writeStream,
writeStream->seek(currentPos);
dumpFile("PaletteData", _castId, MKTAG('C', 'L', 'U', 'T'), dumpData, castSize);
+ free(dumpData);
delete dumpStream;
}
}
diff --git a/engines/director/castmember/text.cpp b/engines/director/castmember/text.cpp
index 86dd8ce11fe..b7a45403e8c 100644
--- a/engines/director/castmember/text.cpp
+++ b/engines/director/castmember/text.cpp
@@ -1046,6 +1046,7 @@ uint32 TextCastMember::writeSTXTResource(Common::MemoryWriteStream *writeStream,
writeStream->seek(currentPos);
dumpFile("TextData", _castId, MKTAG('S', 'T', 'X', 'T'), dumpData, getSTXTResourceSize() + 8);
+ free(dumpData);
delete dumpStream;
}
diff --git a/engines/director/frame.cpp b/engines/director/frame.cpp
index 35073a21b3b..7a5fd652caa 100644
--- a/engines/director/frame.cpp
+++ b/engines/director/frame.cpp
@@ -594,6 +594,7 @@ void Frame::readSpriteD4(Common::MemoryReadStreamEndian &stream, uint16 offset,
}
void readSpriteDataD4(Common::SeekableReadStreamEndian &stream, Sprite &sprite, uint32 startPosition, uint32 finishPosition) {
+ debugC(8, kDebugLoading, "stream.pos(): %ld, startPosition: %d, finishPosition: %d", stream.pos(), startPosition, finishPosition);
while (stream.pos() < finishPosition) {
switch (stream.pos() - startPosition) {
case 0:
@@ -724,7 +725,7 @@ void writeSpriteDataD4(Common::MemoryWriteStream *writeStream, Sprite &sprite) {
// If the sprite is a puppet (controlled by lingo scripting)
// The rest of the data isn't read
if (sprite._puppet) {
- writeStream->write(0, 19);
+ writeStream->write(0, 19); // 1-19
} else {
writeStream->writeByte((byte) sprite._spriteType); // 1
writeStream->writeByte(sprite._foreColor); // 2
Commit: faafe78d54e00bdad45e6d49b5750c07069e8698
https://github.com/scummvm/scummvm/commit/faafe78d54e00bdad45e6d49b5750c07069e8698
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Fix pascal string reading/writing of CastInfo
The name, directory, filename and type need to be read by treating
them as pascal strings. So when writing them back, we have to skip
the first byte, accordingly the lengths also need to be increased
by 1
Changed paths:
engines/director/archive.cpp
engines/director/cast.cpp
engines/director/castmember/script.cpp
engines/director/lingo/lingo-builtins.cpp
engines/director/lingo/lingo-bytecode.cpp
diff --git a/engines/director/archive.cpp b/engines/director/archive.cpp
index db057225e2c..79632d26879 100644
--- a/engines/director/archive.cpp
+++ b/engines/director/archive.cpp
@@ -1266,7 +1266,6 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
}
}
- debug("What is the parentIndex = %d, cast->_castArraySize = %d", parentIndex, cast->_castArrayStart);
Resource parent = castResMap[parentIndex];
BitmapCastMember *target = (BitmapCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
@@ -1344,9 +1343,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
writeStream->seek(it->offset);
writeStream->writeUint32LE(it->tag);
writeStream->writeUint32LE(it->size);
- Common::SeekableReadStreamEndian *r = getResource(it->tag, it->index);
- r->hexdump(r->size());
- writeStream->writeStream(r);
+ writeStream->writeStream(getResource(it->tag, it->index));
break;
}
}
@@ -1356,6 +1353,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
}
Common::DumpFile out;
+ Common::Path dirname(path.getParent());
// Write the movie out, stored in dumpData
if (out.open(path, true)) {
@@ -1515,7 +1513,7 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
// RIFX // STXT
// KEY* // SCVW (filmloop)
// CAS* // VWCF
- // CASt //
+ // CASt
// Resources yet to be handled
// Any external file
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index 7843f1c335f..251d097d9d5 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -825,11 +825,9 @@ void Cast::saveCastData(Common::MemoryWriteStream *writeStream, Resource *res) {
// It is basically the index at which it occurs in the CAS* resource
// So, RIFXArchive::getResourceDetail will return the list of indexes of the 'CASt' resources in the mmap (read in the 'CAS*' resource)
// Whereas, in the _loadedCast, the key of these Cast members is given by _castId_
- // I need to call this just because I need the id
uint32 castSize = 0;
uint16 id = res->castId + _castArrayStart;
-
CastType type = kCastTypeAny;
if (_loadedCast->contains(id)) {
@@ -909,11 +907,11 @@ void Cast::writeCastInfo(Common::MemoryWriteStream *writeStream, uint32 castId)
break;
case 2:
- castInfo.strings[1].writeString(ci->name, false);
+ castInfo.strings[1].writeString(ci->name);
break;
case 3:
- castInfo.strings[2].writeString(ci->directory, false);
+ castInfo.strings[2].writeString(ci->directory);
break;
case 4:
@@ -1039,19 +1037,19 @@ uint32 Cast::getCastInfoStringLength(uint32 stringIndex, CastMemberInfo *ci) {
return 0;
case 1:
- return ci->script.size();
+ return ci->script.size(); // not pascal string
case 2:
- return ci->name.size();
+ return ci->name.size() ? ci->name.size() + 1 : 0; // pascal string
case 3:
- return ci->directory.size();
+ return ci->directory.size() ? ci->directory.size() + 1 : 0; // pascal string
case 4:
- return ci->fileName.size();
+ return ci->fileName.size() ? ci->fileName.size() + 1 : 0; // pascal string
case 5:
- return ci->type.size();
+ return ci->type.size() ? ci->type.size() + 1 : 0; // pascal string
case 6:
// Need a better check to see if the script edit info is valid
@@ -1820,16 +1818,16 @@ void Cast::loadCastInfo(Common::SeekableReadStreamEndian &stream, uint16 id) {
}
// fallthrough
case 5:
- ci->type = castInfo.strings[4].readString(false);
+ ci->type = castInfo.strings[4].readString();
// fallthrough
case 4:
- ci->fileName = castInfo.strings[3].readString(false);
+ ci->fileName = castInfo.strings[3].readString();
// fallthrough
case 3:
- ci->directory = castInfo.strings[2].readString(false);
+ ci->directory = castInfo.strings[2].readString();
// fallthrough
case 2:
- ci->name = castInfo.strings[1].readString(false);
+ ci->name = castInfo.strings[1].readString();
// fallthrough
case 1:
ci->script = castInfo.strings[0].readString(false);
diff --git a/engines/director/castmember/script.cpp b/engines/director/castmember/script.cpp
index bb26707b305..aebf7375510 100644
--- a/engines/director/castmember/script.cpp
+++ b/engines/director/castmember/script.cpp
@@ -155,10 +155,7 @@ uint32 ScriptCastMember::getCastDataSize() {
}
void ScriptCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
- debug("************* I NEED TO CHECK HERE *************");
- debug("what is the version: %d", _cast->_version);
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer600) {
- debug("I'm here");
writeStream->writeByte(0); // unknown
switch (_scriptType) {
diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index 3db5e377148..a94f0ff566c 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -1577,7 +1577,7 @@ void LB::b_save(int nargs) {
void LB::b_saveMovie(int nargs) {
Common::Path path;
if (nargs) {
- path = Common::Path(g_lingo->pop().asString());
+ path = Common::Path("./" + g_lingo->pop().asString());
}
g_director->getMainArchive()->writeToFile(path, g_director->getCurrentMovie());
}
diff --git a/engines/director/lingo/lingo-bytecode.cpp b/engines/director/lingo/lingo-bytecode.cpp
index adc6c7758c9..e5eef56b38c 100644
--- a/engines/director/lingo/lingo-bytecode.cpp
+++ b/engines/director/lingo/lingo-bytecode.cpp
@@ -1203,7 +1203,6 @@ ScriptContext *LingoCompiler::compileLingoV4(Common::SeekableReadStreamEndian &s
for (uint16 i = 0; i < constsCount; i++) {
Datum constant;
uint32 constType = 0;
- debug("what is the version %d", version);
if (version >= kFileVer500) {
constType = stream.readUint32();
} else {
Commit: 40ace668ad3a6e1d13d4a3ff25a2d61fd9539048
https://github.com/scummvm/scummvm/commit/40ace668ad3a6e1d13d4a3ff25a2d61fd9539048
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Fix `readHex` in STXT writing
Make it less error prone
Changed paths:
engines/director/archive.cpp
engines/director/cast.cpp
engines/director/castmember/bitmap.cpp
engines/director/castmember/text.cpp
diff --git a/engines/director/archive.cpp b/engines/director/archive.cpp
index 79632d26879..d7c97774065 100644
--- a/engines/director/archive.cpp
+++ b/engines/director/archive.cpp
@@ -20,7 +20,6 @@
*/
#include "common/config-manager.h"
-#include "common/hashmap.h"
#include "common/file.h"
#include "common/substream.h"
#include "common/macresman.h"
@@ -1222,7 +1221,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
ResourceMap castResMap = _types[MKTAG('C', 'A', 'S', 't')];
for (auto &it : builtResources) {
- debugC(5, kDebugSaving, "RIFXArchive::writeToFile: writing resource: index: %d, size: %d, tag = %s, offset = %d", it->index, it->size, tag2str(it->tag), it->offset);
+ debugC(5, kDebugSaving, "RIFXArchive::writeToFile: writing resource '%s': index: %d, size: %d, offset = %d", tag2str(it->tag), it->index, it->size, it->offset);
switch (it->tag) {
case MKTAG('R', 'I', 'F', 'X'):
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index 251d097d9d5..2ca26b8d377 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -597,12 +597,12 @@ void Cast::saveConfig(Common::MemoryWriteStream *writeStream, uint32 offset) {
}
if (debugChannelSet(7, kDebugSaving)) {
- // Adding +8 because the stream doesn't include the header and the entry for the size itself
- byte *dumpData = (byte *)calloc(configSize + 8, sizeof(byte));
+ // Adding +8 because the stream doesn't include the header and the entry for the size itself
+ byte *dumpData = (byte *)calloc(configSize + 8, sizeof(byte));
Common::SeekableMemoryWriteStream *dumpStream = new Common::SeekableMemoryWriteStream(dumpData, configSize + 8);
- uint32 currentPos = writeStream->pos();
+ int64 currentPos = writeStream->pos();
writeStream->seek(offset);
dumpStream->write(writeStream, configSize + 8);
writeStream->seek(currentPos);
diff --git a/engines/director/castmember/bitmap.cpp b/engines/director/castmember/bitmap.cpp
index 1b58d978647..3b1c0de5cf2 100644
--- a/engines/director/castmember/bitmap.cpp
+++ b/engines/director/castmember/bitmap.cpp
@@ -700,6 +700,8 @@ void BitmapCastMember::load() {
return;
}
+ // dumpFile("LoadedBitmap", _castId, MKTAG('B', 'I', 'T', 'D'), (byte *)img->getSurface()->getPixels(), img->getSurface()->h * img->getSurface()->w);
+
setPicture(*img, true);
if (ConfMan.getBool("dump_scripts")) {
@@ -1059,9 +1061,10 @@ uint32 BitmapCastMember::writeBITDResource(Common::MemoryWriteStream *writeStrea
offset = 0;
if (_bitsPerPixel == 8 && _picture->_surface.w < (int)(pixels.size() / _picture->_surface.h)) {
- offset = _picture->_surface.w % 2;
+ offset = (_pitch - _picture->_surface.w) % 2;
}
+ debugC(5, kDebugSaving, "BitmapCastMember::writeBITDResource: Saving 'BITD' Resource: bitsPerPixel: %d, castId: %d", _bitsPerPixel, _castId);
for (int y = 0; y < _picture->_surface.h; y++) {
for (int x = 0; x < _picture->_surface.w;) {
uint32 color = 0;
@@ -1069,23 +1072,23 @@ uint32 BitmapCastMember::writeBITDResource(Common::MemoryWriteStream *writeStrea
switch (_bitsPerPixel) {
case 1:
for (int c = 0; c < 8 && x < _picture->_surface.w; c++, x++) {
- color += *((byte *)_picture->_surface.getBasePtr(x, y)) & (1 << (7 - c));
+ color += (*((byte *)_picture->_surface.getBasePtr(x, y))) & (1 << (7 - c));
}
- pixels[(y * _pitch) + (x >> 3)] = color;
+ pixels[(y * _pitch) + ((x - 8) >> 3)] = color;
break;
case 2:
for (int c = 0; c < 4 && x < _picture->_surface.w; c++, x++) {
color += (*((byte *)_picture->_surface.getBasePtr(x, y)) & 0x3) << (2 * (3 - c));
}
- pixels[(y * _pitch) + (x >> 2)] = color;
+ pixels[(y * _pitch) + ((x - 4) >> 2)] = color;
break;
case 4:
for (int c = 0; c < 2 && x < _picture->_surface.w; c++, x++) {
- color = (*((byte *)_picture->_surface.getBasePtr(x, y)) & 0xF) << (4 * (1 - c));
+ color += (*((byte *)_picture->_surface.getBasePtr(x, y)) & 0xF) << (4 * (1 - c));
}
- pixels[(y * _pitch) + (x >> 1)] = color;
+ pixels[(y * _pitch) + ((x - 2) >> 1)] = color;
break;
case 8:
diff --git a/engines/director/castmember/text.cpp b/engines/director/castmember/text.cpp
index b7a45403e8c..c80e83c4509 100644
--- a/engines/director/castmember/text.cpp
+++ b/engines/director/castmember/text.cpp
@@ -1018,12 +1018,14 @@ uint32 TextCastMember::writeSTXTResource(Common::MemoryWriteStream *writeStream,
// Ignoring height and ascent for now from FontStyle
uint16 temp;
style.formatStartOffset = rIndex;
- Graphics::readHex(&style.fontId, _ftext.substr(it, 4).c_str(), 4);
- Graphics::readHex(&temp, _ftext.substr(it, 2).c_str(), 2);
- Graphics::readHex(&style.fontSize, _ftext.substr(it + 6, 4).c_str(), 4);
- Graphics::readHex(&style.r, _ftext.substr(it + 10, 4).c_str(), 4);
- Graphics::readHex(&style.g, _ftext.substr(it + 14, 4).c_str(), 4);
- Graphics::readHex(&style.b, _ftext.substr(it + 18, 4).c_str(), 4);
+ const Common::u32char_type_t *s = _ftext.substr(it, 22).c_str();
+
+ s = Graphics::readHex(&style.fontId, s, 4);
+ s = Graphics::readHex(&temp, s, 2);
+ s = Graphics::readHex(&style.fontSize, s, 4);
+ s = Graphics::readHex(&style.r, s, 4);
+ s = Graphics::readHex(&style.g, s, 4);
+ s = Graphics::readHex(&style.b, s, 4);
style.textSlant = temp;
style.write(writeStream);
Commit: d611befc5a066db179026d60912b7c0a2a72e853
https://github.com/scummvm/scummvm/commit/d611befc5a066db179026d60912b7c0a2a72e853
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Fix bitmap writing for 1/2/4bpp
Fix a bug where the last column of bits wasn't being written
Changed paths:
engines/director/castmember/bitmap.cpp
diff --git a/engines/director/castmember/bitmap.cpp b/engines/director/castmember/bitmap.cpp
index 3b1c0de5cf2..990d2de1263 100644
--- a/engines/director/castmember/bitmap.cpp
+++ b/engines/director/castmember/bitmap.cpp
@@ -1068,27 +1068,28 @@ uint32 BitmapCastMember::writeBITDResource(Common::MemoryWriteStream *writeStrea
for (int y = 0; y < _picture->_surface.h; y++) {
for (int x = 0; x < _picture->_surface.w;) {
uint32 color = 0;
+ int startX = x;
switch (_bitsPerPixel) {
case 1:
for (int c = 0; c < 8 && x < _picture->_surface.w; c++, x++) {
color += (*((byte *)_picture->_surface.getBasePtr(x, y))) & (1 << (7 - c));
}
- pixels[(y * _pitch) + ((x - 8) >> 3)] = color;
+ pixels[(y * _pitch) + (startX >> 3)] = color;
break;
case 2:
for (int c = 0; c < 4 && x < _picture->_surface.w; c++, x++) {
color += (*((byte *)_picture->_surface.getBasePtr(x, y)) & 0x3) << (2 * (3 - c));
}
- pixels[(y * _pitch) + ((x - 4) >> 2)] = color;
+ pixels[(y * _pitch) + (startX >> 2)] = color;
break;
case 4:
for (int c = 0; c < 2 && x < _picture->_surface.w; c++, x++) {
color += (*((byte *)_picture->_surface.getBasePtr(x, y)) & 0xF) << (4 * (1 - c));
}
- pixels[(y * _pitch) + ((x - 2) >> 1)] = color;
+ pixels[(y * _pitch) + (startX >> 1)] = color;
break;
case 8:
Commit: a7912823729b924c5c6e6095d736cb039bdf0a39
https://github.com/scummvm/scummvm/commit/a7912823729b924c5c6e6095d736cb039bdf0a39
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Fix writing duplicated 'CASt' resources
Changed paths:
engines/director/archive.cpp
engines/director/castmember/castmember.cpp
engines/director/castmember/filmloop.cpp
diff --git a/engines/director/archive.cpp b/engines/director/archive.cpp
index d7c97774065..79809a78b10 100644
--- a/engines/director/archive.cpp
+++ b/engines/director/archive.cpp
@@ -1400,7 +1400,7 @@ bool RIFXArchive::writeMemoryMap(Common::SeekableMemoryWriteStream *writeStream,
writeStream->writeUint32LE(0);
for (auto &it : resources) {
- debugC(3, kDebugSaving, "Writing RIFX Resource: tag: %s, size: %d, offset: %08x, flags: %x, unk1: %x, nextFreeResourceID: %d",
+ debugC(3, kDebugSaving, "RIFXArchive::writeMemoryMap: Memory map entry: '%s', size: %d, offset: %08x, flags: %x, unk1: %x, nextFreeResourceID: %d",
tag2str(it->tag), it->size, it->offset, it->flags, it->unk1, it->nextFreeResourceID);
// Write down the tag, the size and offset of the current resource
@@ -1495,10 +1495,16 @@ bool RIFXArchive::writeCast(Common::SeekableWriteStream *writeStream, uint32 off
}
}
+ debugC(5, kDebugSaving, "RIFXArchive::writeCast: Writing CAS* resource:");
+ debugCN(5, kDebugSaving, "'CASt' indexes: [");
for (uint32 i = 0; i <= maxCastId; i++) {
uint32 castIndex = castIndexes.getValOrDefault(i, 0);
- writeStream->writeUint32BE(castIndex);
+ if (castIndex) {
+ debugCN(5, kDebugSaving, "%d, ", castIndex);
+ writeStream->writeUint32BE(castIndex);
+ }
}
+ debugC(5, kDebugSaving, "\b\b]");
return true;
}
@@ -1514,33 +1520,11 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
// CAS* // VWCF
// CASt
- // Resources yet to be handled
- // Any external file
- // Script 'Lscr'
- // Script Names 'Lnam'
- // Lingo Context 'Lctx'
// Score 'SCVW'
// Rich Text 'RTE0', 'RTE1', 'RTE2'
// Sound ('snd ')
// 'SCVW' External Movies
- // Transitions and OLE are Director 5+, not yet loaded in ScummVM Director
-
- // Resources that are loaded but probably need not be handled
- // 'VWFM' Font Mapping
- // 'FXmp' Corss Platform Font Mapping
- // 'VWTL' Pattern Tiles
- // 'STR ' External Sound files
- // 'MooV' External Digital Video
- // 'XCOD' XObjects
- // 'Cinf' Cast Lib Info
- // 'VWCI' Cast Info (For what cast though?)
- // 'Sord' Score Order List Resource
- // 'PICT' Picture data
- // 'SCRF' External Cast Reference
- // 'Xtra' Xtras
- // 'THUM' Thumbnail
-
// First we'll have to update the _types table to include all the newly added
// cast members, and their 'CASt' resources
// Only handling movies with a single cast for now
@@ -1548,16 +1532,21 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
ResourceMap &castResMap = _types[MKTAG('C', 'A', 'S', 't')];
for (auto it : *(cast->_loadedCast)) {
- if (!castResMap.contains(it._value->_index)) {
- Resource *res = &castResMap[it._value->_index - cast->_castArrayStart];
+ if (it._value->_index == -1) {
+ // Assigning the next available index to the resource
+ Resource *res = &castResMap[_resources.size()];
res->tag = MKTAG('C', 'A', 'S', 't');
res->accessed = true;
- res->libResourceId = cast->_castLibID;
+
+ // Again considering here that there is only one CAS* resource, so the first resource will have our necessary libResourceId
+ res->libResourceId = _types[MKTAG('C', 'A', 'S', '*')].begin()->_value.libResourceId;
res->children = it._value->_children;
- res->index = _resources.size() + 1;
+ res->index = _resources.size();
+ res->castId = it._value->getID() - cast->_castArrayStart;
+ debug("What is res->castId = %d", it._value->getID());
for (auto child : it._value->_children) {
- _keyData[child.tag][res->index].push_back(child.index); // Indices are a huge problem, need to figure out them first
+ _keyData[child.tag][res->index].push_back(child.index);
_keyTableUsedCount += 1;
_keyTableEntryCount += 1;
}
diff --git a/engines/director/castmember/castmember.cpp b/engines/director/castmember/castmember.cpp
index d34ed50940f..f9f2466daa5 100644
--- a/engines/director/castmember/castmember.cpp
+++ b/engines/director/castmember/castmember.cpp
@@ -50,6 +50,7 @@ CastMember::CastMember(Cast *cast, uint16 castId, Common::SeekableReadStreamEndi
_widget = nullptr;
_erase = false;
+ _index = -1;
}
CastMember::CastMember(Cast *cast, uint16 castId) : Object<CastMember>("CastMember") {
@@ -69,6 +70,7 @@ CastMember::CastMember(Cast *cast, uint16 castId) : Object<CastMember>("CastMemb
_widget = nullptr;
_erase = false;
+ _index = -1;
}
CastMember *CastMember::duplicate(Cast *cast, uint16 castId) {
diff --git a/engines/director/castmember/filmloop.cpp b/engines/director/castmember/filmloop.cpp
index 163ab37b564..ddfd81518d5 100644
--- a/engines/director/castmember/filmloop.cpp
+++ b/engines/director/castmember/filmloop.cpp
@@ -88,7 +88,6 @@ FilmLoopCastMember::FilmLoopCastMember(Cast *cast, uint16 castId, FilmLoopCastMe
_center = source._center;
_frames = source._frames;
_subchannels = source._subchannels;
- _index = -1;
}
FilmLoopCastMember::~FilmLoopCastMember() {
@@ -304,8 +303,8 @@ void FilmLoopCastMember::loadFilmLoopDataD4(Common::SeekableReadStreamEndian &st
continue;
}
frameSize -= 2;
- debugC(2, kDebugLoading, "loadFilmLoopDataD4: Frame entry: %d", frameSize);
if (debugChannelSet(8, kDebugLoading)) {
+ debugC(8, kDebugLoading, "loadFilmLoopDataD4: Frame entry: %d", frameSize);
stream.hexdump(frameSize);
}
@@ -318,7 +317,7 @@ void FilmLoopCastMember::loadFilmLoopDataD4(Common::SeekableReadStreamEndian &st
int channelOffset = order % channelSize;
int offset = order;
- debugC(2, kDebugLoading, "loadFilmLoopDataD4: Message: msgWidth %d, order: %d, channel %d, channelOffset %d", msgWidth, order, channel, channelOffset);
+ debugC(8, kDebugLoading, "loadFilmLoopDataD4: Message: msgWidth %d, order: %d, channel %d, channelOffset %d", msgWidth, order, channel, channelOffset);
if (debugChannelSet(8, kDebugLoading)) {
stream.hexdump(msgWidth);
}
@@ -351,7 +350,7 @@ void FilmLoopCastMember::loadFilmLoopDataD4(Common::SeekableReadStreamEndian &st
}
for (auto &s : newFrame.sprites) {
- debugC(2, kDebugLoading, "loadFilmLoopDataD4: Sprite: channel %d, castId %s, bbox %d %d %d %d", s._key,
+ debugC(8, kDebugLoading, "loadFilmLoopDataD4: Sprite: channel %d, castId %s, bbox %d %d %d %d", s._key,
s._value._castId.asString().c_str(), s._value._startPoint.x, s._value._startPoint.y,
s._value._width, s._value._height);
Commit: 5174de231cf6e0640178caed8cade70be5cc587f
https://github.com/scummvm/scummvm/commit/5174de231cf6e0640178caed8cade70be5cc587f
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Save D5 Filmloop Sprite Data
Changed paths:
engines/director/archive.cpp
engines/director/castmember/filmloop.cpp
engines/director/frame.cpp
engines/director/frame.h
engines/director/images.cpp
diff --git a/engines/director/archive.cpp b/engines/director/archive.cpp
index 79809a78b10..3a6a3bdd683 100644
--- a/engines/director/archive.cpp
+++ b/engines/director/archive.cpp
@@ -1543,7 +1543,6 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
res->children = it._value->_children;
res->index = _resources.size();
res->castId = it._value->getID() - cast->_castArrayStart;
- debug("What is res->castId = %d", it._value->getID());
for (auto child : it._value->_children) {
_keyData[child.tag][res->index].push_back(child.index);
diff --git a/engines/director/castmember/filmloop.cpp b/engines/director/castmember/filmloop.cpp
index ddfd81518d5..8fea58ab6eb 100644
--- a/engines/director/castmember/filmloop.cpp
+++ b/engines/director/castmember/filmloop.cpp
@@ -88,6 +88,7 @@ FilmLoopCastMember::FilmLoopCastMember(Cast *cast, uint16 castId, FilmLoopCastMe
_center = source._center;
_frames = source._frames;
_subchannels = source._subchannels;
+ _looping = source._looping;
}
FilmLoopCastMember::~FilmLoopCastMember() {
@@ -754,7 +755,7 @@ void FilmLoopCastMember::writeSCVWResource(Common::MemoryWriteStream *writeStrea
uint32 channelSize = 0;
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
channelSize = kSprChannelSizeD4;
- } else if (_cast->_version <= kFileVer500 && _cast->_version < kFileVer600) {
+ } else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
channelSize = kSprChannelSizeD5;
} else {
warning("FilmLoopCastMember::writeSCVWResource: Writing Director Version 6+ not supported yet");
@@ -807,7 +808,11 @@ void FilmLoopCastMember::writeSCVWResource(Common::MemoryWriteStream *writeStrea
Sprite sprite = it._value;
- writeSpriteDataD4(writeStream, sprite);
+ if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
+ writeSpriteDataD4(writeStream, sprite);
+ } else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
+ writeSpriteDataD5(writeStream, sprite);
+ }
}
}
@@ -823,7 +828,7 @@ void FilmLoopCastMember::writeSCVWResource(Common::MemoryWriteStream *writeStrea
dumpStream->write(writeStream, filmloopSize + 8);
writeStream->seek(currentPos);
- dumpFile("FilmLoopData", 0, MKTAG('V', 'W', 'C', 'F'), dumpData, filmloopSize);
+ dumpFile("FilmLoopData", 0, MKTAG('V', 'W', 'C', 'F'), dumpData, filmloopSize);
free(dumpData);
delete dumpStream;
}
diff --git a/engines/director/frame.cpp b/engines/director/frame.cpp
index 7a5fd652caa..bee2bbf6998 100644
--- a/engines/director/frame.cpp
+++ b/engines/director/frame.cpp
@@ -1063,6 +1063,34 @@ void readSpriteDataD5(Common::SeekableReadStreamEndian &stream, Sprite &sprite,
}
+void writeSpriteDataD5(Common::MemoryWriteStream *writeStream, Sprite &sprite) {
+ // Writing 20 bytes of sprite data
+ // The original data for a certain sprite might be less
+ writeStream->writeByte(sprite._spriteType); // 0
+
+ // If the sprite is a puppet (controlled by lingo scripting)
+ // The rest of the data isn't read
+ if (sprite._puppet) {
+ writeStream->write(0, 19); // 1-19
+ } else {
+ writeStream->writeByte(sprite._inkData); // 1
+ writeStream->writeSint16BE(sprite._castId.castLib); // 2, 3
+ writeStream->writeUint16BE(sprite._castId.member); // 4, 5
+ writeStream->writeSint16BE(sprite._scriptId.castLib); // 6, 7
+ writeStream->writeUint16BE(sprite._scriptId.member); // 8, 9
+ writeStream->writeByte(sprite._foreColor); // 10
+ writeStream->writeByte(sprite._backColor); // 11
+ writeStream->writeUint16BE(sprite._startPoint.y); // 12, 13
+ writeStream->writeUint16BE(sprite._startPoint.x); // 14, 15
+ writeStream->writeUint16BE(sprite._height); // 16, 17
+ writeStream->writeUint16BE(sprite._width); // 18, 19
+ writeStream->writeByte(sprite._colorcode); // 20
+ writeStream->writeByte(sprite._blendAmount); // 21
+ writeStream->writeByte(sprite._thickness); // 22
+ writeStream->writeByte(0); // 23, unused
+ }
+}
+
/**************************
*
* D6 Loading
diff --git a/engines/director/frame.h b/engines/director/frame.h
index b9d9632f376..7ca6cdfe8d8 100644
--- a/engines/director/frame.h
+++ b/engines/director/frame.h
@@ -201,6 +201,7 @@ void readSpriteDataD5(Common::SeekableReadStreamEndian &stream, Sprite &sprite,
void readSpriteDataD6(Common::SeekableReadStreamEndian &stream, Sprite &sprite, uint32 startPosition, uint32 finishPosition);
void writeSpriteDataD4(Common::MemoryWriteStream *writeStream, Sprite &sprite);
+void writeSpriteDataD5(Common::MemoryWriteStream *writeStream, Sprite &sprite);
} // End of namespace Director
diff --git a/engines/director/images.cpp b/engines/director/images.cpp
index 7bb95e7c12e..804d02ed2b2 100644
--- a/engines/director/images.cpp
+++ b/engines/director/images.cpp
@@ -20,9 +20,6 @@
*/
#include "common/substream.h"
-#include "common/macresman.h"
-#include "common/memstream.h"
-#include "common/file.h"
#include "graphics/macgui/macwindowmanager.h"
#include "graphics/pixelformat.h"
#include "image/codecs/bmp_raw.h"
Commit: ccc93bd0f0a8c001a237c60516d72d231e70984c
https://github.com/scummvm/scummvm/commit/ccc93bd0f0a8c001a237c60516d72d231e70984c
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Write D4/D5 score main channels
Changed paths:
engines/director/frame.cpp
engines/director/frame.h
engines/director/score.cpp
engines/director/score.h
diff --git a/engines/director/frame.cpp b/engines/director/frame.cpp
index bee2bbf6998..d4e712ff4ab 100644
--- a/engines/director/frame.cpp
+++ b/engines/director/frame.cpp
@@ -569,6 +569,48 @@ void Frame::readMainChannelsD4(Common::MemoryReadStreamEndian &stream, uint16 of
_mainChannels.transDuration = CLIP<uint16>(_mainChannels.transDuration, 0, 32000); // restrict to 32 secs
}
+void Frame::writeMainChannelsD4(Common::MemoryWriteStream *writeStream) {
+ writeStream->writeByte(0); // Unknown: Sound/Tempo/Transition // 0
+
+ writeStream->writeByte(_mainChannels.soundType1); // 1
+ writeStream->writeByte((_mainChannels.transArea ? 0x80 : 0x00) | (_mainChannels.transDuration / 250) & 0x7F); // 2
+ writeStream->writeByte(_mainChannels.transChunkSize); // 3
+ writeStream->writeByte(_mainChannels.tempo); // 4
+ writeStream->writeByte(_mainChannels.transType); // 5
+
+ writeStream->writeUint16BE(_mainChannels.sound1.member); // 6, 7
+ writeStream->writeUint16BE(_mainChannels.sound2.member); // 8, 9
+
+ writeStream->writeByte(_mainChannels.soundType2); // 10
+ writeStream->writeByte(_mainChannels.skipFrameFlag); // 11
+ writeStream->writeByte(_mainChannels.blend); // 12
+ writeStream->writeByte(_mainChannels.colorTempo); // 13
+ writeStream->writeByte(_mainChannels.colorSound1); // 14
+ writeStream->writeByte(_mainChannels.colorSound2); // 15
+ writeStream->writeUint16BE(_mainChannels.actionId.member); // 16, 17
+ writeStream->writeByte(_mainChannels.colorScript); // 18
+ writeStream->writeByte(_mainChannels.colorTrans); // 19
+
+ // palette
+ writeStream->writeSint16BE(_mainChannels.palette.paletteId.member); // 20, 21
+ writeStream->writeByte(_mainChannels.palette.firstColor ^ 0x80); // 22
+ writeStream->writeByte(_mainChannels.palette.lastColor ^ 0x80); // 23
+ writeStream->writeByte(_mainChannels.palette.flags); // 24
+ writeStream->writeByte(_mainChannels.palette.speed); // 25
+ writeStream->writeUint16BE(_mainChannels.palette.frameCount); // 26, 27
+ writeStream->writeUint16BE(_mainChannels.palette.cycleCount); // 28, 29
+ writeStream->writeByte(_mainChannels.palette.fade); // 30
+ writeStream->writeByte(_mainChannels.palette.delay); // 31
+ writeStream->writeByte(_mainChannels.palette.style); // 32
+
+ writeStream->writeByte(0); // Unknown // 33
+ writeStream->writeUint16BE(0); // Unknown // 34, 35
+ writeStream->writeUint16BE(0); // Unknown // 36, 37
+
+ writeStream->writeByte(_mainChannels.palette.colorCode); // 38
+ writeStream->writeByte(0); // Unknown // 39
+}
+
void Frame::readSpriteD4(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size) {
uint16 spritePosition = (offset - kMainChannelSizeD4) / kSprChannelSizeD4;
uint16 spriteStart = spritePosition * kSprChannelSizeD4 + kMainChannelSizeD4;
@@ -904,6 +946,38 @@ void Frame::readMainChannelsD5(Common::MemoryReadStreamEndian &stream, uint16 of
_mainChannels.transDuration = CLIP<uint16>(_mainChannels.transDuration, 0, 32000); // restrict to 32 secs
}
+void Frame::writeMainChannelsD5(Common::MemoryWriteStream *writeStream) {
+ writeStream->writeUint16BE(_mainChannels.actionId.castLib); // 0, 1
+ writeStream->writeUint16BE(_mainChannels.actionId.member); // 2, 3
+ writeStream->writeUint16BE(_mainChannels.sound1.castLib); // 4, 5
+ writeStream->writeUint16BE(_mainChannels.sound1.member); // 6, 7
+ writeStream->writeUint16BE(_mainChannels.sound2.castLib); // 8, 9
+ writeStream->writeUint16BE(_mainChannels.sound2.member); // 10, 11
+ writeStream->writeUint16BE(_mainChannels.trans.member); // 12, 13
+ writeStream->writeUint16BE(_mainChannels.trans.member); // 14, 15
+
+ writeStream->writeUint16BE(0); // Unknown // 16, 17
+ writeStream->writeUint16BE(0); // Unknown // 18, 19
+ writeStream->writeByte(0); // Unknown: Sound/Tempo/Transition // 20
+
+ writeStream->writeByte(_mainChannels.tempo); // 21
+ writeStream->writeUint16BE(0); // Unknown // 22, 23
+
+ writeStream->writeSint16BE(_mainChannels.palette.paletteId.castLib); // 24, 25
+ writeStream->writeSint16BE(_mainChannels.palette.paletteId.member); // 26, 27
+ writeStream->writeByte(_mainChannels.palette.speed); // 28
+ writeStream->writeByte(_mainChannels.palette.flags); // 29
+ writeStream->writeByte(_mainChannels.palette.firstColor ^ 0x80); // 30
+ writeStream->writeByte(_mainChannels.palette.lastColor ^ 0x80); // 31
+
+ writeStream->writeUint16BE(_mainChannels.palette.frameCount); // 32, 33
+ writeStream->writeUint16BE(_mainChannels.palette.cycleCount); // 34, 35
+
+ writeStream->writeUint16BE(0); // Unknown // 36, 37
+ writeStream->writeUint16BE(0); // Unknown // 38, 39
+ writeStream->writeUint64BE(0); // Unknown // 40, 41, 42, 43, 44, 45, 46, 47
+}
+
void Frame::readSpriteD5(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size) {
uint16 spritePosition = (offset - kMainChannelSizeD5) / kSprChannelSizeD5;
uint16 spriteStart = spritePosition * kSprChannelSizeD5 + kMainChannelSizeD5;
diff --git a/engines/director/frame.h b/engines/director/frame.h
index 7ca6cdfe8d8..8fb8a7a1cfc 100644
--- a/engines/director/frame.h
+++ b/engines/director/frame.h
@@ -175,10 +175,14 @@ private:
void readSpriteD4(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size);
void readMainChannelsD4(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size);
+ void writeMainChannelsD4(Common::MemoryWriteStream *writeStream);
+
void readChannelD5(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size);
void readSpriteD5(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size);
void readMainChannelsD5(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size);
+ void writeMainChannelsD5(Common::MemoryWriteStream *writeStream);
+
void readChannelD6(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size);
void readSpriteD6(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size);
void readMainChannelsD6(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size);
diff --git a/engines/director/score.cpp b/engines/director/score.cpp
index 20615b0b64f..34cc3240e8e 100644
--- a/engines/director/score.cpp
+++ b/engines/director/score.cpp
@@ -1744,7 +1744,7 @@ void Score::loadFrames(Common::SeekableReadStreamEndian &stream, uint16 version)
uint32 frame1Offset = _framesStream->readUint32();
/* uint32 numOfFrames = */ _framesStream->readUint32();
_framesVersion = _framesStream->readUint16();
- uint16 spriteRecordSize = _framesStream->readUint16();
+ _spriteRecordSize = _framesStream->readUint16();
_numChannels = _framesStream->readUint16();
if (_framesVersion > 13) {
@@ -1759,7 +1759,7 @@ void Score::loadFrames(Common::SeekableReadStreamEndian &stream, uint16 version)
}
debugC(1, kDebugLoading, "Score::loadFrames(): frame1Offset: 0x%x, version: %d, spriteRecordSize: 0x%x, numChannels: %d, numChannelsDisplayed: %d",
- frame1Offset, _framesVersion, spriteRecordSize, _numChannels, _numChannelsDisplayed);
+ frame1Offset, _framesVersion, _spriteRecordSize, _numChannels, _numChannelsDisplayed);
// Unknown, some bytes - constant (refer to contuinity).
} else {
error("STUB: Score::loadFrames(): score not yet supported for version %d", version);
@@ -2066,4 +2066,36 @@ Common::String Score::formatChannelInfo() {
}
+void Score::writeVWSCResource(Common::MemoryWriteStream *writeStream, uint32 offset) {
+ writeStream->seek(offset);
+
+ uint32 scoreSize = getVWSCResourceSize();
+
+ writeStream->writeUint32LE(MKTAG('V', 'W', 'S', 'C'));
+ writeStream->writeUint32LE(scoreSize);
+
+ // The format of a score is similar to that of FilmLoopCastMember, or rather its vice-verca
+ writeStream->writeUint32BE(scoreSize);
+
+ // Headers
+ writeStream->writeUint32BE(0); // frame10Offset
+ writeStream->writeUint32BE(0); // numOfFrames
+ writeStream->writeUint16BE(_framesVersion);
+ writeStream->writeUint16BE(_spriteRecordSize);
+ writeStream->writeUint16BE(_numChannels);
+
+ writeStream->writeUint16BE(_numChannelsDisplayed); // In case _framesVersion > 13, we ignore this while loading
+
+ // until there are no more frames
+ // size of the frame ->
+ // until there are no more channels in the frame
+ // width of message (One chunk of data) (This is the size of data for the sprite that needs to be read) ->
+ // order of message (this order tells us the channel we're reading) ->
+ // 1-20 bytes of Sprite data
+
+ for (Frame *frame : _scoreCache) {
+
+ }
+}
+
} // End of namespace Director
diff --git a/engines/director/score.h b/engines/director/score.h
index ce182d6fe8e..c8d4b17348f 100644
--- a/engines/director/score.h
+++ b/engines/director/score.h
@@ -73,6 +73,9 @@ public:
void updateFrame(Frame *frame);
Frame *getFrameData(int frameNum);
+ void writeVWSCResource(Common::MemoryWriteStream *writeStream, uint32 offset);
+ uint32 getVWSCResourceSize();
+
void loadLabels(Common::SeekableReadStreamEndian &stream);
void loadActions(Common::SeekableReadStreamEndian &stream);
void loadSampleSounds(uint type);
@@ -198,6 +201,9 @@ public:
int _numChannelsDisplayed;
+ /* Data to be saved */
+ uint16 _spriteRecordSize;
+
private:
DirectorEngine *_vm;
Lingo *_lingo;
Commit: 650c28df21ae4f436ed26a837b360131c967cdff
https://github.com/scummvm/scummvm/commit/650c28df21ae4f436ed26a837b360131c967cdff
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Write Score ('VWSC' resource) while saving movies
Changed paths:
engines/director/archive.cpp
engines/director/castmember/filmloop.cpp
engines/director/frame.cpp
engines/director/frame.h
engines/director/score.cpp
engines/director/score.h
diff --git a/engines/director/archive.cpp b/engines/director/archive.cpp
index 3a6a3bdd683..d74e7ed624c 100644
--- a/engines/director/archive.cpp
+++ b/engines/director/archive.cpp
@@ -27,6 +27,7 @@
#include "director/director.h"
#include "director/archive.h"
+#include "director/score.h"
#include "director/movie.h"
#include "director/cast.h"
#include "director/window.h"
@@ -1338,6 +1339,10 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
}
break;
+ case MKTAG('V', 'W', 'S', 'C'):
+ movie->getScore()->writeVWSCResource(writeStream, it->offset);
+ break;
+
default:
writeStream->seek(it->offset);
writeStream->writeUint32LE(it->tag);
@@ -1759,6 +1764,13 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
}
break;
+ case MKTAG('V', 'W', 'S', 'C'):
+ resSize = movie->getScore()->getVWSCResourceSize();
+ it->size = resSize;
+ it->offset = currentSize;
+ currentSize += resSize + 8; // The size doesn't include the header and the size entry
+ break;
+
case MKTAG('f', 'r', 'e', 'e'):
case MKTAG('j', 'u', 'n', 'k'):
// These resources do not hold any data
diff --git a/engines/director/castmember/filmloop.cpp b/engines/director/castmember/filmloop.cpp
index 8fea58ab6eb..360aaeeb39d 100644
--- a/engines/director/castmember/filmloop.cpp
+++ b/engines/director/castmember/filmloop.cpp
@@ -835,10 +835,6 @@ void FilmLoopCastMember::writeSCVWResource(Common::MemoryWriteStream *writeStrea
}
uint32 FilmLoopCastMember::getSCVWResourceSize() {
- // Size: 4 bytes
- // frameoffset: 4 bytes
- // Header (Ignored data): 16 bytes
-
uint32 channelSize = 0;
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
channelSize = kSprChannelSizeD4;
@@ -859,6 +855,10 @@ uint32 FilmLoopCastMember::getSCVWResourceSize() {
framesSize += 2 + 2 + channelSize;
}
}
+
+ // Size: 4 bytes
+ // frameoffset: 4 bytes
+ // Header (Ignored data): 16 bytes
return 4 + 4 + 16 + framesSize;
}
diff --git a/engines/director/frame.cpp b/engines/director/frame.cpp
index d4e712ff4ab..82c6b94b579 100644
--- a/engines/director/frame.cpp
+++ b/engines/director/frame.cpp
@@ -110,6 +110,17 @@ void Frame::readChannel(Common::MemoryReadStreamEndian &stream, uint16 offset, u
}
}
+void Frame::writeMainChannels(Common::MemoryWriteStream *writeStream, uint16 version) {
+ debugC(6, kDebugLoading, "Frame::writeChannel: writing main channels for version %d", version);
+
+ if (version >= kFileVer400 && version < kFileVer500) {
+ writeMainChannelsD4(writeStream);
+ } else if (version >= kFileVer500 && version < kFileVer600) {
+ writeMainChannelsD5(writeStream);
+ } else {
+ warning("Frame::writeChannel(): Unsupported Director version: %d", version);
+ }
+}
/**************************
*
* D2 Loading
@@ -573,7 +584,7 @@ void Frame::writeMainChannelsD4(Common::MemoryWriteStream *writeStream) {
writeStream->writeByte(0); // Unknown: Sound/Tempo/Transition // 0
writeStream->writeByte(_mainChannels.soundType1); // 1
- writeStream->writeByte((_mainChannels.transArea ? 0x80 : 0x00) | (_mainChannels.transDuration / 250) & 0x7F); // 2
+ writeStream->writeByte((_mainChannels.transArea ? 0x80 : 0x00) | ((_mainChannels.transDuration / 250) & 0x7F)); // 2
writeStream->writeByte(_mainChannels.transChunkSize); // 3
writeStream->writeByte(_mainChannels.tempo); // 4
writeStream->writeByte(_mainChannels.transType); // 5
diff --git a/engines/director/frame.h b/engines/director/frame.h
index 8fb8a7a1cfc..910d8c2c80e 100644
--- a/engines/director/frame.h
+++ b/engines/director/frame.h
@@ -160,6 +160,7 @@ public:
Score *getScore() const { return _score; }
void readChannel(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size, uint16 version);
+ void writeMainChannels(Common::MemoryWriteStream *writeStream, uint16 version);
void executeImmediateScripts();
diff --git a/engines/director/score.cpp b/engines/director/score.cpp
index 34cc3240e8e..af69706c62a 100644
--- a/engines/director/score.cpp
+++ b/engines/director/score.cpp
@@ -2067,6 +2067,19 @@ Common::String Score::formatChannelInfo() {
}
void Score::writeVWSCResource(Common::MemoryWriteStream *writeStream, uint32 offset) {
+ uint32 channelSize = 0;
+ uint32 mainChannelSize = 0;
+ if (_version >= kFileVer400 && _version < kFileVer500) {
+ channelSize = kSprChannelSizeD4;
+ mainChannelSize = kMainChannelSizeD4;
+ } else if (_version >= kFileVer500 && _version < kFileVer600) {
+ channelSize = kSprChannelSizeD5;
+ mainChannelSize = kMainChannelSizeD5;
+ } else {
+ warning("FilmLoopCastMember::writeSCVWResource: Writing Director Version 6+ not supported yet");
+ return;
+ }
+
writeStream->seek(offset);
uint32 scoreSize = getVWSCResourceSize();
@@ -2092,10 +2105,70 @@ void Score::writeVWSCResource(Common::MemoryWriteStream *writeStream, uint32 off
// width of message (One chunk of data) (This is the size of data for the sprite that needs to be read) ->
// order of message (this order tells us the channel we're reading) ->
// 1-20 bytes of Sprite data
+
+ for (uint it = 0; it < _scoreCache.size(); it ++) {
+ Frame frame = *(_scoreCache[it]);
+
+ // The frame._sprites had an empty 0th index
+ writeStream->writeUint16BE((mainChannelSize + 4) + (frame._sprites.size() - 1) * (channelSize + 4) + 2); // frameSize
+
+ // It's not necessary to make this code this modular, but for the sake of paralleling loading code
+ // dividing this writing function into different parts
+ // writeFrame is parallel to the combination of (loadFrame() and readOneFrame())
+ writeFrame(writeStream, frame, channelSize, mainChannelSize);
+ }
+}
+
+void Score::writeFrame(Common::MemoryWriteStream *writeStream, Frame frame, uint32 channelSize, uint32 mainChannelSize) {
+ // The first sprite is the main channel
+ writeStream->writeUint16BE(mainChannelSize);
+ // the offset for the main channel should be 0 since we're writing all the 40/48 bytes
+ writeStream->writeUint16BE(0);
+ frame.writeMainChannels(writeStream, _version);
- for (Frame *frame : _scoreCache) {
-
+ for (uint it = 1; it < frame._sprites.size(); it++) {
+ Sprite sprite = *(frame._sprites[it]);
+
+ writeStream->writeUint16BE(channelSize);
+ writeStream->writeUint16BE(((it - 1) * channelSize) + mainChannelSize);
+
+ if (_version >= kFileVer400 && _version < kFileVer500) {
+ writeSpriteDataD4(writeStream, sprite);
+ } else if (_version >= kFileVer500 && _version < kFileVer600) {
+ writeSpriteDataD5(writeStream, sprite);
+ }
+ }
+}
+
+uint32 Score::getVWSCResourceSize() {
+ uint32 channelSize = 0;
+ uint32 mainChannelSize = 0;
+ if (_version >= kFileVer400 && _version < kFileVer500) {
+ channelSize = kSprChannelSizeD4;
+ mainChannelSize = kMainChannelSizeD4;
+ } else if (_version >= kFileVer500) {
+ channelSize = kSprChannelSizeD5;
+ mainChannelSize = kMainChannelSizeD5;
+ } else {
+ warning("FilmLoopCastMember::getSCVWResourceSize: Director version unsupported");
+ }
+
+ uint32 framesSize = 0;
+ for (Frame *frame : _scoreCache) {
+ // Frame size
+ framesSize += 2;
+
+ framesSize += (2 + 2 + mainChannelSize);
+ for (uint it = 1; it < frame->_sprites.size(); it++) {
+ // message width: 2 bytes
+ // order: 2 bytes
+ // Sprite data: 20 bytes
+ framesSize += 2 + 2 + channelSize;
+ }
}
+ // _firstFramePosition is the header size
+ // header + frames (main channel + sprite channels) (20 bytes)
+ return _firstFramePosition + framesSize;
}
} // End of namespace Director
diff --git a/engines/director/score.h b/engines/director/score.h
index c8d4b17348f..1248d0baf2e 100644
--- a/engines/director/score.h
+++ b/engines/director/score.h
@@ -36,6 +36,7 @@ namespace Common {
class ReadStreamEndian;
class MemoryReadStreamEndian;
class SeekableReadStreamEndian;
+ class MemoryWriteStream;
}
namespace Director {
@@ -154,6 +155,8 @@ private:
bool processImmediateFrameScript(Common::String s, int id);
bool processFrozenScripts(bool recursion = false, int count = 0);
+ void writeFrame(Common::MemoryWriteStream *writeStream, Frame frame, uint32 channelSize, uint32 mainChannelSize);
+
public:
Common::Array<Channel *> _channels;
Common::SortedArray<Label *> *_labels;
Commit: 9d07259b02fafab8574a00077f3a8c4f78d7ba0d
https://github.com/scummvm/scummvm/commit/9d07259b02fafab8574a00077f3a8c4f78d7ba0d
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Erase overwritten cast members while saving
When a cast member is replaced from the cast, the previous cast
member details need to be removed from the `_resources` array
while saving and be replaced with the new details
Changed paths:
engines/director/archive.cpp
engines/director/cast.cpp
diff --git a/engines/director/archive.cpp b/engines/director/archive.cpp
index d74e7ed624c..411cd8e8361 100644
--- a/engines/director/archive.cpp
+++ b/engines/director/archive.cpp
@@ -1524,11 +1524,8 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
// KEY* // SCVW (filmloop)
// CAS* // VWCF
// CASt
-
- // Score 'SCVW'
- // Rich Text 'RTE0', 'RTE1', 'RTE2'
- // Sound ('snd ')
- // 'SCVW' External Movies
+ // SCVW
+ // 'RTE0', 'RTE1', 'RTE2'
// First we'll have to update the _types table to include all the newly added
// cast members, and their 'CASt' resources
@@ -1538,25 +1535,55 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
for (auto it : *(cast->_loadedCast)) {
if (it._value->_index == -1) {
- // Assigning the next available index to the resource
- Resource *res = &castResMap[_resources.size()];
- res->tag = MKTAG('C', 'A', 'S', 't');
- res->accessed = true;
-
- // Again considering here that there is only one CAS* resource, so the first resource will have our necessary libResourceId
- res->libResourceId = _types[MKTAG('C', 'A', 'S', '*')].begin()->_value.libResourceId;
- res->children = it._value->_children;
- res->index = _resources.size();
- res->castId = it._value->getID() - cast->_castArrayStart;
-
- for (auto child : it._value->_children) {
- _keyData[child.tag][res->index].push_back(child.index);
- _keyTableUsedCount += 1;
- _keyTableEntryCount += 1;
+ Resource *res = nullptr;
+ uint16 targetCastId = it._value->getID() - cast->_castArrayStart;
+
+ // Checking if the castId already exists in the CASt resources
+ for (auto castRes : castResMap) {
+ if (castRes._value.castId == targetCastId) {
+ res = &castRes._value;
+ }
}
- _resources.push_back(res);
- debugC(5, kDebugSaving, "RIFXArchive::rebuildResources(): new 'CASt' resource added");
+ if (!res) {
+ // If the castId is new, create a new resource
+ // Assigning the next available index to the resource
+ res = &castResMap[_resources.size()];
+ res->tag = MKTAG('C', 'A', 'S', 't');
+ res->accessed = true;
+
+ // Again considering here that there is only one CAS* resource, so the first resource will have our necessary libResourceId
+ res->libResourceId = _types[MKTAG('C', 'A', 'S', '*')].begin()->_value.libResourceId;
+ res->children = it._value->_children;
+ res->index = _resources.size();
+ res->castId = it._value->getID() - cast->_castArrayStart;
+
+ for (auto child : it._value->_children) {
+ _keyData[child.tag][res->index].push_back(child.index);
+ _keyTableUsedCount += 1;
+ _keyTableEntryCount += 1;
+ }
+ _resources.push_back(res);
+
+ debugC(5, kDebugSaving, "RIFXArchive::rebuildResources(): new 'CASt' resource added");
+ } else {
+ // The castId is not new, overwrite the key data of the previous cast
+ for (auto child : res->children) {
+ // Remove the data of the previous (removed) 'CASt'
+ int8 count = _keyData[child.tag][res->index].size();
+ _keyData[child.tag][res->index].clear();
+ _keyTableUsedCount -= count;
+ _keyTableEntryCount -= count;
+ }
+
+ res->children = it._value->_children;
+
+ for (auto child : res->children) {
+ _keyData[child.tag][res->index].push_back(child.index);
+ _keyTableUsedCount += 1;
+ _keyTableEntryCount += 1;
+ }
+ }
}
}
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index 2ca26b8d377..f4d5f21ba4d 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -258,6 +258,13 @@ bool Cast::duplicateCastMember(CastMember *source, CastMemberInfo *info, int tar
_castsInfo[targetId] = newInfo;
}
setCastMember(targetId, target);
+
+ debugN("loaded cast: [");
+ for (auto it: (*_loadedCast)) {
+ debugN("%d:%d, ", it._key, it._value->getID());
+ }
+ debug("\b\b]");
+
if (info) {
rebuildCastNameCache();
}
Commit: 9c77c06e5f6381d9b00c40fcfac1d634e777d9ff
https://github.com/scummvm/scummvm/commit/9c77c06e5f6381d9b00c40fcfac1d634e777d9ff
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Fix writing STXT formatting
move the rIndex variable outside the while loop
Changed paths:
engines/director/castmember/text.cpp
engines/director/stxt.cpp
diff --git a/engines/director/castmember/text.cpp b/engines/director/castmember/text.cpp
index c80e83c4509..590bcc48f4f 100644
--- a/engines/director/castmember/text.cpp
+++ b/engines/director/castmember/text.cpp
@@ -983,6 +983,8 @@ uint32 TextCastMember::getCastDataSize() {
}
uint32 TextCastMember::writeSTXTResource(Common::MemoryWriteStream *writeStream, uint32 offset) {
+ debugC(3, kDebugSaving, "writeSTXTResource(): _ptext: %s, _ftext = %s", _ptext.encode().c_str(), Common::toPrintable(_ftext).encode().c_str());
+
uint32 stxtSize = getSTXTResourceSize() + 8;
writeStream->seek(offset);
@@ -1004,10 +1006,14 @@ uint32 TextCastMember::writeSTXTResource(Common::MemoryWriteStream *writeStream,
writeStream->writeUint16BE(formatting);
uint32 it = 0;
+ uint32 rIndex = 0;
while (it < _ftext.size() - 1) {
- uint32 rIndex = 0;
if (_ftext[it] == '\001' && _ftext[it + 1] == '\016') {
// Styling header found
+ debugC(3, kDebugSaving, "Format start offset: %d, text: %s", rIndex,
+ Common::toPrintable(_rtext.substr(style.formatStartOffset, rIndex - style.formatStartOffset)).c_str());
+
+ debugC(3, kDebugSaving, "Formatting: %s", Common::toPrintable(_ftext.substr(it, 22)).encode().c_str());
it += 2;
if (it + 22 > _ftext.size()) {
@@ -1037,6 +1043,9 @@ uint32 TextCastMember::writeSTXTResource(Common::MemoryWriteStream *writeStream,
it++;
}
+ debugC(3, kDebugSaving, "format start offset: %d, text: %s", rIndex,
+ Common::toPrintable(_rtext.substr(style.formatStartOffset, rIndex - style.formatStartOffset)).c_str());
+
if (debugChannelSet(7, kDebugSaving)) {
byte *dumpData = nullptr;
dumpData = (byte *)calloc(stxtSize, sizeof(byte));
diff --git a/engines/director/stxt.cpp b/engines/director/stxt.cpp
index 45646c467dd..6228247a5e5 100644
--- a/engines/director/stxt.cpp
+++ b/engines/director/stxt.cpp
@@ -154,6 +154,9 @@ void FontStyle::read(Common::ReadStreamEndian &stream, Cast *cast) {
}
void FontStyle::write(Common::MemoryWriteStream *writeStream) {
+ debugC(3, kDebugSaving, "FontStyle::write(): formatStartOffset: %d, height: %d ascent: %d, fontId: %d, textSlant: %d, fontSize: %d, r: %x g: %x b: %x",
+ formatStartOffset, height, ascent, fontId, textSlant, fontSize, r, g, b);
+
writeStream->writeUint32BE(formatStartOffset);
writeStream->writeUint16BE(height);
writeStream->writeUint16BE(ascent);
Commit: 3a95842ee061c85769e191f8a356601efc2152e6
https://github.com/scummvm/scummvm/commit/3a95842ee061c85769e191f8a356601efc2152e6
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Fix STXT formatting writing
Update _ftext in `TextCastMember::updateFromWidget`
Also write _ptext instead of _rtext
Correctly encode the _ptext in 'STXT' writing
Changed paths:
engines/director/castmember/text.cpp
diff --git a/engines/director/castmember/text.cpp b/engines/director/castmember/text.cpp
index 590bcc48f4f..61919d4d199 100644
--- a/engines/director/castmember/text.cpp
+++ b/engines/director/castmember/text.cpp
@@ -588,6 +588,11 @@ void TextCastMember::updateFromWidget(Graphics::MacWidget *widget, bool spriteEd
Common::String content = ((Graphics::MacText *)widget)->getEditedString();
content.replace('\n', '\r');
_ptext = content;
+
+ // This string will be formatted with the default formatting
+ Common::String format = Common::String::format("\001\016%04x%02x%04x%04x%04x%04x", _fontId, _textSlant, _fontSize, _fgpalinfo1, _fgpalinfo2, _fgpalinfo2);
+ _ftext = format;
+ _ftext += _ptext;
}
}
@@ -983,7 +988,8 @@ uint32 TextCastMember::getCastDataSize() {
}
uint32 TextCastMember::writeSTXTResource(Common::MemoryWriteStream *writeStream, uint32 offset) {
- debugC(3, kDebugSaving, "writeSTXTResource(): _ptext: %s, _ftext = %s", _ptext.encode().c_str(), Common::toPrintable(_ftext).encode().c_str());
+ debugC(3, kDebugSaving, "writeSTXTResource(): _ptext: %s\n_ftext = %s\n_rtext: %s",
+ _ptext.encode().c_str(), Common::toPrintable(_ftext).encode().c_str(), Common::toPrintable(_rtext).c_str());
uint32 stxtSize = getSTXTResourceSize() + 8;
@@ -995,63 +1001,89 @@ uint32 TextCastMember::writeSTXTResource(Common::MemoryWriteStream *writeStream,
writeStream->writeUint32BE(12); // This is the offset, if it's not 12, we throw an error, other offsets are not handled
int8 formatting = getFormattingCount();
- writeStream->writeUint32BE(_rtext.size()); // Length of the string
+
+ writeStream->writeUint32BE(_ptext.size()); // Length of the string
// Encode only in one format, original may be encoded in multiple formats
// Size of one Font Style is 20 + The number of encodings takes 2 bytes
writeStream->writeUint32BE(20 * formatting + 2); // Data Length
- FontStyle style;
- writeStream->writeString(_rtext);
-
+ uint64 textPos = writeStream->pos();
+ writeStream->seek(_ptext.size(), SEEK_CUR);
writeStream->writeUint16BE(formatting);
+ debug("Number of formattings: %d", formatting);
+
+ FontStyle style;
+ Common::String rawText;
uint32 it = 0;
- uint32 rIndex = 0;
- while (it < _ftext.size() - 1) {
- if (_ftext[it] == '\001' && _ftext[it + 1] == '\016') {
- // Styling header found
- debugC(3, kDebugSaving, "Format start offset: %d, text: %s", rIndex,
- Common::toPrintable(_rtext.substr(style.formatStartOffset, rIndex - style.formatStartOffset)).c_str());
-
- debugC(3, kDebugSaving, "Formatting: %s", Common::toPrintable(_ftext.substr(it, 22)).encode().c_str());
- it += 2;
-
- if (it + 22 > _ftext.size()) {
- warning("TextCastMember::writeSTXTResource: incorrect format sequence");
- break;
+ uint32 pIndex = 0;
+
+ if (!_ftext.empty()) {
+ while (it < _ftext.size() - 1) {
+ if (_ftext[it] == '\001' && _ftext[it + 1] == '\016') {
+ // Styling header found
+ debugC(3, kDebugSaving, "Format start offset: %d, text: %s", style.formatStartOffset,
+ Common::toPrintable(_ptext.substr(style.formatStartOffset, pIndex - style.formatStartOffset)).encode().c_str());
+
+ Common::CodePage encoding = detectFontEncoding(_cast->_platform, style.fontId);
+ rawText += _ptext.substr(style.formatStartOffset, pIndex - style.formatStartOffset).encode(encoding);
+
+ debugC(3, kDebugSaving, "Formatting: %s", Common::toPrintable(_ftext.substr(it, 22)).encode().c_str());
+ it += 2;
+
+ if (it + 22 > _ftext.size()) {
+ warning("TextCastMember::writeSTXTResource: incorrect format sequence");
+ break;
+ }
+
+ // Ignoring height and ascent for now from FontStyle
+ uint16 temp;
+ style.formatStartOffset = pIndex;
+ const Common::u32char_type_t *s = _ftext.substr(it, 22).c_str();
+
+ s = Graphics::readHex(&style.fontId, s, 4);
+ s = Graphics::readHex(&temp, s, 2);
+ s = Graphics::readHex(&style.fontSize, s, 4);
+ s = Graphics::readHex(&style.r, s, 4);
+ s = Graphics::readHex(&style.g, s, 4);
+ s = Graphics::readHex(&style.b, s, 4);
+ style.textSlant = temp;
+ style.height = _height;
+ style.ascent = _ascent;
+
+ style.write(writeStream);
+ it += 22;
+ continue;
}
- // Ignoring height and ascent for now from FontStyle
- uint16 temp;
- style.formatStartOffset = rIndex;
- const Common::u32char_type_t *s = _ftext.substr(it, 22).c_str();
-
- s = Graphics::readHex(&style.fontId, s, 4);
- s = Graphics::readHex(&temp, s, 2);
- s = Graphics::readHex(&style.fontSize, s, 4);
- s = Graphics::readHex(&style.r, s, 4);
- s = Graphics::readHex(&style.g, s, 4);
- s = Graphics::readHex(&style.b, s, 4);
- style.textSlant = temp;
-
- style.write(writeStream);
- it += 22;
- continue;
+ pIndex += 1;
+ it++;
}
-
- rIndex += 1;
- it++;
+ // Because we iterate over _ftext.size() - 1
+ pIndex += 1;
+ } else {
+ pIndex = _ptext.size() - 1;
}
- debugC(3, kDebugSaving, "format start offset: %d, text: %s", rIndex,
- Common::toPrintable(_rtext.substr(style.formatStartOffset, rIndex - style.formatStartOffset)).c_str());
+ debugC(3, kDebugSaving, "format start offset: %d, text: %s", style.formatStartOffset,
+ Common::toPrintable(_ptext.substr(style.formatStartOffset, pIndex - style.formatStartOffset)).encode().c_str());
+
+ Common::CodePage encoding = detectFontEncoding(_cast->_platform, style.fontId);
+ _ptext.substr(style.formatStartOffset, pIndex - style.formatStartOffset).encode(encoding);
+ rawText += _ptext.substr(style.formatStartOffset, pIndex - style.formatStartOffset).encode(encoding);
+
+ debug("ptext length: %d, rawText length: %d", _ptext.size(), rawText.size());
+ uint64 currentPos = writeStream->pos();
+ writeStream->seek(textPos);
+ writeStream->writeString(rawText);
+ writeStream->seek(currentPos);
if (debugChannelSet(7, kDebugSaving)) {
byte *dumpData = nullptr;
dumpData = (byte *)calloc(stxtSize, sizeof(byte));
Common::MemoryWriteStream *dumpStream = new Common::SeekableMemoryWriteStream(dumpData, stxtSize);
- int32 currentPos = writeStream->pos();
+ currentPos = writeStream->pos();
writeStream->seek(offset);
dumpStream->write(writeStream, stxtSize);
writeStream->seek(currentPos);
@@ -1066,10 +1098,14 @@ uint32 TextCastMember::writeSTXTResource(Common::MemoryWriteStream *writeStream,
uint32 TextCastMember::getSTXTResourceSize() {
// Header (offset, string length, data length) + text string + data (FontStyle)
- return 12 + _rtext.size() + getFormattingCount() * 20 + 2;
+ return 12 + _ptext.size() + getFormattingCount() * 20 + 2;
}
uint8 TextCastMember::getFormattingCount() {
+ if (_ftext.empty()) {
+ return 0;
+ }
+
uint8 count = 0;
for (uint32 i = 0; i < _ftext.size() - 1; i++) {
if (_ftext[i] == '\001' && _ftext[i + 1] == '\016') {
Commit: 77c712200edb8bc8532bbc3ef829708be1c7a969
https://github.com/scummvm/scummvm/commit/77c712200edb8bc8532bbc3ef829708be1c7a969
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Move the saving functionality from archive.cpp to
archive-save.cpp
Changed paths:
A engines/director/archive-save.cpp
engines/director/archive.cpp
engines/director/archive.h
engines/director/module.mk
diff --git a/engines/director/archive-save.cpp b/engines/director/archive-save.cpp
new file mode 100644
index 00000000000..3578069bee4
--- /dev/null
+++ b/engines/director/archive-save.cpp
@@ -0,0 +1,744 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/memstream.h"
+
+#include "director/director.h"
+#include "director/archive.h"
+#include "director/cast.h"
+#include "director/movie.h"
+#include "director/score.h"
+#include "director/window.h"
+#include "director/sprite.h"
+
+#include "director/castmember/castmember.h"
+#include "director/castmember/bitmap.h"
+#include "director/castmember/text.h"
+#include "director/castmember/palette.h"
+#include "director/castmember/filmloop.h"
+
+
+namespace Director {
+
+bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
+ // Update the resources, their sizes and offsets
+ Common::Array<Resource *> builtResources = rebuildResources(movie);
+
+ // ignoring the startOffset
+ // For RIFX stream, moreoffset = 0, we won't be writing macbinary
+ byte *dumpData = nullptr;
+
+ // Don't need to allocate this much size in case 'junk' and 'free' resources are ignored
+ // Or might need to allocate even more size if extra chunks are written
+ _size = getArchiveSize(builtResources);
+ dumpData = (byte *)calloc(_size, sizeof(byte));
+
+ Common::SeekableMemoryWriteStream *writeStream = new Common::SeekableMemoryWriteStream(dumpData, _size);
+
+ writeStream->writeUint32LE(_metaTag); // The _metaTag is "RIFX" or "XFIR"
+
+ writeStream->writeUint32LE(getResourceSize(_metaTag, 0) - 8); // The size of the RIFX archive, except header and size
+ writeStream->writeUint32LE(_rifxType); // e.g. "MV93", "MV95"
+
+ switch (_rifxType) {
+ case MKTAG('M', 'V', '9', '3'):
+ case MKTAG('M', 'C', '9', '5'):
+ case MKTAG('A', 'P', 'P', 'L'):
+ writeMemoryMap(writeStream, builtResources);
+ break;
+
+ case MKTAG('F', 'G', 'D', 'M'):
+ case MKTAG('F', 'G', 'D', 'C'):
+ writeAfterBurnerMap(writeStream);
+ break;
+ default:
+ break;
+ }
+
+ Cast *cast = movie->getCast();
+ ResourceMap castResMap = _types[MKTAG('C', 'A', 'S', 't')];
+
+ for (auto &it : builtResources) {
+ debugC(5, kDebugSaving, "RIFXArchive::writeToFile: writing resource '%s': index: %d, size: %d, offset = %d", tag2str(it->tag), it->index, it->size, it->offset);
+
+ switch (it->tag) {
+ case MKTAG('R', 'I', 'F', 'X'):
+ case MKTAG('X', 'F', 'I', 'R'):
+ // meta resource
+ break;
+
+ case MKTAG('i', 'm', 'a', 'p'):
+ case MKTAG('m', 'm', 'a', 'p'):
+ // Already written
+ break;
+
+ case MKTAG('K', 'E', 'Y', '*'):
+ writeKeyTable(writeStream, it->offset);
+ break;
+
+ case MKTAG('C', 'A', 'S', '*'):
+ writeCast(writeStream, it->offset, it->libResourceId);
+ break;
+
+ case MKTAG('C', 'A', 'S', 't'):
+ cast->saveCastData(writeStream, it);
+ break;
+
+ case MKTAG('V', 'W', 'C', 'F'):
+ cast->saveConfig(writeStream, it->offset);
+ break;
+
+ case MKTAG('B', 'I', 'T', 'D'):
+ {
+ uint32 parentIndex = 0;
+ for (auto &jt : _keyData[MKTAG('B', 'I', 'T', 'D')]) {
+ for (auto &kt : jt._value) {
+ if (kt == it->index) {
+ parentIndex = jt._key;
+ break;
+ }
+ }
+ if (parentIndex) {
+ break;
+ }
+ }
+
+ Resource parent = castResMap[parentIndex];
+
+ BitmapCastMember *target = (BitmapCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
+ target->writeBITDResource(writeStream, it->offset);
+ }
+ break;
+
+ case MKTAG('S', 'T', 'X', 'T'):
+ {
+ uint32 parentIndex = 0;
+ for (auto &jt : _keyData[MKTAG('S', 'T', 'X', 'T')]) {
+ for (auto &kt : jt._value) {
+ if (kt == it->index) {
+ parentIndex = jt._key;
+ break;
+ }
+ }
+ if (parentIndex) {
+ break;
+ }
+ }
+
+ Resource parent = castResMap[parentIndex];
+
+ TextCastMember *target = (TextCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
+ target->writeSTXTResource(writeStream, it->offset);
+ }
+ break;
+
+ case MKTAG('C', 'L', 'U', 'T'):
+ {
+ uint32 parentIndex = 0;
+ for (auto &jt : _keyData[MKTAG('C', 'L', 'U', 'T')]) {
+ for (auto &kt : jt._value) {
+ if (kt == it->index) {
+ parentIndex = jt._key;
+ break;
+ }
+ }
+ if (parentIndex) {
+ break;
+ }
+ }
+
+ Resource parent = castResMap[parentIndex];
+
+ PaletteCastMember *target = (PaletteCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
+ target->writePaletteData(writeStream, it->offset);
+ }
+ break;
+
+ case MKTAG('S', 'C', 'V', 'W'):
+ {
+ uint32 parentIndex = 0;
+ for (auto &jt : _keyData[MKTAG('S', 'C', 'V', 'W')]) {
+ for (auto &kt : jt._value) {
+ if (kt == it->index) {
+ parentIndex = jt._key;
+ break;
+ }
+ }
+ if (parentIndex) {
+ break;
+ }
+ }
+
+ Resource parent = castResMap[parentIndex];
+
+ FilmLoopCastMember *target = (FilmLoopCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
+ target->writeSCVWResource(writeStream, it->offset);
+ }
+ break;
+
+ case MKTAG('V', 'W', 'S', 'C'):
+ movie->getScore()->writeVWSCResource(writeStream, it->offset);
+ break;
+
+ default:
+ writeStream->seek(it->offset);
+ writeStream->writeUint32LE(it->tag);
+ writeStream->writeUint32LE(it->size);
+ writeStream->writeStream(getResource(it->tag, it->index));
+ break;
+ }
+ }
+
+ if (path.empty()) {
+ path = Common::Path("./dumps/writtenMovie.dir");
+ }
+
+ Common::DumpFile out;
+ Common::Path dirname(path.getParent());
+
+ // Write the movie out, stored in dumpData
+ if (out.open(path, true)) {
+ out.write(dumpData, _size);
+ out.flush();
+ out.close();
+ debugC(3, kDebugLoading, "RIFXArchive::writeStream:Saved the movie as file %s", path.toString().c_str());
+ } else {
+ warning("RIFXArchive::writeStream: Error saving the file %s", path.toString().c_str());
+ }
+
+ free(dumpData);
+ delete writeStream;
+ return true;
+}
+
+bool RIFXArchive::writeMemoryMap(Common::SeekableMemoryWriteStream *writeStream, Common::Array<Resource *> resources) {
+ Resource mmap;
+
+ for (auto it : resources) {
+ if (it->tag == MKTAG('m', 'm', 'a', 'p')) {
+ mmap = *it;
+ }
+ }
+
+ writeStream->writeUint32LE(MKTAG('i', 'm', 'a', 'p')); // The "imap" resource
+ writeStream->writeUint32LE(_imapLength); // length of "imap" resource
+ writeStream->writeUint32LE(_mapversion); // "imap" version
+ writeStream->writeUint32LE(mmap.offset); // offset of the "mmap" resource
+ writeStream->writeUint32LE(_version);
+
+ writeStream->seek(mmap.offset);
+ writeStream->writeUint32LE(MKTAG('m', 'm', 'a', 'p'));
+ writeStream->writeUint32LE(mmap.size);
+
+ writeStream->writeUint16LE(_mmapHeaderSize);
+ writeStream->writeUint16LE(_mmapEntrySize);
+
+ uint32 newResCount = resources.size();
+ writeStream->writeUint32LE(newResCount + _totalCount - _resCount); // _totalCount - _resCount is the number of empty entries
+ writeStream->writeUint32LE(newResCount);
+ writeStream->seek(8, SEEK_CUR); // In the original file, these 8 bytes are all 0xFF, so this will produce a diff
+
+ // ID of the first 'free' resource, we don't make use of it
+ writeStream->writeUint32LE(0);
+
+ for (auto &it : resources) {
+ debugC(3, kDebugSaving, "RIFXArchive::writeMemoryMap: Memory map entry: '%s', size: %d, offset: %08x, flags: %x, unk1: %x, nextFreeResourceID: %d",
+ tag2str(it->tag), it->size, it->offset, it->flags, it->unk1, it->nextFreeResourceID);
+
+ // Write down the tag, the size and offset of the current resource
+ writeStream->writeUint32LE(it->tag);
+ writeStream->writeUint32LE(it->size);
+ writeStream->writeUint32LE(it->offset);
+
+ // Currently ignoring flags, unk1 and nextFreeResourceID
+ writeStream->writeUint16LE(it->flags);
+ writeStream->writeUint16LE(it->unk1);
+ writeStream->writeUint32LE(it->nextFreeResourceID);
+ }
+
+ return true;
+}
+
+bool RIFXArchive::writeAfterBurnerMap(Common::SeekableMemoryWriteStream *writeStream) {
+ warning("RIFXArchive::writeAfterBurnerMap: STUB: Incomplete function, needs further changes, AfterBurnerMap not written");
+ return false;
+
+#if 0
+ writeStream->writeUint32LE(MKTAG('F', 'v', 'e', 'r'));
+
+ writeStream->writeUint32LE(_fverLength);
+ uint32 start = writeStream->pos();
+
+ writeStream->writeUint32LE(_afterBurnerVersion);
+ uint32 end = writeStream->pos();
+
+ if (end - start != _fverLength) {
+ warning("RIFXArchive::writeAfterburnerMap(): Expected Fver of length %d but read %d bytes", _fverLength, end - start);
+ writeStream->seek(start + _fverLength);
+ }
+
+ writeStream->writeUint32LE(MKTAG('F', 'c', 'd', 'r'));
+ writeStream->writeUint32LE(_fcdrLength);
+ writeStream->seek(_fcdrLength, SEEK_CUR);
+
+ writeStream->writeUint32LE(MKTAG('A', 'B', 'M', 'P'));
+
+ return true;
+#endif
+}
+
+bool RIFXArchive::writeKeyTable(Common::SeekableMemoryWriteStream *writeStream, uint32 offset) {
+ writeStream->seek(offset);
+
+ writeStream->writeUint32LE(MKTAG('K', 'E', 'Y', '*'));
+ writeStream->writeUint32LE(getResourceSize(MKTAG('K', 'E', 'Y', '*'), getResourceIDList(MKTAG('K', 'E', 'Y', '*'))[0]));
+
+ writeStream->writeUint16LE(_keyTableEntrySize);
+ writeStream->writeUint16LE(_keyTableEntrySize2);
+ writeStream->writeUint32LE(_keyTableEntryCount);
+ writeStream->writeUint32LE(_keyTableUsedCount);
+
+ debugC(3, kDebugSaving, "RIFXArchive::writeKeyTable: writing key table:");
+
+ for (auto &childTag : _keyData) {
+ KeyMap keyMap = childTag._value;
+
+ for (auto &parentIndex : keyMap) {
+ KeyArray keyArray = parentIndex._value;
+
+ for (auto childIndex : keyArray) {
+ debugC(3, kDebugSaving, "_keyData contains tag: %s, parentIndex: %d, childIndex: %d", tag2str(childTag._key), parentIndex._key, childIndex);
+ writeStream->writeUint32LE(childIndex);
+ writeStream->writeUint32LE(parentIndex._key);
+ writeStream->writeUint32LE(childTag._key);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool RIFXArchive::writeCast(Common::SeekableWriteStream *writeStream, uint32 offset, uint32 castLib) {
+ writeStream->seek(offset);
+
+ uint castTag = MKTAG('C', 'A', 'S', 't');
+ writeStream->writeUint32LE(MKTAG('C', 'A', 'S', '*'));
+ writeStream->writeUint32LE(getCASResourceSize(castLib));
+
+ Common::HashMap<uint16, uint16> castIndexes;
+
+ // We can't just write all the 'CASt' indices randomly, we have to sort them by castId
+ // Since the order they appear matters, they are given castIds accordingly
+ uint32 maxCastId = 0;
+ for (auto &it : _types[castTag]) {
+ if (it._value.libResourceId == castLib) {
+ castIndexes[it._value.castId] = it._value.index;
+ maxCastId = MAX(maxCastId, it._value.castId);
+ }
+ }
+
+ debugC(5, kDebugSaving, "RIFXArchive::writeCast: Writing CAS* resource:");
+ debugCN(5, kDebugSaving, "'CASt' indexes: [");
+ for (uint32 i = 0; i <= maxCastId; i++) {
+ uint32 castIndex = castIndexes.getValOrDefault(i, 0);
+ if (castIndex) {
+ debugCN(5, kDebugSaving, "%d, ", castIndex);
+ writeStream->writeUint32BE(castIndex);
+ }
+ }
+ debugC(5, kDebugSaving, "\b\b]");
+ return true;
+}
+
+Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
+ // Currently I'm modifying the original _resources, _types and _keydata structures, this is what happens in the original Director
+ // However, if we don't want that, we could make copies of them and then modify them
+
+ // Currently handled Resource types:
+ // imap // BITD
+ // mmap // CLUT
+ // RIFX // STXT
+ // KEY* // SCVW (filmloop)
+ // CAS* // VWCF
+ // CASt
+ // SCVW
+ // 'RTE0', 'RTE1', 'RTE2'
+
+ // First we'll have to update the _types table to include all the newly added
+ // cast members, and their 'CASt' resources
+ // Only handling movies with a single cast for now
+ Cast *cast = movie->getCast();
+ ResourceMap &castResMap = _types[MKTAG('C', 'A', 'S', 't')];
+
+ for (auto it : *(cast->_loadedCast)) {
+ if (it._value->_index == -1) {
+ Resource *res = nullptr;
+ uint16 targetCastId = it._value->getID() - cast->_castArrayStart;
+
+ // Checking if the castId already exists in the CASt resources
+ for (auto castRes : castResMap) {
+ if (castRes._value.castId == targetCastId) {
+ res = &castRes._value;
+ }
+ }
+
+ if (!res) {
+ // If the castId is new, create a new resource
+ // Assigning the next available index to the resource
+ res = &castResMap[_resources.size()];
+ res->tag = MKTAG('C', 'A', 'S', 't');
+ res->accessed = true;
+
+ // Again considering here that there is only one CAS* resource, so the first resource will have our necessary libResourceId
+ res->libResourceId = _types[MKTAG('C', 'A', 'S', '*')].begin()->_value.libResourceId;
+ res->children = it._value->_children;
+ res->index = _resources.size();
+ res->castId = it._value->getID() - cast->_castArrayStart;
+
+ for (auto child : it._value->_children) {
+ _keyData[child.tag][res->index].push_back(child.index);
+ _keyTableUsedCount += 1;
+ _keyTableEntryCount += 1;
+ }
+ _resources.push_back(res);
+
+ debugC(5, kDebugSaving, "RIFXArchive::rebuildResources(): new 'CASt' resource added");
+ } else {
+ // The castId is not new, overwrite the key data of the previous cast
+ for (auto child : res->children) {
+ // Remove the data of the previous (removed) 'CASt'
+ int8 count = _keyData[child.tag][res->index].size();
+ _keyData[child.tag][res->index].clear();
+ _keyTableUsedCount -= count;
+ _keyTableEntryCount -= count;
+ }
+
+ res->children = it._value->_children;
+
+ for (auto child : res->children) {
+ _keyData[child.tag][res->index].push_back(child.index);
+ _keyTableUsedCount += 1;
+ _keyTableEntryCount += 1;
+ }
+ }
+ }
+ }
+
+ // TODO: Then we'll need to see if there are any other newly added resources
+ // Now when you duplicate a cast member, say BitmapCastMember, the cast member is duplicated
+ // but its children resources are not, meaning the duplicated BitmapCastMember is also loaded from the same 'BITD' resource
+ // So it is not necessary to duplicate the 'BITD' resource
+ // However, in case an entirely new cast member is added, say a filmloop is recorded, then that requires a new 'SCVW' resource
+ // Same goes for if a new cast is added to the movie
+ // Ignoring that for now
+
+ // Next step is to recalculate the sizes and the offsets of all the resources
+
+ // Since the first 3 resources are determined (RIFX, imap and mmap)
+ // (the mmap doesn't need to be the third resource, but for simplicity, it's better there)
+ // We'll start writing after that RIFX header, mmap and imap resources
+ // The first 12 bytes are metaTag ('RIFX'), size of file, and RIFX type ('MV93', 'MV95', etc.)
+ // The +8 bytes are to account for the header and size
+ uint32 currentSize = 12 + (getImapSize() + 8) + (getMmapSize() + 8);
+
+ // This switch statement can be simplified by keeping a pointer to the write function of the resrouce in the Resource
+ // But that will require accessing _resource every time so not doing that right now
+
+ // need to make a new resources array, because we need the old offsets as well as new ones
+ Common::Array<Resource *> builtResources;
+
+ for (auto it: _resources) {
+ builtResources.push_back(new Resource(it));
+ }
+
+ uint32 resSize = 0;
+ for (auto &it : builtResources) {
+ switch (it->tag) {
+ case MKTAG('R', 'I', 'F', 'X'):
+ case MKTAG('X', 'F', 'I', 'R'):
+ // only one resource only
+ // Size will be determined after all other sizes have been calculated
+ it->offset = 0;
+ break;
+
+ case MKTAG('i', 'm', 'a', 'p'):
+ // one resource only
+ it->size = getImapSize();
+ it->offset = 12; // First 12 bytes are reserved for metaTag ('RIFX'), size of file, and RIFX type ('MV93', 'MV95', etc.)
+ break;
+
+ case MKTAG('m', 'm', 'a', 'p'):
+ // one resource only
+ it->size = getMmapSize();
+ it->offset = 12 + (getImapSize() + 8); // The +8 is to account for header and size
+ break;
+
+ case MKTAG('C', 'A', 'S', 't'):
+ {
+ // The castIds of cast members start from _castArrayStart
+ CastMember *target = cast->getCastMember(it->castId + cast->_castArrayStart);
+
+ if (target) {
+ resSize = target->getCastResourceSize();
+ it->size = resSize; // getCastResourceSize returns size without header and size
+ } else {
+ resSize = it->size;
+ }
+ it->offset = currentSize;
+ currentSize += resSize + 8;
+ }
+ break;
+
+ case MKTAG('C', 'A', 'S', '*'):
+ // Currently handling only movies with one 'CAS*' resource, i.e. only one cast
+ resSize = getCASResourceSize(it->libResourceId); // getCASResourceSize() returns size without header and size
+ it->size = resSize;
+ it->offset = currentSize;
+ currentSize += resSize + 8;
+ break;
+
+ case MKTAG('K', 'E', 'Y', '*'):
+ resSize = getKeyTableResourceSize();
+ it->size = resSize;
+ it->offset = currentSize;
+ currentSize += resSize + 8;
+ break;
+
+ case MKTAG('V', 'W', 'C', 'F'):
+ {
+ // Cast config, as many resources as Casts
+ // No need to update the key mapping
+ resSize = cast->getConfigSize();
+
+ it->offset = currentSize;
+
+ currentSize += resSize + 8; // getConfigSize() doesn't include header and size
+ it->size = resSize;
+ }
+ break;
+
+ case MKTAG('S', 'T', 'X', 'T'):
+ {
+ uint32 parentIndex = 0;
+ for (auto &jt : _keyData[MKTAG('S', 'T', 'X', 'T')]) {
+ for (auto &kt : jt._value) {
+ if (kt == it->index) {
+ parentIndex = jt._key;
+ break;
+ }
+ }
+ if (parentIndex) {
+ break;
+ }
+ }
+
+ Resource parent = castResMap[parentIndex];
+
+ TextCastMember *target = (TextCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
+ resSize = target->getSTXTResourceSize();
+
+ it->offset = currentSize;
+ it->size = resSize;
+
+ currentSize += resSize + 8;
+ }
+ break;
+
+ case MKTAG('C', 'L', 'U', 'T'):
+ {
+ // We have to find the parent
+ // Look into the keyData, for all parents of 'CLUT' resource
+ // If the parent contains this 'CLUT' resource's index, that's our parent
+ uint32 parentIndex = 0;
+ for (auto &jt : _keyData[MKTAG('C', 'L', 'U', 'T')]) {
+ for (auto &kt : jt._value) {
+ if (kt == it->index) {
+ parentIndex = jt._key;
+ break;
+ }
+ }
+ if (parentIndex) {
+ break;
+ }
+ }
+
+ Resource parent = castResMap[parentIndex];
+
+ PaletteCastMember *target = (PaletteCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
+ resSize = target->getPaletteDataSize();
+
+ it->offset = currentSize;
+ it->size = resSize;
+
+ currentSize += resSize + 8;
+ }
+ break;
+
+ case MKTAG('B', 'I', 'T', 'D'):
+ {
+ uint32 parentIndex = 0;
+ for (auto &jt : _keyData[MKTAG('B', 'I', 'T', 'D')]) {
+ for (auto &kt : jt._value) {
+ if (kt == it->index) {
+ parentIndex = jt._key;
+ break;
+ }
+ }
+ if (parentIndex) {
+ break;
+ }
+ }
+
+ Resource parent = castResMap[parentIndex];
+
+ BitmapCastMember *target = (BitmapCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
+ resSize = target->getBITDResourceSize();
+
+ it->offset = currentSize;
+ it->size = resSize;
+
+ currentSize += resSize + 8;
+ }
+ break;
+
+ case MKTAG('S', 'C', 'V', 'W'):
+ {
+ uint32 parentIndex = 0;
+ for (auto &jt : _keyData[MKTAG('S', 'C', 'V', 'W')]) {
+ for (auto &kt : jt._value) {
+ if (kt == it->index) {
+ parentIndex = jt._key;
+ break;
+ }
+ }
+ if (parentIndex) {
+ break;
+ }
+ }
+
+ Resource parent = castResMap[parentIndex];
+
+ FilmLoopCastMember *target = (FilmLoopCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
+ resSize = target->getSCVWResourceSize();
+
+ it->offset = currentSize;
+ it->size = resSize;
+
+ currentSize += resSize + 8;
+ }
+ break;
+
+ case MKTAG('V', 'W', 'S', 'C'):
+ resSize = movie->getScore()->getVWSCResourceSize();
+ it->size = resSize;
+ it->offset = currentSize;
+ currentSize += resSize + 8; // The size doesn't include the header and the size entry
+ break;
+
+ case MKTAG('f', 'r', 'e', 'e'):
+ case MKTAG('j', 'u', 'n', 'k'):
+ // These resources do not hold any data
+ it->size = 0;
+
+ // We could just ignore these and not write them at all
+ it->offset = currentSize;
+ currentSize += 8;
+ break;
+
+ default:
+ it->offset = currentSize;
+ currentSize += it->size + 8; // This size doesn't include the header and size entry
+ break;
+
+ }
+ debugC(3, kDebugSaving, "Rebuild RIFX resource index %d: '%s', %d bytes @ 0x%08x (%d), flags: %x unk1: %x nextFreeResourceId: %d",
+ it->index, tag2str(it->tag), it->size, it->offset, it->offset, it->flags, it->unk1, it->nextFreeResourceID);
+ }
+
+ // Now that all sizes have been updated, we can safely calculate the overall archive size
+ for (auto &it : builtResources) {
+ if (it->tag == MKTAG('R', 'I', 'F', 'X') || it->tag == MKTAG('X', 'F', 'I', 'R')) {
+ it->size = getArchiveSize(builtResources) + 8;
+ }
+ }
+
+ return builtResources;
+}
+
+uint32 RIFXArchive::getImapSize() {
+ // The length of imap doesn't change
+ // This is the length without header and size
+ return _imapLength;
+}
+
+uint32 RIFXArchive::getMmapSize() {
+ // The headers: 24 bytes and and 20 bytes per resources
+ return 24 + 20 * _resources.size();
+}
+
+uint32 RIFXArchive::getArchiveSize(Common::Array<Resource *> resources) {
+ // This will be called after updating the size of all the resources
+ uint32 size = 0;
+
+ for (auto it : resources) {
+ if (it->tag != MKTAG('R', 'I', 'F', 'X') && it->tag != MKTAG('X', 'F', 'I', 'R')) {
+ size += it->size + 8; // The 8 is to account for the header and size
+ }
+ }
+ return size;
+}
+
+uint32 RIFXArchive::getCASResourceSize(uint32 castLib) {
+ uint castTag = MKTAG('C', 'A', 'S', 't');
+ uint32 maxCastId = 0;
+
+ // maxCastId is the basically the number of cast members present in the cast
+ // This is the number of entries present in the 'CAS*' resource
+ for (auto &it : _types[castTag]) {
+ if (it._value.libResourceId == castLib) {
+ maxCastId = MAX(maxCastId, it._value.castId);
+ }
+ }
+
+ return (maxCastId + 1) * 4;
+}
+
+uint32 RIFXArchive::getKeyTableResourceSize() {
+ // 12 bytes of header + 12 * number of entries
+ return 12 + _keyTableUsedCount * 12;
+}
+
+void dumpFile(Common::String fileName, uint32 id, uint32 tag, byte *dumpData, uint32 dumpSize) {
+ debug("dumpFile():: dumping file %s, resource: %s (id: %d)", fileName.c_str(), tag2str(tag), id);
+ Common::DumpFile out;
+ Common::String fname = Common::String::format("./dumps/%d-%s-%s", id, tag2str(tag), fileName.c_str());
+
+ // Write the movie out, stored in dumpData
+ if (out.open(Common::Path(fname), true)) {
+ out.write(dumpData, dumpSize);
+ out.flush();
+ out.close();
+ } else {
+ warning("RIFXArchive::writeStream: Error saving the file %s", fname.c_str());
+ }
+}
+
+} // End of namespace Director
diff --git a/engines/director/archive.cpp b/engines/director/archive.cpp
index 411cd8e8361..e9f3cf425e8 100644
--- a/engines/director/archive.cpp
+++ b/engines/director/archive.cpp
@@ -31,13 +31,6 @@
#include "director/movie.h"
#include "director/cast.h"
#include "director/window.h"
-#include "director/sprite.h"
-
-#include "director/castmember/castmember.h"
-#include "director/castmember/bitmap.h"
-#include "director/castmember/text.h"
-#include "director/castmember/palette.h"
-#include "director/castmember/filmloop.h"
namespace Director {
@@ -1183,707 +1176,4 @@ Common::String RIFXArchive::formatArchiveInfo() {
return result;
}
-bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
- // Update the resources, their sizes and offsets
- Common::Array<Resource *> builtResources = rebuildResources(movie);
-
- // ignoring the startOffset
- // For RIFX stream, moreoffset = 0, we won't be writing macbinary
- byte *dumpData = nullptr;
-
- // Don't need to allocate this much size in case 'junk' and 'free' resources are ignored
- // Or might need to allocate even more size if extra chunks are written
- _size = getArchiveSize(builtResources);
- dumpData = (byte *)calloc(_size, sizeof(byte));
-
- Common::SeekableMemoryWriteStream *writeStream = new Common::SeekableMemoryWriteStream(dumpData, _size);
-
- writeStream->writeUint32LE(_metaTag); // The _metaTag is "RIFX" or "XFIR"
-
- writeStream->writeUint32LE(getResourceSize(_metaTag, 0) - 8); // The size of the RIFX archive, except header and size
- writeStream->writeUint32LE(_rifxType); // e.g. "MV93", "MV95"
-
- switch (_rifxType) {
- case MKTAG('M', 'V', '9', '3'):
- case MKTAG('M', 'C', '9', '5'):
- case MKTAG('A', 'P', 'P', 'L'):
- writeMemoryMap(writeStream, builtResources);
- break;
-
- case MKTAG('F', 'G', 'D', 'M'):
- case MKTAG('F', 'G', 'D', 'C'):
- writeAfterBurnerMap(writeStream);
- break;
- default:
- break;
- }
-
- Cast *cast = movie->getCast();
- ResourceMap castResMap = _types[MKTAG('C', 'A', 'S', 't')];
-
- for (auto &it : builtResources) {
- debugC(5, kDebugSaving, "RIFXArchive::writeToFile: writing resource '%s': index: %d, size: %d, offset = %d", tag2str(it->tag), it->index, it->size, it->offset);
-
- switch (it->tag) {
- case MKTAG('R', 'I', 'F', 'X'):
- case MKTAG('X', 'F', 'I', 'R'):
- // meta resource
- break;
-
- case MKTAG('i', 'm', 'a', 'p'):
- case MKTAG('m', 'm', 'a', 'p'):
- // Already written
- break;
-
- case MKTAG('K', 'E', 'Y', '*'):
- writeKeyTable(writeStream, it->offset);
- break;
-
- case MKTAG('C', 'A', 'S', '*'):
- writeCast(writeStream, it->offset, it->libResourceId);
- break;
-
- case MKTAG('C', 'A', 'S', 't'):
- cast->saveCastData(writeStream, it);
- break;
-
- case MKTAG('V', 'W', 'C', 'F'):
- cast->saveConfig(writeStream, it->offset);
- break;
-
- case MKTAG('B', 'I', 'T', 'D'):
- {
- uint32 parentIndex = 0;
- for (auto &jt : _keyData[MKTAG('B', 'I', 'T', 'D')]) {
- for (auto &kt : jt._value) {
- if (kt == it->index) {
- parentIndex = jt._key;
- break;
- }
- }
- if (parentIndex) {
- break;
- }
- }
-
- Resource parent = castResMap[parentIndex];
-
- BitmapCastMember *target = (BitmapCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
- target->writeBITDResource(writeStream, it->offset);
- }
- break;
-
- case MKTAG('S', 'T', 'X', 'T'):
- {
- uint32 parentIndex = 0;
- for (auto &jt : _keyData[MKTAG('S', 'T', 'X', 'T')]) {
- for (auto &kt : jt._value) {
- if (kt == it->index) {
- parentIndex = jt._key;
- break;
- }
- }
- if (parentIndex) {
- break;
- }
- }
-
- Resource parent = castResMap[parentIndex];
-
- TextCastMember *target = (TextCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
- target->writeSTXTResource(writeStream, it->offset);
- }
- break;
-
- case MKTAG('C', 'L', 'U', 'T'):
- {
- uint32 parentIndex = 0;
- for (auto &jt : _keyData[MKTAG('C', 'L', 'U', 'T')]) {
- for (auto &kt : jt._value) {
- if (kt == it->index) {
- parentIndex = jt._key;
- break;
- }
- }
- if (parentIndex) {
- break;
- }
- }
-
- Resource parent = castResMap[parentIndex];
-
- PaletteCastMember *target = (PaletteCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
- target->writePaletteData(writeStream, it->offset);
- }
- break;
-
- case MKTAG('S', 'C', 'V', 'W'):
- {
- uint32 parentIndex = 0;
- for (auto &jt : _keyData[MKTAG('S', 'C', 'V', 'W')]) {
- for (auto &kt : jt._value) {
- if (kt == it->index) {
- parentIndex = jt._key;
- break;
- }
- }
- if (parentIndex) {
- break;
- }
- }
-
- Resource parent = castResMap[parentIndex];
-
- FilmLoopCastMember *target = (FilmLoopCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
- target->writeSCVWResource(writeStream, it->offset);
- }
- break;
-
- case MKTAG('V', 'W', 'S', 'C'):
- movie->getScore()->writeVWSCResource(writeStream, it->offset);
- break;
-
- default:
- writeStream->seek(it->offset);
- writeStream->writeUint32LE(it->tag);
- writeStream->writeUint32LE(it->size);
- writeStream->writeStream(getResource(it->tag, it->index));
- break;
- }
- }
-
- if (path.empty()) {
- path = Common::Path("./dumps/writtenMovie.dir");
- }
-
- Common::DumpFile out;
- Common::Path dirname(path.getParent());
-
- // Write the movie out, stored in dumpData
- if (out.open(path, true)) {
- out.write(dumpData, _size);
- out.flush();
- out.close();
- debugC(3, kDebugLoading, "RIFXArchive::writeStream:Saved the movie as file %s", path.toString().c_str());
- } else {
- warning("RIFXArchive::writeStream: Error saving the file %s", path.toString().c_str());
- }
-
- free(dumpData);
- delete writeStream;
- return true;
-}
-
-bool RIFXArchive::writeMemoryMap(Common::SeekableMemoryWriteStream *writeStream, Common::Array<Resource *> resources) {
- Resource mmap;
-
- for (auto it : resources) {
- if (it->tag == MKTAG('m', 'm', 'a', 'p')) {
- mmap = *it;
- }
- }
-
- writeStream->writeUint32LE(MKTAG('i', 'm', 'a', 'p')); // The "imap" resource
- writeStream->writeUint32LE(_imapLength); // length of "imap" resource
- writeStream->writeUint32LE(_mapversion); // "imap" version
- writeStream->writeUint32LE(mmap.offset); // offset of the "mmap" resource
- writeStream->writeUint32LE(_version);
-
- writeStream->seek(mmap.offset);
- writeStream->writeUint32LE(MKTAG('m', 'm', 'a', 'p'));
- writeStream->writeUint32LE(mmap.size);
-
- writeStream->writeUint16LE(_mmapHeaderSize);
- writeStream->writeUint16LE(_mmapEntrySize);
-
- uint32 newResCount = resources.size();
- writeStream->writeUint32LE(newResCount + _totalCount - _resCount); // _totalCount - _resCount is the number of empty entries
- writeStream->writeUint32LE(newResCount);
- writeStream->seek(8, SEEK_CUR); // In the original file, these 8 bytes are all 0xFF, so this will produce a diff
-
- // ID of the first 'free' resource, we don't make use of it
- writeStream->writeUint32LE(0);
-
- for (auto &it : resources) {
- debugC(3, kDebugSaving, "RIFXArchive::writeMemoryMap: Memory map entry: '%s', size: %d, offset: %08x, flags: %x, unk1: %x, nextFreeResourceID: %d",
- tag2str(it->tag), it->size, it->offset, it->flags, it->unk1, it->nextFreeResourceID);
-
- // Write down the tag, the size and offset of the current resource
- writeStream->writeUint32LE(it->tag);
- writeStream->writeUint32LE(it->size);
- writeStream->writeUint32LE(it->offset);
-
- // Currently ignoring flags, unk1 and nextFreeResourceID
- writeStream->writeUint16LE(it->flags);
- writeStream->writeUint16LE(it->unk1);
- writeStream->writeUint32LE(it->nextFreeResourceID);
- }
-
- return true;
-}
-
-bool RIFXArchive::writeAfterBurnerMap(Common::SeekableMemoryWriteStream *writeStream) {
- warning("RIFXArchive::writeAfterBurnerMap: STUB: Incomplete function, needs further changes, AfterBurnerMap not written");
- return false;
-
-#if 0
- writeStream->writeUint32LE(MKTAG('F', 'v', 'e', 'r'));
-
- writeStream->writeUint32LE(_fverLength);
- uint32 start = writeStream->pos();
-
- writeStream->writeUint32LE(_afterBurnerVersion);
- uint32 end = writeStream->pos();
-
- if (end - start != _fverLength) {
- warning("RIFXArchive::writeAfterburnerMap(): Expected Fver of length %d but read %d bytes", _fverLength, end - start);
- writeStream->seek(start + _fverLength);
- }
-
- writeStream->writeUint32LE(MKTAG('F', 'c', 'd', 'r'));
- writeStream->writeUint32LE(_fcdrLength);
- writeStream->seek(_fcdrLength, SEEK_CUR);
-
- writeStream->writeUint32LE(MKTAG('A', 'B', 'M', 'P'));
-
- return true;
-#endif
-}
-
-bool RIFXArchive::writeKeyTable(Common::SeekableMemoryWriteStream *writeStream, uint32 offset) {
- writeStream->seek(offset);
-
- writeStream->writeUint32LE(MKTAG('K', 'E', 'Y', '*'));
- writeStream->writeUint32LE(getResourceSize(MKTAG('K', 'E', 'Y', '*'), getResourceIDList(MKTAG('K', 'E', 'Y', '*'))[0]));
-
- writeStream->writeUint16LE(_keyTableEntrySize);
- writeStream->writeUint16LE(_keyTableEntrySize2);
- writeStream->writeUint32LE(_keyTableEntryCount);
- writeStream->writeUint32LE(_keyTableUsedCount);
-
- debugC(3, kDebugSaving, "RIFXArchive::writeKeyTable: writing key table:");
-
- for (auto &childTag : _keyData) {
- KeyMap keyMap = childTag._value;
-
- for (auto &parentIndex : keyMap) {
- KeyArray keyArray = parentIndex._value;
-
- for (auto childIndex : keyArray) {
- debugC(3, kDebugSaving, "_keyData contains tag: %s, parentIndex: %d, childIndex: %d", tag2str(childTag._key), parentIndex._key, childIndex);
- writeStream->writeUint32LE(childIndex);
- writeStream->writeUint32LE(parentIndex._key);
- writeStream->writeUint32LE(childTag._key);
- }
- }
- }
-
- return true;
-}
-
-bool RIFXArchive::writeCast(Common::SeekableWriteStream *writeStream, uint32 offset, uint32 castLib) {
- writeStream->seek(offset);
-
- uint castTag = MKTAG('C', 'A', 'S', 't');
- writeStream->writeUint32LE(MKTAG('C', 'A', 'S', '*'));
- writeStream->writeUint32LE(getCASResourceSize(castLib));
-
- Common::HashMap<uint16, uint16> castIndexes;
-
- // We can't just write all the 'CASt' indices randomly, we have to sort them by castId
- // Since the order they appear matters, they are given castIds accordingly
- uint32 maxCastId = 0;
- for (auto &it : _types[castTag]) {
- if (it._value.libResourceId == castLib) {
- castIndexes[it._value.castId] = it._value.index;
- maxCastId = MAX(maxCastId, it._value.castId);
- }
- }
-
- debugC(5, kDebugSaving, "RIFXArchive::writeCast: Writing CAS* resource:");
- debugCN(5, kDebugSaving, "'CASt' indexes: [");
- for (uint32 i = 0; i <= maxCastId; i++) {
- uint32 castIndex = castIndexes.getValOrDefault(i, 0);
- if (castIndex) {
- debugCN(5, kDebugSaving, "%d, ", castIndex);
- writeStream->writeUint32BE(castIndex);
- }
- }
- debugC(5, kDebugSaving, "\b\b]");
- return true;
-}
-
-Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
- // Currently I'm modifying the original _resources, _types and _keydata structures, this is what happens in the original Director
- // However, if we don't want that, we could make copies of them and then modify them
-
- // Currently handled Resource types:
- // imap // BITD
- // mmap // CLUT
- // RIFX // STXT
- // KEY* // SCVW (filmloop)
- // CAS* // VWCF
- // CASt
- // SCVW
- // 'RTE0', 'RTE1', 'RTE2'
-
- // First we'll have to update the _types table to include all the newly added
- // cast members, and their 'CASt' resources
- // Only handling movies with a single cast for now
- Cast *cast = movie->getCast();
- ResourceMap &castResMap = _types[MKTAG('C', 'A', 'S', 't')];
-
- for (auto it : *(cast->_loadedCast)) {
- if (it._value->_index == -1) {
- Resource *res = nullptr;
- uint16 targetCastId = it._value->getID() - cast->_castArrayStart;
-
- // Checking if the castId already exists in the CASt resources
- for (auto castRes : castResMap) {
- if (castRes._value.castId == targetCastId) {
- res = &castRes._value;
- }
- }
-
- if (!res) {
- // If the castId is new, create a new resource
- // Assigning the next available index to the resource
- res = &castResMap[_resources.size()];
- res->tag = MKTAG('C', 'A', 'S', 't');
- res->accessed = true;
-
- // Again considering here that there is only one CAS* resource, so the first resource will have our necessary libResourceId
- res->libResourceId = _types[MKTAG('C', 'A', 'S', '*')].begin()->_value.libResourceId;
- res->children = it._value->_children;
- res->index = _resources.size();
- res->castId = it._value->getID() - cast->_castArrayStart;
-
- for (auto child : it._value->_children) {
- _keyData[child.tag][res->index].push_back(child.index);
- _keyTableUsedCount += 1;
- _keyTableEntryCount += 1;
- }
- _resources.push_back(res);
-
- debugC(5, kDebugSaving, "RIFXArchive::rebuildResources(): new 'CASt' resource added");
- } else {
- // The castId is not new, overwrite the key data of the previous cast
- for (auto child : res->children) {
- // Remove the data of the previous (removed) 'CASt'
- int8 count = _keyData[child.tag][res->index].size();
- _keyData[child.tag][res->index].clear();
- _keyTableUsedCount -= count;
- _keyTableEntryCount -= count;
- }
-
- res->children = it._value->_children;
-
- for (auto child : res->children) {
- _keyData[child.tag][res->index].push_back(child.index);
- _keyTableUsedCount += 1;
- _keyTableEntryCount += 1;
- }
- }
- }
- }
-
- // TODO: Then we'll need to see if there are any other newly added resources
- // Now when you duplicate a cast member, say BitmapCastMember, the cast member is duplicated
- // but its children resources are not, meaning the duplicated BitmapCastMember is also loaded from the same 'BITD' resource
- // So it is not necessary to duplicate the 'BITD' resource
- // However, in case an entirely new cast member is added, say a filmloop is recorded, then that requires a new 'SCVW' resource
- // Same goes for if a new cast is added to the movie
- // Ignoring that for now
-
- // Next step is to recalculate the sizes and the offsets of all the resources
-
- // Since the first 3 resources are determined (RIFX, imap and mmap)
- // (the mmap doesn't need to be the third resource, but for simplicity, it's better there)
- // We'll start writing after that RIFX header, mmap and imap resources
- // The first 12 bytes are metaTag ('RIFX'), size of file, and RIFX type ('MV93', 'MV95', etc.)
- // The +8 bytes are to account for the header and size
- uint32 currentSize = 12 + (getImapSize() + 8) + (getMmapSize() + 8);
-
- // This switch statement can be simplified by keeping a pointer to the write function of the resrouce in the Resource
- // But that will require accessing _resource every time so not doing that right now
-
- // need to make a new resources array, because we need the old offsets as well as new ones
- Common::Array<Resource *> builtResources;
-
- for (auto it: _resources) {
- builtResources.push_back(new Resource(it));
- }
-
- uint32 resSize = 0;
- for (auto &it : builtResources) {
- switch (it->tag) {
- case MKTAG('R', 'I', 'F', 'X'):
- case MKTAG('X', 'F', 'I', 'R'):
- // only one resource only
- // Size will be determined after all other sizes have been calculated
- it->offset = 0;
- break;
-
- case MKTAG('i', 'm', 'a', 'p'):
- // one resource only
- it->size = getImapSize();
- it->offset = 12; // First 12 bytes are reserved for metaTag ('RIFX'), size of file, and RIFX type ('MV93', 'MV95', etc.)
- break;
-
- case MKTAG('m', 'm', 'a', 'p'):
- // one resource only
- it->size = getMmapSize();
- it->offset = 12 + (getImapSize() + 8); // The +8 is to account for header and size
- break;
-
- case MKTAG('C', 'A', 'S', 't'):
- {
- // The castIds of cast members start from _castArrayStart
- CastMember *target = cast->getCastMember(it->castId + cast->_castArrayStart);
-
- if (target) {
- resSize = target->getCastResourceSize();
- it->size = resSize; // getCastResourceSize returns size without header and size
- } else {
- resSize = it->size;
- }
- it->offset = currentSize;
- currentSize += resSize + 8;
- }
- break;
-
- case MKTAG('C', 'A', 'S', '*'):
- // Currently handling only movies with one 'CAS*' resource, i.e. only one cast
- resSize = getCASResourceSize(it->libResourceId); // getCASResourceSize() returns size without header and size
- it->size = resSize;
- it->offset = currentSize;
- currentSize += resSize + 8;
- break;
-
- case MKTAG('K', 'E', 'Y', '*'):
- resSize = getKeyTableResourceSize();
- it->size = resSize;
- it->offset = currentSize;
- currentSize += resSize + 8;
- break;
-
- case MKTAG('V', 'W', 'C', 'F'):
- {
- // Cast config, as many resources as Casts
- // No need to update the key mapping
- resSize = cast->getConfigSize();
-
- it->offset = currentSize;
-
- currentSize += resSize + 8; // getConfigSize() doesn't include header and size
- it->size = resSize;
- }
- break;
-
- case MKTAG('S', 'T', 'X', 'T'):
- {
- uint32 parentIndex = 0;
- for (auto &jt : _keyData[MKTAG('S', 'T', 'X', 'T')]) {
- for (auto &kt : jt._value) {
- if (kt == it->index) {
- parentIndex = jt._key;
- break;
- }
- }
- if (parentIndex) {
- break;
- }
- }
-
- Resource parent = castResMap[parentIndex];
-
- TextCastMember *target = (TextCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
- resSize = target->getSTXTResourceSize();
-
- it->offset = currentSize;
- it->size = resSize;
-
- currentSize += resSize + 8;
- }
- break;
-
- case MKTAG('C', 'L', 'U', 'T'):
- {
- // We have to find the parent
- // Look into the keyData, for all parents of 'CLUT' resource
- // If the parent contains this 'CLUT' resource's index, that's our parent
- uint32 parentIndex = 0;
- for (auto &jt : _keyData[MKTAG('C', 'L', 'U', 'T')]) {
- for (auto &kt : jt._value) {
- if (kt == it->index) {
- parentIndex = jt._key;
- break;
- }
- }
- if (parentIndex) {
- break;
- }
- }
-
- Resource parent = castResMap[parentIndex];
-
- PaletteCastMember *target = (PaletteCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
- resSize = target->getPaletteDataSize();
-
- it->offset = currentSize;
- it->size = resSize;
-
- currentSize += resSize + 8;
- }
- break;
-
- case MKTAG('B', 'I', 'T', 'D'):
- {
- uint32 parentIndex = 0;
- for (auto &jt : _keyData[MKTAG('B', 'I', 'T', 'D')]) {
- for (auto &kt : jt._value) {
- if (kt == it->index) {
- parentIndex = jt._key;
- break;
- }
- }
- if (parentIndex) {
- break;
- }
- }
-
- Resource parent = castResMap[parentIndex];
-
- BitmapCastMember *target = (BitmapCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
- resSize = target->getBITDResourceSize();
-
- it->offset = currentSize;
- it->size = resSize;
-
- currentSize += resSize + 8;
- }
- break;
-
- case MKTAG('S', 'C', 'V', 'W'):
- {
- uint32 parentIndex = 0;
- for (auto &jt : _keyData[MKTAG('S', 'C', 'V', 'W')]) {
- for (auto &kt : jt._value) {
- if (kt == it->index) {
- parentIndex = jt._key;
- break;
- }
- }
- if (parentIndex) {
- break;
- }
- }
-
- Resource parent = castResMap[parentIndex];
-
- FilmLoopCastMember *target = (FilmLoopCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
- resSize = target->getSCVWResourceSize();
-
- it->offset = currentSize;
- it->size = resSize;
-
- currentSize += resSize + 8;
- }
- break;
-
- case MKTAG('V', 'W', 'S', 'C'):
- resSize = movie->getScore()->getVWSCResourceSize();
- it->size = resSize;
- it->offset = currentSize;
- currentSize += resSize + 8; // The size doesn't include the header and the size entry
- break;
-
- case MKTAG('f', 'r', 'e', 'e'):
- case MKTAG('j', 'u', 'n', 'k'):
- // These resources do not hold any data
- it->size = 0;
-
- // We could just ignore these and not write them at all
- it->offset = currentSize;
- currentSize += 8;
- break;
-
- default:
- it->offset = currentSize;
- currentSize += it->size + 8; // This size doesn't include the header and size entry
- break;
-
- }
- debugC(3, kDebugSaving, "Rebuild RIFX resource index %d: '%s', %d bytes @ 0x%08x (%d), flags: %x unk1: %x nextFreeResourceId: %d",
- it->index, tag2str(it->tag), it->size, it->offset, it->offset, it->flags, it->unk1, it->nextFreeResourceID);
- }
-
- // Now that all sizes have been updated, we can safely calculate the overall archive size
- for (auto &it : builtResources) {
- if (it->tag == MKTAG('R', 'I', 'F', 'X') || it->tag == MKTAG('X', 'F', 'I', 'R')) {
- it->size = getArchiveSize(builtResources) + 8;
- }
- }
-
- return builtResources;
-}
-
-uint32 RIFXArchive::getImapSize() {
- // The length of imap doesn't change
- // This is the length without header and size
- return _imapLength;
-}
-
-uint32 RIFXArchive::getMmapSize() {
- // The headers: 24 bytes and and 20 bytes per resources
- return 24 + 20 * _resources.size();
-}
-
-uint32 RIFXArchive::getArchiveSize(Common::Array<Resource *> resources) {
- // This will be called after updating the size of all the resources
- uint32 size = 0;
-
- for (auto it : resources) {
- if (it->tag != MKTAG('R', 'I', 'F', 'X') && it->tag != MKTAG('X', 'F', 'I', 'R')) {
- size += it->size + 8; // The 8 is to account for the header and size
- }
- }
- return size;
-}
-
-uint32 RIFXArchive::getCASResourceSize(uint32 castLib) {
- uint castTag = MKTAG('C', 'A', 'S', 't');
- uint32 maxCastId = 0;
-
- // maxCastId is the basically the number of cast members present in the cast
- // This is the number of entries present in the 'CAS*' resource
- for (auto &it : _types[castTag]) {
- if (it._value.libResourceId == castLib) {
- maxCastId = MAX(maxCastId, it._value.castId);
- }
- }
-
- return (maxCastId + 1) * 4;
-}
-
-uint32 RIFXArchive::getKeyTableResourceSize() {
- // 12 bytes of header + 12 * number of entries
- return 12 + _keyTableUsedCount * 12;
-}
-
-void dumpFile(Common::String fileName, uint32 id, uint32 tag, byte *dumpData, uint32 dumpSize) {
- debug("dumpFile():: dumping file %s, resource: %s (id: %d)", fileName.c_str(), tag2str(tag), id);
- Common::DumpFile out;
- Common::String fname = Common::String::format("./dumps/%d-%s-%s", id, tag2str(tag), fileName.c_str());
-
- // Write the movie out, stored in dumpData
- if (out.open(Common::Path(fname), true)) {
- out.write(dumpData, dumpSize);
- out.flush();
- out.close();
- } else {
- warning("RIFXArchive::writeStream: Error saving the file %s", fname.c_str());
- }
-}
-
} // End of namespace Director
diff --git a/engines/director/archive.h b/engines/director/archive.h
index f8e6edab35c..32debe5b617 100644
--- a/engines/director/archive.h
+++ b/engines/director/archive.h
@@ -177,6 +177,7 @@ public:
Common::String formatArchiveInfo() override;
private:
+ /* archive-save.cpp */
/* These functions are for writing movies */
bool writeMemoryMap(Common::SeekableMemoryWriteStream *writeStream, Common::Array<Resource *> resource); // Parallel to readMemoryMap
bool writeAfterBurnerMap(Common::SeekableMemoryWriteStream *writeStreaa); // Parallel to readAfterBurnerMap
diff --git a/engines/director/module.mk b/engines/director/module.mk
index 9070e179ae3..ae958a5c596 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -2,6 +2,7 @@ MODULE := engines/director
MODULE_OBJS = \
archive.o \
+ archive-save.o \
cast.o \
channel.o \
cursor.o \
Commit: 5c4b4e787b1a94a4caf076bf775496694ce988d5
https://github.com/scummvm/scummvm/commit/5c4b4e787b1a94a4caf076bf775496694ce988d5
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Minor formatting fixes
Changed paths:
engines/director/archive-save.cpp
engines/director/archive.h
engines/director/cast.cpp
engines/director/castmember/bitmap.cpp
engines/director/frame.cpp
diff --git a/engines/director/archive-save.cpp b/engines/director/archive-save.cpp
index 3578069bee4..7517a1d0c2f 100644
--- a/engines/director/archive-save.cpp
+++ b/engines/director/archive-save.cpp
@@ -108,19 +108,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
case MKTAG('B', 'I', 'T', 'D'):
{
- uint32 parentIndex = 0;
- for (auto &jt : _keyData[MKTAG('B', 'I', 'T', 'D')]) {
- for (auto &kt : jt._value) {
- if (kt == it->index) {
- parentIndex = jt._key;
- break;
- }
- }
- if (parentIndex) {
- break;
- }
- }
-
+ uint32 parentIndex = findParentIndex(it->tag, it->index);
Resource parent = castResMap[parentIndex];
BitmapCastMember *target = (BitmapCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
@@ -130,19 +118,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
case MKTAG('S', 'T', 'X', 'T'):
{
- uint32 parentIndex = 0;
- for (auto &jt : _keyData[MKTAG('S', 'T', 'X', 'T')]) {
- for (auto &kt : jt._value) {
- if (kt == it->index) {
- parentIndex = jt._key;
- break;
- }
- }
- if (parentIndex) {
- break;
- }
- }
-
+ uint32 parentIndex = findParentIndex(it->tag, it->index);
Resource parent = castResMap[parentIndex];
TextCastMember *target = (TextCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
@@ -152,19 +128,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
case MKTAG('C', 'L', 'U', 'T'):
{
- uint32 parentIndex = 0;
- for (auto &jt : _keyData[MKTAG('C', 'L', 'U', 'T')]) {
- for (auto &kt : jt._value) {
- if (kt == it->index) {
- parentIndex = jt._key;
- break;
- }
- }
- if (parentIndex) {
- break;
- }
- }
-
+ uint32 parentIndex = findParentIndex(it->tag, it->index);
Resource parent = castResMap[parentIndex];
PaletteCastMember *target = (PaletteCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
@@ -174,19 +138,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
case MKTAG('S', 'C', 'V', 'W'):
{
- uint32 parentIndex = 0;
- for (auto &jt : _keyData[MKTAG('S', 'C', 'V', 'W')]) {
- for (auto &kt : jt._value) {
- if (kt == it->index) {
- parentIndex = jt._key;
- break;
- }
- }
- if (parentIndex) {
- break;
- }
- }
-
+ uint32 parentIndex = findParentIndex(it->tag, it->index);
Resource parent = castResMap[parentIndex];
FilmLoopCastMember *target = (FilmLoopCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
@@ -199,6 +151,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
break;
default:
+ debugC(7, kDebugLoading, "Saving resource %s as it is, without modification", tag2str(it->tag));
writeStream->seek(it->offset);
writeStream->writeUint32LE(it->tag);
writeStream->writeUint32LE(it->size);
@@ -208,11 +161,10 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
}
if (path.empty()) {
- path = Common::Path("./dumps/writtenMovie.dir");
+ path = Common::Path(movie->getMacName());
}
Common::DumpFile out;
- Common::Path dirname(path.getParent());
// Write the movie out, stored in dumpData
if (out.open(path, true)) {
@@ -360,18 +312,16 @@ bool RIFXArchive::writeCast(Common::SeekableWriteStream *writeStream, uint32 off
for (uint32 i = 0; i <= maxCastId; i++) {
uint32 castIndex = castIndexes.getValOrDefault(i, 0);
if (castIndex) {
- debugCN(5, kDebugSaving, "%d, ", castIndex);
+ debugCN(5, kDebugSaving, (i == 0 ? "%d" : ", %d"), castIndex);
writeStream->writeUint32BE(castIndex);
}
}
- debugC(5, kDebugSaving, "\b\b]");
+ debugC(5, kDebugSaving, "]");
+
return true;
}
Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
- // Currently I'm modifying the original _resources, _types and _keydata structures, this is what happens in the original Director
- // However, if we don't want that, we could make copies of them and then modify them
-
// Currently handled Resource types:
// imap // BITD
// mmap // CLUT
@@ -537,19 +487,7 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
case MKTAG('S', 'T', 'X', 'T'):
{
- uint32 parentIndex = 0;
- for (auto &jt : _keyData[MKTAG('S', 'T', 'X', 'T')]) {
- for (auto &kt : jt._value) {
- if (kt == it->index) {
- parentIndex = jt._key;
- break;
- }
- }
- if (parentIndex) {
- break;
- }
- }
-
+ uint32 parentIndex = findParentIndex(it->tag, it->index);
Resource parent = castResMap[parentIndex];
TextCastMember *target = (TextCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
@@ -564,22 +502,7 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
case MKTAG('C', 'L', 'U', 'T'):
{
- // We have to find the parent
- // Look into the keyData, for all parents of 'CLUT' resource
- // If the parent contains this 'CLUT' resource's index, that's our parent
- uint32 parentIndex = 0;
- for (auto &jt : _keyData[MKTAG('C', 'L', 'U', 'T')]) {
- for (auto &kt : jt._value) {
- if (kt == it->index) {
- parentIndex = jt._key;
- break;
- }
- }
- if (parentIndex) {
- break;
- }
- }
-
+ uint32 parentIndex = findParentIndex(it->tag, it->index);
Resource parent = castResMap[parentIndex];
PaletteCastMember *target = (PaletteCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
@@ -594,19 +517,7 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
case MKTAG('B', 'I', 'T', 'D'):
{
- uint32 parentIndex = 0;
- for (auto &jt : _keyData[MKTAG('B', 'I', 'T', 'D')]) {
- for (auto &kt : jt._value) {
- if (kt == it->index) {
- parentIndex = jt._key;
- break;
- }
- }
- if (parentIndex) {
- break;
- }
- }
-
+ uint32 parentIndex = findParentIndex(it->tag, it->index);
Resource parent = castResMap[parentIndex];
BitmapCastMember *target = (BitmapCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
@@ -621,19 +532,7 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
case MKTAG('S', 'C', 'V', 'W'):
{
- uint32 parentIndex = 0;
- for (auto &jt : _keyData[MKTAG('S', 'C', 'V', 'W')]) {
- for (auto &kt : jt._value) {
- if (kt == it->index) {
- parentIndex = jt._key;
- break;
- }
- }
- if (parentIndex) {
- break;
- }
- }
-
+ uint32 parentIndex = findParentIndex(it->tag, it->index);
Resource parent = castResMap[parentIndex];
FilmLoopCastMember *target = (FilmLoopCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
@@ -741,4 +640,20 @@ void dumpFile(Common::String fileName, uint32 id, uint32 tag, byte *dumpData, ui
}
}
+uint32 RIFXArchive::findParentIndex(uint32 tag, uint16 index) {
+ // We have to find the parent
+ // Look into the keyData, for all parents of resource tag
+ // If the parent contains this resource's index, that's our parent
+ for (auto &it : _keyData[tag]) {
+ for (auto &kt : it._value) {
+ if (kt == index) {
+ return it._key;
+ }
+ }
+ }
+
+ warning("RIFXArchive::findParentIndex: The parent for resource: %s, index: %d, was not found", tag2str(tag), index);
+ return 0;
+}
+
} // End of namespace Director
diff --git a/engines/director/archive.h b/engines/director/archive.h
index 32debe5b617..fa236e43fab 100644
--- a/engines/director/archive.h
+++ b/engines/director/archive.h
@@ -196,9 +196,10 @@ private:
void readCast(Common::SeekableReadStreamEndian &casStream, uint16 libResourceId);
void readKeyTable(Common::SeekableReadStreamEndian &keyStream);
+ uint32 findParentIndex(uint32 tag, uint16 index);
+
/* Memory Map data to save the file */
uint32 _metaTag;
- uint32 _moreOffset;
uint32 _mapversion;
uint32 _mmapHeaderSize;
uint32 _mmapEntrySize;
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index f4d5f21ba4d..44b022293e0 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -259,11 +259,11 @@ bool Cast::duplicateCastMember(CastMember *source, CastMemberInfo *info, int tar
}
setCastMember(targetId, target);
- debugN("loaded cast: [");
+ debugCN(7, kDebugSaving, "loaded cast: [");
for (auto it: (*_loadedCast)) {
- debugN("%d:%d, ", it._key, it._value->getID());
+ debugCN(7, kDebugSaving, ((it._key == _castArrayStart) ? "%d:%d" : ", %d:%d"), it._key, it._value->getID());
}
- debug("\b\b]");
+ debugC(7, kDebugSaving, "]");
if (info) {
rebuildCastNameCache();
@@ -550,7 +550,7 @@ void Cast::saveConfig(Common::MemoryWriteStream *writeStream, uint32 offset) {
writeStream->writeUint16BE(_castArrayStart); // 12
// This will change
- writeStream->writeUint16BE(_castArrayStart + _castArchive->getResourceIDList(MKTAG('C', 'A', 'S', 't')).size()); // 14
+ writeStream->writeUint16BE(_castArrayStart + _castArchive->getResourceIDList(MKTAG('C', 'A', 'S', 't')).size()); // 14
writeStream->writeByte(_readRate); // 16
writeStream->writeByte(_lightswitch); // 17
@@ -858,7 +858,7 @@ void Cast::saveCastData(Common::MemoryWriteStream *writeStream, Resource *res) {
}
debugC(5, kDebugSaving, "Cast::saveCastData()::Saving 'CASt' resource, id: %d, size: %d, type: %s", id, castSize, castType2str(type));
-
+
if (debugChannelSet(7, kDebugSaving)) {
byte *dumpData = (byte *)calloc(castSize + 8, sizeof(byte));
Common::SeekableMemoryWriteStream *dumpStream = new Common::SeekableMemoryWriteStream(dumpData, castSize + 8);
diff --git a/engines/director/castmember/bitmap.cpp b/engines/director/castmember/bitmap.cpp
index 990d2de1263..d9a4e2597a7 100644
--- a/engines/director/castmember/bitmap.cpp
+++ b/engines/director/castmember/bitmap.cpp
@@ -687,10 +687,10 @@ void BitmapCastMember::load() {
warning("BitmapCastMember::load(): Unknown Bitmap CastMember Tag: [%d] %s", tag, tag2str(tag));
break;
}
-
+
if (debugChannelSet(7, kDebugLoading)) {
debug("BitmapCastMember::load(): Bitmap data:");
- pic->hexdump(pic->size());
+ pic->hexdump(MIN((int)pic->size(), 512));
}
if (!img || !img->loadStream(*pic)) {
@@ -1036,7 +1036,9 @@ void BitmapCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
if (_flags2 != 0) {
// Skipping 14 bytes because they are not stored in ScummVM Director
// May need to save in the future, see BitCastMember::BitCastMember constructor
- writeStream->seek(14, SEEK_CUR);
+ writeStream->writeUint64BE(0);
+ writeStream->writeUint32BE(0);
+ writeStream->writeUint16BE(0);
writeStream->writeUint16BE(_flags2);
}
}
@@ -1050,7 +1052,7 @@ uint32 BitmapCastMember::writeBITDResource(Common::MemoryWriteStream *writeStrea
writeStream->writeUint32LE(getBITDResourceSize());
if (_external) {
- warning("BitmapCastMember::writeBITDResource: the bitmap is external, ignoring for now");
+ warning("BitmapCastMember::writeBITDResource: the bitmap is external, ignoring for now");
return 8; // 8 for the tag and size
}
@@ -1063,7 +1065,7 @@ uint32 BitmapCastMember::writeBITDResource(Common::MemoryWriteStream *writeStrea
if (_bitsPerPixel == 8 && _picture->_surface.w < (int)(pixels.size() / _picture->_surface.h)) {
offset = (_pitch - _picture->_surface.w) % 2;
}
-
+
debugC(5, kDebugSaving, "BitmapCastMember::writeBITDResource: Saving 'BITD' Resource: bitsPerPixel: %d, castId: %d", _bitsPerPixel, _castId);
for (int y = 0; y < _picture->_surface.h; y++) {
for (int x = 0; x < _picture->_surface.w;) {
@@ -1130,7 +1132,7 @@ uint32 BitmapCastMember::writeBITDResource(Common::MemoryWriteStream *writeStrea
uint32 BitmapCastMember::getBITDResourceSize() {
// No compression for now
- return _pitch * _picture->_surface.h;
+ return _pitch * _picture->_surface.h;
}
} // End of namespace Director
diff --git a/engines/director/frame.cpp b/engines/director/frame.cpp
index 82c6b94b579..bc45b0ade9c 100644
--- a/engines/director/frame.cpp
+++ b/engines/director/frame.cpp
@@ -590,8 +590,8 @@ void Frame::writeMainChannelsD4(Common::MemoryWriteStream *writeStream) {
writeStream->writeByte(_mainChannels.transType); // 5
writeStream->writeUint16BE(_mainChannels.sound1.member); // 6, 7
- writeStream->writeUint16BE(_mainChannels.sound2.member); // 8, 9
-
+ writeStream->writeUint16BE(_mainChannels.sound2.member); // 8, 9
+
writeStream->writeByte(_mainChannels.soundType2); // 10
writeStream->writeByte(_mainChannels.skipFrameFlag); // 11
writeStream->writeByte(_mainChannels.blend); // 12
@@ -647,7 +647,7 @@ void Frame::readSpriteD4(Common::MemoryReadStreamEndian &stream, uint16 offset,
}
void readSpriteDataD4(Common::SeekableReadStreamEndian &stream, Sprite &sprite, uint32 startPosition, uint32 finishPosition) {
- debugC(8, kDebugLoading, "stream.pos(): %ld, startPosition: %d, finishPosition: %d", stream.pos(), startPosition, finishPosition);
+ debugC(8, kDebugLoading, "stream.pos(): %0x, startPosition: %d, finishPosition: %d", (int)stream.pos(), startPosition, finishPosition);
while (stream.pos() < finishPosition) {
switch (stream.pos() - startPosition) {
case 0:
@@ -774,7 +774,7 @@ void writeSpriteDataD4(Common::MemoryWriteStream *writeStream, Sprite &sprite) {
// Writing 20 bytes of sprite data
// The original data for a certain sprite might be less
writeStream->writeByte(sprite._scriptId.member); // 0
-
+
// If the sprite is a puppet (controlled by lingo scripting)
// The rest of the data isn't read
if (sprite._puppet) {
@@ -963,10 +963,10 @@ void Frame::writeMainChannelsD5(Common::MemoryWriteStream *writeStream) {
writeStream->writeUint16BE(_mainChannels.sound1.castLib); // 4, 5
writeStream->writeUint16BE(_mainChannels.sound1.member); // 6, 7
writeStream->writeUint16BE(_mainChannels.sound2.castLib); // 8, 9
- writeStream->writeUint16BE(_mainChannels.sound2.member); // 10, 11
- writeStream->writeUint16BE(_mainChannels.trans.member); // 12, 13
- writeStream->writeUint16BE(_mainChannels.trans.member); // 14, 15
-
+ writeStream->writeUint16BE(_mainChannels.sound2.member); // 10, 11
+ writeStream->writeUint16BE(_mainChannels.trans.member); // 12, 13
+ writeStream->writeUint16BE(_mainChannels.trans.member); // 14, 15
+
writeStream->writeUint16BE(0); // Unknown // 16, 17
writeStream->writeUint16BE(0); // Unknown // 18, 19
writeStream->writeByte(0); // Unknown: Sound/Tempo/Transition // 20
@@ -1152,7 +1152,7 @@ void writeSpriteDataD5(Common::MemoryWriteStream *writeStream, Sprite &sprite) {
// Writing 20 bytes of sprite data
// The original data for a certain sprite might be less
writeStream->writeByte(sprite._spriteType); // 0
-
+
// If the sprite is a puppet (controlled by lingo scripting)
// The rest of the data isn't read
if (sprite._puppet) {
Commit: a5887eb10d065b5236fe859d8872e689affa68c8
https://github.com/scummvm/scummvm/commit/a5887eb10d065b5236fe859d8872e689affa68c8
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Save current movie rather than initial movie
Save the movie that calls `saveMovie` rather than the main movie
Changed paths:
engines/director/archive-save.cpp
engines/director/castmember/text.cpp
engines/director/director.cpp
engines/director/lingo/lingo-builtins.cpp
engines/director/movie.cpp
diff --git a/engines/director/archive-save.cpp b/engines/director/archive-save.cpp
index 7517a1d0c2f..fa0764183ad 100644
--- a/engines/director/archive-save.cpp
+++ b/engines/director/archive-save.cpp
@@ -77,7 +77,8 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
ResourceMap castResMap = _types[MKTAG('C', 'A', 'S', 't')];
for (auto &it : builtResources) {
- debugC(5, kDebugSaving, "RIFXArchive::writeToFile: writing resource '%s': index: %d, size: %d, offset = %d", tag2str(it->tag), it->index, it->size, it->offset);
+ debugC(5, kDebugSaving, "RIFXArchive::writeToFile: writing resource '%s': index: %d, size: %d, offset = %d, index: %d",
+ tag2str(it->tag), it->index, it->size, it->offset, it->index);
switch (it->tag) {
case MKTAG('R', 'I', 'F', 'X'):
@@ -151,7 +152,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
break;
default:
- debugC(7, kDebugLoading, "Saving resource %s as it is, without modification", tag2str(it->tag));
+ debugC(7, kDebugSaving, "Saving resource %s as it is, without modification", tag2str(it->tag));
writeStream->seek(it->offset);
writeStream->writeUint32LE(it->tag);
writeStream->writeUint32LE(it->size);
@@ -161,7 +162,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
}
if (path.empty()) {
- path = Common::Path(movie->getMacName());
+ path = g_director->_gameDataDir.getPath().join(Common::Path(movie->getMacName()));
}
Common::DumpFile out;
@@ -171,7 +172,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
out.write(dumpData, _size);
out.flush();
out.close();
- debugC(3, kDebugLoading, "RIFXArchive::writeStream:Saved the movie as file %s", path.toString().c_str());
+ debugC(3, kDebugSaving, "RIFXArchive::writeStream:Saved the movie as file %s", path.toString().c_str());
} else {
warning("RIFXArchive::writeStream: Error saving the file %s", path.toString().c_str());
}
@@ -622,7 +623,19 @@ uint32 RIFXArchive::getCASResourceSize(uint32 castLib) {
uint32 RIFXArchive::getKeyTableResourceSize() {
// 12 bytes of header + 12 * number of entries
- return 12 + _keyTableUsedCount * 12;
+ uint32 size = 0;
+ for (auto &childTag : _keyData) {
+ KeyMap keyMap = childTag._value;
+
+ for (auto &parentIndex : keyMap) {
+ KeyArray keyArray = parentIndex._value;
+
+ for (auto _ : keyArray) {
+ size += 12;
+ }
+ }
+ }
+ return size + 12;
}
void dumpFile(Common::String fileName, uint32 id, uint32 tag, byte *dumpData, uint32 dumpSize) {
diff --git a/engines/director/castmember/text.cpp b/engines/director/castmember/text.cpp
index 61919d4d199..b35d6dbf217 100644
--- a/engines/director/castmember/text.cpp
+++ b/engines/director/castmember/text.cpp
@@ -988,7 +988,7 @@ uint32 TextCastMember::getCastDataSize() {
}
uint32 TextCastMember::writeSTXTResource(Common::MemoryWriteStream *writeStream, uint32 offset) {
- debugC(3, kDebugSaving, "writeSTXTResource(): _ptext: %s\n_ftext = %s\n_rtext: %s",
+ debugC(3, kDebugSaving, "writeSTXTResource(): _ptext: %s\n_ftext = %s\n_rtext: %s",
_ptext.encode().c_str(), Common::toPrintable(_ftext).encode().c_str(), Common::toPrintable(_rtext).c_str());
uint32 stxtSize = getSTXTResourceSize() + 8;
@@ -1017,12 +1017,12 @@ uint32 TextCastMember::writeSTXTResource(Common::MemoryWriteStream *writeStream,
uint32 it = 0;
uint32 pIndex = 0;
-
+
if (!_ftext.empty()) {
while (it < _ftext.size() - 1) {
if (_ftext[it] == '\001' && _ftext[it + 1] == '\016') {
// Styling header found
- debugC(3, kDebugSaving, "Format start offset: %d, text: %s", style.formatStartOffset,
+ debugC(3, kDebugSaving, "Format start offset: %d, text: %s", style.formatStartOffset,
Common::toPrintable(_ptext.substr(style.formatStartOffset, pIndex - style.formatStartOffset)).encode().c_str());
Common::CodePage encoding = detectFontEncoding(_cast->_platform, style.fontId);
@@ -1065,7 +1065,7 @@ uint32 TextCastMember::writeSTXTResource(Common::MemoryWriteStream *writeStream,
pIndex = _ptext.size() - 1;
}
- debugC(3, kDebugSaving, "format start offset: %d, text: %s", style.formatStartOffset,
+ debugC(3, kDebugSaving, "format start offset: %d, text: %s", style.formatStartOffset,
Common::toPrintable(_ptext.substr(style.formatStartOffset, pIndex - style.formatStartOffset)).encode().c_str());
Common::CodePage encoding = detectFontEncoding(_cast->_platform, style.fontId);
@@ -1073,7 +1073,7 @@ uint32 TextCastMember::writeSTXTResource(Common::MemoryWriteStream *writeStream,
rawText += _ptext.substr(style.formatStartOffset, pIndex - style.formatStartOffset).encode(encoding);
debug("ptext length: %d, rawText length: %d", _ptext.size(), rawText.size());
- uint64 currentPos = writeStream->pos();
+ uint64 currentPos = writeStream->pos();
writeStream->seek(textPos);
writeStream->writeString(rawText);
writeStream->seek(currentPos);
@@ -1097,16 +1097,21 @@ uint32 TextCastMember::writeSTXTResource(Common::MemoryWriteStream *writeStream,
}
uint32 TextCastMember::getSTXTResourceSize() {
- // Header (offset, string length, data length) + text string + data (FontStyle)
+ // Header (offset, string length, data length) + text string + data (FontStyle)
return 12 + _ptext.size() + getFormattingCount() * 20 + 2;
}
uint8 TextCastMember::getFormattingCount() {
+ if (_ftext.c_str() == nullptr) {
+ warning("TextCastMember::getFormattingCount(): The Text cast member has invalid formatted text");
+ return 0;
+ }
+
if (_ftext.empty()) {
return 0;
}
- uint8 count = 0;
+ uint8 count = 0;
for (uint32 i = 0; i < _ftext.size() - 1; i++) {
if (_ftext[i] == '\001' && _ftext[i + 1] == '\016') {
count++;
diff --git a/engines/director/director.cpp b/engines/director/director.cpp
index 90077adddc9..e6cf9920118 100644
--- a/engines/director/director.cpp
+++ b/engines/director/director.cpp
@@ -383,8 +383,8 @@ Common::Error DirectorEngine::run() {
_system->setImGuiCallbacks(ImGuiCallbacks());
#endif
- if (debugChannelSet(-1, kDebugSaving)) {
- Common::Path writePath("./dumps/writtenMovie.dir");
+ if (debugChannelSet(10, kDebugSaving)) {
+ Common::Path writePath;
_mainArchive->writeToFile(writePath, getCurrentMovie());
}
diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index a94f0ff566c..a8849599905 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -1577,7 +1577,7 @@ void LB::b_save(int nargs) {
void LB::b_saveMovie(int nargs) {
Common::Path path;
if (nargs) {
- path = Common::Path("./" + g_lingo->pop().asString());
+ path = Common::Path(g_director->_gameDataDir.getPath().join(g_lingo->pop().asString()));
}
g_director->getMainArchive()->writeToFile(path, g_director->getCurrentMovie());
}
diff --git a/engines/director/movie.cpp b/engines/director/movie.cpp
index 1c456ba5002..5a53e6a456e 100644
--- a/engines/director/movie.cpp
+++ b/engines/director/movie.cpp
@@ -745,12 +745,8 @@ void InfoEntry::writeString(Common::String string, bool pascal) {
data = (byte *)malloc(len);
- uint16 start = pascal ? 1 : 0;
-
- for (uint16 i = start; i < len && string.size(); i++) {
- data[i] = string.firstChar();
- string.deleteChar(0);
- }
+ uint16 start = pascal ? 1 : 0;
+ memcpy(data + start, string.c_str(), string.size());
}
} // End of namespace Director
Commit: 4228fd0026d13499aab5f452224b6d3ed73996ee
https://github.com/scummvm/scummvm/commit/4228fd0026d13499aab5f452224b6d3ed73996ee
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Convert all `MemoryWriteStream` to `SeekableWriteStream` while
saving
To be compatible with `Common::OutSaveFile`
Changed paths:
engines/director/archive-save.cpp
engines/director/archive.h
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/digitalvideo.cpp
engines/director/castmember/digitalvideo.h
engines/director/castmember/filmloop.cpp
engines/director/castmember/filmloop.h
engines/director/castmember/palette.cpp
engines/director/castmember/palette.h
engines/director/castmember/richtext.cpp
engines/director/castmember/richtext.h
engines/director/castmember/script.cpp
engines/director/castmember/script.h
engines/director/castmember/shape.cpp
engines/director/castmember/shape.h
engines/director/castmember/sound.cpp
engines/director/castmember/sound.h
engines/director/castmember/text.cpp
engines/director/castmember/text.h
engines/director/castmember/transition.cpp
engines/director/castmember/transition.h
engines/director/director.cpp
engines/director/frame.cpp
engines/director/frame.h
engines/director/lingo/lingo-builtins.cpp
engines/director/movie.cpp
engines/director/movie.h
engines/director/score.cpp
engines/director/score.h
engines/director/stxt.cpp
engines/director/stxt.h
diff --git a/engines/director/archive-save.cpp b/engines/director/archive-save.cpp
index fa0764183ad..1774d1f9dcc 100644
--- a/engines/director/archive-save.cpp
+++ b/engines/director/archive-save.cpp
@@ -20,6 +20,8 @@
*/
#include "common/memstream.h"
+#include "common/savefile.h"
+#include "common/config-manager.h"
#include "director/director.h"
#include "director/archive.h"
@@ -38,36 +40,42 @@
namespace Director {
-bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
+bool RIFXArchive::writeToFile(Common::String filename, Movie *movie) {
+ // If the filename is empty, we save the movie with the name of the current movie
+ if (filename.empty()) {
+ filename = movie->getMacName();
+ }
+
+ Common::OutSaveFile *saveFile = g_engine->getSaveFileManager()->openForSaving(g_director->getTargetName() + "-" + filename);
+
+ if (!saveFile) {
+ warning("RIFXArchive::writeToFile: Failed to open file %s for saving");
+ return false;
+ }
+
// Update the resources, their sizes and offsets
Common::Array<Resource *> builtResources = rebuildResources(movie);
// ignoring the startOffset
// For RIFX stream, moreoffset = 0, we won't be writing macbinary
- byte *dumpData = nullptr;
-
// Don't need to allocate this much size in case 'junk' and 'free' resources are ignored
// Or might need to allocate even more size if extra chunks are written
_size = getArchiveSize(builtResources);
- dumpData = (byte *)calloc(_size, sizeof(byte));
+ saveFile->writeUint32LE(_metaTag); // The _metaTag is "RIFX" or "XFIR"
- Common::SeekableMemoryWriteStream *writeStream = new Common::SeekableMemoryWriteStream(dumpData, _size);
-
- writeStream->writeUint32LE(_metaTag); // The _metaTag is "RIFX" or "XFIR"
-
- writeStream->writeUint32LE(getResourceSize(_metaTag, 0) - 8); // The size of the RIFX archive, except header and size
- writeStream->writeUint32LE(_rifxType); // e.g. "MV93", "MV95"
+ saveFile->writeUint32LE(getResourceSize(_metaTag, 0) - 8); // The size of the RIFX archive, except header and size
+ saveFile->writeUint32LE(_rifxType); // e.g. "MV93", "MV95"
switch (_rifxType) {
case MKTAG('M', 'V', '9', '3'):
case MKTAG('M', 'C', '9', '5'):
case MKTAG('A', 'P', 'P', 'L'):
- writeMemoryMap(writeStream, builtResources);
+ writeMemoryMap(saveFile, builtResources);
break;
case MKTAG('F', 'G', 'D', 'M'):
case MKTAG('F', 'G', 'D', 'C'):
- writeAfterBurnerMap(writeStream);
+ writeAfterBurnerMap(saveFile);
break;
default:
break;
@@ -92,19 +100,19 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
break;
case MKTAG('K', 'E', 'Y', '*'):
- writeKeyTable(writeStream, it->offset);
+ writeKeyTable(saveFile, it->offset);
break;
case MKTAG('C', 'A', 'S', '*'):
- writeCast(writeStream, it->offset, it->libResourceId);
+ writeCast(saveFile, it->offset, it->libResourceId);
break;
case MKTAG('C', 'A', 'S', 't'):
- cast->saveCastData(writeStream, it);
+ cast->saveCastData(saveFile, it);
break;
case MKTAG('V', 'W', 'C', 'F'):
- cast->saveConfig(writeStream, it->offset);
+ cast->saveConfig(saveFile, it->offset);
break;
case MKTAG('B', 'I', 'T', 'D'):
@@ -113,7 +121,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
Resource parent = castResMap[parentIndex];
BitmapCastMember *target = (BitmapCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
- target->writeBITDResource(writeStream, it->offset);
+ target->writeBITDResource(saveFile, it->offset);
}
break;
@@ -123,7 +131,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
Resource parent = castResMap[parentIndex];
TextCastMember *target = (TextCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
- target->writeSTXTResource(writeStream, it->offset);
+ target->writeSTXTResource(saveFile, it->offset);
}
break;
@@ -133,7 +141,7 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
Resource parent = castResMap[parentIndex];
PaletteCastMember *target = (PaletteCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
- target->writePaletteData(writeStream, it->offset);
+ target->writePaletteData(saveFile, it->offset);
}
break;
@@ -143,46 +151,39 @@ bool RIFXArchive::writeToFile(Common::Path path, Movie *movie) {
Resource parent = castResMap[parentIndex];
FilmLoopCastMember *target = (FilmLoopCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
- target->writeSCVWResource(writeStream, it->offset);
+ target->writeSCVWResource(saveFile, it->offset);
}
break;
case MKTAG('V', 'W', 'S', 'C'):
- movie->getScore()->writeVWSCResource(writeStream, it->offset);
+ movie->getScore()->writeVWSCResource(saveFile, it->offset);
break;
default:
debugC(7, kDebugSaving, "Saving resource %s as it is, without modification", tag2str(it->tag));
- writeStream->seek(it->offset);
- writeStream->writeUint32LE(it->tag);
- writeStream->writeUint32LE(it->size);
- writeStream->writeStream(getResource(it->tag, it->index));
+ saveFile->seek(it->offset, SEEK_SET);
+ saveFile->writeUint32LE(it->tag);
+ saveFile->writeUint32LE(it->size);
+ saveFile->writeStream(getResource(it->tag, it->index));
break;
}
}
- if (path.empty()) {
- path = g_director->_gameDataDir.getPath().join(Common::Path(movie->getMacName()));
- }
-
Common::DumpFile out;
// Write the movie out, stored in dumpData
- if (out.open(path, true)) {
- out.write(dumpData, _size);
- out.flush();
- out.close();
- debugC(3, kDebugSaving, "RIFXArchive::writeStream:Saved the movie as file %s", path.toString().c_str());
+ if (saveFile) {
+ saveFile->flush();
+ debugC(3, kDebugSaving, "RIFXArchive::writeStream:Saved the movie as file %s", filename.c_str());
} else {
- warning("RIFXArchive::writeStream: Error saving the file %s", path.toString().c_str());
+ warning("RIFXArchive::writeStream: Error saving the file %s", filename.c_str());
}
- free(dumpData);
- delete writeStream;
+ delete saveFile;
return true;
}
-bool RIFXArchive::writeMemoryMap(Common::SeekableMemoryWriteStream *writeStream, Common::Array<Resource *> resources) {
+bool RIFXArchive::writeMemoryMap(Common::SeekableWriteStream *writeStream, Common::Array<Resource *> resources) {
Resource mmap;
for (auto it : resources) {
@@ -230,7 +231,7 @@ bool RIFXArchive::writeMemoryMap(Common::SeekableMemoryWriteStream *writeStream,
return true;
}
-bool RIFXArchive::writeAfterBurnerMap(Common::SeekableMemoryWriteStream *writeStream) {
+bool RIFXArchive::writeAfterBurnerMap(Common::SeekableWriteStream *writeStream) {
warning("RIFXArchive::writeAfterBurnerMap: STUB: Incomplete function, needs further changes, AfterBurnerMap not written");
return false;
@@ -258,7 +259,7 @@ bool RIFXArchive::writeAfterBurnerMap(Common::SeekableMemoryWriteStream *writeSt
#endif
}
-bool RIFXArchive::writeKeyTable(Common::SeekableMemoryWriteStream *writeStream, uint32 offset) {
+bool RIFXArchive::writeKeyTable(Common::SeekableWriteStream *writeStream, uint32 offset) {
writeStream->seek(offset);
writeStream->writeUint32LE(MKTAG('K', 'E', 'Y', '*'));
diff --git a/engines/director/archive.h b/engines/director/archive.h
index fa236e43fab..36de722650e 100644
--- a/engines/director/archive.h
+++ b/engines/director/archive.h
@@ -79,7 +79,7 @@ public:
virtual bool openFile(const Common::Path &path);
virtual bool openStream(Common::SeekableReadStream *stream, uint32 offset = 0) = 0;
- virtual bool writeToFile(Common::Path path, Movie *movie) {
+ virtual bool writeToFile(Common::String filename, Movie *movie) {
// Saving Director movies was introduced in Director 4
// However, from DirectorEngine::createArchive, it is evident that after Director 4 only RIFX Archives were written
error("Archive::writeToFile was called on a non-RIFX Archive, which is not allowed");
@@ -166,7 +166,7 @@ public:
~RIFXArchive() override;
bool openStream(Common::SeekableReadStream *stream, uint32 startOffset = 0) override;
- bool writeToFile(Common::Path writePath, Movie *movie) override;
+ bool writeToFile(Common::String filename, Movie *movie) override;
Common::SeekableReadStreamEndian *getFirstResource(uint32 tag) override;
virtual Common::SeekableReadStreamEndian *getFirstResource(uint32 tag, bool fileEndianness);
@@ -179,9 +179,9 @@ public:
private:
/* archive-save.cpp */
/* These functions are for writing movies */
- bool writeMemoryMap(Common::SeekableMemoryWriteStream *writeStream, Common::Array<Resource *> resource); // Parallel to readMemoryMap
- bool writeAfterBurnerMap(Common::SeekableMemoryWriteStream *writeStreaa); // Parallel to readAfterBurnerMap
- bool writeKeyTable(Common::SeekableMemoryWriteStream *writeStream, uint32 offset); // Parallel to readKeyTable
+ bool writeMemoryMap(Common::SeekableWriteStream *writeStream, Common::Array<Resource *> resource); // Parallel to readMemoryMap
+ bool writeAfterBurnerMap(Common::SeekableWriteStream *writeStreaa); // Parallel to readAfterBurnerMap
+ bool writeKeyTable(Common::SeekableWriteStream *writeStream, uint32 offset); // Parallel to readKeyTable
bool writeCast(Common::SeekableWriteStream *writeStream, uint32 offset, uint32 castLib); // Parallel to readCast
uint32 getArchiveSize(Common::Array<Resource *> resources);
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index 44b022293e0..afd602bff6a 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -529,7 +529,7 @@ bool Cast::loadConfig() {
return true;
}
-void Cast::saveConfig(Common::MemoryWriteStream *writeStream, uint32 offset) {
+void Cast::saveConfig(Common::SeekableWriteStream *writeStream, uint32 offset) {
if (_version < kFileVer400) {
error("Cast::saveConfig called on a pre-D4 Director movie");
}
@@ -820,7 +820,7 @@ void Cast::loadCast() {
}
}
-void Cast::saveCastData(Common::MemoryWriteStream *writeStream, Resource *res) {
+void Cast::saveCastData(Common::SeekableWriteStream *writeStream, Resource *res) {
// This offset is at which we will start writing our 'CASt' resources
// In the original file, all the 'CASt' resources don't necessarily appear side by side
uint32 offset = res->offset;
@@ -874,7 +874,7 @@ void Cast::saveCastData(Common::MemoryWriteStream *writeStream, Resource *res) {
}
}
-void Cast::writeCastInfo(Common::MemoryWriteStream *writeStream, uint32 castId) {
+void Cast::writeCastInfo(Common::SeekableWriteStream *writeStream, uint32 castId) {
// The structure of the CastMemberInfo is as follows:
// First some headers: offset, unknown and flags, and then a count of strings to be read
// (These strings contain properties of the cast member like filename, script attached to it, name, etc.)
diff --git a/engines/director/cast.h b/engines/director/cast.h
index 41c8ccce1c7..208a4e65509 100644
--- a/engines/director/cast.h
+++ b/engines/director/cast.h
@@ -106,15 +106,15 @@ public:
void loadExternalSound(Common::SeekableReadStreamEndian &stream);
void loadSord(Common::SeekableReadStreamEndian &stream);
- void saveConfig(Common::MemoryWriteStream *writeStream, uint32 offset);
- void saveCastData(Common::MemoryWriteStream *writeStream, Resource *res);
+ void saveConfig(Common::SeekableWriteStream *writeStream, uint32 offset);
+ void saveCastData(Common::SeekableWriteStream *writeStream, Resource *res);
void saveCastData();
- void writeCastInfo(Common::MemoryWriteStream *writeStream, uint32 castId);
+ void writeCastInfo(Common::SeekableWriteStream *writeStream, uint32 castId);
uint32 getCastInfoSize(uint32 castId);
uint32 getCastInfoStringLength(uint32 stringIndex, CastMemberInfo *ci);
uint32 getConfigSize();
-
+
int getCastSize();
int getCastMaxID();
int getNextUnusedID();
diff --git a/engines/director/castmember/bitmap.cpp b/engines/director/castmember/bitmap.cpp
index d9a4e2597a7..142b4fcd458 100644
--- a/engines/director/castmember/bitmap.cpp
+++ b/engines/director/castmember/bitmap.cpp
@@ -1006,7 +1006,7 @@ uint32 BitmapCastMember::getCastDataSize() {
return dataSize;
}
-void BitmapCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
+void BitmapCastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
writeStream->writeUint16BE(_pitch);
Movie::writeRect(writeStream, _initialRect);
@@ -1045,7 +1045,7 @@ void BitmapCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
// Ignoring the tail during loading as well as saving
}
-uint32 BitmapCastMember::writeBITDResource(Common::MemoryWriteStream *writeStream, uint32 offset) {
+uint32 BitmapCastMember::writeBITDResource(Common::SeekableWriteStream *writeStream, uint32 offset) {
writeStream->seek(offset);
writeStream->writeUint32LE(MKTAG('B', 'I', 'T', 'D'));
diff --git a/engines/director/castmember/bitmap.h b/engines/director/castmember/bitmap.h
index 210bcf7373a..77aa9d01f2e 100644
--- a/engines/director/castmember/bitmap.h
+++ b/engines/director/castmember/bitmap.h
@@ -64,8 +64,8 @@ public:
CollisionTest isWithin(const Common::Rect &bbox, const Common::Point &pos, InkType ink) override;
- void writeCastData(Common::MemoryWriteStream *writeStream) override;
- uint32 writeBITDResource(Common::MemoryWriteStream *writeStream, uint32 offset);
+ void writeCastData(Common::SeekableWriteStream *writeStream) override;
+ uint32 writeBITDResource(Common::SeekableWriteStream *writeStream, uint32 offset);
uint32 getCastDataSize() override; // This is the size of the data in the 'CASt' resource
uint32 getBITDResourceSize();
diff --git a/engines/director/castmember/castmember.cpp b/engines/director/castmember/castmember.cpp
index f9f2466daa5..0df8aa7c04f 100644
--- a/engines/director/castmember/castmember.cpp
+++ b/engines/director/castmember/castmember.cpp
@@ -315,7 +315,7 @@ void CastMember::unload() {
// Whereas _info_ is metadata (size, name, flags, etc.)
// Some cast members have their _data_ as well as _info_ in this very 'CASt' resource, e.g. TextCastMember
// Whereas some other have their _info_ in a 'CASt' resource and _data_ in a dedicated resource (e.g. PaletteCastMember has 'CLUT' resource)
-uint32 CastMember::writeCAStResource(Common::MemoryWriteStream *writeStream) {
+uint32 CastMember::writeCAStResource(Common::SeekableWriteStream *writeStream) {
uint32 castResourceSize = getCastResourceSize();
writeStream->writeUint32LE(MKTAG('C', 'A', 'S', 't'));
@@ -370,7 +370,7 @@ uint32 CastMember::getCastDataSize() {
return _castDataSize;
}
-void CastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
+void CastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
warning("CastMember::getDataSize(): Defualt implementation of 'CASt' resource data");
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
diff --git a/engines/director/castmember/castmember.h b/engines/director/castmember/castmember.h
index 2152010c2f4..8123fdb91ee 100644
--- a/engines/director/castmember/castmember.h
+++ b/engines/director/castmember/castmember.h
@@ -105,10 +105,10 @@ public:
// Three parts to a 'CASt' resource (header + _info_, _data_)
// The headers, are common, the _info_ writing is handled by the Cast class, so no worries there
// So, the only thing that differs is the _data_, for which we'll have separate implementations for each CastMember
- uint32 writeCAStResource(Common::MemoryWriteStream *writeStream);
+ uint32 writeCAStResource(Common::SeekableWriteStream *writeStream);
uint32 getCastInfoSize();
uint32 getCastResourceSize();
- virtual void writeCastData(Common::MemoryWriteStream *writeStream);
+ virtual void writeCastData(Common::SeekableWriteStream *writeStream);
virtual uint32 getCastDataSize();
CastType _type;
@@ -125,14 +125,14 @@ public:
uint32 _castDataSize;
uint8 _flags1;
// This index is the index that it appears in the original movie archive
- int16 _index;
+ int16 _index;
protected:
Cast *_cast;
// This is the id of the cast member, this id is unique to only cast members
// Basically the cast members are given ids starting from _castArrayStart to _castArrayEnd
// e.g. 0, 1, 2, 3, 4
- uint16 _castId;
+ uint16 _castId;
// a link to the widget we created, we may use it later
Graphics::MacWidget *_widget;
bool _loaded;
diff --git a/engines/director/castmember/digitalvideo.cpp b/engines/director/castmember/digitalvideo.cpp
index f5d94c2df28..28e0528141e 100644
--- a/engines/director/castmember/digitalvideo.cpp
+++ b/engines/director/castmember/digitalvideo.cpp
@@ -667,7 +667,7 @@ uint32 DigitalVideoCastMember::getCastDataSize() {
return 0;
}
-void DigitalVideoCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
+void DigitalVideoCastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
Movie::writeRect(writeStream, _initialRect);
writeStream->writeUint32BE(_vflags);
}
diff --git a/engines/director/castmember/digitalvideo.h b/engines/director/castmember/digitalvideo.h
index a5f126f8916..4527d4bbfd2 100644
--- a/engines/director/castmember/digitalvideo.h
+++ b/engines/director/castmember/digitalvideo.h
@@ -72,7 +72,7 @@ public:
Common::Point getRegistrationOffset(int16 width, int16 height) override;
uint32 getCastDataSize() override;
- void writeCastData(Common::MemoryWriteStream *writeStream) override;
+ void writeCastData(Common::SeekableWriteStream *writeStream) override;
Common::String _filename;
diff --git a/engines/director/castmember/filmloop.cpp b/engines/director/castmember/filmloop.cpp
index 360aaeeb39d..7b6ee726724 100644
--- a/engines/director/castmember/filmloop.cpp
+++ b/engines/director/castmember/filmloop.cpp
@@ -19,7 +19,6 @@
*
*/
-#include "common/stream.h"
#include "common/memstream.h"
#include "graphics/surface.h"
#include "graphics/macgui/macwidget.h"
@@ -731,7 +730,7 @@ uint32 FilmLoopCastMember::getCastDataSize() {
return 0;
}
-void FilmLoopCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
+void FilmLoopCastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
Movie::writeRect(writeStream, _initialRect);
uint32 flags = 0;
@@ -751,8 +750,8 @@ void FilmLoopCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
writeStream->writeUint16LE(0); // May need to save proper value in the future, currently ignored
}
-void FilmLoopCastMember::writeSCVWResource(Common::MemoryWriteStream *writeStream, uint32 offset) {
- uint32 channelSize = 0;
+void FilmLoopCastMember::writeSCVWResource(Common::SeekableWriteStream *writeStream, uint32 offset) {
+ uint32 channelSize = 0;
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
channelSize = kSprChannelSizeD4;
} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
@@ -777,13 +776,13 @@ void FilmLoopCastMember::writeSCVWResource(Common::MemoryWriteStream *writeStrea
writeStream->writeUint32BE(frameOffset); // framesOffset
writeStream->seek(6, SEEK_CUR); // Ignored data
writeStream->writeUint16BE(channelSize);
- writeStream->seek(frameOffset - 16, SEEK_CUR); // Ignored data
+ writeStream->seek(frameOffset - 16, SEEK_CUR); // Ignored data
// The structure of the filmloop 'SCVW' data is as follows
// The 'SCVW' tag -> the size of the resource ->
// frameoffset (This offset is where the frame date actually starts) ->
// Some headers which we ignore except the Sprite Channel Size (which we also ignore during loading) ->
-
+
// until there are no more frames
// size of the frame ->
// until there are no more channels in the frame
@@ -798,7 +797,7 @@ void FilmLoopCastMember::writeSCVWResource(Common::MemoryWriteStream *writeStrea
int channel = it._key;
// TODO: For now writing the order considering that each sprite will have 20 bytes of data
// In the future, for optimization, we can actually calculate the data of each sprite
- // And write the order accordingly
+ // And write the order accordingly
// But for this we'll need a way to find how many data values (out of 20) of a sprite are valid, i.e. determine message width
// this means while loading, the channelOffset will always be 0, order will always be multiple of 20
// And message width will always be 20
@@ -814,9 +813,9 @@ void FilmLoopCastMember::writeSCVWResource(Common::MemoryWriteStream *writeStrea
writeSpriteDataD5(writeStream, sprite);
}
}
-
- }
-
+
+ }
+
if (debugChannelSet(7, kDebugSaving)) {
// Adding +8 because the stream doesn't include the header and the entry for the size itself
byte *dumpData = (byte *)calloc(filmloopSize + 8, sizeof(byte));
@@ -845,14 +844,14 @@ uint32 FilmLoopCastMember::getSCVWResourceSize() {
}
uint32 framesSize = 0;
- for (FilmLoopFrame frame : _frames) {
+ for (FilmLoopFrame frame : _frames) {
// Frame size
framesSize += 2;
for (auto it : frame.sprites) {
// message width: 2 bytes
// order: 2 bytes
// Sprite data: 20 bytes
- framesSize += 2 + 2 + channelSize;
+ framesSize += 2 + 2 + channelSize;
}
}
diff --git a/engines/director/castmember/filmloop.h b/engines/director/castmember/filmloop.h
index c65b6f9f1ec..762904a9ec0 100644
--- a/engines/director/castmember/filmloop.h
+++ b/engines/director/castmember/filmloop.h
@@ -59,9 +59,9 @@ public:
Common::Point getRegistrationOffset(int16 currentWidth, int16 currentHeight) override;
uint32 getCastDataSize() override;
- void writeCastData(Common::MemoryWriteStream *writeStream) override;
+ void writeCastData(Common::SeekableWriteStream *writeStream) override;
- void writeSCVWResource(Common::MemoryWriteStream *writeStream, uint32 offset);
+ void writeSCVWResource(Common::SeekableWriteStream *writeStream, uint32 offset);
uint32 getSCVWResourceSize();
bool _enableSound;
diff --git a/engines/director/castmember/palette.cpp b/engines/director/castmember/palette.cpp
index 8aa8a364e32..7df8936acec 100644
--- a/engines/director/castmember/palette.cpp
+++ b/engines/director/castmember/palette.cpp
@@ -19,8 +19,6 @@
*
*/
-#include "common/substream.h"
-#include "common/macresman.h"
#include "common/memstream.h"
#include "director/director.h"
@@ -47,13 +45,13 @@ PaletteCastMember::PaletteCastMember(Cast *cast, uint16 castId, PaletteCastMembe
_palette = source._palette ? new PaletteV4(*source._palette) : nullptr;
}
-PaletteCastMember::PaletteCastMember(Cast *cast, uint16 castId, byte *paletteData, PaletteV4 *pal)
+PaletteCastMember::PaletteCastMember(Cast *cast, uint16 castId, byte *paletteData, PaletteV4 *pal)
: CastMember(cast, castId) {
_type = kCastPalette;
_palette = new PaletteV4(pal->id, paletteData, pal->length);
_loaded = true;
}
-
+
// Need to make a deep copy
CastMember *PaletteCastMember::duplicate(Cast *cast, uint16 castId) {
byte *buf = (byte *)malloc(_palette->length);
@@ -150,7 +148,7 @@ uint32 PaletteCastMember::getCastDataSize() {
return 0;
}
-void PaletteCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
+void PaletteCastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
// This should never get triggered
// Since there is no data to write
}
@@ -163,7 +161,7 @@ uint32 PaletteCastMember::getPaletteDataSize() {
return _palette->length * 6;
}
-void PaletteCastMember::writePaletteData(Common::MemoryWriteStream *writeStream, uint32 offset) {
+void PaletteCastMember::writePaletteData(Common::SeekableWriteStream *writeStream, uint32 offset) {
uint32 castSize = getPaletteDataSize();
writeStream->seek(offset);
@@ -175,7 +173,7 @@ void PaletteCastMember::writePaletteData(Common::MemoryWriteStream *writeStream,
for (int i = 0; i < _palette->length; i++) {
// Duplicating the data to convert to 16-bit
// The palette data is converted to 8-bit at the time of loading
- writeStream->writeUint16BE(pal[3 * i] << 8);
+ writeStream->writeUint16BE(pal[3 * i] << 8);
writeStream->writeUint16BE(pal[3 * i + 1] << 8);
writeStream->writeUint16BE(pal[3 * i + 2] << 8);
}
diff --git a/engines/director/castmember/palette.h b/engines/director/castmember/palette.h
index a9d13b5f97a..de5cf1ff98c 100644
--- a/engines/director/castmember/palette.h
+++ b/engines/director/castmember/palette.h
@@ -43,13 +43,12 @@ public:
void load() override;
void unload() override;
- uint32 writeSTXTResource(Common::MemoryWriteStream *writeStream, uint32 offset);
uint32 getCastDataSize() override; // This is the size of the data in the 'CASt' resource
- void writeCastData(Common::MemoryWriteStream *writeStream) override;
+ void writeCastData(Common::SeekableWriteStream *writeStream) override;
- void writePaletteData(Common::MemoryWriteStream *writeStream, uint32 offset);
+ void writePaletteData(Common::SeekableWriteStream *writeStream, uint32 offset);
uint32 getPaletteDataSize();
-
+
PaletteV4 *_palette;
};
diff --git a/engines/director/castmember/richtext.cpp b/engines/director/castmember/richtext.cpp
index 3e357b9c16d..c3f7eade8cf 100644
--- a/engines/director/castmember/richtext.cpp
+++ b/engines/director/castmember/richtext.cpp
@@ -19,7 +19,7 @@
*
*/
-#include "common/memstream.h"
+#include "common/stream.h"
#include "graphics/macgui/macwidget.h"
#include "director/director.h"
@@ -246,7 +246,7 @@ uint32 RichTextCastMember::getCastDataSize() {
}
}
-void RichTextCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
+void RichTextCastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
Movie::writeRect(writeStream, _initialRect);
Movie::writeRect(writeStream, _boundingRect);
diff --git a/engines/director/castmember/richtext.h b/engines/director/castmember/richtext.h
index 006a931a247..6a2bdd524dd 100644
--- a/engines/director/castmember/richtext.h
+++ b/engines/director/castmember/richtext.h
@@ -46,7 +46,7 @@ public:
bool setField(int field, const Datum &value) override;
uint32 getCastDataSize() override;
- void writeCastData(Common::MemoryWriteStream *writeStream) override;
+ void writeCastData(Common::SeekableWriteStream *writeStream) override;
Common::String formatInfo() override;
diff --git a/engines/director/castmember/script.cpp b/engines/director/castmember/script.cpp
index aebf7375510..a3c5fddecc5 100644
--- a/engines/director/castmember/script.cpp
+++ b/engines/director/castmember/script.cpp
@@ -19,7 +19,7 @@
*
*/
-#include "common/memstream.h"
+#include "common/stream.h"
#include "director/director.h"
#include "director/cast.h"
#include "director/castmember/script.h"
@@ -154,7 +154,7 @@ uint32 ScriptCastMember::getCastDataSize() {
}
}
-void ScriptCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
+void ScriptCastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer600) {
writeStream->writeByte(0); // unknown
diff --git a/engines/director/castmember/script.h b/engines/director/castmember/script.h
index cd3d4bfd869..bf2b906c59e 100644
--- a/engines/director/castmember/script.h
+++ b/engines/director/castmember/script.h
@@ -40,7 +40,7 @@ public:
bool setField(int field, const Datum &value) override;
uint32 getCastDataSize() override;
- void writeCastData(Common::MemoryWriteStream *writeStream) override;
+ void writeCastData(Common::SeekableWriteStream *writeStream) override;
Common::String formatInfo() override;
};
diff --git a/engines/director/castmember/shape.cpp b/engines/director/castmember/shape.cpp
index 45bccae24d3..116e175c136 100644
--- a/engines/director/castmember/shape.cpp
+++ b/engines/director/castmember/shape.cpp
@@ -19,7 +19,7 @@
*
*/
-#include "common/memstream.h"
+#include "common/stream.h"
#include "director/director.h"
#include "director/cast.h"
#include "director/movie.h"
@@ -236,7 +236,7 @@ uint32 ShapeCastMember::getCastDataSize() {
}
}
-void ShapeCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
+void ShapeCastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
writeStream->writeByte(0);
writeStream->writeByte(1);
diff --git a/engines/director/castmember/shape.h b/engines/director/castmember/shape.h
index f685075b3ff..bf6b38fa4e3 100644
--- a/engines/director/castmember/shape.h
+++ b/engines/director/castmember/shape.h
@@ -45,7 +45,7 @@ public:
Common::String formatInfo() override;
uint32 getCastDataSize() override;
- void writeCastData(Common::MemoryWriteStream *writeStream) override;
+ void writeCastData(Common::SeekableWriteStream *writeStream) override;
ShapeType _shapeType;
uint16 _pattern;
diff --git a/engines/director/castmember/sound.cpp b/engines/director/castmember/sound.cpp
index 85b3ae62231..ef1a82ed5b5 100644
--- a/engines/director/castmember/sound.cpp
+++ b/engines/director/castmember/sound.cpp
@@ -211,7 +211,7 @@ uint32 SoundCastMember::getCastDataSize() {
return 0;
}
-void SoundCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
+void SoundCastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
// This should never get triggered
// since there is no data to write
}
diff --git a/engines/director/castmember/sound.h b/engines/director/castmember/sound.h
index 2477ea2a5f0..f7b0df09327 100644
--- a/engines/director/castmember/sound.h
+++ b/engines/director/castmember/sound.h
@@ -45,7 +45,7 @@ public:
bool setField(int field, const Datum &value) override;
uint32 getCastDataSize() override;
- void writeCastData(Common::MemoryWriteStream *writeStream) override;
+ void writeCastData(Common::SeekableWriteStream *writeStream) override;
bool _looping;
AudioDecoder *_audio;
diff --git a/engines/director/castmember/text.cpp b/engines/director/castmember/text.cpp
index b35d6dbf217..a225137778a 100644
--- a/engines/director/castmember/text.cpp
+++ b/engines/director/castmember/text.cpp
@@ -955,7 +955,7 @@ bool TextCastMember::setChunkField(int field, int start, int end, const Datum &d
return false;
}
-void TextCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
+void TextCastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
writeStream->writeByte(_borderSize); // 1 byte
writeStream->writeByte(_gutterSize); // 2 bytes
writeStream->writeByte(_boxShadow); // 3 bytes
@@ -987,7 +987,7 @@ uint32 TextCastMember::getCastDataSize() {
return size;
}
-uint32 TextCastMember::writeSTXTResource(Common::MemoryWriteStream *writeStream, uint32 offset) {
+uint32 TextCastMember::writeSTXTResource(Common::SeekableWriteStream *writeStream, uint32 offset) {
debugC(3, kDebugSaving, "writeSTXTResource(): _ptext: %s\n_ftext = %s\n_rtext: %s",
_ptext.encode().c_str(), Common::toPrintable(_ftext).encode().c_str(), Common::toPrintable(_rtext).c_str());
diff --git a/engines/director/castmember/text.h b/engines/director/castmember/text.h
index f1c2fcb193b..5e36fd7921a 100644
--- a/engines/director/castmember/text.h
+++ b/engines/director/castmember/text.h
@@ -92,11 +92,11 @@ public:
void load() override;
void unload() override;
- void writeCastData(Common::MemoryWriteStream *writeStream) override;
+ void writeCastData(Common::SeekableWriteStream *writeStream) override;
uint32 getCastDataSize() override; // This is the size of the data in the 'CASt' resource
uint32 getSTXTResourceSize();
- uint32 writeSTXTResource(Common::MemoryWriteStream *writeStream, uint32 offset);
+ uint32 writeSTXTResource(Common::SeekableWriteStream *writeStream, uint32 offset);
uint8 getFormattingCount();
uint8 _borderSize;
diff --git a/engines/director/castmember/transition.cpp b/engines/director/castmember/transition.cpp
index aac0a4b3dbc..63965eb01dc 100644
--- a/engines/director/castmember/transition.cpp
+++ b/engines/director/castmember/transition.cpp
@@ -140,7 +140,7 @@ uint32 TransitionCastMember::getCastDataSize() {
}
}
-void TransitionCastMember::writeCastData(Common::MemoryWriteStream *writeStream) {
+void TransitionCastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer600) {
writeStream->writeByte(0);
writeStream->writeByte(_chunkSize);
diff --git a/engines/director/castmember/transition.h b/engines/director/castmember/transition.h
index 6498f0d0559..c7368e7cf4b 100644
--- a/engines/director/castmember/transition.h
+++ b/engines/director/castmember/transition.h
@@ -40,7 +40,7 @@ public:
Common::String formatInfo() override;
uint32 getCastDataSize() override;
- void writeCastData(Common::MemoryWriteStream *writeStream) override;
+ void writeCastData(Common::SeekableWriteStream *writeStream) override;
TransitionType _transType;
uint16 _durationMillis;
diff --git a/engines/director/director.cpp b/engines/director/director.cpp
index e6cf9920118..f2eca3dffc7 100644
--- a/engines/director/director.cpp
+++ b/engines/director/director.cpp
@@ -384,8 +384,7 @@ Common::Error DirectorEngine::run() {
#endif
if (debugChannelSet(10, kDebugSaving)) {
- Common::Path writePath;
- _mainArchive->writeToFile(writePath, getCurrentMovie());
+ _mainArchive->writeToFile(Common::String(""), getCurrentMovie());
}
return Common::kNoError;
diff --git a/engines/director/frame.cpp b/engines/director/frame.cpp
index bc45b0ade9c..d76d2cb1dbf 100644
--- a/engines/director/frame.cpp
+++ b/engines/director/frame.cpp
@@ -110,7 +110,7 @@ void Frame::readChannel(Common::MemoryReadStreamEndian &stream, uint16 offset, u
}
}
-void Frame::writeMainChannels(Common::MemoryWriteStream *writeStream, uint16 version) {
+void Frame::writeMainChannels(Common::SeekableWriteStream *writeStream, uint16 version) {
debugC(6, kDebugLoading, "Frame::writeChannel: writing main channels for version %d", version);
if (version >= kFileVer400 && version < kFileVer500) {
@@ -580,7 +580,7 @@ void Frame::readMainChannelsD4(Common::MemoryReadStreamEndian &stream, uint16 of
_mainChannels.transDuration = CLIP<uint16>(_mainChannels.transDuration, 0, 32000); // restrict to 32 secs
}
-void Frame::writeMainChannelsD4(Common::MemoryWriteStream *writeStream) {
+void Frame::writeMainChannelsD4(Common::SeekableWriteStream *writeStream) {
writeStream->writeByte(0); // Unknown: Sound/Tempo/Transition // 0
writeStream->writeByte(_mainChannels.soundType1); // 1
@@ -770,7 +770,7 @@ void readSpriteDataD4(Common::SeekableReadStreamEndian &stream, Sprite &sprite,
sprite._width = sprite._height = 0;
}
-void writeSpriteDataD4(Common::MemoryWriteStream *writeStream, Sprite &sprite) {
+void writeSpriteDataD4(Common::SeekableWriteStream *writeStream, Sprite &sprite) {
// Writing 20 bytes of sprite data
// The original data for a certain sprite might be less
writeStream->writeByte(sprite._scriptId.member); // 0
@@ -957,7 +957,7 @@ void Frame::readMainChannelsD5(Common::MemoryReadStreamEndian &stream, uint16 of
_mainChannels.transDuration = CLIP<uint16>(_mainChannels.transDuration, 0, 32000); // restrict to 32 secs
}
-void Frame::writeMainChannelsD5(Common::MemoryWriteStream *writeStream) {
+void Frame::writeMainChannelsD5(Common::SeekableWriteStream *writeStream) {
writeStream->writeUint16BE(_mainChannels.actionId.castLib); // 0, 1
writeStream->writeUint16BE(_mainChannels.actionId.member); // 2, 3
writeStream->writeUint16BE(_mainChannels.sound1.castLib); // 4, 5
@@ -1148,7 +1148,7 @@ void readSpriteDataD5(Common::SeekableReadStreamEndian &stream, Sprite &sprite,
}
-void writeSpriteDataD5(Common::MemoryWriteStream *writeStream, Sprite &sprite) {
+void writeSpriteDataD5(Common::SeekableWriteStream *writeStream, Sprite &sprite) {
// Writing 20 bytes of sprite data
// The original data for a certain sprite might be less
writeStream->writeByte(sprite._spriteType); // 0
diff --git a/engines/director/frame.h b/engines/director/frame.h
index 910d8c2c80e..f63b2a6a6fc 100644
--- a/engines/director/frame.h
+++ b/engines/director/frame.h
@@ -34,7 +34,7 @@ struct Surface;
namespace Common {
class ReadStreamEndian;
class MemoryReadStreamEndian;
-class MemoryWriteStream;
+class SeekableWriteStream;
}
namespace Director {
@@ -160,7 +160,7 @@ public:
Score *getScore() const { return _score; }
void readChannel(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size, uint16 version);
- void writeMainChannels(Common::MemoryWriteStream *writeStream, uint16 version);
+ void writeMainChannels(Common::SeekableWriteStream *writeStream, uint16 version);
void executeImmediateScripts();
@@ -176,13 +176,13 @@ private:
void readSpriteD4(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size);
void readMainChannelsD4(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size);
- void writeMainChannelsD4(Common::MemoryWriteStream *writeStream);
+ void writeMainChannelsD4(Common::SeekableWriteStream *writeStream);
void readChannelD5(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size);
void readSpriteD5(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size);
void readMainChannelsD5(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size);
- void writeMainChannelsD5(Common::MemoryWriteStream *writeStream);
+ void writeMainChannelsD5(Common::SeekableWriteStream *writeStream);
void readChannelD6(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size);
void readSpriteD6(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size);
@@ -205,8 +205,8 @@ void readSpriteDataD4(Common::SeekableReadStreamEndian &stream, Sprite &sprite,
void readSpriteDataD5(Common::SeekableReadStreamEndian &stream, Sprite &sprite, uint32 startPosition, uint32 finishPosition);
void readSpriteDataD6(Common::SeekableReadStreamEndian &stream, Sprite &sprite, uint32 startPosition, uint32 finishPosition);
-void writeSpriteDataD4(Common::MemoryWriteStream *writeStream, Sprite &sprite);
-void writeSpriteDataD5(Common::MemoryWriteStream *writeStream, Sprite &sprite);
+void writeSpriteDataD4(Common::SeekableWriteStream *writeStream, Sprite &sprite);
+void writeSpriteDataD5(Common::SeekableWriteStream *writeStream, Sprite &sprite);
} // End of namespace Director
diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index a8849599905..1254f822d10 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -1575,11 +1575,11 @@ void LB::b_save(int nargs) {
}
void LB::b_saveMovie(int nargs) {
- Common::Path path;
+ Common::String filename;
if (nargs) {
- path = Common::Path(g_director->_gameDataDir.getPath().join(g_lingo->pop().asString()));
+ filename = g_lingo->pop().asString();
}
- g_director->getMainArchive()->writeToFile(path, g_director->getCurrentMovie());
+ g_director->getMainArchive()->writeToFile(filename, g_director->getCurrentMovie());
}
void LB::b_setCallBack(int nargs) {
diff --git a/engines/director/movie.cpp b/engines/director/movie.cpp
index 5a53e6a456e..8d8e1afc4ff 100644
--- a/engines/director/movie.cpp
+++ b/engines/director/movie.cpp
@@ -284,7 +284,7 @@ Common::Rect Movie::readRect(Common::ReadStreamEndian &stream) {
return rect;
}
-void Movie::writeRect(Common::MemoryWriteStream *writeStream, Common::Rect rect) {
+void Movie::writeRect(Common::SeekableWriteStream *writeStream, Common::Rect rect) {
writeStream->writeSint16BE(rect.top);
writeStream->writeSint16BE(rect.left);
writeStream->writeSint16BE(rect.bottom);
@@ -331,7 +331,7 @@ InfoEntries Movie::loadInfoEntries(Common::SeekableReadStreamEndian &stream, uin
return res;
}
-void Movie::saveInfoEntries(Common::MemoryWriteStream *writeStream, InfoEntries info) {
+void Movie::saveInfoEntries(Common::SeekableWriteStream *writeStream, InfoEntries info) {
// The writing functionality was intrioduced in Director 4
writeStream->writeUint32BE(20); // offset: d4 and up movies is always 20
writeStream->writeUint32BE(info.unk1);
diff --git a/engines/director/movie.h b/engines/director/movie.h
index 0933da3197e..a66d7009bd8 100644
--- a/engines/director/movie.h
+++ b/engines/director/movie.h
@@ -92,9 +92,9 @@ public:
static Common::Rect readRect(Common::ReadStreamEndian &stream);
static InfoEntries loadInfoEntries(Common::SeekableReadStreamEndian &stream, uint16 version);
- static void saveInfoEntries(Common::MemoryWriteStream *writeStream, InfoEntries info);
+ static void saveInfoEntries(Common::SeekableWriteStream *writeStream, InfoEntries info);
- static void writeRect(Common::MemoryWriteStream *writeStream, Common::Rect rect);
+ static void writeRect(Common::SeekableWriteStream *writeStream, Common::Rect rect);
void loadCastLibMapping(Common::SeekableReadStreamEndian &stream);
bool loadArchive();
diff --git a/engines/director/score.cpp b/engines/director/score.cpp
index af69706c62a..77b266a9f24 100644
--- a/engines/director/score.cpp
+++ b/engines/director/score.cpp
@@ -2066,7 +2066,7 @@ Common::String Score::formatChannelInfo() {
}
-void Score::writeVWSCResource(Common::MemoryWriteStream *writeStream, uint32 offset) {
+void Score::writeVWSCResource(Common::SeekableWriteStream *writeStream, uint32 offset) {
uint32 channelSize = 0;
uint32 mainChannelSize = 0;
if (_version >= kFileVer400 && _version < kFileVer500) {
@@ -2079,22 +2079,22 @@ void Score::writeVWSCResource(Common::MemoryWriteStream *writeStream, uint32 off
warning("FilmLoopCastMember::writeSCVWResource: Writing Director Version 6+ not supported yet");
return;
}
-
+
writeStream->seek(offset);
uint32 scoreSize = getVWSCResourceSize();
writeStream->writeUint32LE(MKTAG('V', 'W', 'S', 'C'));
writeStream->writeUint32LE(scoreSize);
-
- // The format of a score is similar to that of FilmLoopCastMember, or rather its vice-verca
+
+ // The format of a score is similar to that of FilmLoopCastMember, or rather its vice-verca
writeStream->writeUint32BE(scoreSize);
// Headers
writeStream->writeUint32BE(0); // frame10Offset
writeStream->writeUint32BE(0); // numOfFrames
writeStream->writeUint16BE(_framesVersion);
- writeStream->writeUint16BE(_spriteRecordSize);
+ writeStream->writeUint16BE(_spriteRecordSize);
writeStream->writeUint16BE(_numChannels);
writeStream->writeUint16BE(_numChannelsDisplayed); // In case _framesVersion > 13, we ignore this while loading
@@ -2104,7 +2104,7 @@ void Score::writeVWSCResource(Common::MemoryWriteStream *writeStream, uint32 off
// until there are no more channels in the frame
// width of message (One chunk of data) (This is the size of data for the sprite that needs to be read) ->
// order of message (this order tells us the channel we're reading) ->
- // 1-20 bytes of Sprite data
+ // 1-20 bytes of Sprite data
for (uint it = 0; it < _scoreCache.size(); it ++) {
Frame frame = *(_scoreCache[it]);
@@ -2119,13 +2119,13 @@ void Score::writeVWSCResource(Common::MemoryWriteStream *writeStream, uint32 off
}
}
-void Score::writeFrame(Common::MemoryWriteStream *writeStream, Frame frame, uint32 channelSize, uint32 mainChannelSize) {
+void Score::writeFrame(Common::SeekableWriteStream *writeStream, Frame frame, uint32 channelSize, uint32 mainChannelSize) {
// The first sprite is the main channel
writeStream->writeUint16BE(mainChannelSize);
// the offset for the main channel should be 0 since we're writing all the 40/48 bytes
writeStream->writeUint16BE(0);
frame.writeMainChannels(writeStream, _version);
-
+
for (uint it = 1; it < frame._sprites.size(); it++) {
Sprite sprite = *(frame._sprites[it]);
@@ -2154,7 +2154,7 @@ uint32 Score::getVWSCResourceSize() {
}
uint32 framesSize = 0;
- for (Frame *frame : _scoreCache) {
+ for (Frame *frame : _scoreCache) {
// Frame size
framesSize += 2;
@@ -2163,7 +2163,7 @@ uint32 Score::getVWSCResourceSize() {
// message width: 2 bytes
// order: 2 bytes
// Sprite data: 20 bytes
- framesSize += 2 + 2 + channelSize;
+ framesSize += 2 + 2 + channelSize;
}
}
// _firstFramePosition is the header size
diff --git a/engines/director/score.h b/engines/director/score.h
index 1248d0baf2e..12a0796e666 100644
--- a/engines/director/score.h
+++ b/engines/director/score.h
@@ -36,7 +36,7 @@ namespace Common {
class ReadStreamEndian;
class MemoryReadStreamEndian;
class SeekableReadStreamEndian;
- class MemoryWriteStream;
+ class SeekableWriteStream;
}
namespace Director {
@@ -74,7 +74,7 @@ public:
void updateFrame(Frame *frame);
Frame *getFrameData(int frameNum);
- void writeVWSCResource(Common::MemoryWriteStream *writeStream, uint32 offset);
+ void writeVWSCResource(Common::SeekableWriteStream *writeStream, uint32 offset);
uint32 getVWSCResourceSize();
void loadLabels(Common::SeekableReadStreamEndian &stream);
@@ -155,7 +155,7 @@ private:
bool processImmediateFrameScript(Common::String s, int id);
bool processFrozenScripts(bool recursion = false, int count = 0);
- void writeFrame(Common::MemoryWriteStream *writeStream, Frame frame, uint32 channelSize, uint32 mainChannelSize);
+ void writeFrame(Common::SeekableWriteStream *writeStream, Frame frame, uint32 channelSize, uint32 mainChannelSize);
public:
Common::Array<Channel *> _channels;
diff --git a/engines/director/stxt.cpp b/engines/director/stxt.cpp
index 6228247a5e5..c6378413123 100644
--- a/engines/director/stxt.cpp
+++ b/engines/director/stxt.cpp
@@ -153,7 +153,7 @@ void FontStyle::read(Common::ReadStreamEndian &stream, Cast *cast) {
formatStartOffset, originalHeight, height, ascent, originalFontId, fontId, textSlant, fontSize, r, g, b);
}
-void FontStyle::write(Common::MemoryWriteStream *writeStream) {
+void FontStyle::write(Common::SeekableWriteStream *writeStream) {
debugC(3, kDebugSaving, "FontStyle::write(): formatStartOffset: %d, height: %d ascent: %d, fontId: %d, textSlant: %d, fontSize: %d, r: %x g: %x b: %x",
formatStartOffset, height, ascent, fontId, textSlant, fontSize, r, g, b);
diff --git a/engines/director/stxt.h b/engines/director/stxt.h
index 95bc1245048..b82a12f99ec 100644
--- a/engines/director/stxt.h
+++ b/engines/director/stxt.h
@@ -24,7 +24,7 @@
namespace Common {
class ReadStreamEndian;
-class MemoryWriteStream;
+class SeekableWriteStream;
}
namespace Director {
@@ -45,7 +45,7 @@ struct FontStyle {
FontStyle();
void read(Common::ReadStreamEndian &textStream, Cast *cast);
- void write(Common::MemoryWriteStream *writeStream);
+ void write(Common::SeekableWriteStream *writeStream);
};
class Stxt {
Commit: 1d7e0589f181efc040ef2bd3db5c8862bac6b2d4
https://github.com/scummvm/scummvm/commit/1d7e0589f181efc040ef2bd3db5c8862bac6b2d4
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Add saved files to the search set while loading
Create a sub class of `Common::Archive` named `SavedArchive`
During the startup, scan for saved files and add the archive
the `SearchSet` with higher priority than the game files
So when loading a movie, the saved games are loaded before
game files
Changed paths:
engines/director/archive-save.cpp
engines/director/archive.h
engines/director/director.cpp
engines/director/director.h
engines/director/types.h
diff --git a/engines/director/archive-save.cpp b/engines/director/archive-save.cpp
index 1774d1f9dcc..9963fc17c05 100644
--- a/engines/director/archive-save.cpp
+++ b/engines/director/archive-save.cpp
@@ -46,16 +46,17 @@ bool RIFXArchive::writeToFile(Common::String filename, Movie *movie) {
filename = movie->getMacName();
}
- Common::OutSaveFile *saveFile = g_engine->getSaveFileManager()->openForSaving(g_director->getTargetName() + "-" + filename);
+ Common::String saveFileName = g_director->getTargetName() + "-" + filename;
+ // Don't open the save file as compressed which doesn't support seeking
+ Common::OutSaveFile *saveFile = g_engine->getSaveFileManager()->openForSaving(saveFileName, false);
if (!saveFile) {
- warning("RIFXArchive::writeToFile: Failed to open file %s for saving");
+ warning("RIFXArchive::writeToFile: Failed to open file %s for saving", saveFileName.c_str());
return false;
}
// Update the resources, their sizes and offsets
Common::Array<Resource *> builtResources = rebuildResources(movie);
-
// ignoring the startOffset
// For RIFX stream, moreoffset = 0, we won't be writing macbinary
// Don't need to allocate this much size in case 'junk' and 'free' resources are ignored
@@ -174,9 +175,9 @@ bool RIFXArchive::writeToFile(Common::String filename, Movie *movie) {
// Write the movie out, stored in dumpData
if (saveFile) {
saveFile->flush();
- debugC(3, kDebugSaving, "RIFXArchive::writeStream:Saved the movie as file %s", filename.c_str());
+ debugC(3, kDebugSaving, "RIFXArchive::writeStream: Saved the movie as file %s", saveFileName.c_str());
} else {
- warning("RIFXArchive::writeStream: Error saving the file %s", filename.c_str());
+ warning("RIFXArchive::writeStream: Error saving the file %s", saveFileName.c_str());
}
delete saveFile;
@@ -655,19 +656,63 @@ void dumpFile(Common::String fileName, uint32 id, uint32 tag, byte *dumpData, ui
}
uint32 RIFXArchive::findParentIndex(uint32 tag, uint16 index) {
- // We have to find the parent
- // Look into the keyData, for all parents of resource tag
- // If the parent contains this resource's index, that's our parent
- for (auto &it : _keyData[tag]) {
- for (auto &kt : it._value) {
- if (kt == index) {
- return it._key;
- }
- }
- }
-
- warning("RIFXArchive::findParentIndex: The parent for resource: %s, index: %d, was not found", tag2str(tag), index);
- return 0;
+ // We have to find the parent
+ // Look into the keyData, for all parents of resource tag
+ // If the parent contains this resource's index, that's our parent
+ for (auto &it : _keyData[tag]) {
+ for (auto &kt : it._value) {
+ if (kt == index) {
+ return it._key;
+ }
+ }
+ }
+
+ warning("RIFXArchive::findParentIndex: The parent for resource: %s, index: %d, was not found", tag2str(tag), index);
+ return 0;
+}
+
+SavedArchive::SavedArchive(Common::String target) {
+ Common::StringArray saveFileList = g_engine->getSaveFileManager()->listSavefiles(target + "-*");
+
+ debugC(3, kDebugLoading, "DirectorEngine:: loadSaveFiles: Loading save files");
+ for (auto saveFileName : saveFileList) {
+ // Derive the original file name from the save file name
+ // Save files are named target_name-save_filename
+ Common::String origFileName = saveFileName.substr(target.size() + 1);
+ debugC(3, kDebugLoading, "Found save file: %s -> %s", saveFileName.c_str(), origFileName.c_str());
+
+ _files[origFileName] = saveFileName;
+ }
+}
+
+bool SavedArchive::hasFile(const Common::Path &path) const {
+ return (_files.find(path.toString()) != _files.end());
+}
+
+int SavedArchive::listMembers(Common::ArchiveMemberList &list) const {
+ int count = 0;
+
+ for (FileMap::const_iterator i = _files.begin(); i != _files.end(); ++i) {
+ list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(i->_key, *this)));
+ ++count;
+ }
+
+ return count;
+}
+
+const Common::ArchiveMemberPtr SavedArchive::getMember(const Common::Path &path) const {
+ if (!hasFile(path))
+ return Common::ArchiveMemberPtr();
+
+ return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(path, *this));
+}
+
+Common::SeekableReadStream *SavedArchive::createReadStreamForMember(const Common::Path &path) const {
+ FileMap::const_iterator fDesc = _files.find(path.toString());
+ if (fDesc == _files.end())
+ return nullptr;
+
+ return g_engine->getSaveFileManager()->openForLoading(fDesc->_value);
}
-} // End of namespace Director
+} // End of namespace Director
diff --git a/engines/director/archive.h b/engines/director/archive.h
index 36de722650e..38ca6e9f68a 100644
--- a/engines/director/archive.h
+++ b/engines/director/archive.h
@@ -274,6 +274,18 @@ private:
void dumpFile(Common::String filename, uint32 id, uint32 tag, byte *dumpData, uint32 dumpSize);
-} // End of namespace Director
+class SavedArchive : public Common::Archive {
+public:
+ SavedArchive(Common::String target) ;
+ bool hasFile(const Common::Path &path) const override;
+ int listMembers(Common::ArchiveMemberList &list) const override;
+ const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
+ Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
+private:
+ typedef Common::HashMap<Common::String, Common::String> FileMap;
+ FileMap _files;
+};
+
+}
#endif
diff --git a/engines/director/director.cpp b/engines/director/director.cpp
index f2eca3dffc7..78abf8f4ac2 100644
--- a/engines/director/director.cpp
+++ b/engines/director/director.cpp
@@ -275,6 +275,9 @@ Common::Error DirectorEngine::run() {
_wm->setEngine(this);
gameQuirks(_gameDescription->desc.gameId, _gameDescription->desc.platform);
+ // Mix in all the saved files for the current target
+ // Assign higher priority to save games to load them before original game files
+ SearchMan.add(kSavedFilesArchive, new SavedArchive(_targetName), 1);
_wm->setDesktopMode(_wmMode);
diff --git a/engines/director/director.h b/engines/director/director.h
index abfd2a09ca9..0f640bee7a2 100644
--- a/engines/director/director.h
+++ b/engines/director/director.h
@@ -289,6 +289,7 @@ protected:
public:
const DirectorGameDescription *_gameDescription;
+ Common::HashMap<Common::String, Common::String> _cachedSaveFiles;
Common::FSNode _gameDataDir;
CastMemberID *_clipBoard;
uint32 _wmMode;
diff --git a/engines/director/types.h b/engines/director/types.h
index 357395a927b..0388a774957 100644
--- a/engines/director/types.h
+++ b/engines/director/types.h
@@ -36,6 +36,7 @@ enum {
};
#define kQuirksCacheArchive "quirks"
+#define kSavedFilesArchive "saved"
enum MovieFlag {
kMovieFlagRemapPalettesWhenNeeded = (1 << 6),
Commit: 463bdaf4b605936fc89d668b0ee4afefa983f4a8
https://github.com/scummvm/scummvm/commit/463bdaf4b605936fc89d668b0ee4afefa983f4a8
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Write _platformID instead of _platform
Changed paths:
engines/director/cast.cpp
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index afd602bff6a..a0bf695cada 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -578,7 +578,7 @@ void Cast::saveConfig(Common::SeekableWriteStream *writeStream, uint32 offset) {
writeStream->writeSByte(_field26); // 53
writeStream->writeSint16BE(_frameRate); // 54
- writeStream->writeUint16BE(_platform); // 56
+ writeStream->writeUint16BE(_platformID); // 56
writeStream->writeSint16BE(_protection); // 58
writeStream->writeSint32BE(_field29); // 60
Commit: e0fa481694e628eb9d16dd4537a359a9f2095c60
https://github.com/scummvm/scummvm/commit/e0fa481694e628eb9d16dd4537a359a9f2095c60
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Save the correct movie in `saveMovie`
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 1254f822d10..0855953dba1 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -1579,7 +1579,7 @@ void LB::b_saveMovie(int nargs) {
if (nargs) {
filename = g_lingo->pop().asString();
}
- g_director->getMainArchive()->writeToFile(filename, g_director->getCurrentMovie());
+ g_director->getCurrentMovie()->getArchive()->writeToFile(filename, g_director->getCurrentMovie());
}
void LB::b_setCallBack(int nargs) {
Commit: 25788134e0664d799f3ad6cf24ac1fc229e4af35
https://github.com/scummvm/scummvm/commit/25788134e0664d799f3ad6cf24ac1fc229e4af35
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Remove unnecessary header files and indentation fixes
Changed paths:
engines/director/archive-save.cpp
engines/director/archive.cpp
engines/director/cast.cpp
engines/director/castmember/bitmap.cpp
engines/director/castmember/castmember.cpp
engines/director/castmember/digitalvideo.cpp
engines/director/castmember/text.cpp
engines/director/stxt.cpp
diff --git a/engines/director/archive-save.cpp b/engines/director/archive-save.cpp
index 9963fc17c05..7a7294597e7 100644
--- a/engines/director/archive-save.cpp
+++ b/engines/director/archive-save.cpp
@@ -161,7 +161,7 @@ bool RIFXArchive::writeToFile(Common::String filename, Movie *movie) {
break;
default:
- debugC(7, kDebugSaving, "Saving resource %s as it is, without modification", tag2str(it->tag));
+ debugC(7, kDebugSaving, "Saving resource %s as it is, without modification", tag2str(it->tag));
saveFile->seek(it->offset, SEEK_SET);
saveFile->writeUint32LE(it->tag);
saveFile->writeUint32LE(it->size);
@@ -315,11 +315,11 @@ bool RIFXArchive::writeCast(Common::SeekableWriteStream *writeStream, uint32 off
for (uint32 i = 0; i <= maxCastId; i++) {
uint32 castIndex = castIndexes.getValOrDefault(i, 0);
if (castIndex) {
- debugCN(5, kDebugSaving, (i == 0 ? "%d" : ", %d"), castIndex);
+ debugCN(5, kDebugSaving, (i == 0 ? "%d" : ", %d"), castIndex);
writeStream->writeUint32BE(castIndex);
}
}
- debugC(5, kDebugSaving, "]");
+ debugC(5, kDebugSaving, "]");
return true;
}
diff --git a/engines/director/archive.cpp b/engines/director/archive.cpp
index e9f3cf425e8..9d0b84a3531 100644
--- a/engines/director/archive.cpp
+++ b/engines/director/archive.cpp
@@ -27,9 +27,7 @@
#include "director/director.h"
#include "director/archive.h"
-#include "director/score.h"
#include "director/movie.h"
-#include "director/cast.h"
#include "director/window.h"
namespace Director {
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index a0bf695cada..5ab924001e0 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -603,7 +603,7 @@ void Cast::saveConfig(Common::SeekableWriteStream *writeStream, uint32 offset) {
writeStream->writeSint16BE(_defaultPalette.member); // 78
}
- if (debugChannelSet(7, kDebugSaving)) {
+ if (debugChannelSet(7, kDebugSaving)) {
// Adding +8 because the stream doesn't include the header and the entry for the size itself
byte *dumpData = (byte *)calloc(configSize + 8, sizeof(byte));
@@ -614,10 +614,10 @@ void Cast::saveConfig(Common::SeekableWriteStream *writeStream, uint32 offset) {
dumpStream->write(writeStream, configSize + 8);
writeStream->seek(currentPos);
- dumpFile("ConfigData", 0, MKTAG('V', 'W', 'C', 'F'), dumpData, configSize + 8);
+ dumpFile("ConfigData", 0, MKTAG('V', 'W', 'C', 'F'), dumpData, configSize + 8);
free(dumpData);
- delete dumpStream;
- }
+ delete dumpStream;
+ }
}
diff --git a/engines/director/castmember/bitmap.cpp b/engines/director/castmember/bitmap.cpp
index 142b4fcd458..995f2d45704 100644
--- a/engines/director/castmember/bitmap.cpp
+++ b/engines/director/castmember/bitmap.cpp
@@ -20,9 +20,8 @@
*/
#include "common/config-manager.h"
+#include "common/stream.h"
#include "common/macresman.h"
-#include "common/substream.h"
-#include "common/memstream.h"
#include "graphics/surface.h"
#include "graphics/macgui/macwidget.h"
#include "image/bmp.h"
diff --git a/engines/director/castmember/castmember.cpp b/engines/director/castmember/castmember.cpp
index 0df8aa7c04f..17fac718680 100644
--- a/engines/director/castmember/castmember.cpp
+++ b/engines/director/castmember/castmember.cpp
@@ -20,9 +20,7 @@
*/
#include "common/events.h"
-#include "common/substream.h"
-#include "common/macresman.h"
-#include "common/memstream.h"
+#include "common/stream.h"
#include "director/director.h"
#include "director/cast.h"
diff --git a/engines/director/castmember/digitalvideo.cpp b/engines/director/castmember/digitalvideo.cpp
index 28e0528141e..0b6471ae76c 100644
--- a/engines/director/castmember/digitalvideo.cpp
+++ b/engines/director/castmember/digitalvideo.cpp
@@ -20,8 +20,8 @@
*/
#include "audio/decoders/aiff.h"
+#include "common/stream.h"
#include "common/macresman.h"
-#include "common/memstream.h"
#include "graphics/paletteman.h"
#include "graphics/surface.h"
diff --git a/engines/director/castmember/text.cpp b/engines/director/castmember/text.cpp
index a225137778a..441e309aa09 100644
--- a/engines/director/castmember/text.cpp
+++ b/engines/director/castmember/text.cpp
@@ -20,8 +20,6 @@
*/
#include "common/events.h"
-#include "common/substream.h"
-#include "common/macresman.h"
#include "common/memstream.h"
#include "graphics/macgui/macbutton.h"
diff --git a/engines/director/stxt.cpp b/engines/director/stxt.cpp
index c6378413123..35fc9785241 100644
--- a/engines/director/stxt.cpp
+++ b/engines/director/stxt.cpp
@@ -20,8 +20,7 @@
*/
#include "common/macresman.h"
-#include "common/memstream.h"
-#include "common/substream.h"
+#include "common/stream.h"
#include "director/director.h"
#include "director/cast.h"
Commit: c866bd6b88241a749b16b2591b15c6c2167d47b8
https://github.com/scummvm/scummvm/commit/c866bd6b88241a749b16b2591b15c6c2167d47b8
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Implement Stubbed `Cast::computeChecksum()`
Changed paths:
engines/director/cast.cpp
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index 5ab924001e0..ea092ae5a6c 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -441,50 +441,7 @@ bool Cast::loadConfig() {
_checksum = stream->readUint32();
//Calculation and verification of checksum
- uint32 check = _len + 1;
- check *= _fileVersion + 2;
- check /= _checkRect.top + 3;
- check *= _checkRect.left + 4;
- check /= _checkRect.bottom + 5;
- check *= _checkRect.right + 6;
- check -= _castArrayStart + 7;
- check *= _castArrayEnd + 8;
- check -= (int8)_readRate + 9;
- check -= _lightswitch + 10;
-
- if (humanVer < 700)
- check += _unk1 + 11;
- else
- warning("STUB: skipped using stageColorG, stageColorB for post-D7 movie in checksum calulation");
-
- check *= _commentFont + 12;
- check += _commentSize + 13;
-
- if (humanVer < 800)
- check *= (uint8)((_commentStyle >> 8) & 0xFF) + 14;
- else
- check *= _commentStyle + 14;
-
- if (humanVer < 700)
- check += _stageColor + 15;
- else
- check += (uint8)(_stageColor & 0xFF) + 15; // Taking lower 8 bits to take into account stageColorR
-
-
- check += _bitdepth + 16;
- check += _field17 + 17;
- check *= _field18 + 18;
- check += _field19 + 19;
- check *= _version + 20;
- check += _field21 + 21;
- check += _field22 + 22;
- check += _field23 + 23;
- check += _field24 + 24;
- check *= _field25 + 25;
- check += _frameRate + 26;
- check *= _platformID + 27;
- check *= (_protection * 0xE06) + 0xFF450000u;
- check ^= MKTAG('r', 'a', 'l', 'f');
+ uint32 check = computeChecksum();
if (check != _checksum)
warning("BUILDBOT: The checksum for this VWCF resource is incorrect. Got %04x, but expected %04x", check, _checksum);
@@ -582,7 +539,6 @@ void Cast::saveConfig(Common::SeekableWriteStream *writeStream, uint32 offset) {
writeStream->writeSint16BE(_protection); // 58
writeStream->writeSint32BE(_field29); // 60
- // Currently a stub
uint32 checksum = computeChecksum();
writeStream->writeUint32BE(checksum); // 64
@@ -1031,8 +987,55 @@ uint32 Cast::getCastInfoSize(uint32 castId) {
}
uint32 Cast::computeChecksum() {
- warning("STUB::ConfigChunk::computeChecksum() is not implemented yet");
- return 0;
+ uint humanVer = humanVersion(_version);
+
+ //Calculation and verification of checksum
+ uint32 check = _len + 1;
+ check *= _fileVersion + 2;
+ check /= _checkRect.top + 3;
+ check *= _checkRect.left + 4;
+ check /= _checkRect.bottom + 5;
+ check *= _checkRect.right + 6;
+ check -= _castArrayStart + 7;
+ check *= _castArrayEnd + 8;
+ check -= (int8)_readRate + 9;
+ check -= _lightswitch + 10;
+
+ if (humanVer < 700)
+ check += _unk1 + 11;
+ else
+ warning("STUB: skipped using stageColorG, stageColorB for post-D7 movie in checksum calulation");
+
+ check *= _commentFont + 12;
+ check += _commentSize + 13;
+
+ if (humanVer < 800)
+ check *= (uint8)((_commentStyle >> 8) & 0xFF) + 14;
+ else
+ check *= _commentStyle + 14;
+
+ if (humanVer < 700)
+ check += _stageColor + 15;
+ else
+ check += (uint8)(_stageColor & 0xFF) + 15; // Taking lower 8 bits to take into account stageColorR
+
+
+ check += _bitdepth + 16;
+ check += _field17 + 17;
+ check *= _field18 + 18;
+ check += _field19 + 19;
+ check *= _version + 20;
+ check += _field21 + 21;
+ check += _field22 + 22;
+ check += _field23 + 23;
+ check += _field24 + 24;
+ check *= _field25 + 25;
+ check += _frameRate + 26;
+ check *= _platformID + 27;
+ check *= (_protection * 0xE06) + 0xFF450000u;
+ check ^= MKTAG('r', 'a', 'l', 'f');
+
+ return check;
}
// The 'CASt' resource has strings containing information about the respective CastMember
@@ -1375,7 +1378,7 @@ void Cast::loadCastData(Common::SeekableReadStreamEndian &stream, uint16 id, Res
error("Cast::loadCastData: unsupported Director version (%d)", _version);
}
- debug("Cast::loadCastData(): CASt: id: %d type: %x castDataSize: %d castInfoSize: %d (%x) unk1: %d unk2: %d unk3: %d",
+ 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
Commit: 84fec1adeedb572c074d1251c6b1508c71965972
https://github.com/scummvm/scummvm/commit/84fec1adeedb572c074d1251c6b1508c71965972
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Delete rebuilt resources after saving file
To prevent memory leakage
Changed paths:
engines/director/archive-save.cpp
diff --git a/engines/director/archive-save.cpp b/engines/director/archive-save.cpp
index 7a7294597e7..a5372eecbc6 100644
--- a/engines/director/archive-save.cpp
+++ b/engines/director/archive-save.cpp
@@ -181,6 +181,9 @@ bool RIFXArchive::writeToFile(Common::String filename, Movie *movie) {
}
delete saveFile;
+ for (auto it : builtResources) {
+ delete it;
+ }
return true;
}
@@ -396,7 +399,7 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
}
// TODO: Then we'll need to see if there are any other newly added resources
- // Now when you duplicate a cast member, say BitmapCastMember, the cast member is duplicated
+ // Now when you use `duplicate`, say BitmapCastMember, the cast member is duplicated
// but its children resources are not, meaning the duplicated BitmapCastMember is also loaded from the same 'BITD' resource
// So it is not necessary to duplicate the 'BITD' resource
// However, in case an entirely new cast member is added, say a filmloop is recorded, then that requires a new 'SCVW' resource
@@ -412,9 +415,6 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
// The +8 bytes are to account for the header and size
uint32 currentSize = 12 + (getImapSize() + 8) + (getMmapSize() + 8);
- // This switch statement can be simplified by keeping a pointer to the write function of the resrouce in the Resource
- // But that will require accessing _resource every time so not doing that right now
-
// need to make a new resources array, because we need the old offsets as well as new ones
Common::Array<Resource *> builtResources;
Commit: c7720100146d890b284cf199f357d7bfa0b1eb93
https://github.com/scummvm/scummvm/commit/c7720100146d890b284cf199f357d7bfa0b1eb93
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Write movies with multiple casts
Changed paths:
engines/director/archive-save.cpp
engines/director/cast.cpp
engines/director/castmember/bitmap.cpp
engines/director/castmember/filmloop.cpp
engines/director/castmember/palette.cpp
engines/director/castmember/text.cpp
engines/director/movie.cpp
engines/director/movie.h
diff --git a/engines/director/archive-save.cpp b/engines/director/archive-save.cpp
index a5372eecbc6..ed9266a9dad 100644
--- a/engines/director/archive-save.cpp
+++ b/engines/director/archive-save.cpp
@@ -109,10 +109,15 @@ bool RIFXArchive::writeToFile(Common::String filename, Movie *movie) {
break;
case MKTAG('C', 'A', 'S', 't'):
+ cast = movie->getCastByLibResourceID(it->libResourceId);
cast->saveCastData(saveFile, it);
break;
case MKTAG('V', 'W', 'C', 'F'):
+ // There is only 'VWCF' resource, that is for the internal cast
+ // The external casts don't have a config
+ // movie->getCast() returns the internal cast
+ cast = movie->getCast();
cast->saveConfig(saveFile, it->offset);
break;
@@ -121,6 +126,7 @@ bool RIFXArchive::writeToFile(Common::String filename, Movie *movie) {
uint32 parentIndex = findParentIndex(it->tag, it->index);
Resource parent = castResMap[parentIndex];
+ cast = movie->getCastByLibResourceID(parent.libResourceId);
BitmapCastMember *target = (BitmapCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
target->writeBITDResource(saveFile, it->offset);
}
@@ -131,6 +137,7 @@ bool RIFXArchive::writeToFile(Common::String filename, Movie *movie) {
uint32 parentIndex = findParentIndex(it->tag, it->index);
Resource parent = castResMap[parentIndex];
+ cast = movie->getCastByLibResourceID(parent.libResourceId);
TextCastMember *target = (TextCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
target->writeSTXTResource(saveFile, it->offset);
}
@@ -141,6 +148,7 @@ bool RIFXArchive::writeToFile(Common::String filename, Movie *movie) {
uint32 parentIndex = findParentIndex(it->tag, it->index);
Resource parent = castResMap[parentIndex];
+ cast = movie->getCastByLibResourceID(parent.libResourceId);
PaletteCastMember *target = (PaletteCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
target->writePaletteData(saveFile, it->offset);
}
@@ -148,9 +156,11 @@ bool RIFXArchive::writeToFile(Common::String filename, Movie *movie) {
case MKTAG('S', 'C', 'V', 'W'):
{
+
uint32 parentIndex = findParentIndex(it->tag, it->index);
Resource parent = castResMap[parentIndex];
+ cast = movie->getCastByLibResourceID(parent.libResourceId);
FilmLoopCastMember *target = (FilmLoopCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
target->writeSCVWResource(saveFile, it->offset);
}
@@ -294,12 +304,13 @@ bool RIFXArchive::writeKeyTable(Common::SeekableWriteStream *writeStream, uint32
return true;
}
-bool RIFXArchive::writeCast(Common::SeekableWriteStream *writeStream, uint32 offset, uint32 castLib) {
+bool RIFXArchive::writeCast(Common::SeekableWriteStream *writeStream, uint32 offset, uint32 libResourceId) {
writeStream->seek(offset);
+ uint32 casSize = getCASResourceSize(libResourceId);
uint castTag = MKTAG('C', 'A', 'S', 't');
writeStream->writeUint32LE(MKTAG('C', 'A', 'S', '*'));
- writeStream->writeUint32LE(getCASResourceSize(castLib));
+ writeStream->writeUint32LE(casSize);
Common::HashMap<uint16, uint16> castIndexes;
@@ -307,13 +318,13 @@ bool RIFXArchive::writeCast(Common::SeekableWriteStream *writeStream, uint32 off
// Since the order they appear matters, they are given castIds accordingly
uint32 maxCastId = 0;
for (auto &it : _types[castTag]) {
- if (it._value.libResourceId == castLib) {
+ if (it._value.libResourceId == libResourceId) {
castIndexes[it._value.castId] = it._value.index;
maxCastId = MAX(maxCastId, it._value.castId);
}
}
- debugC(5, kDebugSaving, "RIFXArchive::writeCast: Writing CAS* resource:");
+ debugC(5, kDebugSaving, "RIFXArchive::writeCast: Writing CAS* resource: size: %d, maxCastID: %d, libResourceID: %d", casSize, maxCastId, libResourceId);
debugCN(5, kDebugSaving, "'CASt' indexes: [");
for (uint32 i = 0; i <= maxCastId; i++) {
uint32 castIndex = castIndexes.getValOrDefault(i, 0);
@@ -340,59 +351,64 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
// First we'll have to update the _types table to include all the newly added
// cast members, and their 'CASt' resources
- // Only handling movies with a single cast for now
- Cast *cast = movie->getCast();
+ Cast *cast = nullptr;
ResourceMap &castResMap = _types[MKTAG('C', 'A', 'S', 't')];
- for (auto it : *(cast->_loadedCast)) {
- if (it._value->_index == -1) {
- Resource *res = nullptr;
- uint16 targetCastId = it._value->getID() - cast->_castArrayStart;
-
- // Checking if the castId already exists in the CASt resources
- for (auto castRes : castResMap) {
- if (castRes._value.castId == targetCastId) {
- res = &castRes._value;
+ // Iterate over all the casts
+ for (auto it : *(movie->getCasts())) {
+ cast = it._value;
+
+ // Iterate over all the loaded members of the cast to check for new cast members
+ for (auto jt : *(cast->_loadedCast)) {
+ // If the index is -1, that means the cast member is new or duplicated
+ if (jt._value->_index == -1) {
+ Resource *res = nullptr;
+ uint16 targetCastId = jt._value->getID() - cast->_castArrayStart;
+
+ // Checking if the castId already exists in the CASt resources
+ for (auto castRes : castResMap) {
+ if (castRes._value.castId == targetCastId) {
+ res = &castRes._value;
+ }
}
- }
- if (!res) {
- // If the castId is new, create a new resource
- // Assigning the next available index to the resource
- res = &castResMap[_resources.size()];
- res->tag = MKTAG('C', 'A', 'S', 't');
- res->accessed = true;
-
- // Again considering here that there is only one CAS* resource, so the first resource will have our necessary libResourceId
- res->libResourceId = _types[MKTAG('C', 'A', 'S', '*')].begin()->_value.libResourceId;
- res->children = it._value->_children;
- res->index = _resources.size();
- res->castId = it._value->getID() - cast->_castArrayStart;
-
- for (auto child : it._value->_children) {
- _keyData[child.tag][res->index].push_back(child.index);
- _keyTableUsedCount += 1;
- _keyTableEntryCount += 1;
- }
- _resources.push_back(res);
-
- debugC(5, kDebugSaving, "RIFXArchive::rebuildResources(): new 'CASt' resource added");
- } else {
- // The castId is not new, overwrite the key data of the previous cast
- for (auto child : res->children) {
- // Remove the data of the previous (removed) 'CASt'
- int8 count = _keyData[child.tag][res->index].size();
- _keyData[child.tag][res->index].clear();
- _keyTableUsedCount -= count;
- _keyTableEntryCount -= count;
- }
-
- res->children = it._value->_children;
-
- for (auto child : res->children) {
- _keyData[child.tag][res->index].push_back(child.index);
- _keyTableUsedCount += 1;
- _keyTableEntryCount += 1;
+ if (!res) {
+ // If the castId is new, create a new resource
+ // Assigning the next available index to the resource
+ res = &castResMap[_resources.size()];
+ res->tag = MKTAG('C', 'A', 'S', 't');
+ res->accessed = true;
+
+ res->libResourceId = cast->_libResourceId;
+ res->children = jt._value->_children;
+ res->index = _resources.size();
+ res->castId = jt._value->getID() - cast->_castArrayStart;
+
+ for (auto child : jt._value->_children) {
+ _keyData[child.tag][res->index].push_back(child.index);
+ _keyTableUsedCount += 1;
+ _keyTableEntryCount += 1;
+ }
+ _resources.push_back(res);
+
+ debugC(5, kDebugSaving, "RIFXArchive::rebuildResources(): new 'CASt' resource added");
+ } else {
+ // The castId is not new, overwrite the key data of the previous cast
+ for (auto child : res->children) {
+ // Remove the data of the previous (removed) 'CASt'
+ int8 count = _keyData[child.tag][res->index].size();
+ _keyData[child.tag][res->index].clear();
+ _keyTableUsedCount -= count;
+ _keyTableEntryCount -= count;
+ }
+
+ res->children = jt._value->_children;
+
+ for (auto child : res->children) {
+ _keyData[child.tag][res->index].push_back(child.index);
+ _keyTableUsedCount += 1;
+ _keyTableEntryCount += 1;
+ }
}
}
}
@@ -446,6 +462,7 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
case MKTAG('C', 'A', 'S', 't'):
{
+ cast = movie->getCastByLibResourceID(it->libResourceId);
// The castIds of cast members start from _castArrayStart
CastMember *target = cast->getCastMember(it->castId + cast->_castArrayStart);
@@ -477,8 +494,9 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
case MKTAG('V', 'W', 'C', 'F'):
{
- // Cast config, as many resources as Casts
+ // Only one cast config per movie
// No need to update the key mapping
+ cast = movie->getCast();
resSize = cast->getConfigSize();
it->offset = currentSize;
@@ -508,6 +526,8 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
uint32 parentIndex = findParentIndex(it->tag, it->index);
Resource parent = castResMap[parentIndex];
+ // Get the appropriate cast in case of multiple casts
+ cast = movie->getCastByLibResourceID(parent.libResourceId);
PaletteCastMember *target = (PaletteCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
resSize = target->getPaletteDataSize();
@@ -523,6 +543,8 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
uint32 parentIndex = findParentIndex(it->tag, it->index);
Resource parent = castResMap[parentIndex];
+ // Get the appropriate cast in case of multiple casts
+ cast = movie->getCastByLibResourceID(parent.libResourceId);
BitmapCastMember *target = (BitmapCastMember *)cast->getCastMember(parent.castId + cast->_castArrayStart);
resSize = target->getBITDResourceSize();
@@ -608,14 +630,14 @@ uint32 RIFXArchive::getArchiveSize(Common::Array<Resource *> resources) {
return size;
}
-uint32 RIFXArchive::getCASResourceSize(uint32 castLib) {
+uint32 RIFXArchive::getCASResourceSize(uint32 libResourceID) {
uint castTag = MKTAG('C', 'A', 'S', 't');
uint32 maxCastId = 0;
// maxCastId is the basically the number of cast members present in the cast
// This is the number of entries present in the 'CAS*' resource
for (auto &it : _types[castTag]) {
- if (it._value.libResourceId == castLib) {
+ if (it._value.libResourceId == libResourceID) {
maxCastId = MAX(maxCastId, it._value.castId);
}
}
diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index ea092ae5a6c..48549766409 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -147,6 +147,12 @@ CastMember *Cast::getCastMember(int castId, bool load) {
} else if (result) {
_loadQueue.push_back(result);
}
+
+ if (result) {
+ debugC(4, kDebugSaving, "Returned castmember with castId: %d, type: %s", castId, castType2str(result->_type));
+ } else {
+ warning("No castmember with castId: %d, found for cast with libResourceID: %d", castId, _libResourceId);
+ }
return result;
}
diff --git a/engines/director/castmember/bitmap.cpp b/engines/director/castmember/bitmap.cpp
index 995f2d45704..8b917989bfa 100644
--- a/engines/director/castmember/bitmap.cpp
+++ b/engines/director/castmember/bitmap.cpp
@@ -1045,6 +1045,11 @@ void BitmapCastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
}
uint32 BitmapCastMember::writeBITDResource(Common::SeekableWriteStream *writeStream, uint32 offset) {
+ // Load it before writing
+ if (!_loaded) {
+ load();
+ }
+
writeStream->seek(offset);
writeStream->writeUint32LE(MKTAG('B', 'I', 'T', 'D'));
diff --git a/engines/director/castmember/filmloop.cpp b/engines/director/castmember/filmloop.cpp
index 7b6ee726724..54071a7c585 100644
--- a/engines/director/castmember/filmloop.cpp
+++ b/engines/director/castmember/filmloop.cpp
@@ -751,6 +751,11 @@ void FilmLoopCastMember::writeCastData(Common::SeekableWriteStream *writeStream)
}
void FilmLoopCastMember::writeSCVWResource(Common::SeekableWriteStream *writeStream, uint32 offset) {
+ // Load it before writing
+ if (!_loaded) {
+ load();
+ }
+
uint32 channelSize = 0;
if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
channelSize = kSprChannelSizeD4;
@@ -764,9 +769,9 @@ void FilmLoopCastMember::writeSCVWResource(Common::SeekableWriteStream *writeStr
// Go to the desired offset put in the memory map
writeStream->seek(offset);
- debugC(5, kDebugSaving, "FilmLoopCastmember::writeSCVWResource: Saving FilmLoop 'SCVW' data at offset %d", offset);
-
uint32 filmloopSize = getSCVWResourceSize();
+ debugC(5, kDebugSaving, "FilmLoopCastmember::writeSCVWResource: Saving FilmLoop 'SCVW' data of size: %d", filmloopSize);
+
writeStream->writeUint32LE(MKTAG('S', 'C', 'V', 'W'));
writeStream->writeUint32LE(filmloopSize); // Size of the resource
@@ -817,8 +822,8 @@ void FilmLoopCastMember::writeSCVWResource(Common::SeekableWriteStream *writeStr
}
if (debugChannelSet(7, kDebugSaving)) {
- // Adding +8 because the stream doesn't include the header and the entry for the size itself
- byte *dumpData = (byte *)calloc(filmloopSize + 8, sizeof(byte));
+ // Adding +8 because the stream doesn't include the header and the entry for the size itself
+ byte *dumpData = (byte *)calloc(filmloopSize + 8, sizeof(byte));
Common::SeekableMemoryWriteStream *dumpStream = new Common::SeekableMemoryWriteStream(dumpData, filmloopSize + 8);
@@ -829,8 +834,8 @@ void FilmLoopCastMember::writeSCVWResource(Common::SeekableWriteStream *writeStr
dumpFile("FilmLoopData", 0, MKTAG('V', 'W', 'C', 'F'), dumpData, filmloopSize);
free(dumpData);
- delete dumpStream;
- }
+ delete dumpStream;
+ }
}
uint32 FilmLoopCastMember::getSCVWResourceSize() {
diff --git a/engines/director/castmember/palette.cpp b/engines/director/castmember/palette.cpp
index 7df8936acec..dba45cac4e5 100644
--- a/engines/director/castmember/palette.cpp
+++ b/engines/director/castmember/palette.cpp
@@ -162,6 +162,11 @@ uint32 PaletteCastMember::getPaletteDataSize() {
}
void PaletteCastMember::writePaletteData(Common::SeekableWriteStream *writeStream, uint32 offset) {
+ // Load it before writing
+ if (!_loaded) {
+ load();
+ }
+
uint32 castSize = getPaletteDataSize();
writeStream->seek(offset);
diff --git a/engines/director/castmember/text.cpp b/engines/director/castmember/text.cpp
index 441e309aa09..49d1dcb0513 100644
--- a/engines/director/castmember/text.cpp
+++ b/engines/director/castmember/text.cpp
@@ -986,6 +986,11 @@ uint32 TextCastMember::getCastDataSize() {
}
uint32 TextCastMember::writeSTXTResource(Common::SeekableWriteStream *writeStream, uint32 offset) {
+ // Load it before writing
+ if (!_loaded) {
+ load();
+ }
+
debugC(3, kDebugSaving, "writeSTXTResource(): _ptext: %s\n_ftext = %s\n_rtext: %s",
_ptext.encode().c_str(), Common::toPrintable(_ftext).encode().c_str(), Common::toPrintable(_rtext).c_str());
@@ -1008,7 +1013,6 @@ uint32 TextCastMember::writeSTXTResource(Common::SeekableWriteStream *writeStrea
uint64 textPos = writeStream->pos();
writeStream->seek(_ptext.size(), SEEK_CUR);
writeStream->writeUint16BE(formatting);
- debug("Number of formattings: %d", formatting);
FontStyle style;
Common::String rawText;
@@ -1070,7 +1074,6 @@ uint32 TextCastMember::writeSTXTResource(Common::SeekableWriteStream *writeStrea
_ptext.substr(style.formatStartOffset, pIndex - style.formatStartOffset).encode(encoding);
rawText += _ptext.substr(style.formatStartOffset, pIndex - style.formatStartOffset).encode(encoding);
- debug("ptext length: %d, rawText length: %d", _ptext.size(), rawText.size());
uint64 currentPos = writeStream->pos();
writeStream->seek(textPos);
writeStream->writeString(rawText);
diff --git a/engines/director/movie.cpp b/engines/director/movie.cpp
index 8d8e1afc4ff..cb0deacbe6d 100644
--- a/engines/director/movie.cpp
+++ b/engines/director/movie.cpp
@@ -506,6 +506,18 @@ Cast *Movie::getCast(CastMemberID memberID) {
return nullptr;
}
+Cast *Movie::getCastByLibResourceID(int libresourceID) {
+ for (auto it : _casts) {
+ if (it._value->_libResourceId == libresourceID) {
+ debugC(3, kDebugSaving, "Movie::getCastByLibResourceID: Found cast with libresourceID: %d", libresourceID);
+ return it._value;
+ }
+ }
+
+ warning("Movie::getCastByLibResourceID: No cast with libresourceID: %d", libresourceID);
+ return nullptr;
+}
+
CastMember* Movie::createOrReplaceCastMember(CastMemberID memberID, CastMember* cast) {
warning("Movie::createOrReplaceCastMember: stubbed: functions only handles create");
CastMember *result = nullptr;
diff --git a/engines/director/movie.h b/engines/director/movie.h
index a66d7009bd8..a1378c6abd4 100644
--- a/engines/director/movie.h
+++ b/engines/director/movie.h
@@ -105,6 +105,7 @@ public:
DirectorEngine *getVM() const { return _vm; }
Cast *getCast() const { return _casts.getValOrDefault(DEFAULT_CAST_LIB, nullptr); }
Cast *getCast(CastMemberID memberID);
+ Cast *getCastByLibResourceID(int libresourceID);
Cast *getSharedCast() const { return _sharedCast; }
const Common::HashMap<int, Cast *> *getCasts() const { return &_casts; }
Score *getScore() const { return _score; }
Commit: 5c2cf9dd267ff9330aa71f914ef7b1cf5cce3eb1
https://github.com/scummvm/scummvm/commit/5c2cf9dd267ff9330aa71f914ef7b1cf5cce3eb1
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Write zero size for external bitmaps
Changed paths:
engines/director/castmember/bitmap.cpp
diff --git a/engines/director/castmember/bitmap.cpp b/engines/director/castmember/bitmap.cpp
index 8b917989bfa..5b3582c72a3 100644
--- a/engines/director/castmember/bitmap.cpp
+++ b/engines/director/castmember/bitmap.cpp
@@ -1135,6 +1135,10 @@ uint32 BitmapCastMember::writeBITDResource(Common::SeekableWriteStream *writeStr
}
uint32 BitmapCastMember::getBITDResourceSize() {
+ if (_external) {
+ return 0;
+ }
+
// No compression for now
return _pitch * _picture->_surface.h;
}
Commit: d0e5c19bd71bf0a1206da31f605495a5f611b232
https://github.com/scummvm/scummvm/commit/d0e5c19bd71bf0a1206da31f605495a5f611b232
Author: Malhar (themalharbdv2046 at gmail.com)
Date: 2025-08-09T18:30:24+02:00
Commit Message:
DIRECTOR: Simplify bitmap saving using `Graphics::Surface`
Changed paths:
engines/director/castmember/bitmap.cpp
diff --git a/engines/director/castmember/bitmap.cpp b/engines/director/castmember/bitmap.cpp
index 5b3582c72a3..88af89d2a51 100644
--- a/engines/director/castmember/bitmap.cpp
+++ b/engines/director/castmember/bitmap.cpp
@@ -1062,60 +1062,70 @@ uint32 BitmapCastMember::writeBITDResource(Common::SeekableWriteStream *writeStr
// No compression for now
// pixels.size() == bytes needed
- Common::Array<byte> pixels;
- pixels.resize(_pitch * _picture->_surface.h); // for <= 8bpp
+ Graphics::Surface pixels;
+ Graphics::PixelFormat format;
+
+ if (_bitsPerPixel >> 3) {
+ format.bytesPerPixel = _bitsPerPixel >> 3;
+ pixels.create(_picture->_surface.w, _picture->_surface.h, format);
+ } else {
+ format.bytesPerPixel = 1;
+ pixels.create(_pitch, _picture->_surface.h, format);
+ }
+
offset = 0;
- if (_bitsPerPixel == 8 && _picture->_surface.w < (int)(pixels.size() / _picture->_surface.h)) {
+ if (_bitsPerPixel == 8 && _picture->_surface.w < (int)(_pitch * _picture->_surface.h / _picture->_surface.h)) {
offset = (_pitch - _picture->_surface.w) % 2;
}
debugC(5, kDebugSaving, "BitmapCastMember::writeBITDResource: Saving 'BITD' Resource: bitsPerPixel: %d, castId: %d", _bitsPerPixel, _castId);
for (int y = 0; y < _picture->_surface.h; y++) {
+ byte *ptr = (byte *)pixels.getBasePtr(0, y);
+
for (int x = 0; x < _picture->_surface.w;) {
uint32 color = 0;
- int startX = x;
switch (_bitsPerPixel) {
case 1:
for (int c = 0; c < 8 && x < _picture->_surface.w; c++, x++) {
color += (*((byte *)_picture->_surface.getBasePtr(x, y))) & (1 << (7 - c));
}
- pixels[(y * _pitch) + (startX >> 3)] = color;
+ *ptr = color; ptr++;
break;
case 2:
for (int c = 0; c < 4 && x < _picture->_surface.w; c++, x++) {
color += (*((byte *)_picture->_surface.getBasePtr(x, y)) & 0x3) << (2 * (3 - c));
}
- pixels[(y * _pitch) + (startX >> 2)] = color;
+ *ptr = color; ptr++;
break;
case 4:
for (int c = 0; c < 2 && x < _picture->_surface.w; c++, x++) {
color += (*((byte *)_picture->_surface.getBasePtr(x, y)) & 0xF) << (4 * (1 - c));
}
- pixels[(y * _pitch) + (startX >> 1)] = color;
+ *ptr = color; ptr++;
break;
case 8:
- pixels[(y * _picture->_surface.w) + x + (y * offset)] = *((byte *)_picture->_surface.getBasePtr(x, y));
- x++;
+ *(ptr + (y * offset)) = *((byte *)_picture->_surface.getBasePtr(x, y));
+ ptr++; x++;
break;
case 16:
color = *((uint16 *)_picture->_surface.getBasePtr(x, y));
- pixels[(y * _picture->_surface.w * 2) + x * 2] = color >> 8;
- pixels[(y * _picture->_surface.w * 2) + x * 2 + 1] = color & 0xFF;
+ *ptr = color >> 8; ptr++;
+ *ptr = color & 0xFF; ptr++;
x++;
break;
case 32:
- color = *((uint32 *)(_picture->_surface.getBasePtr(x, y)));
- // only storing RGB, no alpha
- pixels[(y * _picture->_surface.w * 4) + x * 4 + 1] = (color >> 16) & 0xFF;
- pixels[(y * _picture->_surface.w * 4) + x * 4 + 2] = (color >> 8) & 0xFF;
- pixels[(y * _picture->_surface.w * 4) + x * 4 + 3] = color & 0xFF;
+ color = *((uint32 *)_picture->_surface.getBasePtr(x, y));
+ ptr++; // Ignore the Alpha value
+ *ptr = (color >> 16) & 0xFF; ptr++;
+ *ptr = (color >> 8) & 0xFF; ptr++;
+ *ptr = color & 0xFF; ptr++;
x++;
break;
@@ -1126,10 +1136,10 @@ uint32 BitmapCastMember::writeBITDResource(Common::SeekableWriteStream *writeStr
}
}
- writeStream->write(pixels.data(), pixels.size());
+ writeStream->write(pixels.getPixels(), _picture->_surface.h * _pitch);
if (debugChannelSet(7, kDebugSaving)) {
- dumpFile("BitmapData", _castId, MKTAG('B', 'I', 'T', 'D'), pixels.data(), pixels.size());
+ dumpFile("BitmapData", _castId, MKTAG('B', 'I', 'T', 'D'), (byte *)pixels.getPixels(), _picture->_surface.h * _pitch);
}
return 0;
}
More information about the Scummvm-git-logs
mailing list