diff options
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 Binary files differindex 43fcea12fd..561f2a5dd3 100644 --- a/gui/themes/scummclassic.zip +++ b/gui/themes/scummclassic.zip 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 Binary files differindex 70f7672c57..d80c481ffc 100644 --- a/gui/themes/scummmodern.zip +++ b/gui/themes/scummmodern.zip 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: |