diff options
Diffstat (limited to 'engines/composer/composer.cpp')
| -rw-r--r-- | engines/composer/composer.cpp | 515 | 
1 files changed, 515 insertions, 0 deletions
diff --git a/engines/composer/composer.cpp b/engines/composer/composer.cpp new file mode 100644 index 0000000000..d9bb0cdc54 --- /dev/null +++ b/engines/composer/composer.cpp @@ -0,0 +1,515 @@ +/* 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/scummsys.h" +  +#include "common/config-manager.h" +#include "common/events.h" +#include "common/file.h" +#include "common/random.h" +#include "common/fs.h" +#include "common/keyboard.h" +#include "common/substream.h" + +#include "graphics/cursorman.h" +#include "graphics/surface.h" +#include "graphics/pixelformat.h" + +#include "engines/util.h" +#include "engines/advancedDetector.h" + +#include "audio/audiostream.h" + +#include "composer/composer.h" +#include "composer/graphics.h" +#include "composer/resource.h" + +namespace Composer { + +ComposerEngine::ComposerEngine(OSystem *syst, const ComposerGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) { +	_rnd = new Common::RandomSource("composer"); +	_audioStream = NULL; +} + +ComposerEngine::~ComposerEngine() { +	DebugMan.clearAllDebugChannels(); + +	for (Common::List<Library>::iterator i = _libraries.begin(); i != _libraries.end(); i++) +		delete i->_archive; +	for (Common::List<Sprite>::iterator i = _sprites.begin(); i != _sprites.end(); i++) +		i->_surface.free(); + +	delete _rnd; +} + +Common::Error ComposerEngine::run() { +	Common::Event event; + +	_vars.resize(1000); +	for (uint i = 0; i < _vars.size(); i++) +		_vars[i] = 0; + +	_queuedScripts.resize(10); +	for (uint i = 0; i < _queuedScripts.size(); i++) { +		_queuedScripts[i]._count = 0; +		_queuedScripts[i]._scriptId = 0; +	} + +	_mouseVisible = true; +	_mouseEnabled = false; +	_mouseSpriteId = 0; +	_lastButton = NULL; + +	_directoriesToStrip = 1; +	if (!_bookIni.loadFromFile("book.ini")) { +		_directoriesToStrip = 0; +		if (!_bookIni.loadFromFile("programs/book.ini")) +			error("failed to find book.ini"); +	} + +	uint width = 640; +	if (_bookIni.hasKey("Width", "Common")) +		width = atoi(getStringFromConfig("Common", "Width").c_str()); +	uint height = 480; +	if (_bookIni.hasKey("Height", "Common")) +		height = atoi(getStringFromConfig("Common", "Height").c_str()); +	initGraphics(width, height, true); +	_surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8()); +	_needsUpdate = true; + +	loadLibrary(0); + +	uint fps = atoi(getStringFromConfig("Common", "FPS").c_str()); +	uint frameTime = 1000 / fps; +	uint32 lastDrawTime = 0; +	while (!shouldQuit()) { +		for (uint i = 0; i < _pendingPageChanges.size(); i++) { +			if (_pendingPageChanges[i]._remove) +				unloadLibrary(_pendingPageChanges[i]._pageId); +			else +				loadLibrary(_pendingPageChanges[i]._pageId); + +			lastDrawTime = _system->getMillis(); +		} +		_pendingPageChanges.clear(); + +		uint32 thisTime = _system->getMillis(); +		for (uint i = 0; i < _queuedScripts.size(); i++) { +			QueuedScript &script = _queuedScripts[i]; +			if (!script._count) +				continue; +			if (script._baseTime + script._duration > thisTime) +				continue; +			if (script._count != 0xffffffff) +				script._count--; +			script._baseTime = thisTime; +			runScript(script._scriptId, i, 0, 0); +		} + +		if (lastDrawTime + frameTime <= thisTime) { +			// catch up if we're more than 2 frames behind +			if (lastDrawTime + (frameTime * 2) <= thisTime) +				lastDrawTime = thisTime; +			else +				lastDrawTime += frameTime; + +			redraw(); + +			processAnimFrame(); +		} else if (_needsUpdate) { +			redraw(); +		} + +		while (_eventMan->pollEvent(event)) { +			switch (event.type) { +			case Common::EVENT_LBUTTONDOWN: +				onMouseDown(event.mouse); +				break; + +			case Common::EVENT_LBUTTONUP: +				break; + +			case Common::EVENT_RBUTTONDOWN: +				break; + +			case Common::EVENT_MOUSEMOVE: +				onMouseMove(event.mouse); +				break; + +			case Common::EVENT_KEYDOWN: +				switch (event.kbd.keycode) { +				case Common::KEYCODE_d: +					/*if (event.kbd.hasFlags(Common::KBD_CTRL)) { +						// Start the debugger +						getDebugger()->attach(); +						getDebugger()->onFrame(); +					}*/ +					break; + +				case Common::KEYCODE_q: +					if (event.kbd.hasFlags(Common::KBD_CTRL)) +						quitGame(); +					break; + +				default: +					break; +				} + +				onKeyDown(event.kbd.keycode); +				break; + +			case Common::EVENT_QUIT: +			case Common::EVENT_RTL: +				quitGame(); +				break; + +			default: +				break; +			} +		} + +		_system->delayMillis(20); +	} + +	return Common::kNoError; +} + +void ComposerEngine::onMouseDown(const Common::Point &pos) { +	if (!_mouseEnabled || !_mouseVisible) +		return; + +	const Sprite *sprite = getSpriteAtPos(pos); +	const Button *button = getButtonFor(sprite, pos); +	if (!button) +		return; + +	// TODO: other buttons? +	uint16 buttonsDown = 1; // MK_LBUTTON + +	uint16 spriteId = sprite ? sprite->_id : 0; +	runScript(button->_scriptId, button->_id, buttonsDown, spriteId); +} + +void ComposerEngine::onMouseMove(const Common::Point &pos) { +	_lastMousePos = pos; + +	if (!_mouseEnabled || !_mouseVisible) +		return; + +	// TODO: do we need to keep track of this? +	uint buttonsDown = 0; + +	const Sprite *sprite = getSpriteAtPos(pos); +	const Button *button = getButtonFor(sprite, pos); +	if (_lastButton != button) { +		if (_lastButton && _lastButton->_scriptIdRollOff) +			runScript(_lastButton->_scriptIdRollOff, _lastButton->_id, buttonsDown, 0); +		_lastButton = button; +		if (_lastButton && _lastButton->_scriptIdRollOn) +			runScript(_lastButton->_scriptIdRollOn, _lastButton->_id, buttonsDown, 0); +	} + +	if (_mouseSpriteId) { +		addSprite(_mouseSpriteId, 0, 0, _lastMousePos - _mouseOffset); +		_needsUpdate = true; +	} +} + +void ComposerEngine::onKeyDown(uint16 keyCode) { +	runEvent(kEventKeyDown, keyCode, 0, 0); +	runEvent(kEventChar, keyCode, 0, 0); +} + +void ComposerEngine::setCursor(uint16 id, const Common::Point &offset) { +	_mouseOffset = offset; +	if (_mouseSpriteId == id) +		return; + +	if (_mouseSpriteId && _mouseVisible) { +		removeSprite(_mouseSpriteId, 0); +	} +	_mouseSpriteId = id; +	if (_mouseSpriteId && _mouseVisible) { +		addSprite(_mouseSpriteId, 0, 0, _lastMousePos - _mouseOffset); +	} +} + +void ComposerEngine::setCursorVisible(bool visible) { +	if (visible && !_mouseVisible) { +		_mouseVisible = true; +		if (_mouseSpriteId) +			addSprite(_mouseSpriteId, 0, 0, _lastMousePos - _mouseOffset); +		onMouseMove(_lastMousePos); +	} else if (!visible && _mouseVisible) { +		_mouseVisible = false; +		if (_mouseSpriteId) +			removeSprite(_mouseSpriteId, 0); +	} +} + +Common::String ComposerEngine::getStringFromConfig(const Common::String §ion, const Common::String &key) { +	Common::String value; +	if (!_bookIni.getKey(key, section, value)) +		error("failed to find key '%s' in section '%s' of book config", key.c_str(), section.c_str()); +	return value; +} + +Common::String ComposerEngine::getFilename(const Common::String §ion, uint id) { +	Common::String key = Common::String::format("%d", id); +	Common::String filename = getStringFromConfig(section, key); + +	return mangleFilename(filename); +} + +Common::String ComposerEngine::mangleFilename(Common::String filename) { +	while (filename.size() && (filename[0] == '~' || filename[0] == ':' || filename[0] == '\\')) +		filename = filename.c_str() + 1; + +	uint slashesToStrip = _directoriesToStrip; +	while (slashesToStrip--) { +		for (uint i = 0; i < filename.size(); i++) { +			if (filename[i] != '\\') +				continue; +			filename = filename.c_str() + i + 1; +			break; +		} +	} + +	Common::String outFilename; +	for (uint i = 0; i < filename.size(); i++) { +		if (filename[i] == '\\') +			outFilename += '/'; +		else +			outFilename += filename[i]; +	} +	return outFilename; +} + +void ComposerEngine::loadLibrary(uint id) { +	if (!id) +		id = atoi(getStringFromConfig("Common", "StartUp").c_str()); +	Common::String filename = getFilename("Libs", id); + +	Library library; + +	library._id = id; +	library._archive = new ComposerArchive(); +	if (!library._archive->openFile(filename)) +		error("failed to open '%s'", filename.c_str()); +	_libraries.push_front(library); + +	Library &newLib = _libraries.front(); + +	Common::Array<uint16> buttonResources = library._archive->getResourceIDList(ID_BUTN); +	for (uint i = 0; i < buttonResources.size(); i++) { +		uint16 buttonId = buttonResources[i]; +		Common::SeekableReadStream *stream = library._archive->getResource(ID_BUTN, buttonId); +		Button button(stream, buttonId, getGameType()); + +		bool inserted = false; +		for (Common::List<Button>::iterator b = newLib._buttons.begin(); b != newLib._buttons.end(); b++) { +			if (button._zorder < b->_zorder) +				continue; +			newLib._buttons.insert(b, button); +			inserted = true; +			break; +		} +		if (!inserted) +			newLib._buttons.push_back(button); +	} + +	// add background sprite, if it exists +	if (hasResource(ID_BMAP, 1000)) +		setBackground(1000); + +	// TODO: better CTBL logic +	loadCTBL(1000, 100); + +	// Run the startup script. +	runScript(1000, 0, 0, 0); + +	_mouseEnabled = true; +	onMouseMove(_lastMousePos); + +	runEvent(kEventLoad, id, 0, 0); +} + +void ComposerEngine::unloadLibrary(uint id) { +	for (Common::List<Library>::iterator i = _libraries.begin(); i != _libraries.end(); i++) { +		if (i->_id != id) +			continue; + +		for (Common::List<Animation *>::iterator j = _anims.begin(); j != _anims.end(); j++) { +			delete *j; +		} +		_anims.clear(); +		for (Common::List<Pipe *>::iterator j = _pipes.begin(); j != _pipes.end(); j++) { +			delete *j; +		} +		_pipes.clear(); + +		for (Common::List<Sprite>::iterator j = _sprites.begin(); j != _sprites.end(); j++) { +			j->_surface.free(); +		} +		_sprites.clear(); +		i->_buttons.clear(); + +		_lastButton = NULL; + +		_mixer->stopAll(); +		_audioStream = NULL; + +		for (uint j = 0; j < _queuedScripts.size(); j++) { +			_queuedScripts[j]._count = 0; +			_queuedScripts[j]._scriptId = 0; +		} + +		delete i->_archive; +		_libraries.erase(i); + +		runEvent(kEventUnload, id, 0, 0); + +		return; +	} + +	error("tried to unload library %d, which isn't loaded", id); +} + +bool ComposerEngine::hasResource(uint32 tag, uint16 id) { +	for (Common::List<Library>::iterator i = _libraries.begin(); i != _libraries.end(); i++) +		if (i->_archive->hasResource(tag, id)) +			return true; + +	return false; +} + +Common::SeekableReadStream *ComposerEngine::getResource(uint32 tag, uint16 id) { +	for (Common::List<Library>::iterator i = _libraries.begin(); i != _libraries.end(); i++) +		if (i->_archive->hasResource(tag, id)) +			return i->_archive->getResource(tag, id); + +	error("No loaded library contains '%s' %04x", tag2str(tag), id); +} + +Button::Button(Common::SeekableReadStream *stream, uint16 id, uint gameType) { +	_id = id; + +	_type = stream->readUint16LE(); +	_active = (_type & 0x8000) ? true : false; +	_type &= 0xfff; +	debug(9, "button %d: type %d, active %d", id, _type, _active); + +	uint16 flags = 0; +	uint16 size = 4; +	if (gameType == GType_ComposerV1) { +		flags = stream->readUint16LE(); +		_zorder = 0; +		_scriptId = stream->readUint16LE(); +		_scriptIdRollOn = 0; +		_scriptIdRollOff = 0; +	} else { +		_zorder = stream->readUint16LE(); +		_scriptId = stream->readUint16LE(); +		_scriptIdRollOn = stream->readUint16LE(); +		_scriptIdRollOff = stream->readUint16LE(); + +		stream->skip(4); + +		size = stream->readUint16LE(); +	} + +	switch (_type) { +	case kButtonRect: +	case kButtonEllipse: +		if (size != 4) +			error("button %d of type %d had %d points, not 4", id, _type, size); +		_rect.left = stream->readSint16LE(); +		_rect.top = stream->readSint16LE(); +		_rect.right = stream->readSint16LE(); +		_rect.bottom = stream->readSint16LE(); +		break; +	case kButtonSprites: +		if (gameType == GType_ComposerV1) +			error("encountered kButtonSprites in V1 data"); +		for (uint i = 0; i < size; i++) { +			_spriteIds.push_back(stream->readUint16LE()); +		} +		break; +	default: +		error("unknown button type %d", _type); +	} + +	if (flags & 0x40) { +		_scriptIdRollOn = stream->readUint16LE(); +		_scriptIdRollOff = stream->readUint16LE(); +	} + +	delete stream; +} + +bool Button::contains(const Common::Point &pos) const { +	switch (_type) { +	case kButtonRect: +		return _rect.contains(pos); +	case kButtonEllipse: +		if (!_rect.contains(pos)) +			return false; +		{ +		int16 a = _rect.width() / 2; +		int16 b = _rect.height() / 2; +		if (!a || !b) +			return false; +		Common::Point adjustedPos = pos - Common::Point(_rect.left + a, _rect.top + b); +		return ((adjustedPos.x*adjustedPos.x)/(a*a) + (adjustedPos.y*adjustedPos.y)/(b*b) < 1); +		} +	case kButtonSprites: +		return false; +	default: +		error("internal error (button type %d)", _type); +	} +} + +const Button *ComposerEngine::getButtonFor(const Sprite *sprite, const Common::Point &pos) { +	for (Common::List<Library>::iterator l = _libraries.begin(); l != _libraries.end(); l++) { +		for (Common::List<Button>::iterator i = l->_buttons.reverse_begin(); i != l->_buttons.end(); --i) { +			if (!i->_active) +				continue; + +			if (i->_spriteIds.empty()) { +				if (i->contains(pos)) +					return &(*i); +				continue; +			} + +			if (!sprite) +				continue; + +			for (uint j = 0; j < i->_spriteIds.size(); j++) { +				if (i->_spriteIds[j] == sprite->_id) +					return &(*i); +			} +		} +	} + +	return NULL; +} + +} // End of namespace Composer  | 
