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:  | 
