diff options
53 files changed, 840 insertions, 416 deletions
@@ -3,7 +3,17 @@ For a more comprehensive changelog of the latest experimental code, see:  1.5.0 (????-??-??)   New Games: +   - Added support for Blue Force. +   - Added support for Darby the Dragon. +   - Added support for Dreamweb. +   - Added support for Gregory and the Hot Air Balloon. +   - Added support for Magic Tales: Baba Yaga and the Magic Geese. +   - Added support for Magic Tales: Imo and the King. +   - Added support for Magic Tales: Liam Finds a Story. +   - Added support for Magic Tales: The Little Samurai. +   - Added support for Sleeping Cub's Test of Courage.     - Added support for Soltys. +   - Added support for The Princess and the Crab.   General:     - Updated MT-32 emulation code to latest munt project snapshot. The emulation diff --git a/audio/decoders/quicktime.cpp b/audio/decoders/quicktime.cpp index 48e76a94da..99c1527a71 100644 --- a/audio/decoders/quicktime.cpp +++ b/audio/decoders/quicktime.cpp @@ -449,7 +449,7 @@ void QuickTimeAudioDecoder::QuickTimeAudioTrack::skipSamples(const Timestamp &le  }  void QuickTimeAudioDecoder::QuickTimeAudioTrack::findEdit(const Timestamp &position) { -	for (_curEdit = 0; _curEdit < _parentTrack->editCount && position < Timestamp(0, _parentTrack->editList[_curEdit].timeOffset, _decoder->_timeScale); _curEdit++) +	for (_curEdit = 0; _curEdit < _parentTrack->editCount - 1 && position > Timestamp(0, _parentTrack->editList[_curEdit].timeOffset, _decoder->_timeScale); _curEdit++)  		;  	enterNewEdit(position); diff --git a/backends/graphics/opengl/opengl-graphics.cpp b/backends/graphics/opengl/opengl-graphics.cpp index 45804b5d6e..cd820ae3b2 100644 --- a/backends/graphics/opengl/opengl-graphics.cpp +++ b/backends/graphics/opengl/opengl-graphics.cpp @@ -70,6 +70,11 @@ OpenGLGraphicsManager::~OpenGLGraphicsManager() {  	free(_gamePalette);  	free(_cursorPalette); +	_screenData.free(); +	_overlayData.free(); +	_cursorData.free(); +	_osdSurface.free(); +  	delete _gameTexture;  	delete _overlayTexture;  	delete _cursorTexture; diff --git a/base/main.cpp b/base/main.cpp index c657488758..25e1b881cc 100644 --- a/base/main.cpp +++ b/base/main.cpp @@ -55,6 +55,12 @@  #include "audio/mididrv.h"  #include "audio/musicplugin.h"  /* for music manager */ +#include "graphics/cursorman.h" +#include "graphics/fontman.h" +#ifdef USE_FREETYPE2 +#include "graphics/fonts/ttf.h" +#endif +  #include "backends/keymapper/keymapper.h"  #if defined(_WIN32_WCE) @@ -493,10 +499,18 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {  	PluginManager::destroy();  	GUI::GuiManager::destroy();  	Common::ConfigManager::destroy(); +	Common::DebugManager::destroy(); +	Common::EventRecorder::destroy();  	Common::SearchManager::destroy();  #ifdef USE_TRANSLATION  	Common::TranslationManager::destroy();  #endif +	MusicManager::destroy(); +	Graphics::CursorManager::destroy(); +	Graphics::FontManager::destroy(); +#ifdef USE_FREETYPE2 +	Graphics::shutdownTTF(); +#endif  	return 0;  } diff --git a/common/unzip.cpp b/common/unzip.cpp index 8cfcd605fa..ab659343a2 100644 --- a/common/unzip.cpp +++ b/common/unzip.cpp @@ -1463,22 +1463,16 @@ bool ZipArchive::hasFile(const String &name) const {  }  int ZipArchive::listMembers(ArchiveMemberList &list) const { -	int matches = 0; -	int err = unzGoToFirstFile(_zipFile); +	int members = 0; -	while (err == UNZ_OK) { -		char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; -		if (unzGetCurrentFileInfo(_zipFile, NULL, -		                          szCurrentFileName, sizeof(szCurrentFileName)-1, -		                          NULL, 0, NULL, 0) == UNZ_OK) { -			list.push_back(ArchiveMemberList::value_type(new GenericArchiveMember(szCurrentFileName, this))); -			matches++; -		} - -		err = unzGoToNextFile(_zipFile); +	const unz_s *const archive = (const unz_s *)_zipFile; +	for (ZipHash::const_iterator i = archive->_hash.begin(), end = archive->_hash.end(); +	     i != end; ++i) { +		list.push_back(ArchiveMemberList::value_type(new GenericArchiveMember(i->_key, this))); +		++members;  	} -	return matches; +	return members;  }  const ArchiveMemberPtr ZipArchive::getMember(const String &name) const { @@ -840,7 +840,7 @@ Optional Libraries:    --with-sdl-prefix=DIR    Prefix where the sdl-config script is                             installed (optional) -  --with-freetype-prefix=DIR Prefix where the freetype-config script is +  --with-freetype2-prefix=DIR Prefix where the freetype-config script is                             installed (optional)    --with-nasm-prefix=DIR   Prefix where nasm executable is installed (optional) @@ -1403,6 +1403,10 @@ ps3)  		echo "Please set PS3DEV in your environment. export PS3DEV=<path to ps3 toolchain>"  		exit 1  	fi +	if test -z "$PSL1GHT"; then +		echo "Please set PSL1GHT in your environment. export PSL1GHT=<path to psl1ght>" +		exit 1 +	fi  	;;  psp)  	if test -z "$PSPDEV"; then @@ -2068,8 +2072,8 @@ case $_host_os in  		_sdlpath="$PS3DEV/portlibs/ppu:$PS3DEV/portlibs/ppu/bin"  		DEFINES="$DEFINES -DPLAYSTATION3" -		CXXFLAGS="$CXXFLAGS -mcpu=cell -mminimal-toc -I$PS3DEV/psl1ght/ppu/include -I$PS3DEV/portlibs/ppu/include" -		LDFLAGS="$LDFLAGS -L$PS3DEV/psl1ght/ppu/lib -L$PS3DEV/portlibs/ppu/lib" +		CXXFLAGS="$CXXFLAGS -mcpu=cell -mminimal-toc -I$PSL1GHT/ppu/include -I$PS3DEV/portlibs/ppu/include" +		LDFLAGS="$LDFLAGS -L$PSL1GHT/ppu/lib -L$PS3DEV/portlibs/ppu/lib"  		add_line_to_config_mk 'PLAYSTATION3 = 1'  		add_line_to_config_h "#define PREFIX \"${prefix}\""  		;; diff --git a/devtools/create_project/create_project.cpp b/devtools/create_project/create_project.cpp index 293cc0b2de..df220f0934 100644 --- a/devtools/create_project/create_project.cpp +++ b/devtools/create_project/create_project.cpp @@ -654,14 +654,14 @@ void displayHelp(const char *exe) {  	        "\n"  	        "Engines settings:\n"  	        " --list-engines           list all available engines and their default state\n" -	        " --enable-engine          enable building of the engine with the name \"engine\"\n" -	        " --disable-engine         disable building of the engine with the name \"engine\"\n" +	        " --enable-engine=<name>   enable building of the engine with the name \"name\"\n" +	        " --disable-engine=<name>  disable building of the engine with the name \"name\"\n"  	        " --enable-all-engines     enable building of all engines\n"  	        " --disable-all-engines    disable building of all engines\n"  	        "\n"  	        "Optional features settings:\n" -	        " --enable-name            enable inclusion of the feature \"name\"\n" -	        " --disable-name           disable inclusion of the feature \"name\"\n" +	        " --enable-<name>          enable inclusion of the feature \"name\"\n" +	        " --disable-<name>         disable inclusion of the feature \"name\"\n"  	        "\n"  	        " There are the following features available:\n"  	        "\n"; diff --git a/devtools/scumm-md5.txt b/devtools/scumm-md5.txt index 01d806a484..7d2ea94f10 100644 --- a/devtools/scumm-md5.txt +++ b/devtools/scumm-md5.txt @@ -590,6 +590,7 @@ FreddisFunShop	Freddi Fish's One-Stop Fun Shop  catalog	Humongous Interactive Catalog  	11e6e244078ff09b0f3832e35420e0a7	-1	en	Windows	-	Demo	-	khalek, sev  	037385a953789190298494d92b89b3d0	-1	en	Windows	HE 72	Demo	-	khalek, sev +	f7635a0e2ab82c9a0f9ace5f232a488f	-1	en	Windows	HE 72	Demo	-	Kirben  	a56e8d9d4281c53c3f63c9bd22a59e21	10978342	en	All	HE CUP	Preview	George Kormendi  	74da3494fbe1a7d20213b0afe0954755	10841544	fr	All	HE CUP	Preview	-	George Kormendi  	4c4820518e16e1a0e3616a3b021a04f3	10927456	de	All	HE CUP	Preview	-	Kirben diff --git a/engines/agi/saveload.cpp b/engines/agi/saveload.cpp index 8e524c8d9a..d58e55a6b9 100644 --- a/engines/agi/saveload.cpp +++ b/engines/agi/saveload.cpp @@ -831,6 +831,9 @@ int AgiEngine::scummVMSaveLoadDialog(bool isSave) {  	delete dialog; +	if (slot < 0) +		return true; +	  	if (isSave)  		return doSave(slot, desc);  	else diff --git a/engines/configure.engines b/engines/configure.engines index 93c349378d..91e5e82506 100644 --- a/engines/configure.engines +++ b/engines/configure.engines @@ -7,11 +7,11 @@ add_engine agos "AGOS" yes "agos2"  add_engine agos2 "AGOS 2 games" yes  add_engine cge "CGE" yes  add_engine cine "Cinematique evo 1" yes -add_engine composer "Magic Composer" no +add_engine composer "Magic Composer" yes  add_engine cruise "Cinematique evo 2" yes  add_engine draci "Dragon History" yes  add_engine drascula "Drascula: The Vampire Strikes Back" yes -add_engine dreamweb "Dreamweb" no +add_engine dreamweb "Dreamweb" yes  add_engine gob "Gobli*ns" yes  add_engine groovie "Groovie" yes "groovie2"  add_engine groovie2 "Groovie 2 games" no diff --git a/engines/cruise/cruise_main.cpp b/engines/cruise/cruise_main.cpp index 6e2847d6d7..911041c1a4 100644 --- a/engines/cruise/cruise_main.cpp +++ b/engines/cruise/cruise_main.cpp @@ -1799,32 +1799,60 @@ void CruiseEngine::mainLoop() {  		// Handle frame delay  		uint32 currentTick = g_system->getMillis(); -		if (!bFastMode) { -			// Delay for the specified amount of time, but still respond to events -			bool skipEvents = false; +		// Delay for the specified amount of time, but still respond to events +		bool skipEvents = false; -			do { -				g_system->updateScreen(); +		do { +			if (userEnabled && !userWait && !autoTrack) { +				if (currentActiveMenu == -1) { +					static int16 oldMouseX = -1; +					static int16 oldMouseY = -1; -				g_system->delayMillis(10); -				currentTick = g_system->getMillis(); +					getMouseStatus(&main10, &mouseX, &mouseButton, &mouseY); -				if (!skipEvents) -					skipEvents = manageEvents(); +					if (mouseX != oldMouseX || mouseY != oldMouseY) { +						int objectType; +						int newCursor1; +						int newCursor2; -				if (playerDontAskQuit) -					break; +						oldMouseX = mouseX; +						oldMouseY = mouseY; -				_vm->getDebugger()->onFrame(); -			} while (currentTick < lastTick + _gameSpeed); -		} else { -			manageEvents(); +						objectType = findObject(mouseX, mouseY, &newCursor1, &newCursor2); -			if (currentTick >= (lastTickDebug + 10)) { -				lastTickDebug = currentTick; -				_vm->getDebugger()->onFrame(); +						if (objectType == 9) { +							changeCursor(CURSOR_EXIT); +						} else if (objectType != -1) { +							changeCursor(CURSOR_MAGNIFYING_GLASS); +						} else { +							changeCursor(CURSOR_WALK); +						} +					} +				} else { +					changeCursor(CURSOR_NORMAL); +				} +			} else { +				changeCursor(CURSOR_NORMAL);  			} -		} + +			g_system->updateScreen(); + +			if (!skipEvents || bFastMode) +				skipEvents = manageEvents(); + +			if (bFastMode) { +				if (currentTick >= (lastTickDebug + 10)) +					lastTickDebug = currentTick; +			} else { +				g_system->delayMillis(10); +				currentTick = g_system->getMillis(); +			} + +			if (playerDontAskQuit) +				break; + +			_vm->getDebugger()->onFrame(); +		} while (currentTick < lastTick + _gameSpeed && !bFastMode);  		if (playerDontAskQuit)  			break; @@ -1844,6 +1872,14 @@ void CruiseEngine::mainLoop() {  //      readKeyboard();  		bool isUserWait = userWait != 0; +		// WORKAROUND: This prevents hotspots responding during +		// delays i.e. Menu opening if you click fast on another +		// hotspot after trying to open a locked door, which +		// occurred with the original interpreter. +		if (userDelay) { +			currentMouseButton = 0; +		} +  		playerDontAskQuit = processInput();  		if (playerDontAskQuit)  			break; @@ -1855,7 +1891,6 @@ void CruiseEngine::mainLoop() {  		if (userDelay && !userWait) {  			userDelay--; -			continue;  		}  		if (isUserWait & !userWait) { @@ -1918,38 +1953,6 @@ void CruiseEngine::mainLoop() {  			mainDraw(userWait);  			flipScreen(); -			if (userEnabled && !userWait && !autoTrack) { -				if (currentActiveMenu == -1) { -					static int16 oldMouseX = -1; -					static int16 oldMouseY = -1; - -					getMouseStatus(&main10, &mouseX, &mouseButton, &mouseY); - -					if (mouseX != oldMouseX || mouseY != oldMouseY) { -						int objectType; -						int newCursor1; -						int newCursor2; - -						oldMouseX = mouseX; -						oldMouseY = mouseY; - -						objectType = findObject(mouseX, mouseY, &newCursor1, &newCursor2); - -						if (objectType == 9) { -							changeCursor(CURSOR_EXIT); -						} else if (objectType != -1) { -							changeCursor(CURSOR_MAGNIFYING_GLASS); -						} else { -							changeCursor(CURSOR_WALK); -						} -					} -				} else { -					changeCursor(CURSOR_NORMAL); -				} -			} else { -				changeCursor(CURSOR_NORMAL); -			} -  			if (userWait == 1) {  				// Waiting for press - original wait loop has been integrated into the  				// main event loop diff --git a/engines/dreamweb/detection_tables.h b/engines/dreamweb/detection_tables.h index 8ca24d1724..d54b2402c8 100644 --- a/engines/dreamweb/detection_tables.h +++ b/engines/dreamweb/detection_tables.h @@ -45,7 +45,7 @@ static const DreamWebGameDescription gameDescriptions[] = {  			},  			Common::EN_ANY,  			Common::kPlatformPC, -			ADGF_UNSTABLE, +			ADGF_TESTING,  			GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE)  		},  	}, @@ -62,7 +62,7 @@ static const DreamWebGameDescription gameDescriptions[] = {  			},  			Common::EN_ANY,  			Common::kPlatformPC, -			ADGF_CD | ADGF_UNSTABLE, +			ADGF_CD | ADGF_TESTING,  			GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE)  		},  	}, @@ -96,7 +96,7 @@ static const DreamWebGameDescription gameDescriptions[] = {  			},  			Common::FR_FRA,  			Common::kPlatformPC, -			ADGF_CD | ADGF_UNSTABLE, +			ADGF_CD | ADGF_TESTING,  			GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE)  		},  	}, @@ -113,7 +113,7 @@ static const DreamWebGameDescription gameDescriptions[] = {  			},  			Common::DE_DEU,  			Common::kPlatformPC, -			ADGF_UNSTABLE, +			ADGF_TESTING,  			GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE)  		},  	}, @@ -130,7 +130,7 @@ static const DreamWebGameDescription gameDescriptions[] = {  			},  			Common::DE_DEU,  			Common::kPlatformPC, -			ADGF_CD | ADGF_UNSTABLE, +			ADGF_CD | ADGF_TESTING,  			GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE)  		},  	}, @@ -147,7 +147,7 @@ static const DreamWebGameDescription gameDescriptions[] = {  			},  			Common::ES_ESP,  			Common::kPlatformPC, -			ADGF_UNSTABLE, +			ADGF_TESTING,  			GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE)  		},  	}, @@ -164,7 +164,7 @@ static const DreamWebGameDescription gameDescriptions[] = {  			},  			Common::ES_ESP,  			Common::kPlatformPC, -			ADGF_CD | ADGF_UNSTABLE, +			ADGF_CD | ADGF_TESTING,  			GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE)  		},  	}, @@ -181,7 +181,7 @@ static const DreamWebGameDescription gameDescriptions[] = {  			},  			Common::IT_ITA,  			Common::kPlatformPC, -			ADGF_UNSTABLE, +			ADGF_TESTING,  			GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE)  		},  	}, diff --git a/engines/dreamweb/dreamweb.cpp b/engines/dreamweb/dreamweb.cpp index 2e9c7bb2b8..299dd74b53 100644 --- a/engines/dreamweb/dreamweb.cpp +++ b/engines/dreamweb/dreamweb.cpp @@ -422,25 +422,6 @@ void DreamWebEngine::keyPressed(uint16 ascii) {  	DreamWeb::g_keyBuffer[in] = ascii;  } -void DreamWebEngine::mouseCall(uint16 *x, uint16 *y, uint16 *state) { -	processEvents(); -	Common::Point pos = _eventMan->getMousePos(); -	if (pos.x > 298) -		pos.x = 298; -	if (pos.x < 15) -		pos.x = 15; -	if (pos.y < 15) -		pos.y = 15; -	if (pos.y > 184) -		pos.y = 184; -	*x = pos.x; -	*y = pos.y; - -	unsigned newState = _eventMan->getButtonState(); -	*state = (newState == _oldMouseState? 0 : newState); -	_oldMouseState = newState; -} -  void DreamWebEngine::getPalette(uint8 *data, uint start, uint count) {  	_system->getPaletteManager()->grabPalette(data, start, count);  	while (count--) @@ -541,6 +522,7 @@ uint8 DreamWebEngine::modifyChar(uint8 c) const {  			return c;  		}  	case Common::FR_FRA: +	case Common::IT_ITA:  		switch(c) {  		case 133:  			return 'Z' + 1;	 diff --git a/engines/dreamweb/module.mk b/engines/dreamweb/module.mk index 6bc4f8728e..3e367c6fdf 100644 --- a/engines/dreamweb/module.mk +++ b/engines/dreamweb/module.mk @@ -7,6 +7,7 @@ MODULE_OBJS := \  	dreamweb.o \  	keypad.o \  	monitor.o \ +	mouse.o \  	newplace.o \  	object.o \  	pathfind.o \ diff --git a/engines/dreamweb/mouse.cpp b/engines/dreamweb/mouse.cpp new file mode 100644 index 0000000000..77d907611d --- /dev/null +++ b/engines/dreamweb/mouse.cpp @@ -0,0 +1,171 @@ +/* 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/events.h" +#include "dreamweb/dreamweb.h" + +namespace DreamWeb { + +void DreamWebEngine::mouseCall(uint16 *x, uint16 *y, uint16 *state) { +	processEvents(); +	Common::Point pos = _eventMan->getMousePos(); +	*x = CLIP<int16>(pos.x, 15, 298); +	*y = CLIP<int16>(pos.y, 15, 184); + +	unsigned newState = _eventMan->getButtonState(); +	*state = (newState == _oldMouseState ? 0 : newState); +	_oldMouseState = newState; +} + +void DreamWebEngine::readMouse() { +	_oldButton = _mouseButton; +	_mouseButton = readMouseState(); +} + +uint16 DreamWebEngine::readMouseState() { +	_oldX = _mouseX; +	_oldY = _mouseY; +	uint16 x, y, state; +	mouseCall(&x, &y, &state); +	_mouseX = x; +	_mouseY = y; +	return state; +} + +void DreamWebEngine::dumpPointer() { +	dumpBlink(); +	multiDump(_delHereX, _delHereY, _delXS, _delYS); +	if ((_oldPointerX != _delHereX) || (_oldPointerY != _delHereY)) +		multiDump(_oldPointerX, _oldPointerY, _pointerXS, _pointerYS); +} + +void DreamWebEngine::showPointer() { +	showBlink(); +	uint16 x = _mouseX; +	_oldPointerX = _mouseX; +	uint16 y = _mouseY; +	_oldPointerY = _mouseY; +	if (_pickUp == 1) { +		const GraphicsFile *frames; +		if (_objectType != kExObjectType) +			frames = &_freeFrames; +		else +			frames = &_exFrames; +		const Frame *frame = &frames->_frames[(3 * _itemFrame + 1)]; + +		uint8 width = MAX<uint8>(frame->width, 12); +		uint8 height = MAX<uint8>(frame->height, 12); +		_pointerXS = width; +		_pointerYS = height; +		uint16 xMin = (x >= width / 2) ? x - width / 2 : 0; +		uint16 yMin = (y >= height / 2) ? y - height / 2 : 0; +		_oldPointerX = xMin; +		_oldPointerY = yMin; +		multiGet(_pointerBack, xMin, yMin, width, height); +		showFrame(*frames, x, y, 3 * _itemFrame + 1, 128); +		showFrame(_icons1, x, y, 3, 128); +	} else { +		const Frame *frame = &_icons1._frames[_pointerFrame + 20]; +		uint8 width = MAX<uint8>(frame->width, 12); +		uint8 height = MAX<uint8>(frame->height, 12); +		_pointerXS = width; +		_pointerYS = height; +		multiGet(_pointerBack, x, y, width, height); +		showFrame(_icons1, x, y, _pointerFrame + 20, 0); +	} +} + +void DreamWebEngine::delPointer() { +	if (_oldPointerX == 0xffff) +		return; +	_delHereX = _oldPointerX; +	_delHereY = _oldPointerY; +	_delXS = _pointerXS; +	_delYS = _pointerYS; +	multiPut(_pointerBack, _delHereX, _delHereY, _pointerXS, _pointerYS); +} + +void DreamWebEngine::animPointer() { +	if (_pointerMode == 2) { +		_pointerFrame = 0; +		if ((_realLocation == 14) && (_commandType == 211)) +			_pointerFrame = 5; +		return; +	} else if (_pointerMode == 3) { +		if (_pointerSpeed != 0) { +			--_pointerSpeed; +		} else { +			_pointerSpeed = 5; +			++_pointerCount; +			if (_pointerCount == 16) +				_pointerCount = 0; +		} +		_pointerFrame = (_pointerCount <= 8) ? 1 : 2; +		return; +	} +	if (_vars._watchingTime != 0) { +		_pointerFrame = 11; +		return; +	} +	_pointerFrame = 0; +	if (_inMapArea == 0) +		return; +	if (_pointerFirstPath == 0) +		return; +	uint8 flag, flagEx; +	getFlagUnderP(&flag, &flagEx); +	if (flag < 2) +		return; +	if (flag >= 128) +		return; +	if (flag & 4) { +		_pointerFrame = 3; +		return; +	} +	if (flag & 16) { +		_pointerFrame = 4; +		return; +	} +	if (flag & 2) { +		_pointerFrame = 5; +		return; +	} +	if (flag & 8) { +		_pointerFrame = 6; +		return; +	} +	_pointerFrame = 8; +} + +void DreamWebEngine::checkCoords(const RectWithCallback *rectWithCallbacks) { +	if (_newLocation != 0xff) +		return; +	const RectWithCallback *r; +	for (r = rectWithCallbacks; r->_xMin != 0xffff; ++r) { +		if (r->contains(_mouseX, _mouseY)) { +			(this->*(r->_callback))(); +			return; +		} +	} +} + +} // End of namespace DreamWeb diff --git a/engines/dreamweb/stubs.cpp b/engines/dreamweb/stubs.cpp index 5a53b82510..750dafe7b4 100644 --- a/engines/dreamweb/stubs.cpp +++ b/engines/dreamweb/stubs.cpp @@ -876,22 +876,6 @@ void DreamWebEngine::hangOnCurs(uint16 frameCount) {  	}  } -void DreamWebEngine::readMouse() { -	_oldButton = _mouseButton; -	uint16 state = readMouseState(); -	_mouseButton = state; -} - -uint16 DreamWebEngine::readMouseState() { -	_oldX = _mouseX; -	_oldY = _mouseY; -	uint16 x, y, state; -	mouseCall(&x, &y, &state); -	_mouseX = x; -	_mouseY = y; -	return state; -} -  void DreamWebEngine::dumpTextLine() {  	if (_newTextLine != 1)  		return; @@ -1513,16 +1497,6 @@ void DreamWebEngine::obName(uint8 command, uint8 commandType) {  	}  } -void DreamWebEngine::delPointer() { -	if (_oldPointerX == 0xffff) -		return; -	_delHereX = _oldPointerX; -	_delHereY = _oldPointerY; -	_delXS = _pointerXS; -	_delYS = _pointerYS; -	multiPut(_pointerBack, _delHereX, _delHereY, _pointerXS, _pointerYS); -} -  void DreamWebEngine::showBlink() {  	if (_manIsOffScreen == 1)  		return; @@ -1554,110 +1528,6 @@ void DreamWebEngine::dumpBlink() {  	multiDump(44, 32, 16, 12);  } -void DreamWebEngine::dumpPointer() { -	dumpBlink(); -	multiDump(_delHereX, _delHereY, _delXS, _delYS); -	if ((_oldPointerX != _delHereX) || (_oldPointerY != _delHereY)) -		multiDump(_oldPointerX, _oldPointerY, _pointerXS, _pointerYS); -} - -void DreamWebEngine::showPointer() { -	showBlink(); -	uint16 x = _mouseX; -	_oldPointerX = _mouseX; -	uint16 y = _mouseY; -	_oldPointerY = _mouseY; -	if (_pickUp == 1) { -		const GraphicsFile *frames; -		if (_objectType != kExObjectType) -			frames = &_freeFrames; -		else -			frames = &_exFrames; -		const Frame *frame = &frames->_frames[(3 * _itemFrame + 1)]; - -		uint8 width = frame->width; -		uint8 height = frame->height; -		if (width < 12) -			width = 12; -		if (height < 12) -			height = 12; -		_pointerXS = width; -		_pointerYS = height; -		uint16 xMin = (x >= width / 2) ? x - width / 2 : 0; -		uint16 yMin = (y >= height / 2) ? y - height / 2 : 0; -		_oldPointerX = xMin; -		_oldPointerY = yMin; -		multiGet(_pointerBack, xMin, yMin, width, height); -		showFrame(*frames, x, y, 3 * _itemFrame + 1, 128); -		showFrame(_icons1, x, y, 3, 128); -	} else { -		const Frame *frame = &_icons1._frames[_pointerFrame + 20]; -		uint8 width = frame->width; -		uint8 height = frame->height; -		if (width < 12) -			width = 12; -		if (height < 12) -			height = 12; -		_pointerXS = width; -		_pointerYS = height; -		multiGet(_pointerBack, x, y, width, height); -		showFrame(_icons1, x, y, _pointerFrame + 20, 0); -	} -} - -void DreamWebEngine::animPointer() { - -	if (_pointerMode == 2) { -		_pointerFrame = 0; -		if ((_realLocation == 14) && (_commandType == 211)) -			_pointerFrame = 5; -		return; -	} else if (_pointerMode == 3) { -		if (_pointerSpeed != 0) { -			--_pointerSpeed; -		} else { -			_pointerSpeed = 5; -			++_pointerCount; -			if (_pointerCount == 16) -				_pointerCount = 0; -		} -		_pointerFrame = (_pointerCount <= 8) ? 1 : 2; -		return; -	} -	if (_vars._watchingTime != 0) { -		_pointerFrame = 11; -		return; -	} -	_pointerFrame = 0; -	if (_inMapArea == 0) -		return; -	if (_pointerFirstPath == 0) -		return; -	uint8 flag, flagEx; -	getFlagUnderP(&flag, &flagEx); -	if (flag < 2) -		return; -	if (flag >= 128) -		return; -	if (flag & 4) { -		_pointerFrame = 3; -		return; -	} -	if (flag & 16) { -		_pointerFrame = 4; -		return; -	} -	if (flag & 2) { -		_pointerFrame = 5; -		return; -	} -	if (flag & 8) { -		_pointerFrame = 6; -		return; -	} -	_pointerFrame = 8; -} -  void DreamWebEngine::printMessage(uint16 x, uint16 y, uint8 index, uint8 maxWidth, bool centered) {  	const uint8 *string = (const uint8 *)_commandText.getString(index);  	printDirect(string, x, y, maxWidth, centered); @@ -3183,16 +3053,4 @@ void DreamWebEngine::purgeAnItem() {  	}  } -void DreamWebEngine::checkCoords(const RectWithCallback *rectWithCallbacks) { -	if (_newLocation != 0xff) -		return; -	const RectWithCallback *r; -	for (r = rectWithCallbacks; r->_xMin != 0xffff; ++r) { -		if (r->contains(_mouseX, _mouseY)) { -			(this->*(r->_callback))(); -			return; -		} -	} -} -  } // End of namespace DreamWeb diff --git a/engines/kyra/screen.cpp b/engines/kyra/screen.cpp index d3b4d6f943..711fe15348 100644 --- a/engines/kyra/screen.cpp +++ b/engines/kyra/screen.cpp @@ -95,8 +95,23 @@ bool Screen::init() {  	_use16ColorMode = _vm->gameFlags().use16ColorMode;  	_isAmiga = (_vm->gameFlags().platform == Common::kPlatformAmiga); -	if (ConfMan.hasKey("render_mode")) -		_renderMode = Common::parseRenderMode(ConfMan.get("render_mode")); +	// We only check the "render_mode" setting for both Eye of the Beholder +	// games here, since all the other games do not support the render_mode +	// setting or handle it differently, like Kyra 1 PC-98. This avoids +	// graphics glitches and crashes in other games, when the user sets his +	// global render_mode setting to EGA for example. +	// TODO/FIXME: It would be nice not to hardcode this. But there is no +	// trivial/non annoying way to do mode checks in an easy fashion right +	// now. +	// In a more general sense, we might want to think about a way to only +	// pass valid config values, as in values which the engine can work with, +	// to the engines. We already limit the selection via our GUIO flags in +	// the game specific settings, but this is not enough due to global +	// settings allowing everything. +	if (_vm->game() == GI_EOB1 || _vm->game() == GI_EOB2) { +		if (ConfMan.hasKey("render_mode")) +			_renderMode = Common::parseRenderMode(ConfMan.get("render_mode")); +	}  	// CGA and EGA modes use additional pages to do the CGA/EGA specific graphics conversions.  	if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderEGA) { diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h index d741bb801f..ff78d4f18b 100644 --- a/engines/sci/detection_tables.h +++ b/engines/sci/detection_tables.h @@ -3445,6 +3445,19 @@ static const struct ADGameDescription SciGameDescriptions[] = {  		AD_LISTEND},  	 	Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI)	}, +	// Space Quest 4 1.000 - French DOS Floppy (supplied by misterhands in bug report #3515247) +	{"sq4", "", { + +		{"resource.map", 0, "1fd6f356f6a59ad2057686ce6573caeb", 6159}, +		{"resource.000", 0, "8000a55aebc50a68b7cce07a8c33758c", 205287}, +		{"resource.001", 0, "99a6df6d366b3f061271ff3450ac0d32", 1269850}, +		{"resource.002", 0, "a6a8d7a24dbb7a266a26b084e7275e89", 1242668}, +		{"resource.003", 0, "482a99c8103b4bcb5706e5969d1c1193", 1323083}, +		{"resource.004", 0, "b2cca3afcf2e013b8ce86b64155af766", 1254353}, +		{"resource.005", 0, "9e520577e035547c4b5149a6d12ef85b", 1098814}, +		AD_LISTEND}, +	 	Common::FR_FRA, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI)	}, +  	// Space Quest 4 1.000 - English DOS Floppy (from abevi, bug report #2612718)  	{"sq4", "", {  		{"resource.map", 0, "8f08b97ca093f370c56d99715b015554", 6153}, diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp index cad95b1c18..8a932232f8 100644 --- a/engines/sci/engine/features.cpp +++ b/engines/sci/engine/features.cpp @@ -45,6 +45,7 @@ GameFeatures::GameFeatures(SegManager *segMan, Kernel *kernel) : _segMan(segMan)  	_usesCdTrack = Common::File::exists("cdaudio.map");  	if (!ConfMan.getBool("use_cdaudio"))  		_usesCdTrack = false; +	_forceDOSTracks = false;  }  reg_t GameFeatures::getDetectionAddr(const Common::String &objName, Selector slc, int methodNum) { @@ -642,7 +643,7 @@ MoveCountType GameFeatures::detectMoveCountType() {  }  bool GameFeatures::useAltWinGMSound() { -	if (g_sci && g_sci->getPlatform() == Common::kPlatformWindows && g_sci->isCD()) { +	if (g_sci && g_sci->getPlatform() == Common::kPlatformWindows && g_sci->isCD() && !_forceDOSTracks) {  		SciGameId id = g_sci->getGameId();  		return (id == GID_ECOQUEST ||  				id == GID_JONES || diff --git a/engines/sci/engine/features.h b/engines/sci/engine/features.h index 4592c5be9c..f6bb0b5759 100644 --- a/engines/sci/engine/features.h +++ b/engines/sci/engine/features.h @@ -117,6 +117,12 @@ public:  	 */  	bool useAltWinGMSound(); +	/** +	 * Forces DOS soundtracks in Windows CD versions when the user hasn't +	 * selected a MIDI output device +	 */ +	void forceDOSTracks() { _forceDOSTracks = true; } +  private:  	reg_t getDetectionAddr(const Common::String &objName, Selector slc, int methodNum = -1); @@ -137,6 +143,7 @@ private:  	MoveCountType _moveCountType;  	bool _usesCdTrack; +	bool _forceDOSTracks;  	SegManager *_segMan;  	Kernel *_kernel; diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp index 71f4598afc..14ffa69f91 100644 --- a/engines/sci/graphics/cursor.cpp +++ b/engines/sci/graphics/cursor.cpp @@ -148,11 +148,13 @@ void GfxCursor::kernelSetShape(GuiResourceId resourceId) {  	colorMapping[1] = _screen->getColorWhite(); // White is also hardcoded  	colorMapping[2] = SCI_CURSOR_SCI0_TRANSPARENCYCOLOR;  	colorMapping[3] = _palette->matchColor(170, 170, 170); // Grey -	// Special case for the magnifier cursor in LB1 (bug #3487092). -	// No other SCI0 game has a cursor resource of 1, so this is handled -	// specifically for LB1. +	// TODO: Figure out if the grey color is hardcoded +	// HACK for the magnifier cursor in LB1, fixes its color (bug #3487092)  	if (g_sci->getGameId() == GID_LAURABOW && resourceId == 1)  		colorMapping[3] = _screen->getColorWhite(); +	// HACK for Longbow cursors, fixes the shade of grey they're using (bug #3489101) +	if (g_sci->getGameId() == GID_LONGBOW) +		colorMapping[3] = _palette->matchColor(223, 223, 223); // Light Grey  	// Seek to actual data  	resourceData += 4; diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp index 09cab75c39..918b045cb9 100644 --- a/engines/sci/sound/music.cpp +++ b/engines/sci/sound/music.cpp @@ -88,6 +88,12 @@ void SciMusic::init() {  	uint32 dev = MidiDriver::detectDevice(deviceFlags);  	_musicType = MidiDriver::getMusicType(dev); +	if (g_sci->_features->useAltWinGMSound() && _musicType != MT_GM) { +		warning("A Windows CD version with an alternate MIDI soundtrack has been chosen, " +				"but no MIDI music device has been selected. Reverting to the DOS soundtrack"); +		g_sci->_features->forceDOSTracks(); +	} +  	switch (_musicType) {  	case MT_ADLIB:  		// FIXME: There's no Amiga sound option, so we hook it up to AdLib diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp index b47982af00..2da0abb5df 100644 --- a/engines/scumm/detection.cpp +++ b/engines/scumm/detection.cpp @@ -589,11 +589,11 @@ static void detectGames(const Common::FSList &fslist, Common::List<DetectorResul  						file.c_str(), md5str.c_str(), filesize);  					// Sanity check: We *should* have found a matching gameid / variant at this point. -					// If not, then there's a bug in our data tables... -					assert(dr.game.gameid != 0); - -					// Add it to the list of detected games -					results.push_back(dr); +					// If not, we may have #ifdef'ed the entry out in our detection_tables.h because we +					// don't have the required stuff compiled in, or there's a bug in our data tables... +					if (dr.game.gameid != 0) +						// Add it to the list of detected games +						results.push_back(dr);  				}  			} diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h index c25c875616..3957c7c42d 100644 --- a/engines/scumm/scumm-md5.h +++ b/engines/scumm/scumm-md5.h @@ -1,5 +1,5 @@  /* -  This file was generated by the md5table tool on Mon Apr  9 12:23:48 2012 +  This file was generated by the md5table tool on Tue Apr 24 03:50:58 2012    DO NOT EDIT MANUALLY!   */ @@ -621,6 +621,7 @@ static const MD5Table md5table[] = {  	{ "f40a7f495f59188ca57a9d1d50301bb6", "puttputt", "HE 60", "Demo", -1, Common::EN_ANY, Common::kPlatformMacintosh },  	{ "f5228b0cc1c19e6ea8268ba2eeb61f60", "freddi", "HE 73", "Demo", -1, Common::FR_FRA, Common::kPlatformWindows },  	{ "f73883f13b5a302749a5bad31d909780", "tentacle", "", "CD", -1, Common::DE_DEU, Common::kPlatformMacintosh }, +	{ "f7635a0e2ab82c9a0f9ace5f232a488f", "catalog", "HE 72", "Demo", -1, Common::EN_ANY, Common::kPlatformWindows },  	{ "f7711f9264d4d43c2a1518ec7c10a607", "pajama3", "", "", 79382, Common::EN_USA, Common::kPlatformUnknown },  	{ "f79e60c17cca601e411f1f75e8ee9b5a", "spyfox2", "", "", 51286, Common::UNK_LANG, Common::kPlatformUnknown },  	{ "f8be685007a8b425ba2a455da732f59f", "pajama2", "HE 99", "", -1, Common::FR_FRA, Common::kPlatformMacintosh }, diff --git a/engines/sword25/script/luabindhelper.cpp b/engines/sword25/script/luabindhelper.cpp index 8fbbe7e272..6900305f5c 100644 --- a/engines/sword25/script/luabindhelper.cpp +++ b/engines/sword25/script/luabindhelper.cpp @@ -412,7 +412,7 @@ Common::String LuaBindhelper::stackDump(lua_State *L) {  	oss += "------------------- Stack Dump -------------------\n";  	while (i) { -		oss += i + ": " + getLuaValueInfo(L, i) + "\n"; +		oss += Common::String::format("%d: ", i) + getLuaValueInfo(L, i) + "\n";  		i--;  	} diff --git a/engines/tinsel/detection_tables.h b/engines/tinsel/detection_tables.h index be44b1c462..b6b19f6ee7 100644 --- a/engines/tinsel/detection_tables.h +++ b/engines/tinsel/detection_tables.h @@ -397,7 +397,7 @@ static const TinselGameDescription gameDescriptions[] = {  	{	// multilanguage PSX demo  		{  			"dw", -			"CD demo", +			"CD Demo",  			{  				{"french.txt", 0, "e7020d35f58d0d187052ac406d86cc87", 273914},  				{"german.txt", 0, "52f0a01e0ff0d340b02a36fd5109d705", 263942}, diff --git a/engines/tinsel/music.cpp b/engines/tinsel/music.cpp index 781a378f13..fa5334a033 100644 --- a/engines/tinsel/music.cpp +++ b/engines/tinsel/music.cpp @@ -131,11 +131,6 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {  	g_currentMidi = dwFileOffset;  	g_currentLoop = bLoop; -	// Tinsel V1 PSX uses a different music format, so i -	// disable it here. -	// TODO: Maybe this should be moved to a better place... -	if (TinselV1PSX) return false; -  	if (_vm->_config->_musicVolume != 0) {  		bool mute = false;  		if (ConfMan.hasKey("mute")) @@ -231,14 +226,14 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {  			_vm->_midiMusic->send(0x7F07B0 | 13);  		} -		_vm->_midiMusic->playXMIDI(g_midiBuffer.pDat, dwSeqLen, bLoop); +		_vm->_midiMusic->playMIDI(dwSeqLen, bLoop);  		// Store the length  		//dwLastSeqLen = dwSeqLen;  	} else {  	 	// dwFileOffset == dwLastMidiIndex  		_vm->_midiMusic->stop(); -		_vm->_midiMusic->playXMIDI(g_midiBuffer.pDat, dwSeqLen, bLoop); +		_vm->_midiMusic->playMIDI(dwSeqLen, bLoop);  	}  	return true; @@ -314,8 +309,7 @@ void OpenMidiFiles() {  	Common::File midiStream;  	// Demo version has no midi file -	// Also, Discworld PSX uses still unsupported psx SEQ format for music... -	if ((_vm->getFeatures() & GF_DEMO) || (TinselVersion == TINSEL_V2) || TinselV1PSX) +	if ((_vm->getFeatures() & GF_DEMO) || (TinselVersion == TINSEL_V2))  		return;  	if (g_midiBuffer.pDat) @@ -412,7 +406,7 @@ void MidiMusicPlayer::send(uint32 b) {  	}  } -void MidiMusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) { +void MidiMusicPlayer::playMIDI(uint32 size, bool loop) {  	Common::StackLock lock(_mutex);  	if (_isPlaying) @@ -420,6 +414,13 @@ void MidiMusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) {  	stop(); +	if (TinselV1PSX) +		playSEQ(size, loop); +	else +		playXMIDI(size, loop); +} + +void MidiMusicPlayer::playXMIDI(uint32 size, bool loop) {  	// It seems like not all music (the main menu music, for instance) set  	// all the instruments explicitly. That means the music will sound  	// different, depending on which music played before it. This appears @@ -433,7 +434,78 @@ void MidiMusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) {  	// Load XMID resource data  	MidiParser *parser = MidiParser::createParser_XMIDI(); -	if (parser->loadMusic(midiData, size)) { +	if (parser->loadMusic(g_midiBuffer.pDat, size)) { +		parser->setTrack(0); +		parser->setMidiDriver(this); +		parser->setTimerRate(getBaseTempo()); +		parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1); +		parser->property(MidiParser::mpSendSustainOffOnNotesOff, 1); + +		_parser = parser; + +		_isLooping = loop; +		_isPlaying = true; +	} else { +		delete parser; +	} +} + +void MidiMusicPlayer::playSEQ(uint32 size, bool loop) { +	// MIDI.DAT holds the file names in DW1 PSX +	Common::String baseName((char *)g_midiBuffer.pDat, size); +	Common::String seqName = baseName + ".SEQ"; + +	// TODO: Load the instrument bank (<baseName>.VB and <baseName>.VH) + +	Common::File seqFile; +	if (!seqFile.open(seqName)) +		error("Failed to open SEQ file '%s'", seqName.c_str()); + +	if (seqFile.readUint32LE() != MKTAG('S', 'E', 'Q', 'p')) +		error("Failed to find SEQp tag"); + +	// Make sure we don't have a SEP file (with multiple SEQ's inside) +	if (seqFile.readUint32BE() != 1) +		error("Can only play SEQ files, not SEP"); + +	uint16 ppqn = seqFile.readUint16BE(); +	uint32 tempo = seqFile.readUint16BE() << 8; +	tempo |= seqFile.readByte(); +	/* uint16 beat = */ seqFile.readUint16BE(); + +	// SEQ is directly based on SMF and we'll use that to our advantage here +	// and convert to SMF and then use the SMF MidiParser. + +	// Calculate the SMF size we'll need +	uint32 dataSize = seqFile.size() - 15; +	uint32 actualSize = dataSize + 7 + 22; + +	// Resize the buffer if necessary +	if (g_midiBuffer.size < actualSize) { +		g_midiBuffer.pDat = (byte *)realloc(g_midiBuffer.pDat, actualSize); +		assert(g_midiBuffer.pDat); +	} + +	// Now construct the header +	WRITE_BE_UINT32(g_midiBuffer.pDat, MKTAG('M', 'T', 'h', 'd')); +	WRITE_BE_UINT32(g_midiBuffer.pDat + 4, 6); // header size +	WRITE_BE_UINT16(g_midiBuffer.pDat + 8, 0); // type 0 +	WRITE_BE_UINT16(g_midiBuffer.pDat + 10, 1); // one track +	WRITE_BE_UINT16(g_midiBuffer.pDat + 12, ppqn); +	WRITE_BE_UINT32(g_midiBuffer.pDat + 14, MKTAG('M', 'T', 'r', 'k')); +	WRITE_BE_UINT32(g_midiBuffer.pDat + 18, dataSize + 7); // SEQ data size + tempo change event size + +	// Add in a fake tempo change event +	WRITE_BE_UINT32(g_midiBuffer.pDat + 22, 0x00FF5103); // no delta, meta event, tempo change, param size = 3 +	WRITE_BE_UINT16(g_midiBuffer.pDat + 26, tempo >> 8); +	g_midiBuffer.pDat[28] = tempo & 0xFF; + +	// Now copy in the rest of the events +	seqFile.read(g_midiBuffer.pDat + 29, dataSize); +	seqFile.close(); + +	MidiParser *parser = MidiParser::createParser_SMF(); +	if (parser->loadMusic(g_midiBuffer.pDat, actualSize)) {  		parser->setTrack(0);  		parser->setMidiDriver(this);  		parser->setTimerRate(getBaseTempo()); diff --git a/engines/tinsel/music.h b/engines/tinsel/music.h index d43fed268d..121bf3d79b 100644 --- a/engines/tinsel/music.h +++ b/engines/tinsel/music.h @@ -64,7 +64,7 @@ public:  	virtual void setVolume(int volume); -	void playXMIDI(byte *midiData, uint32 size, bool loop); +	void playMIDI(uint32 size, bool loop);  //	void stop();  	void pause(); @@ -76,6 +76,10 @@ public:  	// The original sets the "sequence timing" to 109 Hz, whatever that  	// means. The default is 120.  	uint32 getBaseTempo()	{ return _driver ? (109 * _driver->getBaseTempo()) / 120 : 0; } + +private: +	void playXMIDI(uint32 size, bool loop); +	void playSEQ(uint32 size, bool loop);  };  class PCMMusicPlayer : public Audio::AudioStream { diff --git a/engines/tinsel/tinlib.cpp b/engines/tinsel/tinlib.cpp index c652abca25..cd65a4ec32 100644 --- a/engines/tinsel/tinlib.cpp +++ b/engines/tinsel/tinlib.cpp @@ -1626,7 +1626,7 @@ static void Play(CORO_PARAM, SCNHANDLE hFilm, int x, int y, bool bComplete, int   */  static void PlayMidi(CORO_PARAM, SCNHANDLE hMidi, int loop, bool complete) {  	// FIXME: This is a workaround for the FIXME below -	if (GetMidiVolume() == 0 || TinselV1PSX) +	if (GetMidiVolume() == 0)  		return;  	CORO_BEGIN_CONTEXT; diff --git a/graphics/decoders/bmp.h b/graphics/decoders/bmp.h index 6360aa81c9..8a37538ee1 100644 --- a/graphics/decoders/bmp.h +++ b/graphics/decoders/bmp.h @@ -51,7 +51,7 @@ public:  	void destroy();  	virtual bool loadStream(Common::SeekableReadStream &stream);  	virtual const Surface *getSurface() const { return _surface; } -	virtual const byte *getPalette() { return _palette; } +	const byte *getPalette() const { return _palette; }  private:  	Surface *_surface; diff --git a/graphics/decoders/pict.cpp b/graphics/decoders/pict.cpp index 957342084e..bdb733a87d 100644 --- a/graphics/decoders/pict.cpp +++ b/graphics/decoders/pict.cpp @@ -361,14 +361,14 @@ void PICTDecoder::unpackBitsRect(Common::SeekableReadStream &stream, bool hasPal  		memcpy(_outputSurface->pixels, buffer, _outputSurface->w * _outputSurface->h);  		break;  	case 2: -		// Convert from 16-bit to whatever surface we need +		// We have a 16-bit surface  		_outputSurface->create(width, height, PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0));  		for (uint16 y = 0; y < _outputSurface->h; y++)  			for (uint16 x = 0; x < _outputSurface->w; x++)  				WRITE_UINT16(_outputSurface->getBasePtr(x, y), READ_UINT16(buffer + (y * _outputSurface->w + x) * 2));  		break;  	case 3: -		// Convert from 24-bit (planar!) to whatever surface we need +		// We have a planar 24-bit surface  		_outputSurface->create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));  		for (uint16 y = 0; y < _outputSurface->h; y++) {  			for (uint16 x = 0; x < _outputSurface->w; x++) { @@ -380,15 +380,18 @@ void PICTDecoder::unpackBitsRect(Common::SeekableReadStream &stream, bool hasPal  		}  		break;  	case 4: -		// Convert from 32-bit (planar!) to whatever surface we need +		// We have a planar 32-bit surface +		// Note that we ignore the alpha channel since it seems to not be correct +		// Mac OS X does not ignore it, but then displays it incorrectly. Photoshop +		// does ignore it and displays it correctly.  		_outputSurface->create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));  		for (uint16 y = 0; y < _outputSurface->h; y++) {  			for (uint16 x = 0; x < _outputSurface->w; x++) { -				byte r = *(buffer + y * _outputSurface->w * 4 + x); -				byte g = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w + x); -				byte b = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 2 + x); -				byte a = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 3 + x); -				*((uint32 *)_outputSurface->getBasePtr(x, y)) = _outputSurface->format.ARGBToColor(r, g, b, a); +				byte a = 0xFF; +				byte r = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w + x); +				byte g = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 2 + x); +				byte b = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 3 + x); +				*((uint32 *)_outputSurface->getBasePtr(x, y)) = _outputSurface->format.ARGBToColor(a, r, g, b);  			}  		}  		break; @@ -541,10 +544,14 @@ void PICTDecoder::decodeCompressedQuickTime(Common::SeekableReadStream &stream)  	// Now we've reached the image descriptor, so read the relevant data from that  	uint32 idStart = stream.pos();  	uint32 idSize = stream.readUint32BE(); -	stream.skip(40); // miscellaneous stuff +	uint32 codec = stream.readUint32BE(); +	stream.skip(36); // miscellaneous stuff  	uint32 jpegSize = stream.readUint32BE();  	stream.skip(idSize - (stream.pos() - idStart)); // more useless stuff +	if (codec != MKTAG('j', 'p', 'e', 'g')) +		error("Unhandled CompressedQuickTime format '%s'", tag2str(codec)); +  	Common::SeekableSubReadStream jpegStream(&stream, stream.pos(), stream.pos() + jpegSize);  	JPEGDecoder jpeg; diff --git a/graphics/fontman.cpp b/graphics/fontman.cpp index 8d967d595f..99dd3d664f 100644 --- a/graphics/fontman.cpp +++ b/graphics/fontman.cpp @@ -47,6 +47,13 @@ FontManager::FontManager() {  }  FontManager::~FontManager() { +	for (uint i = 0; i < _ownedFonts.size(); ++i) { +		const Font *font = _ownedFonts[i]; +		if (font == g_sysfont || font == g_sysfont_big || font == g_consolefont) +			continue; +		delete font; +	} +  	delete g_sysfont;  	g_sysfont = 0;  	delete g_sysfont_big; @@ -90,6 +97,8 @@ bool FontManager::assignFontToName(const Common::String &name, const Font *font)  	Common::String lowercaseName = name;  	lowercaseName.toLowercase();  	_fontMap[lowercaseName] = font; +	if (Common::find(_ownedFonts.begin(), _ownedFonts.end(), font) == _ownedFonts.end()) +		_ownedFonts.push_back(font);  	return true;  } @@ -116,8 +125,35 @@ bool FontManager::setFont(FontUsage usage, const BdfFont *font) {  void FontManager::removeFontName(const Common::String &name) {  	Common::String lowercaseName = name;  	lowercaseName.toLowercase(); +	if (!_fontMap.contains(lowercaseName)) +		return; + +	const Font *font = _fontMap[lowercaseName];  	_fontMap.erase(lowercaseName); +	// Check if we still have a copy of this font in the map. +	bool stillHasFont = false; +	for (Common::HashMap<Common::String, const Font *>::iterator i = _fontMap.begin(); i != _fontMap.end(); ++i) { +		if (i->_value != font) +			continue; +		stillHasFont = true; +		break; +	} + +	if (!stillHasFont) { +		// We don't have a copy of the font, so remove it from our list and delete it. +		stillHasFont = true; +		for (uint i = 0; i < _ownedFonts.size(); ++i) { +			if (_ownedFonts[i] != font) +				continue; +			stillHasFont = false; +			_ownedFonts.remove_at(i); +			break; +		} +		assert(!stillHasFont); +		delete font; +	} +  	// In case the current localized font is removed, we fall back to the  	// default font again.  	if (_localizedFontName == lowercaseName) diff --git a/graphics/fontman.h b/graphics/fontman.h index 42f7d856fa..b06ddea860 100644 --- a/graphics/fontman.h +++ b/graphics/fontman.h @@ -60,7 +60,9 @@ public:  	const Font *getFontByName(const Common::String &name) const;  	/** -	 * Associates a font object with an 'name' +	 * Associates a font object with an 'name'. +	 * The FontManager takes ownership of the provided font object +	 * and will delete it when necesssary.  	 *  	 * @param name	the name of the font  	 * @param font	the font object @@ -111,6 +113,7 @@ private:  	~FontManager();  	Common::HashMap<Common::String, const Font *> _fontMap; +	Common::Array<const Font *> _ownedFonts;  	Common::String _localizedFontName;  }; diff --git a/graphics/fonts/bdf.h b/graphics/fonts/bdf.h index 5b615cc043..b0166a2095 100644 --- a/graphics/fonts/bdf.h +++ b/graphics/fonts/bdf.h @@ -77,7 +77,7 @@ private:  #define DEFINE_FONT(n) \  	const BdfFont *n = 0;   \  	void create_##n() { \ -		n = new BdfFont(desc, DisposeAfterUse::YES);  \ +		n = new BdfFont(desc, DisposeAfterUse::NO);  \  	}  #define FORWARD_DECLARE_FONT(n) \ diff --git a/graphics/fonts/ttf.cpp b/graphics/fonts/ttf.cpp index 7505f7913e..96241e923c 100644 --- a/graphics/fonts/ttf.cpp +++ b/graphics/fonts/ttf.cpp @@ -43,10 +43,6 @@ namespace Graphics {  namespace { -inline int ftFloor26_6(FT_Pos x) { -	return x / 64; -} -  inline int ftCeil26_6(FT_Pos x) {  	return (x + 63) / 64;  } @@ -70,6 +66,10 @@ private:  	bool _initialized;  }; +void shutdownTTF() { +	TTFLibrary::destroy(); +} +  #define g_ttf ::Graphics::TTFLibrary::instance()  TTFLibrary::TTFLibrary() : _library(), _initialized(false) { diff --git a/graphics/fonts/ttf.h b/graphics/fonts/ttf.h index 7222d6e112..ec7dbe04ef 100644 --- a/graphics/fonts/ttf.h +++ b/graphics/fonts/ttf.h @@ -34,6 +34,8 @@ namespace Graphics {  class Font;  Font *loadTTFFont(Common::SeekableReadStream &stream, int size, bool monochrome = false, const uint32 *mapping = 0); +void shutdownTTF(); +  } // End of namespace Graphics  #endif diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp index fdd7750af9..be0a5db601 100644 --- a/gui/ThemeEngine.cpp +++ b/gui/ThemeEngine.cpp @@ -175,6 +175,7 @@ static const DrawDataInfo kDrawDataDefaults[] = {  	{kDDButtonIdle,                 "button_idle",      true,   kDDWidgetBackgroundSlider},  	{kDDButtonHover,                "button_hover",     false,  kDDButtonIdle},  	{kDDButtonDisabled,             "button_disabled",  true,   kDDNone}, +	{kDDButtonPressed,              "button_pressed",   false,  kDDButtonIdle},  	{kDDSliderFull,                 "slider_full",      false,  kDDNone},  	{kDDSliderHover,                "slider_hover",     false,  kDDNone}, @@ -877,6 +878,8 @@ void ThemeEngine::drawButton(const Common::Rect &r, const Common::String &str, W  		dd = kDDButtonHover;  	else if (state == kStateDisabled)  		dd = kDDButtonDisabled; +	else if (state == kStatePressed) +		dd = kDDButtonPressed;  	queueDD(dd, r, 0, hints & WIDGET_CLEARBG);  	queueDDText(getTextData(dd), getTextColor(dd), r, str, false, true, _widgets[dd]->_textAlignH, _widgets[dd]->_textAlignV); @@ -1125,6 +1128,7 @@ void ThemeEngine::drawText(const Common::Rect &r, const Common::String &str, Wid  				break;  			case kStateEnabled: +			case kStatePressed:  				colorId = kTextColorNormal;  				break;  			} @@ -1145,6 +1149,7 @@ void ThemeEngine::drawText(const Common::Rect &r, const Common::String &str, Wid  				break;  			case kStateEnabled: +			case kStatePressed:  				colorId = kTextColorAlternative;  				break;  			} diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h index 0495a85c88..acded085f5 100644 --- a/gui/ThemeEngine.h +++ b/gui/ThemeEngine.h @@ -35,7 +35,7 @@  #include "graphics/pixelformat.h" -#define SCUMMVM_THEME_VERSION_STR "SCUMMVM_STX0.8.11" +#define SCUMMVM_THEME_VERSION_STR "SCUMMVM_STX0.8.12"  class OSystem; @@ -81,6 +81,7 @@ enum DrawData {  	kDDButtonIdle,  	kDDButtonHover,  	kDDButtonDisabled, +	kDDButtonPressed,  	kDDSliderFull,  	kDDSliderHover, @@ -178,7 +179,8 @@ public:  	enum State {  		kStateDisabled,     ///< Indicates that the widget is disabled, that does NOT include that it is invisible  		kStateEnabled,      ///< Indicates that the widget is enabled -		kStateHighlight     ///< Indicates that the widget is highlighted by the user +		kStateHighlight,    ///< Indicates that the widget is highlighted by the user +		kStatePressed       ///< Indicates that the widget is pressed, currently works for buttons  	};  	typedef State WidgetStateInfo; diff --git a/gui/dialog.cpp b/gui/dialog.cpp index 2201e83ca5..ffca15bbc8 100644 --- a/gui/dialog.cpp +++ b/gui/dialog.cpp @@ -42,7 +42,7 @@ namespace GUI {  Dialog::Dialog(int x, int y, int w, int h)  	: GuiObject(x, y, w, h), -	  _mouseWidget(0), _focusedWidget(0), _dragWidget(0), _visible(false), +	  _mouseWidget(0), _focusedWidget(0), _dragWidget(0), _tickleWidget(0), _visible(false),  	_backgroundType(GUI::ThemeEngine::kDialogBackgroundDefault) {  	// Some dialogs like LauncherDialog use internally a fixed size, even though  	// their widgets rely on the layout to be initialized correctly by the theme. @@ -54,7 +54,7 @@ Dialog::Dialog(int x, int y, int w, int h)  Dialog::Dialog(const Common::String &name)  	: GuiObject(name), -	  _mouseWidget(0), _focusedWidget(0), _dragWidget(0), _visible(false), +	  _mouseWidget(0), _focusedWidget(0), _dragWidget(0), _tickleWidget(0), _visible(false),  	_backgroundType(GUI::ThemeEngine::kDialogBackgroundDefault) {  	// It may happen that we have 3x scaler in launcher (960xY) and then 640x480 @@ -117,6 +117,12 @@ void Dialog::reflowLayout() {  	GuiObject::reflowLayout();  } +void Dialog::lostFocus() { +	if (_tickleWidget) { +		_tickleWidget->lostFocus(); +	} +} +  void Dialog::setFocusWidget(Widget *widget) {  	// The focus will change. Tell the old focused widget (if any)  	// that it lost the focus. @@ -308,6 +314,9 @@ void Dialog::handleTickle() {  	// Focused widget receives tickle notifications  	if (_focusedWidget && _focusedWidget->getFlags() & WIDGET_WANT_TICKLE)  		_focusedWidget->handleTickle(); + +	if (_tickleWidget && _tickleWidget->getFlags() & WIDGET_WANT_TICKLE) +		_tickleWidget->handleTickle();  }  void Dialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { diff --git a/gui/dialog.h b/gui/dialog.h index f5a5f94a68..1773c6633e 100644 --- a/gui/dialog.h +++ b/gui/dialog.h @@ -52,6 +52,7 @@ protected:  	Widget	*_mouseWidget;  	Widget  *_focusedWidget;  	Widget  *_dragWidget; +	Widget 	*_tickleWidget;  	bool	_visible;  	ThemeEngine::DialogBackground _backgroundType; @@ -71,7 +72,13 @@ public:  	void	setFocusWidget(Widget *widget);  	Widget *getFocusWidget() { return _focusedWidget; } +	void setTickleWidget(Widget *widget) { _tickleWidget = widget; } +	void unSetTickleWidget() { _tickleWidget = NULL; } +	Widget *getTickleWidget() { return _tickleWidget; } +  	virtual void reflowLayout(); +	virtual void lostFocus(); +	virtual void receivedFocus() {}  protected:  	virtual void open(); diff --git a/gui/gui-manager.cpp b/gui/gui-manager.cpp index ffecd928bc..abd781e1a3 100644 --- a/gui/gui-manager.cpp +++ b/gui/gui-manager.cpp @@ -381,7 +381,7 @@ void GuiManager::runLoop() {  		if (tooltipCheck && _lastMousePosition.time + kTooltipDelay < _system->getMillis()) {  			Widget *wdg = activeDialog->findWidget(_lastMousePosition.x, _lastMousePosition.y); -			if (wdg && wdg->getTooltip()) { +			if (wdg && wdg->getTooltip() && !(wdg->getFlags() & WIDGET_PRESSED)) {  				Tooltip *tooltip = new Tooltip();  				tooltip->setup(activeDialog, wdg, _lastMousePosition.x, _lastMousePosition.y);  				tooltip->runModal(); @@ -441,6 +441,11 @@ void GuiManager::restoreState() {  }  void GuiManager::openDialog(Dialog *dialog) { +	dialog->receivedFocus(); + +	if (!_dialogStack.empty()) +		getTopDialog()->lostFocus(); +  	_dialogStack.push(dialog);  	if (_redrawStatus != kRedrawFull)  		_redrawStatus = kRedrawOpenDialog; @@ -458,7 +463,11 @@ void GuiManager::closeTopDialog() {  		return;  	// Remove the dialog from the stack -	_dialogStack.pop(); +	_dialogStack.pop()->lostFocus(); + +	if (!_dialogStack.empty()) +		getTopDialog()->receivedFocus(); +  	if (_redrawStatus != kRedrawFull)  		_redrawStatus = kRedrawCloseDialog; diff --git a/gui/launcher.cpp b/gui/launcher.cpp index a86a98f819..c8ed3126c4 100644 --- a/gui/launcher.cpp +++ b/gui/launcher.cpp @@ -663,7 +663,6 @@ LauncherDialog::LauncherDialog()  	_list->setEditable(false);  	_list->setNumberingMode(kListNumberingOff); -  	// Populate the list  	updateListing(); diff --git a/gui/predictivedialog.cpp b/gui/predictivedialog.cpp index 9cd18b81ba..b827d49416 100644 --- a/gui/predictivedialog.cpp +++ b/gui/predictivedialog.cpp @@ -69,31 +69,33 @@ enum {  PredictiveDialog::PredictiveDialog() : Dialog("Predictive") {  	new StaticTextWidget(this, "Predictive.Headline", "Enter Text"); -	new ButtonWidget(this, "Predictive.Cancel" , _("Cancel"), 0, kCancelCmd); -	new ButtonWidget(this, "Predictive.OK"     , _("Ok")    , 0, kOkCmd); -	new ButtonWidget(this, "Predictive.Button1", "1  `-.&"  , 0, kBut1Cmd); -	new ButtonWidget(this, "Predictive.Button2", "2  abc"   , 0, kBut2Cmd); -	new ButtonWidget(this, "Predictive.Button3", "3  def"   , 0, kBut3Cmd); -	new ButtonWidget(this, "Predictive.Button4", "4  ghi"   , 0, kBut4Cmd); -	new ButtonWidget(this, "Predictive.Button5", "5  jkl"   , 0, kBut5Cmd); -	new ButtonWidget(this, "Predictive.Button6", "6  mno"   , 0, kBut6Cmd); -	new ButtonWidget(this, "Predictive.Button7", "7  pqrs"  , 0, kBut7Cmd); -	new ButtonWidget(this, "Predictive.Button8", "8  tuv"   , 0, kBut8Cmd); -	new ButtonWidget(this, "Predictive.Button9", "9  wxyz"  , 0, kBut9Cmd); -	new ButtonWidget(this, "Predictive.Button0", "0"        , 0, kBut0Cmd); -	// I18N: You must leave "#" as is, only word 'next' is translatable -	new ButtonWidget(this, "Predictive.Next"   , _("#  next"), 0, kNextCmd); -	_addBtn = new ButtonWidget(this, "Predictive.Add", _("add"), 0, kAddCmd); -	_addBtn->setEnabled(false); - -#ifndef DISABLE_FANCY_THEMES -	_delbtn = new PicButtonWidget(this, "Predictive.Delete", _("Delete char"), kDelCmd); -	((PicButtonWidget *)_delbtn)->useThemeTransparency(true); -	((PicButtonWidget *)_delbtn)->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageDelbtn)); -#endif -	_delbtn = new ButtonWidget(this, "Predictive.Delete" , _("<"), 0, kDelCmd); -	// I18N: Pre means 'Predictive', leave '*' as is -	_modebutton = new ButtonWidget(this, "Predictive.Pre", _("*  Pre"), 0, kModeCmd); +	_btns = (ButtonWidget **)calloc(1, sizeof(ButtonWidget *) * 16); + +	_btns[kCancelAct] = new ButtonWidget(this, "Predictive.Cancel",  _("Cancel")   , 0, kCancelCmd);  +	_btns[kOkAct] = 	new ButtonWidget(this, "Predictive.OK",      _("Ok")       , 0, kOkCmd); +	_btns[kBtn1Act] = 	new ButtonWidget(this, "Predictive.Button1", "1  `-.&" , 0, kBut1Cmd); +	_btns[kBtn2Act] = 	new ButtonWidget(this, "Predictive.Button2", "2  abc"     , 0, kBut2Cmd); +	_btns[kBtn3Act] = 	new ButtonWidget(this, "Predictive.Button3", "3  def"     , 0, kBut3Cmd); +	_btns[kBtn4Act] = 	new ButtonWidget(this, "Predictive.Button4", "4  ghi"     , 0, kBut4Cmd); +	_btns[kBtn5Act] = 	new ButtonWidget(this, "Predictive.Button5", "5  jkl"     , 0, kBut5Cmd); +	_btns[kBtn6Act] = 	new ButtonWidget(this, "Predictive.Button6", "6  mno"     , 0, kBut6Cmd); +	_btns[kBtn7Act] = 	new ButtonWidget(this, "Predictive.Button7", "7  pqrs"    , 0, kBut7Cmd); +	_btns[kBtn8Act] = 	new ButtonWidget(this, "Predictive.Button8", "8  tuv"     , 0, kBut8Cmd); +	_btns[kBtn9Act] = 	new ButtonWidget(this, "Predictive.Button9", "9  wxyz"    , 0, kBut9Cmd); +	_btns[kBtn0Act] = 	new ButtonWidget(this, "Predictive.Button0", "0"        , 0, kBut0Cmd); +  	// I18N: You must leave "#" as is, only word 'next' is translatable +	_btns[kNextAct] = 	new ButtonWidget(this, "Predictive.Next",    _("#  next") , 0, kNextCmd);  +	_btns[kAddAct] = 	new ButtonWidget(this, "Predictive.Add",     _("add") , 0, kAddCmd); +	_btns[kAddAct]->setEnabled(false); +   +  #ifndef DISABLE_FANCY_THEMES +	_btns[kDelAct] = new PicButtonWidget(this, "Predictive.Delete", _("Delete char"), kDelCmd); +	((PicButtonWidget *)_btns[kDelAct])->useThemeTransparency(true); +	((PicButtonWidget *)_btns[kDelAct])->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageDelbtn)); +  #endif +	_btns[kDelAct] = new ButtonWidget(this, "Predictive.Delete" , _("<") , 0, kDelCmd); +  	// I18N: Pre means 'Predictive', leave '*' as is +	_btns[kModeAct] = new ButtonWidget(this, "Predictive.Pre", _("*  Pre"), 0, kModeCmd);  	_edittext = new EditTextWidget(this, "Predictive.Word", _search, 0, 0, 0);  	_userDictHasChanged = false; @@ -164,6 +166,8 @@ PredictiveDialog::~PredictiveDialog() {  	free(_userDict.dictLine);  	free(_predictiveDict.dictLine);  	free(_unitedDict.dictLine); + +	free(_btns);  }  void PredictiveDialog::saveUserDictToFile() { @@ -182,11 +186,23 @@ void PredictiveDialog::saveUserDictToFile() {  	}  } +void PredictiveDialog::handleKeyUp(Common::KeyState state) { +	if (_currBtn != kNoAct && !_needRefresh) { +		_btns[_currBtn]->startAnimatePressedState(); +		processBtnActive(_currBtn); +	} +} +  void PredictiveDialog::handleKeyDown(Common::KeyState state) { -	ButtonId act = kNoAct; +	_currBtn = kNoAct; +	_needRefresh = false;  	if (getFocusWidget() == _edittext) { -		setFocusWidget(_addBtn); +		setFocusWidget(_btns[kAddAct]); +	} + +	if (_lastbutton == kNoAct) { +		_lastbutton = kBtn5Act;  	}  	switch (state.keycode) { @@ -197,96 +213,126 @@ void PredictiveDialog::handleKeyDown(Common::KeyState state) {  	case Common::KEYCODE_LEFT:  		_navigationwithkeys = true;  		if (_lastbutton == kBtn1Act || _lastbutton == kBtn4Act || _lastbutton == kBtn7Act) -			act = ButtonId(_lastbutton + 2); +			_currBtn = ButtonId(_lastbutton + 2); +		else if (_lastbutton == kDelAct)  +			_currBtn = kBtn1Act; +		else if (_lastbutton == kModeAct) +			_currBtn = kNextAct;  		else if (_lastbutton == kNextAct) -			act = kBtn0Act; -		else if (_lastbutton == kDelAct) -			act = kDelAct; +			_currBtn = kBtn0Act; +		else if (_lastbutton == kAddAct) +			_currBtn = kOkAct;  		else if (_lastbutton == kCancelAct) -			act = kOkAct; -		else if (_lastbutton == kModeAct) -			act = kAddAct; +			_currBtn = kAddAct;  		else -			act = ButtonId(_lastbutton - 1); -		_lastbutton = act; -		//needRefresh = true; +			_currBtn = ButtonId(_lastbutton - 1); + +		 +		if (_mode != kModeAbc && _lastbutton == kCancelAct) +			_currBtn = kOkAct; + +		_needRefresh = true;  		break;  	case Common::KEYCODE_RIGHT:  		_navigationwithkeys = true; -		if (_lastbutton == kBtn3Act || _lastbutton == kBtn6Act || _lastbutton == kBtn9Act) -			act = ButtonId(_lastbutton - 2); +		if (_lastbutton == kBtn3Act || _lastbutton == kBtn6Act || _lastbutton == kBtn9Act || _lastbutton == kOkAct) +			_currBtn = ButtonId(_lastbutton - 2); +		else if (_lastbutton == kDelAct)  +			_currBtn = kBtn3Act; +		else if (_lastbutton == kBtn0Act) +			_currBtn = kNextAct; +		else if (_lastbutton == kNextAct) +			_currBtn = kModeAct;  		else if (_lastbutton == kAddAct) -			act = kModeAct; -		else if (_lastbutton == kDelAct) -			act = kDelAct; +			_currBtn = kCancelAct;  		else if (_lastbutton == kOkAct) -			act = kCancelAct; -		else if (_lastbutton == kBtn0Act) -			act = kNextAct; +			_currBtn = kAddAct;  		else -			act = ButtonId(_lastbutton + 1); -		_lastbutton = act; -		//needRefresh = true; +			_currBtn = ButtonId(_lastbutton + 1); +		 +		if (_mode != kModeAbc && _lastbutton == kOkAct) +			_currBtn = kCancelAct; +		_needRefresh = true;  		break;  	case Common::KEYCODE_UP:  		_navigationwithkeys = true;  		if (_lastbutton <= kBtn3Act) -			act = kDelAct; -		else if (_lastbutton == kNextAct || _lastbutton == kAddAct) -			act = ButtonId(_lastbutton - 2); +			_currBtn = kDelAct;  		else if (_lastbutton == kDelAct) -			act = kOkAct; -		else if (_lastbutton == kModeAct) -			act = kBtn9Act; +			_currBtn = kOkAct; +		else if (_lastbutton == kModeAct)  +			_currBtn = kBtn7Act;  		else if (_lastbutton == kBtn0Act) -			act = kBtn7Act; +			_currBtn = kBtn8Act; +		else if (_lastbutton == kNextAct) +			_currBtn = kBtn9Act; +		else if (_lastbutton == kAddAct) +			_currBtn = kModeAct; +		else if (_lastbutton == kCancelAct) +			_currBtn = kBtn0Act; +		else if (_lastbutton == kOkAct) +			_currBtn = kNextAct;  		else -			act = ButtonId(_lastbutton - 3); -		_lastbutton = act; -		//needRefresh = true; +			_currBtn = ButtonId(_lastbutton - 3); +		_needRefresh = true;  		break;  	case Common::KEYCODE_DOWN:  		_navigationwithkeys = true; -		if (_lastbutton == kBtn7Act) -			act = kBtn0Act; -		else if (_lastbutton == kBtn8Act || _lastbutton == kBtn9Act) -			act = ButtonId(_lastbutton + 2); -		else if (_lastbutton == kDelAct) -			act = kBtn1Act; -		else if (_lastbutton == kCancelAct || _lastbutton == kOkAct) -			act = kDelAct; -		else if (_lastbutton == kModeAct || _lastbutton == kBtn0Act) -			act = ButtonId(_lastbutton - 2); +		if (_lastbutton == kDelAct) +			_currBtn = kBtn3Act; +		else if (_lastbutton == kBtn7Act) +			_currBtn = kModeAct; +		else if (_lastbutton == kBtn8Act) +			_currBtn = kBtn0Act; +		else if (_lastbutton == kBtn9Act) +			_currBtn = kNextAct; +		else if (_lastbutton == kModeAct)  +			_currBtn = kAddAct; +		else if (_lastbutton == kBtn0Act) +			_currBtn = kCancelAct; +		else if (_lastbutton == kNextAct) +			_currBtn = kOkAct; +		else if (_lastbutton == kAddAct || _lastbutton == kCancelAct || _lastbutton == kOkAct) +			_currBtn = kDelAct;  		else -			act = ButtonId(_lastbutton + 3); -		_lastbutton = act; -		//needRefresh = true; +			_currBtn = ButtonId(_lastbutton + 3); + +		if (_mode != kModeAbc && _lastbutton == kModeAct) +			_currBtn = kCancelAct; + +		_needRefresh = true;  		break;  	case Common::KEYCODE_KP_ENTER: +	case Common::KEYCODE_RETURN: +		if (state.flags & Common::KBD_CTRL) { +			_currBtn = kOkAct; +			break; +		}  		if (_navigationwithkeys) {  			// when the user has utilized arrow key navigation, -			// interpret enter as 'click' on the act button -			act = _lastbutton; +			// interpret enter as 'click' on the _currBtn button +			_currBtn = _lastbutton; +			_needRefresh = false;  		} else {  			// else it is a shortcut for 'Ok' -			act = kOkAct; +			_currBtn = kOkAct;  		}  		break;  	case Common::KEYCODE_KP_PLUS: -		act = kAddAct; +		_currBtn = kAddAct;  		break;  	case Common::KEYCODE_BACKSPACE:  	case Common::KEYCODE_KP_MINUS: -		act = kDelAct; +		_currBtn = kDelAct;  		break;  	case Common::KEYCODE_KP_DIVIDE: -		act = kNextAct; +		_currBtn = kNextAct;  		break;  	case Common::KEYCODE_KP_MULTIPLY: -		act = kModeAct; +		_currBtn = kModeAct;  		break;  	case Common::KEYCODE_KP0: -		act = kBtn0Act; +		_currBtn = kBtn0Act;  		break;  	case Common::KEYCODE_KP1:  	case Common::KEYCODE_KP2: @@ -297,78 +343,93 @@ void PredictiveDialog::handleKeyDown(Common::KeyState state) {  	case Common::KEYCODE_KP7:  	case Common::KEYCODE_KP8:  	case Common::KEYCODE_KP9: -		act = ButtonId(state.keycode - Common::KEYCODE_KP1); +		_currBtn = ButtonId(state.keycode - Common::KEYCODE_KP1);  		break;  	default:  		Dialog::handleKeyDown(state);  	} -	if (act != kNoAct) { -		processBtnActive(act); +	if (_lastbutton != _currBtn) +		_btns[_lastbutton]->stopAnimatePressedState(); + +	if (_currBtn != kNoAct && !_needRefresh) +		_btns[_currBtn]->setPressedState(); +	else +		updateHighLightedButton(_currBtn); +} + +void PredictiveDialog::updateHighLightedButton(ButtonId act) { +	if (_currBtn != kNoAct) { +		_btns[_lastbutton]->setHighLighted(false); +		_lastbutton = act; +		_btns[_lastbutton]->setHighLighted(true);  	}  }  void PredictiveDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { -	ButtonId act = kNoAct; +	_currBtn = kNoAct;  	_navigationwithkeys = false; +	if (_lastbutton != kNoAct) +		_btns[_lastbutton]->setHighLighted(false); +  	switch (cmd) {  	case kDelCmd: -		act = kDelAct; +		_currBtn = kDelAct;  		break;  	case kNextCmd: -		act = kNextAct; +		_currBtn = kNextAct;  		break;  	case kAddCmd: -		act = kAddAct; +		_currBtn = kAddAct;  		break;  	case kModeCmd: -		act = kModeAct; +		_currBtn = kModeAct;  		break;  	case kBut1Cmd: -		act = kBtn1Act; +		_currBtn = kBtn1Act;  		break;  	case kBut2Cmd: -		act = kBtn2Act; +		_currBtn = kBtn2Act;  		break;  	case kBut3Cmd: -		act = kBtn3Act; +		_currBtn = kBtn3Act;  		break;  	case kBut4Cmd: -		act = kBtn4Act; +		_currBtn = kBtn4Act;  		break;  	case kBut5Cmd: -		act = kBtn5Act; +		_currBtn = kBtn5Act;  		break;  	case kBut6Cmd: -		act = kBtn6Act; +		_currBtn = kBtn6Act;  		break;  	case kBut7Cmd: -		act = kBtn7Act; +		_currBtn = kBtn7Act;  		break;  	case kBut8Cmd: -		act = kBtn8Act; +		_currBtn = kBtn8Act;  		break;  	case kBut9Cmd: -		act = kBtn9Act; +		_currBtn = kBtn9Act;  		break;  	case kBut0Cmd: -		act = kBtn0Act; +		_currBtn = kBtn0Act;  		break;  	case kCancelCmd:  		saveUserDictToFile();  		close();  		return;  	case kOkCmd: -		act = kOkAct; +		_currBtn = kOkAct;  		break;  	default:  		Dialog::handleCommand(sender, cmd, data);  	} -	if (act != kNoAct) { -		processBtnActive(act); +	if (_currBtn != kNoAct) { +		processBtnActive(_currBtn);  	}  } @@ -500,18 +561,18 @@ void PredictiveDialog::processBtnActive(ButtonId button) {  				bringWordtoTop(_unitedDict.dictActLine, _wordNumber);  		} else if (button == kModeAct) { // Mode  			_mode++; -			_addBtn->setEnabled(false); +			_btns[kAddAct]->setEnabled(false);  			if (_mode > kModeAbc) {  				_mode = kModePre;  				// I18N: Pre means 'Predictive', leave '*' as is -				_modebutton->setLabel("*  Pre"); +				_btns[kModeAct]->setLabel("*  Pre");  			} else if (_mode == kModeNum) {  				// I18N: 'Num' means Numbers -				_modebutton->setLabel("*  Num"); +				_btns[kModeAct]->setLabel("*  Num");  			} else {  				// I18N: 'Abc' means Latin alphabet input -				_modebutton->setLabel("*  Abc"); -				_addBtn->setEnabled(true); +				_btns[kModeAct]->setLabel("*  Abc"); +				_btns[kAddAct]->setEnabled(true);  			}  			// truncate current input at mode change @@ -532,18 +593,23 @@ void PredictiveDialog::processBtnActive(ButtonId button) {  	if (button == kOkAct)  		close(); + + 	if (button == kCancelAct) { + 		saveUserDictToFile(); + 		close(); + 	}  }  void PredictiveDialog::handleTickle() { -	// TODO/FIXME: This code does not seem to make any sense. It is only -	// triggered when _lastTime is zero and sets _lastTime to zero again -	// under some condition. This should really be a nop. Probably this -	// code intends to check "_lastTime" instead of "!_lastTime". -	if (!_lastTime) { +	if (_lastTime) {  		if ((_curTime - _lastTime) > kRepeatDelay) {  			_lastTime = 0;  		}  	} + +	if (getTickleWidget()) { +		getTickleWidget()->handleTickle(); +	}  }  void PredictiveDialog::mergeDicts() { @@ -664,7 +730,7 @@ bool PredictiveDialog::matchWord() {  	// The entries in the dictionary consist of a code, a space, and then  	// a space-separated list of words matching this code. -	// To exactly match a code, we therefore match the code plus the trailing +	// To ex_currBtnly match a code, we therefore match the code plus the trailing  	// space in the dictionary.  	Common::String code = _currentCode + " "; diff --git a/gui/predictivedialog.h b/gui/predictivedialog.h index 32de36d5f2..0e3d2967c0 100644 --- a/gui/predictivedialog.h +++ b/gui/predictivedialog.h @@ -68,6 +68,7 @@ public:  	~PredictiveDialog();  	virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); +	virtual void handleKeyUp(Common::KeyState state);  	virtual void handleKeyDown(Common::KeyState state);  	virtual void handleTickle(); @@ -98,6 +99,8 @@ private:  	void saveUserDictToFile();  	void mergeDicts(); + +	void updateHighLightedButton(ButtonId active);  private:  	Dict _unitedDict;  	Dict _predictiveDict; @@ -118,6 +121,7 @@ private:  	uint32 _curTime, _lastTime;  	ButtonId _lastPressBtn; +	ButtonId _currBtn;  	char _temp[kMaxWordLen + 1];  	int _repeatcount[kMaxWordLen]; @@ -128,11 +132,10 @@ private:  	Common::String _search;  	bool _navigationwithkeys; +	bool _needRefresh;  private:  	EditTextWidget *_edittext; -	ButtonWidget   *_modebutton; -	ButtonWidget   *_delbtn; -	ButtonWidget   *_addBtn; +	ButtonWidget   **_btns;  };  } // namespace GUI diff --git a/gui/themes/default.inc b/gui/themes/default.inc index 542f776dbd..5ee9b9202a 100644 --- a/gui/themes/default.inc +++ b/gui/themes/default.inc @@ -460,6 +460,17 @@  "bevel='2' "  "/> "  "</drawdata> " +"<drawdata id='button_pressed' cache='false'> " +"<text font='text_button' " +"text_color='color_alternative_inverted' " +"vertical_align='center' " +"horizontal_align='center' " +"/> " +"<drawstep func='square' " +"fill='foreground' " +"fg_color='green' " +"/> " +"</drawdata> "  "<drawdata id='button_idle' cache='false'> "  "<text font='text_button' "  "text_color='color_button' " diff --git a/gui/themes/scummclassic.zip b/gui/themes/scummclassic.zip Binary files differindex eb5ac2d9f7..ec1728b778 100644 --- a/gui/themes/scummclassic.zip +++ b/gui/themes/scummclassic.zip diff --git a/gui/themes/scummclassic/THEMERC b/gui/themes/scummclassic/THEMERC index b808aee32f..7cbed97e40 100644 --- a/gui/themes/scummclassic/THEMERC +++ b/gui/themes/scummclassic/THEMERC @@ -1 +1 @@ -[SCUMMVM_STX0.8.11:ScummVM Classic Theme:No Author] +[SCUMMVM_STX0.8.12:ScummVM Classic Theme:No Author] diff --git a/gui/themes/scummclassic/classic_gfx.stx b/gui/themes/scummclassic/classic_gfx.stx index f07499ce79..49ecea2e11 100644 --- a/gui/themes/scummclassic/classic_gfx.stx +++ b/gui/themes/scummclassic/classic_gfx.stx @@ -541,6 +541,19 @@  		/>  	</drawdata> +	<!-- Pressed button --> +	<drawdata id = 'button_pressed' cache = 'false'> +		<text	font = 'text_button' +				text_color = 'color_alternative_inverted' +				vertical_align = 'center' +				horizontal_align = 'center' +		/> +		<drawstep	func = 'square' +					fill = 'foreground' +					fg_color = 'green' +		/> +	</drawdata>  +  	<drawdata id = 'button_idle' cache = 'false'>  		<text	font = 'text_button'  				text_color = 'color_button' diff --git a/gui/themes/scummmodern.zip b/gui/themes/scummmodern.zip Binary files differindex 94f7cd8f6e..deae315e30 100644 --- a/gui/themes/scummmodern.zip +++ b/gui/themes/scummmodern.zip diff --git a/gui/themes/scummmodern/THEMERC b/gui/themes/scummmodern/THEMERC index e6c441d543..326993e98d 100644 --- a/gui/themes/scummmodern/THEMERC +++ b/gui/themes/scummmodern/THEMERC @@ -1 +1 @@ -[SCUMMVM_STX0.8.11:ScummVM Modern Theme:No Author] +[SCUMMVM_STX0.8.12:ScummVM Modern Theme:No Author] diff --git a/gui/themes/scummmodern/scummmodern_gfx.stx b/gui/themes/scummmodern/scummmodern_gfx.stx index 403f636fd0..970d78a5ae 100644 --- a/gui/themes/scummmodern/scummmodern_gfx.stx +++ b/gui/themes/scummmodern/scummmodern_gfx.stx @@ -740,6 +740,27 @@  		/>  	</drawdata> +	<!-- Pressed button --> +	<drawdata id = 'button_pressed' cache = 'false'> +		<text	font = 'text_button' +				text_color = 'color_button' +				vertical_align = 'center' +				horizontal_align = 'center' +		/> +		<drawstep	func = 'roundedsq' +					radius = '5' +					stroke = '1' +					fill = 'foreground' +					shadow = '0' +					factor = '0' +					fg_color = '120, 40, 16' +					gradient_start = '255, 0, 0' +					gradient_end = '255, 0, 0' +					bevel = '1' +					bevel_color = 'black' +		/> +	</drawdata>  +  	<!-- Idle button -->  	<drawdata id = 'button_idle' cache = 'false'>  		<text	font = 'text_button' diff --git a/gui/widget.cpp b/gui/widget.cpp index 0e2fd248b1..6ae4e5cee5 100644 --- a/gui/widget.cpp +++ b/gui/widget.cpp @@ -30,6 +30,8 @@  #include "gui/ThemeEval.h" +#include "gui/dialog.h" +  namespace GUI {  Widget::Widget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip) @@ -77,6 +79,8 @@ void Widget::updateState(int oldFlags, int newFlags) {  		_state = ThemeEngine::kStateEnabled;  		if (newFlags & WIDGET_HILITED)  			_state = ThemeEngine::kStateHighlight; +		if (newFlags & WIDGET_PRESSED) +			_state = ThemeEngine::kStatePressed;  	} else {  		_state = ThemeEngine::kStateDisabled;  	} @@ -272,27 +276,33 @@ void StaticTextWidget::drawWidget() {  ButtonWidget::ButtonWidget(GuiObject *boss, int x, int y, int w, int h, const Common::String &label, const char *tooltip, uint32 cmd, uint8 hotkey)  	: StaticTextWidget(boss, x, y, w, h, cleanupHotkey(label), Graphics::kTextAlignCenter, tooltip), CommandSender(boss), -	  _cmd(cmd), _hotkey(hotkey) { +	  _cmd(cmd), _hotkey(hotkey), _lastTime(0) {  	if (hotkey == 0)  		_hotkey = parseHotkey(label); -	setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG); +	setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG | WIDGET_WANT_TICKLE);  	_type = kButtonWidget;  }  ButtonWidget::ButtonWidget(GuiObject *boss, const Common::String &name, const Common::String &label, const char *tooltip, uint32 cmd, uint8 hotkey)  	: StaticTextWidget(boss, name, cleanupHotkey(label), tooltip), CommandSender(boss), -	  _cmd(cmd) { +	  _cmd(cmd), _lastTime(0) {  	if (hotkey == 0)  		_hotkey = parseHotkey(label); -	setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG); +	setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG | WIDGET_WANT_TICKLE);  	_type = kButtonWidget;  }  void ButtonWidget::handleMouseUp(int x, int y, int button, int clickCount) { -	if (isEnabled() && x >= 0 && x < _w && y >= 0 && y < _h) +	if (isEnabled() && x >= 0 && x < _w && y >= 0 && y < _h) {  		sendCommand(_cmd, 0); +		startAnimatePressedState(); +	} +} + +void ButtonWidget::handleMouseDown(int x, int y, int button, int clickCount) { +	setPressedState();  }  void ButtonWidget::drawWidget() { @@ -324,6 +334,44 @@ ButtonWidget *addClearButton(GuiObject *boss, const Common::String &name, uint32  	return button;  } +void ButtonWidget::setHighLighted(bool enable) { +	(enable) ? setFlags(WIDGET_HILITED) : clearFlags(WIDGET_HILITED); +	draw(); +} + +void ButtonWidget::handleTickle() { +	if (_lastTime) { +		uint32 curTime = g_system->getMillis(); +		if (curTime - _lastTime > kPressedButtonTime) { +			stopAnimatePressedState(); +		} +	} +} + +void ButtonWidget::setPressedState() { +	wantTickle(true); +	setFlags(WIDGET_PRESSED); +	draw(); +} + +void ButtonWidget::stopAnimatePressedState() { +	wantTickle(false); +	_lastTime = 0; +	clearFlags(WIDGET_PRESSED); +	draw(); +} + +void ButtonWidget::startAnimatePressedState() { +	_lastTime = g_system->getMillis(); +} + +void ButtonWidget::wantTickle(bool tickled) { +	if (tickled)  +		((GUI::Dialog *)_boss)->setTickleWidget(this); +	else +		((GUI::Dialog *)_boss)->unSetTickleWidget(); +} +  #pragma mark -  PicButtonWidget::PicButtonWidget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip, uint32 cmd, uint8 hotkey) diff --git a/gui/widget.h b/gui/widget.h index 789fc09231..6a6c67ced9 100644 --- a/gui/widget.h +++ b/gui/widget.h @@ -38,6 +38,7 @@ enum {  	WIDGET_INVISIBLE	= 1 <<  1,  	WIDGET_HILITED		= 1 <<  2,  	WIDGET_BORDER		= 1 <<  3, +	WIDGET_PRESSED		= 1 <<	4,  	//WIDGET_INV_BORDER	= 1 <<  4,  	WIDGET_CLEARBG		= 1 <<  5,  	WIDGET_WANT_TICKLE	= 1 <<  7, @@ -73,6 +74,10 @@ enum {  	kCaretBlinkTime = 300  }; +enum { +	kPressedButtonTime = 200 +}; +  /* Widget */  class Widget : public GuiObject {  	friend class Dialog; @@ -189,11 +194,22 @@ public:  	void setLabel(const Common::String &label);  	void handleMouseUp(int x, int y, int button, int clickCount); +	void handleMouseDown(int x, int y, int button, int clickCount);  	void handleMouseEntered(int button)	{ setFlags(WIDGET_HILITED); draw(); } -	void handleMouseLeft(int button)	{ clearFlags(WIDGET_HILITED); draw(); } +	void handleMouseLeft(int button)	{ clearFlags(WIDGET_HILITED | WIDGET_PRESSED); draw(); } +	void handleTickle(); + +	void setHighLighted(bool enable); +	void setPressedState(); +	void startAnimatePressedState(); +	void stopAnimatePressedState(); +	void lostFocusWidget() { stopAnimatePressedState(); }  protected:  	void drawWidget(); +	void wantTickle(bool tickled); +private: +	uint32 _lastTime;  };  /* PicButtonWidget */  | 
