aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--audio/decoders/adpcm.cpp5
-rw-r--r--audio/decoders/adpcm_intern.h2
-rw-r--r--backends/taskbar/macosx/macosx-taskbar.mm2
-rw-r--r--common/gui_options.cpp1
-rw-r--r--common/gui_options.h1
-rw-r--r--common/memstream.h2
-rw-r--r--engines/mohawk/detection.cpp74
-rw-r--r--engines/mohawk/detection_tables.h36
-rw-r--r--engines/mohawk/resource.h2
-rw-r--r--engines/mohawk/riven.cpp16
-rw-r--r--engines/mohawk/riven_saveload.cpp341
-rw-r--r--engines/mohawk/riven_saveload.h35
-rw-r--r--engines/sci/console.cpp15
-rw-r--r--engines/sci/detection.cpp12
-rw-r--r--engines/sci/detection_tables.h348
-rw-r--r--engines/sci/engine/file.cpp261
-rw-r--r--engines/sci/engine/file.h46
-rw-r--r--engines/sci/engine/kernel.cpp6
-rw-r--r--engines/sci/engine/kernel.h20
-rw-r--r--engines/sci/engine/kernel_tables.h35
-rw-r--r--engines/sci/engine/kfile.cpp109
-rw-r--r--engines/sci/engine/kgraphics32.cpp117
-rw-r--r--engines/sci/engine/kmisc.cpp3
-rw-r--r--engines/sci/engine/kvideo.cpp153
-rw-r--r--engines/sci/engine/savegame.cpp11
-rw-r--r--engines/sci/engine/script_patches.cpp168
-rw-r--r--engines/sci/engine/state.cpp6
-rw-r--r--engines/sci/engine/state.h5
-rw-r--r--engines/sci/graphics/celobj32.cpp31
-rw-r--r--engines/sci/graphics/celobj32.h12
-rw-r--r--engines/sci/graphics/controls32.cpp2
-rw-r--r--engines/sci/graphics/cursor.h2
-rw-r--r--engines/sci/graphics/frameout.cpp57
-rw-r--r--engines/sci/graphics/frameout.h19
-rw-r--r--engines/sci/graphics/paint32.cpp2
-rw-r--r--engines/sci/graphics/palette.h10
-rw-r--r--engines/sci/graphics/palette32.cpp278
-rw-r--r--engines/sci/graphics/palette32.h160
-rw-r--r--engines/sci/graphics/plane32.cpp9
-rw-r--r--engines/sci/graphics/plane32.h7
-rw-r--r--engines/sci/graphics/screen_item32.cpp16
-rw-r--r--engines/sci/graphics/screen_item32.h12
-rw-r--r--engines/sci/graphics/video32.cpp406
-rw-r--r--engines/sci/graphics/video32.h312
-rw-r--r--engines/sci/module.mk1
-rw-r--r--engines/sci/sci.cpp36
-rw-r--r--engines/sci/sci.h4
-rw-r--r--engines/sci/sound/audio32.cpp49
-rw-r--r--engines/sci/sound/audio32.h2
-rw-r--r--engines/sci/sound/music.cpp3
-rw-r--r--engines/sci/sound/soundcmd.cpp2
-rw-r--r--graphics/VectorRenderer.cpp29
-rw-r--r--graphics/VectorRenderer.h60
-rw-r--r--graphics/VectorRendererSpec.cpp1716
-rw-r--r--graphics/VectorRendererSpec.h73
-rw-r--r--graphics/nine_patch.cpp35
-rw-r--r--graphics/nine_patch.h1
-rw-r--r--graphics/transparent_surface.cpp133
-rw-r--r--graphics/transparent_surface.h8
-rw-r--r--gui/ThemeEngine.cpp455
-rw-r--r--gui/ThemeEngine.h31
-rw-r--r--gui/ThemeEval.cpp16
-rw-r--r--gui/ThemeLayout.h38
-rw-r--r--gui/dialog.cpp4
-rw-r--r--gui/gui-manager.cpp6
-rw-r--r--gui/gui-manager.h1
-rw-r--r--gui/module.mk1
-rw-r--r--gui/object.cpp13
-rw-r--r--gui/themes/default.inc14
-rw-r--r--gui/themes/scummclassic.zipbin112109 -> 115643 bytes
-rw-r--r--gui/themes/scummclassic/classic_layout.stx6
-rw-r--r--gui/themes/scummclassic/classic_layout_lowres.stx8
-rw-r--r--gui/themes/scummmodern.zipbin1487883 -> 1491681 bytes
-rw-r--r--gui/themes/scummmodern/scummmodern_layout.stx6
-rw-r--r--gui/themes/scummmodern/scummmodern_layout_lowres.stx8
-rw-r--r--gui/widget.cpp53
-rw-r--r--gui/widget.h4
-rw-r--r--gui/widgets/editable.cpp4
-rw-r--r--gui/widgets/edittext.cpp4
-rw-r--r--gui/widgets/list.cpp8
-rw-r--r--gui/widgets/popup.cpp12
-rw-r--r--gui/widgets/scrollbar.cpp7
-rw-r--r--gui/widgets/scrollcontainer.cpp147
-rw-r--r--gui/widgets/scrollcontainer.h63
-rw-r--r--gui/widgets/tab.cpp24
-rw-r--r--gui/widgets/tab.h1
-rw-r--r--video/coktel_decoder.cpp4
-rw-r--r--video/coktel_decoder.h2
88 files changed, 5307 insertions, 952 deletions
diff --git a/audio/decoders/adpcm.cpp b/audio/decoders/adpcm.cpp
index fd97d5d109..ffb61a49c0 100644
--- a/audio/decoders/adpcm.cpp
+++ b/audio/decoders/adpcm.cpp
@@ -320,10 +320,11 @@ int MS_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
_decodedSamples[_decodedSampleCount++] = decodeMS(&_status.ch[0], (data >> 4) & 0x0f);
_decodedSamples[_decodedSampleCount++] = decodeMS(&_status.ch[_channels - 1], data & 0x0f);
}
+ _decodedSampleIndex = 0;
}
- // (1 - (count - 1)) ensures that _decodedSamples acts as a FIFO of depth 2
- buffer[samples] = _decodedSamples[1 - (_decodedSampleCount - 1)];
+ // _decodedSamples acts as a FIFO of depth 2 or 4;
+ buffer[samples] = _decodedSamples[_decodedSampleIndex++];
_decodedSampleCount--;
}
diff --git a/audio/decoders/adpcm_intern.h b/audio/decoders/adpcm_intern.h
index 3a2af91c90..f4a708c3fc 100644
--- a/audio/decoders/adpcm_intern.h
+++ b/audio/decoders/adpcm_intern.h
@@ -209,6 +209,7 @@ public:
error("MS_ADPCMStream(): blockAlign isn't specified for MS ADPCM");
memset(&_status, 0, sizeof(_status));
_decodedSampleCount = 0;
+ _decodedSampleIndex = 0;
}
virtual bool endOfData() const { return (_stream->eos() || _stream->pos() >= _endpos) && (_decodedSampleCount == 0); }
@@ -220,6 +221,7 @@ protected:
private:
uint8 _decodedSampleCount;
+ uint8 _decodedSampleIndex;
int16 _decodedSamples[4];
};
diff --git a/backends/taskbar/macosx/macosx-taskbar.mm b/backends/taskbar/macosx/macosx-taskbar.mm
index 577320b79d..c842eb8090 100644
--- a/backends/taskbar/macosx/macosx-taskbar.mm
+++ b/backends/taskbar/macosx/macosx-taskbar.mm
@@ -269,7 +269,7 @@ void MacOSXTaskbarManager::addRecent(const Common::String &name, const Common::S
// Insert the new game at the start
[newArray insertObject:dict atIndex:0];
// If the game was already present in the array, remove it
- for (int i = 1 ; i < [newArray count] ; ++i) {
+ for (unsigned int i = 1 ; i < [newArray count] ; ++i) {
NSDictionary *oldDict = [newArray objectAtIndex:i];
if (oldDict == nil)
continue;
diff --git a/common/gui_options.cpp b/common/gui_options.cpp
index 473f78c795..df880f4fee 100644
--- a/common/gui_options.cpp
+++ b/common/gui_options.cpp
@@ -74,6 +74,7 @@ const struct GameOpt {
{ GUIO_GAMEOPTIONS6, "gameOption6" },
{ GUIO_GAMEOPTIONS7, "gameOption7" },
{ GUIO_GAMEOPTIONS8, "gameOption8" },
+ { GUIO_GAMEOPTIONS9, "gameOption9" },
{ GUIO_NONE, 0 }
};
diff --git a/common/gui_options.h b/common/gui_options.h
index aa15d906f5..ec3eccd161 100644
--- a/common/gui_options.h
+++ b/common/gui_options.h
@@ -68,6 +68,7 @@
#define GUIO_GAMEOPTIONS6 "\055"
#define GUIO_GAMEOPTIONS7 "\056"
#define GUIO_GAMEOPTIONS8 "\057"
+#define GUIO_GAMEOPTIONS9 "\058"
#define GUIO0() (GUIO_NONE)
#define GUIO1(a) (a)
diff --git a/common/memstream.h b/common/memstream.h
index a01973c6fa..94407f5cc9 100644
--- a/common/memstream.h
+++ b/common/memstream.h
@@ -157,7 +157,7 @@ public:
* that grows as it's written to.
*/
class MemoryWriteStreamDynamic : public WriteStream {
-private:
+protected:
uint32 _capacity;
uint32 _size;
byte *_ptr;
diff --git a/engines/mohawk/detection.cpp b/engines/mohawk/detection.cpp
index 7c202998eb..246d3ec3c1 100644
--- a/engines/mohawk/detection.cpp
+++ b/engines/mohawk/detection.cpp
@@ -41,6 +41,7 @@
#ifdef ENABLE_RIVEN
#include "mohawk/riven.h"
+#include "mohawk/riven_saveload.h"
#endif
namespace Mohawk {
@@ -198,6 +199,7 @@ public:
virtual bool hasFeature(MetaEngineFeature f) const;
virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
virtual SaveStateList listSaves(const char *target) const;
+ SaveStateList listSavesForPrefix(const char *prefix, const char *extension) const;
virtual int getMaximumSaveSlot() const { return 999; }
virtual void removeSaveState(const char *target, int slot) const;
virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -214,40 +216,58 @@ bool MohawkMetaEngine::hasFeature(MetaEngineFeature f) const {
|| (f == kSavesSupportPlayTime);
}
+SaveStateList MohawkMetaEngine::listSavesForPrefix(const char *prefix, const char *extension) const {
+ Common::String pattern = Common::String::format("%s-###.%s", prefix, extension);
+ Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles(pattern);
+ size_t prefixLen = strlen(prefix);
+
+ SaveStateList saveList;
+ for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) {
+ // Extract the slot number from the filename
+ char slot[4];
+ slot[0] = (*filename)[prefixLen + 1];
+ slot[1] = (*filename)[prefixLen + 2];
+ slot[2] = (*filename)[prefixLen + 3];
+ slot[3] = '\0';
+
+ int slotNum = atoi(slot);
+
+ saveList.push_back(SaveStateDescriptor(slotNum, ""));
+ }
+
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
+
+ return saveList;
+}
+
SaveStateList MohawkMetaEngine::listSaves(const char *target) const {
- Common::StringArray filenames;
SaveStateList saveList;
// Loading games is only supported in Myst/Riven currently.
#ifdef ENABLE_MYST
if (strstr(target, "myst")) {
- filenames = g_system->getSavefileManager()->listSavefiles("myst-###.mys");
- size_t prefixLen = sizeof("myst") - 1;
-
- for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) {
- // Extract the slot number from the filename
- char slot[4];
- slot[0] = (*filename)[prefixLen + 1];
- slot[1] = (*filename)[prefixLen + 2];
- slot[2] = (*filename)[prefixLen + 3];
- slot[3] = '\0';
-
- int slotNum = atoi(slot);
+ saveList = listSavesForPrefix("myst", "mys");
+ for (SaveStateList::iterator save = saveList.begin(); save != saveList.end(); ++save) {
// Read the description from the save
- Common::String description = Mohawk::MystGameState::querySaveDescription(slotNum);
- saveList.push_back(SaveStateDescriptor(slotNum, description));
+ int slot = save->getSaveSlot();
+ Common::String description = Mohawk::MystGameState::querySaveDescription(slot);
+ save->setDescription(description);
}
-
- Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
- } else
+ }
#endif
+#ifdef ENABLE_RIVEN
if (strstr(target, "riven")) {
- filenames = g_system->getSavefileManager()->listSavefiles("*.rvn");
+ saveList = listSavesForPrefix("riven", "rvn");
- for (uint32 i = 0; i < filenames.size(); i++)
- saveList.push_back(SaveStateDescriptor(i, filenames[i]));
+ for (SaveStateList::iterator save = saveList.begin(); save != saveList.end(); ++save) {
+ // Read the description from the save
+ int slot = save->getSaveSlot();
+ Common::String description = Mohawk::RivenSaveLoad::querySaveDescription(slot);
+ save->setDescription(description);
+ }
}
+#endif
return saveList;
}
@@ -258,18 +278,24 @@ void MohawkMetaEngine::removeSaveState(const char *target, int slot) const {
#ifdef ENABLE_MYST
if (strstr(target, "myst")) {
Mohawk::MystGameState::deleteSave(slot);
- } else
+ }
#endif
+#ifdef ENABLE_RIVEN
if (strstr(target, "riven")) {
- Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles("*.rvn");
- g_system->getSavefileManager()->removeSavefile(filenames[slot].c_str());
+ Mohawk::RivenSaveLoad::deleteSave(slot);
}
+#endif
}
SaveStateDescriptor MohawkMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
#ifdef ENABLE_MYST
if (strstr(target, "myst")) {
return Mohawk::MystGameState::querySaveMetaInfos(slot);
+ }
+#endif
+#ifdef ENABLE_RIVEN
+ if (strstr(target, "riven")) {
+ return Mohawk::RivenSaveLoad::querySaveMetaInfos(slot);
} else
#endif
{
diff --git a/engines/mohawk/detection_tables.h b/engines/mohawk/detection_tables.h
index e3eab89a34..d9aba26ca3 100644
--- a/engines/mohawk/detection_tables.h
+++ b/engines/mohawk/detection_tables.h
@@ -337,6 +337,24 @@ static const MohawkGameDescription gameDescriptions[] = {
},
// Riven: The Sequel to Myst
+ // Version 1.0 (5CD), 1.02 (DVD, From "Myst: La Trilogie")
+ // From gamin
+ {
+ {
+ "riven",
+ "",
+ AD_ENTRY1("a_Data.MHK", "aff2a384aaa9a0e0ec51010f708c5c04"),
+ Common::FR_FRA,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GType_RIVEN,
+ 0,
+ 0,
+ },
+
+ // Riven: The Sequel to Myst
// Version 1.0 (5CD) - Italian
// From dodomorandi on bug #6629
{
@@ -425,24 +443,6 @@ static const MohawkGameDescription gameDescriptions[] = {
},
// Riven: The Sequel to Myst
- // Version ? (DVD, From "Myst: La Trilogie")
- // From gamin
- {
- {
- "riven",
- "",
- AD_ENTRY1("a_Data.MHK", "aff2a384aaa9a0e0ec51010f708c5c04"),
- Common::FR_FRA,
- Common::kPlatformWindows,
- ADGF_UNSTABLE,
- GUIO1(GUIO_NOASPECT)
- },
- GType_RIVEN,
- GF_DVD,
- 0,
- },
-
- // Riven: The Sequel to Myst
// Version 1.02 (DVD, From "Myst: Antologia")
// From pykman
{
diff --git a/engines/mohawk/resource.h b/engines/mohawk/resource.h
index d9074a5b73..12c5a139e4 100644
--- a/engines/mohawk/resource.h
+++ b/engines/mohawk/resource.h
@@ -68,6 +68,8 @@ namespace Mohawk {
#define ID_VARS MKTAG('V','A','R','S') // Variable Values
#define ID_VERS MKTAG('V','E','R','S') // Version Info
#define ID_ZIPS MKTAG('Z','I','P','S') // Zip Mode Status
+#define ID_META MKTAG('M','E','T','A') // ScummVM save metadata
+#define ID_THMB MKTAG('T','H','M','B') // ScummVM save thumbnail
// Zoombini Resource FourCC's
#define ID_SND MKTAG( 0 ,'S','N','D') // Standard Mohawk Sound
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 0f764aeded..aa168a38d8 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -176,13 +176,10 @@ Common::Error MohawkEngine_Riven::run() {
changeToCard(6);
} else if (ConfMan.hasKey("save_slot")) {
// Load game from launcher/command line if requested
- uint32 gameToLoad = ConfMan.getInt("save_slot");
- Common::StringArray savedGamesList = _saveLoad->generateSaveGameList();
- if (gameToLoad > savedGamesList.size())
- error ("Could not find saved game");
+ int gameToLoad = ConfMan.getInt("save_slot");
// Attempt to load the game. On failure, just send us to the main menu.
- if (_saveLoad->loadGame(savedGamesList[gameToLoad]).getCode() != Common::kNoError) {
+ if (_saveLoad->loadGame(gameToLoad).getCode() != Common::kNoError) {
changeToStack(kStackAspit);
changeToCard(1);
}
@@ -734,16 +731,11 @@ void MohawkEngine_Riven::runLoadDialog() {
}
Common::Error MohawkEngine_Riven::loadGameState(int slot) {
- return _saveLoad->loadGame(_saveLoad->generateSaveGameList()[slot]);
+ return _saveLoad->loadGame(slot);
}
Common::Error MohawkEngine_Riven::saveGameState(int slot, const Common::String &desc) {
- Common::StringArray saveList = _saveLoad->generateSaveGameList();
-
- if ((uint)slot < saveList.size())
- _saveLoad->deleteSave(saveList[slot]);
-
- return _saveLoad->saveGame(desc);
+ return _saveLoad->saveGame(slot, desc);
}
Common::String MohawkEngine_Riven::getStackName(uint16 stack) const {
diff --git a/engines/mohawk/riven_saveload.cpp b/engines/mohawk/riven_saveload.cpp
index 6af66f7a2d..755f87767d 100644
--- a/engines/mohawk/riven_saveload.cpp
+++ b/engines/mohawk/riven_saveload.cpp
@@ -24,25 +24,141 @@
#include "mohawk/riven.h"
#include "mohawk/riven_saveload.h"
-#include "common/util.h"
+#include "common/system.h"
+#include "graphics/thumbnail.h"
namespace Mohawk {
+RivenSaveMetadata::RivenSaveMetadata() {
+ saveDay = 0;
+ saveMonth = 0;
+ saveYear = 0;
+ saveHour = 0;
+ saveMinute = 0;
+ totalPlayTime = 0;
+}
+
+bool RivenSaveMetadata::sync(Common::Serializer &s) {
+ static const Common::Serializer::Version kCurrentVersion = 1;
+
+ if (!s.syncVersion(kCurrentVersion)) {
+ return false;
+ }
+
+ s.syncAsByte(saveDay);
+ s.syncAsByte(saveMonth);
+ s.syncAsUint16BE(saveYear);
+ s.syncAsByte(saveHour);
+ s.syncAsByte(saveMinute);
+ s.syncString(saveDescription);
+ s.syncAsUint32BE(totalPlayTime);
+
+ return true;
+}
+
RivenSaveLoad::RivenSaveLoad(MohawkEngine_Riven *vm, Common::SaveFileManager *saveFileMan) : _vm(vm), _saveFileMan(saveFileMan) {
}
RivenSaveLoad::~RivenSaveLoad() {
}
-Common::StringArray RivenSaveLoad::generateSaveGameList() {
- return _saveFileMan->listSavefiles("*.rvn");
+Common::String RivenSaveLoad::buildSaveFilename(const int slot) {
+ return Common::String::format("riven-%03d.rvn", slot);
+}
+
+Common::String RivenSaveLoad::querySaveDescription(const int slot) {
+ Common::String filename = buildSaveFilename(slot);
+ Common::InSaveFile *loadFile = g_system->getSavefileManager()->openForLoading(filename);
+ if (!loadFile) {
+ return "";
+ }
+
+ MohawkArchive mhk;
+ if (!mhk.openStream(loadFile)) {
+ return "";
+ }
+
+ if (!mhk.hasResource(ID_META, 1)) {
+ return "";
+ }
+
+ Common::SeekableReadStream *metaStream = mhk.getResource(ID_META, 1);
+ if (!metaStream) {
+ return "";
+ }
+
+ Common::Serializer serializer = Common::Serializer(metaStream, nullptr);
+
+ RivenSaveMetadata metadata;
+ if (!metadata.sync(serializer)) {
+ delete metaStream;
+ return "";
+ }
+
+ delete metaStream;
+
+ return metadata.saveDescription;
}
-Common::Error RivenSaveLoad::loadGame(Common::String filename) {
+SaveStateDescriptor RivenSaveLoad::querySaveMetaInfos(const int slot) {
+ Common::String filename = buildSaveFilename(slot);
+ Common::InSaveFile *loadFile = g_system->getSavefileManager()->openForLoading(filename);
+ if (!loadFile) {
+ return SaveStateDescriptor();
+ }
+
+ MohawkArchive mhk;
+ if (!mhk.openStream(loadFile)) {
+ return SaveStateDescriptor();
+ }
+
+ if (!mhk.hasResource(ID_META, 1)) {
+ return SaveStateDescriptor();
+ }
+
+ Common::SeekableReadStream *metaStream = mhk.getResource(ID_META, 1);
+ if (!metaStream) {
+ return SaveStateDescriptor();
+ }
+
+ Common::Serializer serializer = Common::Serializer(metaStream, nullptr);
+
+ RivenSaveMetadata metadata;
+ if (!metadata.sync(serializer)) {
+ delete metaStream;
+ return SaveStateDescriptor();
+ }
+
+ SaveStateDescriptor descriptor;
+ descriptor.setDescription(metadata.saveDescription);
+ descriptor.setPlayTime(metadata.totalPlayTime);
+ descriptor.setSaveDate(metadata.saveYear, metadata.saveMonth, metadata.saveDay);
+ descriptor.setSaveTime(metadata.saveHour, metadata.saveMinute);
+
+ delete metaStream;
+
+ if (!mhk.hasResource(ID_THMB, 1)) {
+ return descriptor;
+ }
+
+ Common::SeekableReadStream *thmbStream = mhk.getResource(ID_THMB, 1);
+ if (!thmbStream) {
+ return descriptor;
+ }
+
+ descriptor.setThumbnail(Graphics::loadThumbnail(*thmbStream));
+
+ delete thmbStream;
+
+ return descriptor;
+}
+
+Common::Error RivenSaveLoad::loadGame(const int slot) {
if (_vm->getFeatures() & GF_DEMO) // Don't load games in the demo
return Common::kNoError;
- Common::InSaveFile *loadFile = _saveFileMan->openForLoading(filename);
+ Common::String filename = buildSaveFilename(slot);
+ Common::InSaveFile *loadFile = _saveFileMan->openForLoading(filename);
if (!loadFile)
return Common::kReadingFailed;
@@ -72,8 +188,11 @@ Common::Error RivenSaveLoad::loadGame(Common::String filename) {
Common::Array<uint32> rawVariables;
while (!vars->eos()) {
- vars->readUint32BE(); // Unknown (Stack?)
- vars->readUint32BE(); // Unknown (0 or 1)
+ // The original engine stores the variables values in an array. All the slots in
+ // the array may not be in use, which is why it needs a reference counter and
+ // a flag to tell if the value has been set.
+ vars->readUint32BE(); // Reference counter
+ vars->readUint32BE(); // Variable initialized flag
rawVariables.push_back(vars->readUint32BE());
}
@@ -144,6 +263,20 @@ Common::Error RivenSaveLoad::loadGame(Common::String filename) {
}
delete zips;
+
+ // Load the ScummVM specific save metadata
+ if (mhk->hasResource(ID_META, 1)) {
+ Common::SeekableReadStream *metadataStream = mhk->getResource(ID_META, 1);
+ Common::Serializer serializer = Common::Serializer(metadataStream, nullptr);
+
+ RivenSaveMetadata metadata;
+ metadata.sync(serializer);
+
+ // Set the saved total play time
+ _vm->setTotalPlayTime(metadata.totalPlayTime);
+
+ delete metadataStream;
+ }
delete mhk;
return Common::kNoError;
@@ -162,14 +295,18 @@ Common::MemoryWriteStreamDynamic *RivenSaveLoad::genVARSSection() {
Common::MemoryWriteStreamDynamic *stream = new Common::MemoryWriteStreamDynamic();
for (RivenVariableMap::const_iterator it = _vm->_vars.begin(); it != _vm->_vars.end(); it++) {
- stream->writeUint32BE(0); // Unknown
- stream->writeUint32BE(0); // Unknown
+ stream->writeUint32BE(1); // Reference counter
+ stream->writeUint32BE(1); // Variable initialized flag
stream->writeUint32BE(it->_value);
}
return stream;
}
+static int stringCompareToIgnoreCase(const Common::String &s1, const Common::String &s2) {
+ return s1.compareToIgnoreCase(s2) < 0;
+}
+
Common::MemoryWriteStreamDynamic *RivenSaveLoad::genNAMESection() {
Common::MemoryWriteStreamDynamic *stream = new Common::MemoryWriteStreamDynamic();
@@ -181,8 +318,28 @@ Common::MemoryWriteStreamDynamic *RivenSaveLoad::genNAMESection() {
curPos += it->_key.size() + 1;
}
- for (uint16 i = 0; i < _vm->_vars.size(); i++)
- stream->writeUint16BE(i);
+ // The original engine does not store the variables in a HashMap, but in a "NameList"
+ // for the keys and an array for the values. The NameList data structure maintains an array
+ // of indices in the string table sorted by case insensitive key alphabetical order.
+ // It is used to perform fast key -> index lookups.
+ // ScummVM does not need the sorted array, but has to write it anyway for the saved games
+ // to be compatible with original engine.
+ Common::Array<Common::String> sortedKeys;
+ for (RivenVariableMap::const_iterator it = _vm->_vars.begin(); it != _vm->_vars.end(); it++) {
+ sortedKeys.push_back(it->_key);
+ }
+ Common::sort(sortedKeys.begin(), sortedKeys.end(), stringCompareToIgnoreCase);
+
+ for (uint i = 0; i < sortedKeys.size(); i++) {
+ uint16 varIndex = 0;
+ for (RivenVariableMap::const_iterator it = _vm->_vars.begin(); it != _vm->_vars.end(); it++) {
+ if (it->_key == sortedKeys[i]) {
+ stream->writeUint16BE(varIndex);
+ break;
+ }
+ varIndex++;
+ }
+ }
for (RivenVariableMap::const_iterator it = _vm->_vars.begin(); it != _vm->_vars.end(); it++) {
stream->write(it->_key.c_str(), it->_key.size());
@@ -206,7 +363,35 @@ Common::MemoryWriteStreamDynamic *RivenSaveLoad::genZIPSSection() {
return stream;
}
-Common::Error RivenSaveLoad::saveGame(Common::String filename) {
+Common::MemoryWriteStreamDynamic *RivenSaveLoad::genTHMBSection() const {
+ Common::MemoryWriteStreamDynamic *stream = new Common::MemoryWriteStreamDynamic();
+
+ Graphics::saveThumbnail(*stream);
+
+ return stream;
+}
+
+Common::MemoryWriteStreamDynamic *RivenSaveLoad::genMETASection(const Common::String &desc) const {
+ Common::MemoryWriteStreamDynamic *stream = new Common::MemoryWriteStreamDynamic();
+ Common::Serializer serializer = Common::Serializer(nullptr, stream);
+
+ TimeDate t;
+ _vm->_system->getTimeAndDate(t);
+
+ RivenSaveMetadata metadata;
+ metadata.saveDay = t.tm_mday;
+ metadata.saveMonth = t.tm_mon + 1;
+ metadata.saveYear = t.tm_year + 1900;
+ metadata.saveHour = t.tm_hour;
+ metadata.saveMinute = t.tm_min;
+ metadata.saveDescription = desc;
+ metadata.totalPlayTime = _vm->getTotalPlayTime();
+ metadata.sync(serializer);
+
+ return stream;
+}
+
+Common::Error RivenSaveLoad::saveGame(const int slot, const Common::String &description) {
// NOTE: This code is designed to only output a Mohawk archive
// for a Riven saved game. It's hardcoded to do this because
// (as of right now) this is the only place in the engine
@@ -214,13 +399,7 @@ Common::Error RivenSaveLoad::saveGame(Common::String filename) {
// games need this, we should think about coming up with some
// more common way of outputting resources to an archive.
- // TODO: Make these saves work with the original interpreter.
- // Not sure why they don't work yet (they still can be loaded
- // by ScummVM).
-
- // Make sure we have the right extension
- if (!filename.matchString("*.rvn", true))
- filename += ".rvn";
+ Common::String filename = buildSaveFilename(slot);
// Convert class variables to variable numbers
_vm->_vars["currentstackid"] = _vm->getCurStack();
@@ -232,16 +411,20 @@ Common::Error RivenSaveLoad::saveGame(Common::String filename) {
debug (0, "Saving game to \'%s\'", filename.c_str());
- Common::MemoryWriteStreamDynamic *versSection = genVERSSection();
+ Common::MemoryWriteStreamDynamic *metaSection = genMETASection(description);
Common::MemoryWriteStreamDynamic *nameSection = genNAMESection();
+ Common::MemoryWriteStreamDynamic *thmbSection = genTHMBSection();
Common::MemoryWriteStreamDynamic *varsSection = genVARSSection();
+ Common::MemoryWriteStreamDynamic *versSection = genVERSSection();
Common::MemoryWriteStreamDynamic *zipsSection = genZIPSSection();
// Let's calculate the file size!
- uint32 fileSize = 142;
- fileSize += versSection->size();
+ uint32 fileSize = 194;
+ fileSize += metaSection->size();
fileSize += nameSection->size();
+ fileSize += thmbSection->size();
fileSize += varsSection->size();
+ fileSize += versSection->size();
fileSize += zipsSection->size();
// MHWK Header (8 bytes - total: 8)
@@ -254,109 +437,151 @@ Common::Error RivenSaveLoad::saveGame(Common::String filename) {
saveFile->writeUint16BE(1); // Compaction -- original saves have this too
saveFile->writeUint32BE(fileSize); // Subtract off the MHWK header size
saveFile->writeUint32BE(28); // Absolute offset: right after both headers
- saveFile->writeUint16BE(70); // File Table Offset
- saveFile->writeUint16BE(44); // File Table Size (4 bytes count + 4 entries * 10 bytes per entry)
+ saveFile->writeUint16BE(102); // File Table Offset
+ saveFile->writeUint16BE(64); // File Table Size (4 bytes count + 6 entries * 10 bytes per entry)
// Type Table (4 bytes - total: 32)
- saveFile->writeUint16BE(36); // String table offset After the Type Table Entries
- saveFile->writeUint16BE(4); // 4 Type Table Entries
+ saveFile->writeUint16BE(52); // String table offset After the Type Table Entries
+ saveFile->writeUint16BE(6); // 6 Type Table Entries
- // Hardcode Entries (32 bytes - total: 64)
- saveFile->writeUint32BE(ID_VERS);
- saveFile->writeUint16BE(46); // Resource table offset
- saveFile->writeUint16BE(38); // String table offset
+ // Hardcode Entries (48 bytes - total: 80)
+ // The original engine relies on the entries being sorted by tag alphabetical order
+ // to optimize its lookup algorithm.
+ saveFile->writeUint32BE(ID_META);
+ saveFile->writeUint16BE(66); // Resource table offset
+ saveFile->writeUint16BE(54); // String table offset
saveFile->writeUint32BE(ID_NAME);
- saveFile->writeUint16BE(52);
- saveFile->writeUint16BE(40);
+ saveFile->writeUint16BE(72);
+ saveFile->writeUint16BE(56);
- saveFile->writeUint32BE(ID_VARS);
+ saveFile->writeUint32BE(ID_THMB);
+ saveFile->writeUint16BE(78);
saveFile->writeUint16BE(58);
- saveFile->writeUint16BE(42);
+
+ saveFile->writeUint32BE(ID_VARS);
+ saveFile->writeUint16BE(84);
+ saveFile->writeUint16BE(60);
+
+ saveFile->writeUint32BE(ID_VERS);
+ saveFile->writeUint16BE(90);
+ saveFile->writeUint16BE(62);
saveFile->writeUint32BE(ID_ZIPS);
+ saveFile->writeUint16BE(96);
saveFile->writeUint16BE(64);
- saveFile->writeUint16BE(44);
- // Pseudo-String Table (2 bytes - total: 66)
+ // Pseudo-String Table (2 bytes - total: 82)
saveFile->writeUint16BE(0); // We don't need a name list
- // Psuedo-Name Tables (8 bytes - total: 74)
+ // Pseudo-Name Tables (12 bytes - total: 94)
+ saveFile->writeUint16BE(0);
+ saveFile->writeUint16BE(0);
saveFile->writeUint16BE(0);
saveFile->writeUint16BE(0);
saveFile->writeUint16BE(0);
saveFile->writeUint16BE(0);
- // VERS Section (Resource Table) (6 bytes - total: 80)
+ // META Section (Resource Table) (6 bytes - total: 100)
saveFile->writeUint16BE(1);
saveFile->writeUint16BE(1);
saveFile->writeUint16BE(1);
- // NAME Section (Resource Table) (6 bytes - total: 86)
+ // NAME Section (Resource Table) (6 bytes - total: 106)
saveFile->writeUint16BE(1);
saveFile->writeUint16BE(1);
saveFile->writeUint16BE(2);
- // VARS Section (Resource Table) (6 bytes - total: 92)
+ // THMB Section (Resource Table) (6 bytes - total: 112)
saveFile->writeUint16BE(1);
saveFile->writeUint16BE(1);
saveFile->writeUint16BE(3);
- // ZIPS Section (Resource Table) (6 bytes - total: 98)
+ // VARS Section (Resource Table) (6 bytes - total: 118)
saveFile->writeUint16BE(1);
saveFile->writeUint16BE(1);
saveFile->writeUint16BE(4);
- // File Table (4 bytes - total: 102)
- saveFile->writeUint32BE(4);
+ // VERS Section (Resource Table) (6 bytes - total: 124)
+ saveFile->writeUint16BE(1);
+ saveFile->writeUint16BE(1);
+ saveFile->writeUint16BE(5);
- // VERS Section (File Table) (10 bytes - total: 112)
- saveFile->writeUint32BE(142);
- saveFile->writeUint16BE(versSection->size() & 0xFFFF);
- saveFile->writeByte((versSection->size() & 0xFF0000) >> 16);
+ // ZIPS Section (Resource Table) (6 bytes - total: 130)
+ saveFile->writeUint16BE(1);
+ saveFile->writeUint16BE(1);
+ saveFile->writeUint16BE(6);
+
+ // File Table (4 bytes - total: 134)
+ saveFile->writeUint32BE(6);
+
+ // META Section (File Table) (10 bytes - total: 144)
+ saveFile->writeUint32BE(194);
+ saveFile->writeUint16BE(metaSection->size() & 0xFFFF);
+ saveFile->writeByte((metaSection->size() & 0xFF0000) >> 16);
saveFile->writeByte(0);
saveFile->writeUint16BE(0);
- // NAME Section (File Table) (10 bytes - total: 122)
- saveFile->writeUint32BE(142 + versSection->size());
+ // NAME Section (File Table) (10 bytes - total: 154)
+ saveFile->writeUint32BE(194 + metaSection->size());
saveFile->writeUint16BE(nameSection->size() & 0xFFFF);
saveFile->writeByte((nameSection->size() & 0xFF0000) >> 16);
saveFile->writeByte(0);
saveFile->writeUint16BE(0);
- // VARS Section (File Table) (10 bytes - total: 132)
- saveFile->writeUint32BE(142 + versSection->size() + nameSection->size());
+ // THMB Section (File Table) (10 bytes - total: 164)
+ saveFile->writeUint32BE(194 + metaSection->size() + nameSection->size());
+ saveFile->writeUint16BE(thmbSection->size() & 0xFFFF);
+ saveFile->writeByte((thmbSection->size() & 0xFF0000) >> 16);
+ saveFile->writeByte(0);
+ saveFile->writeUint16BE(0);
+
+ // VARS Section (File Table) (10 bytes - total: 174)
+ saveFile->writeUint32BE(194 + metaSection->size() + nameSection->size() + thmbSection->size());
saveFile->writeUint16BE(varsSection->size() & 0xFFFF);
saveFile->writeByte((varsSection->size() & 0xFF0000) >> 16);
saveFile->writeByte(0);
saveFile->writeUint16BE(0);
- // ZIPS Section (File Table) (10 bytes - total: 142)
- saveFile->writeUint32BE(142 + versSection->size() + nameSection->size() + varsSection->size());
+ // VERS Section (File Table) (10 bytes - total: 184)
+ saveFile->writeUint32BE(194 + metaSection->size() + nameSection->size() + thmbSection->size() + varsSection->size());
+ saveFile->writeUint16BE(versSection->size() & 0xFFFF);
+ saveFile->writeByte((versSection->size() & 0xFF0000) >> 16);
+ saveFile->writeByte(0);
+ saveFile->writeUint16BE(0);
+
+ // ZIPS Section (File Table) (10 bytes - total: 194)
+ saveFile->writeUint32BE(194 + metaSection->size() + nameSection->size() + thmbSection->size() + varsSection->size() + versSection->size());
saveFile->writeUint16BE(zipsSection->size() & 0xFFFF);
saveFile->writeByte((zipsSection->size() & 0xFF0000) >> 16);
saveFile->writeByte(0);
saveFile->writeUint16BE(0);
- saveFile->write(versSection->getData(), versSection->size());
+ saveFile->write(metaSection->getData(), metaSection->size());
saveFile->write(nameSection->getData(), nameSection->size());
+ saveFile->write(thmbSection->getData(), thmbSection->size());
saveFile->write(varsSection->getData(), varsSection->size());
+ saveFile->write(versSection->getData(), versSection->size());
saveFile->write(zipsSection->getData(), zipsSection->size());
saveFile->finalize();
delete saveFile;
- delete versSection;
+ delete metaSection;
delete nameSection;
+ delete thmbSection;
delete varsSection;
+ delete versSection;
delete zipsSection;
return Common::kNoError;
}
-void RivenSaveLoad::deleteSave(Common::String saveName) {
- debug (0, "Deleting save file \'%s\'", saveName.c_str());
- _saveFileMan->removeSavefile(saveName);
+void RivenSaveLoad::deleteSave(const int slot) {
+ Common::String filename = buildSaveFilename(slot);
+
+ debug (0, "Deleting save file \'%s\'", filename.c_str());
+ g_system->getSavefileManager()->removeSavefile(filename);
}
} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_saveload.h b/engines/mohawk/riven_saveload.h
index a6ddce5713..34bfbdc434 100644
--- a/engines/mohawk/riven_saveload.h
+++ b/engines/mohawk/riven_saveload.h
@@ -23,10 +23,13 @@
#ifndef MOHAWK_SAVELOAD_H
#define MOHAWK_SAVELOAD_H
+#include "common/serializer.h"
#include "common/savefile.h"
#include "common/str.h"
#include "common/memstream.h"
+#include "engines/savestate.h"
+
namespace Mohawk {
class MohawkEngine_Riven;
@@ -36,23 +39,45 @@ enum {
kDVDSaveGameVersion = 0x00010100
};
+struct RivenSaveMetadata {
+ uint8 saveDay;
+ uint8 saveMonth;
+ uint16 saveYear;
+
+ uint8 saveHour;
+ uint8 saveMinute;
+
+ uint32 totalPlayTime;
+
+ Common::String saveDescription;
+
+ RivenSaveMetadata();
+ bool sync(Common::Serializer &s);
+};
+
class RivenSaveLoad {
public:
RivenSaveLoad(MohawkEngine_Riven*, Common::SaveFileManager*);
~RivenSaveLoad();
- Common::StringArray generateSaveGameList();
- Common::Error loadGame(Common::String);
- Common::Error saveGame(Common::String);
- void deleteSave(Common::String);
+ Common::Error loadGame(const int slot);
+ Common::Error saveGame(const int slot, const Common::String &description);
+ static void deleteSave(const int slot);
+
+ static SaveStateDescriptor querySaveMetaInfos(const int slot);
+ static Common::String querySaveDescription(const int slot);
private:
MohawkEngine_Riven *_vm;
Common::SaveFileManager *_saveFileMan;
- Common::MemoryWriteStreamDynamic *genVERSSection();
+ static Common::String buildSaveFilename(const int slot);
+
Common::MemoryWriteStreamDynamic *genNAMESection();
+ Common::MemoryWriteStreamDynamic *genMETASection(const Common::String &desc) const;
+ Common::MemoryWriteStreamDynamic *genTHMBSection() const;
Common::MemoryWriteStreamDynamic *genVARSSection();
+ Common::MemoryWriteStreamDynamic *genVERSSection();
Common::MemoryWriteStreamDynamic *genZIPSSection();
};
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp
index 2c74fe4b0c..6898745858 100644
--- a/engines/sci/console.cpp
+++ b/engines/sci/console.cpp
@@ -497,8 +497,10 @@ bool Console::cmdGetVersion(int argc, const char **argv) {
if (getSciVersion() <= SCI_VERSION_1_1) {
debugPrintf("kAnimate fastCast enabled: %s\n", g_sci->_gfxAnimate->isFastCastEnabled() ? "yes" : "no");
}
- debugPrintf("Uses palette merging: %s\n", g_sci->_gfxPalette16->isMerging() ? "yes" : "no");
- debugPrintf("Uses 16 bit color matching: %s\n", g_sci->_gfxPalette16->isUsing16bitColorMatch() ? "yes" : "no");
+ if (getSciVersion() < SCI_VERSION_2) {
+ debugPrintf("Uses palette merging: %s\n", g_sci->_gfxPalette16->isMerging() ? "yes" : "no");
+ debugPrintf("Uses 16 bit color matching: %s\n", g_sci->_gfxPalette16->isUsing16bitColorMatch() ? "yes" : "no");
+ }
debugPrintf("Resource volume version: %s\n", g_sci->getResMan()->getVolVersionDesc());
debugPrintf("Resource map version: %s\n", g_sci->getResMan()->getMapVersionDesc());
debugPrintf("Contains selector vocabulary (vocab.997): %s\n", hasVocab997 ? "yes" : "no");
@@ -1620,7 +1622,7 @@ bool Console::cmdParserNodes(int argc, const char **argv) {
bool Console::cmdSetPalette(int argc, const char **argv) {
if (argc < 2) {
- debugPrintf("Sets a palette resource\n");
+ debugPrintf("Sets a palette resource (SCI16)\n");
debugPrintf("Usage: %s <resourceId>\n", argv[0]);
debugPrintf("where <resourceId> is the number of the palette resource to set\n");
return true;
@@ -1628,6 +1630,13 @@ bool Console::cmdSetPalette(int argc, const char **argv) {
uint16 resourceId = atoi(argv[1]);
+#ifdef ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2) {
+ debugPrintf("This SCI version does not support this command\n");
+ return true;
+ }
+#endif
+
_engine->_gfxPalette16->kernelSetFromResource(resourceId, true);
return true;
}
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index 6b945d6f62..8691c37b3e 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -107,6 +107,7 @@ static const PlainGameDescriptor s_sciGameTitles[] = {
{"pq4", "Police Quest IV: Open Season"}, // floppy is SCI2, CD SCI2.1
{"qfg4", "Quest for Glory IV: Shadows of Darkness"}, // floppy is SCI2, CD SCI2.1
// === SCI2.1 games ========================================================
+ {"hoyle5", "Hoyle Classic Games"},
{"chest", "Inside the Chest"}, // aka Behind the Developer's Shield
{"gk2", "The Beast Within: A Gabriel Knight Mystery"},
{"kq7", "King's Quest VII: The Princeless Bride"},
@@ -157,6 +158,7 @@ static const GameIdStrToEnum s_gameIdStrToEnum[] = {
{ "hoyle2", GID_HOYLE2 },
{ "hoyle3", GID_HOYLE3 },
{ "hoyle4", GID_HOYLE4 },
+ { "hoyle5", GID_HOYLE5 },
{ "iceman", GID_ICEMAN },
{ "inndemo", GID_INNDEMO },
{ "islandbrain", GID_ISLANDBRAIN },
@@ -397,6 +399,16 @@ static const ADExtraGuiOptionsMap optionsList[] = {
},
{
+ GAMEOPTION_ENABLE_BLACK_LINED_VIDEO,
+ {
+ _s("Enable black-lined video"),
+ _s("Draw black lines over videos to increase their apparent sharpness"),
+ "enable_black_lined_video",
+ false
+ }
+ },
+
+ {
GAMEOPTION_PREFER_DIGITAL_SFX,
{
_s("Prefer digital sound effects"),
diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h
index 2595616f71..f205609e20 100644
--- a/engines/sci/detection_tables.h
+++ b/engines/sci/detection_tables.h
@@ -30,6 +30,7 @@ namespace Sci {
#define GAMEOPTION_SQ4_SILVER_CURSORS GUIO_GAMEOPTIONS6
#define GAMEOPTION_EGA_UNDITHER GUIO_GAMEOPTIONS7
#define GAMEOPTION_HIGH_RESOLUTION_GRAPHICS GUIO_GAMEOPTIONS8
+#define GAMEOPTION_ENABLE_BLACK_LINED_VIDEO GUIO_GAMEOPTIONS9
// SCI3 games have a different script format (in CSC files) and are currently unsupported
#define ENABLE_SCI3_GAMES
@@ -729,13 +730,22 @@ static const struct ADGameDescription SciGameDescriptions[] = {
Common::EN_ANY, Common::kPlatformDOS, 0, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
+#define GUIO_GK1_FLOPPY GUIO4(GUIO_NOSPEECH, \
+ GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
+#define GUIO_GK1_CD GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
+#define GUIO_GK1_MAC GUIO_GK1_FLOPPY
+
// Gabriel Knight - English DOS Floppy
// SCI interpreter version 2.000.000
{"gk1", "", {
{"resource.map", 0, "372d059f75856afa6d73dd84cbb8913d", 10783},
{"resource.000", 0, "69b7516962510f780d38519cc15fcc7c", 13022630},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_GK1_FLOPPY },
// Gabriel Knight - English DOS Floppy (supplied my markcoolio in bug report #2723777)
// SCI interpreter version 2.000.000
@@ -743,7 +753,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "65e8c14092e4c9b3b3538b7602c8c5ec", 10783},
{"resource.000", 0, "69b7516962510f780d38519cc15fcc7c", 13022630},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_GK1_FLOPPY },
// Gabriel Knight - English DOS Floppy
// SCI interpreter version 2.000.000, VERSION file reports "1.0\nGabriel Knight\n11/22/10:33 pm\n\x1A"
@@ -751,7 +761,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "ef41df08cf2c1f680216cdbeed0f8311", 10783},
{"resource.000", 0, "69b7516962510f780d38519cc15fcc7c", 13022630},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_GK1_FLOPPY },
// Gabriel Knight - German DOS Floppy (supplied my markcoolio in bug report #2723775)
// SCI interpreter version 2.000.000
@@ -759,7 +769,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "ad6508b0296b25c07b1f58828dc33696", 10789},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13077029},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::DE_DEU, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_GK1_FLOPPY },
// Gabriel Knight - French DOS Floppy (supplied my kervala in bug report #3611487)
// SCI interpreter version 2.000.000
@@ -767,7 +777,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "236e36cc847cdeafdd5e5fa8cba916ed", 10801},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13033072},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::FR_FRA, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_GK1_FLOPPY },
// Gabriel Knight - English DOS CD (from jvprat)
// Executable scanning reports "2.000.000", VERSION file reports "01.100.000"
@@ -775,7 +785,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "372d059f75856afa6d73dd84cbb8913d", 10996},
{"resource.000", 0, "69b7516962510f780d38519cc15fcc7c", 12581736},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_CD | ADGF_UNSTABLE, GUIO4(GAMEOPTION_HIGH_RESOLUTION_GRAPHICS, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_CD | ADGF_UNSTABLE, GUIO_GK1_CD },
// Gabriel Knight - English Windows CD (from jvprat)
// Executable scanning reports "2.000.000", VERSION file reports "01.100.000"
@@ -783,7 +793,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "372d059f75856afa6d73dd84cbb8913d", 10996},
{"resource.000", 0, "69b7516962510f780d38519cc15fcc7c", 12581736},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_CD | ADGF_UNSTABLE, GUIO5(GUIO_NOASPECT, GAMEOPTION_HIGH_RESOLUTION_GRAPHICS, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_CD | ADGF_UNSTABLE, GUIO_GK1_CD },
// Gabriel Knight - German DOS CD (from Tobis87)
// SCI interpreter version 2.000.000
@@ -791,7 +801,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a7d3e55114c65647310373cb390815ba", 11392},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13400497},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformDOS, ADGF_CD | ADGF_UNSTABLE, GUIO4(GAMEOPTION_HIGH_RESOLUTION_GRAPHICS, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::DE_DEU, Common::kPlatformDOS, ADGF_CD | ADGF_UNSTABLE, GUIO_GK1_CD },
// Gabriel Knight - Spanish DOS CD (from jvprat)
// Executable scanning reports "2.000.000", VERSION file reports "1.000.000, April 13, 1995"
@@ -799,7 +809,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "7cb6e9bba15b544ec7a635c45bde9953", 11404},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13381599},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformDOS, ADGF_CD | ADGF_UNSTABLE, GUIO4(GAMEOPTION_HIGH_RESOLUTION_GRAPHICS, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::ES_ESP, Common::kPlatformDOS, ADGF_CD | ADGF_UNSTABLE, GUIO_GK1_CD },
// Gabriel Knight - French DOS CD (from Hkz)
// VERSION file reports "1.000.000, May 3, 1994"
@@ -807,7 +817,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "55f909ba93a2515042a08d8a2da8414e", 11392},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13325145},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformDOS, ADGF_CD | ADGF_UNSTABLE, GUIO4(GAMEOPTION_HIGH_RESOLUTION_GRAPHICS, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::FR_FRA, Common::kPlatformDOS, ADGF_CD | ADGF_UNSTABLE, GUIO_GK1_CD },
// Gabriel Knight - German Windows CD (from Tobis87)
// SCI interpreter version 2.000.000
@@ -815,7 +825,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a7d3e55114c65647310373cb390815ba", 11392},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13400497},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformWindows, ADGF_CD | ADGF_UNSTABLE, GUIO5(GUIO_NOASPECT, GAMEOPTION_HIGH_RESOLUTION_GRAPHICS, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::DE_DEU, Common::kPlatformWindows, ADGF_CD | ADGF_UNSTABLE, GUIO_GK1_CD },
// Gabriel Knight - Spanish Windows CD (from jvprat)
// Executable scanning reports "2.000.000", VERSION file reports "1.000.000, April 13, 1995"
@@ -823,7 +833,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "7cb6e9bba15b544ec7a635c45bde9953", 11404},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13381599},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformWindows, ADGF_CD | ADGF_UNSTABLE, GUIO5(GUIO_NOASPECT, GAMEOPTION_HIGH_RESOLUTION_GRAPHICS, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::ES_ESP, Common::kPlatformWindows, ADGF_CD | ADGF_UNSTABLE, GUIO_GK1_CD },
// Gabriel Knight - English Macintosh
{"gk1", "", {
@@ -832,7 +842,19 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"Data3", 0, "f25068b408b09275d8b698866462f578", 3677599},
{"Data4", 0, "1cceebbe411b26c860a74f91c337fdf3", 3230086},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK | ADGF_UNSTABLE, GUIO_GK1_MAC },
+
+#define GUIO_GK2_DEMO GUIO5(GUIO_NOSPEECH, \
+ GUIO_NOASPECT, \
+ GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
+#define GUIO_GK2 GUIO5(GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
+ GUIO_NOASPECT, \
+ GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
+#define GUIO_GK2_MAC GUIO_GK2
// Gabriel Knight 2 - English Windows Non-Interactive Demo
// Executable scanning reports "2.100.002"
@@ -840,7 +862,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "e0effce11c4908f4b91838741716c83d", 1351},
{"resource.000", 0, "d04cfc7f04b6f74d13025378be49ec2b", 4640330},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, GUIO_GK2_DEMO },
// Gabriel Knight 2 - English DOS (GOG version) - ressci.* merged in ressci.000
// using Enrico Rolfi's HD/DVD installer: http://gkpatches.vogons.zetafleet.com/
@@ -848,7 +870,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "b996fa1e57389a1e179a00a0049de1f4", 8110},
{"ressci.000", 0, "a19fc3604c6e5407abcf03d59ee87217", 168522221},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_GK2 },
// Gabriel Knight 2 - English DOS (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "1.1"
@@ -866,7 +888,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.006", 0, "ce9359037277b7d7976da185c2fa0aad", 2977},
{"ressci.006", 0, "8e44e03890205a7be12f45aaba9644b4", 60659424},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_GK2 },
// Gabriel Knight 2 - French DOS (6-CDs Sierra Originals reedition)
// Executable scanning reports "2.100.002", VERSION file reports "1.0"
@@ -884,7 +906,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.006", 0, "11b2e722170b8c93fdaa5428e2c7676f", 3001},
{"ressci.006", 0, "4037d941aec39d2e654e20960429aefc", 60568486},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::FR_FRA, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_GK2 },
// Gabriel Knight 2 - English Macintosh
// NOTE: This only contains disc 1 files (as well as the persistent file:
@@ -896,7 +918,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"Data4", 0, "8b843c62eb53136a855d6e0087e3cb0d", 5889553},
{"Data5", 0, "f9fcf9ab2eb13b2125c33a1cda03a093", 14349984},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK | ADGF_UNSTABLE, GUIO_GK2_MAC },
#endif // ENABLE_SCI32
@@ -1110,13 +1132,27 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+#ifdef ENABLE_SCI32
+#define GUIO_HOYLE5_DEMO GUIO3(GUIO_NOSPEECH, \
+ GUIO_NOASPECT, \
+ GAMEOPTION_ORIGINAL_SAVELOAD)
+
+ // Hoyle 5 (Hoyle Classic Games) - Windows demo
+ {"hoyle5", "Demo", {
+ {"ressci.000", 0, "98a39ae535dd01714ac313f8ba925045", 7260363},
+ {"resmap.000", 0, "10267a1542a73d527e50f0340549088b", 4900},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, GUIO_HOYLE5_DEMO },
+
+#endif // ENABLE_SCI32
+
// ImagiNation Network (INN) Demo
// SCI interpreter version 1.001.097
{"inndemo", "", {
{"resource.000", 0, "535b1b920441ec73f42eaa4ccfd47b89", 514578},
{"resource.map", 0, "333daf27c3e8a6d274a3e0061ed7cd5c", 1545},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Jones in the Fast Lane EGA - English DOS
// SCI interpreter version 1.000.172 (not 100% sure FIXME)
@@ -1709,6 +1745,15 @@ static const struct ADGameDescription SciGameDescriptions[] = {
#ifdef ENABLE_SCI32
+#define GUIO_KQ7_DEMO GUIO5(GUIO_NOSPEECH, \
+ GUIO_NOASPECT, \
+ GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
+#define GUIO_KQ7 GUIO4(GUIO_NOASPECT, \
+ GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
// King's Quest 7 - English Windows (from the King's Quest Collection)
// Executable scanning reports "2.100.002", VERSION file reports "1.4"
@@ -1716,7 +1761,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "2be9ab94429c721af8e05c507e048a15", 18697},
{"resource.000", 0, "eb63ea3a2c2469dc2d777d351c626404", 203882535},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO_KQ7 },
// King's Quest 7 - English Windows-interpreter-only (supplied by m_kiewitz)
// SCI interpreter version 2.100.002, VERSION file reports "1.51"
@@ -1725,7 +1770,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "eb63ea3a2c2469dc2d777d351c626404", 206626576},
{"resource.aud", 0, "c2a988a16053eb98c7b73a75139902a0", 217716879},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO_KQ7 },
// King's Quest 7 - German Windows-interpreter-only (supplied by markcoolio in bug report #2727402)
// SCI interpreter version 2.100.002, VERSION file reports "1.51"
@@ -1735,7 +1780,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "eb63ea3a2c2469dc2d777d351c626404", 206626576},
{"resource.aud", 0, "3f17bcaf8a9ff6a6c2d4de1a2078fdcc", 258119621},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO_KQ7 },
// King's Quest 7 - English Windows (from abevi)
// VERSION 1.65c
@@ -1743,7 +1788,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "4948e4e1506f1e1c4e1d47abfa06b7f8", 204385195},
{"resource.map", 0, "40ccafb2195301504eba2e4f4f2c7f3d", 18925},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO_KQ7 },
// King's Quest 7 - English DOS (from FRG)
// SCI interpreter version 2.100.002, VERSION file reports "2.00b"
@@ -1751,7 +1796,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "8676b0fbbd7362989a029fe72fea14c6", 18709},
{"resource.000", 0, "51c1ead1163e19a2de8f121c39df7a76", 200764100},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE | ADGF_CD, GUIO_KQ7 },
// King's Quest 7 - English Windows (from FRG)
// SCI interpreter version 2.100.002, VERSION file reports "2.00b"
@@ -1759,7 +1804,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "8676b0fbbd7362989a029fe72fea14c6", 18709},
{"resource.000", 0, "51c1ead1163e19a2de8f121c39df7a76", 200764100},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO_KQ7 },
// King's Quest 7 - Spanish DOS (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "2.00"
@@ -1767,7 +1812,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "0b62693cbe87e3aaca3e8655a437f27f", 18709},
{"resource.000", 0, "51c1ead1163e19a2de8f121c39df7a76", 200764100},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformDOS, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::ES_ESP, Common::kPlatformDOS, ADGF_UNSTABLE | ADGF_CD, GUIO_KQ7 },
// King's Quest 7 - English DOS Non-Interactive Demo
// SCI interpreter version 2.100.002
@@ -1775,7 +1820,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "b44f774108d63faa1d021101221c5a54", 1690},
{"resource.000", 0, "d9659d2cf0c269c6a9dc776707f5bea0", 2433827},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO | ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO | ADGF_UNSTABLE, GUIO_KQ7_DEMO },
// King's Quest 7 - English Windows Demo (from DrMcCoy)
// SCI interpreter version 2.100.002
@@ -1783,7 +1828,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "38e627a37a975aea40cc72b0518b0709", 18412},
{"resource.000", 0, "bad61d50aaa64298fa57a7c6ccd3bccf", 84020382},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE | ADGF_CD, GUIO_KQ7_DEMO },
// King's Questions mini-game from the King's Quest Collection
// SCI interpreter version 2.000.000
@@ -2473,13 +2518,19 @@ static const struct ADGameDescription SciGameDescriptions[] = {
Common::EN_ANY, Common::kPlatformDOS, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
+
+#define GUIO_LSL6HIRES GUIO4(GUIO_NOASPECT, \
+ GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
+
// Larry 6 - English/German DOS CD - HIRES
// SCI interpreter version 2.100.002
{"lsl6hires", "Hi-res", {
{"resource.map", 0, "0c0804434ea62278dd15032b1947426c", 8872},
{"resource.000", 0, "9a9f4870504444cda863dd14d077a680", 18520872},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_LSL6HIRES },
// Larry 6 - German DOS CD - HIRES (provided by richiefs in bug report #2670691)
// SCI interpreter version 2.100.002
@@ -2487,7 +2538,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "badfdf446ffed569a310d2c63a249421", 8896},
{"resource.000", 0, "bd944d2b06614a5b39f1586906f0ee88", 18534274},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::DE_DEU, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_LSL6HIRES },
// Larry 6 - French DOS CD - HIRES (provided by richiefs in bug report #2670691)
// SCI interpreter version 2.100.002
@@ -2495,7 +2546,18 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "d184e9aa4f2d4b5670ddb3669db82cda", 8896},
{"resource.000", 0, "bd944d2b06614a5b39f1586906f0ee88", 18538987},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::FR_FRA, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_LSL6HIRES },
+
+#define GUIO_LSL7_DEMO GUIO5(GUIO_NOSPEECH, \
+ GUIO_NOASPECT, \
+ GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
+#define GUIO_LSL7 GUIO5(GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
+ GUIO_NOASPECT, \
+ GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
// Larry 7 - English DOS Demo (provided by richiefs in bug report #2670691)
// SCI interpreter version 2.100.002
@@ -2503,7 +2565,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ressci.000", 0, "5cc6159688b2dc03790a67c90ccc67f9", 10195878},
{"resmap.000", 0, "6a2b2811eef82e87cde91cf1de845af8", 2695},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO | ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO | ADGF_UNSTABLE, GUIO_LSL7_DEMO },
#ifdef ENABLE_SCI3_GAMES
// Larry 7 - English DOS CD (from spookypeanut)
@@ -2512,7 +2574,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "eae93e1b1d1ccc58b4691c371281c95d", 8188},
{"ressci.000", 0, "89353723488219e25589165d73ed663e", 66965678},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_LSL7 },
// Larry 7 - German DOS (from Tobis87)
// SCI interpreter version 3.000.000
@@ -2520,7 +2582,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "c11e6bfcfc2f2d05da47e5a7df3e9b1a", 8188},
{"ressci.000", 0, "a8c6817bb94f332ff498a71c8b47f893", 66971724},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::DE_DEU, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_LSL7 },
// Larry 7 - French DOS (provided by richiefs in bug report #2670691)
// SCI interpreter version 3.000.000
@@ -2528,7 +2590,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "4407849fd52fe3efb0c30fba60cd5cd4", 8206},
{"ressci.000", 0, "dc37c3055fffbefb494ff22b145d377b", 66964472},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::FR_FRA, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_LSL7 },
// Larry 7 - Italian DOS CD (from glorifindel)
// SCI interpreter version 3.000.000
@@ -2536,7 +2598,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "9852a97141f789413f29bf956052acdb", 8212},
{"ressci.000", 0, "440b9fed89590abb4e4386ed6f948ee2", 67140181},
AD_LISTEND},
- Common::IT_ITA, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::IT_ITA, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_LSL7 },
// Larry 7 - Spanish DOS (from the Leisure Suit Larry Collection)
// Executable scanning reports "3.000.000", VERSION file reports "1.0s"
@@ -2544,16 +2606,28 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "8f3d603e1acc834a5d598b30cdfc93f3", 8188},
{"ressci.000", 0, "32792f9bc1bf3633a88b382bb3f6e40d", 67071418},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::ES_ESP, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_LSL7 },
#endif
+#define GUIO_LIGHTHOUSE_DEMO GUIO5(GUIO_NOSPEECH, \
+ GUIO_NOASPECT, \
+ GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
+#define GUIO_LIGHTHOUSE GUIO6(GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
+ GUIO_NOSPEECH, \
+ GUIO_NOASPECT, \
+ GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
+
// Lighthouse - English Windows Demo (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "1.00"
{"lighthouse", "Demo", {
{"resource.map", 0, "543124606352bfa5e07696ddf2a669be", 64},
{"resource.000", 0, "5d7714416b612463d750fb9c5690c859", 28952},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO | ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO | ADGF_UNSTABLE, GUIO_LIGHTHOUSE_DEMO },
#ifdef ENABLE_SCI3_GAMES
// Lighthouse - English Windows Demo
@@ -2562,7 +2636,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "3bdee7a16926975a4729f75cf6b80a92", 1525},
{"ressci.000", 0, "3c585827fa4a82f4c04a56a0bc52ccee", 11494351},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO | ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO | ADGF_UNSTABLE, GUIO_LIGHTHOUSE },
// Lighthouse - English DOS (from jvprat)
// Executable scanning reports "3.000.000", VERSION file reports "1.1"
@@ -2572,7 +2646,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.002", 0, "c68db5333f152fea6ca2dfc75cad8b34", 7573},
{"ressci.002", 0, "175468431a979b9f317c294ce3bc1430", 94628315},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_LIGHTHOUSE },
// Lighthouse - Japanese DOS (from m_kiewitz)
// Executable scanning reports "3.000.000", VERSION file reports "1.0C"
@@ -2582,7 +2656,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.002", 0, "723fc742c623d8933e5753a264324cb0", 7657},
{"ressci.002", 0, "175468431a979b9f317c294ce3bc1430", 94627469},
AD_LISTEND},
- Common::JA_JPN, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::JA_JPN, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_LIGHTHOUSE },
// Lighthouse - Spanish DOS (from jvprat)
// Executable scanning reports "3.000.000", VERSION file reports "1.1"
@@ -2592,7 +2666,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.002", 0, "e7dc85884a2417e2eff9de0c63dd65fa", 7630},
{"ressci.002", 0, "3c8d627c555b0e3e4f1d9955bc0f0df4", 94631127},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::ES_ESP, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_LIGHTHOUSE },
#endif // ENABLE_SCI3_GAMES
#endif // ENABLE_SCI32
@@ -2714,13 +2788,19 @@ static const struct ADGameDescription SciGameDescriptions[] = {
Common::JA_JPN, Common::kPlatformFMTowns, ADGF_ADDENGLISH, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
+
+#define GUIO_MOTHERGOOSEHIRES GUIO4(GUIO_NOASPECT, \
+ GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
+
// Mixed-Up Mother Goose Deluxe - English Windows/DOS CD (supplied by markcoolio in bug report #2723810)
// Executable scanning reports "2.100.002"
{"mothergoosehires", "", {
{"resource.map", 0, "5159a1578c4306bfe070a3e4d8c2e1d3", 4741},
{"resource.000", 0, "1926925c95d82f0999590e93b02887c5", 15150768},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_MOTHERGOOSEHIRES },
// Mixed-Up Mother Goose Deluxe - Multilingual Windows CD (English/French/German/Spanish)
// Executable scanning reports "2.100.002"
@@ -2728,7 +2808,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "ef611af561898dcfea87846919ebf3eb", 4969},
{"ressci.000", 0, "227685bc59d90821978d330713e44a7a", 17205800},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_MOTHERGOOSEHIRES },
#endif // ENABLE_SCI32
// Ms. Astro Chicken - English DOS
@@ -2740,6 +2820,20 @@ static const struct ADGameDescription SciGameDescriptions[] = {
Common::EN_ANY, Common::kPlatformDOS, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
+
+#define GUIO_PHANTASMAGORIA_DEMO GUIO6(GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
+ GUIO_NOSPEECH, \
+ GUIO_NOASPECT, \
+ GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
+#define GUIO_PHANTASMAGORIA GUIO_PHANTASMAGORIA_DEMO
+#define GUIO_PHANTASMAGORIA_MAC GUIO5(GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
+ GUIO_NOASPECT, \
+ GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
+
// Phantasmagoria - English DOS/Windows (from csnover)
// Windows executable scanning reports "2.100.002" - "Aug 06 1995"
// DOS executable scanning reports "2.100.002" - "May 24 1995"
@@ -2760,7 +2854,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.007", 0, "aa8175cfc93242af6f5e65bdceaafc0d", 7972},
//{"ressci.007", 0, "3aae6559aa1df273bc542d5ac6330d75", 25859038},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_PHANTASMAGORIA },
// Phantasmagoria - English DOS (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "1.100.000UK"
@@ -2780,7 +2874,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.007", 0, "afbd16ea77869a720afa1c5371de107d", 7972},
//{"ressci.007", 0, "3aae6559aa1df273bc542d5ac6330d75", 25859038},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_PHANTASMAGORIA },
// Phantasmagoria - German DOS/Windows
// Windows executable scanning reports "unknown" - "Sep 19 1995 09:39:48"
@@ -2803,7 +2897,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.007", 0, "06309b8043aecb85bd507b15d16cb544", 7984},
//{"ressci.007", 0, "3aae6559aa1df273bc542d5ac6330d75", 26898681},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::DE_DEU, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_PHANTASMAGORIA },
// Phantasmagoria - French DOS
// Supplied by Kervala in bug #6574
@@ -2822,7 +2916,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ressci.006", 0, "3aae6559aa1df273bc542d5ac6330d75", 85415107},
{"resmap.007", 0, "5633960bc106c39ca91d2d8fce18fd2d", 7984},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::FR_FRA, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_PHANTASMAGORIA },
// Phantasmagoria - English DOS Demo
// Executable scanning reports "2.100.002"
@@ -2830,7 +2924,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.001", 0, "416138651ea828219ca454cae18341a3", 11518},
{"ressci.001", 0, "3aae6559aa1df273bc542d5ac6330d75", 65844612},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO | ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO | ADGF_UNSTABLE, GUIO_PHANTASMAGORIA_DEMO },
// Phantasmagoria - English DOS/Windows (GOG version) - ressci.* merged in ressci.000
// Windows executable scanning reports "2.100.002" - "Sep 19 1995 15:09:43"
@@ -2841,7 +2935,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ressci.000", 0, "cd5967f9b9586e3380645961c0765be3", 116822037},
{"resmap.000", 0, "3cafc1c6a53945c1f3babbfd6380c64c", 16468},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_PHANTASMAGORIA },
// Phantasmagoria - English Macintosh
// NOTE: This only contains disc 1 files (as well as the two persistent files:
@@ -2857,9 +2951,16 @@ static const struct ADGameDescription SciGameDescriptions[] = {
// Data8-12 are empty
{"Data13", 0, "6d2c450fca19a69b5af74ed5b03c0a17", 14923328},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK | ADGF_UNSTABLE, GUIO_PHANTASMAGORIA_MAC },
#ifdef ENABLE_SCI3_GAMES
+
+#define GUIO_PHANTASMAGORIA2 GUIO5(GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
+ GUIO_NOSPEECH, \
+ GUIO_NOASPECT, \
+ GUIO_NOMIDI, \
+ GAMEOPTION_ORIGINAL_SAVELOAD)
+
// Some versions of Phantasmagoria 2 were heavily censored.
// Censored versions (data files are currently unknown to us): UK, Australia, first English release in Germany
@@ -2879,7 +2980,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.005", 0, "8bd5ceeedcbe16dfe55d1b90dcd4be84", 1942},
{"ressci.005", 0, "05f9fe2bee749659acb3cd2c90252fc5", 67905112},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO_PHANTASMAGORIA2 },
// Phantasmagoria 2 - English DOS (GOG version) (supplied by littleboy in patch #1360)
// Note: Fully uncensored, basically the US release, but ressci.* merged into ressci.000
@@ -2890,7 +2991,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ressci.000", 0, "c54f26d9f43f908151263254b6d97053", 108134481},
{"resmap.000", 0, "de154a223a9ef4ea7358b76adc38ef5b", 2956},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO_PHANTASMAGORIA2 },
// Phantasmagoria 2 - German DOS/Windows (supplied by AReim1982)
// Note: Fully uncensored, but one scene is missing probably because of a mastering error (Curtis + Therese meeting near water cooler)
@@ -2910,7 +3011,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.005", 0, "2fc48a4a5a73b726994f189da51a8b2a", 1954},
{"ressci.005", 0, "e94005890d22dd3b7f605a2a7c025803", 68232146},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO_PHANTASMAGORIA2 },
#endif // ENABLE_SCI3_GAMES
#endif // ENABLE_SCI32
@@ -3142,13 +3243,23 @@ static const struct ADGameDescription SciGameDescriptions[] = {
Common::EN_ANY, Common::kPlatformDOS, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
+
+#define GUIO_PQ4_FLOPPY GUIO4(GUIO_NOSPEECH, \
+ GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
+#define GUIO_PQ4_CD GUIO4(GAMEOPTION_HIGH_RESOLUTION_GRAPHICS, \
+ GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
+
// Police Quest 4 - English DOS CD (from the Police Quest Collection)
// Executable scanning reports "2.100.002", VERSION file reports "1.100.000"
{"pq4", "CD", {
{"resource.map", 0, "379dfe80ed6bd16c47e4b950c4722eac", 11374},
{"resource.000", 0, "fd316a09b628b7032248139003369022", 18841068},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_CD | ADGF_UNSTABLE, GUIO4(GAMEOPTION_HIGH_RESOLUTION_GRAPHICS, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_CD | ADGF_UNSTABLE, GUIO_PQ4_CD },
// Police Quest 4 - German DOS CD (German text, English speech)
// Supplied by markcoolio in bug report #3392955
@@ -3156,7 +3267,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a398076371ed0e1e706c8f9fb9fc7ac5", 11386},
{"resource.000", 0, "6ff21954e0a2c5992279e7eb787c8d56", 18918747},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformDOS, ADGF_CD | ADGF_UNSTABLE, GUIO4(GAMEOPTION_HIGH_RESOLUTION_GRAPHICS, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::DE_DEU, Common::kPlatformDOS, ADGF_CD | ADGF_UNSTABLE, GUIO_PQ4_CD },
// Police Quest 4 - English DOS
// SCI interpreter version 2.000.000 (a guess?)
@@ -3164,7 +3275,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "aed9643158ccf01b71f359db33137f82", 9895},
{"resource.000", 0, "da383857b3be1e4514daeba2524359e0", 15141432},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_PQ4_FLOPPY },
// Police Quest 4 - French DOS (supplied by abevi in bug report #2612718)
// SCI interpreter version 2.000.000
@@ -3172,7 +3283,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "008030846edcc7c5c7a812c7f4ae4ceb", 9256},
{"resource.000", 0, "6ba98bd2e436739d87ecd2a9b99cabb4", 14730153},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::FR_FRA, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_PQ4_FLOPPY },
// Police Quest 4 - German DOS (supplied by markcoolio in bug report #2723840)
// SCI interpreter version 2.000.000 (a guess?)
@@ -3180,7 +3291,18 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "2393ee728ab930b2762cb5889f9b5aff", 9256},
{"resource.000", 0, "6ba98bd2e436739d87ecd2a9b99cabb4", 14730155},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::DE_DEU, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_PQ4_FLOPPY },
+
+#define GUIO_PQSWAT_DEMO GUIO5(GUIO_NOSPEECH, \
+ GUIO_NOASPECT, \
+ GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
+#define GUIO_PQSWAT GUIO5(GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
+ GUIO_NOASPECT, \
+ GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
// Police Quest: SWAT - English DOS/Windows Demo (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "0.001.200"
@@ -3188,7 +3310,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "8c96733ef94c21526792f7ca4e3f2120", 1648},
{"resource.000", 0, "d8892f1b8c56c8f7704325460f49b300", 3676175},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO | ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO | ADGF_UNSTABLE, GUIO_PQSWAT_DEMO },
// Police Quest: SWAT - English DOS (from GOG.com)
// Executable scanning reports "2.100.002", VERSION file reports "1.0c"
@@ -3196,7 +3318,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "1c2563fee189885e29d9348f37306d94", 12175},
{"ressci.000", 0, "b2e1826ca81ce2e7e764587f5a14eee9", 127149181},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_PQSWAT },
// Police Quest: SWAT - English Windows (from the Police Quest Collection)
// Executable scanning reports "2.100.002", VERSION file reports "1.0c"
@@ -3211,7 +3333,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.004", 0, "4228038906f041623e65789500b22285", 6835},
{"ressci.004", 0, "b7e619e6ecf62fe65d5116a3a422e5f0", 46223872},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO_PQSWAT },
#endif // ENABLE_SCI32
// Quest for Glory 1 / Hero's Quest - English DOS 3.5" Floppy (supplied by merkur in bug report #2718784)
@@ -3569,13 +3691,22 @@ static const struct ADGameDescription SciGameDescriptions[] = {
Common::EN_ANY, Common::kPlatformDOS, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
+
+#define GUIO_QFG4_FLOPPY GUIO4(GUIO_NOSPEECH, \
+ GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
+#define GUIO_QFG4_CD GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
+
// Quest for Glory 4 1.1 Floppy - English DOS (supplied by markcool in bug report #2723852)
// SCI interpreter version 2.000.000 (a guess?)
{"qfg4", "", {
{"resource.map", 0, "685bdb1ed47bbbb0e5e25db392da83ce", 9301},
{"resource.000", 0, "f64fd6aa3977939a86ff30783dd677e1", 11004993},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_QFG4_FLOPPY },
// Quest for Glory 4 1.1 Floppy - English DOS (supplied by abevi in bug report #2612718)
// SCI interpreter version 2.000.000
@@ -3583,7 +3714,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "d10a4cc177d2091d744e2ad8c049b0ae", 9295},
{"resource.000", 0, "f64fd6aa3977939a86ff30783dd677e1", 11003589},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_QFG4_FLOPPY },
// Quest for Glory 4 1.1 Floppy - German DOS (supplied by markcool in bug report #2723850)
// Executable scanning reports "2.000.000", VERSION file reports "1.1"
@@ -3591,7 +3722,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "9e0abba8746f40565bc7eb5720522ecd", 9301},
{"resource.000", 0, "57f22cdc54eeb35fce1f26b31b5c3ee1", 11076197},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::DE_DEU, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO_QFG4_FLOPPY },
// Quest for Glory 4 CD - English DOS/Windows (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "1.0"
@@ -3599,7 +3730,14 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "aba367f2102e81782d961b14fbe3d630", 10246},
{"resource.000", 0, "263dce4aa34c49d3ad29bec889007b1c", 11571394},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_CD | ADGF_UNSTABLE, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_CD | ADGF_UNSTABLE, GUIO_QFG4_CD },
+
+#define GUIO_RAMA_DEMO GUIO5(GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
+ GUIO_NOASPECT, \
+ GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
+#define GUIO_RAMA GUIO_RAMA_DEMO
// RAMA - English DOS/Windows Demo
// Executable scanning reports "2.100.002", VERSION file reports "000.000.008"
@@ -3607,7 +3745,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.001", 0, "775304e9b2a545156be4d94209550094", 1393},
{"ressci.001", 0, "259437fd75fdf51e8207fda8c01fa4fd", 2334384},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, GUIO_RAMA_DEMO },
#ifdef ENABLE_SCI3_GAMES
// RAMA - English Windows (from jvprat)
@@ -3620,7 +3758,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.003", 0, "31ef4c0621711585d031f0ae81707251", 1636},
{"ressci.003", 0, "2a68edd064e5e4937b5e9c74b38f2082", 6860492},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO_RAMA },
// RAMA - English Windows (from Quietust, in bug report #2850645)
{"rama", "", {
@@ -3631,7 +3769,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.003", 0, "48841e4b84ef1b98b48d43566fda9e13", 1636},
{"ressci.003", 0, "2a68edd064e5e4937b5e9c74b38f2082", 6870356},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO_RAMA },
// RAMA - German Windows CD (from farmboy0, in pull request 397)
{"rama", "", {
@@ -3642,7 +3780,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.003", 0, "222096000bd83a1d56577114a452cccf", 1636},
{"ressci.003", 0, "2a68edd064e5e4937b5e9c74b38f2082", 6954219},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO_RAMA },
// RAMA - Italian Windows CD (from glorifindel)
// SCI interpreter version 3.000.000 (a guess?)
@@ -3650,23 +3788,29 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ressci.001", 0, "2a68edd064e5e4937b5e9c74b38f2082", 70611091},
{"resmap.001", 0, "70ba2ff04a2b7fb2c52420ba7fbd47c2", 8338},
AD_LISTEND},
- Common::IT_ITA, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::IT_ITA, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO_RAMA },
#endif // ENABLE_SCI3_GAMES
+#define GUIO_SHIVERS_DEMO GUIO4(GUIO_NOASPECT, \
+ GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
+#define GUIO_SHIVERS GUIO_SHIVERS_DEMO
+
// Shivers - English Windows (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "1.02"
{"shivers", "", {
{"resmap.000", 0, "f2ead37749ed8f6535a2445a7d05a0cc", 46525},
{"ressci.000", 0, "4294c6d7510935f2e0a52e302073c951", 262654836},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO_SHIVERS },
// Shivers - German Windows (from Tobis87)
{"shivers", "", {
{"resmap.000", 0, "f483d0a1f78334c18052e92785c3086e", 46537},
{"ressci.000", 0, "6751b144671e2deed919eb9d284b07eb", 262390692},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO_SHIVERS },
// Shivers - English Windows Demo
// Executable scanning reports "2.100.002"
@@ -3674,7 +3818,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "d9e0bc5eddefcbe47f528760085d8927", 1186},
{"ressci.000", 0, "3a93c6340b54e07e65d0e5583354d186", 10505469},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, GUIO_SHIVERS },
// Shivers 2 doesn't contain SCI scripts. The whole game logic has
// been reimplemented from SCI in native code placed in DLL files.
@@ -3692,7 +3836,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "d8659188b84beaef076bd869837cd530", 634},
{"ressci.000", 0, "7fbac0807a044c9543e8ac376d200e59", 4925003},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, GUIO5(GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Shivers 2 - English Windows (from abevi)
// VERSION.TXT Version 1.0 (3/25/97)
@@ -3700,7 +3844,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ressci.001", 0, "a79d03d6eb75be0a79324f14e3d2ace4", 95346793},
{"resmap.001", 0, "a4804d436d90c4ec2e46b537f5e954db", 6268},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO6(GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#endif
@@ -4232,13 +4376,25 @@ static const struct ADGameDescription SciGameDescriptions[] = {
Common::RU_RUS, Common::kPlatformDOS, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
+
+#define GUIO_SQ6_DEMO GUIO5(GUIO_NOSPEECH, \
+ GUIO_NOASPECT, \
+ GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
+#define GUIO_SQ6 GUIO5(GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
+ GUIO_NOASPECT, \
+ GAMEOPTION_PREFER_DIGITAL_SFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_FB01_MIDI)
+
// Space Quest 6 - English DOS/Win3.11 CD (from the Space Quest Collection)
// Executable scanning reports "2.100.002", VERSION file reports "1.0"
{"sq6", "", {
{"resource.map", 0, "6dddfa3a8f3a3a513ec9dfdfae955005", 10528},
{"resource.000", 0, "c4259ab7355aead07773397b1052827d", 41150806},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_CD | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_CD | ADGF_UNSTABLE, GUIO_SQ6 },
// Space Quest 6 - English DOS/Win3.11 CD ver 1.11 (from FRG)
// SCI interpreter version 2.100.002 (just a guess)
@@ -4246,7 +4402,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "e0615d6e4e10e37ae42e6a2a95aaf145", 10528},
{"resource.000", 0, "c4259ab7355aead07773397b1052827d", 41150806},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_CD | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_CD | ADGF_UNSTABLE, GUIO_SQ6 },
// Space Quest 6 - French DOS/Win3.11 CD (from French magazine Joystick - September 1997)
// Executable scanning reports "2.100.002", VERSION file reports "1.0"
@@ -4254,7 +4410,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "3c831625931d5079b73ae8c275f52c95", 10534},
{"resource.000", 0, "4195ca940f759424f62b90e262cc1737", 40932397},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformDOS, ADGF_CD | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::FR_FRA, Common::kPlatformDOS, ADGF_CD | ADGF_UNSTABLE, GUIO_SQ6 },
// Space Quest 6 - German DOS (from Tobis87, updated info from markcoolio in bug report #2723884)
// SCI interpreter version 2.100.002 (just a guess)
@@ -4262,7 +4418,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "664d797415484f85c90b1b45aedc7686", 10534},
{"resource.000", 0, "ba87ba91e5bdabb4169dd0df75777722", 40933685},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformDOS, ADGF_CD | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::DE_DEU, Common::kPlatformDOS, ADGF_CD | ADGF_UNSTABLE, GUIO_SQ6 },
// Space Quest 6 - English DOS/Win3.11 Interactive Demo (from FRG)
// SCI interpreter version 2.100.002 (just a guess)
@@ -4270,7 +4426,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "368f07b07433db3f819fa3fa0e5efee5", 2572},
{"resource.000", 0, "ab12724e078dea34b624e0d2a38dcd7c", 2272050},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO | ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO | ADGF_UNSTABLE, GUIO_SQ6_DEMO },
#endif // ENABLE_SCI32
// The Island of Dr. Brain - English DOS CD (from jvprat)
@@ -4298,13 +4454,23 @@ static const struct ADGameDescription SciGameDescriptions[] = {
Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
+
+#define GUIO_TORIN_DEMO GUIO3(GUIO_NOASPECT, \
+ GUIO_NOMIDI, \
+ GAMEOPTION_ORIGINAL_SAVELOAD)
+#define GUIO_TORIN GUIO4(GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
+ GUIO_NOASPECT, \
+ GUIO_NOMIDI, \
+ GAMEOPTION_ORIGINAL_SAVELOAD)
+#define GUIO_TORIN_MAC GUIO_TORIN
+
// Torin's Passage - English Windows Interactive Demo
// SCI interpreter version 2.100.002
{"torin", "Demo", {
{"resmap.000", 0, "9a3e172cde9963d0a969f26469318cec", 3403},
{"ressci.000", 0, "db3e290481c35c3224e9602e71e4a1f1", 5073868},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE | ADGF_CD, GUIO_TORIN_DEMO },
// Torin's Passage (Multilingual) - English Windows CD
// SCI interpreter version 2.100.002
@@ -4312,7 +4478,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "bb3b0b22ff08df54fbe2d06263409be6", 9799},
{"ressci.000", 0, "693a259d346c9360f4a0c11fdaae430a", 55973887},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO_TORIN },
// Torin's Passage (Multilingual) - Spanish Windows CD (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "1.0"
@@ -4321,7 +4487,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ressci.000", 0, "693a259d346c9360f4a0c11fdaae430a", 55973887},
// TODO: depend on one of the patches?
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::ES_ESP, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO_TORIN },
// Torin's Passage (Multilingual) - French Windows CD
// SCI interpreter version 2.100.002
@@ -4329,7 +4495,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "bb3b0b22ff08df54fbe2d06263409be6", 9799},
{"ressci.000", 0, "693a259d346c9360f4a0c11fdaae430a", 55973887},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::FR_FRA, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO_TORIN },
// Torin's Passage - German Windows CD (from m_kiewitz)
// SCI interpreter version 2.100.002
@@ -4338,7 +4504,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "e55c3097329b3c53752301e01c6af2fb", 9787},
{"ressci.000", 0, "118f9bec04bfe17c4f87bbb5ddb43c18", 56127540},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO_TORIN },
// Torin's Passage (Multilingual) - German Windows CD
// SCI interpreter version 2.100.002
@@ -4346,7 +4512,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "bb3b0b22ff08df54fbe2d06263409be6", 9799},
{"ressci.000", 0, "693a259d346c9360f4a0c11fdaae430a", 55973887},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO_TORIN },
// Torin's Passage (Multilingual) - Italian Windows CD (from glorifindel)
// SCI interpreter version 2.100.002
@@ -4354,7 +4520,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "bb3b0b22ff08df54fbe2d06263409be6", 9799},
{"ressci.000", 0, "693a259d346c9360f4a0c11fdaae430a", 55973887},
AD_LISTEND},
- Common::IT_ITA, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::IT_ITA, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO_TORIN },
// Torin's Passage - French Windows (from LePhilousophe)
// SCI interpreter version 2.100.002
@@ -4362,7 +4528,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "66ed46e3e56f487e688d52f05b33d0ba", 9787},
{"ressci.000", 0, "118f9bec04bfe17c4f87bbb5ddb43c18", 56126981},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::FR_FRA, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO_TORIN },
// Torin's Passage - Russian Windows CD (SoftClub official translate)
// SCI interpreter version 2.100.002
@@ -4372,7 +4538,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{ "ressci.000", 0, "e672da099fb1663b87c78abc6c8ba2a4", 130622695 },
{ "resmap.000", 0, "643859f8f2be8e7701611e29b3b65208", 9799 },
AD_LISTEND },
- Common::RU_RUS, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::RU_RUS, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO_TORIN },
// Torin's Passage - English Macintosh
{"torin", "", {
@@ -4384,7 +4550,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"Data6", 0, "b639487c83d1dae0e001e700f3631566", 7594881},
{"Data7", 0, "2afd9b5434102b89610916b904c3f73a", 7627374},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK | ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK | ADGF_UNSTABLE | ADGF_CD, GUIO_TORIN_MAC },
#endif // ENABLE_SCI32
// SCI Fanmade Games
diff --git a/engines/sci/engine/file.cpp b/engines/sci/engine/file.cpp
index 0b1001bfda..156f6f51f7 100644
--- a/engines/sci/engine/file.cpp
+++ b/engines/sci/engine/file.cpp
@@ -22,6 +22,7 @@
#include "common/savefile.h"
#include "common/stream.h"
+#include "common/memstream.h"
#include "sci/sci.h"
#include "sci/engine/file.h"
@@ -32,6 +33,112 @@
namespace Sci {
+#ifdef ENABLE_SCI32
+/**
+ * A MemoryWriteStreamDynamic with additional read functionality.
+ * The read and write functions share a single stream position.
+ */
+class MemoryDynamicRWStream : public Common::MemoryWriteStreamDynamic, public Common::SeekableReadStream {
+protected:
+ bool _eos;
+public:
+ MemoryDynamicRWStream(DisposeAfterUse::Flag disposeMemory = DisposeAfterUse::NO) : MemoryWriteStreamDynamic(disposeMemory), _eos(false) { }
+
+ uint32 read(void *dataPtr, uint32 dataSize);
+
+ bool eos() const { return _eos; }
+ int32 pos() const { return _pos; }
+ int32 size() const { return _size; }
+ void clearErr() { _eos = false; Common::MemoryWriteStreamDynamic::clearErr(); }
+ bool seek(int32 offs, int whence = SEEK_SET) { return Common::MemoryWriteStreamDynamic::seek(offs, whence); }
+
+};
+
+uint32 MemoryDynamicRWStream::read(void *dataPtr, uint32 dataSize)
+{
+ // Read at most as many bytes as are still available...
+ if (dataSize > _size - _pos) {
+ dataSize = _size - _pos;
+ _eos = true;
+ }
+ memcpy(dataPtr, _ptr, dataSize);
+
+ _ptr += dataSize;
+ _pos += dataSize;
+
+ return dataSize;
+}
+
+/**
+ * A MemoryDynamicRWStream intended to re-write a file.
+ * It reads the contents of `inFile` in the constructor, and writes back
+ * the changes to `fileName` in the destructor (and when calling commit() ).
+ */
+class SaveFileRewriteStream : public MemoryDynamicRWStream {
+public:
+ SaveFileRewriteStream(Common::String fileName,
+ Common::SeekableReadStream *inFile,
+ bool truncate, bool compress);
+ virtual ~SaveFileRewriteStream();
+
+ virtual uint32 write(const void *dataPtr, uint32 dataSize) { _changed = true; return MemoryDynamicRWStream::write(dataPtr, dataSize); }
+
+ void commit(); //< Save back to disk
+
+protected:
+ Common::String _fileName;
+ bool _compress;
+ bool _changed;
+};
+
+SaveFileRewriteStream::SaveFileRewriteStream(Common::String fileName,
+ Common::SeekableReadStream *inFile,
+ bool truncate,
+ bool compress)
+: MemoryDynamicRWStream(DisposeAfterUse::YES),
+ _fileName(fileName), _compress(compress)
+{
+ if (!truncate && inFile) {
+ unsigned int s = inFile->size();
+ ensureCapacity(s);
+ inFile->read(_data, s);
+ _changed = false;
+ } else {
+ _changed = true;
+ }
+}
+
+SaveFileRewriteStream::~SaveFileRewriteStream() {
+ commit();
+}
+
+void SaveFileRewriteStream::commit() {
+ // Write contents of buffer back to file
+
+ if (_changed) {
+ Common::WriteStream *outFile = g_sci->getSaveFileManager()->openForSaving(_fileName, _compress);
+ outFile->write(_data, _size);
+ delete outFile;
+ _changed = false;
+ }
+}
+
+#endif
+
+uint findFreeFileHandle(EngineState *s) {
+ // Find a free file handle
+ uint handle = 1; // Ignore _fileHandles[0]
+ while ((handle < s->_fileHandles.size()) && s->_fileHandles[handle].isOpen())
+ handle++;
+
+ if (handle == s->_fileHandles.size()) {
+ // Hit size limit => Allocate more space
+ s->_fileHandles.resize(s->_fileHandles.size() + 1);
+ }
+
+ return handle;
+}
+
/*
* Note on how file I/O is implemented: In ScummVM, one can not create/write
* arbitrary data files, simply because many of our target platforms do not
@@ -91,6 +198,27 @@ reg_t file_open(EngineState *s, const Common::String &filename, int mode, bool u
break;
}
+#ifdef ENABLE_SCI32
+ if (mode != _K_FILE_MODE_OPEN_OR_FAIL && (
+ (g_sci->getGameId() == GID_PHANTASMAGORIA && filename == "phantsg.dir") ||
+ (g_sci->getGameId() == GID_PQSWAT && filename == "swat.dat"))) {
+ debugC(kDebugLevelFile, " -> file_open opening %s for rewriting", wrappedName.c_str());
+
+ inFile = saveFileMan->openForLoading(wrappedName);
+ // If no matching savestate exists: fall back to reading from a regular
+ // file
+ if (!inFile)
+ inFile = SearchMan.createReadStreamForMember(englishName);
+
+ SaveFileRewriteStream *stream;
+ stream = new SaveFileRewriteStream(wrappedName, inFile, mode == _K_FILE_MODE_CREATE, isCompressed);
+
+ delete inFile;
+
+ inFile = stream;
+ outFile = stream;
+ } else
+#endif
if (mode == _K_FILE_MODE_OPEN_OR_FAIL) {
// Try to open file, abort if not possible
inFile = saveFileMan->openForLoading(wrappedName);
@@ -126,15 +254,7 @@ reg_t file_open(EngineState *s, const Common::String &filename, int mode, bool u
return SIGNAL_REG;
}
- // Find a free file handle
- uint handle = 1; // Ignore _fileHandles[0]
- while ((handle < s->_fileHandles.size()) && s->_fileHandles[handle].isOpen())
- handle++;
-
- if (handle == s->_fileHandles.size()) {
- // Hit size limit => Allocate more space
- s->_fileHandles.resize(s->_fileHandles.size() + 1);
- }
+ uint handle = findFreeFileHandle(s);
s->_fileHandles[handle]._in = inFile;
s->_fileHandles[handle]._out = outFile;
@@ -252,8 +372,12 @@ FileHandle::~FileHandle() {
}
void FileHandle::close() {
- delete _in;
- delete _out;
+ // NB: It is possible _in and _out are both non-null, but
+ // then they point to the same object.
+ if (_in)
+ delete _in;
+ else
+ delete _out;
_in = 0;
_out = 0;
_name.clear();
@@ -365,119 +489,4 @@ reg_t DirSeeker::nextFile(SegManager *segMan) {
return _outbuffer;
}
-
-#ifdef ENABLE_SCI32
-
-VirtualIndexFile::VirtualIndexFile(Common::String fileName) : _fileName(fileName), _changed(false) {
- Common::SeekableReadStream *inFile = g_sci->getSaveFileManager()->openForLoading(fileName);
-
- _bufferSize = inFile->size();
- _buffer = new char[_bufferSize];
- inFile->read(_buffer, _bufferSize);
- _ptr = _buffer;
- delete inFile;
-}
-
-VirtualIndexFile::VirtualIndexFile(uint32 initialSize) : _changed(false) {
- _bufferSize = initialSize;
- _buffer = new char[_bufferSize];
- _ptr = _buffer;
-}
-
-VirtualIndexFile::~VirtualIndexFile() {
- close();
-
- _bufferSize = 0;
- delete[] _buffer;
- _buffer = 0;
-}
-
-uint32 VirtualIndexFile::read(char *buffer, uint32 size) {
- uint32 curPos = _ptr - _buffer;
- uint32 finalSize = MIN<uint32>(size, _bufferSize - curPos);
- char *localPtr = buffer;
-
- for (uint32 i = 0; i < finalSize; i++)
- *localPtr++ = *_ptr++;
-
- return finalSize;
-}
-
-uint32 VirtualIndexFile::write(const char *buffer, uint32 size) {
- _changed = true;
- uint32 curPos = _ptr - _buffer;
-
- // Check if the buffer needs to be resized
- if (curPos + size >= _bufferSize) {
- _bufferSize = curPos + size + 1;
- char *tmp = _buffer;
- _buffer = new char[_bufferSize];
- _ptr = _buffer + curPos;
- memcpy(_buffer, tmp, _bufferSize);
- delete[] tmp;
- }
-
- for (uint32 i = 0; i < size; i++)
- *_ptr++ = *buffer++;
-
- return size;
-}
-
-uint32 VirtualIndexFile::readLine(char *buffer, uint32 size) {
- uint32 startPos = _ptr - _buffer;
- uint32 bytesRead = 0;
- char *localPtr = buffer;
-
- // This is not a full-blown implementation of readLine, but it
- // suffices for Phantasmagoria
- while (startPos + bytesRead < size) {
- bytesRead++;
-
- if (*_ptr == 0 || *_ptr == 0x0A) {
- _ptr++;
- *localPtr = 0;
- return bytesRead;
- } else {
- *localPtr++ = *_ptr++;
- }
- }
-
- return bytesRead;
-}
-
-bool VirtualIndexFile::seek(int32 offset, int whence) {
- uint32 startPos = _ptr - _buffer;
- assert(offset >= 0);
-
- switch (whence) {
- case SEEK_CUR:
- assert(startPos + offset < _bufferSize);
- _ptr += offset;
- break;
- case SEEK_SET:
- assert(offset < (int32)_bufferSize);
- _ptr = _buffer + offset;
- break;
- case SEEK_END:
- assert((int32)_bufferSize - offset >= 0);
- _ptr = _buffer + (_bufferSize - offset);
- break;
- }
-
- return true;
-}
-
-void VirtualIndexFile::close() {
- if (_changed && !_fileName.empty()) {
- Common::WriteStream *outFile = g_sci->getSaveFileManager()->openForSaving(_fileName);
- outFile->write(_buffer, _bufferSize);
- delete outFile;
- }
-
- // Maintain the buffer, and seek to the beginning of it
- _ptr = _buffer;
-}
-
-#endif
-
} // End of namespace Sci
diff --git a/engines/sci/engine/file.h b/engines/sci/engine/file.h
index 54627d5228..982d7b7823 100644
--- a/engines/sci/engine/file.h
+++ b/engines/sci/engine/file.h
@@ -43,7 +43,6 @@ enum {
#define VIRTUALFILE_HANDLE_START 32000
#define VIRTUALFILE_HANDLE_SCI32SAVE 32100
-#define PHANTASMAGORIA_SAVEGAME_INDEX "phantsg.dir"
#define VIRTUALFILE_HANDLE_SCIAUDIO 32300
#define VIRTUALFILE_HANDLE_END 32300
@@ -93,50 +92,7 @@ private:
void addAsVirtualFiles(Common::String title, Common::String fileMask);
};
-
-#ifdef ENABLE_SCI32
-
-/**
- * An implementation of a virtual file that supports basic read and write
- * operations simultaneously.
- *
- * This class has been initially implemented for Phantasmagoria, which has its
- * own custom save/load code. The load code keeps checking for the existence
- * of the save index file and keeps closing and reopening it for each save
- * slot. This is notoriously slow and clumsy, and introduces noticeable delays,
- * especially for non-desktop systems. Also, its game scripts request to open
- * the index file for reading and writing with the same parameters
- * (SaveManager::setCurrentSave and SaveManager::getCurrentSave). Moreover,
- * the game scripts reopen the index file for writing in order to update it
- * and seek within it. We do not support seeking in writeable streams, and the
- * fact that our saved games are ZIP files makes this operation even more
- * expensive. Finally, the savegame index file is supposed to be expanded when
- * a new save slot is added.
- * For the aforementioned reasons, this class has been implemented, which offers
- * the basic functionality needed by the game scripts in Phantasmagoria.
- */
-class VirtualIndexFile {
-public:
- VirtualIndexFile(Common::String fileName);
- VirtualIndexFile(uint32 initialSize);
- ~VirtualIndexFile();
-
- uint32 read(char *buffer, uint32 size);
- uint32 readLine(char *buffer, uint32 size);
- uint32 write(const char *buffer, uint32 size);
- bool seek(int32 offset, int whence);
- void close();
-
-private:
- char *_buffer;
- uint32 _bufferSize;
- char *_ptr;
-
- Common::String _fileName;
- bool _changed;
-};
-
-#endif
+uint findFreeFileHandle(EngineState *s);
} // End of namespace Sci
diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp
index 2fc338b618..2afb8b73d1 100644
--- a/engines/sci/engine/kernel.cpp
+++ b/engines/sci/engine/kernel.cpp
@@ -885,8 +885,8 @@ void Kernel::loadKernelNames(GameFeatures *features) {
// how kDoSound is called from Sound::play().
// Known games that use this:
// GK2 demo
- // KQ7 1.4
- // PQ4 SWAT demo
+ // KQ7 1.4/1.51
+ // PQ:SWAT demo
// LSL6
// PQ4CD
// QFG4CD
@@ -897,7 +897,7 @@ void Kernel::loadKernelNames(GameFeatures *features) {
_kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesGk2Demo);
// OnMe is IsOnMe here, but they should be compatible
- _kernelNames[0x23] = "Robot"; // Graph in SCI2
+ _kernelNames[0x23] = g_sci->getGameId() == GID_LSL6HIRES ? "Empty" : "Robot"; // Graph in SCI2
_kernelNames[0x2e] = "Priority"; // DisposeTextBitmap in SCI2
} else {
// Normal SCI2.1 kernel table
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index d95e228045..5ff4f932be 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -441,6 +441,15 @@ reg_t kDoAudioFade(EngineState *s, int argc, reg_t *argv);
reg_t kDoAudioHasSignal(EngineState *s, int argc, reg_t *argv);
reg_t kDoAudioSetLoop(EngineState *s, int argc, reg_t *argv);
+reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv);
+reg_t kPlayVMDOpen(EngineState *s, int argc, reg_t *argv);
+reg_t kPlayVMDInit(EngineState *s, int argc, reg_t *argv);
+reg_t kPlayVMDClose(EngineState *s, int argc, reg_t *argv);
+reg_t kPlayVMDPlayUntilEvent(EngineState *s, int argc, reg_t *argv);
+reg_t kPlayVMDShowCursor(EngineState *s, int argc, reg_t *argv);
+reg_t kPlayVMDSetBlackoutArea(EngineState *s, int argc, reg_t *argv);
+reg_t kPlayVMDRestrictPalette(EngineState *s, int argc, reg_t *argv);
+
reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv);
reg_t kArray(EngineState *s, int argc, reg_t *argv);
reg_t kListAt(EngineState *s, int argc, reg_t *argv);
@@ -538,8 +547,17 @@ reg_t kMakeSaveCatName(EngineState *s, int argc, reg_t *argv);
reg_t kMakeSaveFileName(EngineState *s, int argc, reg_t *argv);
reg_t kSetScroll(EngineState *s, int argc, reg_t *argv);
-reg_t kPalCycle(EngineState *s, int argc, reg_t *argv);
+reg_t kPaletteSetFromResource32(EngineState *s, int argc, reg_t *argv);
+reg_t kPaletteFindColor32(EngineState *s, int argc, reg_t *argv);
reg_t kPaletteSetFade(EngineState *s, int argc, reg_t *argv);
+
+reg_t kPalCycle(EngineState *s, int argc, reg_t *argv);
+reg_t kPalCycleSetCycle(EngineState *s, int argc, reg_t *argv);
+reg_t kPalCycleDoCycle(EngineState *s, int argc, reg_t *argv);
+reg_t kPalCyclePause(EngineState *s, int argc, reg_t *argv);
+reg_t kPalCycleOn(EngineState *s, int argc, reg_t *argv);
+reg_t kPalCycleOff(EngineState *s, int argc, reg_t *argv);
+
reg_t kPalVarySetVary(EngineState *s, int argc, reg_t *argv);
reg_t kPalVarySetPercent(EngineState *s, int argc, reg_t *argv);
reg_t kPalVaryGetPercent(EngineState *s, int argc, reg_t *argv);
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index dacaafe757..8a1176eed8 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -291,12 +291,13 @@ static const SciKernelMapSubEntry kPalVary_subops[] = {
// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kPalette_subops[] = {
- { SIG_SCIALL, 1, MAP_CALL(PaletteSetFromResource), "i(i)", NULL },
+ { SIG_SCI16, 1, MAP_CALL(PaletteSetFromResource), "i(i)", NULL },
{ SIG_SCI16, 2, MAP_CALL(PaletteSetFlag), "iii", NULL },
{ SIG_SCI16, 3, MAP_CALL(PaletteUnsetFlag), "iii", kPaletteUnsetFlag_workarounds },
#ifdef ENABLE_SCI32
+ { SIG_SCI32, 1, MAP_CALL(PaletteSetFromResource32), "i(i)", NULL },
{ SIG_SCI32, 2, MAP_CALL(PaletteSetFade), "iii", NULL },
- { SIG_SCI32, 3, MAP_CALL(PaletteFindColor), "iii", NULL },
+ { SIG_SCI32, 3, MAP_CALL(PaletteFindColor32), "iii", NULL },
#endif
{ SIG_SCI16, 4, MAP_CALL(PaletteSetIntensity), "iii(i)", NULL },
{ SIG_SCI16, 5, MAP_CALL(PaletteFindColor), "iii", NULL },
@@ -335,6 +336,16 @@ static const SciKernelMapSubEntry kFileIO_subops[] = {
#ifdef ENABLE_SCI32
// version, subId, function-mapping, signature, workarounds
+static const SciKernelMapSubEntry kPalCycle_subops[] = {
+ { SIG_SCI32, 0, MAP_CALL(PalCycleSetCycle), "iii(i)", NULL },
+ { SIG_SCI32, 1, MAP_CALL(PalCycleDoCycle), "i(i)", NULL },
+ { SIG_SCI32, 2, MAP_CALL(PalCyclePause), "(i)", NULL },
+ { SIG_SCI32, 3, MAP_CALL(PalCycleOn), "(i)", NULL },
+ { SIG_SCI32, 4, MAP_CALL(PalCycleOff), "(i)", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
+};
+
+// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kSave_subops[] = {
{ SIG_SCI32, 0, MAP_CALL(SaveGame), "[r0]i[r0](r0)", NULL },
{ SIG_SCI32, 1, MAP_CALL(RestoreGame), "[r0]i[r0]", NULL },
@@ -412,6 +423,22 @@ static const SciKernelMapSubEntry kList_subops[] = {
SCI_SUBOPENTRY_TERMINATOR
};
+// There are a lot of subops to PlayVMD, but only a few of them are ever
+// actually used by games
+// version, subId, function-mapping, signature, workarounds
+static const SciKernelMapSubEntry kPlayVMD_subops[] = {
+ { SIG_SINCE_SCI21, 0, MAP_CALL(PlayVMDOpen), "r(i)(i)", NULL },
+ { SIG_SINCE_SCI21, 1, MAP_CALL(PlayVMDInit), "ii(i)(i)(ii)", NULL },
+ { SIG_SINCE_SCI21, 6, MAP_CALL(PlayVMDClose), "", NULL },
+ { SIG_SINCE_SCI21, 14, MAP_CALL(PlayVMDPlayUntilEvent), "i(i)(i)", NULL },
+ { SIG_SINCE_SCI21, 16, MAP_CALL(PlayVMDShowCursor), "i", NULL },
+ { SIG_SINCE_SCI21, 17, MAP_DUMMY(PlayVMDStartBlob), "", NULL },
+ { SIG_SINCE_SCI21, 18, MAP_DUMMY(PlayVMDStopBlobs), "", NULL },
+ { SIG_SINCE_SCI21, 21, MAP_CALL(PlayVMDSetBlackoutArea), "iiii", NULL },
+ { SIG_SINCE_SCI21, 23, MAP_CALL(PlayVMDRestrictPalette), "ii", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
+};
+
// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kRemapColors_subops[] = {
{ SIG_SCI32, 0, MAP_CALL(RemapColorsOff), "(i)", NULL },
@@ -746,7 +773,7 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(MakeSaveCatName), SIG_EVERYWHERE, "rr", NULL, NULL },
{ MAP_CALL(MakeSaveFileName), SIG_EVERYWHERE, "rri", NULL, NULL },
{ MAP_CALL(SetScroll), SIG_EVERYWHERE, "oiiiii(i)", NULL, NULL },
- { MAP_CALL(PalCycle), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
+ { MAP_CALL(PalCycle), SIG_EVERYWHERE, "(.*)", kPalCycle_subops, NULL },
// SCI2 Empty functions
@@ -792,7 +819,7 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(IsOnMe), SIG_EVERYWHERE, "iioi", NULL, NULL },
{ MAP_CALL(List), SIG_SINCE_SCI21, SIGFOR_ALL, "(.*)", kList_subops, NULL },
{ MAP_CALL(MulDiv), SIG_EVERYWHERE, "iii", NULL, NULL },
- { MAP_CALL(PlayVMD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(PlayVMD), SIG_EVERYWHERE, "(.*)", kPlayVMD_subops, NULL },
{ MAP_CALL(Robot), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_CALL(Save), SIG_EVERYWHERE, "i(.*)", kSave_subops, NULL },
{ MAP_CALL(Text), SIG_SINCE_SCI21MID, SIGFOR_ALL, "i(.*)", kText_subops, NULL },
diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp
index d604bb85d0..4508a481a0 100644
--- a/engines/sci/engine/kfile.cpp
+++ b/engines/sci/engine/kfile.cpp
@@ -29,6 +29,7 @@
#include "common/savefile.h"
#include "common/system.h"
#include "common/translation.h"
+#include "common/memstream.h"
#include "gui/saveload.h"
@@ -263,19 +264,6 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
}
#ifdef ENABLE_SCI32
- if (name == PHANTASMAGORIA_SAVEGAME_INDEX) {
- if (s->_virtualIndexFile) {
- return make_reg(0, VIRTUALFILE_HANDLE_SCI32SAVE);
- } else {
- Common::String englishName = g_sci->getSciLanguageString(name, K_LANG_ENGLISH);
- Common::String wrappedName = g_sci->wrapFilename(englishName);
- if (!g_sci->getSaveFileManager()->listSavefiles(wrappedName).empty()) {
- s->_virtualIndexFile = new VirtualIndexFile(wrappedName);
- return make_reg(0, VIRTUALFILE_HANDLE_SCI32SAVE);
- }
- }
- }
-
// Shivers is trying to store savegame descriptions and current spots in
// separate .SG files, which are hardcoded in the scripts.
// Essentially, there is a normal save file, created by the executable
@@ -313,18 +301,18 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
listSavegames(saves);
int savegameNr = findSavegame(saves, slotNumber - SAVEGAMEID_OFFICIALRANGE_START);
- if (!s->_virtualIndexFile) {
- // Make the virtual file buffer big enough to avoid having it grow dynamically.
- // 50 bytes should be more than enough.
- s->_virtualIndexFile = new VirtualIndexFile(50);
- }
+ int size = strlen(saves[savegameNr].name) + 2;
+ char *buf = (char *)malloc(size);
+ strcpy(buf, saves[savegameNr].name);
+ buf[size - 1] = 0; // Spot description (empty)
- s->_virtualIndexFile->seek(0, SEEK_SET);
- s->_virtualIndexFile->write(saves[savegameNr].name, strlen(saves[savegameNr].name));
- s->_virtualIndexFile->write("\0", 1);
- s->_virtualIndexFile->write("\0", 1); // Spot description (empty)
- s->_virtualIndexFile->seek(0, SEEK_SET);
- return make_reg(0, VIRTUALFILE_HANDLE_SCI32SAVE);
+ uint handle = findFreeFileHandle(s);
+
+ s->_fileHandles[handle]._in = new Common::MemoryReadStream((byte *)buf, size, DisposeAfterUse::YES);
+ s->_fileHandles[handle]._out = nullptr;
+ s->_fileHandles[handle]._name = "";
+
+ return make_reg(0, handle);
}
}
#endif
@@ -349,13 +337,6 @@ reg_t kFileIOClose(EngineState *s, int argc, reg_t *argv) {
uint16 handle = argv[0].toUint16();
-#ifdef ENABLE_SCI32
- if (handle == VIRTUALFILE_HANDLE_SCI32SAVE) {
- s->_virtualIndexFile->close();
- return SIGNAL_REG;
- }
-#endif
-
if (handle >= VIRTUALFILE_HANDLE_START) {
// it's a virtual handle? ignore it
return SIGNAL_REG;
@@ -381,17 +362,9 @@ reg_t kFileIOReadRaw(EngineState *s, int argc, reg_t *argv) {
char *buf = new char[size];
debugC(kDebugLevelFile, "kFileIO(readRaw): %d, %d", handle, size);
-#ifdef ENABLE_SCI32
- if (handle == VIRTUALFILE_HANDLE_SCI32SAVE) {
- bytesRead = s->_virtualIndexFile->read(buf, size);
- } else {
-#endif
- FileHandle *f = getFileFromHandle(s, handle);
- if (f)
- bytesRead = f->_in->read(buf, size);
-#ifdef ENABLE_SCI32
- }
-#endif
+ FileHandle *f = getFileFromHandle(s, handle);
+ if (f)
+ bytesRead = f->_in->read(buf, size);
// TODO: What happens if less bytes are read than what has
// been requested? (i.e. if bytesRead is non-zero, but still
@@ -411,20 +384,11 @@ reg_t kFileIOWriteRaw(EngineState *s, int argc, reg_t *argv) {
s->_segMan->memcpy((byte *)buf, argv[1], size);
debugC(kDebugLevelFile, "kFileIO(writeRaw): %d, %d", handle, size);
-#ifdef ENABLE_SCI32
- if (handle == VIRTUALFILE_HANDLE_SCI32SAVE) {
- s->_virtualIndexFile->write(buf, size);
+ FileHandle *f = getFileFromHandle(s, handle);
+ if (f) {
+ f->_out->write(buf, size);
success = true;
- } else {
-#endif
- FileHandle *f = getFileFromHandle(s, handle);
- if (f) {
- f->_out->write(buf, size);
- success = true;
- }
-#ifdef ENABLE_SCI32
}
-#endif
delete[] buf;
if (success)
@@ -463,13 +427,6 @@ reg_t kFileIOUnlink(EngineState *s, int argc, reg_t *argv) {
const Common::String wrappedName = g_sci->wrapFilename(name);
result = saveFileMan->removeSavefile(wrappedName);
}
-
-#ifdef ENABLE_SCI32
- if (name == PHANTASMAGORIA_SAVEGAME_INDEX) {
- delete s->_virtualIndexFile;
- s->_virtualIndexFile = 0;
- }
-#endif
} else {
const Common::String wrappedName = g_sci->wrapFilename(name);
result = saveFileMan->removeSavefile(wrappedName);
@@ -488,12 +445,7 @@ reg_t kFileIOReadString(EngineState *s, int argc, reg_t *argv) {
debugC(kDebugLevelFile, "kFileIO(readString): %d, %d", handle, maxsize);
uint32 bytesRead;
-#ifdef ENABLE_SCI32
- if (handle == VIRTUALFILE_HANDLE_SCI32SAVE)
- bytesRead = s->_virtualIndexFile->readLine(buf, maxsize);
- else
-#endif
- bytesRead = fgets_wrapper(s, buf, maxsize, handle);
+ bytesRead = fgets_wrapper(s, buf, maxsize, handle);
s->_segMan->memcpy(argv[0], (const byte*)buf, maxsize);
delete[] buf;
@@ -520,13 +472,6 @@ reg_t kFileIOWriteString(EngineState *s, int argc, reg_t *argv) {
return NULL_REG;
}
-#ifdef ENABLE_SCI32
- if (handle == VIRTUALFILE_HANDLE_SCI32SAVE) {
- s->_virtualIndexFile->write(str.c_str(), str.size());
- return NULL_REG;
- }
-#endif
-
FileHandle *f = getFileFromHandle(s, handle);
if (f) {
@@ -547,11 +492,6 @@ reg_t kFileIOSeek(EngineState *s, int argc, reg_t *argv) {
uint16 whence = argv[2].toUint16();
debugC(kDebugLevelFile, "kFileIO(seek): %d, %d, %d", handle, offset, whence);
-#ifdef ENABLE_SCI32
- if (handle == VIRTUALFILE_HANDLE_SCI32SAVE)
- return make_reg(0, s->_virtualIndexFile->seek(offset, whence));
-#endif
-
FileHandle *f = getFileFromHandle(s, handle);
if (f && f->_in) {
@@ -591,14 +531,6 @@ reg_t kFileIOFindNext(EngineState *s, int argc, reg_t *argv) {
reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv) {
Common::String name = s->_segMan->getString(argv[0]);
-#ifdef ENABLE_SCI32
- // Cache the file existence result for the Phantasmagoria
- // save index file, as the game scripts keep checking for
- // its existence.
- if (name == PHANTASMAGORIA_SAVEGAME_INDEX && s->_virtualIndexFile)
- return TRUE_REG;
-#endif
-
bool exists = false;
if (g_sci->getGameId() == GID_PEPPER) {
@@ -611,6 +543,9 @@ reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv) {
return NULL_REG;
}
+ // TODO: It may apparently be worth caching the existence of
+ // phantsg.dir, and possibly even keeping it open persistently
+
// Check for regular file
exists = Common::File::exists(name);
diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp
index 019a06930c..9cfe53255b 100644
--- a/engines/sci/engine/kgraphics32.cpp
+++ b/engines/sci/engine/kgraphics32.cpp
@@ -790,6 +790,19 @@ reg_t kMorphOn(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
+reg_t kPaletteSetFromResource32(EngineState *s, int argc, reg_t *argv) {
+ const GuiResourceId paletteId = argv[0].toUint16();
+ g_sci->_gfxPalette32->loadPalette(paletteId);
+ return s->r_acc;
+}
+
+reg_t kPaletteFindColor32(EngineState *s, int argc, reg_t *argv) {
+ const uint8 r = argv[0].toUint16();
+ const uint8 g = argv[1].toUint16();
+ const uint8 b = argv[2].toUint16();
+ return make_reg(0, g_sci->_gfxPalette32->matchColor(r, g, b));
+}
+
reg_t kPaletteSetFade(EngineState *s, int argc, reg_t *argv) {
uint16 fromColor = argv[0].toUint16();
uint16 toColor = argv[1].toUint16();
@@ -862,67 +875,57 @@ reg_t kPalVaryMergeStart(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, g_sci->_gfxPalette32->getVaryPercent());
}
-enum {
- kSetCycle = 0,
- kDoCycle = 1,
- kCyclePause = 2,
- kCycleOn = 3,
- kCycleOff = 4
-};
-
reg_t kPalCycle(EngineState *s, int argc, reg_t *argv) {
- // Examples: GK1 room 480 (Bayou ritual), LSL6 room 100 (title screen)
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
- switch (argv[0].toUint16()) {
- case kSetCycle: {
- uint16 fromColor = argv[1].toUint16();
- uint16 toColor = argv[2].toUint16();
- int16 direction = argv[3].toSint16();
- uint16 delay = (argc == 4 ? 0 : argv[4].toUint16());
-
- g_sci->_gfxPalette32->setCycle(fromColor, toColor, direction, delay);
- }
- break;
- case kDoCycle: {
- uint16 fromColor = argv[1].toUint16();
- int16 speed = (argc == 2) ? 1 : argv[2].toSint16();
- g_sci->_gfxPalette32->doCycle(fromColor, speed);
- }
- break;
- case kCyclePause: {
- if (argc == 1) {
- g_sci->_gfxPalette32->cycleAllPause();
- } else {
- uint16 fromColor = argv[1].toUint16();
- g_sci->_gfxPalette32->cyclePause(fromColor);
- }
- }
- break;
- case kCycleOn: {
- if (argc == 1) {
- g_sci->_gfxPalette32->cycleAllOn();
- } else {
- uint16 fromColor = argv[1].toUint16();
- g_sci->_gfxPalette32->cycleOn(fromColor);
- }
- }
- break;
- case kCycleOff: {
- if (argc == 1) {
- g_sci->_gfxPalette32->cycleAllOff();
- } else {
- uint16 fromColor = argv[1].toUint16();
- g_sci->_gfxPalette32->cycleOff(fromColor);
- }
- break;
- }
- default:
- // In SCI2.1 there are no values above 4, so should never get here;
- // SCI just returns early if this ever happens.
- assert(false);
- break;
+reg_t kPalCycleSetCycle(EngineState *s, int argc, reg_t *argv) {
+ const uint16 fromColor = argv[0].toUint16();
+ const uint16 toColor = argv[1].toUint16();
+ const int16 direction = argv[2].toSint16();
+ const uint16 delay = argc > 3 ? argv[3].toUint16() : 0;
+
+ g_sci->_gfxPalette32->setCycle(fromColor, toColor, direction, delay);
+ return s->r_acc;
+}
+
+reg_t kPalCycleDoCycle(EngineState *s, int argc, reg_t *argv) {
+ const uint16 fromColor = argv[0].toUint16();
+ const int16 speed = argc > 1 ? argv[1].toSint16() : 1;
+
+ g_sci->_gfxPalette32->doCycle(fromColor, speed);
+ return s->r_acc;
+}
+
+reg_t kPalCyclePause(EngineState *s, int argc, reg_t *argv) {
+ if (argc == 0) {
+ g_sci->_gfxPalette32->cycleAllPause();
+ } else {
+ const uint16 fromColor = argv[0].toUint16();
+ g_sci->_gfxPalette32->cyclePause(fromColor);
}
+ return s->r_acc;
+}
+reg_t kPalCycleOn(EngineState *s, int argc, reg_t *argv) {
+ if (argc == 0) {
+ g_sci->_gfxPalette32->cycleAllOn();
+ } else {
+ const uint16 fromColor = argv[0].toUint16();
+ g_sci->_gfxPalette32->cycleOn(fromColor);
+ }
+ return s->r_acc;
+}
+
+reg_t kPalCycleOff(EngineState *s, int argc, reg_t *argv) {
+ if (argc == 0) {
+ g_sci->_gfxPalette32->cycleAllOff();
+ } else {
+ const uint16 fromColor = argv[0].toUint16();
+ g_sci->_gfxPalette32->cycleOff(fromColor);
+ }
return s->r_acc;
}
diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp
index 065625f85f..0d6831139a 100644
--- a/engines/sci/engine/kmisc.cpp
+++ b/engines/sci/engine/kmisc.cpp
@@ -409,6 +409,9 @@ reg_t kGetConfig(EngineState *s, int argc, reg_t *argv) {
} else if (setting == "startroom") {
// Debug setting in LSL7, specifies the room to start from.
s->_segMan->strcpy(data, "");
+ } else if (setting == "game") {
+ // Hoyle 5 Demo startup.
+ s->_segMan->strcpy(data, "");
} else {
error("GetConfig: Unknown configuration setting %s", setting.c_str());
}
diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp
index 8db0c542eb..1096e78cca 100644
--- a/engines/sci/engine/kvideo.cpp
+++ b/engines/sci/engine/kvideo.cpp
@@ -40,7 +40,8 @@
#include "video/qt_decoder.h"
#include "sci/video/seq_decoder.h"
#ifdef ENABLE_SCI32
-#include "video/coktel_decoder.h"
+#include "sci/graphics/frameout.h"
+#include "sci/graphics/video32.h"
#include "sci/video/robot_decoder.h"
#endif
@@ -289,113 +290,73 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
}
reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv) {
- uint16 operation = argv[0].toUint16();
- Video::VideoDecoder *videoDecoder = 0;
- bool reshowCursor = g_sci->_gfxCursor->isVisible();
- Common::String warningMsg;
-
- switch (operation) {
- case 0: // init
- s->_videoState.reset();
- s->_videoState.fileName = s->_segMan->derefString(argv[1]);
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
- if (argc > 2 && argv[2] != NULL_REG)
- warning("kPlayVMD: third parameter isn't 0 (it's %04x:%04x - %s)", PRINT_REG(argv[2]), s->_segMan->getObjectName(argv[2]));
- break;
- case 1:
- {
- // Set VMD parameters. Called with a maximum of 6 parameters:
- //
- // x, y, flags, gammaBoost, gammaFirst, gammaLast
- //
- // gammaBoost boosts palette colors in the range gammaFirst to
- // gammaLast, but only if bit 4 in flags is set. Percent value such that
- // 0% = no amplification These three parameters are optional if bit 4 is
- // clear. Also note that the x, y parameters play subtle games if used
- // with subfx 21. The subtleness has to do with creation of temporary
- // planes and positioning relative to such planes.
-
- uint16 flags = argv[3].getOffset();
- Common::String flagspec;
-
- if (argc > 3) {
- if (flags & kDoubled)
- flagspec += "doubled ";
- if (flags & kDropFrames)
- flagspec += "dropframes ";
- if (flags & kBlackLines)
- flagspec += "blacklines ";
- if (flags & kUnkBit3)
- flagspec += "bit3 ";
- if (flags & kGammaBoost)
- flagspec += "gammaboost ";
- if (flags & kHoldBlackFrame)
- flagspec += "holdblack ";
- if (flags & kHoldLastFrame)
- flagspec += "holdlast ";
- if (flags & kUnkBit7)
- flagspec += "bit7 ";
- if (flags & kStretch)
- flagspec += "stretch";
-
- warning("VMDFlags: %s", flagspec.c_str());
-
- s->_videoState.flags = flags;
- }
+reg_t kPlayVMDOpen(EngineState *s, int argc, reg_t *argv) {
+ const Common::String fileName = s->_segMan->getString(argv[0]);
+ // argv[1] is an optional cache size argument which we do not use
+ // const uint16 cacheSize = argc > 1 ? CLIP<int16>(argv[1].toSint16(), 16, 1024) : 0;
+ const VMDPlayer::OpenFlags flags = argc > 2 ? (VMDPlayer::OpenFlags)argv[2].toUint16() : VMDPlayer::kOpenFlagNone;
- warning("x, y: %d, %d", argv[1].getOffset(), argv[2].getOffset());
- s->_videoState.x = argv[1].getOffset();
- s->_videoState.y = argv[2].getOffset();
+ return make_reg(0, g_sci->_video32->getVMDPlayer().open(fileName, flags));
+}
- if (argc > 4 && flags & 16)
- warning("gammaBoost: %d%% between palette entries %d and %d", argv[4].getOffset(), argv[5].getOffset(), argv[6].getOffset());
- break;
+reg_t kPlayVMDInit(EngineState *s, int argc, reg_t *argv) {
+ const int16 x = argv[0].toSint16();
+ const int16 y = argv[1].toSint16();
+ const VMDPlayer::PlayFlags flags = argc > 2 ? (VMDPlayer::PlayFlags)argv[2].toUint16() : VMDPlayer::kPlayFlagNone;
+ int16 boostPercent;
+ int16 boostStartColor;
+ int16 boostEndColor;
+ if (argc > 5 && (flags & VMDPlayer::kPlayFlagBoost)) {
+ boostPercent = argv[3].toSint16();
+ boostStartColor = argv[4].toSint16();
+ boostEndColor = argv[5].toSint16();
+ } else {
+ boostPercent = 0;
+ boostStartColor = -1;
+ boostEndColor = -1;
}
- case 6: // Play
- videoDecoder = new Video::AdvancedVMDDecoder();
- if (s->_videoState.fileName.empty()) {
- // Happens in Lighthouse
- warning("kPlayVMD: Empty filename passed");
- return s->r_acc;
- }
+ g_sci->_video32->getVMDPlayer().init(x, y, flags, boostPercent, boostStartColor, boostEndColor);
- if (!videoDecoder->loadFile(s->_videoState.fileName)) {
- warning("Could not open VMD %s", s->_videoState.fileName.c_str());
- break;
- }
+ return make_reg(0, 0);
+}
- if (reshowCursor)
- g_sci->_gfxCursor->kernelHide();
+reg_t kPlayVMDClose(EngineState *s, int argc, reg_t *argv) {
+ return make_reg(0, g_sci->_video32->getVMDPlayer().close());
+}
- playVideo(videoDecoder, s->_videoState);
+reg_t kPlayVMDPlayUntilEvent(EngineState *s, int argc, reg_t *argv) {
+ const VMDPlayer::EventFlags flags = (VMDPlayer::EventFlags)argv[0].toUint16();
+ const int16 lastFrameNo = argc > 1 ? argv[1].toSint16() : -1;
+ const int16 yieldInterval = argc > 2 ? argv[2].toSint16() : -1;
+ return make_reg(0, g_sci->_video32->getVMDPlayer().kernelPlayUntilEvent(flags, lastFrameNo, yieldInterval));
+}
- if (reshowCursor)
- g_sci->_gfxCursor->kernelShow();
- break;
- case 23: // set video palette range
- s->_vmdPalStart = argv[1].toUint16();
- s->_vmdPalEnd = argv[2].toUint16();
- break;
- case 14:
- // Takes an additional integer parameter (e.g. 3)
- case 16:
- // Takes an additional parameter, usually 0
- case 21:
- // Looks to be setting the video size and position. Called with 4 extra integer
- // parameters (e.g. 86, 41, 235, 106)
- default:
- warningMsg = Common::String::format("PlayVMD - unsupported subop %d. Params: %d (", operation, argc);
+reg_t kPlayVMDShowCursor(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_video32->getVMDPlayer().setShowCursor((bool)argv[0].toUint16());
+ return s->r_acc;
+}
- for (int i = 0; i < argc; i++) {
- warningMsg += Common::String::format("%04x:%04x", PRINT_REG(argv[i]));
- warningMsg += (i == argc - 1 ? ")" : ", ");
- }
+reg_t kPlayVMDSetBlackoutArea(EngineState *s, int argc, reg_t *argv) {
+ const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
- warning("%s", warningMsg.c_str());
- break;
- }
+ Common::Rect blackoutArea;
+ blackoutArea.left = MAX((int16)0, argv[0].toSint16());
+ blackoutArea.top = MAX((int16)0, argv[1].toSint16());
+ blackoutArea.right = MIN(scriptWidth, (int16)(argv[2].toSint16() + 1));
+ blackoutArea.bottom = MIN(scriptHeight, (int16)(argv[3].toSint16() + 1));
+ g_sci->_video32->getVMDPlayer().setBlackoutArea(blackoutArea);
+ return s->r_acc;
+}
+reg_t kPlayVMDRestrictPalette(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_video32->getVMDPlayer().restrictPalette(argv[0].toUint16(), argv[1].toUint16());
return s->r_acc;
}
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index 0972aec4a4..31fb848a2c 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -398,8 +398,13 @@ void EngineState::saveLoadWithSerializer(Common::Serializer &s) {
_segMan->saveLoadWithSerializer(s);
g_sci->_soundCmd->syncPlayList(s);
- // NOTE: This will be GfxPalette32 for SCI32 engine games
- g_sci->_gfxPalette16->saveLoadWithSerializer(s);
+
+#ifdef ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2) {
+ g_sci->_gfxPalette32->saveLoadWithSerializer(s);
+ } else
+#endif
+ g_sci->_gfxPalette16->saveLoadWithSerializer(s);
}
void Vocabulary::saveLoadWithSerializer(Common::Serializer &s) {
@@ -767,7 +772,7 @@ void GfxPalette32::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsSint16LE(_varyFromColor);
s.syncAsSint16LE(_varyToColor);
s.syncAsUint16LE(_varyNumTimesPaused);
- s.syncAsByte(_versionUpdated);
+ s.syncAsByte(_needsUpdate);
s.syncAsSint32LE(_varyTime);
s.syncAsUint32LE(_varyLastTick);
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index 70436e1269..e6eed0b4b7 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -1453,6 +1453,167 @@ static const SciScriptPatcherEntry kq6Signatures[] = {
};
// ===========================================================================
+
+// King's Quest 7 has really weird subtitles. It seems as if the subtitles were
+// not fully finished.
+//
+// Method kqMessager::findTalker in script 0 tries to figure out, which class to use for
+// displaying subtitles. It uses the "talker" data of the given message to do that.
+// Strangely this "talker" data seems to be quite broken.
+// For example chapter 2 starts with a cutscene.
+// Troll king: "Welcome, most beautiful of princesses!" - talker 6
+// Which is followed by the princess going
+// "Hmm?" - which is set to talker 99, normally the princess is talker 7.
+//
+// Talker 99 is seen as unknown and thus treated as "narrator", which makes
+// the scripts put the text at the top of the game screen and even use a
+// different font.
+//
+// In other cases, when the player character thinks to himself talker 99
+// is also used. In such situations it may make somewhat sense to do so,
+// but putting the text at the top of the screen is also irritating to the player.
+// It's really weird.
+//
+// The scripts also put the regular text in the middle of the screen, blocking
+// animations.
+//
+// And for certain rooms, the subtitle box may use another color
+// like for example pink/purple at the start of chapter 5.
+//
+// We fix all of that (hopefully - lots of testing is required).
+// We put the text at the bottom of the play screen.
+// We also make the scripts use the regular KQTalker instead of KQNarrator.
+// And we also make the subtitle box use color 255, which is fixed white.
+//
+// Applies to at least: PC CD 1.4 English, 1.51 English, 1.51 German, 2.00 English
+// Patched method: KQNarrator::init (script 31)
+static const uint16 kq7SignatureSubtitleFix1[] = {
+ SIG_MAGICDWORD,
+ 0x39, 0x25, // pushi 25h (fore)
+ 0x78, // push1
+ 0x39, 0x06, // pushi 06 - sets back to 6
+ 0x39, 0x26, // pushi 26 (back)
+ 0x78, // push1
+ 0x78, // push1 - sets back to 1
+ 0x39, 0x2a, // pushi 2Ah (font)
+ 0x78, // push1
+ 0x89, 0x16, // lsg global[16h] - sets font to global[16h]
+ 0x7a, // push2 (y)
+ 0x78, // push1
+ 0x76, // push0 - sets y to 0
+ 0x54, SIG_UINT16(0x0018), // self 18h
+ SIG_END
+};
+
+static const uint16 kq7PatchSubtitleFix1[] = {
+ 0x33, 0x12, // jmp [skip special init code]
+ PATCH_END
+};
+
+// Applies to at least: PC CD 1.51 English, 1.51 German, 2.00 English
+// Patched method: Narrator::init (script 64928)
+static const uint16 kq7SignatureSubtitleFix2[] = {
+ SIG_MAGICDWORD,
+ 0x89, 0x5a, // lsg global[5a]
+ 0x35, 0x02, // ldi 02
+ 0x12, // and
+ 0x31, 0x1e, // bnt [skip audio volume code]
+ 0x38, SIG_ADDTOOFFSET(+2), // pushi masterVolume (0212h for 2.00, 0219h for 1.51)
+ 0x76, // push0
+ 0x81, 0x01, // lag global[1]
+ 0x4a, 0x04, 0x00, // send 04
+ 0x65, 0x32, // aTop curVolume
+ 0x38, SIG_ADDTOOFFSET(+2), // pushi masterVolume (0212h for 2.00, 0219h for 1.51)
+ 0x78, // push1
+ 0x67, 0x32, // pTos curVolume
+ 0x35, 0x02, // ldi 02
+ 0x06, // mul
+ 0x36, // push
+ 0x35, 0x03, // ldi 03
+ 0x08, // div
+ 0x36, // push
+ 0x81, 0x01, // lag global[1]
+ 0x4a, 0x06, 0x00, // send 06
+ // end of volume code
+ 0x35, 0x01, // ldi 01
+ 0x65, 0x28, // aTop initialized
+ SIG_END
+};
+
+static const uint16 kq7PatchSubtitleFix2[] = {
+ PATCH_ADDTOOFFSET(+5), // skip to bnt
+ 0x31, 0x1b, // bnt [skip audio volume code]
+ PATCH_ADDTOOFFSET(+15), // right after "aTop curVolume / pushi masterVolume / push1"
+ 0x7a, // push2
+ 0x06, // mul (saves 3 bytes in total)
+ 0x36, // push
+ 0x35, 0x03, // ldi 03
+ 0x08, // div
+ 0x36, // push
+ 0x81, 0x01, // lag global[1]
+ 0x4a, 0x06, 0x00, // send 06
+ // end of volume code
+ 0x35, 118, // ldi 118d
+ 0x65, 0x16, // aTop y
+ 0x78, // push1 (saves 1 byte)
+ 0x69, 0x28, // sTop initialized
+ PATCH_END
+};
+
+// Applies to at least: PC CD 1.51 English, 1.51 German, 2.00 English
+// Patched method: Narrator::say (script 64928)
+static const uint16 kq7SignatureSubtitleFix3[] = {
+ SIG_MAGICDWORD,
+ 0x63, 0x28, // pToa initialized
+ 0x18, // not
+ 0x31, 0x07, // bnt [skip init code]
+ 0x38, SIG_ADDTOOFFSET(+2), // pushi init (008Eh for 2.00, 0093h for 1.51)
+ 0x76, // push0
+ 0x54, SIG_UINT16(0x0004), // self 04
+ // end of init code
+ 0x8f, 0x00, // lsp param[0]
+ 0x35, 0x01, // ldi 01
+ 0x1e, // gt?
+ 0x31, 0x08, // bnt [set acc to 0]
+ 0x87, 0x02, // lap param[2]
+ 0x31, 0x04, // bnt [set acc to 0]
+ 0x87, 0x02, // lap param[2]
+ 0x33, 0x02, // jmp [over set acc to 0 code]
+ 0x35, 0x00, // ldi 00
+ 0x65, 0x18, // aTop caller
+ SIG_END
+};
+
+static const uint16 kq7PatchSubtitleFix3[] = {
+ PATCH_ADDTOOFFSET(+2), // skip over "pToa initialized code"
+ 0x2f, 0x0c, // bt [skip init code] - saved 1 byte
+ 0x38,
+ PATCH_GETORIGINALBYTE(+6),
+ PATCH_GETORIGINALBYTE(+7), // pushi (init)
+ 0x76, // push0
+ 0x54, PATCH_UINT16(0x0004), // self 04
+ // additionally set background color here (5 bytes)
+ 0x34, PATCH_UINT16(255), // pushi 255d
+ 0x65, 0x2e, // aTop back
+ // end of init code
+ 0x8f, 0x00, // lsp param[0]
+ 0x35, 0x01, // ldi 01 - this may get optimized to get another byte
+ 0x1e, // gt?
+ 0x31, 0x04, // bnt [set acc to 0]
+ 0x87, 0x02, // lap param[2]
+ 0x2f, 0x02, // bt [over set acc to 0 code]
+ PATCH_END
+};
+
+// script, description, signature patch
+static const SciScriptPatcherEntry kq7Signatures[] = {
+ { true, 31, "subtitle fix 1/3", 1, kq7SignatureSubtitleFix1, kq7PatchSubtitleFix1 },
+ { true, 64928, "subtitle fix 2/3", 1, kq7SignatureSubtitleFix2, kq7PatchSubtitleFix2 },
+ { true, 64928, "subtitle fix 3/3", 1, kq7SignatureSubtitleFix3, kq7PatchSubtitleFix3 },
+ SCI_SIGNATUREENTRY_TERMINATOR
+};
+
+// ===========================================================================
// Script 210 in the German version of Longbow handles the case where Robin
// hands out the scroll to Marion and then types his name using the hand code.
// The German version script contains a typo (probably a copy/paste error),
@@ -1506,9 +1667,9 @@ static const uint16 longbowPatchShowHandCode[] = {
// that's why I rather patched the code, that uses the locals for a lookup.
// Which means it doesn't matter anymore when those locals are overwritten.
//
-// Applies to at least: English PC floppy, German PC floppy (not tested), English Amiga floppy
+// Applies to at least: English PC floppy, German PC floppy, English Amiga floppy
// Responsible method: export 2 of script 225
-// Fixes bug: #6571
+// Fixes bug: #6751
static const uint16 longbowSignatureBerryBushFix[] = {
0x89, 0x70, // lsg global[70h]
0x35, 0x03, // ldi 03h
@@ -4453,6 +4614,9 @@ void ScriptPatcher::processScript(uint16 scriptNr, byte *scriptData, const uint3
case GID_KQ6:
signatureTable = kq6Signatures;
break;
+ case GID_KQ7:
+ signatureTable = kq7Signatures;
+ break;
case GID_LAURABOW:
signatureTable = laurabow1Signatures;
break;
diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp
index fda78317b5..2c85907628 100644
--- a/engines/sci/engine/state.cpp
+++ b/engines/sci/engine/state.cpp
@@ -70,9 +70,6 @@ static const uint16 s_halfWidthSJISMap[256] = {
EngineState::EngineState(SegManager *segMan)
: _segMan(segMan),
-#ifdef ENABLE_SCI32
- _virtualIndexFile(0),
-#endif
_dirseeker() {
reset(false);
@@ -80,9 +77,6 @@ EngineState::EngineState(SegManager *segMan)
EngineState::~EngineState() {
delete _msgState;
-#ifdef ENABLE_SCI32
- delete _virtualIndexFile;
-#endif
}
void EngineState::reset(bool isRestoring) {
diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h
index cf9a753f5c..dd8d76f002 100644
--- a/engines/sci/engine/state.h
+++ b/engines/sci/engine/state.h
@@ -131,10 +131,6 @@ public:
int16 _lastSaveVirtualId; // last virtual id fed to kSaveGame, if no kGetSaveFiles was called inbetween
int16 _lastSaveNewId; // last newly created filename-id by kSaveGame
-#ifdef ENABLE_SCI32
- VirtualIndexFile *_virtualIndexFile;
-#endif
-
// see detection.cpp / SciEngine::loadGameState()
bool _delayedRestoreGame; // boolean, that triggers delayed restore (triggered by ScummVM menu)
int _delayedRestoreGameId; // the saved game id, that it supposed to get restored (triggered by ScummVM menu)
@@ -205,6 +201,7 @@ public:
uint16 _memorySegmentSize;
byte _memorySegment[kMemorySegmentMax];
+ // TODO: Excise video code from the state manager
VideoState _videoState;
uint16 _vmdPalStart, _vmdPalEnd;
bool _syncedAudioOptions;
diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp
index f8cd5fd171..48de054a31 100644
--- a/engines/sci/graphics/celobj32.cpp
+++ b/engines/sci/graphics/celobj32.cpp
@@ -83,9 +83,11 @@ const CelScalerTable *CelScaler::getScalerTable(const Ratio &scaleX, const Ratio
#pragma mark -
#pragma mark CelObj
+bool CelObj::_drawBlackLines = false;
void CelObj::init() {
CelObj::deinit();
+ _drawBlackLines = false;
_nextCacheId = 1;
_scaler = new CelScaler();
_cache = new CelCache;
@@ -407,6 +409,7 @@ void CelObj::draw(Buffer &target, const ScreenItem &screenItem, const Common::Re
const Common::Point &scaledPosition = screenItem._scaledPosition;
const Ratio &scaleX = screenItem._ratioX;
const Ratio &scaleY = screenItem._ratioY;
+ _drawBlackLines = screenItem._drawBlackLines;
if (_remap) {
// NOTE: In the original code this check was `g_Remap_numActiveRemaps && _remap`,
@@ -488,6 +491,8 @@ void CelObj::draw(Buffer &target, const ScreenItem &screenItem, const Common::Re
}
}
}
+
+ _drawBlackLines = false;
}
void CelObj::draw(Buffer &target, const ScreenItem &screenItem, const Common::Rect &targetRect, bool mirrorX) {
@@ -565,12 +570,7 @@ uint8 CelObj::readPixel(uint16 x, const uint16 y, bool mirrorX) const {
void CelObj::submitPalette() const {
if (_hunkPaletteOffset) {
- Palette palette;
-
- byte *res = getResPointer();
- // NOTE: In SCI engine this uses HunkPalette::Init.
- // TODO: Use a better size value
- g_sci->_gfxPalette32->createFromData(res + _hunkPaletteOffset, 999999, &palette);
+ HunkPalette palette(getResPointer() + _hunkPaletteOffset);
g_sci->_gfxPalette32->submit(palette);
}
}
@@ -627,7 +627,7 @@ void CelObj::putCopyInCache(const int cacheIndex) const {
#pragma mark -
#pragma mark CelObj - Drawing
-template<typename MAPPER, typename SCALER>
+template<typename MAPPER, typename SCALER, bool DRAW_BLACK_LINES>
struct RENDERER {
MAPPER &_mapper;
SCALER &_scaler;
@@ -645,6 +645,12 @@ struct RENDERER {
const int16 targetWidth = targetRect.width();
const int16 targetHeight = targetRect.height();
for (int16 y = 0; y < targetHeight; ++y) {
+ if (DRAW_BLACK_LINES && (y % 2) == 0) {
+ memset(targetPixel, 0, targetWidth);
+ targetPixel += targetWidth + skipStride;
+ continue;
+ }
+
_scaler.setTarget(targetRect.left, targetRect.top + y);
for (int16 x = 0; x < targetWidth; ++x) {
@@ -661,7 +667,7 @@ void CelObj::render(Buffer &target, const Common::Rect &targetRect, const Common
MAPPER mapper;
SCALER scaler(*this, targetRect.left - scaledPosition.x + targetRect.width(), scaledPosition);
- RENDERER<MAPPER, SCALER> renderer(mapper, scaler, _transparentColor);
+ RENDERER<MAPPER, SCALER, false> renderer(mapper, scaler, _transparentColor);
renderer.draw(target, targetRect, scaledPosition);
}
@@ -670,8 +676,13 @@ void CelObj::render(Buffer &target, const Common::Rect &targetRect, const Common
MAPPER mapper;
SCALER scaler(*this, targetRect, scaledPosition, scaleX, scaleY);
- RENDERER<MAPPER, SCALER> renderer(mapper, scaler, _transparentColor);
- renderer.draw(target, targetRect, scaledPosition);
+ if (_drawBlackLines) {
+ RENDERER<MAPPER, SCALER, true> renderer(mapper, scaler, _transparentColor);
+ renderer.draw(target, targetRect, scaledPosition);
+ } else {
+ RENDERER<MAPPER, SCALER, false> renderer(mapper, scaler, _transparentColor);
+ renderer.draw(target, targetRect, scaledPosition);
+ }
}
void dummyFill(Buffer &target, const Common::Rect &targetRect) {
diff --git a/engines/sci/graphics/celobj32.h b/engines/sci/graphics/celobj32.h
index e405592b5f..eb6ce3a3c9 100644
--- a/engines/sci/graphics/celobj32.h
+++ b/engines/sci/graphics/celobj32.h
@@ -228,6 +228,18 @@ class ScreenItem;
class CelObj {
protected:
/**
+ * When true, every second line of the cel will be
+ * rendered as a black line.
+ *
+ * @see ScreenItem::_drawBlackLines
+ * @note Using a static member because otherwise this
+ * would otherwise need to be copied down through
+ * several calls. (SSCI did similar, using a global
+ * variable.)
+ */
+ static bool _drawBlackLines;
+
+ /**
* When true, this cel will be horizontally mirrored
* when it is drawn. This is an internal flag that is
* set by draw methods based on the combination of the
diff --git a/engines/sci/graphics/controls32.cpp b/engines/sci/graphics/controls32.cpp
index 4cbb4541df..6b91bb4679 100644
--- a/engines/sci/graphics/controls32.cpp
+++ b/engines/sci/graphics/controls32.cpp
@@ -459,7 +459,7 @@ void ScrollWindow::hide() {
return;
}
- g_sci->_gfxFrameout->deleteScreenItem(_screenItem, _plane);
+ g_sci->_gfxFrameout->deleteScreenItem(*_screenItem, _plane);
_screenItem = nullptr;
g_sci->_gfxFrameout->frameOut(true);
diff --git a/engines/sci/graphics/cursor.h b/engines/sci/graphics/cursor.h
index 8d125c45b3..5125469cfe 100644
--- a/engines/sci/graphics/cursor.h
+++ b/engines/sci/graphics/cursor.h
@@ -25,6 +25,8 @@
#include "common/array.h"
#include "common/hashmap.h"
+#include "sci/sci.h"
+#include "sci/graphics/helpers.h"
namespace Sci {
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index c0feea8999..7bb9a4f5cf 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -115,6 +115,7 @@ GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAd
}
switch (g_sci->getGameId()) {
+ case GID_HOYLE5:
case GID_GK2:
case GID_LIGHTHOUSE:
case GID_LSL7:
@@ -278,20 +279,52 @@ bool GfxFrameout::checkForFred(const reg_t object) {
#pragma mark -
#pragma mark Screen items
-void GfxFrameout::deleteScreenItem(ScreenItem *screenItem, Plane *plane) {
- if (screenItem->_created == 0) {
- screenItem->_created = 0;
- screenItem->_updated = 0;
- screenItem->_deleted = getScreenCount();
+void GfxFrameout::addScreenItem(ScreenItem &screenItem) const {
+ Plane *plane = _planes.findByObject(screenItem._plane);
+ if (plane == nullptr) {
+ error("GfxFrameout::addScreenItem: Could not find plane %04x:%04x for screen item %04x:%04x", PRINT_REG(screenItem._plane), PRINT_REG(screenItem._object));
+ }
+ plane->_screenItemList.add(&screenItem);
+}
+
+void GfxFrameout::updateScreenItem(ScreenItem &screenItem) const {
+ // TODO: In SCI3+ this will need to go through Plane
+// Plane *plane = _planes.findByObject(screenItem._plane);
+// if (plane == nullptr) {
+// error("GfxFrameout::updateScreenItem: Could not find plane %04x:%04x for screen item %04x:%04x", PRINT_REG(screenItem._plane), PRINT_REG(screenItem._object));
+// }
+
+ screenItem.update();
+}
+
+void GfxFrameout::deleteScreenItem(ScreenItem &screenItem) {
+ Plane *plane = _planes.findByObject(screenItem._plane);
+ if (plane == nullptr) {
+ error("GfxFrameout::deleteScreenItem: Could not find plane %04x:%04x for screen item %04x:%04x", PRINT_REG(screenItem._plane), PRINT_REG(screenItem._object));
+ }
+ if (plane->_screenItemList.findByObject(screenItem._object) == nullptr) {
+ error("GfxFrameout::deleteScreenItem: Screen item %04x:%04x not found in plane %04x:%04x", PRINT_REG(screenItem._object), PRINT_REG(screenItem._plane));
+ }
+ deleteScreenItem(screenItem, *plane);
+}
+
+void GfxFrameout::deleteScreenItem(ScreenItem &screenItem, Plane &plane) {
+ if (screenItem._created == 0) {
+ screenItem._created = 0;
+ screenItem._updated = 0;
+ screenItem._deleted = getScreenCount();
} else {
- plane->_screenItemList.erase(screenItem);
- plane->_screenItemList.pack();
+ plane._screenItemList.erase(&screenItem);
+ plane._screenItemList.pack();
}
}
-void GfxFrameout::deleteScreenItem(ScreenItem *screenItem, const reg_t planeObject) {
+void GfxFrameout::deleteScreenItem(ScreenItem &screenItem, const reg_t planeObject) {
Plane *plane = _planes.findByObject(planeObject);
- deleteScreenItem(screenItem, plane);
+ if (plane == nullptr) {
+ error("GfxFrameout::deleteScreenItem: Could not find plane %04x:%04x for screen item %04x:%04x", PRINT_REG(planeObject), PRINT_REG(screenItem._object));
+ }
+ deleteScreenItem(screenItem, *plane);
}
void GfxFrameout::kernelAddScreenItem(const reg_t object) {
@@ -364,7 +397,7 @@ void GfxFrameout::kernelDeleteScreenItem(const reg_t object) {
return;
}
- deleteScreenItem(screenItem, plane);
+ deleteScreenItem(*screenItem, *plane);
}
#pragma mark -
@@ -540,7 +573,7 @@ void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &eraseR
// _robot->frameAlmostVisible();
// }
- _palette->updateHardware();
+ _palette->updateHardware(!shouldShowBits);
if (shouldShowBits) {
showBits();
@@ -1111,7 +1144,7 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry
_palette->submit(nextPalette);
_palette->updateFFrame();
- _palette->updateHardware();
+ _palette->updateHardware(false);
showBits();
_frameNowVisible = true;
diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h
index cc62c61d22..99658ede6a 100644
--- a/engines/sci/graphics/frameout.h
+++ b/engines/sci/graphics/frameout.h
@@ -202,14 +202,29 @@ private:
public:
/**
+ * Adds a screen item.
+ */
+ void addScreenItem(ScreenItem &screenItem) const;
+
+ /**
+ * Updates a screen item.
+ */
+ void updateScreenItem(ScreenItem &screenItem) const;
+
+ /**
+ * Deletes a screen item.
+ */
+ void deleteScreenItem(ScreenItem &screenItem);
+
+ /**
* Deletes a screen item from the given plane.
*/
- void deleteScreenItem(ScreenItem *screenItem, Plane *plane);
+ void deleteScreenItem(ScreenItem &screenItem, Plane &plane);
/**
* Deletes a screen item from the given plane.
*/
- void deleteScreenItem(ScreenItem *screenItem, const reg_t plane);
+ void deleteScreenItem(ScreenItem &screenItem, const reg_t plane);
void kernelAddScreenItem(const reg_t object);
void kernelUpdateScreenItem(const reg_t object);
diff --git a/engines/sci/graphics/paint32.cpp b/engines/sci/graphics/paint32.cpp
index bfd46484e9..74eb1629d0 100644
--- a/engines/sci/graphics/paint32.cpp
+++ b/engines/sci/graphics/paint32.cpp
@@ -81,7 +81,7 @@ void GfxPaint32::kernelDeleteLine(const reg_t screenItemObject, const reg_t plan
}
_segMan->freeHunkEntry(screenItem->_celInfo.bitmap);
- g_sci->_gfxFrameout->deleteScreenItem(screenItem, plane);
+ g_sci->_gfxFrameout->deleteScreenItem(*screenItem, *plane);
}
void GfxPaint32::plotter(int x, int y, int color, void *data) {
diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h
index 7335dc59d0..2178de8a91 100644
--- a/engines/sci/graphics/palette.h
+++ b/engines/sci/graphics/palette.h
@@ -51,7 +51,7 @@ public:
bool setAmiga();
void modifyAmigaPalette(byte *data);
void setEGA();
- virtual void set(Palette *sciPal, bool force, bool forceRealMerge = false);
+ void set(Palette *sciPal, bool force, bool forceRealMerge = false);
bool insert(Palette *newPalette, Palette *destPalette);
bool merge(Palette *pFrom, bool force, bool forceRealMerge);
uint16 matchColor(byte r, byte g, byte b);
@@ -63,11 +63,11 @@ public:
void drewPicture(GuiResourceId pictureId);
- virtual bool kernelSetFromResource(GuiResourceId resourceId, bool force);
+ bool kernelSetFromResource(GuiResourceId resourceId, bool force);
void kernelSetFlag(uint16 fromColor, uint16 toColor, uint16 flag);
void kernelUnsetFlag(uint16 fromColor, uint16 toColor, uint16 flag);
void kernelSetIntensity(uint16 fromColor, uint16 toColor, uint16 intensity, bool setPalette);
- virtual int16 kernelFindColor(uint16 r, uint16 g, uint16 b);
+ int16 kernelFindColor(uint16 r, uint16 g, uint16 b);
bool kernelAnimate(byte fromColor, byte toColor, int speed);
void kernelAnimateSet();
reg_t kernelSave();
@@ -81,7 +81,7 @@ public:
int16 kernelPalVaryGetCurrentStep();
int16 kernelPalVaryChangeTarget(GuiResourceId resourceId);
void kernelPalVaryChangeTicks(uint16 ticks);
- virtual void kernelPalVaryPause(bool pause);
+ void kernelPalVaryPause(bool pause);
void kernelPalVaryDeinit();
void palVaryUpdate();
void palVaryPrepareForTransition();
@@ -89,7 +89,7 @@ public:
Palette _sysPalette;
- virtual void saveLoadWithSerializer(Common::Serializer &s);
+ void saveLoadWithSerializer(Common::Serializer &s);
void palVarySaveLoadPalette(Common::Serializer &s, Palette *palette);
byte findMacIconBarColor(byte r, byte g, byte b);
diff --git a/engines/sci/graphics/palette32.cpp b/engines/sci/graphics/palette32.cpp
index 0840e82a40..2a98c237b0 100644
--- a/engines/sci/graphics/palette32.cpp
+++ b/engines/sci/graphics/palette32.cpp
@@ -27,19 +27,109 @@
#include "sci/sci.h"
#include "sci/event.h"
#include "sci/resource.h"
+#include "sci/util.h"
#include "sci/graphics/palette32.h"
#include "sci/graphics/remap32.h"
#include "sci/graphics/screen.h"
namespace Sci {
-GfxPalette32::GfxPalette32(ResourceManager *resMan, GfxScreen *screen)
- : GfxPalette(resMan, screen),
+#pragma mark HunkPalette
+
+HunkPalette::HunkPalette(byte *rawPalette) :
+ _version(0),
+ // NOTE: The header size in palettes is garbage. In at least KQ7 2.00b and
+ // Phant1, the 999.pal sets this value to 0. In most other palettes it is
+ // set to 14, but the *actual* size of the header structure used in SSCI is
+ // 13, which is reflected by `kHunkPaletteHeaderSize`.
+ // _headerSize(rawPalette[0]),
+ _numPalettes(rawPalette[10]),
+ _data(nullptr) {
+ assert(_numPalettes == 0 || _numPalettes == 1);
+ if (_numPalettes) {
+ _data = rawPalette;
+ _version = getEntryHeader().version;
+ }
+}
+
+void HunkPalette::setVersion(const uint32 version) {
+ if (_numPalettes != _data[10]) {
+ error("Invalid HunkPalette");
+ }
+
+ if (_numPalettes) {
+ const EntryHeader header = getEntryHeader();
+ if (header.version != _version) {
+ error("Invalid HunkPalette");
+ }
+
+ WRITE_SCI11ENDIAN_UINT32(getPalPointer() + kEntryVersionOffset, version);
+ _version = version;
+ }
+}
+
+const HunkPalette::EntryHeader HunkPalette::getEntryHeader() const {
+ const byte *const data = getPalPointer();
+
+ EntryHeader header;
+ header.startColor = data[10];
+ header.numColors = READ_SCI11ENDIAN_UINT16(data + 14);
+ header.used = data[16];
+ header.sharedUsed = data[17];
+ header.version = READ_SCI11ENDIAN_UINT32(data + kEntryVersionOffset);
+
+ return header;
+}
+
+const Palette HunkPalette::toPalette() const {
+ Palette outPalette;
+
+ for (int16 i = 0; i < ARRAYSIZE(outPalette.colors); ++i) {
+ outPalette.colors[i].used = false;
+ outPalette.colors[i].r = 0;
+ outPalette.colors[i].g = 0;
+ outPalette.colors[i].b = 0;
+ }
+
+ if (_numPalettes) {
+ const EntryHeader header = getEntryHeader();
+ byte *data = getPalPointer() + kEntryHeaderSize;
+
+ int16 end = header.startColor + header.numColors;
+ assert(end <= 256);
+
+ if (header.sharedUsed) {
+ for (int16 i = header.startColor; i < end; ++i) {
+ outPalette.colors[i].used = header.used;
+ outPalette.colors[i].r = *data++;
+ outPalette.colors[i].g = *data++;
+ outPalette.colors[i].b = *data++;
+ }
+ } else {
+ for (int16 i = header.startColor; i < end; ++i) {
+ outPalette.colors[i].used = *data++;
+ outPalette.colors[i].r = *data++;
+ outPalette.colors[i].g = *data++;
+ outPalette.colors[i].b = *data++;
+ }
+ }
+ }
+
+ return outPalette;
+}
+
+
+#pragma mark -
+#pragma mark GfxPalette32
+
+GfxPalette32::GfxPalette32(ResourceManager *resMan)
+ : _resMan(resMan),
// Palette versioning
_version(1),
- _versionUpdated(false),
- _sourcePalette(_sysPalette),
- _nextPalette(_sysPalette),
+ _needsUpdate(false),
+ _currentPalette(),
+ _sourcePalette(),
+ _nextPalette(),
// Clut
_clutTable(nullptr),
// Palette varying
@@ -55,102 +145,91 @@ GfxPalette32::GfxPalette32(ResourceManager *resMan, GfxScreen *screen)
// Palette cycling
_cyclers(),
_cycleMap() {
- _varyPercent = _varyTargetPercent;
- for (int i = 0, len = ARRAYSIZE(_fadeTable); i < len; ++i) {
- _fadeTable[i] = 100;
- }
- // NOTE: In SCI engine, the palette manager constructor loads
- // the default palette, but in ScummVM this initialisation
- // is performed by SciEngine::run; see r49523 for details
+ _varyPercent = _varyTargetPercent;
+ for (int i = 0, len = ARRAYSIZE(_fadeTable); i < len; ++i) {
+ _fadeTable[i] = 100;
+ }
+
+ loadPalette(999);
}
GfxPalette32::~GfxPalette32() {
+#ifdef ENABLE_SCI3_GAMES
unloadClut();
+#endif
varyOff();
cycleAllOff();
}
inline void mergePaletteInternal(Palette *const to, const Palette *const from) {
- for (int i = 0, len = ARRAYSIZE(to->colors); i < len; ++i) {
+ // The last color is always white, so it is not copied.
+ // (Some palettes try to set the last color, which causes
+ // churning in the palettes when they are merged)
+ for (int i = 0, len = ARRAYSIZE(to->colors) - 1; i < len; ++i) {
if (from->colors[i].used) {
to->colors[i] = from->colors[i];
}
}
}
-void GfxPalette32::submit(Palette &palette) {
- // TODO: The resource manager in SCI32 retains raw data of palettes from
- // the ResourceManager (ResourceMgr) through SegManager (MemoryMgr), and
- // the version number for submitted palettes is set in the raw palette
- // data in memory as an int at an offset
- // `rawData + *rawData[0x0a] * 2 + 31`. However, ScummVM does not retain
- // resource data like this, so this versioning code, while accurate to
- // the original engine, does not do much.
- // (Hopefully this was an optimisation mechanism in SCI engine and not a
- // clever thing to keep the same palette submitted many times from
- // overwriting other palette entries.)
- if (palette.timestamp == _version) {
+void GfxPalette32::submit(const Palette &palette) {
+ const Palette oldSourcePalette(_sourcePalette);
+ mergePaletteInternal(&_sourcePalette, &palette);
+
+ if (!_needsUpdate && _sourcePalette != oldSourcePalette) {
+ ++_version;
+ _needsUpdate = true;
+ }
+}
+
+void GfxPalette32::submit(HunkPalette &hunkPalette) {
+ if (hunkPalette.getVersion() == _version) {
return;
}
- Palette oldSourcePalette(_sourcePalette);
+ const Palette oldSourcePalette(_sourcePalette);
+ const Palette palette = hunkPalette.toPalette();
mergePaletteInternal(&_sourcePalette, &palette);
- if (!_versionUpdated && _sourcePalette != oldSourcePalette) {
+ if (!_needsUpdate && oldSourcePalette != _sourcePalette) {
++_version;
- _versionUpdated = true;
+ _needsUpdate = true;
}
- // Technically this information is supposed to be persisted through a
- // HunkPalette object; right now it would just be lost once the temporary
- // palette was destroyed.
- palette.timestamp = _version;
+ hunkPalette.setVersion(_version);
}
-bool GfxPalette32::kernelSetFromResource(GuiResourceId resourceId, bool force) {
- // TODO: In SCI32, palettes that come from resources come in as
- // HunkPalette objects, not SOLPalette objects. The HunkPalettes
- // have some extra persistence stuff associated with them, such that
- // when they are passed to GfxPalette32::submit, they would get the
- // version number of GfxPalette32 assigned to them.
- Palette palette;
+bool GfxPalette32::loadPalette(const GuiResourceId resourceId) {
+ Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), false);
- if (createPaletteFromResourceInternal(resourceId, &palette)) {
- submit(palette);
- return true;
+ if (!palResource) {
+ return false;
}
- return false;
+ HunkPalette palette(palResource->data);
+ submit(palette);
+ return true;
}
-// In SCI32 engine this method is SOLPalette::Match(Rgb24 *)
-// and is called as PaletteMgr.Current().Match(color)
-int16 GfxPalette32::kernelFindColor(uint16 r, uint16 g, uint16 b) {
- // SQ6 SCI32 engine takes the 16-bit r, g, b arguments from the
- // VM and puts them into al, ah, dl. For compatibility, make sure
- // to discard any high bits here too
- r = r & 0xFF;
- g = g & 0xFF;
- b = b & 0xFF;
+int16 GfxPalette32::matchColor(const uint8 r, const uint8 g, const uint8 b) {
int16 bestIndex = 0;
int bestDifference = 0xFFFFF;
int difference;
- // SQ6 DOS really does check only the first 236 entries
- for (int i = 0, channelDifference; i < 236; ++i) {
- difference = _sysPalette.colors[i].r - r;
+ for (int i = 0, channelDifference; i < g_sci->_gfxRemap32->getStartColor(); ++i) {
+ difference = _currentPalette.colors[i].r - r;
difference *= difference;
if (bestDifference <= difference) {
continue;
}
- channelDifference = _sysPalette.colors[i].g - g;
+ channelDifference = _currentPalette.colors[i].g - g;
difference += channelDifference * channelDifference;
if (bestDifference <= difference) {
continue;
}
- channelDifference = _sysPalette.colors[i].b - b;
+ channelDifference = _currentPalette.colors[i].b - b;
difference += channelDifference * channelDifference;
if (bestDifference <= difference) {
continue;
@@ -162,56 +241,56 @@ int16 GfxPalette32::kernelFindColor(uint16 r, uint16 g, uint16 b) {
return bestIndex;
}
-// TODO: set is overridden for the time being to send palettes coming from
-// various draw methods like GfxPicture::drawSci32Vga and GfxView::draw to
-// _nextPalette instead of _sysPalette. In the SCI32 engine, CelObj palettes
-// (which are stored as Hunk palettes) are submitted by GraphicsMgr::FrameOut
-// to PaletteMgr::Submit by way of calls to CelObj::SubmitPalette.
-// GfxPalette::set is very similar to GfxPalette32::submit, except that SCI32
-// does not do any fancy best-fit merging and so does not accept arguments
-// like `force` and `forceRealMerge`.
-void GfxPalette32::set(Palette *newPalette, bool force, bool forceRealMerge) {
- submit(*newPalette);
-}
-
bool GfxPalette32::updateForFrame() {
applyAll();
- _versionUpdated = false;
- return g_sci->_gfxRemap32->remapAllTables(_nextPalette != _sysPalette);
+ _needsUpdate = false;
+ return g_sci->_gfxRemap32->remapAllTables(_nextPalette != _currentPalette);
}
void GfxPalette32::updateFFrame() {
for (int i = 0; i < ARRAYSIZE(_nextPalette.colors); ++i) {
_nextPalette.colors[i] = _sourcePalette.colors[i];
}
- _versionUpdated = false;
- g_sci->_gfxRemap32->remapAllTables(_nextPalette != _sysPalette);
+ _needsUpdate = false;
+ g_sci->_gfxRemap32->remapAllTables(_nextPalette != _currentPalette);
}
-void GfxPalette32::updateHardware() {
- if (_sysPalette == _nextPalette) {
+void GfxPalette32::updateHardware(const bool updateScreen) {
+ if (_currentPalette == _nextPalette) {
return;
}
byte bpal[3 * 256];
- for (int i = 0; i < ARRAYSIZE(_sysPalette.colors); ++i) {
- _sysPalette.colors[i] = _nextPalette.colors[i];
+ for (int i = 0; i < ARRAYSIZE(_currentPalette.colors) - 1; ++i) {
+ _currentPalette.colors[i] = _nextPalette.colors[i];
// NOTE: If the brightness option in the user configuration file is set,
// SCI engine adjusts palette brightnesses here by mapping RGB values to values
// in some hard-coded brightness tables. There is no reason on modern hardware
// to implement this, unless it is discovered that some game uses a non-standard
// brightness setting by default
- if (_sysPalette.colors[i].used) {
- bpal[i * 3 ] = _sysPalette.colors[i].r;
- bpal[i * 3 + 1] = _sysPalette.colors[i].g;
- bpal[i * 3 + 2] = _sysPalette.colors[i].b;
- }
+
+ // All color entries MUST be copied, not just "used" entries, otherwise
+ // uninitialised memory from bpal makes its way into the system palette.
+ // This would not normally be a problem, except that games sometimes use
+ // unused palette entries. e.g. Phant1 title screen references palette
+ // entries outside its own palette, so will render garbage colors where
+ // the game expects them to be black
+ bpal[i * 3 ] = _currentPalette.colors[i].r;
+ bpal[i * 3 + 1] = _currentPalette.colors[i].g;
+ bpal[i * 3 + 2] = _currentPalette.colors[i].b;
}
+ // The last color must always be white
+ bpal[255 * 3 ] = 255;
+ bpal[255 * 3 + 1] = 255;
+ bpal[255 * 3 + 2] = 255;
+
g_system->getPaletteManager()->setPalette(bpal, 0, 256);
- g_sci->getEventManager()->updateScreen();
+ if (updateScreen) {
+ g_sci->getEventManager()->updateScreen();
+ }
}
void GfxPalette32::applyAll() {
@@ -223,6 +302,7 @@ void GfxPalette32::applyAll() {
#pragma mark -
#pragma mark Colour look-up
+#ifdef ENABLE_SCI3_GAMES
bool GfxPalette32::loadClut(uint16 clutId) {
// loadClut() will load a color lookup table from a clu file and set
// the palette found in the file. This is to be used with Phantasmagoria 2.
@@ -272,27 +352,20 @@ void GfxPalette32::unloadClut() {
delete[] _clutTable;
_clutTable = nullptr;
}
+#endif
#pragma mark -
#pragma mark Varying
-inline bool GfxPalette32::createPaletteFromResourceInternal(const GuiResourceId paletteId, Palette *const out) const {
- Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, paletteId), false);
+inline Palette GfxPalette32::getPaletteFromResourceInternal(const GuiResourceId resourceId) const {
+ Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), false);
if (!palResource) {
- return false;
+ error("Could not load vary palette %d", resourceId);
}
- createFromData(palResource->data, palResource->size, out);
- return true;
-}
-
-inline Palette GfxPalette32::getPaletteFromResourceInternal(const GuiResourceId paletteId) const {
- Palette palette;
- if (!createPaletteFromResourceInternal(paletteId, &palette)) {
- error("Could not load vary target %d", paletteId);
- }
- return palette;
+ HunkPalette rawPalette(palResource->data);
+ return rawPalette.toPalette();
}
inline void GfxPalette32::setVaryTimeInternal(const int16 percent, const int time) {
@@ -316,8 +389,6 @@ inline void GfxPalette32::setVaryTimeInternal(const int16 percent, const int tim
}
}
-// TODO: This gets called *a lot* in at least the first scene
-// of SQ6. Optimisation would not be the worst idea in the world.
void GfxPalette32::kernelPalVarySet(const GuiResourceId paletteId, const int16 percent, const int time, const int16 fromColor, const int16 toColor) {
Palette palette = getPaletteFromResourceInternal(paletteId);
setVary(&palette, percent, time, fromColor, toColor);
@@ -585,18 +656,15 @@ void GfxPalette32::setCycle(const uint8 fromColor, const uint8 toColor, const in
// SCI engine overrides the first oldest cycler that it finds where
// “oldest” is determined by the difference between the tick and now
if (cycler == nullptr) {
- int maxUpdateDelta = -1;
- // Optimization: Unlike actual SCI (SQ6) engine, we call
- // getTickCount only once and store it, instead of calling it
- // twice on each iteration through the loop
const uint32 now = g_sci->getTickCount();
+ uint32 minUpdateDelta = 0xFFFFFFFF;
for (cyclerIndex = 0; cyclerIndex < numCyclers; ++cyclerIndex) {
PalCycler *candidate = _cyclers[cyclerIndex];
- const int32 updateDelta = now - candidate->lastUpdateTick;
- if (updateDelta >= maxUpdateDelta) {
- maxUpdateDelta = updateDelta;
+ const uint32 updateDelta = now - candidate->lastUpdateTick;
+ if (updateDelta < minUpdateDelta) {
+ minUpdateDelta = updateDelta;
cycler = candidate;
}
}
diff --git a/engines/sci/graphics/palette32.h b/engines/sci/graphics/palette32.h
index 7dda53e5c1..dc2158022f 100644
--- a/engines/sci/graphics/palette32.h
+++ b/engines/sci/graphics/palette32.h
@@ -26,6 +26,118 @@
#include "sci/graphics/palette.h"
namespace Sci {
+
+/**
+ * HunkPalette represents a raw palette resource
+ * read from disk.
+ */
+class HunkPalette {
+public:
+ HunkPalette(byte *rawPalette);
+
+ /**
+ * Gets the version of the palette.
+ */
+ uint32 getVersion() const { return _version; }
+
+ /**
+ * Sets the version of the palette.
+ */
+ void setVersion(const uint32 version);
+
+ /**
+ * Converts the hunk palette to a standard
+ * palette.
+ */
+ const Palette toPalette() const;
+
+private:
+ enum {
+ /**
+ * The size of the HunkPalette header.
+ */
+ kHunkPaletteHeaderSize = 13,
+
+ /**
+ * The size of a palette entry header.
+ */
+ kEntryHeaderSize = 22,
+
+ /**
+ * The offset of the hunk palette version
+ * within the palette entry header.
+ */
+ kEntryVersionOffset = 18
+ };
+
+ /**
+ * The header for a palette inside the
+ * HunkPalette.
+ */
+ struct EntryHeader {
+ /**
+ * The start color.
+ */
+ uint8 startColor;
+
+ /**
+ * The number of palette colors in this
+ * entry.
+ */
+ uint16 numColors;
+
+ /**
+ * The default `used` flag.
+ */
+ bool used;
+
+ /**
+ * Whether or not all palette entries
+ * share the same `used` value in
+ * `defaultFlag`.
+ */
+ bool sharedUsed;
+
+ /**
+ * The palette version.
+ */
+ uint32 version;
+ };
+
+ /**
+ * The version number from the last time this
+ * palette was submitted to GfxPalette32.
+ */
+ uint32 _version;
+
+ /**
+ * The number of palettes stored in the hunk
+ * palette. In SCI32 games this is always 1.
+ */
+ uint8 _numPalettes;
+
+ /**
+ * The raw palette data for this hunk palette.
+ */
+ byte *_data;
+
+ /**
+ * Returns a struct that describes the palette
+ * held by this HunkPalette. The entry header
+ * is reconstructed on every call from the raw
+ * palette data.
+ */
+ const EntryHeader getEntryHeader() const;
+
+ /**
+ * Returns a pointer to the palette data within
+ * the hunk palette.
+ */
+ byte *getPalPointer() const {
+ return _data + kHunkPaletteHeaderSize + (2 * _numPalettes);
+ }
+};
+
enum PalCyclerDirection {
PalCycleBackward = 0,
PalCycleForward = 1
@@ -72,28 +184,29 @@ struct PalCycler {
uint16 numTimesPaused;
};
-class GfxPalette32 : public GfxPalette {
+class GfxPalette32 {
public:
- GfxPalette32(ResourceManager *resMan, GfxScreen *screen);
+ GfxPalette32(ResourceManager *resMan);
~GfxPalette32();
private:
- // NOTE: currentPalette in SCI engine is called _sysPalette
- // here.
+ ResourceManager *_resMan;
/**
* The palette revision version. Increments once per game
- * loop that changes the source palette. TODO: Possibly
- * other areas also change _version, double-check once it
- * is all implemented.
+ * loop that changes the source palette.
*/
uint32 _version;
/**
- * Whether or not the palette manager version was updated
- * during this loop.
+ * Whether or not the hardware palette needs updating.
+ */
+ bool _needsUpdate;
+
+ /**
+ * The currently displayed palette.
*/
- bool _versionUpdated;
+ Palette _currentPalette;
/**
* The unmodified source palette loaded by kPalette. Additional
@@ -104,7 +217,7 @@ private:
/**
* The palette to be used when the hardware is next updated.
- * On update, _nextPalette is transferred to _sysPalette.
+ * On update, _nextPalette is transferred to _currentPalette.
*/
Palette _nextPalette;
@@ -112,24 +225,33 @@ private:
Palette getPaletteFromResourceInternal(const GuiResourceId paletteId) const;
public:
- virtual void saveLoadWithSerializer(Common::Serializer &s) override;
+ void saveLoadWithSerializer(Common::Serializer &s);
inline const Palette &getNextPalette() const { return _nextPalette; };
- inline const Palette &getCurrentPalette() const { return _sysPalette; };
+ inline const Palette &getCurrentPalette() const { return _currentPalette; };
- bool kernelSetFromResource(GuiResourceId resourceId, bool force) override;
- int16 kernelFindColor(uint16 r, uint16 g, uint16 b) override;
- void set(Palette *newPalette, bool force, bool forceRealMerge = false) override;
+ /**
+ * Loads a palette into GfxPalette32 with the given resource
+ * ID.
+ */
+ bool loadPalette(const GuiResourceId resourceId);
+
+ /**
+ * Finds the nearest color in the current palette matching the
+ * given RGB value.
+ */
+ int16 matchColor(const uint8 r, const uint8 g, const uint8 b);
/**
* Submits a palette to display. Entries marked as “used” in the
* submitted palette are merged into the existing entries of
* _sourcePalette.
*/
- void submit(Palette &palette);
+ void submit(const Palette &palette);
+ void submit(HunkPalette &palette);
bool updateForFrame();
void updateFFrame();
- void updateHardware();
+ void updateHardware(const bool updateScreen = true);
void applyAll();
#pragma mark -
@@ -213,7 +335,7 @@ public:
void kernelPalVarySetTarget(const GuiResourceId paletteId);
void kernelPalVarySetStart(const GuiResourceId paletteId);
void kernelPalVaryMergeStart(const GuiResourceId paletteId);
- virtual void kernelPalVaryPause(bool pause) override;
+ void kernelPalVaryPause(bool pause);
void setVary(const Palette *const targetPalette, const int16 percent, const int time, const int16 fromColor, const int16 toColor);
void setVaryPercent(const int16 percent, const int time, const int16 fromColor, const int16 fromColorAlternate);
diff --git a/engines/sci/graphics/plane32.cpp b/engines/sci/graphics/plane32.cpp
index 175875c414..aa629e4081 100644
--- a/engines/sci/graphics/plane32.cpp
+++ b/engines/sci/graphics/plane32.cpp
@@ -47,6 +47,7 @@ uint16 Plane::_nextObjectId = 20000;
Plane::Plane(const Common::Rect &gameRect, PlanePictureCodes pictureId) :
_pictureId(pictureId),
_mirrored(false),
+_type(kPlaneTypeColored),
_back(0),
_priorityChanged(0),
_object(make_reg(0, _nextObjectId++)),
@@ -63,6 +64,7 @@ _gameRect(gameRect) {
}
Plane::Plane(reg_t object) :
+_type(kPlaneTypeColored),
_priorityChanged(false),
_object(object),
_redrawAllCount(g_sci->_gfxFrameout->getScreenCount()),
@@ -93,6 +95,7 @@ _moved(0) {
Plane::Plane(const Plane &other) :
_pictureId(other._pictureId),
_mirrored(other._mirrored),
+_type(other._type),
_back(other._back),
_object(other._object),
_priority(other._priority),
@@ -595,8 +598,10 @@ void Plane::filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectLi
Common::Rect outRects[4];
const Common::Rect &r2 = *higherEraseList[i];
int splitCount = splitRects(r2, r, outRects);
- while (splitCount--) {
- higherEraseList.add(outRects[splitCount]);
+ if (splitCount > 0) {
+ while (splitCount--) {
+ higherEraseList.add(outRects[splitCount]);
+ }
}
higherEraseList.erase_at(i);
}
diff --git a/engines/sci/graphics/plane32.h b/engines/sci/graphics/plane32.h
index a1739264e8..3981a2b319 100644
--- a/engines/sci/graphics/plane32.h
+++ b/engines/sci/graphics/plane32.h
@@ -150,6 +150,11 @@ private:
public:
/**
+ * The type of the plane.
+ */
+ PlaneType _type;
+
+ /**
* The color to use when erasing the plane. Only
* applies to planes of type kPlaneTypeColored.
*/
@@ -186,8 +191,6 @@ public:
*/
int _redrawAllCount;
- PlaneType _type;
-
/**
* Flags indicating the state of the plane.
* - `created` is set when the plane is first created,
diff --git a/engines/sci/graphics/screen_item32.cpp b/engines/sci/graphics/screen_item32.cpp
index ebaf132890..7383dc222e 100644
--- a/engines/sci/graphics/screen_item32.cpp
+++ b/engines/sci/graphics/screen_item32.cpp
@@ -42,7 +42,8 @@ _pictureId(-1),
_created(g_sci->_gfxFrameout->getScreenCount()),
_updated(0),
_deleted(0),
-_mirrorX(false) {
+_mirrorX(false),
+_drawBlackLines(false) {
SegManager *segMan = g_sci->getEngineState()->_segMan;
setFromObject(segMan, object, true, true);
@@ -62,7 +63,8 @@ _pictureId(-1),
_created(g_sci->_gfxFrameout->getScreenCount()),
_updated(0),
_deleted(0),
-_mirrorX(false) {}
+_mirrorX(false),
+_drawBlackLines(false) {}
ScreenItem::ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Rect &rect) :
_plane(plane),
@@ -77,7 +79,8 @@ _pictureId(-1),
_created(g_sci->_gfxFrameout->getScreenCount()),
_updated(0),
_deleted(0),
-_mirrorX(false) {
+_mirrorX(false),
+_drawBlackLines(false) {
if (celInfo.type == kCelTypeColor) {
_insetRect = rect;
}
@@ -97,7 +100,8 @@ _pictureId(-1),
_created(g_sci->_gfxFrameout->getScreenCount()),
_updated(0),
_deleted(0),
-_mirrorX(false) {}
+_mirrorX(false),
+_drawBlackLines(false) {}
ScreenItem::ScreenItem(const ScreenItem &other) :
_plane(other._plane),
@@ -108,7 +112,8 @@ _celObj(nullptr),
_object(other._object),
_mirrorX(other._mirrorX),
_scaledPosition(other._scaledPosition),
-_screenRect(other._screenRect) {
+_screenRect(other._screenRect),
+_drawBlackLines(other._drawBlackLines) {
if (other._useInsetRect) {
_insetRect = other._insetRect;
}
@@ -134,6 +139,7 @@ void ScreenItem::operator=(const ScreenItem &other) {
}
_scale = other._scale;
_scaledPosition = other._scaledPosition;
+ _drawBlackLines = other._drawBlackLines;
}
ScreenItem::~ScreenItem() {
diff --git a/engines/sci/graphics/screen_item32.h b/engines/sci/graphics/screen_item32.h
index caa7a9d725..3d9d5ef3d7 100644
--- a/engines/sci/graphics/screen_item32.h
+++ b/engines/sci/graphics/screen_item32.h
@@ -64,12 +64,12 @@ private:
*/
static uint16 _nextObjectId;
+public:
/**
* The parent plane of this screen item.
*/
reg_t _plane;
-public:
/**
* Scaling data used to calculate the final screen
* dimensions of the screen item as well as the scaling
@@ -180,7 +180,7 @@ public:
* plane is a pic type and its picture resource ID has
* changed
*/
- int _created, _updated, _deleted; // ?
+ int _created, _updated, _deleted;
/**
* For screen items that represent picture cels, this
@@ -214,6 +214,14 @@ public:
Common::Rect _screenRect;
/**
+ * Whether or not the screen item should be drawn
+ * with black lines drawn every second line. This is
+ * used when pixel doubling videos to improve apparent
+ * sharpness at the cost of your eyesight.
+ */
+ bool _drawBlackLines;
+
+ /**
* Initialises static Plane members.
*/
static void init();
diff --git a/engines/sci/graphics/video32.cpp b/engines/sci/graphics/video32.cpp
new file mode 100644
index 0000000000..dfddac1036
--- /dev/null
+++ b/engines/sci/graphics/video32.cpp
@@ -0,0 +1,406 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "audio/mixer.h"
+#include "common/config-manager.h"
+#include "sci/console.h"
+#include "sci/event.h"
+#include "sci/graphics/cursor.h"
+#include "sci/graphics/frameout.h"
+#include "sci/graphics/palette32.h"
+#include "sci/graphics/text32.h"
+#include "sci/graphics/video32.h"
+#include "sci/sci.h"
+#include "video/coktel_decoder.h"
+
+namespace Sci {
+
+#pragma mark VMDPlayer
+
+VMDPlayer::VMDPlayer(SegManager *segMan, EventManager *eventMan) :
+ _segMan(segMan),
+ _eventMan(eventMan),
+ _decoder(new Video::AdvancedVMDDecoder(Audio::Mixer::kSFXSoundType)),
+
+ _isOpen(false),
+ _isInitialized(false),
+ _yieldInterval(0),
+ _lastYieldedFrameNo(0),
+
+ _plane(nullptr),
+ _screenItem(nullptr),
+ _planeIsOwned(true),
+ _priority(0),
+ _doublePixels(false),
+ _stretchVertical(false),
+ _blackLines(false),
+ _leaveScreenBlack(false),
+ _leaveLastFrame(false),
+
+ _blackoutPlane(nullptr),
+
+ _startColor(0),
+ _endColor(255),
+ _blackPalette(false),
+
+ _boostPercent(100),
+ _boostStartColor(0),
+ _boostEndColor(255),
+
+ _showCursor(false) {}
+
+VMDPlayer::~VMDPlayer() {
+ close();
+ delete _decoder;
+}
+
+#pragma mark -
+#pragma mark VMDPlayer - Playback
+
+VMDPlayer::IOStatus VMDPlayer::open(const Common::String &fileName, const OpenFlags flags) {
+ if (_isOpen) {
+ error("Attempted to play %s, but another VMD was loaded", fileName.c_str());
+ }
+
+ if (_decoder->loadFile(fileName)) {
+ if (flags & kOpenFlagMute) {
+ _decoder->setVolume(0);
+ }
+ _isOpen = true;
+ return kIOSuccess;
+ } else {
+ return kIOError;
+ }
+}
+
+void VMDPlayer::init(const int16 x, const int16 y, const PlayFlags flags, const int16 boostPercent, const int16 boostStartColor, const int16 boostEndColor) {
+ _x = getSciVersion() >= SCI_VERSION_3 ? x : (x & ~1);
+ _y = y;
+ _doublePixels = flags & kPlayFlagDoublePixels;
+ _blackLines = ConfMan.getBool("enable_black_lined_video") && (flags & kPlayFlagBlackLines);
+ _boostPercent = 100 + (flags & kPlayFlagBoost ? boostPercent : 0);
+ _boostStartColor = CLIP<int16>(boostStartColor, 0, 255);
+ _boostEndColor = CLIP<int16>(boostEndColor, 0, 255);
+ _leaveScreenBlack = flags & kPlayFlagLeaveScreenBlack;
+ _leaveLastFrame = flags & kPlayFlagLeaveLastFrame;
+ _blackPalette = flags & kPlayFlagBlackPalette;
+ _stretchVertical = flags & kPlayFlagStretchVertical;
+}
+
+VMDPlayer::IOStatus VMDPlayer::close() {
+ if (!_isOpen) {
+ return kIOSuccess;
+ }
+
+ _decoder->close();
+ _isOpen = false;
+ _isInitialized = false;
+
+ if (!_planeIsOwned && _screenItem != nullptr) {
+ g_sci->_gfxFrameout->deleteScreenItem(*_screenItem);
+ _screenItem = nullptr;
+ } else if (_plane != nullptr) {
+ g_sci->_gfxFrameout->deletePlane(*_plane);
+ _plane = nullptr;
+ }
+
+ if (!_leaveLastFrame && _leaveScreenBlack) {
+ // This call *actually* deletes the plane/screen item
+ g_sci->_gfxFrameout->frameOut(true);
+ }
+
+ if (_blackoutPlane != nullptr) {
+ g_sci->_gfxFrameout->deletePlane(*_blackoutPlane);
+ _blackoutPlane = nullptr;
+ }
+
+ if (!_leaveLastFrame && !_leaveScreenBlack) {
+ // This call *actually* deletes the blackout plane
+ g_sci->_gfxFrameout->frameOut(true);
+ }
+
+ if (!_showCursor) {
+ g_sci->_gfxCursor->kernelShow();
+ }
+
+ _lastYieldedFrameNo = 0;
+ _planeIsOwned = true;
+ _priority = 0;
+ return kIOSuccess;
+}
+
+VMDPlayer::EventFlags VMDPlayer::kernelPlayUntilEvent(const EventFlags flags, const int16 lastFrameNo, const int16 yieldInterval) {
+ assert(lastFrameNo >= -1);
+
+ const int32 maxFrameNo = (int32)(_decoder->getFrameCount() - 1);
+
+ if ((flags & kEventFlagToFrame) && lastFrameNo > 0) {
+ _decoder->setEndFrame(MIN((int32)lastFrameNo, maxFrameNo));
+ } else {
+ _decoder->setEndFrame(maxFrameNo);
+ }
+
+ if (flags & kEventFlagYieldToVM) {
+ _yieldInterval = 3;
+ if (yieldInterval == -1 && !(flags & kEventFlagToFrame)) {
+ _yieldInterval = lastFrameNo;
+ } else if (yieldInterval != -1) {
+ _yieldInterval = MIN((int32)yieldInterval, maxFrameNo);
+ }
+ } else {
+ _yieldInterval = maxFrameNo;
+ }
+
+ return playUntilEvent(flags);
+}
+
+VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags) {
+ // Flushing all the keyboard and mouse events out of the event manager to
+ // avoid letting any events queued from before the video started from
+ // accidentally activating an event callback
+ for (;;) {
+ const SciEvent event = _eventMan->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_MOUSE_PRESS | SCI_EVENT_MOUSE_RELEASE | SCI_EVENT_QUIT);
+ if (event.type == SCI_EVENT_NONE) {
+ break;
+ } else if (event.type == SCI_EVENT_QUIT) {
+ return kEventFlagEnd;
+ }
+ }
+
+ _decoder->pauseVideo(false);
+
+ if (flags & kEventFlagReverse) {
+ // NOTE: This flag may not work properly since SSCI does not care
+ // if a video has audio, but the VMD decoder does.
+ const bool success = _decoder->setReverse(true);
+ assert(success);
+ _decoder->setVolume(0);
+ }
+
+ if (!_isInitialized) {
+ _isInitialized = true;
+
+ if (!_showCursor) {
+ g_sci->_gfxCursor->kernelHide();
+ }
+
+ Common::Rect vmdRect(_x,
+ _y,
+ _x + _decoder->getWidth(),
+ _y + _decoder->getHeight());
+ ScaleInfo vmdScaleInfo;
+
+ if (!_blackoutRect.isEmpty() && _planeIsOwned) {
+ _blackoutPlane = new Plane(_blackoutRect);
+ g_sci->_gfxFrameout->addPlane(*_blackoutPlane);
+ }
+
+ if (_doublePixels) {
+ vmdScaleInfo.x = 256;
+ vmdScaleInfo.y = 256;
+ vmdScaleInfo.signal = kScaleSignalDoScaling32;
+ vmdRect.right += vmdRect.width();
+ vmdRect.bottom += vmdRect.height();
+ } else if (_stretchVertical) {
+ vmdScaleInfo.y = 256;
+ vmdScaleInfo.signal = kScaleSignalDoScaling32;
+ vmdRect.bottom += vmdRect.height();
+ }
+
+ const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
+ const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
+ const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
+
+ BitmapResource vmdBitmap(_segMan, vmdRect.width(), vmdRect.height(), 255, 0, 0, screenWidth, screenHeight, 0, false);
+
+ if (screenWidth != scriptWidth || screenHeight != scriptHeight) {
+ mulru(vmdRect, Ratio(scriptWidth, screenWidth), Ratio(scriptHeight, screenHeight), 1);
+ }
+
+ CelInfo32 vmdCelInfo;
+ vmdCelInfo.bitmap = vmdBitmap.getObject();
+ _decoder->setSurfaceMemory(vmdBitmap.getPixels(), vmdBitmap.getWidth(), vmdBitmap.getHeight(), 1);
+
+ if (_planeIsOwned) {
+ _x = 0;
+ _y = 0;
+ _plane = new Plane(vmdRect, kPlanePicColored);
+ if (_priority) {
+ _plane->_priority = _priority;
+ }
+ g_sci->_gfxFrameout->addPlane(*_plane);
+ _screenItem = new ScreenItem(_plane->_object, vmdCelInfo, Common::Point(), vmdScaleInfo);
+ } else {
+ _screenItem = new ScreenItem(_plane->_object, vmdCelInfo, Common::Point(_x, _y), vmdScaleInfo);
+ if (_priority) {
+ _screenItem->_priority = _priority;
+ }
+ }
+
+ if (_blackLines) {
+ _screenItem->_drawBlackLines = true;
+ }
+
+ // NOTE: There was code for positioning the screen item using insetRect
+ // here, but none of the game scripts seem to use this functionality.
+
+ g_sci->_gfxFrameout->addScreenItem(*_screenItem);
+
+ _decoder->start();
+ }
+
+ EventFlags stopFlag = kEventFlagNone;
+ while (!g_engine->shouldQuit()) {
+ if (_decoder->endOfVideo()) {
+ stopFlag = kEventFlagEnd;
+ break;
+ }
+
+ g_sci->getEngineState()->speedThrottler(_decoder->getTimeToNextFrame());
+ g_sci->getEngineState()->_throttleTrigger = true;
+ if (_decoder->needsUpdate()) {
+ renderFrame();
+ }
+
+ const int currentFrameNo = _decoder->getCurFrame();
+
+ if (_yieldInterval > 0 &&
+ currentFrameNo != _lastYieldedFrameNo &&
+ (currentFrameNo % _yieldInterval) == 0
+ ) {
+ _lastYieldedFrameNo = currentFrameNo;
+ stopFlag = kEventFlagYieldToVM;
+ break;
+ }
+
+ SciEvent event = _eventMan->getSciEvent(SCI_EVENT_MOUSE_PRESS | SCI_EVENT_PEEK);
+ if ((flags & kEventFlagMouseDown) && event.type == SCI_EVENT_MOUSE_PRESS) {
+ stopFlag = kEventFlagMouseDown;
+ break;
+ }
+
+ event = _eventMan->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_PEEK);
+ if ((flags & kEventFlagEscapeKey) && event.type == SCI_EVENT_KEYBOARD) {
+ bool stop = false;
+ if (getSciVersion() < SCI_VERSION_3) {
+ while ((event = _eventMan->getSciEvent(SCI_EVENT_KEYBOARD)),
+ event.type != SCI_EVENT_NONE) {
+ if (event.character == SCI_KEY_ESC) {
+ stop = true;
+ break;
+ }
+ }
+ } else {
+ stop = (event.character == SCI_KEY_ESC);
+ }
+
+ if (stop) {
+ stopFlag = kEventFlagEscapeKey;
+ break;
+ }
+ }
+
+ // TODO: Hot rectangles
+ if ((flags & kEventFlagHotRectangle) /* && event.type == SCI_EVENT_HOT_RECTANGLE */) {
+ warning("Hot rectangles not implemented in VMD player");
+ stopFlag = kEventFlagHotRectangle;
+ break;
+ }
+ }
+
+ _decoder->pauseVideo(true);
+ return stopFlag;
+}
+
+#pragma mark -
+#pragma mark VMDPlayer - Rendering
+
+void VMDPlayer::renderFrame() const {
+ // This writes directly to the CelObjMem we already created,
+ // so no need to take its return value
+ _decoder->decodeNextFrame();
+
+ // NOTE: Normally this would write a hunk palette at the end of the
+ // video bitmap that CelObjMem would read out and submit, but instead
+ // we are just submitting it directly here because the decoder exposes
+ // this information a little bit differently than the one in SSCI
+ const bool dirtyPalette = _decoder->hasDirtyPalette();
+ if (dirtyPalette) {
+ Palette palette;
+ palette.timestamp = g_sci->getTickCount();
+ if (_blackPalette) {
+ for (uint16 i = _startColor; i <= _endColor; ++i) {
+ palette.colors[i].r = palette.colors[i].g = palette.colors[i].b = 0;
+ palette.colors[i].used = true;
+ }
+ } else {
+ fillPalette(palette);
+ }
+
+ g_sci->_gfxPalette32->submit(palette);
+ g_sci->_gfxFrameout->updateScreenItem(*_screenItem);
+ g_sci->_gfxFrameout->frameOut(true);
+
+ if (_blackPalette) {
+ fillPalette(palette);
+ g_sci->_gfxPalette32->submit(palette);
+ g_sci->_gfxPalette32->updateForFrame();
+ g_sci->_gfxPalette32->updateHardware();
+ }
+ } else {
+ g_sci->_gfxFrameout->updateScreenItem(*_screenItem);
+ g_sci->getSciDebugger()->onFrame();
+ g_sci->_gfxFrameout->frameOut(true);
+ g_sci->_gfxFrameout->throttle();
+ }
+}
+
+void VMDPlayer::fillPalette(Palette &palette) const {
+ const byte *vmdPalette = _decoder->getPalette() + _startColor * 3;
+ for (uint16 i = _startColor; i <= _endColor; ++i) {
+ int16 r = *vmdPalette++;
+ int16 g = *vmdPalette++;
+ int16 b = *vmdPalette++;
+
+ if (_boostPercent != 100 && i >= _boostStartColor && i <= _boostEndColor) {
+ r = CLIP<int16>(r * _boostPercent / 100, 0, 255);
+ g = CLIP<int16>(g * _boostPercent / 100, 0, 255);
+ b = CLIP<int16>(b * _boostPercent / 100, 0, 255);
+ }
+
+ palette.colors[i].r = r;
+ palette.colors[i].g = g;
+ palette.colors[i].b = b;
+ palette.colors[i].used = true;
+ }
+}
+
+#pragma mark -
+#pragma mark VMDPlayer - Palette
+
+void VMDPlayer::restrictPalette(const uint8 startColor, const uint8 endColor) {
+ _startColor = startColor;
+ _endColor = endColor;
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/graphics/video32.h b/engines/sci/graphics/video32.h
new file mode 100644
index 0000000000..cf863ba41d
--- /dev/null
+++ b/engines/sci/graphics/video32.h
@@ -0,0 +1,312 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SCI_GRAPHICS_VIDEO32_H
+#define SCI_GRAPHICS_VIDEO32_H
+
+namespace Video { class AdvancedVMDDecoder; }
+namespace Sci {
+class Plane;
+class ScreenItem;
+class SegManager;
+
+#pragma mark VMDPlayer
+
+/**
+ * VMDPlayer is used to play VMD videos.
+ */
+class VMDPlayer {
+public:
+ enum OpenFlags {
+ kOpenFlagNone = 0,
+ kOpenFlagMute = 1
+ };
+
+ enum IOStatus {
+ kIOSuccess = 0,
+ kIOError = 0xFFFF
+ };
+
+ enum PlayFlags {
+ kPlayFlagNone = 0,
+ kPlayFlagDoublePixels = 1,
+ kPlayFlagBlackLines = 4,
+ kPlayFlagBoost = 0x10,
+ kPlayFlagLeaveScreenBlack = 0x20,
+ kPlayFlagLeaveLastFrame = 0x40,
+ kPlayFlagBlackPalette = 0x80,
+ kPlayFlagStretchVertical = 0x100
+ };
+
+ enum EventFlags {
+ kEventFlagNone = 0,
+ kEventFlagEnd = 1,
+ kEventFlagEscapeKey = 2,
+ kEventFlagMouseDown = 4,
+ kEventFlagHotRectangle = 8,
+ kEventFlagToFrame = 0x10,
+ kEventFlagYieldToVM = 0x20,
+ kEventFlagReverse = 0x80
+ };
+
+ VMDPlayer(SegManager *segMan, EventManager *eventMan);
+ ~VMDPlayer();
+
+private:
+ SegManager *_segMan;
+ EventManager *_eventMan;
+ Video::AdvancedVMDDecoder *_decoder;
+
+#pragma mark -
+#pragma mark VMDPlayer - Playback
+public:
+ /**
+ * Opens a stream to a VMD resource.
+ */
+ IOStatus open(const Common::String &fileName, const OpenFlags flags);
+
+ /**
+ * Initializes the VMD rendering parameters for the
+ * current VMD. This must be called after `open`.
+ */
+ void init(const int16 x, const int16 y, const PlayFlags flags, const int16 boostPercent, const int16 boostStartColor, const int16 boostEndColor);
+
+ /**
+ * Stops playback and closes the currently open VMD stream.
+ */
+ IOStatus close();
+
+ // NOTE: Was WaitForEvent in SSCI
+ EventFlags kernelPlayUntilEvent(const EventFlags flags, const int16 lastFrameNo, const int16 yieldInterval);
+
+private:
+ /**
+ * Whether or not a VMD stream has been opened with
+ * `open`.
+ */
+ bool _isOpen;
+
+ /**
+ * Whether or not a VMD player has been initialised
+ * with `init`.
+ */
+ bool _isInitialized;
+
+ /**
+ * For VMDs played with the `kEventFlagYieldToVM` flag,
+ * the number of frames that should be rendered until
+ * yielding back to the SCI VM.
+ */
+ int32 _yieldInterval;
+
+ /**
+ * For VMDs played with the `kEventFlagYieldToVM` flag,
+ * the last frame when control of the main thread was
+ * yielded back to the SCI VM.
+ */
+ int _lastYieldedFrameNo;
+
+ /**
+ * Plays the VMD until an event occurs (e.g. user
+ * presses escape, clicks, etc.).
+ */
+ EventFlags playUntilEvent(const EventFlags flags);
+
+#pragma mark -
+#pragma mark VMDPlayer - Rendering
+private:
+ /**
+ * The location of the VMD plane, in game script
+ * coordinates.
+ */
+ int16 _x, _y;
+
+ /**
+ * The plane where the VMD will be drawn.
+ */
+ Plane *_plane;
+
+ /**
+ * The screen item representing the VMD surface.
+ */
+ ScreenItem *_screenItem;
+
+ // TODO: planeIsOwned and priority are used in SCI3+ only
+
+ /**
+ * If true, the plane for this VMD was set
+ * externally and is not owned by this VMDPlayer.
+ */
+ bool _planeIsOwned;
+
+ /**
+ * The screen priority of the video.
+ * @see ScreenItem::_priority
+ */
+ int _priority;
+
+ /**
+ * Whether or not the video should be pixel doubled.
+ */
+ bool _doublePixels;
+
+ /**
+ * Whether or not the video should be pixel doubled
+ * vertically only.
+ */
+ bool _stretchVertical;
+
+ /**
+ * Whether or not black lines should be rendered
+ * across the video.
+ */
+ bool _blackLines;
+
+ /**
+ * Whether or not the playback area of the VMD
+ * should be left black at the end of playback.
+ */
+ bool _leaveScreenBlack;
+
+ /**
+ * Whether or not the area of the VMD should be left
+ * displaying the final frame of the video.
+ */
+ bool _leaveLastFrame;
+
+ /**
+ * Renders a frame of video to the output bitmap.
+ */
+ void renderFrame() const;
+
+ /**
+ * Fills the given palette with RGB values from
+ * the VMD palette, applying brightness boost if
+ * it is enabled.
+ */
+ void fillPalette(Palette &palette) const;
+
+#pragma mark -
+#pragma mark VMDPlayer - Blackout
+public:
+ /**
+ * Sets the area of the screen that should be blacked out
+ * during VMD playback.
+ */
+ void setBlackoutArea(const Common::Rect &rect) { _blackoutRect = rect; }
+
+private:
+ /**
+ * The dimensions of the blackout plane.
+ */
+ Common::Rect _blackoutRect;
+
+ /**
+ * An optional plane that will be used to black out
+ * areas of the screen outside of the VMD surface.
+ */
+ Plane *_blackoutPlane;
+
+#pragma mark -
+#pragma mark VMDPlayer - Palette
+public:
+ /**
+ * Restricts use of the system palette by VMD playback to
+ * the given range of palette indexes.
+ */
+ void restrictPalette(const uint8 startColor, const uint8 endColor);
+
+private:
+ /**
+ * The first color in the system palette that the VMD
+ * can write to.
+ */
+ uint8 _startColor;
+
+ /**
+ * The last color in the system palette that the VMD
+ * can write to.
+ */
+ uint8 _endColor;
+
+ /**
+ * If true, video frames are rendered after a blank
+ * palette is submitted to the palette manager,
+ * which is then restored after the video pixels
+ * have already been rendered.
+ */
+ bool _blackPalette;
+
+#pragma mark -
+#pragma mark VMDPlayer - Brightness boost
+private:
+ /**
+ * The amount of brightness boost for the video.
+ * Values above 100 increase brightness; values below
+ * 100 reduce it.
+ */
+ int16 _boostPercent;
+
+ /**
+ * The first color in the palette that should be
+ * brightness boosted.
+ */
+ uint8 _boostStartColor;
+
+ /**
+ * The last color in the palette that should be
+ * brightness boosted.
+ */
+ uint8 _boostEndColor;
+
+#pragma mark -
+#pragma mark VMDPlayer - Mouse cursor
+public:
+ /**
+ * Sets whether or not the mouse cursor should be drawn.
+ * This does not have any effect during playback, but can
+ * be used to prevent the mouse cursor from being shown
+ * again after the video has finished.
+ */
+ void setShowCursor(const bool shouldShow) { _showCursor = shouldShow; }
+
+private:
+ /**
+ * Whether or not the mouse cursor should be shown
+ * during playback.
+ */
+ bool _showCursor;
+};
+
+class Video32 {
+public:
+ Video32(SegManager *segMan, EventManager *eventMan) :
+ _VMDPlayer(segMan, eventMan) {}
+
+ VMDPlayer &getVMDPlayer() { return _VMDPlayer; }
+
+private:
+ VMDPlayer _VMDPlayer;
+};
+} // End of namespace Sci
+
+#endif
diff --git a/engines/sci/module.mk b/engines/sci/module.mk
index 5d54e2a52c..1511356747 100644
--- a/engines/sci/module.mk
+++ b/engines/sci/module.mk
@@ -91,6 +91,7 @@ MODULE_OBJS += \
graphics/remap32.o \
graphics/screen_item32.o \
graphics/text32.o \
+ graphics/video32.o \
sound/audio32.o \
sound/decoders/sol.o \
video/robot_decoder.o
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 41fa144b06..f9481bb301 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -69,7 +69,9 @@
#include "sci/graphics/palette32.h"
#include "sci/graphics/remap32.h"
#include "sci/graphics/text32.h"
+#include "sci/graphics/video32.h"
#include "sci/sound/audio32.h"
+// TODO: Move this to video32
#include "sci/video/robot_decoder.h"
#endif
@@ -92,6 +94,7 @@ SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc, SciGameId gam
_sync = nullptr;
#ifdef ENABLE_SCI32
_audio32 = nullptr;
+ _video32 = nullptr;
#endif
_features = 0;
_resMan = 0;
@@ -164,9 +167,7 @@ SciEngine::~SciEngine() {
DebugMan.clearAllDebugChannels();
#ifdef ENABLE_SCI32
- // _gfxPalette32 is the same as _gfxPalette16
- // and will be destroyed when _gfxPalette16 is
- // destroyed
+ delete _gfxPalette32;
delete _gfxControls32;
delete _gfxPaint32;
delete _gfxText32;
@@ -277,15 +278,20 @@ Common::Error SciEngine::run() {
if (getGameId() == GID_CHRISTMAS1990)
_vocabulary = new Vocabulary(_resMan, false);
+ _gamestate = new EngineState(segMan);
+ _eventMan = new EventManager(_resMan->detectFontExtended());
#ifdef ENABLE_SCI32
if (getSciVersion() >= SCI_VERSION_2_1_EARLY) {
_audio32 = new Audio32(_resMan);
} else
#endif
_audio = new AudioPlayer(_resMan);
+#ifdef ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2) {
+ _video32 = new Video32(segMan, _eventMan);
+ }
+#endif
_sync = new Sync(_resMan, segMan);
- _gamestate = new EngineState(segMan);
- _eventMan = new EventManager(_resMan->detectFontExtended());
// Create debugger console. It requires GFX and _gamestate to be initialized
_console = new Console(this);
@@ -358,6 +364,17 @@ Common::Error SciEngine::run() {
}
}
+ if (getGameId() == GID_KQ7 && ConfMan.getBool("subtitles")) {
+ showScummVMDialog("Subtitles are enabled, but subtitling in King's"
+ " Quest 7 was unfinished and disabled in the release"
+ " version of the game. ScummVM allows the subtitles"
+ " to be re-enabled, but because they were removed from"
+ " the original game, they do not always render"
+ " properly or reflect the actual game speech."
+ " This is not a ScummVM bug -- it is a problem with"
+ " the game's assets.");
+ }
+
// Show a warning if the user has selected a General MIDI device, no GM patch exists
// (i.e. patch 4) and the game is one of the known 8 SCI1 games that Sierra has provided
// after market patches for in their "General MIDI Utility".
@@ -698,8 +715,7 @@ void SciEngine::initGraphics() {
#ifdef ENABLE_SCI32
if (getSciVersion() >= SCI_VERSION_2) {
- _gfxPalette32 = new GfxPalette32(_resMan, _gfxScreen);
- _gfxPalette16 = _gfxPalette32;
+ _gfxPalette32 = new GfxPalette32(_resMan);
_gfxRemap32 = new GfxRemap32();
} else {
#endif
@@ -748,8 +764,10 @@ void SciEngine::initGraphics() {
}
#endif
- // Set default (EGA, amiga or resource 999) palette
- _gfxPalette16->setDefault();
+ if (getSciVersion() < SCI_VERSION_2) {
+ // Set default (EGA, amiga or resource 999) palette
+ _gfxPalette16->setDefault();
+ }
}
void SciEngine::initStackBaseWithSelector(Selector selector) {
diff --git a/engines/sci/sci.h b/engines/sci/sci.h
index 3216fa13b9..0020d25b91 100644
--- a/engines/sci/sci.h
+++ b/engines/sci/sci.h
@@ -80,9 +80,11 @@ class GfxText32;
class GfxTransitions;
#ifdef ENABLE_SCI32
+// TODO: Move RobotDecoder to Video32
class RobotDecoder;
class GfxFrameout;
class Audio32;
+class Video32;
#endif
// our engine debug levels
@@ -138,6 +140,7 @@ enum SciGameId {
GID_HOYLE2,
GID_HOYLE3,
GID_HOYLE4,
+ GID_HOYLE5,
GID_ICEMAN,
GID_INNDEMO,
GID_ISLANDBRAIN,
@@ -371,6 +374,7 @@ public:
#ifdef ENABLE_SCI32
Audio32 *_audio32;
+ Video32 *_video32;
RobotDecoder *_robotDecoder;
GfxFrameout *_gfxFrameout; // kFrameout and the like for 32-bit gfx
#endif
diff --git a/engines/sci/sound/audio32.cpp b/engines/sci/sound/audio32.cpp
index 4689d13d7d..0cf8e3cb13 100644
--- a/engines/sci/sound/audio32.cpp
+++ b/engines/sci/sound/audio32.cpp
@@ -124,10 +124,10 @@ Audio32::Audio32(ResourceManager *resMan) :
_attenuatedMixing(true),
- _monitoredChannelIndex(-1),
- _monitoredBuffer(nullptr),
- _monitoredBufferSize(0),
- _numMonitoredSamples(0) {
+ _monitoredChannelIndex(-1),
+ _monitoredBuffer(nullptr),
+ _monitoredBufferSize(0),
+ _numMonitoredSamples(0) {
if (getSciVersion() < SCI_VERSION_3) {
_channels.resize(5);
@@ -146,19 +146,13 @@ Audio32::Audio32(ResourceManager *resMan) :
default:
break;
}
- } else if (getSciVersion() == SCI_VERSION_2_1_EARLY) {
- switch (g_sci->getGameId()) {
- // 1.51 uses the non-standard attenuation, but 2.00b
+ } else if (getSciVersion() == SCI_VERSION_2_1_EARLY && g_sci->getGameId() == GID_KQ7) {
+ // KQ7 1.51 uses the non-standard attenuation, but 2.00b
// does not, which is strange.
- case GID_KQ7:
- _useModifiedAttenuation = true;
- default:
- break;
- }
+ _useModifiedAttenuation = true;
}
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_handle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
- _mixer->pauseHandle(_handle, true);
}
Audio32::~Audio32() {
@@ -240,16 +234,16 @@ int Audio32::writeAudioInternal(Audio::RewindableAudioStream *const sourceStream
int Audio32::readBuffer(Audio::st_sample_t *buffer, const int numSamples) {
Common::StackLock lock(_mutex);
+ if (_pausedAtTick != 0 || _numActiveChannels == 0) {
+ return 0;
+ }
+
// ResourceManager is not thread-safe so we need to
// avoid calling into it from the audio thread, but at
// the same time we need to be able to clear out any
// finished channels on a regular basis
_inAudioThread = true;
- // The system mixer should not try to get data when
- // Audio32 is paused
- assert(_pausedAtTick == 0 && _numActiveChannels > 0);
-
freeUnusedChannels();
// The caller of `readBuffer` is a rate converter,
@@ -359,6 +353,16 @@ int Audio32::readBuffer(Audio::st_sample_t *buffer, const int numSamples) {
maxSamplesWritten = _numMonitoredSamples;
}
} else if (!channel.stream->endOfStream() || channel.loop) {
+ if (_monitoredChannelIndex != -1) {
+ // Audio that is not on the monitored channel is silent
+ // when the monitored channel is active, but the stream still
+ // needs to be read in order to ensure that sound effects sync
+ // up once the monitored channel is turned off. The easiest
+ // way to guarantee this is to just do the normal channel read,
+ // but set the channel volume to zero so nothing is mixed in
+ leftVolume = rightVolume = 0;
+ }
+
const int channelSamplesWritten = writeAudioInternal(channel.stream, channel.converter, buffer, numSamples, leftVolume, rightVolume, channel.loop);
if (channelSamplesWritten > maxSamplesWritten) {
maxSamplesWritten = channelSamplesWritten;
@@ -652,7 +656,6 @@ uint16 Audio32::play(int16 channelIndex, const ResourceId resourceId, const bool
if (_numActiveChannels == 1) {
_startedAtTick = now;
- _mixer->pauseHandle(_handle, false);
}
return channel.duration;
@@ -683,7 +686,6 @@ bool Audio32::resume(const int16 channelIndex) {
_startedAtTick += now - _pausedAtTick;
_pausedAtTick = 0;
- _mixer->pauseHandle(_handle, false);
return true;
} else if (channelIndex == kRobotChannel) {
for (int i = 0; i < _numActiveChannels; ++i) {
@@ -720,7 +722,6 @@ bool Audio32::pause(const int16 channelIndex) {
if (channelIndex == kAllChannels) {
if (_pausedAtTick == 0) {
_pausedAtTick = now;
- _mixer->pauseHandle(_handle, true);
didPause = true;
}
} else if (channelIndex == kRobotChannel) {
@@ -773,9 +774,6 @@ int16 Audio32::stop(const int16 channelIndex) {
// NOTE: SSCI stops the DSP interrupt and frees the
// global decompression buffer here if there are no
// more active channels
- if (_numActiveChannels == 0) {
- _mixer->pauseHandle(_handle, true);
- }
return oldNumChannels;
}
@@ -891,17 +889,15 @@ reg_t Audio32::kernelPlay(const bool autoPlay, const int argc, const reg_t *cons
#pragma mark Effects
int16 Audio32::getVolume(const int16 channelIndex) const {
- Common::StackLock lock(_mutex);
if (channelIndex < 0 || channelIndex >= _numActiveChannels) {
return _mixer->getChannelVolume(_handle) * kMaxVolume / Audio::Mixer::kMaxChannelVolume;
}
+ Common::StackLock lock(_mutex);
return getChannel(channelIndex).volume;
}
void Audio32::setVolume(const int16 channelIndex, int16 volume) {
- Common::StackLock lock(_mutex);
-
volume = MIN((int16)kMaxVolume, volume);
if (channelIndex == kAllChannels) {
ConfMan.setInt("sfx_volume", volume * Audio::Mixer::kMaxChannelVolume / kMaxVolume);
@@ -909,6 +905,7 @@ void Audio32::setVolume(const int16 channelIndex, int16 volume) {
_mixer->setChannelVolume(_handle, volume * Audio::Mixer::kMaxChannelVolume / kMaxVolume);
g_engine->syncSoundSettings();
} else if (channelIndex != kNoExistingChannel) {
+ Common::StackLock lock(_mutex);
getChannel(channelIndex).volume = volume;
}
}
diff --git a/engines/sci/sound/audio32.h b/engines/sci/sound/audio32.h
index 42211eb890..b0c1ba1998 100644
--- a/engines/sci/sound/audio32.h
+++ b/engines/sci/sound/audio32.h
@@ -108,7 +108,7 @@ struct AudioChannel {
/**
* The end volume of a fade.
*/
- uint32 fadeTargetVolume;
+ int fadeTargetVolume;
/**
* Whether or not the channel should be stopped and
diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp
index 3f34ecc2f8..487d30e840 100644
--- a/engines/sci/sound/music.cpp
+++ b/engines/sci/sound/music.cpp
@@ -354,12 +354,14 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) {
pSnd->pStreamAud = Audio::makeRawStream(channelData + track->digitalSampleStart,
track->digitalSampleSize - track->digitalSampleStart - endPart,
track->digitalSampleRate, flags, DisposeAfterUse::NO);
+ assert(pSnd->pStreamAud);
delete pSnd->pLoopStream;
pSnd->pLoopStream = 0;
pSnd->soundType = Audio::Mixer::kSFXSoundType;
pSnd->hCurrentAud = Audio::SoundHandle();
pSnd->playBed = false;
pSnd->overridePriority = false;
+ pSnd->isSample = true;
} else {
// play MIDI track
Common::StackLock lock(_mutex);
@@ -659,6 +661,7 @@ void SciMusic::soundKill(MusicEntry *pSnd) {
pSnd->pStreamAud = NULL;
delete pSnd->pLoopStream;
pSnd->pLoopStream = 0;
+ pSnd->isSample = false;
}
_mutex.lock();
diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp
index c3c159de55..b9a764c93a 100644
--- a/engines/sci/sound/soundcmd.cpp
+++ b/engines/sci/sound/soundcmd.cpp
@@ -722,7 +722,7 @@ reg_t SoundCommandParser::kDoSoundSetVolume(int argc, reg_t *argv, reg_t acc) {
// SSCI unconditionally sets volume if it is digital audio
if (_soundVersion >= SCI_VERSION_2_1_EARLY && musicSlot->isSample) {
_music->soundSetVolume(musicSlot, value);
- } else
+ }
#endif
if (musicSlot->volume != value) {
musicSlot->volume = value;
diff --git a/graphics/VectorRenderer.cpp b/graphics/VectorRenderer.cpp
index f426dd8c41..73dc6309b2 100644
--- a/graphics/VectorRenderer.cpp
+++ b/graphics/VectorRenderer.cpp
@@ -55,7 +55,34 @@ void VectorRenderer::drawStep(const Common::Rect &area, const DrawStep &step, ui
_dynamicData = extra;
- (this->*(step.drawingCall))(area, step);
+ Common::Rect noClip = Common::Rect(0, 0, 0, 0);
+ (this->*(step.drawingCall))(area, step, noClip);
+}
+
+void VectorRenderer::drawStepClip(const Common::Rect &area, const Common::Rect &clip, const DrawStep &step, uint32 extra) {
+
+ if (step.bgColor.set)
+ setBgColor(step.bgColor.r, step.bgColor.g, step.bgColor.b);
+
+ if (step.fgColor.set)
+ setFgColor(step.fgColor.r, step.fgColor.g, step.fgColor.b);
+
+ if (step.bevelColor.set)
+ setBevelColor(step.bevelColor.r, step.bevelColor.g, step.bevelColor.b);
+
+ if (step.gradColor1.set && step.gradColor2.set)
+ setGradientColors(step.gradColor1.r, step.gradColor1.g, step.gradColor1.b,
+ step.gradColor2.r, step.gradColor2.g, step.gradColor2.b);
+
+ setShadowOffset(_disableShadows ? 0 : step.shadow);
+ setBevel(step.bevel);
+ setGradientFactor(step.factor);
+ setStrokeWidth(step.stroke);
+ setFillMode((FillMode)step.fillMode);
+
+ _dynamicData = extra;
+
+ (this->*(step.drawingCall))(area, step, clip);
}
int VectorRenderer::stepGetRadius(const DrawStep &step, const Common::Rect &area) {
diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h
index 6b657f758d..0352808706 100644
--- a/graphics/VectorRenderer.h
+++ b/graphics/VectorRenderer.h
@@ -38,7 +38,7 @@ class VectorRenderer;
struct DrawStep;
-typedef void (VectorRenderer::*DrawingFunctionCallback)(const Common::Rect &, const Graphics::DrawStep &);
+typedef void (VectorRenderer::*DrawingFunctionCallback)(const Common::Rect &, const Graphics::DrawStep &, const Common::Rect &);
struct DrawStep {
@@ -142,6 +142,7 @@ public:
* @param y2 Vertical (Y) coordinate for the line end
*/
virtual void drawLine(int x1, int y1, int x2, int y2) = 0;
+ virtual void drawLineClip(int x1, int y1, int x2, int y2, Common::Rect clipping) = 0;
/**
* Draws a circle centered at (x,y) with radius r.
@@ -151,6 +152,7 @@ public:
* @param r Radius of the circle.
*/
virtual void drawCircle(int x, int y, int r) = 0;
+ virtual void drawCircleClip(int x, int y, int r, Common::Rect clipping) = 0;
/**
* Draws a square starting at (x,y) with the given width and height.
@@ -161,6 +163,7 @@ public:
* @param h Height of the square
*/
virtual void drawSquare(int x, int y, int w, int h) = 0;
+ virtual void drawSquareClip(int x, int y, int w, int h, Common::Rect clipping) = 0;
/**
* Draws a rounded square starting at (x,y) with the given width and height.
@@ -173,6 +176,7 @@ public:
* @param r Radius of the corners.
*/
virtual void drawRoundedSquare(int x, int y, int r, int w, int h) = 0;
+ virtual void drawRoundedSquareClip(int x, int y, int r, int w, int h, Common::Rect clipping) = 0;
/**
* Draws a triangle starting at (x,y) with the given base and height.
@@ -186,6 +190,7 @@ public:
* @param orient Orientation of the triangle.
*/
virtual void drawTriangle(int x, int y, int base, int height, TriangleOrientation orient) = 0;
+ virtual void drawTriangleClip(int x, int y, int base, int height, TriangleOrientation orient, Common::Rect clipping) = 0;
/**
* Draws a beveled square like the ones in the Classic GUI themes.
@@ -199,6 +204,7 @@ public:
* @param bevel Amount of bevel. Must be positive.
*/
virtual void drawBeveledSquare(int x, int y, int w, int h, int bevel) = 0;
+ virtual void drawBeveledSquareClip(int x, int y, int w, int h, int bevel, Common::Rect clipping) = 0;
/**
* Draws a tab-like shape, specially thought for the Tab widget.
@@ -212,6 +218,7 @@ public:
* @param r Radius of the corners of the tab (0 for squared tabs).
*/
virtual void drawTab(int x, int y, int r, int w, int h) = 0;
+ virtual void drawTabClip(int x, int y, int r, int w, int h, Common::Rect clipping) = 0;
/**
@@ -222,6 +229,11 @@ public:
drawLine(x + w, y, x, y + h);
}
+ virtual void drawCrossClip(int x, int y, int w, int h, Common::Rect clipping) {
+ drawLineClip(x, y, x + w, y + w, clipping);
+ drawLineClip(x + w, y, x, y + h, clipping);
+ }
+
/**
* Set the active foreground painting color for the renderer.
* All the foreground drawing from then on will be done with that color, unless
@@ -278,6 +290,7 @@ public:
* Defaults to using the active Foreground color for filling.
*/
virtual void fillSurface() = 0;
+ virtual void fillSurfaceClip(Common::Rect clipping) = 0;
/**
* Clears the active surface.
@@ -355,68 +368,68 @@ public:
/**
* DrawStep callback functions for each drawing feature
*/
- void drawCallback_CIRCLE(const Common::Rect &area, const DrawStep &step) {
+ void drawCallback_CIRCLE(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
uint16 x, y, w, h, radius;
radius = stepGetRadius(step, area);
stepGetPositions(step, area, x, y, w, h);
- drawCircle(x + radius, y + radius, radius);
+ drawCircleClip(x + radius, y + radius, radius, clip);
}
- void drawCallback_SQUARE(const Common::Rect &area, const DrawStep &step) {
+ void drawCallback_SQUARE(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
uint16 x, y, w, h;
stepGetPositions(step, area, x, y, w, h);
- drawSquare(x, y, w, h);
+ drawSquareClip(x, y, w, h, clip);
}
- void drawCallback_LINE(const Common::Rect &area, const DrawStep &step) {
+ void drawCallback_LINE(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
uint16 x, y, w, h;
stepGetPositions(step, area, x, y, w, h);
- drawLine(x, y, x + w, y + w);
+ drawLineClip(x, y, x + w, y + w, clip);
}
- void drawCallback_ROUNDSQ(const Common::Rect &area, const DrawStep &step) {
+ void drawCallback_ROUNDSQ(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
uint16 x, y, w, h;
stepGetPositions(step, area, x, y, w, h);
- drawRoundedSquare(x, y, stepGetRadius(step, area), w, h);
+ drawRoundedSquareClip(x, y, stepGetRadius(step, area), w, h, clip);
}
- void drawCallback_FILLSURFACE(const Common::Rect &area, const DrawStep &step) {
- fillSurface();
+ void drawCallback_FILLSURFACE(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
+ fillSurfaceClip(clip);
}
- void drawCallback_TRIANGLE(const Common::Rect &area, const DrawStep &step) {
+ void drawCallback_TRIANGLE(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
uint16 x, y, w, h;
stepGetPositions(step, area, x, y, w, h);
- drawTriangle(x, y, w, h, (TriangleOrientation)step.extraData);
+ drawTriangleClip(x, y, w, h, (TriangleOrientation)step.extraData, clip);
}
- void drawCallback_BEVELSQ(const Common::Rect &area, const DrawStep &step) {
+ void drawCallback_BEVELSQ(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
uint16 x, y, w, h;
stepGetPositions(step, area, x, y, w, h);
- drawBeveledSquare(x, y, w, h, _bevel);
+ drawBeveledSquareClip(x, y, w, h, _bevel, clip);
}
- void drawCallback_TAB(const Common::Rect &area, const DrawStep &step) {
+ void drawCallback_TAB(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
uint16 x, y, w, h;
stepGetPositions(step, area, x, y, w, h);
- drawTab(x, y, stepGetRadius(step, area), w, h);
+ drawTabClip(x, y, stepGetRadius(step, area), w, h, clip);
}
- void drawCallback_BITMAP(const Common::Rect &area, const DrawStep &step) {
+ void drawCallback_BITMAP(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
uint16 x, y, w, h;
stepGetPositions(step, area, x, y, w, h);
- blitAlphaBitmap(step.blitSrc, Common::Rect(x, y, x + w, y + h));
+ blitAlphaBitmapClip(step.blitSrc, Common::Rect(x, y, x + w, y + h), clip);
}
- void drawCallback_CROSS(const Common::Rect &area, const DrawStep &step) {
+ void drawCallback_CROSS(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
uint16 x, y, w, h;
stepGetPositions(step, area, x, y, w, h);
- drawCross(x, y, w, h);
+ drawCrossClip(x, y, w, h, clip);
}
- void drawCallback_VOID(const Common::Rect &area, const DrawStep &step) {}
+ void drawCallback_VOID(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {}
/**
* Draws the specified draw step on the screen.
@@ -426,6 +439,7 @@ public:
* @param step Pointer to a DrawStep struct.
*/
virtual void drawStep(const Common::Rect &area, const DrawStep &step, uint32 extra = 0);
+ virtual void drawStepClip(const Common::Rect &area, const Common::Rect &clip, const DrawStep &step, uint32 extra = 0);
/**
* Copies the part of the current frame to the system overlay.
@@ -466,8 +480,10 @@ public:
* blitted into the active surface, at the position specified by "r".
*/
virtual void blitSubSurface(const Graphics::Surface *source, const Common::Rect &r) = 0;
+ virtual void blitSubSurfaceClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping) = 0;
virtual void blitAlphaBitmap(const Graphics::Surface *source, const Common::Rect &r) = 0;
+ virtual void blitAlphaBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping) = 0;
/**
* Draws a string into the screen. Wrapper for the Graphics::Font string drawing
diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp
index 258d935440..fc741f6e77 100644
--- a/graphics/VectorRendererSpec.cpp
+++ b/graphics/VectorRendererSpec.cpp
@@ -108,6 +108,33 @@ inline frac_t fp_sqroot(uint32 x) {
BE_DRAWCIRCLE_BOTTOM(ptr3,ptr4,x,y,px,py); \
} while (0)
+#define BE_DRAWCIRCLE_TOP_CLIP(ptr1,ptr2,x,y,px,py,realX1,realY1,realX2,realY2) do { \
+ if (IS_IN_CLIP((realX1) + (y), (realY1) - (x))) \
+ *(ptr1 + (y) - (px)) = color; \
+ if (IS_IN_CLIP((realX1) + (x), (realY1) - (y))) \
+ *(ptr1 + (x) - (py)) = color; \
+ if (IS_IN_CLIP((realX2) - (x), (realY2) - (y))) \
+ *(ptr2 - (x) - (py)) = color; \
+ if (IS_IN_CLIP((realX2) - (y), (realY2) - (x))) \
+ *(ptr2 - (y) - (px)) = color; \
+} while (0)
+
+#define BE_DRAWCIRCLE_BOTTOM_CLIP(ptr3,ptr4,x,y,px,py,realX3,realY3,realX4,realY4) do { \
+ if (IS_IN_CLIP((realX3) - (y), (realY3) + (x))) \
+ *(ptr3 - (y) + (px)) = color; \
+ if (IS_IN_CLIP((realX3) - (x), (realY3) + (y))) \
+ *(ptr3 - (x) + (py)) = color; \
+ if (IS_IN_CLIP((realX4) + (x), (realY4) + (y))) \
+ *(ptr4 + (x) + (py)) = color; \
+ if (IS_IN_CLIP((realX4) + (y), (realY4) + (x))) \
+ *(ptr4 + (y) + (px)) = color; \
+} while (0)
+
+#define BE_DRAWCIRCLE_CLIP(ptr1,ptr2,ptr3,ptr4,x,y,px,py,realX1,realY1,realX2,realY2,realX3,realY3,realX4,realY4) do { \
+ BE_DRAWCIRCLE_TOP_CLIP(ptr1,ptr2,x,y,px,py,realX1,realY1,realX2,realY2); \
+ BE_DRAWCIRCLE_BOTTOM_CLIP(ptr3,ptr4,x,y,px,py,realX3,realY3,realX4,realY4); \
+} while (0)
+
#define BE_DRAWCIRCLE_BCOLOR(ptr1,ptr2,ptr3,ptr4,x,y,px,py) do { \
*(ptr1 + (y) - (px)) = color1; \
*(ptr1 + (x) - (py)) = color1; \
@@ -119,6 +146,25 @@ inline frac_t fp_sqroot(uint32 x) {
*(ptr4 + (y) + (px)) = color2; \
} while (0)
+#define BE_DRAWCIRCLE_BCOLOR_CLIP(ptr1,ptr2,ptr3,ptr4,x,y,px,py,realX1,realY1,realX2,realY2,realX3,realY3,realX4,realY4) do { \
+ if (IS_IN_CLIP((realX1) + (y), (realY1) - (x))) \
+ *(ptr1 + (y) - (px)) = color1; \
+ if (IS_IN_CLIP((realX1) + (x), (realY1) - (y))) \
+ *(ptr1 + (x) - (py)) = color1; \
+ if (IS_IN_CLIP((realX2) - (x), (realY2) - (y))) \
+ *(ptr2 - (x) - (py)) = color1; \
+ if (IS_IN_CLIP((realX2) - (y), (realY2) - (x))) \
+ *(ptr2 - (y) - (px)) = color1; \
+ if (IS_IN_CLIP((realX3) - (y), (realY3) + (x))) \
+ *(ptr3 - (y) + (px)) = color1; \
+ if (IS_IN_CLIP((realX3) - (x), (realY3) + (y))) \
+ *(ptr3 - (x) + (py)) = color1; \
+ if (IS_IN_CLIP((realX4) + (x), (realY4) + (y))) \
+ *(ptr4 + (x) + (py)) = color2; \
+ if (IS_IN_CLIP((realX4) + (y), (realY4) + (x))) \
+ *(ptr4 + (y) + (px)) = color2; \
+} while (0)
+
#define BE_DRAWCIRCLE_BCOLOR_TR_CW(ptr,x,y,px,py,a) do { \
this->blendPixelPtr(ptr + (y) - (px), color, a); \
} while (0)
@@ -127,6 +173,16 @@ inline frac_t fp_sqroot(uint32 x) {
this->blendPixelPtr(ptr + (x) - (py), color, a); \
} while (0)
+#define BE_DRAWCIRCLE_BCOLOR_TR_CW_CLIP(ptr,x,y,px,py,a,realX,realY) do { \
+ if (IS_IN_CLIP((realX) + (y), (realY) - (x))) \
+ this->blendPixelPtr(ptr + (y) - (px), color, a); \
+} while (0)
+
+#define BE_DRAWCIRCLE_BCOLOR_TR_CCW_CLIP(ptr,x,y,px,py,a,realX,realY) do { \
+ if (IS_IN_CLIP((realX) + (x), (realY) - (y))) \
+ this->blendPixelPtr(ptr + (x) - (py), color, a); \
+} while (0)
+
#define BE_DRAWCIRCLE_BCOLOR_TL_CW(ptr,x,y,px,py,a) do { \
this->blendPixelPtr(ptr - (x) - (py), color, a); \
} while (0)
@@ -135,6 +191,16 @@ inline frac_t fp_sqroot(uint32 x) {
this->blendPixelPtr(ptr - (y) - (px), color, a); \
} while (0)
+#define BE_DRAWCIRCLE_BCOLOR_TL_CW_CLIP(ptr,x,y,px,py,a,realX,realY) do { \
+ if (IS_IN_CLIP((realX) - (x), (realY) - (y))) \
+ this->blendPixelPtr(ptr - (x) - (py), color, a); \
+} while (0)
+
+#define BE_DRAWCIRCLE_BCOLOR_TL_CCW_CLIP(ptr,x,y,px,py,a,realX,realY) do { \
+ if (IS_IN_CLIP((realX) - (y), (realY) - (x))) \
+ this->blendPixelPtr(ptr - (y) - (px), color, a); \
+} while (0)
+
#define BE_DRAWCIRCLE_BCOLOR_BL_CW(ptr,x,y,px,py,a) do { \
this->blendPixelPtr(ptr - (y) + (px), color, a); \
} while (0)
@@ -143,6 +209,16 @@ inline frac_t fp_sqroot(uint32 x) {
this->blendPixelPtr(ptr - (x) + (py), color, a); \
} while (0)
+#define BE_DRAWCIRCLE_BCOLOR_BL_CW_CLIP(ptr,x,y,px,py,a,realX,realY) do { \
+ if (IS_IN_CLIP((realX) - (y), (realY) + (x))) \
+ this->blendPixelPtr(ptr - (y) + (px), color, a); \
+} while (0)
+
+#define BE_DRAWCIRCLE_BCOLOR_BL_CCW_CLIP(ptr,x,y,px,py,a,realX,realY) do { \
+ if (IS_IN_CLIP((realX) - (x), (realY) + (y))) \
+ this->blendPixelPtr(ptr - (x) + (py), color, a); \
+} while (0)
+
#define BE_DRAWCIRCLE_BCOLOR_BR_CW(ptr,x,y,px,py,a) do { \
this->blendPixelPtr(ptr + (x) + (py), color, a); \
} while (0)
@@ -151,6 +227,16 @@ inline frac_t fp_sqroot(uint32 x) {
this->blendPixelPtr(ptr + (y) + (px), color, a); \
} while (0)
+#define BE_DRAWCIRCLE_BCOLOR_BR_CW_CLIP(ptr,x,y,px,py,a,realX,realY) do { \
+ if (IS_IN_CLIP((realX) + (x), (realY) + (y))) \
+ this->blendPixelPtr(ptr + (x) + (py), color, a); \
+} while (0)
+
+#define BE_DRAWCIRCLE_BCOLOR_BR_CCW_CLIP(ptr,x,y,px,py,a,realX,realY) do { \
+ if (IS_IN_CLIP((realX) + (y), (realY) + (x))) \
+ this->blendPixelPtr(ptr + (y) + (px), color, a); \
+} while (0)
+
#define BE_DRAWCIRCLE_XCOLOR_TOP(ptr1,ptr2,x,y,px,py) do { \
*(ptr1 + (y) - (px)) = color1; \
*(ptr1 + (x) - (py)) = color2; \
@@ -170,6 +256,42 @@ inline frac_t fp_sqroot(uint32 x) {
BE_DRAWCIRCLE_XCOLOR_BOTTOM(ptr3,ptr4,x,y,px,py); \
} while (0)
+#define IS_IN_CLIP(x,y) (_clippingArea.left <= (x) && (x) < _clippingArea.right \
+ && _clippingArea.top <= (y) && (y) < _clippingArea.bottom)
+
+#define BE_DRAWCIRCLE_XCOLOR_TOP_CLIP(ptr1,ptr2,x,y,px,py,realX1,realY1,realX2,realY2) do { \
+ if (IS_IN_CLIP((realX1) + (y), (realY1) - (x))) \
+ *(ptr1 + (y) - (px)) = color1; \
+\
+ if (IS_IN_CLIP((realX1) + (x), (realY1) - (y))) \
+ *(ptr1 + (x) - (py)) = color2; \
+\
+ if (IS_IN_CLIP((realX2) - (x), (realY2) - (y))) \
+ *(ptr2 - (x) - (py)) = color2; \
+\
+ if (IS_IN_CLIP((realX2) - (y), (realY2) - (x))) \
+ *(ptr2 - (y) - (px)) = color1; \
+} while (0)
+
+#define BE_DRAWCIRCLE_XCOLOR_BOTTOM_CLIP(ptr3,ptr4,x,y,px,py,realX3,realY3,realX4,realY4) do { \
+ if (IS_IN_CLIP((realX3) - (y), (realY3) + (x))) \
+ *(ptr3 - (y) + (px)) = color3; \
+\
+ if (IS_IN_CLIP((realX3) - (x), (realY3) + (y))) \
+ *(ptr3 - (x) + (py)) = color4; \
+\
+ if (IS_IN_CLIP((realX4) + (x), (realY4) + (y))) \
+ *(ptr4 + (x) + (py)) = color4; \
+\
+ if (IS_IN_CLIP((realX4) + (y), (realY4) + (x))) \
+ *(ptr4 + (y) + (px)) = color3; \
+} while (0)
+
+#define BE_DRAWCIRCLE_XCOLOR_CLIP(ptr1,ptr2,ptr3,ptr4,x,y,px,py,realX1,realY1,realX2,realY2,realX3,realY3,realX4,realY4) do { \
+ BE_DRAWCIRCLE_XCOLOR_TOP_CLIP(ptr1,ptr2,x,y,px,py,realX1,realY1,realX2,realY2); \
+ BE_DRAWCIRCLE_XCOLOR_BOTTOM_CLIP(ptr3,ptr4,x,y,px,py,realX3,realY3,realX4,realY4); \
+} while (0)
+
#define BE_RESET() do { \
f = 1 - r; \
@@ -337,6 +459,45 @@ void colorFill(PixelType *first, PixelType *last, PixelType color) {
}
}
+template<typename PixelType>
+void colorFillClip(PixelType *first, PixelType *last, PixelType color, int realX, int realY, Common::Rect &clippingArea) {
+ if (realY < clippingArea.top || realY >= clippingArea.bottom)
+ return;
+
+ register int count = (last - first);
+
+ if (realX > clippingArea.right || realX + count < clippingArea.left)
+ return;
+
+ if (realX < clippingArea.left) {
+ register int diff = (clippingArea.left - realX);
+ realX += diff;
+ count -= diff;
+ }
+
+ if (clippingArea.right <= realX + count) {
+ register int diff = (realX + count - clippingArea.right);
+ count -= diff;
+ }
+
+ if (!count)
+ return;
+
+ register int n = (count + 7) >> 3;
+ switch (count % 8) {
+ case 0: do {
+ *first++ = color;
+ case 7: *first++ = color;
+ case 6: *first++ = color;
+ case 5: *first++ = color;
+ case 4: *first++ = color;
+ case 3: *first++ = color;
+ case 2: *first++ = color;
+ case 1: *first++ = color;
+ } while (--n > 0);
+ }
+}
+
VectorRenderer *createRenderer(int mode) {
#ifdef DISABLE_FANCY_THEMES
@@ -376,6 +537,7 @@ VectorRendererSpec(PixelFormat format) :
_alphaMask((0xFF >> format.aLoss) << format.aShift) {
_bitmapAlphaColor = _format.RGBToColor(255, 0, 255);
+ _clippingArea = Common::Rect(0, 0, 32767, 32767);
}
/****************************
@@ -480,6 +642,49 @@ gradientFill(PixelType *ptr, int width, int x, int y) {
template<typename PixelType>
void VectorRendererSpec<PixelType>::
+gradientFillClip(PixelType *ptr, int width, int x, int y, int realX, int realY) {
+ if (realY < _clippingArea.top || realY >= _clippingArea.bottom) return;
+ bool ox = ((y & 1) == 1);
+ int curGrad = 0;
+
+ while (_gradIndexes[curGrad + 1] <= y)
+ curGrad++;
+
+ // precalcGradient assures that _gradIndexes entries always differ in
+ // their value. This assures stripSize is always different from zero.
+ int stripSize = _gradIndexes[curGrad + 1] - _gradIndexes[curGrad];
+
+ int grad = (((y - _gradIndexes[curGrad]) % stripSize) << 2) / stripSize;
+
+ // Dithering:
+ // +--+ +--+ +--+ +--+
+ // | | | | | *| | *|
+ // | | | *| |* | |**|
+ // +--+ +--+ +--+ +--+
+ // 0 1 2 3
+ if (grad == 0 ||
+ _gradCache[curGrad] == _gradCache[curGrad + 1] || // no color change
+ stripSize < 2) { // the stip is small
+ colorFill<PixelType>(ptr, ptr + width, _gradCache[curGrad]);
+ } else if (grad == 3 && ox) {
+ colorFill<PixelType>(ptr, ptr + width, _gradCache[curGrad + 1]);
+ } else {
+ for (int j = x; j < x + width; j++, ptr++) {
+ if (realX + j - x < _clippingArea.left || realX + j - x >= _clippingArea.right) continue;
+ bool oy = ((j & 1) == 1);
+
+ if ((ox && oy) ||
+ ((grad == 2 || grad == 3) && ox && !oy) ||
+ (grad == 3 && oy))
+ *ptr = _gradCache[curGrad + 1];
+ else
+ *ptr = _gradCache[curGrad];
+ }
+ }
+}
+
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
fillSurface() {
byte *ptr = (byte *)_activeSurface->getPixels();
@@ -503,6 +708,44 @@ fillSurface() {
template<typename PixelType>
void VectorRendererSpec<PixelType>::
+fillSurfaceClip(Common::Rect clipping) {
+ int w = _activeSurface->w;
+ int h = _activeSurface->h;
+ if (clipping.isEmpty() || (clipping.left == 0 && clipping.top == 0 && clipping.right == w && clipping.bottom == h)) {
+ fillSurface();
+ return;
+ }
+
+ byte *ptr = (byte *)_activeSurface->getPixels();
+ int pitch = _activeSurface->pitch;
+
+ if (Base::_fillMode == kFillBackground || Base::_fillMode == kFillForeground) {
+ PixelType color = (Base::_fillMode == kFillBackground ? _bgColor : _fgColor);
+ byte *ptrLeft = (ptr + _clippingArea.left), *ptrRight = ptr + _clippingArea.right;
+ for (int i = 0; i < h; i++) {
+ if (_clippingArea.top <= i && i < _clippingArea.bottom) {
+ colorFill<PixelType>((PixelType *)ptrLeft, (PixelType *)ptrRight, color);
+ }
+
+ ptrLeft += pitch;
+ ptrRight += pitch;
+ }
+
+ } else if (Base::_fillMode == kFillGradient) {
+ precalcGradient(h);
+
+ for (int i = 0; i < h; i++) {
+ if (_clippingArea.top <= i && i < _clippingArea.bottom) {
+ gradientFill((PixelType *)ptr + _clippingArea.left, _clippingArea.width(), 0, i);
+ }
+
+ ptr += pitch;
+ }
+ }
+}
+
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
copyFrame(OSystem *sys, const Common::Rect &r) {
sys->copyRectToOverlay(
@@ -554,6 +797,58 @@ blitSubSurface(const Graphics::Surface *source, const Common::Rect &r) {
template<typename PixelType>
void VectorRendererSpec<PixelType>::
+blitSubSurfaceClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping) {
+ if (clipping.isEmpty() || clipping.contains(r)) {
+ blitSubSurface(source, r);
+ return;
+ }
+
+ int16 x = r.left;
+ int16 y = r.top;
+
+ if (r.width() > source->w)
+ x = x + (r.width() >> 1) - (source->w >> 1);
+
+ if (r.height() > source->h)
+ y = y + (r.height() >> 1) - (source->h >> 1);
+
+ int w = source->w, h = source->h;
+ int usedW = w, usedH = h;
+ int offsetX = 0, offsetY = 0;
+
+ if (x > clipping.right || x + w < clipping.left) return;
+ if (y > clipping.bottom || y + h < clipping.top) return;
+ if (x < clipping.left) {
+ offsetX = clipping.left - x;
+ usedW -= offsetX;
+ x = clipping.left;
+ }
+ if (y < clipping.top) {
+ offsetY = clipping.top - y;
+ usedH -= offsetY;
+ y = clipping.top;
+ }
+ if (usedW > clipping.width()) usedW = clipping.width();
+ if (usedH > clipping.height()) usedH = clipping.height();
+
+ byte *dst_ptr = (byte *)_activeSurface->getBasePtr(x, y);
+ const byte *src_ptr = (const byte *)source->getBasePtr(offsetX, offsetY);
+
+ const int dst_pitch = _activeSurface->pitch;
+ const int src_pitch = source->pitch;
+
+ int lines = usedH;
+ const int sz = usedW * sizeof(PixelType);
+
+ while (lines--) {
+ memcpy(dst_ptr, src_ptr, sz);
+ dst_ptr += dst_pitch;
+ src_ptr += src_pitch;
+ }
+}
+
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
blitAlphaBitmap(const Graphics::Surface *source, const Common::Rect &r) {
int16 x = r.left;
int16 y = r.top;
@@ -590,6 +885,65 @@ blitAlphaBitmap(const Graphics::Surface *source, const Common::Rect &r) {
template<typename PixelType>
void VectorRendererSpec<PixelType>::
+blitAlphaBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping) {
+ if (clipping.isEmpty() || clipping.contains(r)) {
+ blitAlphaBitmap(source, r);
+ return;
+ }
+
+ int16 x = r.left;
+ int16 y = r.top;
+
+ if (r.width() > source->w)
+ x = x + (r.width() >> 1) - (source->w >> 1);
+
+ if (r.height() > source->h)
+ y = y + (r.height() >> 1) - (source->h >> 1);
+
+ int w = source->w, h = source->h;
+ int usedW = w, usedH = h;
+ int offsetX = 0, offsetY = 0;
+
+ if (x > clipping.right || x + w < clipping.left) return;
+ if (y > clipping.bottom || y + h < clipping.top) return;
+ if (x < clipping.left) {
+ offsetX = clipping.left - x;
+ usedW -= offsetX;
+ x = clipping.left;
+ }
+ if (y < clipping.top) {
+ offsetY = clipping.top - y;
+ usedH -= offsetY;
+ y = clipping.top;
+ }
+ if (usedW > clipping.width()) usedW = clipping.width();
+ if (usedH > clipping.height()) usedH = clipping.height();
+
+ PixelType *dst_ptr = (PixelType *)_activeSurface->getBasePtr(x, y);
+ const PixelType *src_ptr = (const PixelType *)source->getBasePtr(offsetX, offsetY);
+
+ int dst_pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+ int src_pitch = source->pitch / source->format.bytesPerPixel;
+
+ h = usedH;
+ while (h--) {
+ w = usedW;
+
+ while (w--) {
+ if (*src_ptr != _bitmapAlphaColor)
+ *dst_ptr = *src_ptr;
+
+ dst_ptr++;
+ src_ptr++;
+ }
+
+ dst_ptr = dst_ptr - usedW + dst_pitch;
+ src_ptr = src_ptr - usedW + src_pitch;
+ }
+}
+
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
applyScreenShading(GUI::ThemeEngine::ShadingStyle shadingStyle) {
int pixels = _activeSurface->w * _activeSurface->h;
PixelType *ptr = (PixelType *)_activeSurface->getPixels();
@@ -667,6 +1021,13 @@ blendPixelPtr(PixelType *ptr, PixelType color, uint8 alpha) {
template<typename PixelType>
inline void VectorRendererSpec<PixelType>::
+blendPixelPtrClip(PixelType *ptr, PixelType color, uint8 alpha, int x, int y) {
+ if (IS_IN_CLIP(x, y))
+ blendPixelPtr(ptr, color, alpha);
+}
+
+template<typename PixelType>
+inline void VectorRendererSpec<PixelType>::
blendPixelDestAlphaPtr(PixelType *ptr, PixelType color, uint8 alpha) {
int idst = *ptr;
// This function is only used for corner pixels in rounded rectangles, so
@@ -710,6 +1071,36 @@ darkenFill(PixelType *ptr, PixelType *end) {
}
}
+template<typename PixelType>
+inline void VectorRendererSpec<PixelType>::
+darkenFillClip(PixelType *ptr, PixelType *end, int x, int y) {
+ PixelType mask = (PixelType)((3 << _format.rShift) | (3 << _format.gShift) | (3 << _format.bShift));
+
+ if (!g_system->hasFeature(OSystem::kFeatureOverlaySupportsAlpha)) {
+ // !kFeatureOverlaySupportsAlpha (but might have alpha bits)
+
+ while (ptr != end) {
+ if (IS_IN_CLIP(x, y)) *ptr = ((*ptr & ~mask) >> 2) | _alphaMask;
+ ++ptr;
+ ++x;
+ }
+ } else {
+ // kFeatureOverlaySupportsAlpha
+ // assuming at least 3 alpha bits
+
+ mask |= 3 << _format.aShift;
+ PixelType addA = (PixelType)(3 << (_format.aShift + 6 - _format.aLoss));
+
+ while (ptr != end) {
+ // Darken the color, and increase the alpha
+ // (0% -> 75%, 100% -> 100%)
+ if (IS_IN_CLIP(x, y)) *ptr = (PixelType)(((*ptr & ~mask) >> 2) + addA);
+ ++ptr;
+ ++x;
+ }
+ }
+}
+
/********************************************************************
********************************************************************
* Primitive shapes drawing - Public API calls - VectorRendererSpec *
@@ -818,6 +1209,77 @@ drawLine(int x1, int y1, int x2, int y2) {
}
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawLineClip(int x1, int y1, int x2, int y2, Common::Rect clipping) {
+ x1 = CLIP(x1, 0, (int)Base::_activeSurface->w);
+ x2 = CLIP(x2, 0, (int)Base::_activeSurface->w);
+ y1 = CLIP(y1, 0, (int)Base::_activeSurface->h);
+ y2 = CLIP(y2, 0, (int)Base::_activeSurface->h);
+
+ // we draw from top to bottom
+ if (y2 < y1) {
+ SWAP(x1, x2);
+ SWAP(y1, y2);
+ }
+
+ uint dx = ABS(x2 - x1);
+ uint dy = ABS(y2 - y1);
+
+ // this is a point, not a line. stoopid.
+ if (dy == 0 && dx == 0)
+ return;
+
+ if (Base::_strokeWidth == 0)
+ return;
+
+ PixelType *ptr = (PixelType *)_activeSurface->getBasePtr(x1, y1);
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+ int st = Base::_strokeWidth >> 1;
+
+ Common::Rect backup = _clippingArea;
+ _clippingArea = clipping;
+ bool needsClipping = !_clippingArea.isEmpty() && (!_clippingArea.contains(x1, y1) || !_clippingArea.contains(x2, y2));
+ if (!needsClipping) {
+ drawLine(x1, y1, x2, y2);
+ _clippingArea = backup;
+ return;
+ }
+
+ int ptr_x = x1, ptr_y = y1;
+
+ if (dy == 0) { // horizontal lines
+ colorFillClip<PixelType>(ptr, ptr + dx + 1, (PixelType)_fgColor, x1, y1, _clippingArea);
+
+ for (int i = 0, p = pitch; i < st; ++i, p += pitch) {
+ colorFillClip<PixelType>(ptr + p, ptr + dx + 1 + p, (PixelType)_fgColor, x1, y1 + p/pitch, _clippingArea);
+ colorFillClip<PixelType>(ptr - p, ptr + dx + 1 - p, (PixelType)_fgColor, x1, y1 - p/pitch, _clippingArea);
+ }
+
+ } else if (dx == 0) { // vertical lines
+ // these ones use a static pitch increase.
+ while (y1++ <= y2) {
+ colorFillClip<PixelType>(ptr - st, ptr + st, (PixelType)_fgColor, x1 - st, ptr_y, _clippingArea);
+ ptr += pitch;
+ ++ptr_y;
+ }
+
+ } else if (dx == dy) { // diagonal lines
+ // these ones also use a fixed pitch increase
+ pitch += (x2 > x1) ? 1 : -1;
+
+ while (dy--) {
+ colorFillClip<PixelType>(ptr - st, ptr + st, (PixelType)_fgColor, ptr_x - st, ptr_y, _clippingArea);
+ ptr += pitch;
+ ++ptr_y;
+ if (x2 > x1) ++ptr_x; else --ptr_x;
+ }
+
+ } else { // generic lines, use the standard algorithm...
+ drawLineAlgClip(x1, y1, x2, y2, dx, dy, (PixelType)_fgColor);
+ }
+}
+
/** CIRCLES **/
template<typename PixelType>
void VectorRendererSpec<PixelType>::
@@ -857,6 +1319,70 @@ drawCircle(int x, int y, int r) {
}
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawCircleClip(int x, int y, int r, Common::Rect clipping) {
+ if (x + r > Base::_activeSurface->w || y + r > Base::_activeSurface->h ||
+ x - r < 0 || y - r < 0 || x == 0 || y == 0 || r <= 0)
+ return;
+
+ Common::Rect backup = _clippingArea;
+ _clippingArea = clipping;
+ bool useClippingVersions = !(_clippingArea.isEmpty() || _clippingArea.contains(Common::Rect(x - r, y - r, x + r, y + r)));
+
+ if (Base::_fillMode != kFillDisabled && Base::_shadowOffset
+ && x + r + Base::_shadowOffset < Base::_activeSurface->w
+ && y + r + Base::_shadowOffset < Base::_activeSurface->h) {
+ if (useClippingVersions)
+ drawCircleAlgClip(x + Base::_shadowOffset + 1, y + Base::_shadowOffset + 1, r, 0, kFillForeground);
+ else
+ drawCircleAlg(x + Base::_shadowOffset + 1, y + Base::_shadowOffset + 1, r, 0, kFillForeground);
+ }
+
+ switch (Base::_fillMode) {
+ case kFillDisabled:
+ if (Base::_strokeWidth) {
+ if (useClippingVersions)
+ drawCircleAlgClip(x, y, r, _fgColor, kFillDisabled);
+ else
+ drawCircleAlg(x, y, r, _fgColor, kFillDisabled);
+ }
+ break;
+
+ case kFillForeground:
+ if (useClippingVersions)
+ drawCircleAlgClip(x, y, r, _fgColor, kFillForeground);
+ else
+ drawCircleAlg(x, y, r, _fgColor, kFillForeground);
+ break;
+
+ case kFillBackground:
+ if (Base::_strokeWidth > 1) {
+ if (useClippingVersions) {
+ drawCircleAlgClip(x, y, r, _fgColor, kFillForeground);
+ drawCircleAlgClip(x, y, r - Base::_strokeWidth, _bgColor, kFillBackground);
+ } else {
+ drawCircleAlg(x, y, r, _fgColor, kFillForeground);
+ drawCircleAlg(x, y, r - Base::_strokeWidth, _bgColor, kFillBackground);
+ }
+ } else {
+ if (useClippingVersions) {
+ drawCircleAlgClip(x, y, r, _bgColor, kFillBackground);
+ drawCircleAlgClip(x, y, r, _fgColor, kFillDisabled);
+ } else {
+ drawCircleAlg(x, y, r, _bgColor, kFillBackground);
+ drawCircleAlg(x, y, r, _fgColor, kFillDisabled);
+ }
+ }
+ break;
+
+ case kFillGradient:
+ break;
+ }
+
+ _clippingArea = backup;
+}
+
/** SQUARES **/
template<typename PixelType>
void VectorRendererSpec<PixelType>::
@@ -894,6 +1420,67 @@ drawSquare(int x, int y, int w, int h) {
}
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawSquareClip(int x, int y, int w, int h, Common::Rect clipping) {
+ if (x + w > Base::_activeSurface->w || y + h > Base::_activeSurface->h ||
+ w <= 0 || h <= 0 || x < 0 || y < 0)
+ return;
+
+ Common::Rect backup = _clippingArea;
+ _clippingArea = clipping;
+ bool useClippingVersions = !(_clippingArea.isEmpty() || _clippingArea.contains(Common::Rect(x, y, x + w, y + h)));
+
+ if (Base::_fillMode != kFillDisabled && Base::_shadowOffset
+ && x + w + Base::_shadowOffset < Base::_activeSurface->w
+ && y + h + Base::_shadowOffset < Base::_activeSurface->h) {
+ if (useClippingVersions)
+ drawSquareShadowClip(x, y, w, h, Base::_shadowOffset);
+ else
+ drawSquareShadow(x, y, w, h, Base::_shadowOffset);
+ }
+
+ switch (Base::_fillMode) {
+ case kFillDisabled:
+ if (Base::_strokeWidth) {
+ if (useClippingVersions)
+ drawSquareAlgClip(x, y, w, h, _fgColor, kFillDisabled);
+ else
+ drawSquareAlg(x, y, w, h, _fgColor, kFillDisabled);
+ }
+ break;
+
+ case kFillForeground:
+ if (useClippingVersions)
+ drawSquareAlgClip(x, y, w, h, _fgColor, kFillForeground);
+ else
+ drawSquareAlg(x, y, w, h, _fgColor, kFillForeground);
+ break;
+
+ case kFillBackground:
+ if (useClippingVersions) {
+ drawSquareAlgClip(x, y, w, h, _bgColor, kFillBackground);
+ drawSquareAlgClip(x, y, w, h, _fgColor, kFillDisabled);
+ } else {
+ drawSquareAlg(x, y, w, h, _bgColor, kFillBackground);
+ drawSquareAlg(x, y, w, h, _fgColor, kFillDisabled);
+ }
+ break;
+
+ case kFillGradient:
+ VectorRendererSpec::drawSquareAlg(x, y, w, h, 0, kFillGradient);
+ if (Base::_strokeWidth) {
+ if (useClippingVersions)
+ drawSquareAlgClip(x, y, w, h, _fgColor, kFillDisabled);
+ else
+ drawSquareAlg(x, y, w, h, _fgColor, kFillDisabled);
+ }
+ break;
+ }
+
+ _clippingArea = backup;
+}
+
/** ROUNDED SQUARES **/
template<typename PixelType>
void VectorRendererSpec<PixelType>::
@@ -920,6 +1507,43 @@ drawRoundedSquare(int x, int y, int r, int w, int h) {
template<typename PixelType>
void VectorRendererSpec<PixelType>::
+drawRoundedSquareClip(int x, int y, int r, int w, int h, Common::Rect clipping) {
+ if (x + w > Base::_activeSurface->w || y + h > Base::_activeSurface->h ||
+ w <= 0 || h <= 0 || x < 0 || y < 0 || r <= 0)
+ return;
+
+ if ((r * 2) > w || (r * 2) > h)
+ r = MIN(w / 2, h / 2);
+
+ if (r <= 0)
+ return;
+
+ Common::Rect backup = _clippingArea;
+ _clippingArea = clipping;
+ bool useOriginal = (_clippingArea.isEmpty() || _clippingArea.contains(Common::Rect(x, y, x + w, y + h)));
+
+ if (Base::_fillMode != kFillDisabled && Base::_shadowOffset
+ && x + w + Base::_shadowOffset + 1 < Base::_activeSurface->w
+ && y + h + Base::_shadowOffset + 1 < Base::_activeSurface->h
+ && h > (Base::_shadowOffset + 1) * 2) {
+ if (useOriginal) {
+ drawRoundedSquareShadow(x, y, r, w, h, Base::_shadowOffset);
+ } else {
+ drawRoundedSquareShadowClip(x, y, r, w, h, Base::_shadowOffset);
+ }
+ }
+
+ if (useOriginal) {
+ drawRoundedSquareAlg(x, y, r, w, h, _fgColor, Base::_fillMode);
+ } else {
+ drawRoundedSquareAlgClip(x, y, r, w, h, _fgColor, Base::_fillMode);
+ }
+
+ _clippingArea = backup;
+}
+
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
drawTab(int x, int y, int r, int w, int h) {
if (x + w > Base::_activeSurface->w || y + h > Base::_activeSurface->h ||
w <= 0 || h <= 0 || x < 0 || y < 0 || r > w || r > h)
@@ -956,6 +1580,66 @@ drawTab(int x, int y, int r, int w, int h) {
template<typename PixelType>
void VectorRendererSpec<PixelType>::
+drawTabClip(int x, int y, int r, int w, int h, Common::Rect clipping) {
+ if (x + w > Base::_activeSurface->w || y + h > Base::_activeSurface->h ||
+ w <= 0 || h <= 0 || x < 0 || y < 0 || r > w || r > h)
+ return;
+
+ Common::Rect backup = _clippingArea;
+ _clippingArea = clipping;
+ bool useClippingVersions = !(_clippingArea.isEmpty() || _clippingArea.contains(Common::Rect(x, y, x + w, y + h)));
+
+ if (r == 0 && Base::_bevel > 0) {
+ if (useClippingVersions)
+ drawBevelTabAlgClip(x, y, w, h, Base::_bevel, _bevelColor, _fgColor, (Base::_dynamicData >> 16), (Base::_dynamicData & 0xFFFF));
+ else
+ drawBevelTabAlg(x, y, w, h, Base::_bevel, _bevelColor, _fgColor, (Base::_dynamicData >> 16), (Base::_dynamicData & 0xFFFF));
+ _clippingArea = backup;
+ return;
+ }
+
+ if (r == 0) {
+ _clippingArea = backup;
+ return;
+ }
+
+ switch (Base::_fillMode) {
+ case kFillDisabled:
+ // FIXME: Implement this
+ _clippingArea = backup;
+ return;
+
+ case kFillGradient:
+ case kFillBackground:
+ // FIXME: This is broken for the AA renderer.
+ // See the rounded rect alg for how to fix it. (The border should
+ // be drawn before the interior, both inside drawTabAlg.)
+ if (useClippingVersions) {
+ drawTabShadowClip(x, y, w - 2, h, r);
+ drawTabAlgClip(x, y, w - 2, h, r, _bgColor, Base::_fillMode);
+ if (Base::_strokeWidth)
+ drawTabAlgClip(x, y, w, h, r, _fgColor, kFillDisabled, (Base::_dynamicData >> 16), (Base::_dynamicData & 0xFFFF));
+ } else {
+ drawTabShadow(x, y, w - 2, h, r);
+ drawTabAlg(x, y, w - 2, h, r, _bgColor, Base::_fillMode);
+ if (Base::_strokeWidth)
+ drawTabAlg(x, y, w, h, r, _fgColor, kFillDisabled, (Base::_dynamicData >> 16), (Base::_dynamicData & 0xFFFF));
+ }
+ break;
+
+ case kFillForeground:
+ if (useClippingVersions)
+ drawTabAlgClip(x, y, w, h, r, _fgColor, Base::_fillMode);
+ else
+ drawTabAlg(x, y, w, h, r, _fgColor, Base::_fillMode);
+ break;
+ }
+
+ _clippingArea = backup;
+}
+
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
drawTriangle(int x, int y, int w, int h, TriangleOrientation orient) {
if (x + w > Base::_activeSurface->w || y + h > Base::_activeSurface->h)
@@ -1022,8 +1706,88 @@ drawTriangle(int x, int y, int w, int h, TriangleOrientation orient) {
}
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawTriangleClip(int x, int y, int w, int h, TriangleOrientation orient, Common::Rect clipping) {
+ if (x + w > Base::_activeSurface->w || y + h > Base::_activeSurface->h)
+ return;
+ PixelType color = 0;
+ if (Base::_strokeWidth <= 1) {
+ if (Base::_fillMode == kFillForeground)
+ color = _fgColor;
+ else if (Base::_fillMode == kFillBackground)
+ color = _bgColor;
+ } else {
+ if (Base::_fillMode == kFillDisabled)
+ return;
+ color = _fgColor;
+ }
+
+ if (Base::_dynamicData != 0)
+ orient = (TriangleOrientation)Base::_dynamicData;
+
+ Common::Rect backup = _clippingArea;
+ _clippingArea = clipping;
+ bool useClippingVersions = !(_clippingArea.isEmpty() || _clippingArea.contains(Common::Rect(x, y, x + w, y + h)));
+
+ if (w == h) {
+ int newW = w;
+
+ switch (orient) {
+ case kTriangleUp:
+ case kTriangleDown:
+ if (useClippingVersions)
+ drawTriangleVertAlgClip(x, y, newW, newW, (orient == kTriangleDown), color, Base::_fillMode);
+ else
+ drawTriangleVertAlg(x, y, newW, newW, (orient == kTriangleDown), color, Base::_fillMode);
+ break;
+
+ case kTriangleLeft:
+ case kTriangleRight:
+ case kTriangleAuto:
+ break;
+ }
+
+ if (Base::_strokeWidth > 0)
+ if (Base::_fillMode == kFillBackground || Base::_fillMode == kFillGradient) {
+ if (useClippingVersions)
+ drawTriangleVertAlgClip(x, y, newW, newW, (orient == kTriangleDown), color, Base::_fillMode);
+ else
+ drawTriangleVertAlg(x, y, newW, newW, (orient == kTriangleDown), color, Base::_fillMode);
+ }
+ } else {
+ int newW = w;
+ int newH = h;
+
+ switch (orient) {
+ case kTriangleUp:
+ case kTriangleDown:
+ if (useClippingVersions)
+ drawTriangleVertAlgClip(x, y, newW, newH, (orient == kTriangleDown), color, Base::_fillMode);
+ else
+ drawTriangleVertAlg(x, y, newW, newH, (orient == kTriangleDown), color, Base::_fillMode);
+ break;
+
+ case kTriangleLeft:
+ case kTriangleRight:
+ case kTriangleAuto:
+ break;
+ }
+
+ if (Base::_strokeWidth > 0) {
+ if (Base::_fillMode == kFillBackground || Base::_fillMode == kFillGradient) {
+ if (useClippingVersions)
+ drawTriangleVertAlgClip(x, y, newW, newH, (orient == kTriangleDown), _fgColor, kFillDisabled);
+ else
+ drawTriangleVertAlg(x, y, newW, newH, (orient == kTriangleDown), _fgColor, kFillDisabled);
+ }
+ }
+ }
+
+ _clippingArea = backup;
+}
/********************************************************************
@@ -1134,6 +1898,120 @@ drawTabAlg(int x1, int y1, int w, int h, int r, PixelType color, VectorRenderer:
}
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawTabAlgClip(int x1, int y1, int w, int h, int r, PixelType color, VectorRenderer::FillMode fill_m, int baseLeft, int baseRight) {
+ // Don't draw anything for empty rects.
+ if (w <= 0 || h <= 0) {
+ return;
+ }
+
+ int f, ddF_x, ddF_y;
+ int x, y, px, py;
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+ int sw = 0, sp = 0, hp = 0;
+
+ PixelType *ptr_tl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + r);
+ PixelType *ptr_tr = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + r);
+ PixelType *ptr_fill = (PixelType *)Base::_activeSurface->getBasePtr(x1, y1);
+ int tl_x = x1 + r, tl_y = y1 + r;
+ int tr_x = x1 + w - r, tr_y = y1 + r;
+ int fill_x = x1, fill_y = y1;
+
+ int real_radius = r;
+ int short_h = h - r + 2;
+ int long_h = h;
+
+ if (fill_m == kFillDisabled) {
+ while (sw++ < Base::_strokeWidth) {
+ colorFillClip<PixelType>(ptr_fill + sp + r, ptr_fill + w + 1 + sp - r, color, fill_x + r, fill_y + sp/pitch, _clippingArea);
+ colorFillClip<PixelType>(ptr_fill + hp - sp + r, ptr_fill + w + hp + 1 - sp - r, color, fill_x + r, fill_y + hp / pitch - sp / pitch, _clippingArea);
+ sp += pitch;
+
+ BE_RESET();
+ r--;
+
+ while (x++ < y) {
+ BE_ALGORITHM();
+ BE_DRAWCIRCLE_TOP_CLIP(ptr_tr, ptr_tl, x, y, px, py, tr_x, tr_y, tl_x, tl_y);
+
+ if (Base::_strokeWidth > 1)
+ BE_DRAWCIRCLE_TOP_CLIP(ptr_tr, ptr_tl, x, y, px - pitch, py, tr_x, tr_y, tl_x, tl_y);
+ }
+ }
+
+ ptr_fill += pitch * real_radius;
+ fill_y += real_radius;
+ while (short_h--) {
+ colorFillClip<PixelType>(ptr_fill, ptr_fill + Base::_strokeWidth, color, fill_x, fill_y, _clippingArea);
+ colorFillClip<PixelType>(ptr_fill + w - Base::_strokeWidth + 1, ptr_fill + w + 1, color, fill_x + w - Base::_strokeWidth + 1, fill_y, _clippingArea);
+ ptr_fill += pitch;
+ ++fill_y;
+ }
+
+ if (baseLeft) {
+ sw = 0;
+ ptr_fill = (PixelType *)Base::_activeSurface->getBasePtr(x1, y1 + h + 1);
+ fill_x = x1;
+ fill_y = y1 + h + 1;
+ while (sw++ < Base::_strokeWidth) {
+ colorFillClip<PixelType>(ptr_fill - baseLeft, ptr_fill, color, fill_x - baseLeft, fill_y, _clippingArea);
+ ptr_fill += pitch;
+ ++fill_y;
+ }
+ }
+
+ if (baseRight) {
+ sw = 0;
+ ptr_fill = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w, y1 + h + 1);
+ fill_x = x1 + w;
+ fill_y = y1 + h + 1;
+ while (sw++ < Base::_strokeWidth) {
+ colorFillClip<PixelType>(ptr_fill, ptr_fill + baseRight, color, fill_x, fill_y, _clippingArea);
+ ptr_fill += pitch;
+ ++fill_y;
+ }
+ }
+ } else {
+ BE_RESET();
+
+ precalcGradient(long_h);
+
+ PixelType color1, color2;
+ color1 = color2 = color;
+
+ while (x++ < y) {
+ BE_ALGORITHM();
+
+ if (fill_m == kFillGradient) {
+ color1 = calcGradient(real_radius - x, long_h);
+ color2 = calcGradient(real_radius - y, long_h);
+
+ gradientFillClip(ptr_tl - x - py, w - 2 * r + 2 * x, x1 + r - x - y, real_radius - y, tl_x - x, tl_y - y);
+ gradientFillClip(ptr_tl - y - px, w - 2 * r + 2 * y, x1 + r - y - x, real_radius - x, tl_x - y, tl_y - x);
+
+ BE_DRAWCIRCLE_XCOLOR_TOP_CLIP(ptr_tr, ptr_tl, x, y, px, py, tr_x, tr_y, tl_x, tl_y);
+ } else {
+ colorFillClip<PixelType>(ptr_tl - x - py, ptr_tr + x - py, color, tl_x - x, tl_y - y, _clippingArea);
+ colorFillClip<PixelType>(ptr_tl - y - px, ptr_tr + y - px, color, tl_x - y, tl_y - x, _clippingArea);
+
+ BE_DRAWCIRCLE_TOP_CLIP(ptr_tr, ptr_tl, x, y, px, py, tr_x, tr_y, tl_x, tl_y);
+ }
+ }
+
+ ptr_fill += pitch * r;
+ fill_y += r;
+ while (short_h--) {
+ if (fill_m == kFillGradient) {
+ gradientFillClip(ptr_fill, w + 1, x1, real_radius++, fill_x, fill_y);
+ } else {
+ colorFillClip<PixelType>(ptr_fill, ptr_fill + w + 1, color, fill_x, fill_y, _clippingArea);
+ }
+ ptr_fill += pitch;
+ ++fill_y;
+ }
+ }
+}
template<typename PixelType>
void VectorRendererSpec<PixelType>::
@@ -1196,6 +2074,72 @@ drawTabShadow(int x1, int y1, int w, int h, int r) {
}
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawTabShadowClip(int x1, int y1, int w, int h, int r) {
+ int offset = 3;
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+
+ // "Harder" shadows when having lower BPP, since we will have artifacts (greenish tint on the modern theme)
+ uint8 expFactor = 3;
+ uint16 alpha = (_activeSurface->format.bytesPerPixel > 2) ? 4 : 8;
+
+ int xstart = x1;
+ int ystart = y1;
+ int width = w;
+ int height = h + offset + 1;
+
+ for (int i = offset; i >= 0; i--) {
+ int f, ddF_x, ddF_y;
+ int x, y, px, py;
+
+ PixelType *ptr_tl = (PixelType *)Base::_activeSurface->getBasePtr(xstart + r, ystart + r);
+ PixelType *ptr_tr = (PixelType *)Base::_activeSurface->getBasePtr(xstart + width - r, ystart + r);
+ PixelType *ptr_fill = (PixelType *)Base::_activeSurface->getBasePtr(xstart, ystart);
+
+ int tl_x = xstart + r, tl_y = ystart + r;
+ int fill_x = xstart, fill_y = ystart;
+
+ int short_h = height - (2 * r) + 2;
+ PixelType color = _format.RGBToColor(0, 0, 0);
+
+ BE_RESET();
+
+ // HACK: As we are drawing circles exploting 8-axis symmetry,
+ // there are 4 pixels on each circle which are drawn twice.
+ // this is ok on filled circles, but when blending on surfaces,
+ // we cannot let it blend twice. awful.
+ uint32 hb = 0;
+
+ while (x++ < y) {
+ BE_ALGORITHM();
+
+ if (((1 << x) & hb) == 0) {
+ blendFillClip(ptr_tl - y - px, ptr_tr + y - px, color, (uint8)alpha, tl_x - y, tl_y - x);
+ hb |= (1 << x);
+ }
+
+ if (((1 << y) & hb) == 0) {
+ blendFillClip(ptr_tl - x - py, ptr_tr + x - py, color, (uint8)alpha, tl_x - x, tl_y - y);
+ hb |= (1 << y);
+ }
+ }
+
+ ptr_fill += pitch * r;
+ fill_y += r;
+ while (short_h--) {
+ blendFillClip(ptr_fill, ptr_fill + width + 1, color, (uint8)alpha, fill_x, fill_y);
+ ptr_fill += pitch;
+ ++fill_y;
+ }
+
+ // Move shadow one pixel upward each iteration
+ xstart += 1;
+ // Multiply with expfactor
+ alpha = (alpha * (expFactor << 8)) >> 9;
+ }
+}
+
/** BEVELED TABS FOR CLASSIC THEME **/
template<typename PixelType>
void VectorRendererSpec<PixelType>::
@@ -1240,6 +2184,57 @@ drawBevelTabAlg(int x, int y, int w, int h, int bevel, PixelType top_color, Pixe
}
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawBevelTabAlgClip(int x, int y, int w, int h, int bevel, PixelType top_color, PixelType bottom_color, int baseLeft, int baseRight) {
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+ int i, j;
+
+ PixelType *ptr_left = (PixelType *)_activeSurface->getBasePtr(x, y);
+ int ptr_x = x, ptr_y = y;
+
+ i = bevel;
+ while (i--) {
+ colorFillClip<PixelType>(ptr_left, ptr_left + w, top_color, ptr_x, ptr_y, _clippingArea);
+ ptr_left += pitch;
+ ++ptr_y;
+ }
+
+ if (baseLeft > 0) {
+ i = h - bevel;
+ ptr_left = (PixelType *)_activeSurface->getBasePtr(x, y);
+ ptr_x = x; ptr_y = y;
+ while (i--) {
+ colorFillClip<PixelType>(ptr_left, ptr_left + bevel, top_color, ptr_x, ptr_y, _clippingArea);
+ ptr_left += pitch;
+ ++ptr_y;
+ }
+ }
+
+ i = h - bevel;
+ j = bevel - 1;
+ ptr_left = (PixelType *)_activeSurface->getBasePtr(x + w - bevel, y);
+ ptr_x = x + w - bevel; ptr_y = y;
+ while (i--) {
+ colorFillClip<PixelType>(ptr_left + j, ptr_left + bevel, bottom_color, ptr_x + j, ptr_y, _clippingArea);
+ if (j > 0) j--;
+ ptr_left += pitch;
+ ++ptr_y;
+ }
+
+ i = bevel;
+ ptr_left = (PixelType *)_activeSurface->getBasePtr(x + w - bevel, y + h - bevel);
+ ptr_x = x + w - bevel; ptr_y = y + h - bevel;
+ while (i--) {
+ colorFillClip<PixelType>(ptr_left, ptr_left + baseRight + bevel, bottom_color, ptr_x, ptr_y, _clippingArea);
+
+ if (baseLeft)
+ colorFillClip<PixelType>(ptr_left - w - baseLeft + bevel, ptr_left - w + bevel + bevel, top_color, ptr_x - w - baseLeft + bevel, ptr_y, _clippingArea);
+ ptr_left += pitch;
+ ++ptr_y;
+ }
+}
+
/** SQUARE ALGORITHM **/
template<typename PixelType>
void VectorRendererSpec<PixelType>::
@@ -1278,6 +2273,46 @@ drawSquareAlg(int x, int y, int w, int h, PixelType color, VectorRenderer::FillM
}
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawSquareAlgClip(int x, int y, int w, int h, PixelType color, VectorRenderer::FillMode fill_m) {
+ // Do not draw anything for empty rects.
+ if (w <= 0 || h <= 0) {
+ return;
+ }
+
+ PixelType *ptr = (PixelType *)_activeSurface->getBasePtr(x, y);
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+ int max_h = h;
+ int ptr_y = y;
+
+ if (fill_m != kFillDisabled) {
+ while (h--) {
+ if (fill_m == kFillGradient)
+ color = calcGradient(max_h - h, max_h);
+
+ colorFillClip<PixelType>(ptr, ptr + w, color, x, ptr_y, _clippingArea);
+ ptr += pitch;
+ ++ptr_y;
+ }
+ } else {
+ int sw = Base::_strokeWidth, sp = 0, hp = pitch * (h - 1);
+
+ while (sw--) {
+ colorFillClip<PixelType>(ptr + sp, ptr + w + sp, color, x, ptr_y + sp/pitch, _clippingArea);
+ colorFillClip<PixelType>(ptr + hp - sp, ptr + w + hp - sp, color, x, ptr_y + h - sp/pitch, _clippingArea);
+ sp += pitch;
+ }
+
+ while (h--) {
+ colorFillClip<PixelType>(ptr, ptr + Base::_strokeWidth, color, x, ptr_y, _clippingArea);
+ colorFillClip<PixelType>(ptr + w - Base::_strokeWidth, ptr + w, color, x + w - Base::_strokeWidth, ptr_y, _clippingArea);
+ ptr += pitch;
+ ptr_y += 1;
+ }
+ }
+}
+
/** SQUARE ALGORITHM **/
template<typename PixelType>
void VectorRendererSpec<PixelType>::
@@ -1334,6 +2369,72 @@ drawBevelSquareAlg(int x, int y, int w, int h, int bevel, PixelType top_color, P
}
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawBevelSquareAlgClip(int x, int y, int w, int h, int bevel, PixelType top_color, PixelType bottom_color, bool fill) {
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+ int i, j;
+ PixelType *ptr_left;
+ int ptr_x, ptr_y;
+
+ // Fill Background
+ ptr_left = (PixelType *)_activeSurface->getBasePtr(x, y);
+ ptr_x = x; ptr_y = y;
+ i = h;
+ if (fill) {
+ assert((_bgColor & ~_alphaMask) == 0); // only support black
+ while (i--) {
+ darkenFillClip(ptr_left, ptr_left + w, ptr_x, ptr_y);
+ ptr_left += pitch;
+ ++ptr_y;
+ }
+ }
+
+ x = MAX(x - bevel, 0);
+ y = MAX(y - bevel, 0);
+
+ w = MIN(w + (bevel * 2), (int)_activeSurface->w);
+ h = MIN(h + (bevel * 2), (int)_activeSurface->h);
+
+ ptr_left = (PixelType *)_activeSurface->getBasePtr(x, y);
+ ptr_x = x; ptr_y = y;
+ i = bevel;
+ while (i--) {
+ colorFillClip<PixelType>(ptr_left, ptr_left + w, top_color, ptr_x, ptr_y, _clippingArea);
+ ptr_left += pitch;
+ ++ptr_y;
+ }
+
+ ptr_left = (PixelType *)_activeSurface->getBasePtr(x, y + bevel);
+ ptr_x = x; ptr_y = y + bevel;
+ i = h - bevel;
+ while (i--) {
+ colorFillClip<PixelType>(ptr_left, ptr_left + bevel, top_color, ptr_x, ptr_y, _clippingArea);
+ ptr_left += pitch;
+ ++ptr_y;
+ }
+
+ ptr_left = (PixelType *)_activeSurface->getBasePtr(x, y + h - bevel);
+ ptr_x = x; ptr_y = y + h - bevel;
+ i = bevel;
+ while (i--) {
+ colorFillClip<PixelType>(ptr_left + i, ptr_left + w, bottom_color, ptr_x + i, ptr_y, _clippingArea);
+ ptr_left += pitch;
+ ++ptr_y;
+ }
+
+ ptr_left = (PixelType *)_activeSurface->getBasePtr(x + w - bevel, y);
+ ptr_x = x + w - bevel; ptr_y = y;
+ i = h - bevel;
+ j = bevel - 1;
+ while (i--) {
+ colorFillClip<PixelType>(ptr_left + j, ptr_left + bevel, bottom_color, ptr_x + j, ptr_y, _clippingArea);
+ if (j > 0) j--;
+ ptr_left += pitch;
+ ++ptr_y;
+ }
+}
+
/** GENERIC LINE ALGORITHM **/
template<typename PixelType>
void VectorRendererSpec<PixelType>::
@@ -1382,6 +2483,59 @@ drawLineAlg(int x1, int y1, int x2, int y2, uint dx, uint dy, PixelType color) {
*ptr = (PixelType)color;
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawLineAlgClip(int x1, int y1, int x2, int y2, uint dx, uint dy, PixelType color) {
+ PixelType *ptr = (PixelType *)_activeSurface->getBasePtr(x1, y1);
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+ int xdir = (x2 > x1) ? 1 : -1;
+ int ptr_x = x1, ptr_y = y1;
+
+ if (IS_IN_CLIP(ptr_x, ptr_y)) *ptr = (PixelType)color;
+
+ if (dx > dy) {
+ int ddy = dy * 2;
+ int dysub = ddy - (dx * 2);
+ int error_term = ddy - dx;
+
+ while (dx--) {
+ if (error_term >= 0) {
+ ptr += pitch;
+ ++ptr_y;
+ error_term += dysub;
+ } else {
+ error_term += ddy;
+ }
+
+ ptr += xdir;
+ ptr_x += xdir;
+ if (IS_IN_CLIP(ptr_x, ptr_y)) *ptr = (PixelType)color;
+ }
+ } else {
+ int ddx = dx * 2;
+ int dxsub = ddx - (dy * 2);
+ int error_term = ddx - dy;
+
+ while (dy--) {
+ if (error_term >= 0) {
+ ptr += xdir;
+ ptr_x += xdir;
+ error_term += dxsub;
+ } else {
+ error_term += ddx;
+ }
+
+ ptr += pitch;
+ ++ptr_y;
+ if (IS_IN_CLIP(ptr_x, ptr_y)) *ptr = (PixelType)color;
+ }
+ }
+
+ ptr = (PixelType *)_activeSurface->getBasePtr(x2, y2);
+ ptr_x = x2; ptr_y = y2;
+ if (IS_IN_CLIP(ptr_x, ptr_y)) *ptr = (PixelType)color;
+}
+
/** VERTICAL TRIANGLE DRAWING ALGORITHM **/
/**
FIXED POINT ARITHMETIC
@@ -1580,6 +2734,212 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
}
+/////////////
+
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawTriangleVertAlgClip(int x1, int y1, int w, int h, bool inverted, PixelType color, VectorRenderer::FillMode fill_m) {
+ // Don't draw anything for empty rects. This assures dy is always different
+ // from zero.
+ if (w <= 0 || h <= 0) {
+ return;
+ }
+
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+ int gradient_h = 0;
+ int y_pitch_sign = 1;
+ if (!inverted) {
+ pitch = -pitch;
+ y1 += h;
+ y_pitch_sign = -1;
+ }
+
+ PixelType *ptr_right = (PixelType *)_activeSurface->getBasePtr(x1, y1);
+ PixelType *floor = ptr_right - 1;
+ PixelType *ptr_left = (PixelType *)_activeSurface->getBasePtr(x1 + w, y1);
+
+ int x2 = x1 + w / 2;
+ int y2 = y1 + h;
+ int x_right = x1;
+ int y_right = y1;
+ int x_left = x1 + w;
+ int y_left = y1;
+ int x_floor = x_right - 1;
+ int y_floor = y_right;
+
+#if FIXED_POINT
+ int dx = (x2 - x1) << 8;
+ int dy = (y2 - y1) << 8;
+
+ if (abs(dx) > abs(dy)) {
+#else
+ double dx = (double)x2 - (double)x1;
+ double dy = (double)y2 - (double)y1;
+
+ if (fabs(dx) > fabs(dy)) {
+#endif
+ while (floor++ != ptr_left)
+ blendPixelPtrClip(floor, color, 50, ++x_floor, y_floor);
+
+#if FIXED_POINT
+ // In this branch dx is always different from zero. This is because
+ // abs(dx) is strictly greater than abs(dy), and abs returns zero
+ // as minimal value.
+ int gradient = (dy << 8) / dx;
+ int intery = (y1 << 8) + gradient;
+#else
+ double gradient = dy / dx;
+ double intery = y1 + gradient;
+#endif
+
+ for (int x = x1 + 1; x < x2; x++) {
+#if FIXED_POINT
+ if (intery + gradient > ipart(intery) + 0x100) {
+#else
+ if (intery + gradient > ipart(intery) + 1) {
+#endif
+ ptr_right++;
+ ptr_left--;
+ ++x_right;
+ --x_left;
+ }
+
+ ptr_left += pitch;
+ ptr_right += pitch;
+ y_right += y_pitch_sign;
+ y_left += y_pitch_sign;
+
+ intery += gradient;
+
+ switch (fill_m) {
+ case kFillDisabled:
+ if (IS_IN_CLIP(x_left, y_left)) *ptr_left = color;
+ if (IS_IN_CLIP(x_right, y_right)) *ptr_right = color;
+ break;
+ case kFillForeground:
+ case kFillBackground:
+ colorFillClip<PixelType>(ptr_right + 1, ptr_left, color, x_right+1, y_right, _clippingArea);
+ blendPixelPtrClip(ptr_right, color, rfpart(intery), x_right, y_right);
+ blendPixelPtrClip(ptr_left, color, rfpart(intery), x_left, y_left);
+ break;
+ case kFillGradient:
+ colorFillClip<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h), x_right, y_right, _clippingArea);
+ blendPixelPtrClip(ptr_right, color, rfpart(intery), x_right, y_right);
+ blendPixelPtrClip(ptr_left, color, rfpart(intery), x_left, y_left);
+ break;
+ }
+ }
+
+ return;
+ }
+
+#if FIXED_POINT
+ if (abs(dx) < abs(dy)) {
+#else
+ if (fabs(dx) < fabs(dy)) {
+#endif
+ ptr_left--;
+ --x_left;
+ while (floor++ != ptr_left)
+ blendPixelPtrClip(floor, color, 50, ++x_floor, y_floor);
+
+#if FIXED_POINT
+ int gradient = (dx << 8) / (dy + 0x100);
+ int interx = (x1 << 8) + gradient;
+#else
+ double gradient = dx / (dy + 1);
+ double interx = x1 + gradient;
+#endif
+
+ for (int y = y1 + 1; y < y2; y++) {
+#if FIXED_POINT
+ if (interx + gradient > ipart(interx) + 0x100) {
+#else
+ if (interx + gradient > ipart(interx) + 1) {
+#endif
+ ptr_right++;
+ ptr_left--;
+ ++x_right;
+ --x_left;
+ }
+
+ ptr_left += pitch;
+ ptr_right += pitch;
+ y_right += y_pitch_sign;
+ y_left += y_pitch_sign;
+
+ interx += gradient;
+
+ switch (fill_m) {
+ case kFillDisabled:
+ if (IS_IN_CLIP(x_left, y_left)) *ptr_left = color;
+ if (IS_IN_CLIP(x_right, y_right)) *ptr_right = color;
+ break;
+ case kFillForeground:
+ case kFillBackground:
+ colorFillClip<PixelType>(ptr_right + 1, ptr_left, color, x_right+1, y_right, _clippingArea);
+ blendPixelPtrClip(ptr_right, color, rfpart(interx), x_right, y_right);
+ blendPixelPtrClip(ptr_left, color, rfpart(interx), x_left, y_left);
+ break;
+ case kFillGradient:
+ colorFillClip<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h), x_right, y_right, _clippingArea);
+ blendPixelPtrClip(ptr_right, color, rfpart(interx), x_right, y_right);
+ blendPixelPtrClip(ptr_left, color, rfpart(interx), x_left, y_left);
+ break;
+ }
+ }
+
+ return;
+ }
+
+ ptr_left--;
+ --x_left;
+ while (floor++ != ptr_left)
+ blendPixelPtrClip(floor, color, 50, ++x_floor, y_floor);
+
+#if FIXED_POINT
+ int gradient = (dx / dy) << 8;
+ int interx = (x1 << 8) + gradient;
+#else
+ double gradient = dx / dy;
+ double interx = x1 + gradient;
+#endif
+
+ for (int y = y1 + 1; y < y2; y++) {
+ ptr_right++;
+ ptr_left--;
+ ++x_right;
+ --x_left;
+
+ ptr_left += pitch;
+ ptr_right += pitch;
+ y_right += y_pitch_sign;
+ y_left += y_pitch_sign;
+
+ interx += gradient;
+
+ switch (fill_m) {
+ case kFillDisabled:
+ if (IS_IN_CLIP(x_left, y_left)) *ptr_left = color;
+ if (IS_IN_CLIP(x_right, y_right)) *ptr_right = color;
+ break;
+ case kFillForeground:
+ case kFillBackground:
+ colorFillClip<PixelType>(ptr_right + 1, ptr_left, color, x_right+1, y_right, _clippingArea);
+ blendPixelPtrClip(ptr_right, color, rfpart(interx), x_right, y_right);
+ blendPixelPtrClip(ptr_left, color, rfpart(interx), x_left, y_left);
+ break;
+ case kFillGradient:
+ colorFillClip<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h), x_right, y_right, _clippingArea);
+ blendPixelPtrClip(ptr_right, color, rfpart(interx), x_right, y_right);
+ blendPixelPtrClip(ptr_left, color, rfpart(interx), x_left, y_left);
+ break;
+ }
+ }
+}
+
+/////////////
+
/** VERTICAL TRIANGLE DRAWING - FAST VERSION FOR SQUARED TRIANGLES */
template<typename PixelType>
void VectorRendererSpec<PixelType>::
@@ -1682,6 +3042,9 @@ drawBorderRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color,
while (x++ < (y - 2)) {
BE_ALGORITHM();
+ if (x < _clippingArea.left || x > _clippingArea.right) continue;
+ if (y < _clippingArea.top || y > _clippingArea.bottom) continue;
+
BE_DRAWCIRCLE_BCOLOR_TR_CW(ptr_tr, x, y, px, py, (uint8)(alpha_r + (alphaStep_tr * x)));
BE_DRAWCIRCLE_BCOLOR_BR_CW(ptr_br, x, y, px, py, (uint8)(alpha_b + (alphaStep_br * x)));
BE_DRAWCIRCLE_BCOLOR_BL_CW(ptr_bl, x, y, px, py, (uint8)(alpha_l + (alphaStep_bl * x)));
@@ -1709,6 +3072,74 @@ drawBorderRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color,
template<typename PixelType>
void VectorRendererSpec<PixelType>::
+drawBorderRoundedSquareAlgClip(int x1, int y1, int r, int w, int h, PixelType color, VectorRenderer::FillMode fill_m, uint8 alpha_t, uint8 alpha_r, uint8 alpha_b, uint8 alpha_l) {
+ int f, ddF_x, ddF_y;
+ int x, y, px, py;
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+ int sw = 0, sp = 0, hp = h * pitch;
+
+ PixelType *ptr_tl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + r);
+ PixelType *ptr_tr = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + r);
+ PixelType *ptr_bl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + h - r);
+ PixelType *ptr_br = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + h - r);
+ PixelType *ptr_fill = (PixelType *)Base::_activeSurface->getBasePtr(x1, y1);
+
+ int real_radius = r;
+ int short_h = h - (2 * r) + 2;
+
+ PixelType color1 = color;
+ PixelType color2 = color;
+
+ while (sw++ < Base::_strokeWidth) {
+ blendFillClip(ptr_fill + sp + r, ptr_fill + w + 1 + sp - r, color1, alpha_t,
+ x1 + r, y1 + sp/pitch); // top
+ blendFillClip(ptr_fill + hp - sp + r, ptr_fill + w + hp + 1 - sp - r, color2, alpha_b,
+ x1 + r, y1 + (hp - sp)/ pitch); // bottom
+ sp += pitch;
+
+ BE_RESET();
+ r--;
+
+ int alphaStep_tr = ((alpha_t - alpha_r) / (y + 1));
+ int alphaStep_br = ((alpha_r - alpha_b) / (y + 1));
+ int alphaStep_bl = ((alpha_b - alpha_l) / (y + 1));
+ int alphaStep_tl = ((alpha_l - alpha_t) / (y + 1));
+
+ // Avoid blending the last pixels twice, since we have an alpha
+ while (x++ < (y - 2)) {
+ BE_ALGORITHM();
+
+ BE_DRAWCIRCLE_BCOLOR_TR_CW_CLIP(ptr_tr, x, y, px, py, (uint8)(alpha_r + (alphaStep_tr * x)), x1 + w - r, y1 + r);
+ BE_DRAWCIRCLE_BCOLOR_BR_CW_CLIP(ptr_br, x, y, px, py, (uint8)(alpha_b + (alphaStep_br * x)), x1 + w - r, y1 + h - r);
+ BE_DRAWCIRCLE_BCOLOR_BL_CW_CLIP(ptr_bl, x, y, px, py, (uint8)(alpha_l + (alphaStep_bl * x)), x1 + r, y1 + h - r);
+ BE_DRAWCIRCLE_BCOLOR_TL_CW_CLIP(ptr_tl, x, y, px, py, (uint8)(alpha_t + (alphaStep_tl * x)), x1 + r, y1 + r);
+
+ BE_DRAWCIRCLE_BCOLOR_TR_CCW_CLIP(ptr_tr, x, y, px, py, (uint8)(alpha_t - (alphaStep_tr * x)), x1 + w - r, y1 + r);
+ BE_DRAWCIRCLE_BCOLOR_BR_CCW_CLIP(ptr_br, x, y, px, py, (uint8)(alpha_r - (alphaStep_br * x)), x1 + w - r, y1 + h - r);
+ BE_DRAWCIRCLE_BCOLOR_BL_CCW_CLIP(ptr_bl, x, y, px, py, (uint8)(alpha_b - (alphaStep_bl * x)), x1 + r, y1 + h - r);
+ BE_DRAWCIRCLE_BCOLOR_TL_CCW_CLIP(ptr_tl, x, y, px, py, (uint8)(alpha_l - (alphaStep_tl * x)), x1 + r, y1 + r);
+
+ if (Base::_strokeWidth > 1) {
+ BE_DRAWCIRCLE_BCOLOR_CLIP(ptr_tr, ptr_tl, ptr_bl, ptr_br, x - 1, y, px, py,
+ x1 + w - r, y1 + r, x1 + r, y1 + r, x1 + r, y1 + h - r, x1 + w - r, y1 + h - r);
+ BE_DRAWCIRCLE_BCOLOR_CLIP(ptr_tr, ptr_tl, ptr_bl, ptr_br, x, y, px - pitch, py,
+ x1 + w - r, y1 + r, x1 + r, y1 + r, x1 + r, y1 + h - r, x1 + w - r, y1 + h - r);
+ }
+ }
+ }
+
+ ptr_fill += pitch * real_radius;
+ while (short_h--) {
+ blendFillClip(ptr_fill, ptr_fill + Base::_strokeWidth, color1, alpha_l,
+ x1, y1 + real_radius + h - (2 * r) + 2 - short_h - 1); // left
+ blendFillClip(ptr_fill + w - Base::_strokeWidth + 1, ptr_fill + w + 1, color2, alpha_r,
+ x1 + w - Base::_strokeWidth + 1, y1 + real_radius + h - (2 * r) + 2 - short_h - 1); // right
+ ptr_fill += pitch;
+ }
+}
+
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
drawInteriorRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color, VectorRenderer::FillMode fill_m) {
// Do not draw empty space rounded squares.
if (w <= 0 || h <= 0) {
@@ -1740,6 +3171,8 @@ drawInteriorRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType colo
while (x++ < y) {
BE_ALGORITHM();
+ if (y1 + r + y < _clippingArea.top || y1 + r + y > _clippingArea.bottom) continue;
+
color1 = calcGradient(real_radius - x, long_h);
color2 = calcGradient(real_radius - y, long_h);
color3 = calcGradient(long_h - r + x, long_h);
@@ -1781,6 +3214,91 @@ drawInteriorRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType colo
template<typename PixelType>
void VectorRendererSpec<PixelType>::
+drawInteriorRoundedSquareAlgClip(int x1, int y1, int r, int w, int h, PixelType color, VectorRenderer::FillMode fill_m) {
+ // Do not draw empty space rounded squares.
+ if (w <= 0 || h <= 0) {
+ return;
+ }
+
+ int f, ddF_x, ddF_y;
+ int x, y, px, py;
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+
+ PixelType *ptr_tl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + r);
+ PixelType *ptr_tr = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + r);
+ PixelType *ptr_bl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + h - r);
+ PixelType *ptr_br = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + h - r);
+ PixelType *ptr_fill = (PixelType *)Base::_activeSurface->getBasePtr(x1, y1);
+
+ int real_radius = r;
+ int short_h = h - (2 * r) + 2;
+ int long_h = h;
+
+ BE_RESET();
+
+ PixelType color1 = color;
+
+ if (fill_m == kFillGradient) {
+ PixelType color2, color3, color4;
+ precalcGradient(long_h);
+
+ while (x++ < y) {
+ BE_ALGORITHM();
+
+ color1 = calcGradient(real_radius - x, long_h);
+ color2 = calcGradient(real_radius - y, long_h);
+ color3 = calcGradient(long_h - r + x, long_h);
+ color4 = calcGradient(long_h - r + y, long_h);
+
+ //TL = (x1 + r, y1 + r)
+ gradientFillClip(ptr_tl - x - py, w - 2 * r + 2 * x, x1 + r - x - y, real_radius - y,
+ x1 + r - x, y1 + r - y);
+ gradientFillClip(ptr_tl - y - px, w - 2 * r + 2 * y, x1 + r - y - x, real_radius - x,
+ x1 + r - y, y1 + r - x);
+
+ //BL = (x1 + r, y1 + h - r)
+ gradientFillClip(ptr_bl - x + py, w - 2 * r + 2 * x, x1 + r - x - y, long_h - r + y,
+ x1 + r - x, y1 + h - r + y);
+ gradientFillClip(ptr_bl - y + px, w - 2 * r + 2 * y, x1 + r - y - x, long_h - r + x,
+ x1 + r - y, y1 + h - r + x);
+
+ BE_DRAWCIRCLE_XCOLOR_CLIP(ptr_tr, ptr_tl, ptr_bl, ptr_br, x, y, px, py,
+ x1 + w - r, y1 + r, x1 + r, y1 + r, x1 + r, y1 + h - r, x1 + w - r, y1 + h - r);
+ }
+ } else {
+ while (x++ < y) {
+ BE_ALGORITHM();
+
+ colorFillClip<PixelType>(ptr_tl - x - py, ptr_tr + x - py, color1,
+ x1 + r - x, y1 + r - y, _clippingArea);
+ colorFillClip<PixelType>(ptr_tl - y - px, ptr_tr + y - px, color1,
+ x1 + r - y, y1 + r - x, _clippingArea);
+
+ colorFillClip<PixelType>(ptr_bl - x + py, ptr_br + x + py, color1,
+ x1 + r - x, y1 + h - r + y, _clippingArea);
+ colorFillClip<PixelType>(ptr_bl - y + px, ptr_br + y + px, color1,
+ x1 + r - y, y1 + h - r + x, _clippingArea);
+
+ // do not remove - messes up the drawing at lower resolutions
+ BE_DRAWCIRCLE_CLIP(ptr_tr, ptr_tl, ptr_bl, ptr_br, x, y, px, py,
+ x1 + w - r, y1 + r, x1 + r, y1 + r, x1 + r, y1 + h - r, x1 + w - r, y1 + h - r);
+ }
+ }
+
+ ptr_fill += pitch * r;
+ int short_h_orig = short_h;
+ while (short_h--) {
+ if (fill_m == kFillGradient) {
+ gradientFillClip(ptr_fill, w + 1, x1, real_radius++, x1, y1 + r + short_h_orig - short_h -1);
+ } else {
+ colorFillClip<PixelType>(ptr_fill, ptr_fill + w + 1, color1, x1, y1 + r + short_h_orig - short_h - 1, _clippingArea);
+ }
+ ptr_fill += pitch;
+ }
+}
+
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
drawRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color, VectorRenderer::FillMode fill_m) {
const uint8 borderAlpha_t = 0;
const uint8 borderAlpha_r = 127;
@@ -1810,6 +3328,38 @@ drawRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color, Vecto
}
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawRoundedSquareAlgClip(int x1, int y1, int r, int w, int h, PixelType color, VectorRenderer::FillMode fill_m) {
+ const uint8 borderAlpha_t = 0;
+ const uint8 borderAlpha_r = 127;
+ const uint8 borderAlpha_b = 255;
+ const uint8 borderAlpha_l = 63;
+
+ const uint8 bevelAlpha_t = 255;
+ const uint8 bevelAlpha_r = 31;
+ const uint8 bevelAlpha_b = 0;
+ const uint8 bevelAlpha_l = 127;
+
+ // If only border is visible
+ if ((!(w <= 0 || h <= 0)) && (fill_m != Base::kFillDisabled)) {
+ if (fill_m == Base::kFillBackground)
+ drawInteriorRoundedSquareAlgClip(x1, y1, r, w, h, _bgColor, fill_m);
+ else
+ drawInteriorRoundedSquareAlgClip(x1, y1, r, w, h, color, fill_m);
+ }
+
+ //I expect these to work fine with clipping:
+ if (Base::_strokeWidth) {
+ if (r != 0 && _bevel > 0) {
+ drawBorderRoundedSquareAlgClip(x1, y1, r, w, h, color, fill_m, borderAlpha_t, borderAlpha_r, borderAlpha_b, borderAlpha_l);
+ drawBorderRoundedSquareAlgClip(x1, y1, r, w, h, _bevelColor, fill_m, bevelAlpha_t, bevelAlpha_r, bevelAlpha_b, bevelAlpha_l);
+ } else {
+ drawBorderRoundedSquareAlgClip(x1, y1, r, w, h, color, fill_m, 255, 255, 255, 255);
+ }
+ }
+}
+
/** CIRCLE ALGORITHM **/
template<typename PixelType>
void VectorRendererSpec<PixelType>::
@@ -1854,7 +3404,47 @@ drawCircleAlg(int x1, int y1, int r, PixelType color, VectorRenderer::FillMode f
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawCircleAlgClip(int x1, int y1, int r, PixelType color, VectorRenderer::FillMode fill_m) {
+ int f, ddF_x, ddF_y;
+ int x, y, px, py, sw = 0;
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+ PixelType *ptr = (PixelType *)Base::_activeSurface->getBasePtr(x1, y1);
+
+ if (fill_m == kFillDisabled) {
+ while (sw++ < Base::_strokeWidth) {
+ BE_RESET();
+ r--;
+
+ if (IS_IN_CLIP(x1 + y, y1)) *(ptr + y) = color;
+ if (IS_IN_CLIP(x1 - y, y1)) *(ptr - y) = color;
+ if (IS_IN_CLIP(x1, y1 + y)) *(ptr + py) = color;
+ if (IS_IN_CLIP(x1, y1 - y)) *(ptr - py) = color;
+
+ while (x++ < y) {
+ BE_ALGORITHM();
+ BE_DRAWCIRCLE_CLIP(ptr, ptr, ptr, ptr, x, y, px, py, x1, y1, x1, y1, x1, y1, x1, y1);
+
+ if (Base::_strokeWidth > 1) {
+ BE_DRAWCIRCLE_CLIP(ptr, ptr, ptr, ptr, x - 1, y, px, py, x1, y1, x1, y1, x1, y1, x1, y1);
+ BE_DRAWCIRCLE_CLIP(ptr, ptr, ptr, ptr, x, y, px - pitch, py, x1, y1, x1, y1, x1, y1, x1, y1);
+ }
+ }
+ }
+ } else {
+ colorFillClip<PixelType>(ptr - r, ptr + r, color, x1 - r, y1 + r, _clippingArea);
+ BE_RESET();
+ while (x++ < y) {
+ BE_ALGORITHM();
+ colorFillClip<PixelType>(ptr - x + py, ptr + x + py, color, x1 - x, y1 + y, _clippingArea);
+ colorFillClip<PixelType>(ptr - x - py, ptr + x - py, color, x1 - x, y1 - y, _clippingArea);
+ colorFillClip<PixelType>(ptr - y + px, ptr + y + px, color, x1 - y, y1 + x, _clippingArea);
+ colorFillClip<PixelType>(ptr - y - px, ptr + y - px, color, x1 - y, y1 - x, _clippingArea);
+ }
+ }
+}
/********************************************************************
@@ -1905,6 +3495,54 @@ drawSquareShadow(int x, int y, int w, int h, int offset) {
template<typename PixelType>
void VectorRendererSpec<PixelType>::
+drawSquareShadowClip(int x, int y, int w, int h, int offset) {
+ // Do nothing for empty rects or no shadow offset.
+ if (w <= 0 || h <= 0 || offset <= 0) {
+ return;
+ }
+
+ PixelType *ptr = (PixelType *)_activeSurface->getBasePtr(x + w - 1, y + offset);
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+ int i, j, ptr_x = x+w-1, ptr_y = y+offset;
+
+ i = h - offset;
+
+ while (i--) {
+ j = offset;
+ while (j--)
+ blendPixelPtrClip(ptr + j, 0, ((offset - j) << 8) / offset, ptr_x + j, ptr_y);
+ ptr += pitch;
+ ++ptr_y;
+ }
+
+ ptr = (PixelType *)_activeSurface->getBasePtr(x + offset, y + h - 1);
+ ptr_x = x + offset;
+ ptr_y = y + h - 1;
+
+ while (i++ < offset) {
+ j = w - offset;
+ while (j--)
+ blendPixelPtrClip(ptr + j, 0, ((offset - i) << 8) / offset, ptr_x + j, ptr_y);
+ ptr += pitch;
+ ++ptr_y;
+ }
+
+ ptr = (PixelType *)_activeSurface->getBasePtr(x + w, y + h);
+ ptr_x = x + w;
+ ptr_y = y + h;
+
+ i = 0;
+ while (i++ < offset) {
+ j = offset - 1;
+ while (j--)
+ blendPixelPtrClip(ptr + j, 0, (((offset - j) * (offset - i)) << 8) / (offset * offset), ptr_x + j, ptr_y);
+ ptr += pitch;
+ ++ptr_y;
+ }
+}
+
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
drawRoundedSquareShadow(int x1, int y1, int r, int w, int h, int offset) {
int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
@@ -1942,7 +3580,6 @@ drawRoundedSquareShadow(int x1, int y1, int r, int w, int h, int offset) {
while (x++ < y) {
BE_ALGORITHM();
-
if (((1 << x) & hb) == 0) {
blendFill(ptr_tl - y - px, ptr_tr + y - px, color, (uint8)alpha);
@@ -1978,6 +3615,83 @@ drawRoundedSquareShadow(int x1, int y1, int r, int w, int h, int offset) {
}
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawRoundedSquareShadowClip(int x1, int y1, int r, int w, int h, int offset) {
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+
+ // "Harder" shadows when having lower BPP, since we will have artifacts (greenish tint on the modern theme)
+ uint8 expFactor = 3;
+ uint16 alpha = (_activeSurface->format.bytesPerPixel > 2) ? 4 : 8;
+
+ // These constants ensure a border of 2px on the left and of each rounded square
+ int xstart = (x1 > 2) ? x1 - 2 : x1;
+ int ystart = y1;
+ int width = w + offset + 2;
+ int height = h + offset + 1;
+
+ for (int i = offset; i >= 0; i--) {
+ int f, ddF_x, ddF_y;
+ int x, y, px, py;
+
+ PixelType *ptr_tl = (PixelType *)Base::_activeSurface->getBasePtr(xstart + r, ystart + r);
+ PixelType *ptr_tr = (PixelType *)Base::_activeSurface->getBasePtr(xstart + width - r, ystart + r);
+ PixelType *ptr_bl = (PixelType *)Base::_activeSurface->getBasePtr(xstart + r, ystart + height - r);
+ PixelType *ptr_br = (PixelType *)Base::_activeSurface->getBasePtr(xstart + width - r, ystart + height - r);
+ PixelType *ptr_fill = (PixelType *)Base::_activeSurface->getBasePtr(xstart, ystart);
+
+ int short_h = height - (2 * r) + 2;
+ PixelType color = _format.RGBToColor(0, 0, 0);
+
+ BE_RESET();
+
+ // HACK: As we are drawing circles exploting 8-axis symmetry,
+ // there are 4 pixels on each circle which are drawn twice.
+ // this is ok on filled circles, but when blending on surfaces,
+ // we cannot let it blend twice. awful.
+ uint32 hb = 0;
+
+ while (x++ < y) {
+ BE_ALGORITHM();
+
+ if (((1 << x) & hb) == 0) {
+ blendFillClip(ptr_tl - y - px, ptr_tr + y - px, color, (uint8)alpha,
+ xstart + r - y, ystart + r - x);
+
+ // Will create a dark line of pixles if left out
+ if (hb > 0) {
+ blendFillClip(ptr_bl - y + px, ptr_br + y + px, color, (uint8)alpha,
+ xstart + r - y, ystart + height - r + x);
+ }
+ hb |= (1 << x);
+ }
+
+ if (((1 << y) & hb) == 0) {
+ blendFillClip(ptr_tl - x - py, ptr_tr + x - py, color, (uint8)alpha, xstart + r - x, ystart + r - y);
+ blendFillClip(ptr_bl - x + py, ptr_br + x + py, color, (uint8)alpha, xstart + r - x, ystart + height - r + y);
+ hb |= (1 << y);
+ }
+ }
+
+ ptr_fill += pitch * r;
+ int orig_short_h = short_h;
+ while (short_h--) {
+ blendFillClip(ptr_fill, ptr_fill + width + 1, color, (uint8)alpha,
+ xstart, ystart + r + orig_short_h - short_h - 1);
+ ptr_fill += pitch;
+ }
+
+ // Make shadow smaller each iteration, and move it one pixel inward
+ xstart += 1;
+ ystart += 1;
+ width -= 2;
+ height -= 2;
+
+ if (_shadowFillMode == kShadowExponential)
+ // Multiply with expfactor
+ alpha = (alpha * (expFactor << 8)) >> 9;
+ }
+}
/******************************************************************************/
diff --git a/graphics/VectorRendererSpec.h b/graphics/VectorRendererSpec.h
index 3e54608b8e..bee6d4c425 100644
--- a/graphics/VectorRendererSpec.h
+++ b/graphics/VectorRendererSpec.h
@@ -51,14 +51,31 @@ public:
VectorRendererSpec(PixelFormat format);
void drawLine(int x1, int y1, int x2, int y2);
+ void drawLineClip(int x1, int y1, int x2, int y2, Common::Rect clipping);
void drawCircle(int x, int y, int r);
+ void drawCircleClip(int x, int y, int r, Common::Rect clipping);
void drawSquare(int x, int y, int w, int h);
+ void drawSquareClip(int x, int y, int w, int h, Common::Rect clipping);
void drawRoundedSquare(int x, int y, int r, int w, int h);
+ void drawRoundedSquareClip(int x, int y, int r, int w, int h, Common::Rect clipping);
void drawTriangle(int x, int y, int base, int height, TriangleOrientation orient);
+ void drawTriangleClip(int x, int y, int base, int height, TriangleOrientation orient, Common::Rect clipping);
void drawTab(int x, int y, int r, int w, int h);
+ void drawTabClip(int x, int y, int r, int w, int h, Common::Rect clipping);
void drawBeveledSquare(int x, int y, int w, int h, int bevel) {
drawBevelSquareAlg(x, y, w, h, bevel, _bevelColor, _fgColor, Base::_fillMode != kFillDisabled);
}
+ void drawBeveledSquareClip(int x, int y, int w, int h, int bevel, Common::Rect clipping) {
+ bool useClippingVersions = !(clipping.isEmpty() || clipping.contains(Common::Rect(x, y, x + w, y + h)));
+ if (useClippingVersions) {
+ Common::Rect backup = _clippingArea;
+ _clippingArea = clipping;
+ drawBevelSquareAlgClip(x, y, w, h, bevel, _bevelColor, _fgColor, Base::_fillMode != kFillDisabled);
+ _clippingArea = backup;
+ } else {
+ drawBevelSquareAlg(x, y, w, h, bevel, _bevelColor, _fgColor, Base::_fillMode != kFillDisabled);
+ }
+ }
void drawString(const Graphics::Font *font, const Common::String &text,
const Common::Rect &area, Graphics::TextAlign alignH,
GUI::ThemeEngine::TextAlignVertical alignV, int deltax, bool elipsis, const Common::Rect &textDrawableArea = Common::Rect(0, 0, 0, 0));
@@ -72,14 +89,19 @@ public:
void copyWholeFrame(OSystem *sys) { copyFrame(sys, Common::Rect(0, 0, _activeSurface->w, _activeSurface->h)); }
void fillSurface();
+ void fillSurfaceClip(Common::Rect clipping);
void blitSurface(const Graphics::Surface *source, const Common::Rect &r);
void blitSubSurface(const Graphics::Surface *source, const Common::Rect &r);
+ void blitSubSurfaceClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping);
void blitAlphaBitmap(const Graphics::Surface *source, const Common::Rect &r);
+ void blitAlphaBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping);
void applyScreenShading(GUI::ThemeEngine::ShadingStyle shadingStyle);
protected:
+ Common::Rect _clippingArea;
+
/**
* Draws a single pixel on the surface with the given coordinates and
* the given color.
@@ -119,6 +141,7 @@ protected:
* @param alpha Alpha intensity of the pixel (0-255)
*/
inline void blendPixelPtr(PixelType *ptr, PixelType color, uint8 alpha);
+ inline void blendPixelPtrClip(PixelType *ptr, PixelType color, uint8 alpha, int x, int y);
/**
* Blends a single pixel on the surface in the given pixel pointer, using supplied color
@@ -152,40 +175,74 @@ protected:
virtual void drawLineAlg(int x1, int y1, int x2, int y2,
uint dx, uint dy, PixelType color);
+ virtual void drawLineAlgClip(int x1, int y1, int x2, int y2,
+ uint dx, uint dy, PixelType color);
+
virtual void drawCircleAlg(int x, int y, int r,
PixelType color, FillMode fill_m);
+ virtual void drawCircleAlgClip(int x, int y, int r,
+ PixelType color, FillMode fill_m);
+
virtual void drawRoundedSquareAlg(int x1, int y1, int r, int w, int h,
PixelType color, FillMode fill_m);
+ virtual void drawRoundedSquareAlgClip(int x1, int y1, int r, int w, int h,
+ PixelType color, FillMode fill_m);
+
virtual void drawBorderRoundedSquareAlg(int x1, int y1, int r, int w, int h,
PixelType color, FillMode fill_m, uint8 alpha_t, uint8 alpha_r, uint8 alpha_b, uint8 alpha_l);
+ virtual void drawBorderRoundedSquareAlgClip(int x1, int y1, int r, int w, int h,
+ PixelType color, FillMode fill_m, uint8 alpha_t, uint8 alpha_r, uint8 alpha_b, uint8 alpha_l);
+
virtual void drawInteriorRoundedSquareAlg(int x1, int y1, int r, int w, int h,
PixelType color, FillMode fill_m);
+ virtual void drawInteriorRoundedSquareAlgClip(int x1, int y1, int r, int w, int h,
+ PixelType color, FillMode fill_m);
+
virtual void drawSquareAlg(int x, int y, int w, int h,
PixelType color, FillMode fill_m);
+ virtual void drawSquareAlgClip(int x, int y, int w, int h,
+ PixelType color, FillMode fill_m);
+
virtual void drawTriangleVertAlg(int x, int y, int w, int h,
bool inverted, PixelType color, FillMode fill_m);
+ virtual void drawTriangleVertAlgClip(int x, int y, int w, int h,
+ bool inverted, PixelType color, FillMode fill_m);
+
virtual void drawTriangleFast(int x, int y, int size,
bool inverted, PixelType color, FillMode fill_m);
virtual void drawBevelSquareAlg(int x, int y, int w, int h,
int bevel, PixelType top_color, PixelType bottom_color, bool fill);
+ virtual void drawBevelSquareAlgClip(int x, int y, int w, int h,
+ int bevel, PixelType top_color, PixelType bottom_color, bool fill);
+
virtual void drawTabAlg(int x, int y, int w, int h, int r,
PixelType color, VectorRenderer::FillMode fill_m,
int baseLeft = 0, int baseRight = 0);
+ virtual void drawTabAlgClip(int x, int y, int w, int h, int r,
+ PixelType color, VectorRenderer::FillMode fill_m,
+ int baseLeft = 0, int baseRight = 0);
+
virtual void drawTabShadow(int x, int y, int w, int h, int r);
+ virtual void drawTabShadowClip(int x, int y, int w, int h, int r);
+
virtual void drawBevelTabAlg(int x, int y, int w, int h,
int bevel, PixelType topColor, PixelType bottomColor,
int baseLeft = 0, int baseRight = 0);
+ virtual void drawBevelTabAlgClip(int x, int y, int w, int h,
+ int bevel, PixelType topColor, PixelType bottomColor,
+ int baseLeft = 0, int baseRight = 0);
+
/**
* SHADOW DRAWING ALGORITHMS
*
@@ -197,7 +254,9 @@ protected:
* @param offset Intensity/size of the shadow.
*/
virtual void drawSquareShadow(int x, int y, int w, int h, int offset);
+ virtual void drawSquareShadowClip(int x, int y, int w, int h, int offset);
virtual void drawRoundedSquareShadow(int x, int y, int r, int w, int h, int offset);
+ virtual void drawRoundedSquareShadowClip(int x, int y, int r, int w, int h, int offset);
/**
* Calculates the color gradient on a given point.
@@ -212,6 +271,7 @@ protected:
void precalcGradient(int h);
void gradientFill(PixelType *first, int width, int x, int y);
+ void gradientFillClip(PixelType *first, int width, int x, int y, int realX, int realY);
/**
* Fills several pixels in a row with a given color and the specified alpha blending.
@@ -227,7 +287,20 @@ protected:
while (first != last) blendPixelPtr(first++, color, alpha);
}
+ inline void blendFillClip(PixelType *first, PixelType *last, PixelType color, uint8 alpha, int realX, int realY) {
+ if (_clippingArea.top <= realY && realY < _clippingArea.bottom) {
+ while (first != last) {
+ if (_clippingArea.left <= realX && realX < _clippingArea.right)
+ blendPixelPtr(first++, color, alpha);
+ else
+ ++first;
+ ++realX;
+ }
+ }
+ }
+
void darkenFill(PixelType *first, PixelType *last);
+ void darkenFillClip(PixelType *first, PixelType *last, int x, int y);
const PixelFormat _format;
const PixelType _redMask, _greenMask, _blueMask, _alphaMask;
diff --git a/graphics/nine_patch.cpp b/graphics/nine_patch.cpp
index a193200208..8ac6977eed 100644
--- a/graphics/nine_patch.cpp
+++ b/graphics/nine_patch.cpp
@@ -236,6 +236,41 @@ void NinePatchBitmap::blit(Graphics::Surface &target, int dx, int dy, int dw, in
}
}
+void NinePatchBitmap::blitClip(Graphics::Surface &target, Common::Rect clip, int dx, int dy, int dw, int dh) {
+ /* don't draw bitmaps that are smaller than the fixed area */
+ if (dw < _h._fix || dh < _v._fix)
+ return;
+
+ /* if the bitmap is the same size as the origin, then draw it as-is */
+ if (dw == _width && dh == _height) {
+ Common::Rect r(1, 1, dw, dh);
+
+ _bmp->blitClip(target, clip, dx, dy, Graphics::FLIP_NONE, &r);
+ return;
+ }
+
+ /* only recalculate the offsets if they have changed since the last draw */
+ if (_cached_dw != dw || _cached_dh != dh) {
+ _h.calcOffsets(dw);
+ _v.calcOffsets(dh);
+
+ _cached_dw = dw;
+ _cached_dh = dh;
+ }
+
+ /* draw each region */
+ for (uint i = 0; i < _v._m.size(); ++i) {
+ for (uint j = 0; j < _h._m.size(); ++j) {
+ Common::Rect r(_h._m[j]->offset, _v._m[i]->offset,
+ _h._m[j]->offset + _h._m[j]->length, _v._m[i]->offset + _v._m[i]->length);
+
+ _bmp->blitClip(target, clip, dx + _h._m[j]->dest_offset, dy + _v._m[i]->dest_offset,
+ Graphics::FLIP_NONE, &r, TS_ARGB(255, 255, 255, 255),
+ _h._m[j]->dest_length, _v._m[i]->dest_length);
+ }
+ }
+}
+
NinePatchBitmap::~NinePatchBitmap() {
if (_destroy_bmp)
delete _bmp;
diff --git a/graphics/nine_patch.h b/graphics/nine_patch.h
index c62de3f6e2..45e4e0918a 100644
--- a/graphics/nine_patch.h
+++ b/graphics/nine_patch.h
@@ -83,6 +83,7 @@ public:
~NinePatchBitmap();
void blit(Graphics::Surface &target, int dx, int dy, int dw, int dh);
+ void blitClip(Graphics::Surface &target, Common::Rect clip, int dx, int dy, int dw, int dh);
int getWidth() { return _width; }
int getHeight() { return _height; }
diff --git a/graphics/transparent_surface.cpp b/graphics/transparent_surface.cpp
index 19e7655a93..c2903d42c3 100644
--- a/graphics/transparent_surface.cpp
+++ b/graphics/transparent_surface.cpp
@@ -462,6 +462,139 @@ Common::Rect TransparentSurface::blit(Graphics::Surface &target, int posX, int p
return retSize;
}
+Common::Rect TransparentSurface::blitClip(Graphics::Surface &target, Common::Rect clippingArea, int posX, int posY, int flipping, Common::Rect *pPartRect, uint color, int width, int height, TSpriteBlendMode blendMode) {
+ Common::Rect retSize;
+ retSize.top = 0;
+ retSize.left = 0;
+ retSize.setWidth(0);
+ retSize.setHeight(0);
+ // Check if we need to draw anything at all
+ int ca = (color >> kAModShift) & 0xff;
+
+ if (ca == 0) {
+ return retSize;
+ }
+
+ // Create an encapsulating surface for the data
+ TransparentSurface srcImage(*this, false);
+ // TODO: Is the data really in the screen format?
+ if (format.bytesPerPixel != 4) {
+ warning("TransparentSurface can only blit 32bpp images, but got %d", format.bytesPerPixel * 8);
+ return retSize;
+ }
+
+ if (pPartRect) {
+
+ int xOffset = pPartRect->left;
+ int yOffset = pPartRect->top;
+
+ if (flipping & FLIP_V) {
+ yOffset = srcImage.h - pPartRect->bottom;
+ }
+
+ if (flipping & FLIP_H) {
+ xOffset = srcImage.w - pPartRect->right;
+ }
+
+ srcImage.pixels = getBasePtr(xOffset, yOffset);
+ srcImage.w = pPartRect->width();
+ srcImage.h = pPartRect->height();
+
+ debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping,
+ pPartRect->left, pPartRect->top, pPartRect->width(), pPartRect->height(), color, width, height);
+ } else {
+
+ debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping, 0, 0,
+ srcImage.w, srcImage.h, color, width, height);
+ }
+
+ if (width == -1) {
+ width = srcImage.w;
+ }
+ if (height == -1) {
+ height = srcImage.h;
+ }
+
+#ifdef SCALING_TESTING
+ // Hardcode scaling to 66% to test scaling
+ width = width * 2 / 3;
+ height = height * 2 / 3;
+#endif
+
+ Graphics::Surface *img = nullptr;
+ Graphics::Surface *imgScaled = nullptr;
+ byte *savedPixels = nullptr;
+ if ((width != srcImage.w) || (height != srcImage.h)) {
+ // Scale the image
+ img = imgScaled = srcImage.scale(width, height);
+ savedPixels = (byte *)img->getPixels();
+ } else {
+ img = &srcImage;
+ }
+
+ // Handle off-screen clipping
+ if (posY < clippingArea.top) {
+ img->h = MAX(0, (int)img->h - (clippingArea.top - posY));
+ img->setPixels((byte *)img->getBasePtr(0, clippingArea.top - posY));
+ posY = clippingArea.top;
+ }
+
+ if (posX < clippingArea.left) {
+ img->w = MAX(0, (int)img->w - (clippingArea.left - posX));
+ img->setPixels((byte *)img->getBasePtr(clippingArea.left - posX, 0));
+ posX = clippingArea.left;
+ }
+
+ img->w = CLIP((int)img->w, 0, (int)MAX((int)clippingArea.right - posX, 0));
+ img->h = CLIP((int)img->h, 0, (int)MAX((int)clippingArea.bottom - posY, 0));
+
+ if ((img->w > 0) && (img->h > 0)) {
+ int xp = 0, yp = 0;
+
+ int inStep = 4;
+ int inoStep = img->pitch;
+ if (flipping & FLIP_H) {
+ inStep = -inStep;
+ xp = img->w - 1;
+ }
+
+ if (flipping & FLIP_V) {
+ inoStep = -inoStep;
+ yp = img->h - 1;
+ }
+
+ byte *ino = (byte *)img->getBasePtr(xp, yp);
+ byte *outo = (byte *)target.getBasePtr(posX, posY);
+
+ if (color == 0xFFFFFFFF && blendMode == BLEND_NORMAL && _alphaMode == ALPHA_OPAQUE) {
+ doBlitOpaqueFast(ino, outo, img->w, img->h, target.pitch, inStep, inoStep);
+ } else if (color == 0xFFFFFFFF && blendMode == BLEND_NORMAL && _alphaMode == ALPHA_BINARY) {
+ doBlitBinaryFast(ino, outo, img->w, img->h, target.pitch, inStep, inoStep);
+ } else {
+ if (blendMode == BLEND_ADDITIVE) {
+ doBlitAdditiveBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
+ } else if (blendMode == BLEND_SUBTRACTIVE) {
+ doBlitSubtractiveBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
+ } else {
+ assert(blendMode == BLEND_NORMAL);
+ doBlitAlphaBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
+ }
+ }
+
+ }
+
+ retSize.setWidth(img->w);
+ retSize.setHeight(img->h);
+
+ if (imgScaled) {
+ imgScaled->setPixels(savedPixels);
+ imgScaled->free();
+ delete imgScaled;
+ }
+
+ return retSize;
+}
+
/**
* Writes a color key to the alpha channel of the surface
* @param rKey the red component of the color key
diff --git a/graphics/transparent_surface.h b/graphics/transparent_surface.h
index 0cd7d5b2e9..c0d3d26e52 100644
--- a/graphics/transparent_surface.h
+++ b/graphics/transparent_surface.h
@@ -123,6 +123,14 @@ struct TransparentSurface : public Graphics::Surface {
uint color = TS_ARGB(255, 255, 255, 255),
int width = -1, int height = -1,
TSpriteBlendMode blend = BLEND_NORMAL);
+ Common::Rect blitClip(Graphics::Surface &target, Common::Rect clippingArea,
+ int posX = 0, int posY = 0,
+ int flipping = FLIP_NONE,
+ Common::Rect *pPartRect = nullptr,
+ uint color = TS_ARGB(255, 255, 255, 255),
+ int width = -1, int height = -1,
+ TSpriteBlendMode blend = BLEND_NORMAL);
+
void applyColorKey(uint8 r, uint8 g, uint8 b, bool overwriteAlpha = false);
/**
diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp
index 0ed020ebb8..c850a6a02e 100644
--- a/gui/ThemeEngine.cpp
+++ b/gui/ThemeEngine.cpp
@@ -121,6 +121,19 @@ protected:
const WidgetDrawData *_data;
};
+class ThemeItemDrawDataClip: public ThemeItem{
+public:
+ ThemeItemDrawDataClip(ThemeEngine *engine, const WidgetDrawData *data, const Common::Rect &area, const Common::Rect &clip, uint32 dynData) :
+ ThemeItem(engine, area), _dynamicData(dynData), _data(data), _clip(clip) {}
+
+ void drawSelf(bool draw, bool restore);
+
+protected:
+ uint32 _dynamicData;
+ const WidgetDrawData *_data;
+ const Common::Rect _clip;
+};
+
class ThemeItemTextData : public ThemeItem {
public:
ThemeItemTextData(ThemeEngine *engine, const TextDrawData *data, const TextColorData *color, const Common::Rect &area, const Common::Rect &textDrawableArea,
@@ -155,7 +168,18 @@ protected:
bool _alpha;
};
+class ThemeItemBitmapClip : public ThemeItem {
+public:
+ ThemeItemBitmapClip(ThemeEngine *engine, const Common::Rect &area, const Common::Rect &clip, const Graphics::Surface *bitmap, bool alpha) :
+ ThemeItem(engine, area), _bitmap(bitmap), _alpha(alpha), _clip(clip) {}
+ void drawSelf(bool draw, bool restore);
+
+protected:
+ const Graphics::Surface *_bitmap;
+ bool _alpha;
+ const Common::Rect _clip;
+};
/**********************************************************
* Data definitions for theme engine elements
@@ -242,16 +266,40 @@ void ThemeItemDrawData::drawSelf(bool draw, bool restore) {
_engine->addDirtyRect(extendedRect);
}
+void ThemeItemDrawDataClip::drawSelf(bool draw, bool restore) {
+
+ Common::Rect extendedRect = _area;
+ extendedRect.grow(_engine->kDirtyRectangleThreshold + _data->_backgroundOffset);
+
+ if (restore)
+ _engine->restoreBackground(extendedRect);
+
+ if (draw) {
+ Common::List<Graphics::DrawStep>::const_iterator step;
+ for (step = _data->_steps.begin(); step != _data->_steps.end(); ++step) {
+ _engine->renderer()->drawStepClip(_area, _clip, *step, _dynamicData);
+ }
+ }
+
+ extendedRect.clip(_clip);
+
+ _engine->addDirtyRect(extendedRect);
+}
+
void ThemeItemTextData::drawSelf(bool draw, bool restore) {
+ Common::Rect dirty = _textDrawableArea;
+ if (dirty.isEmpty()) dirty = _area;
+ else dirty.clip(_area);
+
if (_restoreBg || restore)
- _engine->restoreBackground(_area);
+ _engine->restoreBackground(dirty);
if (draw) {
_engine->renderer()->setFgColor(_color->r, _color->g, _color->b);
_engine->renderer()->drawString(_data->_fontPtr, _text, _area, _alignH, _alignV, _deltax, _ellipsis, _textDrawableArea);
}
- _engine->addDirtyRect(_area);
+ _engine->addDirtyRect(dirty);
}
void ThemeItemBitmap::drawSelf(bool draw, bool restore) {
@@ -268,7 +316,21 @@ void ThemeItemBitmap::drawSelf(bool draw, bool restore) {
_engine->addDirtyRect(_area);
}
+void ThemeItemBitmapClip::drawSelf(bool draw, bool restore) {
+ if (restore)
+ _engine->restoreBackground(_area);
+ if (draw) {
+ if (_alpha)
+ _engine->renderer()->blitAlphaBitmapClip(_bitmap, _area, _clip);
+ else
+ _engine->renderer()->blitSubSurfaceClip(_bitmap, _area, _clip);
+ }
+
+ Common::Rect dirtyRect = _area;
+ dirtyRect.clip(_clip);
+ _engine->addDirtyRect(dirtyRect);
+}
/**********************************************************
* ThemeEngine class
@@ -862,6 +924,30 @@ void ThemeEngine::queueDD(DrawData type, const Common::Rect &r, uint32 dynamic,
}
}
+void ThemeEngine::queueDDClip(DrawData type, const Common::Rect &r, const Common::Rect &clippingRect, uint32 dynamic, bool restore) {
+ if (_widgets[type] == 0)
+ return;
+
+ Common::Rect area = r;
+ area.clip(_screen.w, _screen.h);
+
+ ThemeItemDrawDataClip *q = new ThemeItemDrawDataClip(this, _widgets[type], area, clippingRect, dynamic);
+
+ if (_buffering) {
+ if (_widgets[type]->_buffer) {
+ _bufferQueue.push_back(q);
+ } else {
+ if (kDrawDataDefaults[type].parent != kDDNone && kDrawDataDefaults[type].parent != type)
+ queueDDClip(kDrawDataDefaults[type].parent, r, clippingRect);
+
+ _screenQueue.push_back(q);
+ }
+ } else {
+ q->drawSelf(!_widgets[type]->_buffer, restore || _widgets[type]->_buffer);
+ delete q;
+ }
+}
+
void ThemeEngine::queueDDText(TextData type, TextColor color, const Common::Rect &r, const Common::String &text, bool restoreBg,
bool ellipsis, Graphics::TextAlign alignH, TextAlignVertical alignV, int deltax, const Common::Rect &drawableTextArea) {
@@ -881,6 +967,28 @@ void ThemeEngine::queueDDText(TextData type, TextColor color, const Common::Rect
}
}
+void ThemeEngine::queueDDTextClip(TextData type, TextColor color, const Common::Rect &r, const Common::Rect &clippingArea, const Common::String &text, bool restoreBg,
+ bool ellipsis, Graphics::TextAlign alignH, TextAlignVertical alignV, int deltax, const Common::Rect &drawableTextArea) {
+
+ if (_texts[type] == 0)
+ return;
+
+ Common::Rect area = r;
+ area.clip(_screen.w, _screen.h);
+ Common::Rect textArea = drawableTextArea;
+ if (textArea.isEmpty()) textArea = clippingArea;
+ else textArea.clip(clippingArea);
+
+ ThemeItemTextData *q = new ThemeItemTextData(this, _texts[type], _textColors[color], area, textArea, text, alignH, alignV, ellipsis, restoreBg, deltax);
+
+ if (_buffering) {
+ _screenQueue.push_back(q);
+ } else {
+ q->drawSelf(true, false);
+ delete q;
+ }
+}
+
void ThemeEngine::queueBitmap(const Graphics::Surface *bitmap, const Common::Rect &r, bool alpha) {
Common::Rect area = r;
@@ -896,7 +1004,20 @@ void ThemeEngine::queueBitmap(const Graphics::Surface *bitmap, const Common::Rec
}
}
+void ThemeEngine::queueBitmapClip(const Graphics::Surface *bitmap, const Common::Rect &r, const Common::Rect &clip, bool alpha) {
+
+ Common::Rect area = r;
+ area.clip(_screen.w, _screen.h);
+ ThemeItemBitmapClip *q = new ThemeItemBitmapClip(this, area, clip, bitmap, alpha);
+
+ if (_buffering) {
+ _screenQueue.push_back(q);
+ } else {
+ q->drawSelf(true, false);
+ delete q;
+ }
+}
/**********************************************************
* Widget drawing functions
@@ -920,6 +1041,25 @@ void ThemeEngine::drawButton(const Common::Rect &r, const Common::String &str, W
queueDDText(getTextData(dd), getTextColor(dd), r, str, false, true, _widgets[dd]->_textAlignH, _widgets[dd]->_textAlignV);
}
+void ThemeEngine::drawButtonClip(const Common::Rect &r, const Common::Rect &clippingRect, const Common::String &str, WidgetStateInfo state, uint16 hints) {
+ if (!ready())
+ return;
+
+ DrawData dd = kDDButtonIdle;
+
+ if (state == kStateEnabled)
+ dd = kDDButtonIdle;
+ else if (state == kStateHighlight)
+ dd = kDDButtonHover;
+ else if (state == kStateDisabled)
+ dd = kDDButtonDisabled;
+ else if (state == kStatePressed)
+ dd = kDDButtonPressed;
+
+ queueDDClip(dd, r, clippingRect, 0, hints & WIDGET_CLEARBG);
+ queueDDTextClip(getTextData(dd), getTextColor(dd), r, clippingRect, str, false, true, _widgets[dd]->_textAlignH, _widgets[dd]->_textAlignV);
+}
+
void ThemeEngine::drawLineSeparator(const Common::Rect &r, WidgetStateInfo state) {
if (!ready())
return;
@@ -927,6 +1067,13 @@ void ThemeEngine::drawLineSeparator(const Common::Rect &r, WidgetStateInfo state
queueDD(kDDSeparator, r);
}
+void ThemeEngine::drawLineSeparatorClip(const Common::Rect &r, const Common::Rect &clippingRect, WidgetStateInfo state) {
+ if (!ready())
+ return;
+
+ queueDDClip(kDDSeparator, r, clippingRect);
+}
+
void ThemeEngine::drawCheckbox(const Common::Rect &r, const Common::String &str, bool checked, WidgetStateInfo state) {
if (!ready())
return;
@@ -953,6 +1100,32 @@ void ThemeEngine::drawCheckbox(const Common::Rect &r, const Common::String &str,
queueDDText(getTextData(dd), getTextColor(dd), r2, str, true, false, _widgets[kDDCheckboxDefault]->_textAlignH, _widgets[dd]->_textAlignV);
}
+void ThemeEngine::drawCheckboxClip(const Common::Rect &r, const Common::Rect &clip, const Common::String &str, bool checked, WidgetStateInfo state) {
+ if (!ready())
+ return;
+
+ Common::Rect r2 = r;
+ DrawData dd = kDDCheckboxDefault;
+
+ if (checked)
+ dd = kDDCheckboxSelected;
+
+ if (state == kStateDisabled)
+ dd = kDDCheckboxDisabled;
+
+ const int checkBoxSize = MIN((int)r.height(), getFontHeight());
+
+ r2.bottom = r2.top + checkBoxSize;
+ r2.right = r2.left + checkBoxSize;
+
+ queueDDClip(dd, r2, clip);
+
+ r2.left = r2.right + checkBoxSize;
+ r2.right = r.right;
+
+ queueDDTextClip(getTextData(dd), getTextColor(dd), r2, clip, str, true, false, _widgets[kDDCheckboxDefault]->_textAlignH, _widgets[dd]->_textAlignV);
+}
+
void ThemeEngine::drawRadiobutton(const Common::Rect &r, const Common::String &str, bool checked, WidgetStateInfo state) {
if (!ready())
return;
@@ -979,6 +1152,32 @@ void ThemeEngine::drawRadiobutton(const Common::Rect &r, const Common::String &s
queueDDText(getTextData(dd), getTextColor(dd), r2, str, true, false, _widgets[kDDRadiobuttonDefault]->_textAlignH, _widgets[dd]->_textAlignV);
}
+void ThemeEngine::drawRadiobuttonClip(const Common::Rect &r, const Common::Rect &clippingRect, const Common::String &str, bool checked, WidgetStateInfo state) {
+ if (!ready())
+ return;
+
+ Common::Rect r2 = r;
+ DrawData dd = kDDRadiobuttonDefault;
+
+ if (checked)
+ dd = kDDRadiobuttonSelected;
+
+ if (state == kStateDisabled)
+ dd = kDDRadiobuttonDisabled;
+
+ const int checkBoxSize = MIN((int)r.height(), getFontHeight());
+
+ r2.bottom = r2.top + checkBoxSize;
+ r2.right = r2.left + checkBoxSize;
+
+ queueDDClip(dd, r2, clippingRect);
+
+ r2.left = r2.right + checkBoxSize;
+ r2.right = r.right;
+
+ queueDDTextClip(getTextData(dd), getTextColor(dd), r2, clippingRect, str, true, false, _widgets[kDDRadiobuttonDefault]->_textAlignH, _widgets[dd]->_textAlignV);
+}
+
void ThemeEngine::drawSlider(const Common::Rect &r, int width, WidgetStateInfo state) {
if (!ready())
return;
@@ -999,6 +1198,26 @@ void ThemeEngine::drawSlider(const Common::Rect &r, int width, WidgetStateInfo s
queueDD(dd, r2);
}
+void ThemeEngine::drawSliderClip(const Common::Rect &r, const Common::Rect &clip, int width, WidgetStateInfo state) {
+ if (!ready())
+ return;
+
+ DrawData dd = kDDSliderFull;
+
+ if (state == kStateHighlight)
+ dd = kDDSliderHover;
+ else if (state == kStateDisabled)
+ dd = kDDSliderDisabled;
+
+ Common::Rect r2 = r;
+ r2.setWidth(MIN((int16)width, r.width()));
+ // r2.top++; r2.bottom--; r2.left++; r2.right--;
+
+ drawWidgetBackgroundClip(r, clip, 0, kWidgetBackgroundSlider, kStateEnabled);
+
+ queueDDClip(dd, r2, clip);
+}
+
void ThemeEngine::drawScrollbar(const Common::Rect &r, int sliderY, int sliderHeight, ScrollbarState scrollState, WidgetStateInfo state) {
if (!ready())
return;
@@ -1020,11 +1239,34 @@ void ThemeEngine::drawScrollbar(const Common::Rect &r, int sliderY, int sliderHe
r2.top += sliderY;
r2.bottom = r2.top + sliderHeight;
- r2.top += r.width() / 5;
- r2.bottom -= r.width() / 5;
+ //r2.top += r.width() / 5;
+ //r2.bottom -= r.width() / 5;
queueDD(scrollState == kScrollbarStateSlider ? kDDScrollbarHandleHover : kDDScrollbarHandleIdle, r2);
}
+void ThemeEngine::drawScrollbarClip(const Common::Rect &r, const Common::Rect &clippingRect, int sliderY, int sliderHeight, ScrollbarState scrollState, WidgetStateInfo state) {
+ if (!ready())
+ return;
+
+ queueDDClip(kDDScrollbarBase, r, clippingRect);
+
+ Common::Rect r2 = r;
+ const int buttonExtra = (r.width() * 120) / 100;
+
+ r2.bottom = r2.top + buttonExtra;
+ queueDDClip(scrollState == kScrollbarStateUp ? kDDScrollbarButtonHover : kDDScrollbarButtonIdle, r2, clippingRect, Graphics::VectorRenderer::kTriangleUp);
+
+ r2.translate(0, r.height() - r2.height());
+ queueDDClip(scrollState == kScrollbarStateDown ? kDDScrollbarButtonHover : kDDScrollbarButtonIdle, r2, clippingRect, Graphics::VectorRenderer::kTriangleDown);
+
+ r2 = r;
+ r2.left += 1;
+ r2.right -= 1;
+ r2.top += sliderY;
+ r2.bottom = r2.top + sliderHeight;
+ queueDDClip(scrollState == kScrollbarStateSlider ? kDDScrollbarHandleHover : kDDScrollbarHandleIdle, r2, clippingRect);
+}
+
void ThemeEngine::drawDialogBackground(const Common::Rect &r, DialogBackground bgtype, WidgetStateInfo state) {
if (!ready())
return;
@@ -1052,6 +1294,33 @@ void ThemeEngine::drawDialogBackground(const Common::Rect &r, DialogBackground b
}
}
+void ThemeEngine::drawDialogBackgroundClip(const Common::Rect &r, const Common::Rect &clip, DialogBackground bgtype, WidgetStateInfo state) {
+ if (!ready())
+ return;
+
+ switch (bgtype) {
+ case kDialogBackgroundMain:
+ queueDDClip(kDDMainDialogBackground, r, clip);
+ break;
+
+ case kDialogBackgroundSpecial:
+ queueDDClip(kDDSpecialColorBackground, r, clip);
+ break;
+
+ case kDialogBackgroundPlain:
+ queueDDClip(kDDPlainColorBackground, r, clip);
+ break;
+
+ case kDialogBackgroundTooltip:
+ queueDDClip(kDDTooltipBackground, r, clip);
+ break;
+
+ case kDialogBackgroundDefault:
+ queueDDClip(kDDDefaultBackground, r, clip);
+ break;
+ }
+}
+
void ThemeEngine::drawCaret(const Common::Rect &r, bool erase, WidgetStateInfo state) {
if (!ready())
return;
@@ -1063,6 +1332,17 @@ void ThemeEngine::drawCaret(const Common::Rect &r, bool erase, WidgetStateInfo s
queueDD(kDDCaret, r);
}
+void ThemeEngine::drawCaretClip(const Common::Rect &r, const Common::Rect &clip, bool erase, WidgetStateInfo state) {
+ if (!ready())
+ return;
+
+ if (erase) {
+ restoreBackground(r);
+ addDirtyRect(r);
+ } else
+ queueDDClip(kDDCaret, r, clip);
+}
+
void ThemeEngine::drawPopUpWidget(const Common::Rect &r, const Common::String &sel, int deltax, WidgetStateInfo state, Graphics::TextAlign align) {
if (!ready())
return;
@@ -1084,6 +1364,27 @@ void ThemeEngine::drawPopUpWidget(const Common::Rect &r, const Common::String &s
}
}
+void ThemeEngine::drawPopUpWidgetClip(const Common::Rect &r, const Common::Rect &clip, const Common::String &sel, int deltax, WidgetStateInfo state, Graphics::TextAlign align) {
+ if (!ready())
+ return;
+
+ DrawData dd = kDDPopUpIdle;
+
+ if (state == kStateEnabled)
+ dd = kDDPopUpIdle;
+ else if (state == kStateHighlight)
+ dd = kDDPopUpHover;
+ else if (state == kStateDisabled)
+ dd = kDDPopUpDisabled;
+
+ queueDDClip(dd, r, clip);
+
+ if (!sel.empty()) {
+ Common::Rect text(r.left + 3, r.top + 1, r.right - 10, r.bottom);
+ queueDDTextClip(getTextData(dd), getTextColor(dd), text, clip, sel, true, false, _widgets[dd]->_textAlignH, _widgets[dd]->_textAlignV, deltax);
+ }
+}
+
void ThemeEngine::drawSurface(const Common::Rect &r, const Graphics::Surface &surface, WidgetStateInfo state, int alpha, bool themeTrans) {
if (!ready())
return;
@@ -1091,6 +1392,13 @@ void ThemeEngine::drawSurface(const Common::Rect &r, const Graphics::Surface &su
queueBitmap(&surface, r, themeTrans);
}
+void ThemeEngine::drawSurfaceClip(const Common::Rect &r, const Common::Rect &clip, const Graphics::Surface &surface, WidgetStateInfo state, int alpha, bool themeTrans) {
+ if (!ready())
+ return;
+
+ queueBitmapClip(&surface, r, clip, themeTrans);
+}
+
void ThemeEngine::drawWidgetBackground(const Common::Rect &r, uint16 hints, WidgetBackground background, WidgetStateInfo state) {
if (!ready())
return;
@@ -1114,6 +1422,29 @@ void ThemeEngine::drawWidgetBackground(const Common::Rect &r, uint16 hints, Widg
}
}
+void ThemeEngine::drawWidgetBackgroundClip(const Common::Rect &r, const Common::Rect &clip, uint16 hints, WidgetBackground background, WidgetStateInfo state) {
+ if (!ready())
+ return;
+
+ switch (background) {
+ case kWidgetBackgroundBorderSmall:
+ queueDDClip(kDDWidgetBackgroundSmall, r, clip);
+ break;
+
+ case kWidgetBackgroundEditText:
+ queueDDClip(kDDWidgetBackgroundEditText, r, clip);
+ break;
+
+ case kWidgetBackgroundSlider:
+ queueDDClip(kDDWidgetBackgroundSlider, r, clip);
+ break;
+
+ default:
+ queueDDClip(kDDWidgetBackgroundDefault, r, clip);
+ break;
+ }
+}
+
void ThemeEngine::drawTab(const Common::Rect &r, int tabHeight, int tabWidth, const Common::Array<Common::String> &tabs, int active, uint16 hints, int titleVPad, WidgetStateInfo state) {
if (!ready())
return;
@@ -1142,6 +1473,34 @@ void ThemeEngine::drawTab(const Common::Rect &r, int tabHeight, int tabWidth, co
}
}
+void ThemeEngine::drawTabClip(const Common::Rect &r, const Common::Rect &clip, int tabHeight, int tabWidth, const Common::Array<Common::String> &tabs, int active, uint16 hints, int titleVPad, WidgetStateInfo state) {
+ if (!ready())
+ return;
+
+ queueDDClip(kDDTabBackground, Common::Rect(r.left, r.top, r.right, r.top + tabHeight), clip);
+
+ for (int i = 0; i < (int)tabs.size(); ++i) {
+ if (i == active)
+ continue;
+
+ if (r.left + i * tabWidth > r.right || r.left + (i + 1) * tabWidth > r.right)
+ continue;
+
+ Common::Rect tabRect(r.left + i * tabWidth, r.top, r.left + (i + 1) * tabWidth, r.top + tabHeight);
+ queueDDClip(kDDTabInactive, tabRect, clip);
+ queueDDTextClip(getTextData(kDDTabInactive), getTextColor(kDDTabInactive), tabRect, clip, tabs[i], false, false, _widgets[kDDTabInactive]->_textAlignH, _widgets[kDDTabInactive]->_textAlignV);
+ }
+
+ if (active >= 0 &&
+ (r.left + active * tabWidth < r.right) && (r.left + (active + 1) * tabWidth < r.right)) {
+ Common::Rect tabRect(r.left + active * tabWidth, r.top, r.left + (active + 1) * tabWidth, r.top + tabHeight);
+ const uint16 tabLeft = active * tabWidth;
+ const uint16 tabRight = MAX(r.right - tabRect.right, 0);
+ queueDDClip(kDDTabActive, tabRect, clip, (tabLeft << 16) | (tabRight & 0xFFFF));
+ queueDDTextClip(getTextData(kDDTabActive), getTextColor(kDDTabActive), tabRect, clip, tabs[active], false, false, _widgets[kDDTabActive]->_textAlignH, _widgets[kDDTabActive]->_textAlignV);
+ }
+}
+
void ThemeEngine::drawText(const Common::Rect &r, const Common::String &str, WidgetStateInfo state, Graphics::TextAlign align, TextInversionState inverted, int deltax, bool useEllipsis, FontStyle font, FontColor color, bool restore, const Common::Rect &drawableTextArea) {
if (!ready())
return;
@@ -1215,6 +1574,79 @@ void ThemeEngine::drawText(const Common::Rect &r, const Common::String &str, Wid
queueDDText(textId, colorId, r, str, restore, useEllipsis, align, kTextAlignVCenter, deltax, drawableTextArea);
}
+void ThemeEngine::drawTextClip(const Common::Rect &r, const Common::Rect &clippingArea, const Common::String &str, WidgetStateInfo state, Graphics::TextAlign align, TextInversionState inverted, int deltax, bool useEllipsis, FontStyle font, FontColor color, bool restore, const Common::Rect &drawableTextArea) {
+ if (!ready())
+ return;
+
+ TextColor colorId = kTextColorMAX;
+
+ switch (color) {
+ case kFontColorNormal:
+ if (inverted) {
+ colorId = kTextColorNormalInverted;
+ } else {
+ switch (state) {
+ case kStateDisabled:
+ colorId = kTextColorNormalDisabled;
+ break;
+
+ case kStateHighlight:
+ colorId = kTextColorNormalHover;
+ break;
+
+ case kStateEnabled:
+ case kStatePressed:
+ colorId = kTextColorNormal;
+ break;
+ }
+ }
+ break;
+
+ case kFontColorAlternate:
+ if (inverted) {
+ colorId = kTextColorAlternativeInverted;
+ } else {
+ switch (state) {
+ case kStateDisabled:
+ colorId = kTextColorAlternativeDisabled;
+ break;
+
+ case kStateHighlight:
+ colorId = kTextColorAlternativeHover;
+ break;
+
+ case kStateEnabled:
+ case kStatePressed:
+ colorId = kTextColorAlternative;
+ break;
+ }
+ }
+ break;
+
+ default:
+ return;
+ }
+
+ TextData textId = fontStyleToData(font);
+
+ switch (inverted) {
+ case kTextInversion:
+ queueDDClip(kDDTextSelectionBackground, r, clippingArea);
+ restore = false;
+ break;
+
+ case kTextInversionFocus:
+ queueDDClip(kDDTextSelectionFocusBackground, r, clippingArea);
+ restore = false;
+ break;
+
+ default:
+ break;
+ }
+
+ queueDDTextClip(textId, colorId, r, clippingArea, str, restore, useEllipsis, align, kTextAlignVCenter, deltax, drawableTextArea);
+}
+
void ThemeEngine::drawChar(const Common::Rect &r, byte ch, const Graphics::Font *font, WidgetStateInfo state, FontColor color) {
if (!ready())
return;
@@ -1229,6 +1661,21 @@ void ThemeEngine::drawChar(const Common::Rect &r, byte ch, const Graphics::Font
addDirtyRect(charArea);
}
+void ThemeEngine::drawCharClip(const Common::Rect &r, const Common::Rect &clip, byte ch, const Graphics::Font *font, WidgetStateInfo state, FontColor color) {
+ if (!ready())
+ return;
+
+ Common::Rect charArea = r;
+ charArea.clip(_screen.w, _screen.h);
+ if (!clip.isEmpty()) charArea.clip(clip);
+
+ uint32 rgbColor = _overlayFormat.RGBToColor(_textColors[color]->r, _textColors[color]->g, _textColors[color]->b);
+
+ restoreBackground(charArea);
+ font->drawChar(&_screen, ch, charArea.left, charArea.top, rgbColor);
+ addDirtyRect(charArea);
+}
+
void ThemeEngine::debugWidgetPosition(const char *name, const Common::Rect &r) {
_font->drawString(&_screen, name, r.left, r.top, r.width(), 0xFFFF, Graphics::kTextAlignRight, 0, true);
_screen.hLine(r.left, r.top, r.right, 0xFFFF);
diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h
index a5ef49c78b..3c259b4f9d 100644
--- a/gui/ThemeEngine.h
+++ b/gui/ThemeEngine.h
@@ -340,42 +340,67 @@ public:
void drawWidgetBackground(const Common::Rect &r, uint16 hints,
WidgetBackground background = kWidgetBackgroundPlain, WidgetStateInfo state = kStateEnabled);
+ void drawWidgetBackgroundClip(const Common::Rect &r, const Common::Rect &clippingArea, uint16 hints,
+ WidgetBackground background = kWidgetBackgroundPlain, WidgetStateInfo state = kStateEnabled);
void drawButton(const Common::Rect &r, const Common::String &str,
WidgetStateInfo state = kStateEnabled, uint16 hints = 0);
+ void drawButtonClip(const Common::Rect &r, const Common::Rect &clippingRect, const Common::String &str,
+ WidgetStateInfo state = kStateEnabled, uint16 hints = 0);
void drawSurface(const Common::Rect &r, const Graphics::Surface &surface,
- WidgetStateInfo state = kStateEnabled, int alpha = 256, bool themeTrans = false);
+ WidgetStateInfo state = kStateEnabled, int alpha = 255, bool themeTrans = false);
+ void drawSurfaceClip(const Common::Rect &r, const Common::Rect &clippingRect, const Graphics::Surface &surface,
+ WidgetStateInfo state = kStateEnabled, int alpha = 255, bool themeTrans = false);
void drawSlider(const Common::Rect &r, int width,
WidgetStateInfo state = kStateEnabled);
+ void drawSliderClip(const Common::Rect &r, const Common::Rect &clippingRect, int width,
+ WidgetStateInfo state = kStateEnabled);
void drawCheckbox(const Common::Rect &r, const Common::String &str,
bool checked, WidgetStateInfo state = kStateEnabled);
+ void drawCheckboxClip(const Common::Rect &r, const Common::Rect &clippingRect, const Common::String &str,
+ bool checked, WidgetStateInfo state = kStateEnabled);
void drawRadiobutton(const Common::Rect &r, const Common::String &str,
bool checked, WidgetStateInfo state = kStateEnabled);
+ void drawRadiobuttonClip(const Common::Rect &r, const Common::Rect &clippingRect, const Common::String &str,
+ bool checked, WidgetStateInfo state = kStateEnabled);
void drawTab(const Common::Rect &r, int tabHeight, int tabWidth,
const Common::Array<Common::String> &tabs, int active, uint16 hints,
int titleVPad, WidgetStateInfo state = kStateEnabled);
+ void drawTabClip(const Common::Rect &r, const Common::Rect &clippingRect, int tabHeight, int tabWidth,
+ const Common::Array<Common::String> &tabs, int active, uint16 hints,
+ int titleVPad, WidgetStateInfo state = kStateEnabled);
void drawScrollbar(const Common::Rect &r, int sliderY, int sliderHeight,
ScrollbarState, WidgetStateInfo state = kStateEnabled);
+ void drawScrollbarClip(const Common::Rect &r, const Common::Rect &clippingRect, int sliderY, int sliderHeight,
+ ScrollbarState scrollState, WidgetStateInfo state = kStateEnabled);
void drawPopUpWidget(const Common::Rect &r, const Common::String &sel,
int deltax, WidgetStateInfo state = kStateEnabled, Graphics::TextAlign align = Graphics::kTextAlignLeft);
+ void drawPopUpWidgetClip(const Common::Rect &r, const Common::Rect &clippingArea, const Common::String &sel,
+ int deltax, WidgetStateInfo state = kStateEnabled, Graphics::TextAlign align = Graphics::kTextAlignLeft);
void drawCaret(const Common::Rect &r, bool erase,
WidgetStateInfo state = kStateEnabled);
+ void drawCaretClip(const Common::Rect &r, const Common::Rect &clip, bool erase,
+ WidgetStateInfo state = kStateEnabled);
void drawLineSeparator(const Common::Rect &r, WidgetStateInfo state = kStateEnabled);
+ void drawLineSeparatorClip(const Common::Rect &r, const Common::Rect &clippingArea, WidgetStateInfo state = kStateEnabled);
void drawDialogBackground(const Common::Rect &r, DialogBackground type, WidgetStateInfo state = kStateEnabled);
+ void drawDialogBackgroundClip(const Common::Rect &r, const Common::Rect &clip, DialogBackground type, WidgetStateInfo state = kStateEnabled);
void drawText(const Common::Rect &r, const Common::String &str, WidgetStateInfo state = kStateEnabled, Graphics::TextAlign align = Graphics::kTextAlignCenter, TextInversionState inverted = kTextInversionNone, int deltax = 0, bool useEllipsis = true, FontStyle font = kFontStyleBold, FontColor color = kFontColorNormal, bool restore = true, const Common::Rect &drawableTextArea = Common::Rect(0, 0, 0, 0));
+ void drawTextClip(const Common::Rect &r, const Common::Rect &clippingArea, const Common::String &str, WidgetStateInfo state = kStateEnabled, Graphics::TextAlign align = Graphics::kTextAlignCenter, TextInversionState inverted = kTextInversionNone, int deltax = 0, bool useEllipsis = true, FontStyle font = kFontStyleBold, FontColor color = kFontColorNormal, bool restore = true, const Common::Rect &drawableTextArea = Common::Rect(0, 0, 0, 0));
void drawChar(const Common::Rect &r, byte ch, const Graphics::Font *font, WidgetStateInfo state = kStateEnabled, FontColor color = kFontColorNormal);
+ void drawCharClip(const Common::Rect &r, const Common::Rect &clippingArea, byte ch, const Graphics::Font *font, WidgetStateInfo state = kStateEnabled, FontColor color = kFontColorNormal);
//@}
@@ -584,9 +609,13 @@ protected:
* This function is called from all the Widget Drawing methods.
*/
void queueDD(DrawData type, const Common::Rect &r, uint32 dynamic = 0, bool restore = false);
+ void queueDDClip(DrawData type, const Common::Rect &r, const Common::Rect &clippingRect, uint32 dynamic = 0, bool restore = false);
void queueDDText(TextData type, TextColor color, const Common::Rect &r, const Common::String &text, bool restoreBg,
bool elipsis, Graphics::TextAlign alignH = Graphics::kTextAlignLeft, TextAlignVertical alignV = kTextAlignVTop, int deltax = 0, const Common::Rect &drawableTextArea = Common::Rect(0, 0, 0, 0));
+ void queueDDTextClip(TextData type, TextColor color, const Common::Rect &r, const Common::Rect &clippingRect, const Common::String &text, bool restoreBg,
+ bool elipsis, Graphics::TextAlign alignH = Graphics::kTextAlignLeft, TextAlignVertical alignV = kTextAlignVTop, int deltax = 0, const Common::Rect &drawableTextArea = Common::Rect(0, 0, 0, 0));
void queueBitmap(const Graphics::Surface *bitmap, const Common::Rect &r, bool alpha);
+ void queueBitmapClip(const Graphics::Surface *bitmap, const Common::Rect &clippingRect, const Common::Rect &r, bool alpha);
/**
* DEBUG: Draws a white square and writes some text next to it.
diff --git a/gui/ThemeEval.cpp b/gui/ThemeEval.cpp
index 9d57d2408b..5255587089 100644
--- a/gui/ThemeEval.cpp
+++ b/gui/ThemeEval.cpp
@@ -91,10 +91,18 @@ void ThemeEval::addWidget(const Common::String &name, int w, int h, const Common
typeAlign = (Graphics::TextAlign)getVar("Globals." + type + ".Align", Graphics::kTextAlignInvalid);
}
- ThemeLayoutWidget *widget = new ThemeLayoutWidget(_curLayout.top(), name,
- typeW == -1 ? w : typeW,
- typeH == -1 ? h : typeH,
- typeAlign == Graphics::kTextAlignInvalid ? align : typeAlign);
+ ThemeLayoutWidget *widget;
+ if (type == "TabWidget")
+ widget = new ThemeLayoutTabWidget(_curLayout.top(), name,
+ typeW == -1 ? w : typeW,
+ typeH == -1 ? h : typeH,
+ typeAlign == Graphics::kTextAlignInvalid ? align : typeAlign,
+ getVar("Globals.TabWidget.Tab.Height", 0));
+ else
+ widget = new ThemeLayoutWidget(_curLayout.top(), name,
+ typeW == -1 ? w : typeW,
+ typeH == -1 ? h : typeH,
+ typeAlign == Graphics::kTextAlignInvalid ? align : typeAlign);
_curLayout.top()->addChild(widget);
setVar(_curDialog + "." + name + ".Enabled", enabled ? 1 : 0);
diff --git a/gui/ThemeLayout.h b/gui/ThemeLayout.h
index c4d7e672dd..e738002aa6 100644
--- a/gui/ThemeLayout.h
+++ b/gui/ThemeLayout.h
@@ -45,7 +45,8 @@ public:
kLayoutMain,
kLayoutVertical,
kLayoutHorizontal,
- kLayoutWidget
+ kLayoutWidget,
+ kLayoutTabWidget
};
ThemeLayout(ThemeLayout *p) :
@@ -223,6 +224,41 @@ protected:
Common::String _name;
};
+class ThemeLayoutTabWidget : public ThemeLayoutWidget {
+ int _tabHeight;
+
+public:
+ ThemeLayoutTabWidget(ThemeLayout *p, const Common::String &name, int16 w, int16 h, Graphics::TextAlign align, int tabHeight):
+ ThemeLayoutWidget(p, name, w, h, align) {
+ _tabHeight = tabHeight;
+ }
+
+ void reflowLayout() {
+ for (uint i = 0; i < _children.size(); ++i) {
+ _children[i]->resetLayout();
+ _children[i]->reflowLayout();
+ }
+ }
+
+ virtual bool getWidgetData(const Common::String &name, int16 &x, int16 &y, uint16 &w, uint16 &h) {
+ if (ThemeLayoutWidget::getWidgetData(name, x, y, w, h)) {
+ h -= _tabHeight;
+ return true;
+ }
+
+ return false;
+ }
+
+protected:
+ LayoutType getLayoutType() { return kLayoutTabWidget; }
+
+ ThemeLayout *makeClone(ThemeLayout *newParent) {
+ ThemeLayoutTabWidget *n = new ThemeLayoutTabWidget(*this);
+ n->_parent = newParent;
+ return n;
+ }
+};
+
class ThemeLayoutSpacing : public ThemeLayout {
public:
ThemeLayoutSpacing(ThemeLayout *p, int size) : ThemeLayout(p) {
diff --git a/gui/dialog.cpp b/gui/dialog.cpp
index 50b7755bb3..523227a237 100644
--- a/gui/dialog.cpp
+++ b/gui/dialog.cpp
@@ -113,13 +113,13 @@ void Dialog::reflowLayout() {
// changed, so any cached image may be invalid. The subsequent redraw
// should be treated as the very first draw.
+ GuiObject::reflowLayout();
+
Widget *w = _firstWidget;
while (w) {
w->reflowLayout();
w = w->_next;
}
-
- GuiObject::reflowLayout();
}
void Dialog::lostFocus() {
diff --git a/gui/gui-manager.cpp b/gui/gui-manager.cpp
index 3ce8bee020..9acd9434ff 100644
--- a/gui/gui-manager.cpp
+++ b/gui/gui-manager.cpp
@@ -564,6 +564,12 @@ void GuiManager::processEvent(const Common::Event &event, Dialog *const activeDi
}
}
+void GuiManager::doFullRedraw() {
+ _redrawStatus = kRedrawFull;
+ redraw();
+ _system->updateScreen();
+}
+
void GuiManager::giveFocusToDialog(Dialog *dialog) {
int16 dialogX = _globalMousePosition.x - dialog->_x;
int16 dialogY = _globalMousePosition.y - dialog->_y;
diff --git a/gui/gui-manager.h b/gui/gui-manager.h
index 35779215b2..4dc9af95fb 100644
--- a/gui/gui-manager.h
+++ b/gui/gui-manager.h
@@ -73,6 +73,7 @@ public:
void runLoop();
void processEvent(const Common::Event &event, Dialog *const activeDialog);
+ void doFullRedraw();
bool isActive() const { return ! _dialogStack.empty(); }
diff --git a/gui/module.mk b/gui/module.mk
index 9e821e71a7..6cbc63d24d 100644
--- a/gui/module.mk
+++ b/gui/module.mk
@@ -30,6 +30,7 @@ MODULE_OBJS := \
widgets/list.o \
widgets/popup.o \
widgets/scrollbar.o \
+ widgets/scrollcontainer.o \
widgets/tab.o
# HACK: create_project's XCode generator relies on the following ifdef
diff --git a/gui/object.cpp b/gui/object.cpp
index ef2cc9d6e0..de66d95492 100644
--- a/gui/object.cpp
+++ b/gui/object.cpp
@@ -44,19 +44,6 @@ void GuiObject::reflowLayout() {
if (!g_gui.xmlEval()->getWidgetData(_name, _x, _y, _w, _h)) {
error("Could not load widget position for '%s'", _name.c_str());
}
-
- if (_x < 0)
- error("Widget <%s> has x < 0 (%d)", _name.c_str(), _x);
- if (_x >= g_gui.getWidth())
- error("Widget <%s> has x > %d (%d)", _name.c_str(), g_gui.getWidth(), _x);
- if (_x + _w > g_gui.getWidth())
- error("Widget <%s> has x + w > %d (%d)", _name.c_str(), g_gui.getWidth(), _x + _w);
- if (_y < 0)
- error("Widget <%s> has y < 0 (%d)", _name.c_str(), _y);
- if (_y >= g_gui.getHeight())
- error("Widget <%s> has y > %d (%d)", _name.c_str(), g_gui.getHeight(), _y);
- if (_y + _h > g_gui.getHeight())
- error("Widget <%s> has y + h > %d (%d)", _name.c_str(), g_gui.getHeight(), _y + _h);
}
}
diff --git a/gui/themes/default.inc b/gui/themes/default.inc
index a23e2f4c30..c0ea733de8 100644
--- a/gui/themes/default.inc
+++ b/gui/themes/default.inc
@@ -798,7 +798,7 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
"</dialog>"
"<dialog name='GlobalOptions' overlays='Dialog.Launcher.GameList' shading='dim'>"
"<layout type='vertical' padding='0,0,0,0'>"
-"<widget name='TabWidget'/>"
+"<widget name='TabWidget' type='TabWidget'/>"
"<layout type='horizontal' padding='16,16,16,16'>"
"<space/>"
"<widget name='Cancel' "
@@ -1118,7 +1118,7 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
"</dialog>"
"<dialog name='GameOptions' overlays='Dialog.Launcher.GameList' shading='dim'>"
"<layout type='vertical' padding='0,0,0,0' spacing='16'>"
-"<widget name='TabWidget'/>"
+"<widget name='TabWidget' type='TabWidget'/>"
"<layout type='horizontal' padding='16,16,16,4'>"
"<space/>"
"<widget name='Cancel' "
@@ -1405,7 +1405,7 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
"</dialog>"
"<dialog name='FluidSynthSettings' overlays='GlobalOptions' shading='dim'>"
"<layout type='vertical' padding='0,0,0,0'>"
-"<widget name='TabWidget'/>"
+"<widget name='TabWidget' type='TabWidget'/>"
"<layout type='horizontal' padding='16,16,16,16'>"
"<space/>"
"<widget name='ResetSettings' "
@@ -1975,7 +1975,7 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
"padding='0,0,2,0' "
"/>"
"<widget name='TabWidget.Body' "
-"padding='0,0,0,-8' "
+"padding='0,0,0,0' "
"/>"
"<widget name='TabWidget.NavButton' "
"size='32,18' "
@@ -2093,7 +2093,7 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
"</dialog>"
"<dialog name='GlobalOptions' overlays='screen' inset='16' shading='dim'>"
"<layout type='vertical' padding='0,0,0,0'>"
-"<widget name='TabWidget'/>"
+"<widget name='TabWidget' type='TabWidget'/>"
"<layout type='horizontal' padding='8,8,8,8'>"
"<space/>"
"<widget name='Cancel' "
@@ -2420,7 +2420,7 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
"</dialog>"
"<dialog name='GameOptions' overlays='screen' inset='16' shading='dim'>"
"<layout type='vertical' padding='0,0,0,0' spacing='16'>"
-"<widget name='TabWidget'/>"
+"<widget name='TabWidget' type='TabWidget'/>"
"<layout type='horizontal' padding='8,8,8,8'>"
"<space/>"
"<widget name='Cancel' "
@@ -2716,7 +2716,7 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
"</dialog>"
"<dialog name='FluidSynthSettings' overlays='GlobalOptions' shading='dim'>"
"<layout type='vertical' padding='0,0,0,0'>"
-"<widget name='TabWidget'/>"
+"<widget name='TabWidget' type='TabWidget'/>"
"<layout type='horizontal' padding='8,8,8,8'>"
"<space/>"
"<widget name='ResetSettings' "
diff --git a/gui/themes/scummclassic.zip b/gui/themes/scummclassic.zip
index 43fcea12fd..561f2a5dd3 100644
--- a/gui/themes/scummclassic.zip
+++ b/gui/themes/scummclassic.zip
Binary files differ
diff --git a/gui/themes/scummclassic/classic_layout.stx b/gui/themes/scummclassic/classic_layout.stx
index 65724d9faf..5172326859 100644
--- a/gui/themes/scummclassic/classic_layout.stx
+++ b/gui/themes/scummclassic/classic_layout.stx
@@ -222,7 +222,7 @@
<dialog name = 'GlobalOptions' overlays = 'Dialog.Launcher.GameList' shading = 'dim'>
<layout type = 'vertical' padding = '0, 0, 0, 0'>
- <widget name = 'TabWidget'/>
+ <widget name = 'TabWidget' type = 'TabWidget'/>
<layout type = 'horizontal' padding = '16, 16, 16, 16'>
<space/>
<widget name = 'Cancel'
@@ -551,7 +551,7 @@
<dialog name = 'GameOptions' overlays = 'Dialog.Launcher.GameList' shading = 'dim'>
<layout type = 'vertical' padding = '0, 0, 0, 0' spacing = '16'>
- <widget name = 'TabWidget'/>
+ <widget name = 'TabWidget' type = 'TabWidget'/>
<layout type = 'horizontal' padding = '16, 16, 16, 4'>
<space/>
<widget name = 'Cancel'
@@ -850,7 +850,7 @@
<dialog name = 'FluidSynthSettings' overlays = 'GlobalOptions' shading = 'dim'>
<layout type = 'vertical' padding = '0, 0, 0, 0'>
- <widget name = 'TabWidget'/>
+ <widget name = 'TabWidget' type = 'TabWidget'/>
<layout type = 'horizontal' padding = '16, 16, 16, 16'>
<space/>
<widget name = 'ResetSettings'
diff --git a/gui/themes/scummclassic/classic_layout_lowres.stx b/gui/themes/scummclassic/classic_layout_lowres.stx
index f73f6e864b..0013b91ee2 100644
--- a/gui/themes/scummclassic/classic_layout_lowres.stx
+++ b/gui/themes/scummclassic/classic_layout_lowres.stx
@@ -97,7 +97,7 @@
padding = '0, 0, 2, 0'
/>
<widget name = 'TabWidget.Body'
- padding = '0, 0, 0, -8'
+ padding = '0, 0, 0, 0'
/>
<widget name = 'TabWidget.NavButton'
size = '32, 18'
@@ -219,7 +219,7 @@
<dialog name = 'GlobalOptions' overlays = 'screen' inset = '16' shading = 'dim'>
<layout type = 'vertical' padding = '0, 0, 0, 0'>
- <widget name = 'TabWidget'/>
+ <widget name = 'TabWidget' type = 'TabWidget'/>
<layout type = 'horizontal' padding = '8, 8, 8, 8'>
<space/>
<widget name = 'Cancel'
@@ -556,7 +556,7 @@
<dialog name = 'GameOptions' overlays = 'screen' inset = '16' shading = 'dim'>
<layout type = 'vertical' padding = '0, 0, 0, 0' spacing = '16'>
- <widget name = 'TabWidget'/>
+ <widget name = 'TabWidget' type = 'TabWidget'/>
<layout type = 'horizontal' padding = '8, 8, 8, 8'>
<space/>
<widget name = 'Cancel'
@@ -863,7 +863,7 @@
<dialog name = 'FluidSynthSettings' overlays = 'GlobalOptions' shading = 'dim'>
<layout type = 'vertical' padding = '0, 0, 0, 0'>
- <widget name = 'TabWidget'/>
+ <widget name = 'TabWidget' type = 'TabWidget'/>
<layout type = 'horizontal' padding = '8, 8, 8, 8'>
<space/>
<widget name = 'ResetSettings'
diff --git a/gui/themes/scummmodern.zip b/gui/themes/scummmodern.zip
index 70f7672c57..d80c481ffc 100644
--- a/gui/themes/scummmodern.zip
+++ b/gui/themes/scummmodern.zip
Binary files differ
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index c73ffa1f08..026fa7bc64 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -236,7 +236,7 @@
<dialog name = 'GlobalOptions' overlays = 'Dialog.Launcher.GameList' shading = 'dim'>
<layout type = 'vertical' padding = '0, 0, 0, 0'>
- <widget name = 'TabWidget'/>
+ <widget name = 'TabWidget' type = 'TabWidget'/>
<layout type = 'horizontal' padding = '16, 16, 16, 16'>
<space/>
<widget name = 'Cancel'
@@ -565,7 +565,7 @@
<dialog name = 'GameOptions' overlays = 'Dialog.Launcher.GameList' shading = 'dim'>
<layout type = 'vertical' padding = '0, 0, 0, 0' spacing = '16'>
- <widget name = 'TabWidget'/>
+ <widget name = 'TabWidget' type = 'TabWidget'/>
<layout type = 'horizontal' padding = '16, 16, 16, 4'>
<space/>
<widget name = 'Cancel'
@@ -864,7 +864,7 @@
<dialog name = 'FluidSynthSettings' overlays = 'GlobalOptions' shading = 'dim'>
<layout type = 'vertical' padding = '0, 0, 0, 0'>
- <widget name = 'TabWidget'/>
+ <widget name = 'TabWidget' type = 'TabWidget'/>
<layout type = 'horizontal' padding = '16, 16, 16, 16'>
<space/>
<widget name = 'ResetSettings'
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index 43ec0cdee1..169e61a9bb 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -95,7 +95,7 @@
padding = '0, 0, 2, 0'
/>
<widget name = 'TabWidget.Body'
- padding = '0, 0, 0, -8'
+ padding = '0, 0, 0, 0'
/>
<widget name = 'TabWidget.NavButton'
size = '32, 18'
@@ -217,7 +217,7 @@
<dialog name = 'GlobalOptions' overlays = 'screen' inset = '16' shading = 'dim'>
<layout type = 'vertical' padding = '0, 0, 0, 0'>
- <widget name = 'TabWidget'/>
+ <widget name = 'TabWidget' type = 'TabWidget'/>
<layout type = 'horizontal' padding = '8, 8, 8, 8'>
<space/>
<widget name = 'Cancel'
@@ -554,7 +554,7 @@
<dialog name = 'GameOptions' overlays = 'screen' inset = '16' shading = 'dim'>
<layout type = 'vertical' padding = '0, 0, 0, 0'>
- <widget name = 'TabWidget'/>
+ <widget name = 'TabWidget' type = 'TabWidget'/>
<layout type = 'horizontal' padding = '8, 8, 8, 8'>
<space/>
<widget name = 'Cancel'
@@ -861,7 +861,7 @@
<dialog name = 'FluidSynthSettings' overlays = 'GlobalOptions' shading = 'dim'>
<layout type = 'vertical' padding = '0, 0, 0, 0'>
- <widget name = 'TabWidget'/>
+ <widget name = 'TabWidget' type = 'TabWidget'/>
<layout type = 'horizontal' padding = '8, 8, 8, 8'>
<space/>
<widget name = 'ResetSettings'
diff --git a/gui/widget.cpp b/gui/widget.cpp
index 73d055527c..f2a29c3100 100644
--- a/gui/widget.cpp
+++ b/gui/widget.cpp
@@ -53,6 +53,31 @@ void Widget::init() {
_boss->_firstWidget = this;
}
+Common::Rect Widget::getBossClipRect() const {
+ int bx = _boss->getAbsX();
+ int by = _boss->getAbsY();
+ Common::Rect result = Common::Rect(bx, by, bx + _boss->getWidth(), by + _boss->getHeight());
+ bool needsClipping = false;
+
+ //check whether clipping area is inside the screen
+ if (result.left < 0 && (needsClipping = true))
+ warning("Widget <%s> has clipping area x < 0 (%d)", _name.c_str(), result.left);
+ if (result.left >= g_gui.getWidth() && (needsClipping = true))
+ warning("Widget <%s> has clipping area x > %d (%d)", _name.c_str(), g_gui.getWidth(), result.left);
+ if (result.right > g_gui.getWidth() && (needsClipping = true))
+ warning("Widget <%s> has clipping area x + w > %d (%d)", _name.c_str(), g_gui.getWidth(), result.right);
+ if (result.top < 0 && (needsClipping = true))
+ warning("Widget <%s> has clipping area y < 0 (%d)", _name.c_str(), result.top);
+ if (result.top >= g_gui.getHeight() && (needsClipping = true))
+ warning("Widget <%s> has clipping area y > %d (%d)", _name.c_str(), g_gui.getHeight(), result.top);
+ if (result.bottom > g_gui.getHeight() && (needsClipping = true))
+ warning("Widget <%s> has clipping area y + h > %d (%d)", _name.c_str(), g_gui.getHeight(), result.bottom);
+
+ if (needsClipping)
+ result.clip(g_gui.getWidth(), g_gui.getHeight());
+ return result;
+}
+
Widget::~Widget() {
delete _next;
_next = 0;
@@ -99,7 +124,7 @@ void Widget::draw() {
// Draw border
if (_flags & WIDGET_BORDER) {
- g_gui.theme()->drawWidgetBackground(Common::Rect(_x, _y, _x+_w, _y+_h), 0, ThemeEngine::kWidgetBackgroundBorder);
+ g_gui.theme()->drawWidgetBackgroundClip(Common::Rect(_x, _y, _x+_w, _y+_h), getBossClipRect(), 0, ThemeEngine::kWidgetBackgroundBorder);
_x += 4;
_y += 4;
_w -= 8;
@@ -131,7 +156,7 @@ void Widget::draw() {
Widget *Widget::findWidgetInChain(Widget *w, int x, int y) {
while (w) {
// Stop as soon as we find a widget that contains the point (x,y)
- if (x >= w->_x && x < w->_x + w->_w && y >= w->_y && y < w->_y + w->_h)
+ if (x >= w->_x && x < w->_x + w->_w && y >= w->_y && y < w->_y + w->getHeight())
break;
w = w->_next;
}
@@ -280,7 +305,10 @@ void StaticTextWidget::setAlign(Graphics::TextAlign align) {
void StaticTextWidget::drawWidget() {
- g_gui.theme()->drawText(Common::Rect(_x, _y, _x+_w, _y+_h), _label, _state, _align, ThemeEngine::kTextInversionNone, 0, true, _font);
+ g_gui.theme()->drawTextClip(
+ Common::Rect(_x, _y, _x+_w, _y+_h), getBossClipRect(),
+ _label, _state, _align, ThemeEngine::kTextInversionNone, 0, true, _font
+ );
}
#pragma mark -
@@ -319,7 +347,10 @@ void ButtonWidget::handleMouseDown(int x, int y, int button, int clickCount) {
}
void ButtonWidget::drawWidget() {
- g_gui.theme()->drawButton(Common::Rect(_x, _y, _x+_w, _y+_h), _label, _state, getFlags());
+ g_gui.theme()->drawButtonClip(
+ Common::Rect(_x, _y, _x + _w, _y + _h), getBossClipRect(),
+ _label, _state, getFlags()
+ );
}
void ButtonWidget::setLabel(const Common::String &label) {
@@ -418,7 +449,7 @@ void PicButtonWidget::setGfx(int w, int h, int r, int g, int b) {
}
void PicButtonWidget::drawWidget() {
- g_gui.theme()->drawButton(Common::Rect(_x, _y, _x+_w, _y+_h), "", _state, getFlags());
+ g_gui.theme()->drawButtonClip(Common::Rect(_x, _y, _x + _w, _y + _h), getBossClipRect(), "", _state, getFlags());
if (_gfx.getPixels()) {
// Check whether the set up surface needs to be converted to the GUI
@@ -431,7 +462,7 @@ void PicButtonWidget::drawWidget() {
const int x = _x + (_w - _gfx.w) / 2;
const int y = _y + (_h - _gfx.h) / 2;
- g_gui.theme()->drawSurface(Common::Rect(x, y, x + _gfx.w, y + _gfx.h), _gfx, _state, _alpha, _transparency);
+ g_gui.theme()->drawSurfaceClip(Common::Rect(x, y, x + _gfx.w, y + _gfx.h), getBossClipRect(), _gfx, _state, _alpha, _transparency);
}
}
@@ -466,7 +497,7 @@ void CheckboxWidget::setState(bool state) {
}
void CheckboxWidget::drawWidget() {
- g_gui.theme()->drawCheckbox(Common::Rect(_x, _y, _x+_w, _y+_h), _label, _state, Widget::_state);
+ g_gui.theme()->drawCheckboxClip(Common::Rect(_x, _y, _x+_w, _y+_h), getBossClipRect(), _label, _state, Widget::_state);
}
#pragma mark -
@@ -535,7 +566,7 @@ void RadiobuttonWidget::setState(bool state, bool setGroup) {
}
void RadiobuttonWidget::drawWidget() {
- g_gui.theme()->drawRadiobutton(Common::Rect(_x, _y, _x+_w, _y+_h), _label, _state, Widget::_state);
+ g_gui.theme()->drawRadiobuttonClip(Common::Rect(_x, _y, _x+_w, _y+_h), getBossClipRect(), _label, _state, Widget::_state);
}
#pragma mark -
@@ -603,7 +634,7 @@ void SliderWidget::handleMouseWheel(int x, int y, int direction) {
}
void SliderWidget::drawWidget() {
- g_gui.theme()->drawSlider(Common::Rect(_x, _y, _x + _w, _y + _h), valueToBarWidth(_value), _state);
+ g_gui.theme()->drawSliderClip(Common::Rect(_x, _y, _x + _w, _y + _h), getBossClipRect(), valueToBarWidth(_value), _state);
}
int SliderWidget::valueToBarWidth(int value) {
@@ -680,7 +711,7 @@ void GraphicsWidget::drawWidget() {
const int x = _x + (_w - _gfx.w) / 2;
const int y = _y + (_h - _gfx.h) / 2;
- g_gui.theme()->drawSurface(Common::Rect(x, y, x + _gfx.w, y + _gfx.h), _gfx, _state, _alpha, _transparency);
+ g_gui.theme()->drawSurfaceClip(Common::Rect(x, y, x + _gfx.w, y + _gfx.h), getBossClipRect(), _gfx, _state, _alpha, _transparency);
}
}
@@ -717,7 +748,7 @@ void ContainerWidget::removeWidget(Widget *widget) {
}
void ContainerWidget::drawWidget() {
- g_gui.theme()->drawWidgetBackground(Common::Rect(_x, _y, _x + _w, _y + _h), 0, ThemeEngine::kWidgetBackgroundBorder);
+ g_gui.theme()->drawWidgetBackgroundClip(Common::Rect(_x, _y, _x + _w, _y + _h), getBossClipRect(), 0, ThemeEngine::kWidgetBackgroundBorder);
}
} // End of namespace GUI
diff --git a/gui/widget.h b/gui/widget.h
index 7f6f0c0533..0f4b300233 100644
--- a/gui/widget.h
+++ b/gui/widget.h
@@ -68,7 +68,8 @@ enum {
kPopUpWidget = 'POPU',
kTabWidget = 'TABW',
kGraphicsWidget = 'GFXW',
- kContainerWidget = 'CTNR'
+ kContainerWidget = 'CTNR',
+ kScrollContainerWidget = 'SCTR'
};
enum {
@@ -111,6 +112,7 @@ public:
virtual int16 getAbsX() const { return _x + _boss->getChildX(); }
virtual int16 getAbsY() const { return _y + _boss->getChildY(); }
+ virtual Common::Rect getBossClipRect() const;
virtual void setPos(int x, int y) { _x = x; _y = y; }
virtual void setSize(int w, int h) { _w = w; _h = h; }
diff --git a/gui/widgets/editable.cpp b/gui/widgets/editable.cpp
index 2d929113b1..4f7e584c14 100644
--- a/gui/widgets/editable.cpp
+++ b/gui/widgets/editable.cpp
@@ -274,7 +274,7 @@ void EditableWidget::drawCaret(bool erase) {
x += getAbsX();
y += getAbsY();
- g_gui.theme()->drawCaret(Common::Rect(x, y, x + 1, y + editRect.height()), erase);
+ g_gui.theme()->drawCaretClip(Common::Rect(x, y, x + 1, y + editRect.height()), getBossClipRect(), erase);
if (erase) {
GUI::EditableWidget::String character;
@@ -303,7 +303,7 @@ void EditableWidget::drawCaret(bool erase) {
// possible glitches due to different methods used.
width = MIN(editRect.width() - caretOffset, width);
if (width > 0) {
- g_gui.theme()->drawText(Common::Rect(x, y, x + width, y + editRect.height()), character, _state, Graphics::kTextAlignLeft, _inversion, 0, false, _font, ThemeEngine::kFontColorNormal, true, _textDrawableArea);
+ g_gui.theme()->drawTextClip(Common::Rect(x, y, x + width, y + editRect.height()), getBossClipRect(), character, _state, Graphics::kTextAlignLeft, _inversion, 0, false, _font, ThemeEngine::kFontColorNormal, true, _textDrawableArea);
}
}
diff --git a/gui/widgets/edittext.cpp b/gui/widgets/edittext.cpp
index bef90d4382..0a8725ac9e 100644
--- a/gui/widgets/edittext.cpp
+++ b/gui/widgets/edittext.cpp
@@ -97,7 +97,7 @@ void EditTextWidget::handleMouseDown(int x, int y, int button, int clickCount) {
}
void EditTextWidget::drawWidget() {
- g_gui.theme()->drawWidgetBackground(Common::Rect(_x, _y, _x+_w, _y+_h), 0, ThemeEngine::kWidgetBackgroundEditText);
+ g_gui.theme()->drawWidgetBackgroundClip(Common::Rect(_x, _y, _x+_w, _y+_h), getBossClipRect(), 0, ThemeEngine::kWidgetBackgroundEditText);
// Draw the text
adjustOffset();
@@ -105,7 +105,7 @@ void EditTextWidget::drawWidget() {
const Common::Rect &r = Common::Rect(_x + 2 + _leftPadding, _y + 2, _x + _leftPadding + getEditRect().width() + 8, _y + _h);
setTextDrawableArea(r);
- g_gui.theme()->drawText(Common::Rect(_x + 2 + _leftPadding, _y + 2, _x + _leftPadding + getEditRect().width() + 2, _y + _h), _editString, _state, Graphics::kTextAlignLeft, ThemeEngine::kTextInversionNone, -_editScrollOffset, false, _font, ThemeEngine::kFontColorNormal, true, _textDrawableArea);
+ g_gui.theme()->drawTextClip(Common::Rect(_x + 2 + _leftPadding, _y + 2, _x + _leftPadding + getEditRect().width() + 2, _y + _h), getBossClipRect(), _editString, _state, Graphics::kTextAlignLeft, ThemeEngine::kTextInversionNone, -_editScrollOffset, false, _font, ThemeEngine::kFontColorNormal, true, _textDrawableArea);
}
Common::Rect EditTextWidget::getEditRect() const {
diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index 4b69202fdc..f6e5c67510 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -488,7 +488,7 @@ void ListWidget::drawWidget() {
Common::String buffer;
// Draw a thin frame around the list.
- g_gui.theme()->drawWidgetBackground(Common::Rect(_x, _y, _x + _w, _y + _h), 0, ThemeEngine::kWidgetBackgroundBorder);
+ g_gui.theme()->drawWidgetBackgroundClip(Common::Rect(_x, _y, _x + _w, _y + _h), getBossClipRect(), 0, ThemeEngine::kWidgetBackgroundBorder);
const int scrollbarW = (_scrollBar && _scrollBar->isVisible()) ? _scrollBarWidth : 0;
// Draw the list items
@@ -507,7 +507,7 @@ void ListWidget::drawWidget() {
// If in numbering mode, we first print a number prefix
if (_numberingMode != kListNumberingOff) {
buffer = Common::String::format("%2d. ", (pos + _numberingMode));
- g_gui.theme()->drawText(Common::Rect(_x, y, _x + r.left + _leftPadding, y + fontHeight - 2),
+ g_gui.theme()->drawTextClip(Common::Rect(_x, y, _x + r.left + _leftPadding, y + fontHeight - 2), getBossClipRect(),
buffer, _state, Graphics::kTextAlignLeft, inverted, _leftPadding, true);
pad = 0;
}
@@ -528,12 +528,12 @@ void ListWidget::drawWidget() {
color = _editColor;
adjustOffset();
width = _w - r.left - _hlRightPadding - _leftPadding - scrollbarW;
- g_gui.theme()->drawText(Common::Rect(_x + r.left, y, _x + r.left + width, y + fontHeight - 2), buffer, _state,
+ g_gui.theme()->drawTextClip(Common::Rect(_x + r.left, y, _x + r.left + width, y + fontHeight - 2), getBossClipRect(), buffer, _state,
Graphics::kTextAlignLeft, inverted, pad, true, ThemeEngine::kFontStyleBold, color);
} else {
buffer = _list[pos];
width = _w - r.left - scrollbarW;
- g_gui.theme()->drawText(Common::Rect(_x + r.left, y, _x + r.left + width, y + fontHeight - 2), buffer, _state,
+ g_gui.theme()->drawTextClip(Common::Rect(_x + r.left, y, _x + r.left + width, y + fontHeight - 2), getBossClipRect(), buffer, _state,
Graphics::kTextAlignLeft, inverted, pad, true, ThemeEngine::kFontStyleBold, color);
}
diff --git a/gui/widgets/popup.cpp b/gui/widgets/popup.cpp
index 0a1010f8fa..789064e8fd 100644
--- a/gui/widgets/popup.cpp
+++ b/gui/widgets/popup.cpp
@@ -364,8 +364,11 @@ void PopUpDialog::drawMenuEntry(int entry, bool hilite) {
// Draw a separator
g_gui.theme()->drawLineSeparator(Common::Rect(x, y, x+w, y+kLineHeight));
} else {
- g_gui.theme()->drawText(Common::Rect(x+1, y+2, x+w, y+2+kLineHeight), name, hilite ? ThemeEngine::kStateHighlight : ThemeEngine::kStateEnabled,
- Graphics::kTextAlignLeft, ThemeEngine::kTextInversionNone, _leftPadding);
+ g_gui.theme()->drawText(
+ Common::Rect(x+1, y+2, x+w, y+2+kLineHeight),
+ name, hilite ? ThemeEngine::kStateHighlight : ThemeEngine::kStateEnabled,
+ Graphics::kTextAlignLeft, ThemeEngine::kTextInversionNone, _leftPadding
+ );
}
}
@@ -470,7 +473,10 @@ void PopUpWidget::drawWidget() {
Common::String sel;
if (_selectedItem >= 0)
sel = _entries[_selectedItem].name;
- g_gui.theme()->drawPopUpWidget(Common::Rect(_x, _y, _x + _w, _y + _h), sel, _leftPadding, _state, Graphics::kTextAlignLeft);
+ g_gui.theme()->drawPopUpWidgetClip(
+ Common::Rect(_x, _y, _x + _w, _y + _h), getBossClipRect(),
+ sel, _leftPadding, _state, Graphics::kTextAlignLeft
+ );
}
} // End of namespace GUI
diff --git a/gui/widgets/scrollbar.cpp b/gui/widgets/scrollbar.cpp
index f1306b9c4a..d8bcb18336 100644
--- a/gui/widgets/scrollbar.cpp
+++ b/gui/widgets/scrollbar.cpp
@@ -26,6 +26,7 @@
#include "gui/widgets/scrollbar.h"
#include "gui/gui-manager.h"
#include "gui/ThemeEngine.h"
+#include "gui/widgets/scrollcontainer.h"
namespace GUI {
@@ -202,7 +203,11 @@ void ScrollBarWidget::drawWidget() {
state = ThemeEngine::kScrollbarStateSlider;
}
- g_gui.theme()->drawScrollbar(Common::Rect(_x, _y, _x+_w, _y+_h), _sliderPos, _sliderHeight, state, _state);
+ Common::Rect clipRect = getBossClipRect();
+ //scrollbar is not a usual child of ScrollContainerWidget, so it gets this special treatment
+ if (dynamic_cast<ScrollContainerWidget *>(_boss))
+ clipRect.right += _w;
+ g_gui.theme()->drawScrollbarClip(Common::Rect(_x, _y, _x+_w, _y+_h), clipRect, _sliderPos, _sliderHeight, state, _state);
}
} // End of namespace GUI
diff --git a/gui/widgets/scrollcontainer.cpp b/gui/widgets/scrollcontainer.cpp
new file mode 100644
index 0000000000..1b38478c11
--- /dev/null
+++ b/gui/widgets/scrollcontainer.cpp
@@ -0,0 +1,147 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/util.h"
+#include "gui/widgets/scrollcontainer.h"
+#include "gui/gui-manager.h"
+
+#include "gui/ThemeEval.h"
+
+namespace GUI {
+
+ScrollContainerWidget::ScrollContainerWidget(GuiObject *boss, int x, int y, int w, int h)
+ : Widget(boss, x, y, w, h) {
+ init();
+}
+
+ScrollContainerWidget::ScrollContainerWidget(GuiObject *boss, const Common::String &name)
+ : Widget(boss, name) {
+ init();
+}
+
+void ScrollContainerWidget::init() {
+ setFlags(WIDGET_ENABLED);
+ _type = kScrollContainerWidget;
+ _verticalScroll = new ScrollBarWidget(this, _w-16, 0, 16, _h);
+ _verticalScroll->setTarget(this);
+ _scrolledX = 0;
+ _scrolledY = 0;
+ _limitH = 140;
+ recalc();
+}
+
+void ScrollContainerWidget::recalc() {
+ int scrollbarWidth = g_gui.xmlEval()->getVar("Globals.Scrollbar.Width", 0);
+ _limitH = _h;
+
+ //calculate virtual height
+ const int spacing = g_gui.xmlEval()->getVar("Global.Font.Height", 16); //on the bottom
+ int h = 0;
+ int min = spacing, max = 0;
+ Widget *ptr = _firstWidget;
+ while (ptr) {
+ if (ptr != _verticalScroll) {
+ int y = ptr->getAbsY() - getChildY();
+ min = MIN(min, y - spacing);
+ max = MAX(max, y + ptr->getHeight() + spacing);
+ }
+ ptr = ptr->next();
+ }
+ h = max - min;
+
+ _verticalScroll->_numEntries = h;
+ _verticalScroll->_currentPos = _scrolledY;
+ _verticalScroll->_entriesPerPage = _limitH;
+ _verticalScroll->setPos(_w - scrollbarWidth, _scrolledY+1);
+ _verticalScroll->setSize(scrollbarWidth, _limitH -2);
+}
+
+
+ScrollContainerWidget::~ScrollContainerWidget() {}
+
+int16 ScrollContainerWidget::getChildX() const {
+ return getAbsX() - _scrolledX;
+}
+
+int16 ScrollContainerWidget::getChildY() const {
+ return getAbsY() - _scrolledY;
+}
+
+uint16 ScrollContainerWidget::getWidth() const {
+ return _w - (_verticalScroll->isVisible() ? _verticalScroll->getWidth() : 0);
+}
+
+uint16 ScrollContainerWidget::getHeight() const {
+ return _limitH;
+}
+
+void ScrollContainerWidget::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
+ Widget::handleCommand(sender, cmd, data);
+ switch (cmd) {
+ case kSetPositionCmd:
+ _scrolledY = _verticalScroll->_currentPos;
+ reflowLayout();
+ draw();
+ g_gui.doFullRedraw();
+ break;
+ }
+}
+
+void ScrollContainerWidget::reflowLayout() {
+ Widget::reflowLayout();
+
+ //reflow layout of inner widgets
+ Widget *ptr = _firstWidget;
+ while (ptr) {
+ ptr->reflowLayout();
+ ptr = ptr->next();
+ }
+
+ //recalculate height
+ recalc();
+
+ //hide those widgets which are out of visible area
+ ptr = _firstWidget;
+ while (ptr) {
+ int y = ptr->getAbsY() - getChildY();
+ int h = ptr->getHeight();
+ bool visible = true;
+ if (y + h - _scrolledY < 0) visible = false;
+ if (y - _scrolledY > _limitH) visible = false;
+ ptr->setVisible(visible);
+ ptr = ptr->next();
+ }
+
+ _verticalScroll->setVisible(_verticalScroll->_numEntries > _limitH); //show when there is something to scroll
+}
+
+void ScrollContainerWidget::drawWidget() {
+ g_gui.theme()->drawDialogBackgroundClip(Common::Rect(_x, _y, _x + _w, _y + getHeight() - 1), getBossClipRect(), ThemeEngine::kDialogBackgroundDefault);
+}
+
+Widget *ScrollContainerWidget::findWidget(int x, int y) {
+ if (_verticalScroll->isVisible() && x >= _w - _verticalScroll->getWidth())
+ return _verticalScroll;
+ return Widget::findWidgetInChain(_firstWidget, x + _scrolledX, y + _scrolledY);
+}
+
+} // End of namespace GUI
diff --git a/gui/widgets/scrollcontainer.h b/gui/widgets/scrollcontainer.h
new file mode 100644
index 0000000000..692c7e3507
--- /dev/null
+++ b/gui/widgets/scrollcontainer.h
@@ -0,0 +1,63 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GUI_WIDGETS_SCROLLCONTAINER_H
+#define GUI_WIDGETS_SCROLLCONTAINER_H
+
+#include "gui/widget.h"
+#include "common/str.h"
+#include "scrollbar.h"
+
+namespace GUI {
+
+class ScrollContainerWidget: public Widget {
+ ScrollBarWidget *_verticalScroll;
+ int16 _scrolledX, _scrolledY;
+ uint16 _limitH;
+
+ void recalc();
+
+public:
+ ScrollContainerWidget(GuiObject *boss, int x, int y, int w, int h);
+ ScrollContainerWidget(GuiObject *boss, const Common::String &name);
+ ~ScrollContainerWidget();
+
+ void init();
+ virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
+ virtual void reflowLayout();
+
+protected:
+ // We overload getChildY to make sure child widgets are positioned correctly.
+ // Essentially this compensates for the space taken up by the tab title header.
+ virtual int16 getChildX() const;
+ virtual int16 getChildY() const;
+ virtual uint16 getWidth() const;
+ virtual uint16 getHeight() const;
+
+ virtual void drawWidget();
+
+ virtual Widget *findWidget(int x, int y);
+};
+
+} // End of namespace GUI
+
+#endif
diff --git a/gui/widgets/tab.cpp b/gui/widgets/tab.cpp
index 756781a04b..15e6a9d370 100644
--- a/gui/widgets/tab.cpp
+++ b/gui/widgets/tab.cpp
@@ -80,9 +80,19 @@ TabWidget::~TabWidget() {
}
int16 TabWidget::getChildY() const {
+ // NOTE: if you change that, make sure to do the same
+ // changes in the ThemeLayoutTabWidget (gui/ThemeLayout.cpp)
return getAbsY() + _tabHeight;
}
+uint16 TabWidget::getHeight() const {
+ // NOTE: if you change that, make sure to do the same
+ // changes in the ThemeLayoutTabWidget (gui/ThemeLayout.cpp)
+ // NOTE: this height is used for clipping, so it *includes*
+ // tabs, because it starts from getAbsY(), not getChildY()
+ return _h + _tabHeight;
+}
+
int TabWidget::addTab(const String &title) {
// Add a new tab page
Tab newTab;
@@ -258,6 +268,12 @@ void TabWidget::adjustTabs(int value) {
void TabWidget::reflowLayout() {
Widget::reflowLayout();
+ // NOTE: if you change that, make sure to do the same
+ // changes in the ThemeLayoutTabWidget (gui/ThemeLayout.cpp)
+ _tabHeight = g_gui.xmlEval()->getVar("Globals.TabWidget.Tab.Height");
+ _tabWidth = g_gui.xmlEval()->getVar("Globals.TabWidget.Tab.Width");
+ _titleVPad = g_gui.xmlEval()->getVar("Globals.TabWidget.Tab.Padding.Top");
+
for (uint i = 0; i < _tabs.size(); ++i) {
Widget *w = _tabs[i].firstWidget;
while (w) {
@@ -266,10 +282,6 @@ void TabWidget::reflowLayout() {
}
}
- _tabHeight = g_gui.xmlEval()->getVar("Globals.TabWidget.Tab.Height");
- _tabWidth = g_gui.xmlEval()->getVar("Globals.TabWidget.Tab.Width");
- _titleVPad = g_gui.xmlEval()->getVar("Globals.TabWidget.Tab.Padding.Top");
-
if (_tabWidth == 0) {
_tabWidth = 40;
#ifdef __DS__
@@ -304,9 +316,9 @@ void TabWidget::drawWidget() {
for (int i = _firstVisibleTab; i < (int)_tabs.size(); ++i) {
tabs.push_back(_tabs[i].title);
}
- g_gui.theme()->drawDialogBackground(Common::Rect(_x + _bodyLP, _y + _bodyTP, _x+_w-_bodyRP, _y+_h-_bodyBP), _bodyBackgroundType);
+ g_gui.theme()->drawDialogBackgroundClip(Common::Rect(_x + _bodyLP, _y + _bodyTP, _x+_w-_bodyRP, _y+_h-_bodyBP+_tabHeight), getBossClipRect(), _bodyBackgroundType);
- g_gui.theme()->drawTab(Common::Rect(_x, _y, _x+_w, _y+_h), _tabHeight, _tabWidth, tabs, _activeTab - _firstVisibleTab, 0, _titleVPad);
+ g_gui.theme()->drawTabClip(Common::Rect(_x, _y, _x+_w, _y+_h), getBossClipRect(), _tabHeight, _tabWidth, tabs, _activeTab - _firstVisibleTab, 0, _titleVPad);
}
void TabWidget::draw() {
diff --git a/gui/widgets/tab.h b/gui/widgets/tab.h
index 148f164fbb..17b85986b5 100644
--- a/gui/widgets/tab.h
+++ b/gui/widgets/tab.h
@@ -110,6 +110,7 @@ protected:
// We overload getChildY to make sure child widgets are positioned correctly.
// Essentially this compensates for the space taken up by the tab title header.
virtual int16 getChildY() const;
+ virtual uint16 getHeight() const;
virtual void drawWidget();
diff --git a/video/coktel_decoder.cpp b/video/coktel_decoder.cpp
index 21dda15cd0..278dc2d1fc 100644
--- a/video/coktel_decoder.cpp
+++ b/video/coktel_decoder.cpp
@@ -2810,6 +2810,10 @@ void AdvancedVMDDecoder::close() {
_decoder->close();
}
+void AdvancedVMDDecoder::setSurfaceMemory(void *mem, uint16 width, uint16 height, uint8 bpp) {
+ _decoder->setSurfaceMemory(mem, width, height, bpp);
+}
+
AdvancedVMDDecoder::VMDVideoTrack::VMDVideoTrack(VMDDecoder *decoder) : _decoder(decoder) {
}
diff --git a/video/coktel_decoder.h b/video/coktel_decoder.h
index a72f76eb9d..44de1c7d68 100644
--- a/video/coktel_decoder.h
+++ b/video/coktel_decoder.h
@@ -568,6 +568,8 @@ public:
bool loadStream(Common::SeekableReadStream *stream);
void close();
+ void setSurfaceMemory(void *mem, uint16 width, uint16 height, uint8 bpp);
+
private:
class VMDVideoTrack : public FixedRateVideoTrack {
public: