diff options
63 files changed, 7392 insertions, 249 deletions
| diff --git a/engines/gob/anifile.cpp b/engines/gob/anifile.cpp index 2671fe0405..085ac800cd 100644 --- a/engines/gob/anifile.cpp +++ b/engines/gob/anifile.cpp @@ -37,30 +37,38 @@ ANIFile::ANIFile(GobEngine *vm, const Common::String &fileName,                   uint16 width, uint8 bpp) : _vm(vm),  	_width(width), _bpp(bpp), _hasPadding(false) { -	Common::SeekableReadStream *ani = _vm->_dataIO->getFile(fileName); -	if (ani) { -		Common::SeekableSubReadStreamEndian sub(ani, 0, ani->size(), false, DisposeAfterUse::YES); +	bool bigEndian = false; +	Common::String endianFileName = fileName; -		load(sub, fileName); -		return; -	} +	if ((_vm->getEndiannessMethod() == kEndiannessMethodAltFile) && +	    !_vm->_dataIO->hasFile(fileName)) { +		// If the game has alternate big-endian files, look if one exist -	// File doesn't exist, try to open the big-endian'd alternate file -	Common::String alternateFileName = fileName; -	alternateFileName.setChar('_', 0); +		Common::String alternateFileName = fileName; +		alternateFileName.setChar('_', 0); -	ani = _vm->_dataIO->getFile(alternateFileName); +		if (_vm->_dataIO->hasFile(alternateFileName)) { +			bigEndian      = true; +			endianFileName = alternateFileName; +		} +	} else if ((_vm->getEndiannessMethod() == kEndiannessMethodBE) || +	           ((_vm->getEndiannessMethod() == kEndiannessMethodSystem) && +	            (_vm->getEndianness() == kEndiannessBE))) +		// Game always little endian or it follows the system and it is big endian +		bigEndian = true; + +	Common::SeekableReadStream *ani = _vm->_dataIO->getFile(endianFileName);  	if (ani) { -		Common::SeekableSubReadStreamEndian sub(ani, 0, ani->size(), true, DisposeAfterUse::YES); +		Common::SeekableSubReadStreamEndian sub(ani, 0, ani->size(), bigEndian, DisposeAfterUse::YES);  		// The big endian version pads a few fields to even size -		_hasPadding = true; +		_hasPadding = bigEndian;  		load(sub, fileName);  		return;  	} -	warning("ANIFile::ANIFile(): No such file \"%s\"", fileName.c_str()); +	warning("ANIFile::ANIFile(): No such file \"%s\" (\"%s\")", endianFileName.c_str(), fileName.c_str());  }  ANIFile::~ANIFile() { @@ -281,4 +289,9 @@ void ANIFile::drawLayer(Surface &dest, uint16 layer, uint16 part,  	_layers[layer]->draw(dest, part, x, y, transp);  } +void ANIFile::recolor(uint8 from, uint8 to) { +	for (LayerArray::iterator l = _layers.begin(); l != _layers.end(); ++l) +		(*l)->recolor(from, to); +} +  } // End of namespace Gob diff --git a/engines/gob/anifile.h b/engines/gob/anifile.h index b6d9c735b5..c930aafc6b 100644 --- a/engines/gob/anifile.h +++ b/engines/gob/anifile.h @@ -92,6 +92,9 @@ public:  	/** Draw an animation frame. */  	void draw(Surface &dest, uint16 animation, uint16 frame, int16 x, int16 y) const; +	/** Recolor the animation sprites. */ +	void recolor(uint8 from, uint8 to); +  private:  	typedef Common::Array<CMPFile *> LayerArray;  	typedef Common::Array<Animation> AnimationArray; diff --git a/engines/gob/aniobject.cpp b/engines/gob/aniobject.cpp index 8d739fb3a4..7e3668a0ce 100644 --- a/engines/gob/aniobject.cpp +++ b/engines/gob/aniobject.cpp @@ -28,23 +28,20 @@  namespace Gob {  ANIObject::ANIObject(const ANIFile &ani) : _ani(&ani), _cmp(0), -	_visible(false), _paused(false), _mode(kModeContinuous), -	_x(0), _y(0), _background(0), _drawn(false) { +	_visible(false), _paused(false), _mode(kModeContinuous), _x(0), _y(0) {  	setAnimation(0);  	setPosition();  }  ANIObject::ANIObject(const CMPFile &cmp) : _ani(0), _cmp(&cmp), -	_visible(false), _paused(false), _mode(kModeContinuous), -	_x(0), _y(0), _background(0), _drawn(false) { +	_visible(false), _paused(false), _mode(kModeContinuous), _x(0), _y(0) {  	setAnimation(0);  	setPosition();  }  ANIObject::~ANIObject() { -	delete _background;  }  void ANIObject::setVisible(bool visible) { @@ -104,7 +101,7 @@ void ANIObject::getPosition(int16 &x, int16 &y) const {  	y = _y;  } -void ANIObject::getFramePosition(int16 &x, int16 &y) const { +void ANIObject::getFramePosition(int16 &x, int16 &y, uint16 n) const {  	// CMP "animations" have no specific frame positions  	if (_cmp) {  		getPosition(x, y); @@ -118,11 +115,24 @@ void ANIObject::getFramePosition(int16 &x, int16 &y) const {  	if (_frame >= animation.frameCount)  		return; -	x = _x + animation.frameAreas[_frame].left; -	y = _y + animation.frameAreas[_frame].top; +	// If we're paused, we don't advance any frames +	if (_paused) +		n = 0; + +	// Number of cycles run through after n frames +	uint16 cycles = (_frame + n) / animation.frameCount; +	// Frame position after n frames +	uint16 frame  = (_frame + n) % animation.frameCount; + +	// Only doing one cycle? +	if (_mode == kModeOnce) +		cycles = MAX<uint16>(cycles, 1); + +	x = _x + animation.frameAreas[frame].left + cycles * animation.deltaX; +	y = _y + animation.frameAreas[frame].top  + cycles * animation.deltaY;  } -void ANIObject::getFrameSize(int16 &width, int16 &height) const { +void ANIObject::getFrameSize(int16 &width, int16 &height, uint16 n) const {  	if (_cmp) {  		width  = _cmp->getWidth (_animation);  		height = _cmp->getHeight(_animation); @@ -137,8 +147,15 @@ void ANIObject::getFrameSize(int16 &width, int16 &height) const {  	if (_frame >= animation.frameCount)  		return; -	width  = animation.frameAreas[_frame].right  - animation.frameAreas[_frame].left + 1; -	height = animation.frameAreas[_frame].bottom - animation.frameAreas[_frame].top  + 1; +	// If we're paused, we don't advance any frames +	if (_paused) +		n = 0; + +	// Frame position after n frames +	uint16 frame = (_frame + n) % animation.frameCount; + +	width  = animation.frameAreas[frame].right  - animation.frameAreas[frame].left + 1; +	height = animation.frameAreas[frame].bottom - animation.frameAreas[frame].top  + 1;  }  bool ANIObject::isIn(int16 x, int16 y) const { @@ -188,46 +205,36 @@ bool ANIObject::draw(Surface &dest, int16 &left, int16 &top,  bool ANIObject::drawCMP(Surface &dest, int16 &left, int16 &top,                                         int16 &right, int16 &bottom) { -	if (!_background) { +	if (!hasBuffer()) {  		uint16 width, height;  		_cmp->getMaxSize(width, height); -		_background = new Surface(width, height, dest.getBPP()); +		resizeBuffer(width, height);  	} -	const uint16 cR = _cmp->getWidth (_animation) - 1; -	const uint16 cB = _cmp->getHeight(_animation) - 1; - -	_backgroundLeft   = CLIP<int16>(   + _x, 0, dest.getWidth () - 1); -	_backgroundTop    = CLIP<int16>(   + _y, 0, dest.getHeight() - 1); -	_backgroundRight  = CLIP<int16>(cR + _x, 0, dest.getWidth () - 1); -	_backgroundBottom = CLIP<int16>(cB + _y, 0, dest.getHeight() - 1); +	left   = _x; +	top    = _y; +	right  = _x + _cmp->getWidth (_animation) - 1; +	bottom = _y + _cmp->getHeight(_animation) - 1; -	_background->blit(dest, _backgroundLeft , _backgroundTop, -	                        _backgroundRight, _backgroundBottom, 0, 0); +	if (!saveScreen(dest, left, top, right, bottom)) +		return false;  	_cmp->draw(dest, _animation, _x, _y, 0); -	_drawn = true; - -	left   = _backgroundLeft; -	top    = _backgroundTop; -	right  = _backgroundRight; -	bottom = _backgroundBottom; -  	return true;  }  bool ANIObject::drawANI(Surface &dest, int16 &left, int16 &top,                                         int16 &right, int16 &bottom) { -	if (!_background) { +	if (!hasBuffer()) {  		uint16 width, height;  		_ani->getMaxSize(width, height); -		_background = new Surface(width, height, dest.getBPP()); +		resizeBuffer(width, height);  	}  	const ANIFile::Animation &animation = _ani->getAnimationInfo(_animation); @@ -236,45 +243,23 @@ bool ANIObject::drawANI(Surface &dest, int16 &left, int16 &top,  	const ANIFile::FrameArea &area = animation.frameAreas[_frame]; -	_backgroundLeft   = CLIP<int16>(area.left   + _x, 0, dest.getWidth () - 1); -	_backgroundTop    = CLIP<int16>(area.top    + _y, 0, dest.getHeight() - 1); -	_backgroundRight  = CLIP<int16>(area.right  + _x, 0, dest.getWidth () - 1); -	_backgroundBottom = CLIP<int16>(area.bottom + _y, 0, dest.getHeight() - 1); +	left   = _x + area.left; +	top    = _y + area.top; +	right  = _x + area.right; +	bottom = _y + area.bottom; -	_background->blit(dest, _backgroundLeft , _backgroundTop, -	                        _backgroundRight, _backgroundBottom, 0, 0); +	if (!saveScreen(dest, left, top, right, bottom)) +		return false;  	_ani->draw(dest, _animation, _frame, _x, _y); -	_drawn = true; - -	left   = _backgroundLeft; -	top    = _backgroundTop; -	right  = _backgroundRight; -	bottom = _backgroundBottom; -  	return true;  }  bool ANIObject::clear(Surface &dest, int16 &left, int16 &top,                                       int16 &right, int16 &bottom) { -	if (!_drawn) -		return false; - -	const int16 bgRight  = _backgroundRight  - _backgroundLeft; -	const int16 bgBottom = _backgroundBottom - _backgroundTop; - -	dest.blit(*_background, 0, 0, bgRight, bgBottom, _backgroundLeft, _backgroundTop); - -	_drawn = false; - -	left   = _backgroundLeft; -	top    = _backgroundTop; -	right  = _backgroundRight; -	bottom = _backgroundBottom; - -	return true; +	return restoreScreen(dest, left, top, right, bottom);  }  void ANIObject::advance() { diff --git a/engines/gob/aniobject.h b/engines/gob/aniobject.h index 00f42b43ce..d8c8edc2b8 100644 --- a/engines/gob/aniobject.h +++ b/engines/gob/aniobject.h @@ -25,6 +25,8 @@  #include "common/system.h" +#include "gob/backbuffer.h" +  namespace Gob {  class ANIFile; @@ -32,7 +34,7 @@ class CMPFile;  class Surface;  /** An ANI object, controlling an animation within an ANI file. */ -class ANIObject { +class ANIObject : public BackBuffer {  public:  	enum Mode {  		kModeContinuous, ///< Play the animation continuously. @@ -68,10 +70,10 @@ public:  	/** Return the current position. */  	void getPosition(int16 &x, int16 &y) const; -	/** Return the current frame position. */ -	void getFramePosition(int16 &x, int16 &y) const; -	/** Return the current frame size. */ -	void getFrameSize(int16 &width, int16 &height) const; +	/** Return the frame position after another n frames. */ +	void getFramePosition(int16 &x, int16 &y, uint16 n = 0) const; +	/** Return the current frame size after another n frames. */ +	void getFrameSize(int16 &width, int16 &height, uint16 n = 0) const;  	/** Are there coordinates within the animation sprite? */  	bool isIn(int16 x, int16 y) const; @@ -118,13 +120,6 @@ private:  	int16 _x; ///< The current X position.  	int16 _y; ///< The current Y position. -	Surface *_background; ///< The saved background. -	bool _drawn;          ///< Was the animation drawn? - -	int16 _backgroundLeft;   ///< The left position of the saved background. -	int16 _backgroundTop;    ///< The top of the saved background. -	int16 _backgroundRight;  ///< The right position of the saved background. -	int16 _backgroundBottom; ///< The bottom position of the saved background.  	bool drawCMP(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom);  	bool drawANI(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); diff --git a/engines/gob/backbuffer.cpp b/engines/gob/backbuffer.cpp new file mode 100644 index 0000000000..752042d46e --- /dev/null +++ b/engines/gob/backbuffer.cpp @@ -0,0 +1,100 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/util.h" + +#include "gob/backbuffer.h" +#include "gob/surface.h" + +namespace Gob { + +BackBuffer::BackBuffer() : _background(0), _saved(false) { +} + +BackBuffer::~BackBuffer() { +	delete _background; +} + +bool BackBuffer::hasBuffer() const { +	return _background != 0; +} + +bool BackBuffer::hasSavedBackground() const { +	return _saved; +} + +void BackBuffer::trashBuffer() { +	_saved = false; +} + +void BackBuffer::resizeBuffer(uint16 width, uint16 height) { +	trashBuffer(); + +	if (_background && (_background->getWidth() == width) && (_background->getHeight() == height)) +		return; + +	delete _background; + +	_background = new Surface(width, height, 1); +} + +bool BackBuffer::saveScreen(const Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { +	if (!_background) +		return false; + +	const int16 width  = MIN<int16>(right  - left + 1, _background->getWidth ()); +	const int16 height = MIN<int16>(bottom - top  + 1, _background->getHeight()); +	if ((width <= 0) || (height <= 0)) +		return false; + +	right  = left + width  - 1; +	bottom = top  + height - 1; + +	_saveLeft   = left; +	_saveTop    = top; +	_saveRight  = right; +	_saveBottom = bottom; + +	_background->blit(dest, _saveLeft, _saveTop, _saveRight, _saveBottom, 0, 0); + +	_saved = true; + +	return true; +} + +bool BackBuffer::restoreScreen(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { +	if (!_saved) +		return false; + +	left   = _saveLeft; +	top    = _saveTop; +	right  = _saveRight; +	bottom = _saveBottom; + +	dest.blit(*_background, 0, 0, right - left, bottom - top, left, top); + +	_saved = false; + +	return true; +} + +} // End of namespace Gob diff --git a/engines/gob/backbuffer.h b/engines/gob/backbuffer.h new file mode 100644 index 0000000000..c978689e9f --- /dev/null +++ b/engines/gob/backbuffer.h @@ -0,0 +1,59 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GOB_BACKBUFFER_H +#define GOB_BACKBUFFER_H + +#include "common/system.h" + +namespace Gob { + +class Surface; + +class BackBuffer { +public: +	BackBuffer(); +	~BackBuffer(); + +protected: +	void trashBuffer(); +	void resizeBuffer(uint16 width, uint16 height); + +	bool saveScreen   (const Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); +	bool restoreScreen(      Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); + +	bool hasBuffer() const; +	bool hasSavedBackground() const; + +private: +	Surface *_background; ///< The saved background. +	bool _saved;          ///< Was the background saved? + +	int16 _saveLeft;   ///< The left position of the saved background. +	int16 _saveTop;    ///< The top of the saved background. +	int16 _saveRight;  ///< The right position of the saved background. +	int16 _saveBottom; ///< The bottom position of the saved background. +}; + +} // End of namespace Gob + +#endif // GOB_BACKBUFFER_H diff --git a/engines/gob/cmpfile.cpp b/engines/gob/cmpfile.cpp index 7b21c4c835..d304958f76 100644 --- a/engines/gob/cmpfile.cpp +++ b/engines/gob/cmpfile.cpp @@ -21,6 +21,7 @@   */  #include "common/stream.h" +#include "common/substream.h"  #include "common/str.h"  #include "gob/gob.h" @@ -143,7 +144,13 @@ void CMPFile::loadCMP(Common::SeekableReadStream &cmp) {  }  void CMPFile::loadRXY(Common::SeekableReadStream &rxy) { -	_coordinates = new RXYFile(rxy); +	bool bigEndian = (_vm->getEndiannessMethod() == kEndiannessMethodBE) || +	                 ((_vm->getEndiannessMethod() == kEndiannessMethodSystem) && +	                  (_vm->getEndianness() == kEndiannessBE)); + +	Common::SeekableSubReadStreamEndian sub(&rxy, 0, rxy.size(), bigEndian, DisposeAfterUse::NO); + +	_coordinates = new RXYFile(sub);  	for (uint i = 0; i < _coordinates->size(); i++) {  		const RXYFile::Coordinates &c = (*_coordinates)[i]; @@ -243,4 +250,9 @@ uint16 CMPFile::addSprite(uint16 left, uint16 top, uint16 right, uint16 bottom)  	return _coordinates->add(left, top, right, bottom);  } +void CMPFile::recolor(uint8 from, uint8 to) { +	if (_surface) +		_surface->recolor(from, to); +} +  } // End of namespace Gob diff --git a/engines/gob/cmpfile.h b/engines/gob/cmpfile.h index 2b669e4d38..9c858238af 100644 --- a/engines/gob/cmpfile.h +++ b/engines/gob/cmpfile.h @@ -70,6 +70,8 @@ public:  	uint16 addSprite(uint16 left, uint16 top, uint16 right, uint16 bottom); +	void recolor(uint8 from, uint8 to); +  private:  	GobEngine *_vm; diff --git a/engines/gob/decfile.cpp b/engines/gob/decfile.cpp index fb67c52627..85b4c09ca3 100644 --- a/engines/gob/decfile.cpp +++ b/engines/gob/decfile.cpp @@ -38,30 +38,38 @@ DECFile::DECFile(GobEngine *vm, const Common::String &fileName,                   uint16 width, uint16 height, uint8 bpp) : _vm(vm),  	_width(width), _height(height), _bpp(bpp), _hasPadding(false), _backdrop(0) { -	Common::SeekableReadStream *dec = _vm->_dataIO->getFile(fileName); -	if (dec) { -		Common::SeekableSubReadStreamEndian sub(dec, 0, dec->size(), false, DisposeAfterUse::YES); - -		load(sub, fileName); -		return; -	} - -	// File doesn't exist, try to open the big-endian'd alternate file -	Common::String alternateFileName = fileName; -	alternateFileName.setChar('_', 0); - -	dec = _vm->_dataIO->getFile(alternateFileName); -	if (dec) { -		Common::SeekableSubReadStreamEndian sub(dec, 0, dec->size(), true, DisposeAfterUse::YES); +	bool bigEndian = false; +	Common::String endianFileName = fileName; + +	if ((_vm->getEndiannessMethod() == kEndiannessMethodAltFile) && +	    !_vm->_dataIO->hasFile(fileName)) { +		// If the game has alternate big-endian files, look if one exist + +		Common::String alternateFileName = fileName; +		alternateFileName.setChar('_', 0); + +		if (_vm->_dataIO->hasFile(alternateFileName)) { +			bigEndian      = true; +			endianFileName = alternateFileName; +		} +	} else if ((_vm->getEndiannessMethod() == kEndiannessMethodBE) || +	           ((_vm->getEndiannessMethod() == kEndiannessMethodSystem) && +	            (_vm->getEndianness() == kEndiannessBE))) +		// Game always little endian or it follows the system and it is big endian +		bigEndian = true; + +	Common::SeekableReadStream *ani = _vm->_dataIO->getFile(endianFileName); +	if (ani) { +		Common::SeekableSubReadStreamEndian sub(ani, 0, ani->size(), bigEndian, DisposeAfterUse::YES);  		// The big endian version pads a few fields to even size -		_hasPadding = true; +		_hasPadding = bigEndian;  		load(sub, fileName);  		return;  	} -	warning("DECFile::DECFile(): No such file \"%s\"", fileName.c_str()); +	warning("DECFile::DECFile(): No such file \"%s\" (\"%s\")", endianFileName.c_str(), fileName.c_str());  }  DECFile::~DECFile() { diff --git a/engines/gob/detection/detection.cpp b/engines/gob/detection/detection.cpp index 14da54637b..8fb0052a5b 100644 --- a/engines/gob/detection/detection.cpp +++ b/engines/gob/detection/detection.cpp @@ -25,47 +25,146 @@  #include "engines/obsolete.h"  #include "gob/gob.h" +#include "gob/dataio.h"  #include "gob/detection/tables.h"  class GobMetaEngine : public AdvancedMetaEngine {  public: -	GobMetaEngine() : AdvancedMetaEngine(Gob::gameDescriptions, sizeof(Gob::GOBGameDescription), gobGames) { -		_singleid = "gob"; -		_guioptions = GUIO1(GUIO_NOLAUNCHLOAD); -	} +	GobMetaEngine(); -	virtual GameDescriptor findGame(const char *gameid) const { -		return Engines::findGameID(gameid, _gameids, obsoleteGameIDsTable); -	} +	virtual GameDescriptor findGame(const char *gameid) const; + +	virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const; -	virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { -		ADFilePropertiesMap filesProps; +	virtual const char *getName() const; +	virtual const char *getOriginalCopyright() const; + +	virtual bool hasFeature(MetaEngineFeature f) const; + +	virtual Common::Error createInstance(OSystem *syst, Engine **engine) const; +	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; + +private: +	/** +	 * Inspect the game archives to detect which Once Upon A Time game this is. +	 */ +	static const Gob::GOBGameDescription *detectOnceUponATime(const Common::FSList &fslist); +}; -		const ADGameDescription *game = detectGameFilebased(allFiles, fslist, Gob::fileBased, &filesProps); +GobMetaEngine::GobMetaEngine() : +	AdvancedMetaEngine(Gob::gameDescriptions, sizeof(Gob::GOBGameDescription), gobGames) { + +	_singleid   = "gob"; +	_guioptions = GUIO1(GUIO_NOLAUNCHLOAD); +} + +GameDescriptor GobMetaEngine::findGame(const char *gameid) const { +	return Engines::findGameID(gameid, _gameids, obsoleteGameIDsTable); +} + +const ADGameDescription *GobMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { +	ADFilePropertiesMap filesProps; + +	const Gob::GOBGameDescription *game; +	game = (const Gob::GOBGameDescription *)detectGameFilebased(allFiles, fslist, Gob::fileBased, &filesProps); +	if (!game) +		return 0; + +	if (game->gameType == Gob::kGameTypeOnceUponATime) { +		game = detectOnceUponATime(fslist);  		if (!game)  			return 0; - -		reportUnknown(fslist.begin()->getParent(), filesProps); -		return game;  	} -	virtual const char *getName() const { -		return "Gob"; +	reportUnknown(fslist.begin()->getParent(), filesProps); +	return (const ADGameDescription *)game; +} + +const Gob::GOBGameDescription *GobMetaEngine::detectOnceUponATime(const Common::FSList &fslist) { +	// Add the game path to the search manager +	SearchMan.clear(); +	SearchMan.addDirectory(fslist.begin()->getParent().getPath(), fslist.begin()->getParent()); + +	// Open the archives +	Gob::DataIO dataIO; +	if (!dataIO.openArchive("stk1.stk", true) || +	    !dataIO.openArchive("stk2.stk", true) || +	    !dataIO.openArchive("stk3.stk", true)) { + +		SearchMan.clear(); +		return 0;  	} -	virtual const char *getOriginalCopyright() const { -		return "Goblins Games (C) Coktel Vision"; +	Gob::OnceUponATime gameType         = Gob::kOnceUponATimeInvalid; +	Gob::OnceUponATimePlatform platform = Gob::kOnceUponATimePlatformInvalid; + +	// If these animal files are present, it's Abracadabra +	if (dataIO.hasFile("arai.anm") && +	    dataIO.hasFile("crab.anm") && +	    dataIO.hasFile("crap.anm") && +	    dataIO.hasFile("drag.anm") && +	    dataIO.hasFile("guep.anm") && +	    dataIO.hasFile("loup.anm") && +	    dataIO.hasFile("mous.anm") && +	    dataIO.hasFile("rhin.anm") && +	    dataIO.hasFile("saut.anm") && +	    dataIO.hasFile("scor.anm")) +		gameType = Gob::kOnceUponATimeAbracadabra; + +	// If these animal files are present, it's Baba Yaga +	if (dataIO.hasFile("abei.anm") && +	    dataIO.hasFile("arai.anm") && +	    dataIO.hasFile("drag.anm") && +	    dataIO.hasFile("fauc.anm") && +	    dataIO.hasFile("gren.anm") && +	    dataIO.hasFile("rena.anm") && +	    dataIO.hasFile("sang.anm") && +	    dataIO.hasFile("serp.anm") && +	    dataIO.hasFile("tort.anm") && +	    dataIO.hasFile("vaut.anm")) +		gameType = Gob::kOnceUponATimeBabaYaga; + +	// Detect the platform by endianness and existence of a MOD file +	Common::SeekableReadStream *villeDEC = dataIO.getFile("ville.dec"); +	if (villeDEC && (villeDEC->size() > 6)) { +		byte data[6]; + +		if (villeDEC->read(data, 6) == 6) { +			if        (!memcmp(data, "\000\000\000\001\000\007", 6)) { +				// Big endian -> Amiga or Atari ST + +				if (dataIO.hasFile("mod.babayaga")) +					platform = Gob::kOnceUponATimePlatformAmiga; +				else +					platform = Gob::kOnceUponATimePlatformAtariST; + +			} else if (!memcmp(data, "\000\000\001\000\007\000", 6)) +				// Little endian -> DOS +				platform = Gob::kOnceUponATimePlatformDOS; +		} + +		delete villeDEC;  	} -	virtual bool hasFeature(MetaEngineFeature f) const; +	SearchMan.clear(); -	virtual Common::Error createInstance(OSystem *syst, Engine **engine) const { -		Engines::upgradeTargetIfNecessary(obsoleteGameIDsTable); -		return AdvancedMetaEngine::createInstance(syst, engine); +	if ((gameType == Gob::kOnceUponATimeInvalid) || (platform == Gob::kOnceUponATimePlatformInvalid)) { +		warning("GobMetaEngine::detectOnceUponATime(): Detection failed (%d, %d)", +		        (int) gameType, (int) platform); +		return 0;  	} -	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; -}; + +	return &Gob::fallbackOnceUpon[gameType][platform]; +} + +const char *GobMetaEngine::getName() const { +	return "Gob"; +} + +const char *GobMetaEngine::getOriginalCopyright() const { +	return "Goblins Games (C) Coktel Vision"; +}  bool GobMetaEngine::hasFeature(MetaEngineFeature f) const {  	return false; @@ -75,6 +174,12 @@ bool Gob::GobEngine::hasFeature(EngineFeature f) const {  	return  		(f == kSupportsRTL);  } + +Common::Error GobMetaEngine::createInstance(OSystem *syst, Engine **engine) const { +	Engines::upgradeTargetIfNecessary(obsoleteGameIDsTable); +	return AdvancedMetaEngine::createInstance(syst, engine); +} +  bool GobMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {  	const Gob::GOBGameDescription *gd = (const Gob::GOBGameDescription *)desc;  	if (gd) { @@ -84,6 +189,7 @@ bool GobMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameD  	return gd != 0;  } +  #if PLUGIN_ENABLED_DYNAMIC(GOB)  	REGISTER_PLUGIN_DYNAMIC(GOB, PLUGIN_TYPE_ENGINE, GobMetaEngine);  #else diff --git a/engines/gob/detection/tables.h b/engines/gob/detection/tables.h index 5d211ac7d8..271f75af79 100644 --- a/engines/gob/detection/tables.h +++ b/engines/gob/detection/tables.h @@ -48,7 +48,10 @@ static const PlainGameDescriptor gobGames[] = {  	{"gob2cd", "Gobliins 2 CD"},  	{"ween", "Ween: The Prophecy"},  	{"bargon", "Bargon Attack"}, +	{"babayaga", "Once Upon A Time: Baba Yaga"}, +	{"abracadabra", "Once Upon A Time: Abracadabra"},  	{"littlered", "Once Upon A Time: Little Red Riding Hood"}, +	{"onceupon", "Once Upon A Time"},  	{"ajworld", "A.J.'s World of Discovery"},  	{"gob3", "Goblins Quest 3"},  	{"gob3cd", "Goblins Quest 3 CD"}, @@ -94,6 +97,7 @@ static const GOBGameDescription gameDescriptions[] = {  	#include "gob/detection/tables_ween.h"      // Ween: The Prophecy  	#include "gob/detection/tables_bargon.h"    // Bargon Attack  	#include "gob/detection/tables_littlered.h" // Once Upon A Time: Little Red Riding Hood +	#include "gob/detection/tables_onceupon.h"  // Once Upon A Time: Baba Yaga and Abracadabra  	#include "gob/detection/tables_lit.h"       // Lost in Time  	#include "gob/detection/tables_fascin.h"    // Fascination  	#include "gob/detection/tables_geisha.h"    // Geisha diff --git a/engines/gob/detection/tables_ajworld.h b/engines/gob/detection/tables_ajworld.h index c78d11a6ad..d86bdb16be 100644 --- a/engines/gob/detection/tables_ajworld.h +++ b/engines/gob/detection/tables_ajworld.h @@ -1,4 +1,3 @@ -  /* ScummVM - Graphic Adventure Engine   *   * ScummVM is the legal property of its developers, whose names diff --git a/engines/gob/detection/tables_fallback.h b/engines/gob/detection/tables_fallback.h index 2853ee7b4f..05f579c08c 100644 --- a/engines/gob/detection/tables_fallback.h +++ b/engines/gob/detection/tables_fallback.h @@ -23,6 +23,8 @@  #ifndef GOB_DETECTION_TABLES_FALLBACK_H  #define GOB_DETECTION_TABLES_FALLBACK_H +// -- Tables for the filename-based fallback -- +  static const GOBGameDescription fallbackDescs[] = {  	{ //0  		{ @@ -362,6 +364,20 @@ static const GOBGameDescription fallbackDescs[] = {  	},  	{ //24  		{ +			"onceupon", +			"unknown", +			AD_ENTRY1(0, 0), +			UNK_LANG, +			kPlatformUnknown, +			ADGF_NO_FLAGS, +			GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +		}, +		kGameTypeOnceUponATime, +		kFeaturesEGA, +		0, 0, 0 +	}, +	{ //25 +		{  			"adi2",  			"",  			AD_ENTRY1(0, 0), @@ -374,7 +390,7 @@ static const GOBGameDescription fallbackDescs[] = {  		kFeatures640x480,  		"adi2.stk", 0, 0  	}, -	{ //25 +	{ //26  		{  			"adi4",  			"", @@ -388,7 +404,7 @@ static const GOBGameDescription fallbackDescs[] = {  		kFeatures640x480,  		"adif41.stk", 0, 0  	}, -	{ //26 +	{ //27  		{  			"coktelplayer",  			"unknown", @@ -430,10 +446,119 @@ static const ADFileBasedFallback fileBased[] = {  	{ &fallbackDescs[21].desc, { "disk1.stk", "disk2.stk", "disk3.stk", 0 } },  	{ &fallbackDescs[22].desc, { "intro.stk", "stk2.stk", "stk3.stk", 0 } },  	{ &fallbackDescs[23].desc, { "intro.stk", "stk2.stk", "stk3.stk", "mod.babayaga", 0 } }, -	{ &fallbackDescs[24].desc, { "adi2.stk", 0 } }, -	{ &fallbackDescs[25].desc, { "adif41.stk", "adim41.stk", 0 } }, -	{ &fallbackDescs[26].desc, { "coktelplayer.scn", 0 } }, +	{ &fallbackDescs[24].desc, { "stk1.stk", "stk2.stk", "stk3.stk", 0 } }, +	{ &fallbackDescs[25].desc, { "adi2.stk", 0 } }, +	{ &fallbackDescs[26].desc, { "adif41.stk", "adim41.stk", 0 } }, +	{ &fallbackDescs[27].desc, { "coktelplayer.scn", 0 } },  	{ 0, { 0 } }  }; +// -- Tables for detecting the specific Once Upon A Time game -- + +enum OnceUponATime { +	kOnceUponATimeInvalid     = -1, +	kOnceUponATimeAbracadabra =  0, +	kOnceUponATimeBabaYaga    =  1, +	kOnceUponATimeMAX +}; + +enum OnceUponATimePlatform { +	kOnceUponATimePlatformInvalid = -1, +	kOnceUponATimePlatformDOS     =  0, +	kOnceUponATimePlatformAmiga   =  1, +	kOnceUponATimePlatformAtariST =  2, +	kOnceUponATimePlatformMAX +}; + +static const GOBGameDescription fallbackOnceUpon[kOnceUponATimeMAX][kOnceUponATimePlatformMAX] = { +	{ // kOnceUponATimeAbracadabra +		{ // kOnceUponATimePlatformDOS +			{ +				"abracadabra", +				"", +				AD_ENTRY1(0, 0), +				UNK_LANG, +				kPlatformPC, +				ADGF_NO_FLAGS, +				GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +			}, +			kGameTypeAbracadabra, +			kFeaturesAdLib | kFeaturesEGA, +			0, 0, 0 +		}, +		{ // kOnceUponATimePlatformAmiga +			{ +				"abracadabra", +				"", +				AD_ENTRY1(0, 0), +				UNK_LANG, +				kPlatformAmiga, +				ADGF_NO_FLAGS, +				GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +			}, +			kGameTypeAbracadabra, +			kFeaturesEGA, +			0, 0, 0 +		}, +		{ // kOnceUponATimePlatformAtariST +			{ +				"abracadabra", +				"", +				AD_ENTRY1(0, 0), +				UNK_LANG, +				kPlatformAtariST, +				ADGF_NO_FLAGS, +				GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +			}, +			kGameTypeAbracadabra, +			kFeaturesEGA, +			0, 0, 0 +		} +	}, +	{ // kOnceUponATimeBabaYaga +		{ // kOnceUponATimePlatformDOS +			{ +				"babayaga", +				"", +				AD_ENTRY1(0, 0), +				UNK_LANG, +				kPlatformPC, +				ADGF_NO_FLAGS, +				GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +			}, +			kGameTypeBabaYaga, +			kFeaturesAdLib | kFeaturesEGA, +			0, 0, 0 +		}, +		{ // kOnceUponATimePlatformAmiga +			{ +				"babayaga", +				"", +				AD_ENTRY1(0, 0), +				UNK_LANG, +				kPlatformAmiga, +				ADGF_NO_FLAGS, +				GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +			}, +			kGameTypeBabaYaga, +			kFeaturesEGA, +			0, 0, 0 +		}, +		{ // kOnceUponATimePlatformAtariST +			{ +				"babayaga", +				"", +				AD_ENTRY1(0, 0), +				UNK_LANG, +				kPlatformAtariST, +				ADGF_NO_FLAGS, +				GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +			}, +			kGameTypeBabaYaga, +			kFeaturesEGA, +			0, 0, 0 +		} +	} +}; +  #endif // GOB_DETECTION_TABLES_FALLBACK_H diff --git a/engines/gob/detection/tables_onceupon.h b/engines/gob/detection/tables_onceupon.h new file mode 100644 index 0000000000..366024d43c --- /dev/null +++ b/engines/gob/detection/tables_onceupon.h @@ -0,0 +1,518 @@ +/* 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. + * + */ + +/* Detection tables for Once Upon A Time: Baba Yaga and Abracadabra. */ + +#ifndef GOB_DETECTION_TABLES_ONCEUPON_H +#define GOB_DETECTION_TABLES_ONCEUPON_H + +// -- Once Upon A Time: Abracadabra, Amiga -- + +{ +	{ +		"abracadabra", +		"", +		{ +			{"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, +			{"stk2.stk", 0, "e4b21818af03930dc9cab2ad4c93cb5b", 362106}, +			{"stk3.stk", 0, "76874ad92782f9b2de57beafc05ec877", 353482}, +			{0, 0, 0, 0} +		}, +		FR_FRA, +		kPlatformAmiga, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeAbracadabra, +	kFeaturesEGA, +	0, 0, 0 +}, +{ +	{ +		"abracadabra", +		"", +		{ +			{"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, +			{"stk2.stk", 0, "e4b21818af03930dc9cab2ad4c93cb5b", 362106}, +			{"stk3.stk", 0, "76874ad92782f9b2de57beafc05ec877", 353482}, +			{0, 0, 0, 0} +		}, +		DE_DEU, +		kPlatformAmiga, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeAbracadabra, +	kFeaturesEGA, +	0, 0, 0 +}, +{ +	{ +		"abracadabra", +		"", +		{ +			{"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, +			{"stk2.stk", 0, "e4b21818af03930dc9cab2ad4c93cb5b", 362106}, +			{"stk3.stk", 0, "76874ad92782f9b2de57beafc05ec877", 353482}, +			{0, 0, 0, 0} +		}, +		EN_ANY, +		kPlatformAmiga, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeAbracadabra, +	kFeaturesEGA, +	0, 0, 0 +}, +{ +	{ +		"abracadabra", +		"", +		{ +			{"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, +			{"stk2.stk", 0, "e4b21818af03930dc9cab2ad4c93cb5b", 362106}, +			{"stk3.stk", 0, "76874ad92782f9b2de57beafc05ec877", 353482}, +			{0, 0, 0, 0} +		}, +		IT_ITA, +		kPlatformAmiga, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeAbracadabra, +	kFeaturesEGA, +	0, 0, 0 +}, +{ +	{ +		"abracadabra", +		"", +		{ +			{"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, +			{"stk2.stk", 0, "e4b21818af03930dc9cab2ad4c93cb5b", 362106}, +			{"stk3.stk", 0, "76874ad92782f9b2de57beafc05ec877", 353482}, +			{0, 0, 0, 0} +		}, +		ES_ESP, +		kPlatformAmiga, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeAbracadabra, +	kFeaturesEGA, +	0, 0, 0 +}, + +// -- Once Upon A Time: Abracadabra, Atari ST -- + +{ +	{ +		"abracadabra", +		"", +		{ +			{"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, +			{"stk2.stk", 0, "c6440aaf068ec3149ae89bc5c41ebf02", 362123}, +			{"stk3.stk", 0, "5af3c1202ba6fcf8dad2b2125e1c1383", 353257}, +			{0, 0, 0, 0} +		}, +		FR_FRA, +		kPlatformAtariST, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeAbracadabra, +	kFeaturesEGA, +	0, 0, 0 +}, +{ +	{ +		"abracadabra", +		"", +		{ +			{"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, +			{"stk2.stk", 0, "c6440aaf068ec3149ae89bc5c41ebf02", 362123}, +			{"stk3.stk", 0, "5af3c1202ba6fcf8dad2b2125e1c1383", 353257}, +			{0, 0, 0, 0} +		}, +		DE_DEU, +		kPlatformAtariST, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeAbracadabra, +	kFeaturesEGA, +	0, 0, 0 +}, +{ +	{ +		"abracadabra", +		"", +		{ +			{"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, +			{"stk2.stk", 0, "c6440aaf068ec3149ae89bc5c41ebf02", 362123}, +			{"stk3.stk", 0, "5af3c1202ba6fcf8dad2b2125e1c1383", 353257}, +			{0, 0, 0, 0} +		}, +		EN_ANY, +		kPlatformAtariST, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeAbracadabra, +	kFeaturesEGA, +	0, 0, 0 +}, +{ +	{ +		"abracadabra", +		"", +		{ +			{"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, +			{"stk2.stk", 0, "c6440aaf068ec3149ae89bc5c41ebf02", 362123}, +			{"stk3.stk", 0, "5af3c1202ba6fcf8dad2b2125e1c1383", 353257}, +			{0, 0, 0, 0} +		}, +		IT_ITA, +		kPlatformAtariST, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeAbracadabra, +	kFeaturesEGA, +	0, 0, 0 +}, +{ +	{ +		"abracadabra", +		"", +		{ +			{"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, +			{"stk2.stk", 0, "c6440aaf068ec3149ae89bc5c41ebf02", 362123}, +			{"stk3.stk", 0, "5af3c1202ba6fcf8dad2b2125e1c1383", 353257}, +			{0, 0, 0, 0} +		}, +		ES_ESP, +		kPlatformAtariST, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeAbracadabra, +	kFeaturesEGA, +	0, 0, 0 +}, + +// -- Once Upon A Time: Baba Yaga, DOS EGA Floppy -- + +{ +	{ +		"babayaga", +		"", +		{ +			{"stk1.stk", 0, "3c777f43e6fb49fde9222543447e135a", 204813}, +			{"stk2.stk", 0, "6cf0b009dd185a8f589e91a1f9c33df5", 361582}, +			{"stk3.stk", 0, "6473183ca4db1b5b5cea047f9af59a26", 328925}, +			{0, 0, 0, 0} +		}, +		FR_FRA, +		kPlatformPC, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeBabaYaga, +	kFeaturesAdLib | kFeaturesEGA, +	0, 0, 0 +}, +{ +	{ +		"babayaga", +		"", +		{ +			{"stk1.stk", 0, "3c777f43e6fb49fde9222543447e135a", 204813}, +			{"stk2.stk", 0, "6cf0b009dd185a8f589e91a1f9c33df5", 361582}, +			{"stk3.stk", 0, "6473183ca4db1b5b5cea047f9af59a26", 328925}, +			{0, 0, 0, 0} +		}, +		DE_DEU, +		kPlatformPC, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeBabaYaga, +	kFeaturesAdLib | kFeaturesEGA, +	0, 0, 0 +}, +{ +	{ +		"babayaga", +		"", +		{ +			{"stk1.stk", 0, "3c777f43e6fb49fde9222543447e135a", 204813}, +			{"stk2.stk", 0, "6cf0b009dd185a8f589e91a1f9c33df5", 361582}, +			{"stk3.stk", 0, "6473183ca4db1b5b5cea047f9af59a26", 328925}, +			{0, 0, 0, 0} +		}, +		EN_ANY, +		kPlatformPC, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeBabaYaga, +	kFeaturesAdLib | kFeaturesEGA, +	0, 0, 0 +}, +{ +	{ +		"babayaga", +		"", +		{ +			{"stk1.stk", 0, "3c777f43e6fb49fde9222543447e135a", 204813}, +			{"stk2.stk", 0, "6cf0b009dd185a8f589e91a1f9c33df5", 361582}, +			{"stk3.stk", 0, "6473183ca4db1b5b5cea047f9af59a26", 328925}, +			{0, 0, 0, 0} +		}, +		IT_ITA, +		kPlatformPC, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeBabaYaga, +	kFeaturesAdLib | kFeaturesEGA, +	0, 0, 0 +}, +{ +	{ +		"babayaga", +		"", +		{ +			{"stk1.stk", 0, "3c777f43e6fb49fde9222543447e135a", 204813}, +			{"stk2.stk", 0, "6cf0b009dd185a8f589e91a1f9c33df5", 361582}, +			{"stk3.stk", 0, "6473183ca4db1b5b5cea047f9af59a26", 328925}, +			{0, 0, 0, 0} +		}, +		ES_ESP, +		kPlatformPC, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeBabaYaga, +	kFeaturesAdLib | kFeaturesEGA, +	0, 0, 0 +}, + +// -- Once Upon A Time: Baba Yaga, Amiga -- + +{ +	{ +		"babayaga", +		"", +		{ +			{"stk1.stk", 0, "bcc823d2888057031e54716ed1b3c80e", 205090}, +			{"stk2.stk", 0, "f76bf7c2ff60d816d69962d1a593207c", 362122}, +			{"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, +			{0, 0, 0, 0} +		}, +		FR_FRA, +		kPlatformAmiga, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeBabaYaga, +	kFeaturesEGA, +	0, 0, 0 +}, +{ +	{ +		"babayaga", +		"", +		{ +			{"stk1.stk", 0, "bcc823d2888057031e54716ed1b3c80e", 205090}, +			{"stk2.stk", 0, "f76bf7c2ff60d816d69962d1a593207c", 362122}, +			{"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, +			{0, 0, 0, 0} +		}, +		DE_DEU, +		kPlatformAmiga, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeBabaYaga, +	kFeaturesEGA, +	0, 0, 0 +}, +{ +	{ +		"babayaga", +		"", +		{ +			{"stk1.stk", 0, "bcc823d2888057031e54716ed1b3c80e", 205090}, +			{"stk2.stk", 0, "f76bf7c2ff60d816d69962d1a593207c", 362122}, +			{"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, +			{0, 0, 0, 0} +		}, +		EN_ANY, +		kPlatformAmiga, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeBabaYaga, +	kFeaturesEGA, +	0, 0, 0 +}, +{ +	{ +		"babayaga", +		"", +		{ +			{"stk1.stk", 0, "bcc823d2888057031e54716ed1b3c80e", 205090}, +			{"stk2.stk", 0, "f76bf7c2ff60d816d69962d1a593207c", 362122}, +			{"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, +			{0, 0, 0, 0} +		}, +		IT_ITA, +		kPlatformAmiga, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeBabaYaga, +	kFeaturesEGA, +	0, 0, 0 +}, +{ +	{ +		"babayaga", +		"", +		{ +			{"stk1.stk", 0, "bcc823d2888057031e54716ed1b3c80e", 205090}, +			{"stk2.stk", 0, "f76bf7c2ff60d816d69962d1a593207c", 362122}, +			{"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, +			{0, 0, 0, 0} +		}, +		ES_ESP, +		kPlatformAmiga, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeBabaYaga, +	kFeaturesEGA, +	0, 0, 0 +}, + +// -- Once Upon A Time: Baba Yaga, Atari ST -- + +{ +	{ +		"babayaga", +		"", +		{ +			{"stk1.stk", 0, "17a4e3e7a18cc97231c92d280c7878a1", 205095}, +			{"stk2.stk", 0, "bfbc380e5461f63af28e9e6b10f334b5", 362128}, +			{"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, +			{0, 0, 0, 0} +		}, +		FR_FRA, +		kPlatformAtariST, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeBabaYaga, +	kFeaturesEGA, +	0, 0, 0 +}, +{ +	{ +		"babayaga", +		"", +		{ +			{"stk1.stk", 0, "17a4e3e7a18cc97231c92d280c7878a1", 205095}, +			{"stk2.stk", 0, "bfbc380e5461f63af28e9e6b10f334b5", 362128}, +			{"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, +			{0, 0, 0, 0} +		}, +		DE_DEU, +		kPlatformAtariST, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeBabaYaga, +	kFeaturesEGA, +	0, 0, 0 +}, +{ +	{ +		"babayaga", +		"", +		{ +			{"stk1.stk", 0, "17a4e3e7a18cc97231c92d280c7878a1", 205095}, +			{"stk2.stk", 0, "bfbc380e5461f63af28e9e6b10f334b5", 362128}, +			{"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, +			{0, 0, 0, 0} +		}, +		EN_ANY, +		kPlatformAtariST, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeBabaYaga, +	kFeaturesEGA, +	0, 0, 0 +}, +{ +	{ +		"babayaga", +		"", +		{ +			{"stk1.stk", 0, "17a4e3e7a18cc97231c92d280c7878a1", 205095}, +			{"stk2.stk", 0, "bfbc380e5461f63af28e9e6b10f334b5", 362128}, +			{"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, +			{0, 0, 0, 0} +		}, +		IT_ITA, +		kPlatformAtariST, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeBabaYaga, +	kFeaturesEGA, +	0, 0, 0 +}, +{ +	{ +		"babayaga", +		"", +		{ +			{"stk1.stk", 0, "17a4e3e7a18cc97231c92d280c7878a1", 205095}, +			{"stk2.stk", 0, "bfbc380e5461f63af28e9e6b10f334b5", 362128}, +			{"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, +			{0, 0, 0, 0} +		}, +		ES_ESP, +		kPlatformAtariST, +		ADGF_NO_FLAGS, +		GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) +	}, +	kGameTypeBabaYaga, +	kFeaturesEGA, +	0, 0, 0 +}, + +#endif // GOB_DETECTION_TABLES_ONCEUPON_H diff --git a/engines/gob/draw.cpp b/engines/gob/draw.cpp index fe59b11f76..8c6919416d 100644 --- a/engines/gob/draw.cpp +++ b/engines/gob/draw.cpp @@ -256,7 +256,7 @@ void Draw::blitInvalidated() {  	if (_cursorIndex == 4)  		blitCursor(); -	if (_vm->_inter->_terminate) +	if (_vm->_inter && _vm->_inter->_terminate)  		return;  	if (_noInvalidated && !_applyPal) @@ -271,7 +271,9 @@ void Draw::blitInvalidated() {  		return;  	} -	_showCursor = (_showCursor & ~2) | ((_showCursor & 1) << 1); +	if (_cursorSprites) +		_showCursor = (_showCursor & ~2) | ((_showCursor & 1) << 1); +  	if (_applyPal) {  		clearPalette();  		forceBlit(); @@ -425,28 +427,13 @@ int Draw::stringLength(const char *str, uint16 fontIndex) {  	return len;  } -void Draw::drawString(const char *str, int16 x, int16 y, int16 color1, int16 color2, -		int16 transp, Surface &dest, const Font &font) { - -	while (*str != '\0') { -		const int16 charRight  = x + font.getCharWidth(*str); -		const int16 charBottom = y + font.getCharHeight(); - -		if ((charRight <= dest.getWidth()) && (charBottom <= dest.getHeight())) -			font.drawLetter(dest, *str, x, y, color1, color2, transp); - -		x += font.getCharWidth(*str); -		str++; -	} -} -  void Draw::printTextCentered(int16 id, int16 left, int16 top, int16 right,  		int16 bottom, const char *str, int16 fontIndex, int16 color) {  	adjustCoords(1, &left, &top);  	adjustCoords(1, &right, &bottom); -	uint16 centerOffset = _vm->_game->_script->getFunctionOffset(TOTFile::kFunctionCenter); +	uint16 centerOffset = _vm->_game->_script ? _vm->_game->_script->getFunctionOffset(TOTFile::kFunctionCenter) : 0;  	if (centerOffset != 0) {  		_vm->_game->_script->call(centerOffset); @@ -505,7 +492,7 @@ void Draw::oPlaytoons_sub_F_1B(uint16 id, int16 left, int16 top, int16 right, in  	adjustCoords(1, &left, &top);  	adjustCoords(1, &right,  &bottom); -	uint16 centerOffset = _vm->_game->_script->getFunctionOffset(TOTFile::kFunctionCenter); +	uint16 centerOffset = _vm->_game->_script ? _vm->_game->_script->getFunctionOffset(TOTFile::kFunctionCenter) : 0;  	if (centerOffset != 0) {  		_vm->_game->_script->call(centerOffset); diff --git a/engines/gob/draw.h b/engines/gob/draw.h index e7af1f9af3..b51c6466e0 100644 --- a/engines/gob/draw.h +++ b/engines/gob/draw.h @@ -194,8 +194,6 @@ public:  		adjustCoords(adjust, (int16 *)coord1, (int16 *)coord2);  	}  	int stringLength(const char *str, uint16 fontIndex); -	void drawString(const char *str, int16 x, int16 y, int16 color1, int16 color2, -			int16 transp, Surface &dest, const Font &font);  	void printTextCentered(int16 id, int16 left, int16 top, int16 right,  			int16 bottom, const char *str, int16 fontIndex, int16 color);  	void oPlaytoons_sub_F_1B( uint16 id, int16 left, int16 top, int16 right, int16 bottom, char *paramStr, int16 var3, int16 var4, int16 shortId); diff --git a/engines/gob/draw_fascin.cpp b/engines/gob/draw_fascin.cpp index 54cd52b660..12009d7ee5 100644 --- a/engines/gob/draw_fascin.cpp +++ b/engines/gob/draw_fascin.cpp @@ -222,8 +222,8 @@ void Draw_Fascination::spriteOperation(int16 operation) {  								_destSpriteX, _destSpriteY, _frontColor, _backColor, _transparency);  					}  				} else { -					drawString(_textToPrint, _destSpriteX, _destSpriteY, _frontColor, -							_backColor, _transparency, *_spritesArray[_destSurface], *font); +					font->drawString(_textToPrint, _destSpriteX, _destSpriteY, _frontColor, +							_backColor, _transparency, *_spritesArray[_destSurface]);  					_destSpriteX += len * font->getCharWidth();  				}  			} else { diff --git a/engines/gob/draw_playtoons.cpp b/engines/gob/draw_playtoons.cpp index a443f81ccf..76e2ae591c 100644 --- a/engines/gob/draw_playtoons.cpp +++ b/engines/gob/draw_playtoons.cpp @@ -283,8 +283,8 @@ void Draw_Playtoons::spriteOperation(int16 operation) {  								_destSpriteX, _destSpriteY, _frontColor, _backColor, _transparency);  					}  				} else { -					drawString(_textToPrint, _destSpriteX, _destSpriteY, _frontColor, -							_backColor, _transparency, *_spritesArray[_destSurface], *font); +					font->drawString(_textToPrint, _destSpriteX, _destSpriteY, _frontColor, +							_backColor, _transparency, *_spritesArray[_destSurface]);  					_destSpriteX += len * font->getCharWidth();  				}  			} else { diff --git a/engines/gob/draw_v2.cpp b/engines/gob/draw_v2.cpp index b637ecbd2b..f5475278c4 100644 --- a/engines/gob/draw_v2.cpp +++ b/engines/gob/draw_v2.cpp @@ -74,13 +74,16 @@ void Draw_v2::closeScreen() {  }  void Draw_v2::blitCursor() { -	if (_cursorIndex == -1) +	if (!_cursorSprites || (_cursorIndex == -1))  		return;  	_showCursor = (_showCursor & ~2) | ((_showCursor & 1) << 1);  }  void Draw_v2::animateCursor(int16 cursor) { +	if (!_cursorSprites) +		return; +  	int16 cursorIndex = cursor;  	int16 newX = 0, newY = 0;  	uint16 hotspotX, hotspotY; @@ -831,8 +834,8 @@ void Draw_v2::spriteOperation(int16 operation) {  								getColor(_backColor), _transparency);  					}  				} else { -					drawString(_textToPrint, _destSpriteX, _destSpriteY, getColor(_frontColor), -							getColor(_backColor), _transparency, *_spritesArray[_destSurface], *font); +					font->drawString(_textToPrint, _destSpriteX, _destSpriteY, getColor(_frontColor), +							getColor(_backColor), _transparency, *_spritesArray[_destSurface]);  					_destSpriteX += len * font->getCharWidth();  				}  			} else { diff --git a/engines/gob/game.cpp b/engines/gob/game.cpp index 0d1953322f..de0c3f2d5c 100644 --- a/engines/gob/game.cpp +++ b/engines/gob/game.cpp @@ -64,7 +64,7 @@ void Environments::clear() {  	// Deleting unique variables, script and resources  	for (uint i = 0; i < kEnvironmentCount; i++) { -		if (_environments[i].variables == _vm->_inter->_variables) +		if (_vm->_inter && (_environments[i].variables == _vm->_inter->_variables))  			continue;  		if (!has(_environments[i].variables, i + 1)) diff --git a/engines/gob/gob.cpp b/engines/gob/gob.cpp index 3d8a18ed38..fcf98f0355 100644 --- a/engines/gob/gob.cpp +++ b/engines/gob/gob.cpp @@ -48,6 +48,10 @@  #include "gob/videoplayer.h"  #include "gob/save/saveload.h" +#include "gob/pregob/pregob.h" +#include "gob/pregob/onceupon/abracadabra.h" +#include "gob/pregob/onceupon/babayaga.h" +  namespace Gob {  #define MAX_TIME_DELTA 100 @@ -115,7 +119,7 @@ GobEngine::GobEngine(OSystem *syst) : Engine(syst), _rnd("gob") {  	_vidPlayer = 0; _init     = 0; _inter   = 0;  	_map       = 0; _palAnim  = 0; _scenery = 0;  	_draw      = 0; _util     = 0; _video   = 0; -	_saveLoad  = 0; +	_saveLoad  = 0; _preGob   = 0;  	_pauseStart = 0; @@ -180,6 +184,10 @@ void GobEngine::validateVideoMode(int16 videoMode) {  		error("Video mode 0x%X is not supported", videoMode);  } +EndiannessMethod GobEngine::getEndiannessMethod() const { +	return _endiannessMethod; +} +  Endianness GobEngine::getEndianness() const {  	if ((getPlatform() == Common::kPlatformAmiga) ||  	    (getPlatform() == Common::kPlatformMacintosh) || @@ -274,15 +282,15 @@ void GobEngine::setTrueColor(bool trueColor) {  }  Common::Error GobEngine::run() { -	if (!initGameParts()) { -		GUIErrorMessage("GobEngine::init(): Unknown version of game engine"); -		return Common::kUnknownError; -	} +	Common::Error err; -	if (!initGraphics()) { -		GUIErrorMessage("GobEngine::init(): Failed to set up graphics"); -		return Common::kUnknownError; -	} +	err = initGameParts(); +	if (err.getCode() != Common::kNoError) +		return err; + +	err = initGraphics(); +	if (err.getCode() != Common::kNoError) +		return err;  	// On some systems it's not safe to run CD audio games from the CD.  	if (isCD()) @@ -368,11 +376,12 @@ void GobEngine::pauseEngineIntern(bool pause) {  		_game->_startTimeKey += duration;  		_draw->_cursorTimeKey += duration; -		if (_inter->_soundEndTimeKey != 0) +		if (_inter && (_inter->_soundEndTimeKey != 0))  			_inter->_soundEndTimeKey += duration;  	} -	_vidPlayer->pauseAll(pause); +	if (_vidPlayer) +		_vidPlayer->pauseAll(pause);  	_mixer->pauseAll(pause);  } @@ -392,12 +401,13 @@ void GobEngine::pauseGame() {  	pauseEngineIntern(false);  } -bool GobEngine::initGameParts() { +Common::Error GobEngine::initGameParts() {  	_resourceSizeWorkaround = false;  	// just detect some devices some of which will be always there if the music is not disabled  	_noMusic = MidiDriver::getMusicType(MidiDriver::detectDevice(MDT_PCSPK | MDT_MIDI | MDT_ADLIB)) == MT_NULL ? true : false; -	_saveLoad = 0; + +	_endiannessMethod = kEndiannessMethodSystem;  	_global    = new Global(this);  	_util      = new Util(this); @@ -429,6 +439,8 @@ bool GobEngine::initGameParts() {  		_goblin   = new Goblin_v1(this);  		_scenery  = new Scenery_v1(this);  		_saveLoad = new SaveLoad_Geisha(this, _targetName.c_str()); + +		_endiannessMethod = kEndiannessMethodAltFile;  		break;  	case kGameTypeFascination: @@ -605,20 +617,45 @@ bool GobEngine::initGameParts() {  		_scenery  = new Scenery_v2(this);  		_saveLoad = new SaveLoad_v2(this, _targetName.c_str());  		break; + +	case kGameTypeAbracadabra: +		_init     = new Init_v2(this); +		_video    = new Video_v2(this); +		_mult     = new Mult_v2(this); +		_draw     = new Draw_v2(this); +		_map      = new Map_v2(this); +		_goblin   = new Goblin_v2(this); +		_scenery  = new Scenery_v2(this); +		_preGob   = new OnceUpon::Abracadabra(this); +		break; + +	case kGameTypeBabaYaga: +		_init     = new Init_v2(this); +		_video    = new Video_v2(this); +		_mult     = new Mult_v2(this); +		_draw     = new Draw_v2(this); +		_map      = new Map_v2(this); +		_goblin   = new Goblin_v2(this); +		_scenery  = new Scenery_v2(this); +		_preGob   = new OnceUpon::BabaYaga(this); +		break; +  	default:  		deinitGameParts(); -		return false; +		return Common::kUnsupportedGameidError;  	}  	// Setup mixer  	syncSoundSettings(); -	_inter->setupOpcodes(); +	if (_inter) +		_inter->setupOpcodes(); -	return true; +	return Common::kNoError;  }  void GobEngine::deinitGameParts() { +	delete _preGob;    _preGob = 0;  	delete _saveLoad;  _saveLoad = 0;  	delete _mult;      _mult = 0;  	delete _vidPlayer; _vidPlayer = 0; @@ -637,10 +674,10 @@ void GobEngine::deinitGameParts() {  	delete _dataIO;    _dataIO = 0;  } -bool GobEngine::initGraphics() { +Common::Error GobEngine::initGraphics() {  	if        (is800x600()) {  		warning("GobEngine::initGraphics(): 800x600 games currently unsupported"); -		return false; +		return Common::kUnsupportedGameidError;  	} else if (is640x480()) {  		_width  = 640;  		_height = 480; @@ -664,7 +701,7 @@ bool GobEngine::initGraphics() {  	_global->_primarySurfDesc = SurfacePtr(new Surface(_width, _height, _pixelFormat.bytesPerPixel)); -	return true; +	return Common::kNoError;  }  } // End of namespace Gob diff --git a/engines/gob/gob.h b/engines/gob/gob.h index 52f3ba8f2d..df73404596 100644 --- a/engines/gob/gob.h +++ b/engines/gob/gob.h @@ -75,6 +75,7 @@ class Scenery;  class Util;  class SaveLoad;  class GobConsole; +class PreGob;  #define WRITE_VAR_UINT32(var, val)  _vm->_inter->_variables->writeVar32(var, val)  #define WRITE_VAR_UINT16(var, val)  _vm->_inter->_variables->writeVar16(var, val) @@ -129,7 +130,10 @@ enum GameType {  	kGameTypeAdi4,  	kGameTypeAdibou2,  	kGameTypeAdibou1, +	kGameTypeAbracadabra, +	kGameTypeBabaYaga,  	kGameTypeLittleRed, +	kGameTypeOnceUponATime, // Need more inspection to see if Baba Yaga or Abracadabra  	kGameTypeAJWorld  }; @@ -145,6 +149,13 @@ enum Features {  	kFeaturesTrueColor = 1 << 7  }; +enum EndiannessMethod { +	kEndiannessMethodLE,     ///< Always little endian. +	kEndiannessMethodBE,     ///< Always big endian. +	kEndiannessMethodSystem, ///< Follows system endianness. +	kEndiannessMethodAltFile ///< Different endianness in alternate file. +}; +  enum {  	kDebugFuncOp     = 1 <<  0,  	kDebugDrawOp     = 1 <<  1, @@ -168,6 +179,8 @@ private:  	int32 _features;  	Common::Platform _platform; +	EndiannessMethod _endiannessMethod; +  	uint32 _pauseStart;  	// Engine APIs @@ -176,10 +189,10 @@ private:  	virtual void pauseEngineIntern(bool pause);  	virtual void syncSoundSettings(); -	bool initGameParts(); -	void deinitGameParts(); +	Common::Error initGameParts(); +	Common::Error initGraphics(); -	bool initGraphics(); +	void deinitGameParts();  public:  	static const Common::Language _gobToScummVMLang[]; @@ -220,6 +233,7 @@ public:  	Inter *_inter;  	SaveLoad *_saveLoad;  	VideoPlayer *_vidPlayer; +	PreGob *_preGob;  	const char *getLangDesc(int16 language) const;  	void validateLanguage(); @@ -227,6 +241,7 @@ public:  	void pauseGame(); +	EndiannessMethod getEndiannessMethod() const;  	Endianness getEndianness() const;  	Common::Platform getPlatform() const;  	GameType getGameType() const; diff --git a/engines/gob/init.cpp b/engines/gob/init.cpp index a61261f355..814d4d1821 100644 --- a/engines/gob/init.cpp +++ b/engines/gob/init.cpp @@ -34,9 +34,13 @@  #include "gob/inter.h"  #include "gob/video.h"  #include "gob/videoplayer.h" + +#include "gob/sound/sound.h" +  #include "gob/demos/scnplayer.h"  #include "gob/demos/batplayer.h" -#include "gob/sound/sound.h" + +#include "gob/pregob/pregob.h"  namespace Gob { @@ -118,6 +122,14 @@ void Init::initGame() {  		return;  	} +	if (_vm->_preGob) { +		_vm->_preGob->run(); +		delete _palDesc; +		_vm->_video->initPrimary(-1); +		cleanup(); +		return; +	} +  	Common::SeekableReadStream *infFile = _vm->_dataIO->getFile("intro.inf");  	if (!infFile) { diff --git a/engines/gob/inter_bargon.cpp b/engines/gob/inter_bargon.cpp index 134203fa9d..029f7c697b 100644 --- a/engines/gob/inter_bargon.cpp +++ b/engines/gob/inter_bargon.cpp @@ -119,7 +119,7 @@ void Inter_Bargon::oBargon_intro2(OpGobParams ¶ms) {  	MouseButtons buttons;  	SurfacePtr surface;  	SoundDesc samples[4]; -	int16 comp[5] = { 0, 1, 2, 3, -1 }; +	static const int16 comp[5] = { 0, 1, 2, 3, -1 };  	static const char *const sndFiles[] = {"1INTROII.snd", "2INTROII.snd", "1INTRO3.snd", "2INTRO3.snd"};  	surface = _vm->_video->initSurfDesc(320, 200); @@ -167,8 +167,8 @@ void Inter_Bargon::oBargon_intro3(OpGobParams ¶ms) {  	MouseButtons buttons;  	Video::Color *palBak;  	SoundDesc samples[2]; -	int16 comp[3] = { 0, 1, -1 };  	byte *palettes[4]; +	static const int16 comp[3] = { 0, 1, -1 };  	static const char *const sndFiles[] = {"1INTROIV.snd", "2INTROIV.snd"};  	static const char *const palFiles[] = {"2ou2.clt", "2ou3.clt", "2ou4.clt", "2ou5.clt"}; diff --git a/engines/gob/inter_v5.cpp b/engines/gob/inter_v5.cpp index c0e8978afd..24905b08d1 100644 --- a/engines/gob/inter_v5.cpp +++ b/engines/gob/inter_v5.cpp @@ -281,7 +281,7 @@ void Inter_v5::o5_getSystemCDSpeed(OpGobParams ¶ms) {  	Font *font;  	if ((font = _vm->_draw->loadFont("SPEED.LET"))) { -		_vm->_draw->drawString("100 %", 402, 89, 112, 144, 0, *_vm->_draw->_backSurface, *font); +		font->drawString("100 %", 402, 89, 112, 144, 0, *_vm->_draw->_backSurface);  		_vm->_draw->forceBlit();  		delete font; @@ -293,7 +293,7 @@ void Inter_v5::o5_getSystemRAM(OpGobParams ¶ms) {  	Font *font;  	if ((font = _vm->_draw->loadFont("SPEED.LET"))) { -		_vm->_draw->drawString("100 %", 402, 168, 112, 144, 0, *_vm->_draw->_backSurface, *font); +		font->drawString("100 %", 402, 168, 112, 144, 0, *_vm->_draw->_backSurface);  		_vm->_draw->forceBlit();  		delete font; @@ -305,7 +305,7 @@ void Inter_v5::o5_getSystemCPUSpeed(OpGobParams ¶ms) {  	Font *font;  	if ((font = _vm->_draw->loadFont("SPEED.LET"))) { -		_vm->_draw->drawString("100 %", 402, 248, 112, 144, 0, *_vm->_draw->_backSurface, *font); +		font->drawString("100 %", 402, 248, 112, 144, 0, *_vm->_draw->_backSurface);  		_vm->_draw->forceBlit();  		delete font; @@ -317,7 +317,7 @@ void Inter_v5::o5_getSystemDrawSpeed(OpGobParams ¶ms) {  	Font *font;  	if ((font = _vm->_draw->loadFont("SPEED.LET"))) { -		_vm->_draw->drawString("100 %", 402, 326, 112, 144, 0, *_vm->_draw->_backSurface, *font); +		font->drawString("100 %", 402, 326, 112, 144, 0, *_vm->_draw->_backSurface);  		_vm->_draw->forceBlit();  		delete font; @@ -329,7 +329,7 @@ void Inter_v5::o5_totalSystemSpecs(OpGobParams ¶ms) {  	Font *font;  	if ((font = _vm->_draw->loadFont("SPEED.LET"))) { -		_vm->_draw->drawString("100 %", 402, 405, 112, 144, 0, *_vm->_draw->_backSurface, *font); +		font->drawString("100 %", 402, 405, 112, 144, 0, *_vm->_draw->_backSurface);  		_vm->_draw->forceBlit();  		delete font; diff --git a/engines/gob/minigames/geisha/penetration.cpp b/engines/gob/minigames/geisha/penetration.cpp index 3be9f1f651..c8c4f2bba7 100644 --- a/engines/gob/minigames/geisha/penetration.cpp +++ b/engines/gob/minigames/geisha/penetration.cpp @@ -505,7 +505,7 @@ bool Penetration::play(bool hasAccessPass, bool hasMaxEnergy, bool testMode) {  		// Draw, fade in if necessary and wait for the end of the frame  		_vm->_draw->blitInvalidated();  		fadeIn(); -		_vm->_util->waitEndFrame(); +		_vm->_util->waitEndFrame(false);  		// Handle the input  		checkInput(); @@ -778,29 +778,24 @@ void Penetration::drawFloorText() {  	else if (_floor == 2)  		floorString = strings[kString1stBasement]; +	Surface &surface = *_vm->_draw->_backSurface; +  	if (floorString) -		_vm->_draw->drawString(floorString, 10, 15, kColorFloorText, kColorBlack, 1, -		                       *_vm->_draw->_backSurface, *font); +		font->drawString(floorString, 10, 15, kColorFloorText, kColorBlack, 1, surface);  	if (_exits.size() > 0) {  		int exitCount = kString2Exits;  		if (_exits.size() == 1)  			exitCount = kString1Exit; -		_vm->_draw->drawString(strings[kStringYouHave]    , 10, 38, kColorExitText, kColorBlack, 1, -		                       *_vm->_draw->_backSurface, *font); -		_vm->_draw->drawString(strings[exitCount]         , 10, 53, kColorExitText, kColorBlack, 1, -		                       *_vm->_draw->_backSurface, *font); -		_vm->_draw->drawString(strings[kStringToReach]    , 10, 68, kColorExitText, kColorBlack, 1, -		                       *_vm->_draw->_backSurface, *font); -		_vm->_draw->drawString(strings[kStringUpperLevel1], 10, 84, kColorExitText, kColorBlack, 1, -		                       *_vm->_draw->_backSurface, *font); -		_vm->_draw->drawString(strings[kStringUpperLevel2], 10, 98, kColorExitText, kColorBlack, 1, -		                       *_vm->_draw->_backSurface, *font); +		font->drawString(strings[kStringYouHave]    , 10, 38, kColorExitText, kColorBlack, 1, surface); +		font->drawString(strings[exitCount]         , 10, 53, kColorExitText, kColorBlack, 1, surface); +		font->drawString(strings[kStringToReach]    , 10, 68, kColorExitText, kColorBlack, 1, surface); +		font->drawString(strings[kStringUpperLevel1], 10, 84, kColorExitText, kColorBlack, 1, surface); +		font->drawString(strings[kStringUpperLevel2], 10, 98, kColorExitText, kColorBlack, 1, surface);  	} else -		_vm->_draw->drawString(strings[kStringNoExit], 10, 53, kColorExitText, kColorBlack, 1, -		                       *_vm->_draw->_backSurface, *font); +		font->drawString(strings[kStringNoExit], 10, 53, kColorExitText, kColorBlack, 1, surface);  }  void Penetration::drawEndText() { @@ -814,21 +809,17 @@ void Penetration::drawEndText() {  	if (!font)  		return; +	Surface &surface = *_vm->_draw->_backSurface; +  	const char **strings = kStrings[getLanguage()]; -	_vm->_draw->drawString(strings[kStringLevel0]     , 11, 21, kColorExitText, kColorBlack, 1, -	                       *_vm->_draw->_backSurface, *font); -	_vm->_draw->drawString(strings[kStringPenetration], 11, 42, kColorExitText, kColorBlack, 1, -	                       *_vm->_draw->_backSurface, *font); -	_vm->_draw->drawString(strings[kStringSuccessful] , 11, 58, kColorExitText, kColorBlack, 1, -	                       *_vm->_draw->_backSurface, *font); - -	_vm->_draw->drawString(strings[kStringDanger]   , 11,  82, kColorFloorText, kColorBlack, 1, -	                       *_vm->_draw->_backSurface, *font); -	_vm->_draw->drawString(strings[kStringGynoides] , 11,  98, kColorFloorText, kColorBlack, 1, -	                       *_vm->_draw->_backSurface, *font); -	_vm->_draw->drawString(strings[kStringActivated], 11, 113, kColorFloorText, kColorBlack, 1, -	                       *_vm->_draw->_backSurface, *font); +	font->drawString(strings[kStringLevel0]     , 11, 21, kColorExitText, kColorBlack, 1, surface); +	font->drawString(strings[kStringPenetration], 11, 42, kColorExitText, kColorBlack, 1, surface); +	font->drawString(strings[kStringSuccessful] , 11, 58, kColorExitText, kColorBlack, 1, surface); + +	font->drawString(strings[kStringDanger]   , 11,  82, kColorFloorText, kColorBlack, 1, surface); +	font->drawString(strings[kStringGynoides] , 11,  98, kColorFloorText, kColorBlack, 1, surface); +	font->drawString(strings[kStringActivated], 11, 113, kColorFloorText, kColorBlack, 1, surface);  	_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, kTextAreaLeft, kTextAreaTop, kTextAreaRight, kTextAreaBigBottom);  	_vm->_draw->blitInvalidated(); diff --git a/engines/gob/module.mk b/engines/gob/module.mk index 3395046b6c..d5ee6478be 100644 --- a/engines/gob/module.mk +++ b/engines/gob/module.mk @@ -3,6 +3,7 @@ MODULE := engines/gob  MODULE_OBJS := \  	anifile.o \  	aniobject.o \ +	backbuffer.o \  	cheater.o \  	cheater_geisha.o \  	cmpfile.o \ @@ -77,6 +78,17 @@ MODULE_OBJS := \  	demos/scnplayer.o \  	demos/batplayer.o \  	detection/detection.o \ +	pregob/pregob.o \ +	pregob/txtfile.o \ +	pregob/gctfile.o \ +	pregob/seqfile.o \ +	pregob/onceupon/onceupon.o \ +	pregob/onceupon/abracadabra.o \ +	pregob/onceupon/babayaga.o \ +	pregob/onceupon/title.o \ +	pregob/onceupon/parents.o \ +	pregob/onceupon/stork.o \ +	pregob/onceupon/chargenchild.o \  	minigames/geisha/evilfish.o \  	minigames/geisha/oko.o \  	minigames/geisha/meter.o \ diff --git a/engines/gob/pregob/gctfile.cpp b/engines/gob/pregob/gctfile.cpp new file mode 100644 index 0000000000..08c32cda76 --- /dev/null +++ b/engines/gob/pregob/gctfile.cpp @@ -0,0 +1,306 @@ +/* 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/random.h" +#include "common/stream.h" + +#include "gob/surface.h" +#include "gob/video.h" + +#include "gob/pregob/gctfile.h" + +namespace Gob { + +GCTFile::Chunk::Chunk() : type(kChunkTypeNone) { +} + + +GCTFile::GCTFile(Common::SeekableReadStream &gct, Common::RandomSource &rnd) : _rnd(&rnd), +	_areaLeft(0), _areaTop(0), _areaRight(0), _areaBottom(0), _currentItem(0xFFFF) { + +	load(gct); +} + +GCTFile::~GCTFile() { +} + +void GCTFile::load(Common::SeekableReadStream &gct) { +	gct.skip(4); // Required buffer size +	gct.skip(2); // Unknown + +	// Read the selector and line counts for each item +	const uint16 itemCount = gct.readUint16LE(); +	_items.resize(itemCount); + +	for (Items::iterator i = _items.begin(); i != _items.end(); ++i) { +		const uint16 selector  = gct.readUint16LE(); +		const uint16 lineCount = gct.readUint16LE(); + +		i->selector = selector; +		i->lines.resize(lineCount); +	} + +	// Read all item lines +	for (Items::iterator i = _items.begin(); i != _items.end(); ++i) { +		for (Lines::iterator l = i->lines.begin(); l != i->lines.end(); ++l) { +			const uint16 lineSize = gct.readUint16LE(); + +			readLine(gct, *l, lineSize); +		} +	} + +	if (gct.err()) +		error("GCTFile::load(): Failed reading GCT"); +} + +void GCTFile::readLine(Common::SeekableReadStream &gct, Line &line, uint16 lineSize) const { +	line.chunks.push_back(Chunk()); + +	while (lineSize > 0) { +		byte c = gct.readByte(); +		lineSize--; + +		if (c == 0) { +			// Command byte + +			if (lineSize == 0) +				break; + +			byte cmd = gct.readByte(); +			lineSize--; + +			// Line end command +			if (cmd == 0) +				break; + +			// Item reference command +			if (cmd == 1) { +				if (lineSize < 2) { +					warning("GCTFile::readLine(): Item reference command is missing parameters"); +					break; +				} + +				const uint32 itemRef = gct.readUint16LE(); +				lineSize -= 2; + +				line.chunks.push_back(Chunk()); +				line.chunks.back().type = kChunkTypeItem; +				line.chunks.back().item = itemRef; + +				line.chunks.push_back(Chunk()); +				continue; +			} + +			warning("GCTFile::readLine(): Invalid command 0x%02X", cmd); +			break; +		} + +		// Text +		line.chunks.back().type = kChunkTypeString; +		line.chunks.back().text += (char)c; +	} + +	// Skip bytes we didn't read (because of errors) +	gct.skip(lineSize); + +	// Remove empty chunks from the end of the list +	while (!line.chunks.empty() && (line.chunks.back().type == kChunkTypeNone)) +		line.chunks.pop_back(); +} + +uint16 GCTFile::getLineCount(uint item) const { +	if (item >= _items.size()) +		return 0; + +	return _items[item].lines.size(); +} + +void GCTFile::selectLine(uint item, uint16 line) { +	if ((item >= _items.size()) && (item != kSelectorAll) && (item != kSelectorRandom)) +		return; + +	_items[item].selector = line; +} + +void GCTFile::setText(uint item, uint16 line, const Common::String &text) { +	if ((item >= _items.size()) || (line >= _items[item].lines.size())) +		return; + +	_items[item].lines[line].chunks.clear(); +	_items[item].lines[line].chunks.push_back(Chunk()); + +	_items[item].lines[line].chunks.back().type = kChunkTypeString; +	_items[item].lines[line].chunks.back().text = text; +} + +void GCTFile::setText(uint item, const Common::String &text) { +	if (item >= _items.size()) +		return; + +	_items[item].selector = 0; + +	_items[item].lines.resize(1); + +	setText(item, 0, text); +} + +void GCTFile::reset() { +	_currentItem = 0xFFFF; +	_currentText.clear(); +} + +Common::String GCTFile::getLineText(const Line &line) const { +	Common::String text; + +	// Go over all chunks in this line +	for (Chunks::const_iterator c = line.chunks.begin(); c != line.chunks.end(); ++c) { +		// A chunk is either a direct string, or a reference to another item + +		if        (c->type == kChunkTypeItem) { +			Common::List<Common::String> lines; + +			getItemText(c->item, lines); +			if (lines.empty()) +				continue; + +			if (lines.size() > 1) +				warning("GCTFile::getLineText(): Referenced item has multiple lines"); + +			text += lines.front(); +		} else if (c->type == kChunkTypeString) +			text += c->text; +	} + +	return text; +} + +void GCTFile::getItemText(uint item, Common::List<Common::String> &text) const { +	text.clear(); + +	if ((item >= _items.size()) || _items[item].lines.empty()) +		return; + +	uint16 line = _items[item].selector; + +	// Draw all lines +	if (line == kSelectorAll) { +		for (Lines::const_iterator l = _items[item].lines.begin(); l != _items[item].lines.end(); ++l) +			text.push_back(getLineText(*l)); + +		return; +	} + +	// Draw random line +	if (line == kSelectorRandom) +		line = _rnd->getRandomNumber(_items[item].lines.size() - 1); + +	if (line >= _items[item].lines.size()) +		return; + +	text.push_back(getLineText(_items[item].lines[line])); +} + +void GCTFile::setArea(int16 left, int16 top, int16 right, int16 bottom) { +	trashBuffer(); + +	_hasArea = false; + +	const int16 width  = right  - left + 1; +	const int16 height = bottom - top  + 1; +	if ((width <= 0) || (height <= 0)) +		return; + +	_areaLeft   = left; +	_areaTop    = top; +	_areaRight  = right; +	_areaBottom = bottom; + +	_hasArea = true; + +	resizeBuffer(width, height); +} + +bool GCTFile::clear(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { +	return restoreScreen(dest, left, top, right, bottom); +} + +bool GCTFile::fill(Surface &dest, uint8 color, int16 &left, int16 &top, int16 &right, int16 &bottom) { +	left   = _areaLeft; +	top    = _areaTop; +	right  = _areaRight; +	bottom = _areaBottom; + +	if (!hasSavedBackground()) +		saveScreen(dest, left, top, right, bottom); + +	dest.fillRect(left, top, right, bottom, color); + +	return true; +} + +bool GCTFile::finished() const { +	return (_currentItem != 0xFFFF) && _currentText.empty(); +} + +bool GCTFile::draw(Surface &dest, uint16 item, const Font &font, uint8 color, +                   int16 &left, int16 &top, int16 &right, int16 &bottom) { + +	if ((item >= _items.size()) || !_hasArea) +		return false; + +	left   = _areaLeft; +	top    = _areaTop; +	right  = _areaRight; +	bottom = _areaBottom; + +	const int16 width  = right  - left + 1; +	const int16 height = bottom - top  + 1; + +	const uint lineCount = height / font.getCharHeight(); +	if (lineCount == 0) +		return false; + +	if (!hasSavedBackground()) +		saveScreen(dest, left, top, right, bottom); + +	if (item != _currentItem) { +		_currentItem = item; + +		getItemText(_currentItem, _currentText); +	} + +	if (_currentText.empty()) +		return false; + +	int16 y = top; +	for (uint i = 0; (i < lineCount) && !_currentText.empty(); i++, y += font.getCharHeight()) { +		const Common::String &line = _currentText.front(); +		const int16 x = left + ((width - (line.size() * font.getCharWidth())) / 2); + +		font.drawString(line, x, y, color, 0, true, dest); +		_currentText.pop_front(); +	} + +	return true; +} + +} // End of namespace Gob diff --git a/engines/gob/pregob/gctfile.h b/engines/gob/pregob/gctfile.h new file mode 100644 index 0000000000..ed6351b7a8 --- /dev/null +++ b/engines/gob/pregob/gctfile.h @@ -0,0 +1,149 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GOB_PREGOB_GCTFILE_H +#define GOB_PREGOB_GCTFILE_H + +#include "common/str.h" +#include "common/array.h" +#include "common/list.h" + +#include "gob/backbuffer.h" + +namespace Common { +	class RandomSource; +	class SeekableReadStream; +} + +namespace Gob { + +class Surface; +class Font; + +class GCTFile : public BackBuffer { +public: +	static const uint16 kSelectorAll    = 0xFFFE; ///< Print all lines. +	static const uint16 kSelectorRandom = 0xFFFF; ///< Print a random line. + + +	GCTFile(Common::SeekableReadStream &gct, Common::RandomSource &rnd); +	~GCTFile(); + +	/** Return the number of lines in an item. */ +	uint16 getLineCount(uint item) const; + +	/** Set the area the text will be printed in. */ +	void setArea(int16 left, int16 top, int16 right, int16 bottom); + +	/** Set which line of this item should be printed. */ +	void selectLine(uint item, uint16 line); + +	/** Change the text of an items' line. */ +	void setText(uint item, uint16 line, const Common::String &text); +	/** Change the item into one one line and set that line's text. */ +	void setText(uint item, const Common::String &text); + +	/** Reset the item drawing state. */ +	void reset(); + +	/** Clear the drawn text, restoring the original content. */ +	bool clear(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); + +	/** Fill the text area with a color. */ +	bool fill(Surface &dest, uint8 color, int16 &left, int16 &top, int16 &right, int16 &bottom); + +	/** Draw an item onto the surface, until all text has been drawn or the area is filled. */ +	bool draw(Surface &dest, uint16 item, const Font &font, uint8 color, +	          int16 &left, int16 &top, int16 &right, int16 &bottom); + +	/** Did we draw all text? */ +	bool finished() const; + +private: +	/** The type of a chunk. */ +	enum ChunkType { +		kChunkTypeNone   = 0, ///< Do nothing. +		kChunkTypeString    , ///< A direct string. +		kChunkTypeItem        ///< A reference to an item to print instead. +	}; + +	/** A chunk in an item text line. */ +	struct Chunk { +		ChunkType type; ///< The type of the chunk. + +		Common::String text; ///< Text to print. + +		int item; ///< Item to print instead. + +		Chunk(); +	}; + +	typedef Common::List<Chunk> Chunks; + +	/** A line in an item. */ +	struct Line { +		Chunks chunks; ///< The chunks that make up the line. +	}; + +	typedef Common::Array<Line> Lines; + +	/** A GCT item. */ +	struct Item { +		Lines  lines;    ///< The text lines in the item +		uint16 selector; ///< Which line to print. +	}; + +	typedef Common::Array<Item> Items; + + +	Common::RandomSource *_rnd; + +	Items _items; ///< All GCT items. + +	// The area on which to print +	bool  _hasArea; +	int16 _areaLeft; +	int16 _areaTop; +	int16 _areaRight; +	int16 _areaBottom; + +	/** Index of the current item we're drawing. */ +	uint16 _currentItem; +	/** Text left to draw. */ +	Common::List<Common::String> _currentText; + + +	// -- Loading helpers -- + +	void load(Common::SeekableReadStream &gct); +	void readLine(Common::SeekableReadStream &gct, Line &line, uint16 lineSize) const; + + +	// -- Draw helpers -- + +	Common::String getLineText(const Line &line) const; +	void getItemText(uint item, Common::List<Common::String> &text) const; +}; + +} // End of namespace Gob + +#endif // GOB_PREGOB_GCTFILE_H diff --git a/engines/gob/pregob/onceupon/abracadabra.cpp b/engines/gob/pregob/onceupon/abracadabra.cpp new file mode 100644 index 0000000000..2cf6855ef8 --- /dev/null +++ b/engines/gob/pregob/onceupon/abracadabra.cpp @@ -0,0 +1,137 @@ +/* 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/textconsole.h" + +#include "gob/gob.h" + +#include "gob/pregob/onceupon/abracadabra.h" + +static const uint8 kCopyProtectionColors[7] = { +	14, 11, 13,  1,  7, 12,  2 +}; + +static const uint8 kCopyProtectionShapes[7 * 20] = { +	3, 4, 3, 0, 1, 2, 0, 2, 2, 0, 2, 4, 0, 3, 4, 1, 1, 4, 1, 3, +	0, 2, 0, 4, 2, 4, 4, 2, 3, 0, 1, 1, 1, 1, 3, 0, 4, 2, 3, 4, +	0, 0, 1, 2, 1, 1, 2, 4, 3, 1, 4, 2, 4, 4, 2, 4, 1, 2, 3, 3, +	1, 0, 2, 3, 4, 2, 3, 2, 2, 0, 0, 0, 4, 2, 3, 4, 4, 0, 4, 1, +	4, 2, 1, 1, 1, 1, 4, 3, 4, 2, 3, 0, 0, 3, 0, 2, 3, 0, 2, 4, +	4, 2, 4, 3, 0, 4, 0, 2, 3, 1, 4, 1, 3, 1, 0, 0, 2, 1, 3, 2, +	3, 1, 0, 3, 1, 3, 4, 2, 4, 4, 3, 2, 0, 2, 0, 1, 2, 0, 1, 4 +}; + +static const uint8 kCopyProtectionObfuscate[4] = { +	1, 0, 2, 3 +}; + +namespace Gob { + +namespace OnceUpon { + +const OnceUpon::MenuButton Abracadabra::kAnimalsButtons = { +	true, 131, 127, 183, 164, 193, 0, 243, 35, 132, 128, 0 +}; + +const OnceUpon::MenuButton Abracadabra::kAnimalButtons[] = { +	{false,  37,  89,  95, 127,  37,  89,  95, 127, 131, 25, 0}, +	{false, 114,  65, 172, 111, 114,  65, 172, 111, 131, 25, 1}, +	{false, 186,  72, 227,  96, 186,  72, 227,  96, 139, 25, 2}, +	{false, 249,  87, 282, 112, 249,  87, 282, 112, 143, 25, 3}, +	{false, 180, 102, 234, 138, 180, 102, 234, 138, 133, 25, 4}, +	{false, 197, 145, 242, 173, 197, 145, 242, 173, 137, 25, 5}, +	{false, 113, 151, 171, 176, 113, 151, 171, 176, 131, 25, 6}, +	{false, 114, 122, 151, 150, 114, 122, 151, 150, 141, 25, 7}, +	{false,  36, 136,  94, 176,  36, 136,  94, 176, 131, 25, 8}, +	{false, 243, 123, 295, 155, 243, 123, 295, 155, 136, 25, 9} +}; + +const char *Abracadabra::kAnimalNames[] = { +	"loup", +	"drag", +	"arai", +	"crap", +	"crab", +	"mous", +	"saut", +	"guep", +	"rhin", +	"scor" +}; + +// The houses where the stork can drop a bundle +const OnceUpon::MenuButton Abracadabra::kStorkHouses[] = { +	{false,  16,  80,  87, 125, 0, 0, 0, 0, 0, 0, 0}, // Castle , Lord & Lady +	{false,  61, 123,  96, 149, 0, 0, 0, 0, 0, 0, 1}, // Cottage, Farmers +	{false, 199, 118, 226, 137, 0, 0, 0, 0, 0, 0, 2}, // Hut    , Woodcutters +	{false, 229,  91, 304, 188, 0, 0, 0, 0, 0, 0, 3}  // Palace , King & Queen +}; + +// The stork bundle drop parameters +const Stork::BundleDrop Abracadabra::kStorkBundleDrops[] = { +	{ 14,  65, 127,  true }, +	{ 14,  76, 152,  true }, +	{ 14, 204, 137,  true }, +	{ 11, 275, 179, false } +}; + +// Parameters for the stork section. +const OnceUpon::StorkParam Abracadabra::kStorkParam = { +	"present.cmp", ARRAYSIZE(kStorkHouses), kStorkHouses, kStorkBundleDrops +}; + + +Abracadabra::Abracadabra(GobEngine *vm) : OnceUpon(vm) { +} + +Abracadabra::~Abracadabra() { +} + +void Abracadabra::run() { +	init(); + +	// Copy protection +	bool correctCP = doCopyProtection(kCopyProtectionColors, kCopyProtectionShapes, kCopyProtectionObfuscate); +	if (_vm->shouldQuit() || !correctCP) +		return; + +	// Show the intro +	showIntro(); +	if (_vm->shouldQuit()) +		return; + +	// Handle the start menu +	doStartMenu(&kAnimalsButtons, ARRAYSIZE(kAnimalButtons), kAnimalButtons, kAnimalNames); +	if (_vm->shouldQuit()) +		return; + +	// Play the actual game +	playGame(); +} + +const OnceUpon::StorkParam &Abracadabra::getStorkParameters() const { +	return kStorkParam; +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/abracadabra.h b/engines/gob/pregob/onceupon/abracadabra.h new file mode 100644 index 0000000000..8048213f5f --- /dev/null +++ b/engines/gob/pregob/onceupon/abracadabra.h @@ -0,0 +1,61 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GOB_PREGOB_ONCEUPON_ABRACADABRA_H +#define GOB_PREGOB_ONCEUPON_ABRACADABRA_H + +#include "gob/pregob/onceupon/onceupon.h" + +namespace Gob { + +namespace OnceUpon { + +class Abracadabra : public OnceUpon { +public: +	Abracadabra(GobEngine *vm); +	~Abracadabra(); + +	void run(); + +protected: +	const StorkParam &getStorkParameters() const; + +private: +	/** Definition of the menu button that leads to the animal names screen. */ +	static const MenuButton kAnimalsButtons; + +	/** Definition of the buttons that make up the animals in the animal names screen. */ +	static const MenuButton kAnimalButtons[]; +	/** File prefixes for the name of each animal. */ +	static const char *kAnimalNames[]; + +	// Parameters for the stork section. +	static const MenuButton kStorkHouses[]; +	static const Stork::BundleDrop kStorkBundleDrops[]; +	static const struct StorkParam kStorkParam; +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_ABRACADABRA_H diff --git a/engines/gob/pregob/onceupon/babayaga.cpp b/engines/gob/pregob/onceupon/babayaga.cpp new file mode 100644 index 0000000000..ef56b9dd0b --- /dev/null +++ b/engines/gob/pregob/onceupon/babayaga.cpp @@ -0,0 +1,137 @@ +/* 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/textconsole.h" + +#include "gob/gob.h" + +#include "gob/pregob/onceupon/babayaga.h" + +static const uint8 kCopyProtectionColors[7] = { +	14, 11, 13,  1,  7, 12,  2 +}; + +static const uint8 kCopyProtectionShapes[7 * 20] = { +	0, 0, 1, 2, 1, 1, 2, 4, 3, 1, 4, 2, 4, 4, 2, 4, 1, 2, 3, 3, +	3, 1, 0, 3, 1, 3, 4, 2, 4, 4, 3, 2, 0, 2, 0, 1, 2, 0, 1, 4, +	1, 0, 2, 3, 4, 2, 3, 2, 2, 0, 0, 0, 4, 2, 3, 4, 4, 0, 4, 1, +	0, 2, 0, 4, 2, 4, 4, 2, 3, 0, 1, 1, 1, 1, 3, 0, 4, 2, 3, 4, +	3, 4, 3, 0, 1, 2, 0, 2, 2, 0, 2, 4, 0, 3, 4, 1, 1, 4, 1, 3, +	4, 2, 1, 1, 1, 1, 4, 3, 4, 2, 3, 0, 0, 3, 0, 2, 3, 0, 2, 4, +	4, 2, 4, 3, 0, 4, 0, 2, 3, 1, 4, 1, 3, 1, 0, 0, 2, 1, 3, 2 +}; + +static const uint8 kCopyProtectionObfuscate[4] = { +	0, 1, 2, 3 +}; + +namespace Gob { + +namespace OnceUpon { + +const OnceUpon::MenuButton BabaYaga::kAnimalsButtons = { +	true, 131, 127, 183, 164, 193, 0, 245, 37, 131, 127, 0 +}; + +const OnceUpon::MenuButton BabaYaga::kAnimalButtons[] = { +	{false,  34,  84,  92, 127,  34,  84,  92, 127, 131, 25, 0}, +	{false, 114,  65, 172, 111, 114,  65, 172, 111, 131, 25, 1}, +	{false, 186,  72, 227,  96, 186,  72, 227,  96, 139, 25, 2}, +	{false, 249,  87, 282, 112, 249,  87, 282, 112, 143, 25, 3}, +	{false, 180,  97, 234, 138, 180,  97, 234, 138, 133, 25, 4}, +	{false, 197, 145, 242, 173, 197, 145, 242, 173, 137, 25, 5}, +	{false, 113, 156, 171, 176, 113, 156, 171, 176, 131, 25, 6}, +	{false, 114, 127, 151, 150, 114, 127, 151, 150, 141, 25, 7}, +	{false,  36, 136,  94, 176,  36, 136,  94, 176, 131, 25, 8}, +	{false, 245, 123, 293, 155, 245, 123, 293, 155, 136, 25, 9} +}; + +const char *BabaYaga::kAnimalNames[] = { +	"vaut", +	"drag", +	"arai", +	"gren", +	"fauc", +	"abei", +	"serp", +	"tort", +	"sang", +	"rena" +}; + +// The houses where the stork can drop a bundle +const OnceUpon::MenuButton BabaYaga::kStorkHouses[] = { +	{false,  16,  80,  87, 125, 0, 0, 0, 0, 0, 0, 0}, // Castle , Lord & Lady +	{false,  61, 123,  96, 149, 0, 0, 0, 0, 0, 0, 1}, // Cottage, Farmers +	{false, 199, 118, 226, 137, 0, 0, 0, 0, 0, 0, 2}, // Hut    , Woodcutters +	{false, 229,  91, 304, 188, 0, 0, 0, 0, 0, 0, 3}  // Palace , King & Queen +}; + +// The stork bundle drop parameters +const Stork::BundleDrop BabaYaga::kStorkBundleDrops[] = { +	{ 14,  35, 129,  true }, +	{ 14,  70, 148,  true }, +	{ 14, 206, 136,  true }, +	{ 11, 260, 225, false } +}; + +// Parameters for the stork section. +const OnceUpon::StorkParam BabaYaga::kStorkParam = { +	"present2.cmp", ARRAYSIZE(kStorkHouses), kStorkHouses, kStorkBundleDrops +}; + + +BabaYaga::BabaYaga(GobEngine *vm) : OnceUpon(vm) { +} + +BabaYaga::~BabaYaga() { +} + +void BabaYaga::run() { +	init(); + +	// Copy protection +	bool correctCP = doCopyProtection(kCopyProtectionColors, kCopyProtectionShapes, kCopyProtectionObfuscate); +	if (_vm->shouldQuit() || !correctCP) +		return; + +	// Show the intro +	showIntro(); +	if (_vm->shouldQuit()) +		return; + +	// Handle the start menu +	doStartMenu(&kAnimalsButtons, ARRAYSIZE(kAnimalButtons), kAnimalButtons, kAnimalNames); +	if (_vm->shouldQuit()) +		return; + +	// Play the actual game +	playGame(); +} + +const OnceUpon::StorkParam &BabaYaga::getStorkParameters() const { +	return kStorkParam; +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/babayaga.h b/engines/gob/pregob/onceupon/babayaga.h new file mode 100644 index 0000000000..0241f78f4e --- /dev/null +++ b/engines/gob/pregob/onceupon/babayaga.h @@ -0,0 +1,61 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GOB_PREGOB_ONCEUPON_BABAYAGA_H +#define GOB_PREGOB_ONCEUPON_BABAYAGA_H + +#include "gob/pregob/onceupon/onceupon.h" + +namespace Gob { + +namespace OnceUpon { + +class BabaYaga : public OnceUpon { +public: +	BabaYaga(GobEngine *vm); +	~BabaYaga(); + +	void run(); + +protected: +	const StorkParam &getStorkParameters() const; + +private: +	/** Definition of the menu button that leads to the animal names screen. */ +	static const MenuButton kAnimalsButtons; + +	/** Definition of the buttons that make up the animals in the animal names screen. */ +	static const MenuButton kAnimalButtons[]; +	/** File prefixes for the name of each animal. */ +	static const char *kAnimalNames[]; + +	// Parameters for the stork section. +	static const MenuButton kStorkHouses[]; +	static const Stork::BundleDrop kStorkBundleDrops[]; +	static const struct StorkParam kStorkParam; +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_BABAYAGA_H diff --git a/engines/gob/pregob/onceupon/brokenstrings.h b/engines/gob/pregob/onceupon/brokenstrings.h new file mode 100644 index 0000000000..89acb1c6bd --- /dev/null +++ b/engines/gob/pregob/onceupon/brokenstrings.h @@ -0,0 +1,60 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GOB_PREGOB_ONCEUPON_BROKENSTRINGS_H +#define GOB_PREGOB_ONCEUPON_BROKENSTRINGS_H + +struct BrokenString { +	const char *wrong; +	const char *correct; +}; + +struct BrokenStringLanguage { +	const BrokenString *strings; +	uint count; +}; + +static const BrokenString kBrokenStringsGerman[] = { +	{ "Zeichungen von Kaki,"         , "Zeichnungen von Kaki,"        }, +	{ "die es in seine Wachtr\204ume", "die es in seine Tagtr\204ume" }, +	{ "   Spielerfahrung"            , "    Spielerfahren"            }, +	{ "  Fortgeschrittene"           , "  Fortgeschritten"            }, +	{ "die Vespe"                    , "die Wespe"                    }, +	{ "das Rhinoceros"               , "das Rhinozeros"               }, +	{ "die Heusschrecke"             , "die Heuschrecke"              }, +	{ "Das, von Drachen gebrachte"   , "Das vom Drachen gebrachte"    }, +	{ "Am Waldesrand es sieht"       , "Am Waldesrand sieht es"       }, +	{ " das Kind den Palast."        , "das Kind den Palast."         }, +	{ "Am Waldessaum sieht"          , "Am Waldesrand sieht"          }, +	{ "tipp auf ESC!"                , "dr\201cke ESC!"               }, +	{ "Wohin fliegt der Drachen?"    , "Wohin fliegt der Drache?"     } +}; + +static const BrokenStringLanguage kBrokenStrings[kLanguageCount] = { +	{                    0,                               0 }, // French +	{ kBrokenStringsGerman, ARRAYSIZE(kBrokenStringsGerman) }, // German +	{                    0,                               0 }, // English +	{                    0,                               0 }, // Spanish +	{                    0,                               0 }, // Italian +}; + +#endif // GOB_PREGOB_ONCEUPON_BROKENSTRINGS_H diff --git a/engines/gob/pregob/onceupon/chargenchild.cpp b/engines/gob/pregob/onceupon/chargenchild.cpp new file mode 100644 index 0000000000..ba099e4937 --- /dev/null +++ b/engines/gob/pregob/onceupon/chargenchild.cpp @@ -0,0 +1,117 @@ +/* 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 "gob/surface.h" +#include "gob/anifile.h" + +#include "gob/pregob/onceupon/chargenchild.h" + +enum Animation { +	kAnimWalkLeft  =  0, +	kAnimWalkRight =  1, +	kAnimJumpLeft  =  2, +	kAnimJumpRight =  3, +	kAnimTapFoot   = 14 +}; + +namespace Gob { + +namespace OnceUpon { + +CharGenChild::CharGenChild(const ANIFile &ani) : ANIObject(ani) { +	setPosition(265, 110); +	setAnimation(kAnimWalkLeft); +	setVisible(true); +	setPause(false); +} + +CharGenChild::~CharGenChild() { +} + +void CharGenChild::advance() { +	bool wasLastFrame = lastFrame(); + +	ANIObject::advance(); + +	int16 x, y, left, top, width, height; +	getPosition(x, y); +	getFramePosition(left, top); +	getFrameSize(width, height); + +	const int16 right = left + width - 1; + +	switch (getAnimation()) { +	case kAnimWalkLeft: +		if (left <= 147) +			setAnimation(kAnimWalkRight); +		break; + +	case kAnimWalkRight: +		if (right >= 290) { +			setAnimation(kAnimJumpLeft); + +			setPosition(x, y - 14); +		} +		break; + +	case kAnimJumpLeft: +		if (wasLastFrame) { +			setAnimation(kAnimTapFoot); + +			setPosition(x, y - 10); +		} +		break; + +	case kAnimTapFoot: +		if (wasLastFrame) { +			setAnimation(kAnimJumpRight); + +			setPosition(x, y + 10); +		} +		break; + +	case kAnimJumpRight: +		if (wasLastFrame) { +			setAnimation(kAnimWalkLeft); + +			setPosition(x, y + 14); +		} +		break; +	} +} + +CharGenChild::Sound CharGenChild::shouldPlaySound() const { +	const uint16 anim  = getAnimation(); +	const uint16 frame = getFrame(); + +	if (((anim == kAnimWalkLeft) || (anim == kAnimWalkRight)) && ((frame == 1) || (frame == 6))) +		return kSoundWalk; + +	if (((anim == kAnimJumpLeft) || (anim == kAnimJumpRight)) &&  (frame == 0)) +		return kSoundJump; + +	return kSoundNone; +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/chargenchild.h b/engines/gob/pregob/onceupon/chargenchild.h new file mode 100644 index 0000000000..3b09ef112a --- /dev/null +++ b/engines/gob/pregob/onceupon/chargenchild.h @@ -0,0 +1,60 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GOB_PREGOB_ONCEUPON_CHARGENCHILD_H +#define GOB_PREGOB_ONCEUPON_CHARGENCHILD_H + +#include "common/system.h" + +#include "gob/aniobject.h" + +namespace Gob { + +class Surface; +class ANIFile; + +namespace OnceUpon { + +/** The child running around on the character generator screen. */ +class CharGenChild : public ANIObject { +public: +	enum Sound { +		kSoundNone = 0, +		kSoundWalk    , +		kSoundJump +	}; + +	CharGenChild(const ANIFile &ani); +	~CharGenChild(); + +	/** Advance the animation to the next frame. */ +	void advance(); + +	/** Should we play a sound right now? */ +	Sound shouldPlaySound() const; +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_CHARGENCHILD_H diff --git a/engines/gob/pregob/onceupon/onceupon.cpp b/engines/gob/pregob/onceupon/onceupon.cpp new file mode 100644 index 0000000000..e4c2df34c0 --- /dev/null +++ b/engines/gob/pregob/onceupon/onceupon.cpp @@ -0,0 +1,1904 @@ +/* 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 "gob/gob.h" +#include "gob/global.h" +#include "gob/util.h" +#include "gob/dataio.h" +#include "gob/surface.h" +#include "gob/draw.h" +#include "gob/video.h" +#include "gob/anifile.h" +#include "gob/aniobject.h" + +#include "gob/sound/sound.h" + +#include "gob/pregob/txtfile.h" +#include "gob/pregob/gctfile.h" + +#include "gob/pregob/onceupon/onceupon.h" +#include "gob/pregob/onceupon/palettes.h" +#include "gob/pregob/onceupon/title.h" +#include "gob/pregob/onceupon/parents.h" +#include "gob/pregob/onceupon/chargenchild.h" + +static const uint kLanguageCount = 5; + +static const uint kCopyProtectionHelpStringCount = 3; + +static const char *kCopyProtectionHelpStrings[Gob::OnceUpon::OnceUpon::kLanguageCount][kCopyProtectionHelpStringCount] = { +	{ // French +		"Consulte le livret des animaux, rep\212re la", +		"page correspondant \205 la couleur de l\'\202cran", +		"et clique le symbole associ\202 \205 l\'animal affich\202.", +	}, +	{ // German +		"Suche im Tieralbum die Seite, die der Farbe auf", +		"dem Bildschirm entspricht und klicke auf das", +		"Tiersymbol.", +	}, +	{ // English +		"Consult the book of animals, find the page", +		"corresponding to the colour of screen and click", +		"the symbol associated with the animal displayed.", +	}, +	{ // Spanish +		"Consulta el libro de los animales, localiza la ", +		"p\240gina que corresponde al color de la pantalla.", +		"Cliquea el s\241mbolo asociado al animal que aparece.", +	}, +	{ // Italian +		"Guarda il libretto degli animali, trova la", +		"pagina che corrisponde al colore dello schermo,", +		"clicca il simbolo associato all\'animale presentato", +	} +}; + +static const char *kCopyProtectionWrongStrings[Gob::OnceUpon::OnceUpon::kLanguageCount] = { +	"Tu t\'es tromp\202, dommage...", // French +	"Schade, du hast dich geirrt."  , // German +	"You are wrong, what a pity!"   , // English +	"Te equivocas, l\240stima..."   , // Spanish +	"Sei Sbagliato, peccato..."       // Italian +}; + +static const uint kCopyProtectionShapeCount = 5; + +static const int16 kCopyProtectionShapeCoords[kCopyProtectionShapeCount][6] = { +	{  0,  51,  26,  75,  60, 154}, +	{ 28,  51,  58,  81,  96, 151}, +	{ 60,  51,  94,  79, 136, 152}, +	{ 96,  51, 136,  71, 180, 155}, +	{140,  51, 170,  77, 228, 153} +}; + +enum ClownAnimation { +	kClownAnimationClownCheer = 0, +	kClownAnimationClownStand = 1, +	kClownAnimationClownCry   = 6 +}; + +// 12 seconds delay for one area full of GCT text +static const uint32 kGCTDelay = 12000; + +namespace Gob { + +namespace OnceUpon { + +const OnceUpon::MenuButton OnceUpon::kMainMenuDifficultyButton[] = { +	{false,  29, 18,  77, 57, 0, 0, 0, 0, 0, 0, (int)kDifficultyBeginner}, +	{false, 133, 18, 181, 57, 0, 0, 0, 0, 0, 0, (int)kDifficultyIntermediate}, +	{false, 241, 18, 289, 57, 0, 0, 0, 0, 0, 0, (int)kDifficultyAdvanced}, +}; + +const OnceUpon::MenuButton OnceUpon::kSectionButtons[] = { +	{false,  27, 121,  91, 179,   0, 0,   0,  0,   0,   0,  0}, +	{ true,  95, 121, 159, 179,   4, 1,  56, 49, 100, 126,  2}, +	{ true, 163, 121, 227, 179,  64, 1, 120, 49, 168, 126,  6}, +	{ true, 231, 121, 295, 179, 128, 1, 184, 49, 236, 126, 10} +}; + +const OnceUpon::MenuButton OnceUpon::kIngameButtons[] = { +	{true, 108, 83, 139, 116,   0,   0,  31,  34, 108,  83, 0}, +	{true, 144, 83, 175, 116,  36,   0,  67,  34, 144,  83, 1}, +	{true, 180, 83, 211, 116,  72,   0, 103,  34, 180,  83, 2} +}; + +const OnceUpon::MenuButton OnceUpon::kAnimalNamesBack = { +	true, 19, 13, 50, 46, 36, 0, 67, 34, 19, 13, 1 +}; + +const OnceUpon::MenuButton OnceUpon::kLanguageButtons[] = { +	{true,  43,  80,  93, 115,   0, 55,  50, 90,  43,  80, 0}, +	{true, 132,  80, 182, 115,  53, 55, 103, 90, 132,  80, 1}, +	{true, 234,  80, 284, 115, 106, 55, 156, 90, 234,  80, 2}, +	{true,  43, 138,  93, 173, 159, 55, 209, 90,  43, 138, 3}, +	{true, 132, 138, 182, 173, 212, 55, 262, 90, 132, 138, 4}, +	{true, 234, 138, 284, 173, 265, 55, 315, 90, 234, 138, 2} +}; + +const char *OnceUpon::kSound[kSoundCount] = { +	"diamant.snd", // kSoundClick +	"cigogne.snd", // kSoundStork +	"saute.snd"    // kSoundJump +}; + +const OnceUpon::SectionFunc OnceUpon::kSectionFuncs[kSectionCount] = { +	&OnceUpon::sectionStork, +	&OnceUpon::sectionChapter1, +	&OnceUpon::sectionParents, +	&OnceUpon::sectionChapter2, +	&OnceUpon::sectionForest0, +	&OnceUpon::sectionChapter3, +	&OnceUpon::sectionEvilCastle, +	&OnceUpon::sectionChapter4, +	&OnceUpon::sectionForest1, +	&OnceUpon::sectionChapter5, +	&OnceUpon::sectionBossFight, +	&OnceUpon::sectionChapter6, +	&OnceUpon::sectionForest2, +	&OnceUpon::sectionChapter7, +	&OnceUpon::sectionEnd +}; + + +OnceUpon::ScreenBackup::ScreenBackup() : palette(-1), changedCursor(false), cursorVisible(false) { +	screen = new Surface(320, 200, 1); +} + +OnceUpon::ScreenBackup::~ScreenBackup() { +	delete screen; +} + + +OnceUpon::OnceUpon(GobEngine *vm) : PreGob(vm), _openedArchives(false), +	_jeudak(0), _lettre(0), _plettre(0), _glettre(0) { + +} + +OnceUpon::~OnceUpon() { +	deinit(); +} + +void OnceUpon::init() { +	deinit(); + +	// Open data files + +	bool hasSTK1 = _vm->_dataIO->openArchive("stk1.stk", true); +	bool hasSTK2 = _vm->_dataIO->openArchive("stk2.stk", true); +	bool hasSTK3 = _vm->_dataIO->openArchive("stk3.stk", true); + +	if (!hasSTK1 || !hasSTK2 || !hasSTK3) +		error("OnceUpon::OnceUpon(): Failed to open archives"); + +	_openedArchives = true; + +	// Open fonts + +	_jeudak  = _vm->_draw->loadFont("jeudak.let"); +	_lettre  = _vm->_draw->loadFont("lettre.let"); +	_plettre = _vm->_draw->loadFont("plettre.let"); +	_glettre = _vm->_draw->loadFont("glettre.let"); + +	if (!_jeudak || !_lettre || !_plettre || !_glettre) +		error("OnceUpon::OnceUpon(): Failed to fonts (%d, %d, %d, %d)", +		      _jeudak != 0, _lettre != 0, _plettre != 0, _glettre != 0); + +	// Verify the language + +	if (_vm->_global->_language == kLanguageAmerican) +		_vm->_global->_language = kLanguageBritish; + +	if ((_vm->_global->_language >= kLanguageCount)) +		error("We do not support the language \"%s\".\n" +		      "If you are certain that your game copy includes this language,\n" +		      "please contact the ScummVM team with details about this version.\n" +		      "Thanks", _vm->getLangDesc(_vm->_global->_language)); + +	// Load all our sounds and init the screen + +	loadSounds(kSound, kSoundCount); +	initScreen(); + +	// We start with an invalid palette +	_palette = -1; + +	// No quit requested at start +	_quit = false; + +	// We start with no selected difficulty and at section 0 +	_difficulty = kDifficultyCount; +	_section    = 0; + +	// Default name +	_name = "Nemo"; + +	// Default character properties +	_house         = 0; +	_head          = 0; +	_colorHair     = 0; +	_colorJacket   = 0; +	_colorTrousers = 0; +} + +void OnceUpon::deinit() { +	// Free sounds +	freeSounds(); + +	// Free fonts + +	delete _jeudak; +	delete _lettre; +	delete _plettre; +	delete _glettre; + +	_jeudak  = 0; +	_lettre  = 0; +	_plettre = 0; +	_glettre = 0; + +	// Close archives + +	if (_openedArchives) { +		_vm->_dataIO->closeArchive(true); +		_vm->_dataIO->closeArchive(true); +		_vm->_dataIO->closeArchive(true); +	} + +	_openedArchives = false; +} + +void OnceUpon::setGamePalette(uint palette) { +	if (palette >= kPaletteCount) +		return; + +	_palette = palette; + +	setPalette(kGamePalettes[palette], kPaletteSize); +} + +void OnceUpon::setGameCursor() { +	Surface cursor(320, 16, 1); + +	// Set the default game cursor +	_vm->_video->drawPackedSprite("icon.cmp", cursor); +	setCursor(cursor, 105, 0, 120, 15, 0, 0); +} + +void OnceUpon::drawLineByLine(const Surface &src, int16 left, int16 top, int16 right, int16 bottom, +                              int16 x, int16 y) const { + +	// A special way of drawing something: +	// Draw every other line "downwards", wait a bit after each line +	// Then, draw the remaining lines "upwards" and again wait a bit after each line. + +	if (_vm->shouldQuit()) +		return; + +	const int16 width  = right  - left + 1; +	const int16 height = bottom - top  + 1; + +	if ((width <= 0) || (height <= 0)) +		return; + +	// Draw the even lines downwards +	for (int16 i = 0; i < height; i += 2) { +		if (_vm->shouldQuit()) +			return; + +		_vm->_draw->_backSurface->blit(src, left, top + i, right, top + i, x, y + i); + +		_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, x, y + i, x + width - 1, y + 1); +		_vm->_draw->blitInvalidated(); + +		_vm->_util->longDelay(1); +	} + +	// Draw the odd lines upwards +	for (int16 i = (height & 1) ? height : (height - 1); i >= 0; i -= 2) { +		if (_vm->shouldQuit()) +			return; + +		_vm->_draw->_backSurface->blit(src, left, top + i, right, top + i, x, y + i); + +		_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, x, y + i, x + width - 1, y + 1); +		_vm->_draw->blitInvalidated(); + +		_vm->_util->longDelay(1); +	} +} + +void OnceUpon::backupScreen(ScreenBackup &backup, bool setDefaultCursor) { +	// Backup the screen and palette +	backup.screen->blit(*_vm->_draw->_backSurface); +	backup.palette = _palette; + +	// Backup the cursor + +	backup.cursorVisible = isCursorVisible(); + +	backup.changedCursor = false; +	if (setDefaultCursor) { +		backup.changedCursor = true; + +		addCursor(); +		setGameCursor(); +	} +} + +void OnceUpon::restoreScreen(ScreenBackup &backup) { +	if (_vm->shouldQuit()) +		return; + +	// Restore the screen +	_vm->_draw->_backSurface->blit(*backup.screen); +	_vm->_draw->forceBlit(); + +	// Restore the palette +	if (backup.palette >= 0) +		setGamePalette(backup.palette); + +	// Restore the cursor + +	if (!backup.cursorVisible) +		hideCursor(); + +	if (backup.changedCursor) +		removeCursor(); + +	backup.changedCursor = false; +} + +void OnceUpon::fixTXTStrings(TXTFile &txt) const { +	TXTFile::LineArray &lines = txt.getLines(); +	for (uint i = 0; i < lines.size(); i++) +		lines[i].text = fixString(lines[i].text); +} + +#include "gob/pregob/onceupon/brokenstrings.h" +Common::String OnceUpon::fixString(const Common::String &str) const { +	const BrokenStringLanguage &broken = kBrokenStrings[_vm->_global->_language]; + +	for (uint i = 0; i < broken.count; i++) { +		if (str == broken.strings[i].wrong) +			return broken.strings[i].correct; +	} + +	return str; +} + +enum ClownAnimation { +	kClownAnimationStand = 0, +	kClownAnimationCheer = 1, +	kClownAnimationCry   = 2 +}; + +const PreGob::AnimProperties OnceUpon::kClownAnimations[] = { +	{ 1, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, +	{ 0, 0, ANIObject::kModeOnce      , true, false, false, 0, 0}, +	{ 6, 0, ANIObject::kModeOnce      , true, false, false, 0, 0} +}; + +enum CopyProtectionState { +	kCPStateSetup,     // Set up the screen +	kCPStateWaitUser,  // Waiting for the user to pick a shape +	kCPStateWaitClown, // Waiting for the clown animation to finish +	kCPStateFinish     // Finishing +}; + +bool OnceUpon::doCopyProtection(const uint8 colors[7], const uint8 shapes[7 * 20], const uint8 obfuscate[4]) { +	fadeOut(); +	setPalette(kCopyProtectionPalette, kPaletteSize); + +	// Load the copy protection sprites +	Surface sprites[2] = {Surface(320, 200, 1), Surface(320, 200, 1)}; + +	_vm->_video->drawPackedSprite("grille1.cmp", sprites[0]); +	_vm->_video->drawPackedSprite("grille2.cmp", sprites[1]); + +	// Load the clown animation +	ANIFile ani  (_vm, "grille.ani", 320); +	ANIList anims; + +	loadAnims(anims, ani, 1, &kClownAnimations[kClownAnimationStand]); + +	// Set the copy protection cursor +	setCursor(sprites[1], 5, 110, 20, 134, 3, 0); + +	// We start with 2 tries left, not having a correct answer and the copy protection not set up yet +	CopyProtectionState state = kCPStateSetup; + +	uint8 triesLeft   =  2; +	int8  animalShape = -1; +	bool  hasCorrect  = false; + +	while (!_vm->shouldQuit() && (state != kCPStateFinish)) { +		clearAnim(anims); + +		// Set up the screen +		if (state == kCPStateSetup) { +			animalShape = cpSetup(colors, shapes, obfuscate, sprites); + +			setAnim(*anims[0], kClownAnimations[kClownAnimationStand]); +			state = kCPStateWaitUser; +		} + +		drawAnim(anims); + +		// If we're waiting for the clown and he finished, evaluate if we're finished +		if (!anims[0]->isVisible() && (state == kCPStateWaitClown)) +			state = (hasCorrect || (--triesLeft == 0)) ? kCPStateFinish : kCPStateSetup; + +		showCursor(); +		fadeIn(); + +		endFrame(true); + +		int16 mouseX, mouseY; +		MouseButtons mouseButtons; + +		checkInput(mouseX, mouseY, mouseButtons); + +		if (state == kCPStateWaitUser) { +			// Look if we clicked a shaped and got it right + +			int8 guessedShape = -1; +			if (mouseButtons == kMouseButtonsLeft) +				guessedShape = cpFindShape(mouseX, mouseY); + +			if (guessedShape >= 0) { +				hasCorrect  = guessedShape == animalShape; +				animalShape = -1; + +				setAnim(*anims[0], kClownAnimations[hasCorrect ? kClownAnimationCheer : kClownAnimationCry]); +				state = kCPStateWaitClown; +			} +		} +	} + +	freeAnims(anims); + +	fadeOut(); +	hideCursor(); +	clearScreen(); + +	// Display the "You are wrong" screen +	if (!hasCorrect) +		cpWrong(); + +	return hasCorrect; +} + +int8 OnceUpon::cpSetup(const uint8 colors[7], const uint8 shapes[7 * 20], const uint8 obfuscate[4], +                       const Surface sprites[2]) { + +	fadeOut(); +	hideCursor(); + +	// Get a random animal and animal color +	int8 animalColor = _vm->_util->getRandom(7); +	while ((colors[animalColor] == 1) || (colors[animalColor] == 7) || (colors[animalColor] == 11)) +		animalColor = _vm->_util->getRandom(7); + +	int8 animal = _vm->_util->getRandom(20); + +	int8 animalShape = shapes[animalColor * 20 + animal]; +	if (animal < 4) +		animal = obfuscate[animal]; + +	// Get the position of the animal sprite +	int16 animalLeft = (animal % 4) * 80; +	int16 animalTop  = (animal / 4) * 50; + +	uint8 sprite = 0; +	if (animalTop >= 200) { +		animalTop -= 200; +		sprite = 1; +	} + +	int16 animalRight  = animalLeft + 80 - 1; +	int16 animalBottom = animalTop  + 50 - 1; + +	// Fill with the animal color +	_vm->_draw->_backSurface->fill(colors[animalColor]); + +	// Print the help line strings +	for (uint i = 0; i < kCopyProtectionHelpStringCount; i++) { +		const char * const helpString = kCopyProtectionHelpStrings[_vm->_global->_language][i]; + +		const int x = 160 - (strlen(helpString) * _plettre->getCharWidth()) / 2; +		const int y = i * 10 + 5; + +		_plettre->drawString(helpString, x, y, 8, 0, true, *_vm->_draw->_backSurface); +	} + +	// White rectangle with black border +	_vm->_draw->_backSurface->fillRect( 93, 43, 226, 134, 15); +	_vm->_draw->_backSurface->drawRect( 92, 42, 227, 135,  0); + +	// Draw the animal in the animal color +	_vm->_draw->_backSurface->fillRect(120, 63, 199, 112, colors[animalColor]); +	_vm->_draw->_backSurface->blit(sprites[sprite], animalLeft, animalTop, animalRight, animalBottom, 120, 63, 0); + +	// Draw the shapes +	for (uint i = 0; i < kCopyProtectionShapeCount; i++) { +		const int16 * const coords = kCopyProtectionShapeCoords[i]; + +		_vm->_draw->_backSurface->blit(sprites[1], coords[0], coords[1], coords[2], coords[3], coords[4], coords[5], 0); +	} + +	_vm->_draw->forceBlit(); + +	return animalShape; +} + +int8 OnceUpon::cpFindShape(int16 x, int16 y) const { +	// Look through all shapes and check if the coordinates are inside one of them +	for (uint i = 0; i < kCopyProtectionShapeCount; i++) { +		const int16 * const coords = kCopyProtectionShapeCoords[i]; + +		const int16 left   = coords[4]; +		const int16 top    = coords[5]; +		const int16 right  = coords[4] + (coords[2] - coords[0] + 1) - 1; +		const int16 bottom = coords[5] + (coords[3] - coords[1] + 1) - 1; + +		if ((x >= left) && (x <= right) && (y >= top) && (y <= bottom)) +			return i; +	} + +	return -1; +} + +void OnceUpon::cpWrong() { +	// Display the "You are wrong" string, centered + +	const char * const wrongString = kCopyProtectionWrongStrings[_vm->_global->_language]; +	const int          wrongX      = 160 - (strlen(wrongString) * _plettre->getCharWidth()) / 2; + +	_vm->_draw->_backSurface->clear(); +	_plettre->drawString(wrongString, wrongX, 100, 15, 0, true, *_vm->_draw->_backSurface); + +	_vm->_draw->forceBlit(); + +	fadeIn(); + +	waitInput(); + +	fadeOut(); +	clearScreen(); +} + +void OnceUpon::showIntro() { +	// Show all intro parts + +	// "Loading" +	showWait(10); +	if (_vm->shouldQuit()) +		return; + +	// Quote about fairy tales +	showQuote(); +	if (_vm->shouldQuit()) +		return; + +	// Once Upon A Time title +	showTitle(); +	if (_vm->shouldQuit()) +		return; + +	// Game title screen +	showChapter(0); +	if (_vm->shouldQuit()) +		return; + +	// "Loading" +	showWait(17); +} + +void OnceUpon::showWait(uint palette) { +	// Show the loading floppy + +	fadeOut(); +	clearScreen(); +	setGamePalette(palette); + +	Surface wait(320, 43, 1); + +	_vm->_video->drawPackedSprite("wait.cmp", wait); +	_vm->_draw->_backSurface->blit(wait, 0, 0, 72, 33, 122, 84); + +	_vm->_draw->forceBlit(); + +	fadeIn(); +} + +void OnceUpon::showQuote() { +	// Show the quote about fairytales + +	fadeOut(); +	clearScreen(); +	setGamePalette(11); + +	static const Font *fonts[3] = { _plettre, _glettre, _plettre }; + +	TXTFile *quote = loadTXT(getLocFile("gene.tx"), TXTFile::kFormatStringPositionColorFont); +	quote->draw(*_vm->_draw->_backSurface, fonts, ARRAYSIZE(fonts)); +	delete quote; + +	_vm->_draw->forceBlit(); + +	fadeIn(); + +	waitInput(); + +	fadeOut(); +} + +const PreGob::AnimProperties OnceUpon::kTitleAnimation = { +	8, 0, ANIObject::kModeContinuous, true, false, false, 0, 0 +}; + +void OnceUpon::showTitle() { +	fadeOut(); +	setGamePalette(10); + +	Title title(_vm); +	title.play(); +} + +void OnceUpon::showChapter(int chapter) { +	// Display the intro text to a chapter + +	fadeOut(); +	clearScreen(); +	setGamePalette(11); + +	// Parchment background +	_vm->_video->drawPackedSprite("parch.cmp", *_vm->_draw->_backSurface); + +	static const Font *fonts[3] = { _plettre, _glettre, _plettre }; + +	const Common::String chapterFile = getLocFile(Common::String::format("gene%d.tx", chapter)); + +	TXTFile *gameTitle = loadTXT(chapterFile, TXTFile::kFormatStringPositionColorFont); +	gameTitle->draw(*_vm->_draw->_backSurface, fonts, ARRAYSIZE(fonts)); +	delete gameTitle; + +	_vm->_draw->forceBlit(); + +	fadeIn(); + +	waitInput(); + +	fadeOut(); +} + +void OnceUpon::showByeBye() { +	fadeOut(); +	hideCursor(); +	clearScreen(); +	setGamePalette(1); + +	_plettre->drawString("Bye Bye....", 140, 80, 2, 0, true, *_vm->_draw->_backSurface); +	_vm->_draw->forceBlit(); + +	fadeIn(); + +	_vm->_util->longDelay(1000); + +	fadeOut(); +} + +void OnceUpon::doStartMenu(const MenuButton *animalsButton, uint animalCount, +                           const MenuButton *animalButtons, const char * const *animalNames) { +	clearScreen(); + +	// Wait until we clicked on of the difficulty buttons and are ready to start playing +	while (!_vm->shouldQuit()) { +		MenuAction action = handleStartMenu(animalsButton); +		if (action == kMenuActionPlay) +			break; + +		// If we pressed the "listen to animal names" button, handle that screen +		if (action == kMenuActionAnimals) +			handleAnimalNames(animalCount, animalButtons, animalNames); +	} +} + +OnceUpon::MenuAction OnceUpon::handleStartMenu(const MenuButton *animalsButton) { +	ScreenBackup screenBackup; +	backupScreen(screenBackup, true); + +	fadeOut(); +	setGamePalette(17); +	drawStartMenu(animalsButton); +	showCursor(); +	fadeIn(); + +	MenuAction action = kMenuActionNone; +	while (!_vm->shouldQuit() && (action == kMenuActionNone)) { +		endFrame(true); + +		// Check user input + +		int16 mouseX, mouseY; +		MouseButtons mouseButtons; + +		int16 key = checkInput(mouseX, mouseY, mouseButtons); +		if (key == kKeyEscape) +			// ESC -> Quit +			return kMenuActionQuit; + +		if (mouseButtons != kMouseButtonsLeft) +			continue; + +		playSound(kSoundClick); + +		// If we clicked on a difficulty button, show the selected difficulty and start the game +		int diff = checkButton(kMainMenuDifficultyButton, ARRAYSIZE(kMainMenuDifficultyButton), mouseX, mouseY); +		if (diff >= 0) { +			_difficulty = (Difficulty)diff; +			action      = kMenuActionPlay; + +			drawStartMenu(animalsButton); +			_vm->_util->longDelay(1000); +		} + +		if (animalsButton && (checkButton(animalsButton, 1, mouseX, mouseY) != -1)) +			action = kMenuActionAnimals; + +	} + +	fadeOut(); +	restoreScreen(screenBackup); + +	return action; +} + +OnceUpon::MenuAction OnceUpon::handleMainMenu() { +	ScreenBackup screenBackup; +	backupScreen(screenBackup, true); + +	fadeOut(); +	setGamePalette(17); +	drawMainMenu(); +	showCursor(); +	fadeIn(); + +	MenuAction action = kMenuActionNone; +	while (!_vm->shouldQuit() && (action == kMenuActionNone)) { +		endFrame(true); + +		// Check user input + +		int16 mouseX, mouseY; +		MouseButtons mouseButtons; + +		int16 key = checkInput(mouseX, mouseY, mouseButtons); +		if (key == kKeyEscape) +			// ESC -> Quit +			return kMenuActionQuit; + +		if (mouseButtons != kMouseButtonsLeft) +			continue; + +		playSound(kSoundClick); + +		// If we clicked on a difficulty button, change the current difficulty level +		int diff = checkButton(kMainMenuDifficultyButton, ARRAYSIZE(kMainMenuDifficultyButton), mouseX, mouseY); +		if ((diff >= 0) && (diff != (int)_difficulty)) { +			_difficulty = (Difficulty)diff; + +			drawMainMenu(); +		} + +		// If we clicked on a section button, restart the game from this section +		int section = checkButton(kSectionButtons, ARRAYSIZE(kSectionButtons), mouseX, mouseY); +		if ((section >= 0) && (section <= _section)) { +			_section = section; +			action   = kMenuActionRestart; +		} + +	} + +	fadeOut(); +	restoreScreen(screenBackup); + +	return action; +} + +OnceUpon::MenuAction OnceUpon::handleIngameMenu() { +	ScreenBackup screenBackup; +	backupScreen(screenBackup, true); + +	drawIngameMenu(); +	showCursor(); + +	MenuAction action = kMenuActionNone; +	while (!_vm->shouldQuit() && (action == kMenuActionNone)) { +		endFrame(true); + +		// Check user input + +		int16 mouseX, mouseY; +		MouseButtons mouseButtons; + +		int16 key = checkInput(mouseX, mouseY, mouseButtons); +		if ((key == kKeyEscape) || (mouseButtons == kMouseButtonsRight)) +			// ESC or right mouse button -> Dismiss the menu +			action = kMenuActionPlay; + +		if (mouseButtons != kMouseButtonsLeft) +			continue; + +		int button = checkButton(kIngameButtons, ARRAYSIZE(kIngameButtons), mouseX, mouseY); +		if      (button == 0) +			action = kMenuActionQuit; +		else if (button == 1) +			action = kMenuActionMainMenu; +		else if (button == 2) +			action = kMenuActionPlay; + +	} + +	clearIngameMenu(*screenBackup.screen); +	restoreScreen(screenBackup); + +	return action; +} + +void OnceUpon::drawStartMenu(const MenuButton *animalsButton) { +	// Draw the background +	_vm->_video->drawPackedSprite("menu2.cmp", *_vm->_draw->_backSurface); + +	// Draw the "Listen to animal names" button +	if (animalsButton) { +		Surface elements(320, 38, 1); +		_vm->_video->drawPackedSprite("elemenu.cmp", elements); +		_vm->_draw->_backSurface->fillRect(animalsButton->left , animalsButton->top, +		                                   animalsButton->right, animalsButton->bottom, 0); +		drawButton(*_vm->_draw->_backSurface, elements, *animalsButton); +	} + +	// Highlight the current difficulty +	drawMenuDifficulty(); + +	_vm->_draw->forceBlit(); +} + +void OnceUpon::drawMainMenu() { +	// Draw the background +	_vm->_video->drawPackedSprite("menu.cmp", *_vm->_draw->_backSurface); + +	// Highlight the current difficulty +	drawMenuDifficulty(); + +	// Draw the section buttons +	Surface elements(320, 200, 1); +	_vm->_video->drawPackedSprite("elemenu.cmp", elements); + +	for (uint i = 0; i < ARRAYSIZE(kSectionButtons); i++) { +		const MenuButton &button = kSectionButtons[i]; + +		if (!button.needDraw) +			continue; + +		if (_section >= (int)button.id) +			drawButton(*_vm->_draw->_backSurface, elements, button); +	} + +	_vm->_draw->forceBlit(); +} + +void OnceUpon::drawIngameMenu() { +	Surface menu(320, 34, 1); +	_vm->_video->drawPackedSprite("icon.cmp", menu); + +	// Draw the menu in a special way, button by button +	for (uint i = 0; i < ARRAYSIZE(kIngameButtons); i++) { +		const MenuButton &button = kIngameButtons[i]; + +		drawLineByLine(menu, button.srcLeft, button.srcTop, button.srcRight, button.srcBottom, +		               button.dstX, button.dstY); +	} + +	_vm->_draw->forceBlit(); +	_vm->_video->retrace(); +} + +void OnceUpon::drawMenuDifficulty() { +	if (_difficulty == kDifficultyCount) +		return; + +	TXTFile *difficulties = loadTXT(getLocFile("diffic.tx"), TXTFile::kFormatStringPositionColor); + +	// Draw the difficulty name +	difficulties->draw((uint) _difficulty, *_vm->_draw->_backSurface, &_plettre, 1); + +	// Draw a border around the current difficulty +	drawButtonBorder(kMainMenuDifficultyButton[_difficulty], difficulties->getLines()[_difficulty].color); + +	delete difficulties; +} + +void OnceUpon::clearIngameMenu(const Surface &background) { +	if (_vm->shouldQuit()) +		return; + +	// Find the area encompassing the whole ingame menu + +	int16 left   = 0x7FFF; +	int16 top    = 0x7FFF; +	int16 right  = 0x0000; +	int16 bottom = 0x0000; + +	for (uint i = 0; i < ARRAYSIZE(kIngameButtons); i++) { +		const MenuButton &button = kIngameButtons[i]; + +		if (!button.needDraw) +			continue; + +		left   = MIN<int16>(left  , button.dstX); +		top    = MIN<int16>(top   , button.dstY); +		right  = MAX<int16>(right , button.dstX + (button.srcRight  - button.srcLeft + 1) - 1); +		bottom = MAX<int16>(bottom, button.dstY + (button.srcBottom - button.srcTop  + 1) - 1); +	} + +	if ((left > right) || (top > bottom)) +		return; + +	// Clear it line by line +	drawLineByLine(background, left, top, right, bottom, left, top); +} + +OnceUpon::MenuAction OnceUpon::doIngameMenu() { +	// Show the ingame menu +	MenuAction action = handleIngameMenu(); + +	if ((action == kMenuActionQuit) || _vm->shouldQuit()) { + +		// User pressed the quit button, or quit ScummVM +		_quit  = true; +		action = kMenuActionQuit; + +	} else if (action == kMenuActionPlay) { + +		// User pressed the return to game button +		action = kMenuActionPlay; + +	} else if (kMenuActionMainMenu) { + +		// User pressed the return to main menu button +		action = handleMainMenu(); +	} + +	return action; +} + +OnceUpon::MenuAction OnceUpon::doIngameMenu(int16 &key, MouseButtons &mouseButtons) { +	if ((key != kKeyEscape) && (mouseButtons != kMouseButtonsRight)) +		return kMenuActionNone; + +	key = 0; +	mouseButtons = kMouseButtonsNone; + +	MenuAction action = doIngameMenu(); +	if (action == kMenuActionPlay) +		action = kMenuActionNone; + +	return action; +} + +int OnceUpon::checkButton(const MenuButton *buttons, uint count, int16 x, int16 y, int failValue) const { +	// Look through all buttons, and return the ID of the button we're in + +	for (uint i = 0; i < count; i++) { +		const MenuButton &button = buttons[i]; + +		if ((x >= button.left) && (x <= button.right) && (y >= button.top) && (y <= button.bottom)) +			return (int)button.id; +	} + +	// We're in none of these buttons, return the fail value +	return failValue; +} + +void OnceUpon::drawButton(Surface &dest, const Surface &src, const MenuButton &button, int transp) const { +	dest.blit(src, button.srcLeft, button.srcTop, button.srcRight, button.srcBottom, button.dstX, button.dstY, transp); +} + +void OnceUpon::drawButtons(Surface &dest, const Surface &src, const MenuButton *buttons, uint count, int transp) const { +	for (uint i = 0; i < count; i++) { +		const MenuButton &button = buttons[i]; + +		if (!button.needDraw) +			continue; + +		drawButton(dest, src, button, transp); +	} +} + +void OnceUpon::drawButtonBorder(const MenuButton &button, uint8 color) { +	_vm->_draw->_backSurface->drawRect(button.left, button.top, button.right, button.bottom, color); +	_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, button.left, button.top, button.right, button.bottom); +} + +enum AnimalNamesState { +	kANStateChoose, // We're in the animal chooser +	kANStateNames,  // We're in the language chooser +	kANStateFinish  // We're finished +}; + +void OnceUpon::handleAnimalNames(uint count, const MenuButton *buttons, const char * const *names) { +	fadeOut(); +	clearScreen(); +	setGamePalette(19); + +	bool cursorVisible = isCursorVisible(); + +	// Set the cursor +	addCursor(); +	setGameCursor(); + +	anSetupChooser(); + +	int8 _animal = -1; + +	AnimalNamesState state = kANStateChoose; +	while (!_vm->shouldQuit() && (state != kANStateFinish)) { +		showCursor(); +		fadeIn(); + +		endFrame(true); + +		// Check user input + +		int16 mouseX, mouseY; +		MouseButtons mouseButtons; + +		checkInput(mouseX, mouseY, mouseButtons); + +		// If we moused over an animal button, draw a border around it +		int animal = checkButton(buttons, count, mouseX, mouseY); +		if ((state == kANStateChoose) && (animal != _animal)) { +			// Erase the old border +			if (_animal >= 0) +				drawButtonBorder(buttons[_animal], 15); + +			_animal = animal; + +			// Draw the new border +			if (_animal >= 0) +				drawButtonBorder(buttons[_animal], 10); +		} + +		if (mouseButtons != kMouseButtonsLeft) +			continue; + +		playSound(kSoundClick); + +		// We clicked on a language button, play the animal name +		int language = checkButton(kLanguageButtons, ARRAYSIZE(kLanguageButtons), mouseX, mouseY); +		if ((state == kANStateNames) && (language >= 0)) +			anPlayAnimalName(names[_animal], language); + +		// We clicked on an animal +		if ((state == kANStateChoose) && (_animal >= 0)) { +			anSetupNames(buttons[_animal]); + +			state = kANStateNames; +		} + +		// If we clicked on the back button, go back +		if (checkButton(&kAnimalNamesBack, 1, mouseX, mouseY) != -1) { +			if        (state == kANStateNames) { +				anSetupChooser(); + +				state = kANStateChoose; +			} else if (state == kANStateChoose) +				state = kANStateFinish; +		} +	} + +	fadeOut(); + +	// Restore the cursor +	if (!cursorVisible) +		hideCursor(); +	removeCursor(); +} + +void OnceUpon::anSetupChooser() { +	fadeOut(); + +	_vm->_video->drawPackedSprite("dico.cmp", *_vm->_draw->_backSurface); + +	// Draw the back button +	Surface menu(320, 34, 1); +	_vm->_video->drawPackedSprite("icon.cmp", menu); +	drawButton(*_vm->_draw->_backSurface, menu, kAnimalNamesBack); + +	// "Choose an animal" +	TXTFile *choose = loadTXT(getLocFile("choisi.tx"), TXTFile::kFormatStringPosition); +	choose->draw(*_vm->_draw->_backSurface, &_plettre, 1, 14); +	delete choose; + +	_vm->_draw->forceBlit(); +} + +void OnceUpon::anSetupNames(const MenuButton &animal) { +	fadeOut(); + +	Surface background(320, 200, 1); + +	_vm->_video->drawPackedSprite("dico.cmp", background); + +	// Draw the background and clear what we don't need +	_vm->_draw->_backSurface->blit(background); +	_vm->_draw->_backSurface->fillRect(19, 19, 302, 186, 15); + +	// Draw the back button +	Surface menu(320, 34, 1); +	_vm->_video->drawPackedSprite("icon.cmp", menu); +	drawButton(*_vm->_draw->_backSurface, menu, kAnimalNamesBack); + +	// Draw the animal +	drawButton(*_vm->_draw->_backSurface, background, animal); + +	// Draw the language buttons +	Surface elements(320, 200, 1); +	_vm->_video->drawPackedSprite("elemenu.cmp", elements); +	drawButtons(*_vm->_draw->_backSurface, elements, kLanguageButtons, ARRAYSIZE(kLanguageButtons)); + +	// Draw the language names +	_plettre->drawString("Fran\207ais",  43,  70, 10, 15, true, *_vm->_draw->_backSurface); +	_plettre->drawString("Deutsch"    , 136,  70, 10, 15, true, *_vm->_draw->_backSurface); +	_plettre->drawString("English"    , 238,  70, 10, 15, true, *_vm->_draw->_backSurface); +	_plettre->drawString("Italiano"   ,  43, 128, 10, 15, true, *_vm->_draw->_backSurface); +	_plettre->drawString("Espa\244ol" , 136, 128, 10, 15, true, *_vm->_draw->_backSurface); +	_plettre->drawString("English"    , 238, 128, 10, 15, true, *_vm->_draw->_backSurface); + +	_vm->_draw->forceBlit(); +} + +void OnceUpon::anPlayAnimalName(const Common::String &animal, uint language) { +	// Sound file to play +	Common::String soundFile = animal + "_" + kLanguageSuffixLong[language] + ".snd"; + +	// Get the name of the animal +	TXTFile *names = loadTXT(animal + ".anm", TXTFile::kFormatString); +	Common::String name = names->getLines()[language].text; +	delete names; + +	// It should be centered on the screen +	const int nameX = 160 - (name.size() * _plettre->getCharWidth()) / 2; + +	// Backup the screen surface +	Surface backup(162, 23, 1); +	backup.blit(*_vm->_draw->_backSurface, 78, 123, 239, 145, 0, 0); + +	// Draw the name border +	Surface nameBorder(162, 23, 1); +	_vm->_video->drawPackedSprite("mot.cmp", nameBorder); +	_vm->_draw->_backSurface->blit(nameBorder, 0, 0, 161, 22, 78, 123); + +	// Print the animal name +	_plettre->drawString(name, nameX, 129, 10, 0, true, *_vm->_draw->_backSurface); +	_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 78, 123, 239, 145); + +	playSoundFile(soundFile); + +	// Restore the screen +	_vm->_draw->_backSurface->blit(backup, 0, 0, 161, 22, 78, 123); +	_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 78, 123, 239, 145); +} + +void OnceUpon::playGame() { +	while (!_vm->shouldQuit() && !_quit) { +		// Play a section and advance to the next section if we finished it +		if (playSection()) +			_section = MIN(_section + 1, kSectionCount - 1); +	} + +	// If we quit through the game and not through ScummVM, show the "Bye Bye" screen +	if (!_vm->shouldQuit()) +		showByeBye(); +} + +bool OnceUpon::playSection() { +	if ((_section < 0) || (_section >= ARRAYSIZE(kSectionFuncs))) { +		_quit = true; +		return false; +	} + +	return (this->*kSectionFuncs[_section])(); +} + +const PreGob::AnimProperties OnceUpon::kSectionStorkAnimations[] = { +	{ 0, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, +	{ 1, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, +	{ 2, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, +	{ 3, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, +	{ 4, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, +	{ 5, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, +	{ 6, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, +	{ 7, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, +	{ 8, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, +	{17, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, +	{16, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, +	{15, 0, ANIObject::kModeContinuous, true, false, false, 0, 0} +}; + +enum StorkState { +	kStorkStateWaitUser, +	kStorkStateWaitBundle, +	kStorkStateFinish +}; + +bool OnceUpon::sectionStork() { +	fadeOut(); +	hideCursor(); +	setGamePalette(0); +	setGameCursor(); + +	const StorkParam ¶m = getStorkParameters(); + +	Surface backdrop(320, 200, 1); + +	// Draw the frame +	_vm->_video->drawPackedSprite("cadre.cmp", *_vm->_draw->_backSurface); + +	// Draw the backdrop +	_vm->_video->drawPackedSprite(param.backdrop, backdrop); +	_vm->_draw->_backSurface->blit(backdrop, 0, 0, 288, 175, 16, 12); + +	// "Where does the stork go?" +	TXTFile *whereStork = loadTXT(getLocFile("ouva.tx"), TXTFile::kFormatStringPositionColor); +	whereStork->draw(*_vm->_draw->_backSurface, &_plettre, 1); + +	// Where the stork actually goes +	GCTFile *thereStork = loadGCT(getLocFile("choix.gc")); +	thereStork->setArea(17, 18, 303, 41); + +	ANIFile ani(_vm, "present.ani", 320); +	ANIList anims; + +	Stork *stork = new Stork(_vm, ani); + +	loadAnims(anims, ani, ARRAYSIZE(kSectionStorkAnimations), kSectionStorkAnimations); +	anims.push_back(stork); + +	drawAnim(anims); + +	_vm->_draw->forceBlit(); + +	int8 storkSoundWait = 0; + +	StorkState state  = kStorkStateWaitUser; +	MenuAction action = kMenuActionNone; +	while (!_vm->shouldQuit() && (state != kStorkStateFinish)) { +		// Play the stork sound +		if (--storkSoundWait == 0) +			playSound(kSoundStork); +		if (storkSoundWait <= 0) +			storkSoundWait = 50 - _vm->_util->getRandom(30); + +		// Check if the bundle landed +		if ((state == kStorkStateWaitBundle) && stork->hasBundleLanded()) +			state = kStorkStateFinish; + +		// Check user input + +		int16 mouseX, mouseY; +		MouseButtons mouseButtons; + +		int16 key = checkInput(mouseX, mouseY, mouseButtons); + +		action = doIngameMenu(key, mouseButtons); +		if (action != kMenuActionNone) { +			state = kStorkStateFinish; +			break; +		} + +		clearAnim(anims); + +		if (mouseButtons == kMouseButtonsLeft) { +			stopSound(); +			playSound(kSoundClick); + +			int house = checkButton(param.houses, param.houseCount, mouseX, mouseY); +			if ((state == kStorkStateWaitUser) && (house >= 0)) { + +				_house = house; + +				stork->dropBundle(param.drops[house]); +				state = kStorkStateWaitBundle; + +				// Remove the "Where does the stork go?" text +				int16 left, top, right, bottom; +				if (whereStork->clear(*_vm->_draw->_backSurface, left, top, right, bottom)) +					_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + +				// Print the text where the stork actually goes +				thereStork->selectLine(3, house); // The house +				thereStork->selectLine(4, house); // The house's inhabitants +				if (thereStork->draw(*_vm->_draw->_backSurface, 2, *_plettre, 10, left, top, right, bottom)) +					_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); +			} +		} + +		drawAnim(anims); +		showCursor(); +		fadeIn(); + +		endFrame(true); +	} + +	freeAnims(anims); +	delete thereStork; +	delete whereStork; + +	fadeOut(); +	hideCursor(); + +	// Didn't complete the section +	if (action != kMenuActionNone) +		return false; + +	// Move on to the character generator + +	CharGenAction charGenAction = kCharGenRestart; +	while (charGenAction == kCharGenRestart) +		charGenAction = characterGenerator(); + +	// Did we successfully create a character? +	return charGenAction == kCharGenDone; +} + +const OnceUpon::MenuButton OnceUpon::kCharGenHeadButtons[] = { +	{true, 106, 146, 152, 180,   0,  0,  47, 34, 106, 146, 0}, +	{true, 155, 146, 201, 180,  49,  0,  96, 34, 155, 146, 1}, +	{true, 204, 146, 250, 180,  98,  0, 145, 34, 204, 146, 2}, +	{true, 253, 146, 299, 180, 147,  0, 194, 34, 253, 146, 3} +}; + +const OnceUpon::MenuButton OnceUpon::kCharGenHeads[] = { +	{true,   0,   0,   0,   0,  29,  4,  68, 31,  40,  51, 0}, +	{true,   0,   0,   0,   0,  83,  4, 113, 31,  45,  51, 1}, +	{true,   0,   0,   0,   0, 132,  4, 162, 31,  45,  51, 2}, +	{true,   0,   0,   0,   0, 182,  4, 211, 31,  45,  51, 3} +}; + +const OnceUpon::MenuButton OnceUpon::kCharGenHairButtons[] = { +	{true, 105,  55, 124,  70, 271,  1, 289, 15, 105,  55, 0x04}, +	{true, 105,  74, 124,  89, 271, 20, 289, 34, 105,  74, 0x07} +}; + +const OnceUpon::MenuButton OnceUpon::kCharGenJacketButtons[] = { +	{true, 105,  90, 124, 105, 271, 39, 289, 53, 105,  90, 0x06}, +	{true, 105, 109, 124, 124, 271, 58, 289, 72, 105, 109, 0x02} +}; + +const OnceUpon::MenuButton OnceUpon::kCharGenTrousersButtons[] = { +	{true, 105, 140, 124, 155, 271, 77, 289,  91, 105, 140, 0x01}, +	{true, 105, 159, 124, 174, 271, 96, 289, 110, 105, 159, 0x03} +}; + +const OnceUpon::MenuButton OnceUpon::kCharGenNameEntry[] = { +	{true, 0, 0, 0, 0,   0,  38,  54,  48, 140, 145, 0}, +	{true, 0, 0, 0, 0, 106,  38, 159,  48, 195, 145, 0}, +	{true, 0, 0, 0, 0,   0, 105,  54, 121, 140, 156, 0}, +	{true, 0, 0, 0, 0, 106, 105, 159, 121, 195, 156, 0} +}; + +enum CharGenState { +	kCharGenStateHead     = 0, // Choose a head +	kCharGenStateHair        , // Choose hair color +	kCharGenStateJacket      , // Choose jacket color +	kCharGenStateTrousers    , // Choose trousers color +	kCharGenStateName        , // Choose name +	kCharGenStateSure        , // "Are you sure?" +	kCharGenStateStoryName   , // "We're going to tell the story of $NAME" +	kCharGenStateFinish        // Finished +}; + +void OnceUpon::charGenSetup(uint stage) { +	Surface choix(320, 200, 1), elchoix(320, 200, 1), paperDoll(65, 137, 1); + +	_vm->_video->drawPackedSprite("choix.cmp"  , choix); +	_vm->_video->drawPackedSprite("elchoix.cmp", elchoix); + +	paperDoll.blit(choix, 200, 0, 264, 136, 0, 0); + +	GCTFile *text = loadGCT(getLocFile("choix.gc")); +	text->setArea(17, 18, 303, 41); +	text->setText(9, _name); + +	// Background +	_vm->_video->drawPackedSprite("cadre.cmp", *_vm->_draw->_backSurface); +	_vm->_draw->_backSurface->fillRect(16, 50, 303, 187, 5); + +	// Character sprite frame +	_vm->_draw->_backSurface->blit(choix, 0, 38, 159, 121, 140, 54); + +	// Recolor the paper doll parts +	if (_colorHair != 0xFF) +		elchoix.recolor(0x0C, _colorHair); + +	if (_colorJacket != 0xFF) +		paperDoll.recolor(0x0A, _colorJacket); + +	if (_colorTrousers != 0xFF) +		paperDoll.recolor(0x09, _colorTrousers); + +	_vm->_draw->_backSurface->blit(paperDoll, 32, 51); + +	// Paper doll head +	if (_head != 0xFF) +		drawButton(*_vm->_draw->_backSurface, elchoix, kCharGenHeads[_head], 0); + +	if (stage == kCharGenStateHead) { +		// Head buttons +		drawButtons(*_vm->_draw->_backSurface, choix, kCharGenHeadButtons, ARRAYSIZE(kCharGenHeadButtons)); + +		// "Choose a head" +		int16 left, top, right, bottom; +		text->draw(*_vm->_draw->_backSurface, 5, *_plettre, 10, left, top, right, bottom); + +	} else if (stage == kCharGenStateHair) { +		// Hair color buttons +		drawButtons(*_vm->_draw->_backSurface, choix, kCharGenHairButtons, ARRAYSIZE(kCharGenHairButtons)); + +		// "What color is the hair?" +		int16 left, top, right, bottom; +		text->draw(*_vm->_draw->_backSurface, 6, *_plettre, 10, left, top, right, bottom); + +	} else if (stage == kCharGenStateJacket) { +		// Jacket color buttons +		drawButtons(*_vm->_draw->_backSurface, choix, kCharGenJacketButtons, ARRAYSIZE(kCharGenJacketButtons)); + +		// "What color is the jacket?" +		int16 left, top, right, bottom; +		text->draw(*_vm->_draw->_backSurface, 7, *_plettre, 10, left, top, right, bottom); + +	} else if (stage == kCharGenStateTrousers) { +		// Trousers color buttons +		drawButtons(*_vm->_draw->_backSurface, choix, kCharGenTrousersButtons, ARRAYSIZE(kCharGenTrousersButtons)); + +		// "What color are the trousers?" +		int16 left, top, right, bottom; +		text->draw(*_vm->_draw->_backSurface, 8, *_plettre, 10, left, top, right, bottom); + +	} else if (stage == kCharGenStateName) { +		// Name entry field +		drawButtons(*_vm->_draw->_backSurface, choix, kCharGenNameEntry, ARRAYSIZE(kCharGenNameEntry)); + +		// "Enter name" +		int16 left, top, right, bottom; +		text->draw(*_vm->_draw->_backSurface, 10, *_plettre, 10, left, top, right, bottom); + +		charGenDrawName(); +	} else if (stage == kCharGenStateSure) { +		// Name entry field +		drawButtons(*_vm->_draw->_backSurface, choix, kCharGenNameEntry, ARRAYSIZE(kCharGenNameEntry)); + +		// "Are you sure?" +		TXTFile *sure = loadTXT(getLocFile("estu.tx"), TXTFile::kFormatStringPositionColor); +		sure->draw(*_vm->_draw->_backSurface, &_plettre, 1); +		delete sure; + +		charGenDrawName(); +	} else if (stage == kCharGenStateStoryName) { + +		// "We're going to tell the story of $NAME" +		int16 left, top, right, bottom; +		text->draw(*_vm->_draw->_backSurface, 11, *_plettre, 10, left, top, right, bottom); +	} + +	delete text; +} + +bool OnceUpon::enterString(Common::String &name, int16 key, uint maxLength, const Font &font) { +	if (key == 0) +		return true; + +	if (key == kKeyBackspace) { +		name.deleteLastChar(); +		return true; +	} + +	if (key == kKeySpace) +		key = ' '; + +	if ((key >= ' ') && (key <= 0xFF)) { +		if (name.size() >= maxLength) +			return false; + +		if (!font.hasChar(key)) +			return false; + +		name += (char) key; +		return true; +	} + +	return false; +} + +void OnceUpon::charGenDrawName() { +	_vm->_draw->_backSurface->fillRect(147, 151, 243, 166, 1); + +	const int16 nameY = 151 + ((166 - 151 + 1 -       _plettre->getCharHeight())  / 2); +	const int16 nameX = 147 + ((243 - 147 + 1 - (15 * _plettre->getCharWidth ())) / 2); + +	_plettre->drawString(_name, nameX, nameY, 10, 0, true, *_vm->_draw->_backSurface); + +	const int16 cursorLeft   = nameX + _name.size() * _plettre->getCharWidth(); +	const int16 cursorTop    = nameY; +	const int16 cursorRight  = cursorLeft + _plettre->getCharWidth() - 1; +	const int16 cursorBottom = cursorTop + _plettre->getCharHeight() - 1; + +	_vm->_draw->_backSurface->fillRect(cursorLeft, cursorTop, cursorRight, cursorBottom, 10); + +	_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 147, 151, 243, 166); +} + +OnceUpon::CharGenAction OnceUpon::characterGenerator() { +	fadeOut(); +	hideCursor(); +	setGameCursor(); + +	showWait(1); + +	_name.clear(); + +	_head          = 0xFF; +	_colorHair     = 0xFF; +	_colorJacket   = 0xFF; +	_colorTrousers = 0xFF; + +	CharGenState state = kCharGenStateHead; +	charGenSetup(state); + +	ANIFile ani(_vm, "ba.ani", 320); + +	ani.recolor(0x0F, 0x0C); +	ani.recolor(0x0E, 0x0A); +	ani.recolor(0x08, 0x09); + +	CharGenChild *child = new CharGenChild(ani); + +	ANIList anims; +	anims.push_back(child); + +	fadeOut(); +	_vm->_draw->forceBlit(); + +	CharGenAction action = kCharGenRestart; +	while (!_vm->shouldQuit() && (state != kCharGenStateFinish)) { +		// Check user input + +		int16 mouseX, mouseY; +		MouseButtons mouseButtons; + +		int16 key = checkInput(mouseX, mouseY, mouseButtons); + +		MenuAction menuAction = doIngameMenu(key, mouseButtons); +		if (menuAction != kMenuActionNone) { +			state  = kCharGenStateFinish; +			action = kCharGenAbort; +			break; +		} + +		clearAnim(anims); + +		if (state == kCharGenStateStoryName) { +			if ((mouseButtons != kMouseButtonsNone) || (key != 0)) { +				state = kCharGenStateFinish; +				action = kCharGenDone; +				break; +			} +		} + +		if (state == kCharGenStateSure) { +			// Not sure => restart +			if ((key == 'N') || (key == 'n')) { // No / Nein / Non +				state = kCharGenStateFinish; +				action = kCharGenRestart; +				break; +			} + +			if ((key == 'Y') || (key == 'y') || // Yes +			    (key == 'J') || (key == 'j') || // Ja +			    (key == 'S') || (key == 's') || // Si +			    (key == 'O') || (key == 'o')) { // Oui + +				state = kCharGenStateStoryName; +				charGenSetup(state); +				_vm->_draw->forceBlit(); +			} +		} + +		if (state == kCharGenStateName) { +			if (enterString(_name, key, 14, *_plettre)) { +				_vm->_draw->_backSurface->fillRect(147, 151, 243, 166, 1); + +				const int16 nameY = 151 + ((166 - 151 + 1 -       _plettre->getCharHeight())  / 2); +				const int16 nameX = 147 + ((243 - 147 + 1 - (15 * _plettre->getCharWidth ())) / 2); + +				_plettre->drawString(_name, nameX, nameY, 10, 0, true, *_vm->_draw->_backSurface); + +				const int16 cursorLeft   = nameX + _name.size() * _plettre->getCharWidth(); +				const int16 cursorTop    = nameY; +				const int16 cursorRight  = cursorLeft + _plettre->getCharWidth() - 1; +				const int16 cursorBottom = cursorTop + _plettre->getCharHeight() - 1; + +				_vm->_draw->_backSurface->fillRect(cursorLeft, cursorTop, cursorRight, cursorBottom, 10); + +				_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 147, 151, 243, 166); +			} + +			if ((key == kKeyReturn) && !_name.empty()) { +				_name.trim(); +				_name.setChar(Util::toCP850Upper(_name[0]), 0); + +				state = kCharGenStateSure; +				charGenSetup(state); +				_vm->_draw->forceBlit(); +			} +		} + +		if (mouseButtons == kMouseButtonsLeft) { +			stopSound(); +			playSound(kSoundClick); + +			int trousers = checkButton(kCharGenTrousersButtons, ARRAYSIZE(kCharGenTrousersButtons), mouseX, mouseY); +			if ((state == kCharGenStateTrousers) && (trousers >= 0)) { +				_colorTrousers = trousers; + +				ani.recolor(0x09, _colorTrousers); + +				state = kCharGenStateName; +				charGenSetup(state); +				_vm->_draw->forceBlit(); +			} + +			int jacket = checkButton(kCharGenJacketButtons, ARRAYSIZE(kCharGenJacketButtons), mouseX, mouseY); +			if ((state == kCharGenStateJacket) && (jacket >= 0)) { +				_colorJacket = jacket; + +				ani.recolor(0x0A, _colorJacket); + +				state = kCharGenStateTrousers; +				charGenSetup(state); +				_vm->_draw->forceBlit(); +			} + +			int hair = checkButton(kCharGenHairButtons, ARRAYSIZE(kCharGenHairButtons), mouseX, mouseY); +			if ((state == kCharGenStateHair) && (hair >= 0)) { +				_colorHair = hair; + +				ani.recolor(0x0C, _colorHair); + +				state = kCharGenStateJacket; +				charGenSetup(state); +				_vm->_draw->forceBlit(); +			} + +			int head = checkButton(kCharGenHeadButtons, ARRAYSIZE(kCharGenHeadButtons), mouseX, mouseY); +			if ((state == kCharGenStateHead) && (head >= 0)) { +				_head = head; + +				state = kCharGenStateHair; +				charGenSetup(state); +				_vm->_draw->forceBlit(); +			} +		} + +		drawAnim(anims); + +		// Play the child sounds +		CharGenChild::Sound childSound = child->shouldPlaySound(); +		if        (childSound == CharGenChild::kSoundWalk) { +			beep(50, 10); +		} else if (childSound == CharGenChild::kSoundJump) { +			stopSound(); +			playSound(kSoundJump); +		} + +		showCursor(); +		fadeIn(); + +		endFrame(true); +	} + +	fadeOut(); +	hideCursor(); + +	freeAnims(anims); + +	if (_vm->shouldQuit()) +		return kCharGenAbort; + +	return action; +} + +bool OnceUpon::sectionChapter1() { +	showChapter(1); +	return true; +} + +bool OnceUpon::sectionParents() { +	fadeOut(); +	setGamePalette(14); +	clearScreen(); + +	const Common::String seq = ((_house == 1) || (_house == 2)) ? "parents.seq" : "parents2.seq"; +	const Common::String gct = getLocFile("mefait.gc"); + +	Parents parents(_vm, seq, gct, _name, _house, *_plettre, kGamePalettes[14], kGamePalettes[13], kPaletteSize); +	parents.play(); + +	warning("OnceUpon::sectionParents(): TODO: Item search"); +	return true; +} + +bool OnceUpon::sectionChapter2() { +	showChapter(2); +	return true; +} + +bool OnceUpon::sectionForest0() { +	warning("OnceUpon::sectionForest0(): TODO"); +	return true; +} + +bool OnceUpon::sectionChapter3() { +	showChapter(3); +	return true; +} + +bool OnceUpon::sectionEvilCastle() { +	warning("OnceUpon::sectionEvilCastle(): TODO"); +	return true; +} + +bool OnceUpon::sectionChapter4() { +	showChapter(4); +	return true; +} + +bool OnceUpon::sectionForest1() { +	warning("OnceUpon::sectionForest1(): TODO"); +	return true; +} + +bool OnceUpon::sectionChapter5() { +	showChapter(5); +	return true; +} + +bool OnceUpon::sectionBossFight() { +	warning("OnceUpon::sectionBossFight(): TODO"); +	return true; +} + +bool OnceUpon::sectionChapter6() { +	showChapter(6); +	return true; +} + +bool OnceUpon::sectionForest2() { +	warning("OnceUpon::sectionForest2(): TODO"); +	return true; +} + +bool OnceUpon::sectionChapter7() { +	showChapter(7); +	return true; +} + +const PreGob::AnimProperties OnceUpon::kSectionEndAnimations[] = { +	{ 0, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, +	{ 6, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, +	{ 9, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, +	{11, 0, ANIObject::kModeContinuous, true, false, false, 0, 0} +}; + +bool OnceUpon::sectionEnd() { +	fadeOut(); +	setGamePalette(9); + +	_vm->_video->drawPackedSprite("cadre.cmp", *_vm->_draw->_backSurface); + +	Surface endBackground(320, 200, 1); +	_vm->_video->drawPackedSprite("fin.cmp", endBackground); + +	_vm->_draw->_backSurface->blit(endBackground, 0, 0, 288, 137, 16, 50); + +	GCTFile *endText = loadGCT(getLocFile("final.gc")); +	endText->setArea(17, 18, 303, 41); +	endText->setText(1, _name); + +	ANIFile ani(_vm, "fin.ani", 320); +	ANIList anims; + +	loadAnims(anims, ani, ARRAYSIZE(kSectionEndAnimations), kSectionEndAnimations); +	drawAnim(anims); + +	_vm->_draw->forceBlit(); + +	uint32 textStartTime = 0; + +	MenuAction action = kMenuActionNone; +	while (!_vm->shouldQuit() && (action == kMenuActionNone)) { +		// Check user input + +		int16 mouseX, mouseY; +		MouseButtons mouseButtons; + +		int16 key = checkInput(mouseX, mouseY, mouseButtons); + +		action = doIngameMenu(key, mouseButtons); +		if (action != kMenuActionNone) +			break; + +		clearAnim(anims); + +		// Pressed a key or mouse button => Skip to next area-full of text +		if ((mouseButtons == kMouseButtonsLeft) || (key != 0)) +			textStartTime = 0; + +		// Draw the next area-full of text +		uint32 now = _vm->_util->getTimeKey(); +		if (!endText->finished() && ((textStartTime == 0) || (now >= (textStartTime + kGCTDelay)))) { +			textStartTime = now; + +			int16 left, top, right, bottom; +			if (endText->clear(*_vm->_draw->_backSurface, left, top, right, bottom)) +				_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + +			if (endText->draw(*_vm->_draw->_backSurface, 0, *_plettre, 10, left, top, right, bottom)) +				_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); +		} + +		drawAnim(anims); +		fadeIn(); + +		endFrame(true); +	} + +	freeAnims(anims); +	delete endText; + +	// Restart requested +	if (action == kMenuActionRestart) +		return false; + +	// Last scene. Even if we didn't explicitly request a quit, the game ends here +	_quit = true; +	return false; +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/onceupon.h b/engines/gob/pregob/onceupon/onceupon.h new file mode 100644 index 0000000000..66ef877618 --- /dev/null +++ b/engines/gob/pregob/onceupon/onceupon.h @@ -0,0 +1,344 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GOB_PREGOB_ONCEUPON_ONCEUPON_H +#define GOB_PREGOB_ONCEUPON_ONCEUPON_H + +#include "common/system.h" +#include "common/str.h" + +#include "gob/pregob/pregob.h" + +#include "gob/pregob/onceupon/stork.h" + +namespace Gob { + +class Surface; +class Font; + +class ANIObject; + +namespace OnceUpon { + +class OnceUpon : public PreGob { +public: +	/** Number of languages we support. */ +	static const uint kLanguageCount = 5; + + +	OnceUpon(GobEngine *vm); +	~OnceUpon(); + + +protected: +	/** A description of a menu button. */ +	struct MenuButton { +		bool needDraw; ///< Does the button need drawing? + +		int16 left;   ///< Left   coordinate of the button. +		int16 top;    ///< Top    coordinate of the button. +		int16 right;  ///< Right  coordinate of the button. +		int16 bottom; ///< Bottom coordinate of the button. + +		int16 srcLeft;   ///< Left  coordinate of the button's sprite. +		int16 srcTop;    ///< Top   coordinate of the button's sprite. +		int16 srcRight;  ///< Right coordinate of the button's sprite. +		int16 srcBottom; ///< Right coordinate of the button's sprite. + +		int16 dstX; ///< Destination X coordinate of the button's sprite. +		int16 dstY; ///< Destination Y coordinate of the button's sprite. + +		uint id; ///< The button's ID. +	}; + +	/** Parameters for the stork section. */ +	struct StorkParam { +		const char *backdrop; ///< Backdrop image file. + +		uint  houseCount;         ///< Number of houses. +		const MenuButton *houses; ///< House button definitions. + +		const Stork::BundleDrop *drops; ///< The bundle drop parameters. +	}; + +	void init(); +	void deinit(); + +	/** Handle the copy protection. +	 * +	 *  @param  colors    Colors the copy protection animals can be. +	 *  @param  shapes    The shape that's the correct answer for each animal in each color. +	 *  @param  obfuscate Extra obfuscate table. correctShape = shapes[colors][obfuscate[animal]]. +	 *  @return true if the user guessed the correct shape, false otherwise. +	 */ +	bool doCopyProtection(const uint8 colors[7], const uint8 shapes[7 * 20], const uint8 obfuscate[4]); + +	/** Show the intro. */ +	void showIntro(); + +	/** Handle the start menu. +	 * +	 *  @param animalsButton Definition of the menu button that leads to the animal names screen. Can be 0. +	 *  @param animalCount   Number of animals in the animal names screen. +	 *  @param animalButtons Definition of the buttons that make up the animals in the animal names screen. +	 *  @param animalNames   File prefixes for the name of each animal. +	 */ +	void doStartMenu(const MenuButton *animalsButton, uint animalCount, +	                 const MenuButton *animalButtons, const char * const *animalNames); + +	/** Play the game proper. */ +	void playGame(); + + +	/** Return the parameters for the stork section. */ +	virtual const StorkParam &getStorkParameters() const = 0; + + +private: +	/** All actions a user can request in a menu. */ +	enum MenuAction { +		kMenuActionNone = 0, ///< No action. +		kMenuActionAnimals , ///< Do the animal names. +		kMenuActionPlay    , ///< Play the game. +		kMenuActionRestart , ///< Restart the section. +		kMenuActionMainMenu, ///< Go to the main menu. +		kMenuActionQuit      ///< Quit the game. +	}; + +	/** Difficulty levels. */ +	enum Difficulty { +		kDifficultyBeginner     = 0, +		kDifficultyIntermediate = 1, +		kDifficultyAdvanced     = 2, +		kDifficultyCount +	}; + +	/** The different sounds common in the game. */ +	enum Sound { +		kSoundClick = 0, +		kSoundStork    , +		kSoundJump     , +		kSoundCount +	}; + +	/** Action the character generation wants us to take. */ +	enum CharGenAction { +		kCharGenDone    = 0, ///< Created a character, move on. +		kCharGenAbort      , ///< Aborted the character generation. +		kCharGenRestart      ///< Restart the character generation. +	}; + +	/** A complete screen backup. */ +	struct ScreenBackup { +		Surface *screen; ///< Screen contents. +		int palette;     ///< Screen palette. + +		bool changedCursor; ///< Did we change the cursor? +		bool cursorVisible; ///< Was the cursor visible? + +		ScreenBackup(); +		~ScreenBackup(); +	}; + + +	/** The number of game sections. */ +	static const int kSectionCount = 15; + +	static const MenuButton kMainMenuDifficultyButton[]; ///< Difficulty buttons. +	static const MenuButton kSectionButtons[];           ///< Section buttons. + +	static const MenuButton kIngameButtons[];            ///< Ingame menu buttons. + +	static const MenuButton kAnimalNamesBack;   ///< "Back" button in the animal names screens. +	static const MenuButton kLanguageButtons[]; ///< Language buttons in the animal names screen. + +	static const MenuButton kSectionStorkHouses[]; + +	static const MenuButton kCharGenHeadButtons[]; +	static const MenuButton kCharGenHeads[]; +	static const MenuButton kCharGenHairButtons[]; +	static const MenuButton kCharGenJacketButtons[]; +	static const MenuButton kCharGenTrousersButtons[]; +	static const MenuButton kCharGenNameEntry[]; + +	/** All general game sounds we know about. */ +	static const char *kSound[kSoundCount]; + + +	static const AnimProperties kClownAnimations[]; +	static const AnimProperties kTitleAnimation; +	static const AnimProperties kSectionStorkAnimations[]; +	static const AnimProperties kSectionEndAnimations[]; + + +	/** Function pointer type for a section handler. */ +	typedef bool (OnceUpon::*SectionFunc)(); +	/** Section handler function. */ +	static const SectionFunc kSectionFuncs[kSectionCount]; + + +	/** Did we open the game archives? */ +	bool _openedArchives; + +	// Fonts +	Font *_jeudak; +	Font *_lettre; +	Font *_plettre; +	Font *_glettre; + +	/** The current palette. */ +	int _palette; + +	bool _quit; ///< Did the user request a normal game quit? + +	Difficulty _difficulty; ///< The current difficulty. +	int        _section;    ///< The current game section. + +	Common::String _name; ///< The name of the child. + +	uint8 _house; + +	uint8 _head; +	uint8 _colorHair; +	uint8 _colorJacket; +	uint8 _colorTrousers; + + +	// -- General helpers -- + +	void setGamePalette(uint palette); ///< Set a game palette. +	void setGameCursor();              ///< Set the default game cursor. + +	/** Draw this sprite in a fancy, animated line-by-line way. */ +	void drawLineByLine(const Surface &src, int16 left, int16 top, int16 right, int16 bottom, +	                    int16 x, int16 y) const; + +	/** Backup the screen contents. */ +	void backupScreen(ScreenBackup &backup, bool setDefaultCursor = false); +	/** Restore the screen contents with a previously made backup. */ +	void restoreScreen(ScreenBackup &backup); + +	Common::String fixString(const Common::String &str) const; ///< Fix a string if necessary. +	void fixTXTStrings(TXTFile &txt) const;                    ///< Fix all strings in a TXT. + + +	// -- Copy protection helpers -- + +	/** Set up the copy protection. */ +	int8 cpSetup(const uint8 colors[7], const uint8 shapes[7 * 20], +	             const uint8 obfuscate[4], const Surface sprites[2]); +	/** Find the shape under these coordinates. */ +	int8 cpFindShape(int16 x, int16 y) const; +	/** Display the "You are wrong" screen. */ +	void cpWrong(); + + +	// -- Show different game screens -- + +	void showWait(uint palette = 0xFFFF); ///< Show the wait / loading screen. +	void showQuote();                     ///< Show the quote about fairytales. +	void showTitle();                     ///< Show the Once Upon A Time title. +	void showChapter(int chapter);        ///< Show a chapter intro text. +	void showByeBye();                    ///< Show the "bye bye" screen. + +	/** Handle the "listen to animal names" part. */ +	void handleAnimalNames(uint count, const MenuButton *buttons, const char * const *names); + + +	// -- Menu helpers -- + +	MenuAction handleStartMenu(const MenuButton *animalsButton); ///< Handle the start  menu. +	MenuAction handleMainMenu();                                 ///< Handle the main   menu. +	MenuAction handleIngameMenu();                               ///< Handle the ingame menu. + +	void drawStartMenu(const MenuButton *animalsButton); ///< Draw the start  menu. +	void drawMainMenu();                                 ///< Draw the main   menu. +	void drawIngameMenu();                               ///< Draw the ingame menu. + +	/** Draw the difficulty label. */ +	void drawMenuDifficulty(); + +	/** Clear the ingame menu in an animated way. */ +	void clearIngameMenu(const Surface &background); + +	/** Handle the whole ingame menu. */ +	MenuAction doIngameMenu(); +	/** Handle the whole ingame menu if ESC or right mouse button was pressed. */ +	MenuAction doIngameMenu(int16 &key, MouseButtons &mouseButtons); + + +	// -- Menu button helpers -- + +	/** Find the button under these coordinates. */ +	int checkButton(const MenuButton *buttons, uint count, int16 x, int16 y, int failValue = -1) const; + +	/** Draw a menu button. */ +	void drawButton (Surface &dest, const Surface &src, const MenuButton &button, int transp = -1) const; +	/** Draw multiple menu buttons. */ +	void drawButtons(Surface &dest, const Surface &src, const MenuButton *buttons, uint count, int transp = -1) const; + +	/** Draw a border around a button. */ +	void drawButtonBorder(const MenuButton &button, uint8 color); + + +	// -- Animal names helpers -- + +	/** Set up the animal chooser. */ +	void anSetupChooser(); +	/** Set up the language chooser for one animal. */ +	void anSetupNames(const MenuButton &animal); +	/** Play / Display the name of an animal in one language. */ +	void anPlayAnimalName(const Common::String &animal, uint language); + + +	// -- Game sections -- + +	bool playSection(); + +	bool sectionStork(); +	bool sectionChapter1(); +	bool sectionParents(); +	bool sectionChapter2(); +	bool sectionForest0(); +	bool sectionChapter3(); +	bool sectionEvilCastle(); +	bool sectionChapter4(); +	bool sectionForest1(); +	bool sectionChapter5(); +	bool sectionBossFight(); +	bool sectionChapter6(); +	bool sectionForest2(); +	bool sectionChapter7(); +	bool sectionEnd(); + +	CharGenAction characterGenerator(); +	void charGenSetup(uint stage); +	void charGenDrawName(); + +	static bool enterString(Common::String &name, int16 key, uint maxLength, const Font &font); +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_ONCEUPON_H diff --git a/engines/gob/pregob/onceupon/palettes.h b/engines/gob/pregob/onceupon/palettes.h new file mode 100644 index 0000000000..952581041c --- /dev/null +++ b/engines/gob/pregob/onceupon/palettes.h @@ -0,0 +1,411 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GOB_PREGOB_ONCEUPON_PALETTES_H +#define GOB_PREGOB_ONCEUPON_PALETTES_H + +static const  int kPaletteSize  = 16; +static const uint kPaletteCount = 20; + +static const byte kCopyProtectionPalette[3 * kPaletteSize] = { +	0x00, 0x00, 0x00, +	0x19, 0x00, 0x19, +	0x00, 0x3F, 0x00, +	0x00, 0x2A, 0x2A, +	0x2A, 0x00, 0x00, +	0x2A, 0x00, 0x2A, +	0x2A, 0x15, 0x00, +	0x00, 0x19, 0x12, +	0x00, 0x00, 0x00, +	0x15, 0x15, 0x3F, +	0x15, 0x3F, 0x15, +	0x00, 0x20, 0x3F, +	0x3F, 0x00, 0x00, +	0x3F, 0x00, 0x20, +	0x3F, 0x3F, 0x00, +	0x3F, 0x3F, 0x3F +}; + +static const byte kGamePalettes[kPaletteCount][3 * kPaletteSize] = { +	{ +		0x00, 0x00, 0x00, +		0x00, 0x00, 0x10, +		0x00, 0x00, 0x18, +		0x00, 0x00, 0x3C, +		0x1C, 0x28, 0x00, +		0x10, 0x18, 0x00, +		0x1C, 0x1C, 0x20, +		0x14, 0x14, 0x14, +		0x14, 0x20, 0x04, +		0x00, 0x00, 0x24, +		0x3C, 0x3C, 0x3C, +		0x00, 0x00, 0x00, +		0x3C, 0x2C, 0x00, +		0x3C, 0x18, 0x00, +		0x3C, 0x04, 0x00, +		0x1C, 0x00, 0x00 +	}, +	{ +		0x00, 0x00, 0x00, +		0x00, 0x00, 0x24, +		0x3C, 0x3C, 0x3C, +		0x14, 0x20, 0x04, +		0x3C, 0x2C, 0x00, +		0x02, 0x00, 0x18, +		0x3C, 0x04, 0x00, +		0x1C, 0x00, 0x00, +		0x14, 0x20, 0x04, +		0x00, 0x00, 0x24, +		0x3C, 0x3C, 0x3C, +		0x00, 0x00, 0x00, +		0x3C, 0x2C, 0x00, +		0x3C, 0x18, 0x00, +		0x3C, 0x04, 0x00, +		0x1C, 0x00, 0x00 +	}, +	{ +		0x00, 0x00, 0x00, +		0x38, 0x20, 0x3C, +		0x2C, 0x10, 0x30, +		0x20, 0x08, 0x28, +		0x14, 0x00, 0x1C, +		0x20, 0x20, 0x38, +		0x18, 0x18, 0x2C, +		0x10, 0x10, 0x24, +		0x14, 0x20, 0x04, +		0x00, 0x00, 0x24, +		0x3C, 0x3C, 0x3C, +		0x00, 0x00, 0x00, +		0x3C, 0x2C, 0x00, +		0x3C, 0x18, 0x00, +		0x3C, 0x04, 0x00, +		0x1C, 0x00, 0x00 +	}, +	{ +		0x00, 0x00, 0x00, +		0x3C, 0x20, 0x20, +		0x24, 0x14, 0x14, +		0x1C, 0x10, 0x10, +		0x14, 0x0C, 0x0C, +		0x1C, 0x1C, 0x1C, +		0x18, 0x18, 0x18, +		0x10, 0x10, 0x10, +		0x14, 0x20, 0x04, +		0x00, 0x00, 0x24, +		0x3C, 0x3C, 0x3C, +		0x00, 0x00, 0x00, +		0x3C, 0x2C, 0x00, +		0x3C, 0x18, 0x00, +		0x3C, 0x04, 0x00, +		0x1C, 0x00, 0x00 +	}, +	{ +		0x00, 0x00, 0x00, +		0x10, 0x28, 0x1C, +		0x10, 0x1C, 0x10, +		0x10, 0x14, 0x0C, +		0x1C, 0x1C, 0x3C, +		0x24, 0x24, 0x3C, +		0x18, 0x18, 0x24, +		0x10, 0x10, 0x18, +		0x14, 0x20, 0x04, +		0x00, 0x00, 0x24, +		0x3C, 0x3C, 0x3C, +		0x00, 0x00, 0x00, +		0x3C, 0x2C, 0x00, +		0x3C, 0x18, 0x00, +		0x3C, 0x04, 0x00, +		0x1C, 0x00, 0x00 +	}, +	{ +		0x00, 0x00, 0x00, +		0x3F, 0x26, 0x3F, +		0x36, 0x1C, 0x36, +		0x2C, 0x12, 0x2A, +		0x27, 0x0C, 0x24, +		0x22, 0x07, 0x1E, +		0x1D, 0x03, 0x18, +		0x16, 0x00, 0x10, +		0x14, 0x20, 0x04, +		0x00, 0x00, 0x24, +		0x3C, 0x3C, 0x3A, +		0x00, 0x00, 0x00, +		0x3C, 0x2C, 0x00, +		0x3C, 0x18, 0x00, +		0x3C, 0x04, 0x00, +		0x1C, 0x00, 0x00 +	}, +	{ +		0x00, 0x00, 0x00, +		0x3F, 0x39, 0x26, +		0x38, 0x34, 0x1C, +		0x30, 0x2F, 0x13, +		0x27, 0x29, 0x0C, +		0x1D, 0x22, 0x07, +		0x14, 0x1B, 0x03, +		0x0C, 0x14, 0x00, +		0x14, 0x20, 0x04, +		0x00, 0x00, 0x24, +		0x3C, 0x3C, 0x3A, +		0x00, 0x00, 0x00, +		0x3C, 0x2C, 0x00, +		0x3C, 0x18, 0x00, +		0x3C, 0x04, 0x00, +		0x1C, 0x00, 0x00 +	}, +	{ +		0x00, 0x00, 0x00, +		0x24, 0x3C, 0x3C, +		0x1C, 0x34, 0x38, +		0x14, 0x2C, 0x30, +		0x0C, 0x20, 0x2C, +		0x08, 0x18, 0x28, +		0x04, 0x10, 0x20, +		0x00, 0x08, 0x1C, +		0x14, 0x20, 0x04, +		0x00, 0x00, 0x24, +		0x3C, 0x3C, 0x38, +		0x00, 0x00, 0x00, +		0x3C, 0x2C, 0x00, +		0x3C, 0x18, 0x00, +		0x3C, 0x04, 0x00, +		0x1C, 0x00, 0x00 +	}, +	{ +		0x00, 0x00, 0x00, +		0x3C, 0x2C, 0x24, +		0x38, 0x24, 0x1C, +		0x30, 0x1C, 0x14, +		0x28, 0x18, 0x0C, +		0x20, 0x10, 0x04, +		0x1C, 0x0C, 0x00, +		0x14, 0x08, 0x00, +		0x14, 0x20, 0x04, +		0x00, 0x00, 0x24, +		0x3C, 0x3C, 0x38, +		0x00, 0x00, 0x00, +		0x3C, 0x2C, 0x00, +		0x3C, 0x18, 0x00, +		0x3C, 0x04, 0x00, +		0x1C, 0x00, 0x00 +	}, +	{ +		0x00, 0x00, 0x00, +		0x3C, 0x34, 0x24, +		0x38, 0x2C, 0x1C, +		0x30, 0x24, 0x14, +		0x2C, 0x1C, 0x10, +		0x30, 0x30, 0x3C, +		0x1C, 0x1C, 0x38, +		0x0C, 0x0C, 0x38, +		0x14, 0x20, 0x04, +		0x00, 0x00, 0x24, +		0x3C, 0x3C, 0x3C, +		0x00, 0x00, 0x00, +		0x3C, 0x2C, 0x00, +		0x3C, 0x18, 0x00, +		0x3C, 0x04, 0x00, +		0x1C, 0x00, 0x00 +	}, +	{ +		0x00, 0x00, 0x00, +		0x00, 0x00, 0x0C, +		0x02, 0x03, 0x14, +		0x07, 0x07, 0x1D, +		0x0E, 0x0E, 0x25, +		0x17, 0x17, 0x2E, +		0x21, 0x22, 0x36, +		0x2F, 0x2F, 0x3F, +		0x3F, 0x3F, 0x3F, +		0x3F, 0x3B, 0x0D, +		0x3A, 0x31, 0x0A, +		0x35, 0x28, 0x07, +		0x30, 0x21, 0x04, +		0x2B, 0x19, 0x02, +		0x26, 0x12, 0x01, +		0x16, 0x0B, 0x00 +	}, +	{ +		0x00, 0x00, 0x00, +		0x18, 0x00, 0x00, +		0x21, 0x01, 0x00, +		0x2A, 0x02, 0x00, +		0x33, 0x03, 0x00, +		0x3D, 0x06, 0x00, +		0x2A, 0x19, 0x05, +		0x15, 0x14, 0x14, +		0x22, 0x1F, 0x1E, +		0x2F, 0x2C, 0x28, +		0x3F, 0x3C, 0x29, +		0x3F, 0x38, 0x0B, +		0x3B, 0x30, 0x0A, +		0x37, 0x29, 0x08, +		0x33, 0x23, 0x07, +		0x2F, 0x1D, 0x06 +	}, +	{ +		0x00, 0x00, 0x00, +		0x00, 0x1C, 0x38, +		0x34, 0x30, 0x28, +		0x2C, 0x24, 0x1C, +		0x24, 0x18, 0x10, +		0x1C, 0x10, 0x08, +		0x14, 0x04, 0x04, +		0x10, 0x00, 0x00, +		0x14, 0x20, 0x04, +		0x00, 0x00, 0x24, +		0x3C, 0x3C, 0x38, +		0x00, 0x00, 0x00, +		0x3C, 0x2C, 0x00, +		0x3C, 0x18, 0x00, +		0x3C, 0x04, 0x00, +		0x1C, 0x00, 0x00 +	}, +	{ +		0x00, 0x00, 0x00, +		0x00, 0x1C, 0x38, +		0x34, 0x30, 0x28, +		0x2C, 0x24, 0x1C, +		0x3F, 0x3F, 0x3F, +		0x3F, 0x3F, 0x3F, +		0x3F, 0x3F, 0x3F, +		0x3F, 0x3F, 0x3F, +		0x14, 0x20, 0x04, +		0x00, 0x00, 0x24, +		0x3C, 0x3C, 0x38, +		0x00, 0x00, 0x00, +		0x3C, 0x2C, 0x00, +		0x3C, 0x18, 0x00, +		0x3C, 0x04, 0x00, +		0x1C, 0x00, 0x00 +	}, +	{ +		0x00, 0x00, 0x00, +		0x1A, 0x30, 0x37, +		0x14, 0x28, 0x31, +		0x10, 0x20, 0x2C, +		0x0C, 0x19, 0x27, +		0x08, 0x12, 0x21, +		0x05, 0x0C, 0x1C, +		0x03, 0x07, 0x16, +		0x01, 0x03, 0x11, +		0x00, 0x00, 0x0C, +		0x3C, 0x3C, 0x3C, +		0x00, 0x00, 0x00, +		0x3C, 0x2C, 0x00, +		0x3C, 0x18, 0x00, +		0x3C, 0x04, 0x00, +		0x1C, 0x00, 0x00 +	}, +	{ +		0x00, 0x00, 0x00, +		0x34, 0x30, 0x34, +		0x30, 0x24, 0x30, +		0x28, 0x1C, 0x28, +		0x24, 0x14, 0x24, +		0x1C, 0x0C, 0x1C, +		0x18, 0x08, 0x18, +		0x14, 0x04, 0x14, +		0x0C, 0x04, 0x0C, +		0x08, 0x00, 0x08, +		0x3C, 0x3C, 0x3C, +		0x00, 0x00, 0x00, +		0x3C, 0x2C, 0x00, +		0x3C, 0x18, 0x00, +		0x3C, 0x04, 0x00, +		0x1C, 0x00, 0x00 +	}, +	{ +		0x00, 0x00, 0x00, +		0x2C, 0x24, 0x0C, +		0x34, 0x34, 0x28, +		0x2C, 0x2C, 0x1C, +		0x24, 0x24, 0x10, +		0x1C, 0x18, 0x08, +		0x14, 0x14, 0x08, +		0x10, 0x10, 0x04, +		0x0C, 0x0C, 0x04, +		0x00, 0x00, 0x24, +		0x3C, 0x3C, 0x38, +		0x00, 0x00, 0x00, +		0x3C, 0x2C, 0x00, +		0x3C, 0x18, 0x00, +		0x3C, 0x04, 0x00, +		0x1C, 0x00, 0x00 +	}, +	{ +		0x00, 0x00, 0x00, +		0x00, 0x00, 0x00, +		0x14, 0x28, 0x31, +		0x10, 0x20, 0x2C, +		0x0C, 0x19, 0x27, +		0x08, 0x12, 0x21, +		0x05, 0x0C, 0x1C, +		0x03, 0x07, 0x16, +		0x01, 0x03, 0x11, +		0x00, 0x3C, 0x00, +		0x3C, 0x3C, 0x3C, +		0x00, 0x00, 0x00, +		0x3C, 0x2C, 0x00, +		0x3C, 0x18, 0x00, +		0x3C, 0x04, 0x00, +		0x1C, 0x00, 0x00 +	}, +	{ +		0x00, 0x00, 0x00, +		0x10, 0x28, 0x1C, +		0x10, 0x1C, 0x10, +		0x10, 0x14, 0x0C, +		0x1C, 0x1C, 0x3C, +		0x24, 0x24, 0x3C, +		0x18, 0x18, 0x24, +		0x10, 0x10, 0x18, +		0x14, 0x20, 0x04, +		0x00, 0x00, 0x24, +		0x3C, 0x3C, 0x3C, +		0x00, 0x00, 0x00, +		0x3C, 0x2C, 0x00, +		0x3C, 0x18, 0x00, +		0x3C, 0x04, 0x00, +		0x1C, 0x00, 0x00 +	}, +	{ +		0x00, 0x00, 0x00, +		0x10, 0x28, 0x1C, +		0x10, 0x1C, 0x10, +		0x10, 0x14, 0x0C, +		0x1C, 0x1C, 0x3C, +		0x24, 0x24, 0x3C, +		0x18, 0x18, 0x24, +		0x10, 0x10, 0x18, +		0x14, 0x20, 0x04, +		0x00, 0x00, 0x24, +		0x3C, 0x3C, 0x3C, +		0x00, 0x00, 0x00, +		0x3C, 0x2C, 0x00, +		0x3C, 0x18, 0x00, +		0x3C, 0x04, 0x00, +		0x1C, 0x00, 0x00 +	} +}; + +#endif // GOB_PREGOB_ONCEUPON_PALETTES_H diff --git a/engines/gob/pregob/onceupon/parents.cpp b/engines/gob/pregob/onceupon/parents.cpp new file mode 100644 index 0000000000..cdaee6a38d --- /dev/null +++ b/engines/gob/pregob/onceupon/parents.cpp @@ -0,0 +1,217 @@ +/* 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 "gob/gob.h" +#include "gob/global.h" +#include "gob/dataio.h" +#include "gob/palanim.h" +#include "gob/draw.h" +#include "gob/video.h" + +#include "gob/sound/sound.h" + +#include "gob/pregob/gctfile.h" + +#include "gob/pregob/onceupon/palettes.h" +#include "gob/pregob/onceupon/parents.h" + +namespace Gob { + +namespace OnceUpon { + +const char *Parents::kSound[kSoundCount] = { +	"rire.snd", // kSoundCackle +	"tonn.snd"  // kSoundThunder +}; + +// So that every GCT line is displayed for 12 seconds +const uint16 Parents::kLoop[kLoopCount][3] = { +	{ 72,  77, 33}, +	{105, 109, 38}, +	{141, 145, 38}, +	{446, 454, 23}, +	{456, 464, 23}, +	{466, 474, 23}, +	{476, 484, 23} +}; + + +Parents::Parents(GobEngine *vm, const Common::String &seq, const Common::String &gct, +                 const Common::String &childName, uint8 house, const Font &font, +                 const byte *normalPalette, const byte *brightPalette, uint paletteSize) : +	SEQFile(vm, seq), +	_gct(0), _house(house), _font(&font), +	_paletteSize(paletteSize), _normalPalette(normalPalette), _brightPalette(brightPalette) { + +	// Load sounds +	for (int i = 0; i < kSoundCount; i++) +		_vm->_sound->sampleLoad(&_sounds[i], SOUND_SND, kSound[i]); + +	// Load GCT +	Common::SeekableReadStream *gctStream = _vm->_dataIO->getFile(gct); +	if (gctStream) { +		_gct = new GCTFile(*gctStream, _vm->_rnd); + +		delete gctStream; +	} else +		error("Parents::Parents(): Failed to open \"%s\"", gct.c_str()); + +	_gct->setArea(17, 18, 303, 41); +	_gct->setText(1, childName); + +	_gct->selectLine(2, _house); +	_gct->selectLine(4, _house); + +	for (uint i = 0; i < kLoopCount; i++) +		_loopID[i] = addLoop(kLoop[i][0], kLoop[i][1], kLoop[i][2]); +} + +Parents::~Parents() { +	delete _gct; +} + +void Parents::play() { +	_currentLoop = 0; + +	SEQFile::play(true, 496, 15); + +	// After playback, fade out +	if (!_vm->shouldQuit()) +		_vm->_palAnim->fade(0, 0, 0); +} + +void Parents::handleFrameEvent() { +	switch (getFrame()) { +	case 0: +		// On fame 0, fade in +		_vm->_draw->forceBlit(); +		_vm->_palAnim->fade(_vm->_global->_pPaletteDesc, 0, 0); +		break; + +	case 4: +		drawGCT(0); +		break; + +	case 55: +		drawGCT(3, 0); +		break; + +	case 79: +		drawGCT(_house + 5, 1); +		break; + +	case 110: +		drawGCT(_house + 9, 2); +		break; + +	case 146: +		drawGCT(17); +		break; + +	case 198: +		drawGCT(13); +		break; + +	case 445: +		drawGCT(14, 3); +		break; + +	case 455: +		drawGCT(18, 4); +		break; + +	case 465: +		drawGCT(19, 5); +		break; + +	case 475: +		drawGCT(20, 6); +		break; + +	case 188: +	case 228: +	case 237: +	case 257: +	case 275: +	case 426: +		lightningEffect(); +		break; + +	case 203: +	case 243: +	case 252: +	case 272: +	case 290: +	case 441: +		playSound(kSoundThunder); +		break; + +	case 340: +		playSound(kSoundCackle); +		break; +	} +} + +void Parents::handleInput(int16 key, int16 mouseX, int16 mouseY, MouseButtons mouseButtons) { +	if ((key == kKeyEscape) || (mouseButtons == kMouseButtonsRight)) +		abortPlay(); + +	if (((key == kKeySpace) || (mouseButtons == kMouseButtonsLeft)) && (_currentLoop < kLoopCount)) +		skipLoop(_loopID[_currentLoop]); +} + +void Parents::playSound(Sound sound) { +	_vm->_sound->blasterStop(0); +	_vm->_sound->blasterPlay(&_sounds[sound], 0, 0); +} + +void Parents::lightningEffect() { +	for (int i = 0; (i < 5) && !_vm->shouldQuit(); i++) { + +		setPalette(_brightPalette, _paletteSize); +		_vm->_util->delay(5); + +		setPalette(_normalPalette, _paletteSize); +		_vm->_util->delay(5); +	} +} + +void Parents::setPalette(const byte *palette, uint size) { +	memcpy(_vm->_draw->_vgaPalette, palette, 3 * size); + +	_vm->_video->setFullPalette(_vm->_global->_pPaletteDesc); +	_vm->_video->retrace(); +} + +void Parents::drawGCT(uint item, uint loop) { +	int16 left, top, right, bottom; +	if (_gct->clear(*_vm->_draw->_backSurface, left, top, right, bottom)) +		_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); +	if (_gct->draw(*_vm->_draw->_backSurface, item, *_font, 10, left, top, right, bottom)) +		_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + +	_currentLoop = loop; +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/parents.h b/engines/gob/pregob/onceupon/parents.h new file mode 100644 index 0000000000..f5c8307b73 --- /dev/null +++ b/engines/gob/pregob/onceupon/parents.h @@ -0,0 +1,94 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GOB_PREGOB_ONCEUPON_PARENTS_H +#define GOB_PREGOB_ONCEUPON_PARENTS_H + +#include "gob/sound/sounddesc.h" + +#include "gob/pregob/seqfile.h" + +namespace Gob { + +class Font; + +class GCTFile; + +namespace OnceUpon { + +/** The home / parents animation sequence. */ +class Parents : public SEQFile { +public: +	Parents(GobEngine *vm, const Common::String &seq, const Common::String &gct, +	        const Common::String &childName, uint8 house, const Font &font, +	        const byte *normalPalette, const byte *brightPalette, uint paletteSize); +	~Parents(); + +	void play(); + +protected: +	void handleFrameEvent(); +	void handleInput(int16 key, int16 mouseX, int16 mouseY, MouseButtons mouseButtons); + +private: +	static const uint kLoopCount = 7; + +	static const uint16 kLoop[kLoopCount][3]; + +	enum Sound { +		kSoundCackle  = 0, +		kSoundThunder    , +		kSoundCount +	}; + +	static const char *kSound[kSoundCount]; + + +	uint8 _house; + +	const Font *_font; + +	uint _paletteSize; +	const byte *_normalPalette; +	const byte *_brightPalette; + +	SoundDesc _sounds[kSoundCount]; + +	GCTFile *_gct; + +	uint _loopID[kLoopCount]; +	uint _currentLoop; + + +	void lightningEffect(); + +	void playSound(Sound sound); +	void setPalette(const byte *palette, uint size); + +	void drawGCT(uint item, uint loop = 0xFFFF); +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_PARENTS_H diff --git a/engines/gob/pregob/onceupon/stork.cpp b/engines/gob/pregob/onceupon/stork.cpp new file mode 100644 index 0000000000..3c38037d08 --- /dev/null +++ b/engines/gob/pregob/onceupon/stork.cpp @@ -0,0 +1,234 @@ +/* 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/str.h" + +#include "gob/gob.h" +#include "gob/surface.h" +#include "gob/anifile.h" +#include "gob/video.h" + +#include "gob/pregob/onceupon/stork.h" + +enum Animation { +	kAnimFlyNearWithBundle    =  9, +	kAnimFlyFarWithBundle     = 12, +	kAnimFlyNearWithoutBundle = 10, +	kAnimFlyFarWithoutBundle  = 13, +	kAnimBundleNear           = 11, +	kAnimBundleFar            = 14 +}; + +namespace Gob { + +namespace OnceUpon { + +Stork::Stork(GobEngine *vm, const ANIFile &ani) : ANIObject(ani), _shouldDrop(false) { +	_frame = new Surface(320, 200, 1); +	vm->_video->drawPackedSprite("cadre.cmp", *_frame); + +	_bundle = new ANIObject(ani); + +	_bundle->setVisible(false); +	_bundle->setPause(true); + +	setState(kStateFlyNearWithBundle, kAnimFlyNearWithBundle, -80); +} + +Stork::~Stork() { +	delete _frame; + +	delete _bundle; +} + +bool Stork::hasBundleLanded() const { +	if (!_shouldDrop || !_bundle->isVisible() || _bundle->isPaused()) +		return false; + +	int16 x, y, width, height; +	_bundle->getFramePosition(x, y); +	_bundle->getFrameSize(width, height); + +	return (y + height) >= _bundleDrop.landY; +} + +void Stork::dropBundle(const BundleDrop &drop) { +	if (_shouldDrop) +		return; + +	_shouldDrop = true; +	_bundleDrop = drop; +} + +bool Stork::draw(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { +	left   = 0x7FFF; +	top    = 0x7FFF; +	right  = 0x0000; +	bottom = 0x0000; + +	bool drawn = ANIObject::draw(dest, left, top, right, bottom); +	if (drawn) { +		// Left frame edge +		if (left <= 15) +			dest.blit(*_frame, left, top, MIN<int16>(15, right), bottom, left, top); + +		// Right frame edge +		if (right >= 304) +			dest.blit(*_frame, MAX<int16>(304, left), top, right, bottom, MAX<int16>(304, left), top); +	} + +	int16 bLeft, bTop, bRight, bBottom; +	if (_bundle->draw(dest, bLeft, bTop, bRight, bBottom)) { +		// Bottom frame edge +		if (bBottom >= 188) +			dest.blit(*_frame, bLeft, MAX<int16>(188, bTop), bRight, bBottom, bLeft, MAX<int16>(188, bTop)); + +		left   = MIN(left  , bLeft  ); +		top    = MIN(top   , bTop   ); +		right  = MAX(right , bRight ); +		bottom = MAX(bottom, bBottom); + +		drawn = true; +	} + +	return drawn; +} + +bool Stork::clear(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { +	left   = 0x7FFF; +	top    = 0x7FFF; +	right  = 0x0000; +	bottom = 0x0000; + +	bool cleared = _bundle->clear(dest, left, top, right, bottom); + +	int16 sLeft, sTop, sRight, sBottom; +	if (ANIObject::clear(dest, sLeft, sTop, sRight, sBottom)) { +		left   = MIN(left  , sLeft  ); +		top    = MIN(top   , sTop   ); +		right  = MAX(right , sRight ); +		bottom = MAX(bottom, sBottom); + +		cleared = true; +	} + +	return cleared; +} + +void Stork::advance() { +	_bundle->advance(); + +	ANIObject::advance(); + +	int16 curX, curY, curWidth, curHeight; +	getFramePosition(curX, curY, 0); +	getFrameSize(curWidth, curHeight, 0); + +	const int16 curRight = curX + curWidth - 1; + +	int16 nextX, nextY, nextWidth, nextHeight; +	getFramePosition(nextX, nextY, 1); +	getFrameSize(nextWidth, nextHeight, 1); + +	const int16 nextRight = nextX + nextWidth - 1; + +	switch (_state) { +	case kStateFlyNearWithBundle: +		if (curX >= 330) +			setState(kStateFlyFarWithBundle, kAnimFlyFarWithBundle, 330); + +		if ((curRight  <= _bundleDrop.dropX) && +		    (nextRight >= _bundleDrop.dropX) && _shouldDrop && !_bundleDrop.dropWhileFar) +			dropBundle(kStateFlyNearWithoutBundle, kAnimFlyNearWithoutBundle); + +		break; + +	case kStateFlyFarWithBundle: +		if (curX <= -80) +			setState(kStateFlyNearWithBundle, kAnimFlyNearWithBundle, -80); + +		if ((curX  >= _bundleDrop.dropX) && +		    (nextX <= _bundleDrop.dropX) && _shouldDrop && _bundleDrop.dropWhileFar) +			dropBundle(kStateFlyFarWithoutBundle, kAnimFlyFarWithoutBundle); + +		break; + +	case kStateFlyNearWithoutBundle: +		if (curX >= 330) +			setState(kStateFlyFarWithoutBundle, kAnimFlyFarWithoutBundle, 330); +		break; + +	case kStateFlyFarWithoutBundle: +		if (curX <= -80) +			setState(kStateFlyNearWithoutBundle, kAnimFlyNearWithoutBundle, -80); +		break; + +	default: +		break; +	} +} + +void Stork::dropBundle(State state, uint16 anim) { +	setState(state, anim); + +	int16 x, y, width, height; +	getFramePosition(x, y); +	getFrameSize(width, height); + +	_bundle->setAnimation(_bundleDrop.anim); +	_bundle->setPause(false); +	_bundle->setVisible(true); + +	int16 bWidth, bHeight; +	_bundle->getFrameSize(bWidth, bHeight); + +	// Drop position +	x = _bundleDrop.dropX; +	y = y + height - bHeight; + +	// If the stork is flying near (from left to right), drop the bundle at the right edge +	if (!_bundleDrop.dropWhileFar) +		x = x - bWidth; + +	_bundle->setPosition(x, y); +} + +void Stork::setState(State state, uint16 anim) { +	setAnimation(anim); +	setVisible(true); +	setPause(false); + +	_state = state; +} + +void Stork::setState(State state, uint16 anim, int16 x) { +	setState(state, anim); +	setPosition(); + +	int16 pX, pY; +	getPosition(pX, pY); +	setPosition( x, pY); +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/stork.h b/engines/gob/pregob/onceupon/stork.h new file mode 100644 index 0000000000..756f5258c7 --- /dev/null +++ b/engines/gob/pregob/onceupon/stork.h @@ -0,0 +1,103 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GOB_PREGOB_ONCEUPON_STORK_H +#define GOB_PREGOB_ONCEUPON_STORK_H + +#include "common/system.h" + +#include "gob/aniobject.h" + +namespace Common { +	class String; +} + +namespace Gob { + +class GobEngine; + +class Surface; +class ANIFile; + +namespace OnceUpon { + +/** The stork in Baba Yaga / dragon in Abracadabra. */ +class Stork : public ANIObject { +public: +	/** Information on how to drop the bundle. */ +	struct BundleDrop { +		int16 anim; ///< Animation of the bundle floating down + +		int16 dropX; ///< X position the stork drops the bundle +		int16 landY; ///< Y position the bundle lands + +		bool dropWhileFar; ///< Does the stork drop the bundle while far instead of near? +	}; + +	Stork(GobEngine *vm, const ANIFile &ani); +	~Stork(); + +	/** Has the bundle landed? */ +	bool hasBundleLanded() const; + +	/** Drop the bundle. */ +	void dropBundle(const BundleDrop &drop); + +	/** Draw the current frame onto the surface and return the affected rectangle. */ +	bool draw(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); +	/** Draw the current frame from the surface and return the affected rectangle. */ +	bool clear(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); + +	/** Advance the animation to the next frame. */ +	void advance(); + +private: +	enum State { +		kStateFlyNearWithBundle    = 0, +		kStateFlyFarWithBundle       , +		kStateFlyNearWithoutBundle   , +		kStateFlyFarWithoutBundle +	}; + + +	GobEngine *_vm; + +	Surface   *_frame; +	ANIObject *_bundle; + +	State _state; + +	bool       _shouldDrop; +	BundleDrop _bundleDrop; + + +	void setState(State state, uint16 anim); +	void setState(State state, uint16 anim, int16 x); + +	void dropBundle(State state, uint16 anim); +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_STORK_H diff --git a/engines/gob/pregob/onceupon/title.cpp b/engines/gob/pregob/onceupon/title.cpp new file mode 100644 index 0000000000..5163ff6822 --- /dev/null +++ b/engines/gob/pregob/onceupon/title.cpp @@ -0,0 +1,117 @@ +/* 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 "gob/gob.h" +#include "gob/global.h" +#include "gob/palanim.h" +#include "gob/draw.h" + +#include "gob/sound/sound.h" + +#include "gob/pregob/onceupon/title.h" + +namespace Gob { + +namespace OnceUpon { + +Title::Title(GobEngine *vm) : SEQFile(vm, "ville.seq") { +} + +Title::~Title() { +} + +void Title::play() { +	SEQFile::play(true, 0xFFFF, 15); + +	// After playback, fade out and stop the music +	if (!_vm->shouldQuit()) +		_vm->_palAnim->fade(0, 0, 0); + +	stopMusic(); +} + +void Title::handleFrameEvent() { +	// On fame 0, start the music and fade in +	if (getFrame() == 0) { +		playMusic(); + +		_vm->_draw->forceBlit(); +		_vm->_palAnim->fade(_vm->_global->_pPaletteDesc, 0, 0); +	} +} + +void Title::playMusic() { +	// Look at what platform this is and play the appropriate music type + +	if      (_vm->getPlatform() == Common::kPlatformPC) +		playMusicDOS(); +	else if (_vm->getPlatform() == Common::kPlatformAmiga) +		playMusicAmiga(); +	else if (_vm->getPlatform() == Common::kPlatformAtariST) +		playMusicAtariST(); +} + +void Title::playMusicDOS() { +	// Play an AdLib track + +	_vm->_sound->adlibLoadTBR("babayaga.tbr"); +	_vm->_sound->adlibLoadMDY("babayaga.mdy"); +	_vm->_sound->adlibSetRepeating(-1); +	_vm->_sound->adlibPlay(); +} + +void Title::playMusicAmiga() { +	// Play a Protracker track + +	_vm->_sound->protrackerPlay("mod.babayaga"); +} + +void Title::playMusicAtariST() { +	// Play a Soundblaster composition + +	static const int16        titleMusic[21] = { 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 2, 0, 1, 0, 0, 0, 0, 0, -1}; +	static const char * const titleFiles[ 3] = {"baba1.snd", "baba2.snd", "baba3.snd"}; + +	for (uint i = 0; i < ARRAYSIZE(titleFiles); i++) +		_vm->_sound->sampleLoad(_vm->_sound->sampleGetBySlot(i), SOUND_SND, titleFiles[i]); + +	_vm->_sound->blasterPlayComposition(titleMusic, 0); +	_vm->_sound->blasterRepeatComposition(-1); +} + +void Title::stopMusic() { +	// Just stop everything + +	_vm->_sound->adlibSetRepeating(0); +	_vm->_sound->blasterRepeatComposition(0); + +	_vm->_sound->adlibStop(); +	_vm->_sound->blasterStopComposition(); +	_vm->_sound->protrackerStop(); + +	for (int i = 0; i < ::Gob::Sound::kSoundsCount; i++) +		_vm->_sound->sampleFree(_vm->_sound->sampleGetBySlot(i)); +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/title.h b/engines/gob/pregob/onceupon/title.h new file mode 100644 index 0000000000..5e7ef76d40 --- /dev/null +++ b/engines/gob/pregob/onceupon/title.h @@ -0,0 +1,55 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GOB_PREGOB_ONCEUPON_TITLE_H +#define GOB_PREGOB_ONCEUPON_TITLE_H + +#include "gob/pregob/seqfile.h" + +namespace Gob { + +namespace OnceUpon { + +/** The Once Upon A Time title animation sequence. */ +class Title : public SEQFile { +public: +	Title(GobEngine *vm); +	~Title(); + +	void play(); + +protected: +	void handleFrameEvent(); + +private: +	void playMusic();        ///< Play the title music. +	void playMusicDOS();     ///< Play the title music of the DOS      version. +	void playMusicAmiga();   ///< Play the title music of the Amiga    version. +	void playMusicAtariST(); ///< Play the title music of the Atari ST version. +	void stopMusic();        ///< Stop the title music. +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_TITLE_H diff --git a/engines/gob/pregob/pregob.cpp b/engines/gob/pregob/pregob.cpp new file mode 100644 index 0000000000..54eb3c6795 --- /dev/null +++ b/engines/gob/pregob/pregob.cpp @@ -0,0 +1,351 @@ +/* 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 "graphics/cursorman.h" + +#include "gob/gob.h" +#include "gob/global.h" +#include "gob/util.h" +#include "gob/surface.h" +#include "gob/dataio.h" +#include "gob/palanim.h" +#include "gob/draw.h" +#include "gob/video.h" +#include "gob/aniobject.h" + +#include "gob/sound/sound.h" + +#include "gob/pregob/pregob.h" +#include "gob/pregob/gctfile.h" + + +namespace Gob { + +const char  PreGob::kLanguageSuffixShort[5] = { 't',  'g',  'a',  'e',  'i'}; +const char *PreGob::kLanguageSuffixLong [5] = {"fr", "al", "an", "it", "es"}; + + +PreGob::PreGob(GobEngine *vm) : _vm(vm), _fadedOut(false) { +} + +PreGob::~PreGob() { +} + +void PreGob::fadeOut() { +	if (_fadedOut || _vm->shouldQuit()) +		return; + +	// Fade to black +	_vm->_palAnim->fade(0, 0, 0); + +	_fadedOut = true; +} + +void PreGob::fadeIn() { +	if (!_fadedOut || _vm->shouldQuit()) +		return; + +	// Fade to palette +	_vm->_draw->blitInvalidated(); +	_vm->_palAnim->fade(_vm->_global->_pPaletteDesc, 0, 0); +	_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 0, 0, 319, 199); + +	_fadedOut = false; +} + +void PreGob::clearScreen() { +	_vm->_draw->_backSurface->clear(); +	_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 0, 0, 319, 199); +	_vm->_draw->blitInvalidated(); +	_vm->_video->retrace(); +} + +void PreGob::initScreen() { +	_vm->_util->setFrameRate(15); + +	_fadedOut = true; + +	_vm->_draw->initScreen(); + +	_vm->_draw->_backSurface->clear(); +	_vm->_util->clearPalette(); + +	_vm->_draw->forceBlit(); +	_vm->_video->retrace(); + +	_vm->_util->processInput(); +} + +void PreGob::setPalette(const byte *palette, uint16 size) { +	memcpy(_vm->_draw->_vgaPalette, palette, 3 * size); + +	// If we didn't fade out prior, immediately set the palette +	if (!_fadedOut) +		_vm->_video->setFullPalette(_vm->_global->_pPaletteDesc); +} + +void PreGob::addCursor() { +	CursorMan.pushCursor(0, 0, 0, 0, 0, 0); +} + +void PreGob::removeCursor() { +	CursorMan.popCursor(); +} + +void PreGob::setCursor(Surface &sprite, int16 hotspotX, int16 hotspotY) { +	CursorMan.replaceCursor(sprite.getData(), sprite.getWidth(), sprite.getHeight(), hotspotX, hotspotY, 0); +} + +void PreGob::setCursor(Surface &sprite, int16 left, int16 top, int16 right, int16 bottom, +                       int16 hotspotX, int16 hotspotY) { + +	const int width  = right  - left + 1; +	const int height = bottom - top  + 1; + +	if ((width <= 0) || (height <= 0)) +		return; + +	Surface cursor(width, height, 1); + +	cursor.blit(sprite, left, top, right, bottom, 0, 0); + +	setCursor(cursor, hotspotX, hotspotX); +} + +void PreGob::showCursor() { +	CursorMan.showMouse(true); + +	_vm->_draw->_showCursor = 4; +} + +void PreGob::hideCursor() { +	CursorMan.showMouse(false); + +	_vm->_draw->_showCursor = 0; +} + +bool PreGob::isCursorVisible() const { +	return CursorMan.isVisible(); +} + +void PreGob::loadSounds(const char * const *sounds, uint soundCount) { +	freeSounds(); + +	_sounds.resize(soundCount); + +	for (uint i = 0; i < soundCount; i++) +		loadSound(_sounds[i], sounds[i]); +} + +void PreGob::freeSounds() { +	_sounds.clear(); +} + +bool PreGob::loadSound(SoundDesc &sound, const Common::String &file) const { +	return _vm->_sound->sampleLoad(&sound, SOUND_SND, file.c_str()); +} + +void PreGob::playSound(uint sound, int16 frequency, int16 repCount) { +	if (sound >= _sounds.size()) +		return; + +	_vm->_sound->blasterPlay(&_sounds[sound], repCount, frequency); +} + +void PreGob::stopSound() { +	_vm->_sound->blasterStop(0); +} + +void PreGob::playSoundFile(const Common::String &file, int16 frequency, int16 repCount, bool interruptible) { +	stopSound(); + +	SoundDesc sound; +	if (!loadSound(sound, file)) +		return; + +	_vm->_sound->blasterPlay(&sound, repCount, frequency); + +	_vm->_util->forceMouseUp(); + +	bool finished = false; +	while (!_vm->shouldQuit() && !finished && _vm->_sound->blasterPlayingSound()) { +		endFrame(true); + +		finished = hasInput(); +	} + +	_vm->_util->forceMouseUp(); + +	stopSound(); +} + +void PreGob::beep(int16 frequency, int32 length) { +	_vm->_sound->speakerOn(frequency, length); +} + +void PreGob::endFrame(bool doInput) { +	_vm->_draw->blitInvalidated(); +	_vm->_util->waitEndFrame(); + +	if (doInput) +		_vm->_util->processInput(); +} + +int16 PreGob::checkInput(int16 &mouseX, int16 &mouseY, MouseButtons &mouseButtons) { +	_vm->_util->getMouseState(&mouseX, &mouseY, &mouseButtons); +	_vm->_util->forceMouseUp(); + +	return _vm->_util->checkKey(); +} + +int16 PreGob::waitInput(int16 &mouseX, int16 &mouseY, MouseButtons &mouseButtons) { +	bool finished = false; + +	int16 key = 0; +	while (!_vm->shouldQuit() && !finished) { +		endFrame(true); + +		key = checkInput(mouseX, mouseY, mouseButtons); + +		finished = (mouseButtons != kMouseButtonsNone) || (key != 0); +	} + +	return key; +} + +int16 PreGob::waitInput() { +	int16 mouseX, mouseY; +	MouseButtons mouseButtons; + +	return waitInput(mouseX, mouseY, mouseButtons); +} + +bool PreGob::hasInput() { +	int16 mouseX, mouseY; +	MouseButtons mouseButtons; + +	return checkInput(mouseX, mouseY, mouseButtons) || (mouseButtons != kMouseButtonsNone); +} + +void PreGob::clearAnim(ANIObject &anim) { +	int16 left, top, right, bottom; + +	if (anim.clear(*_vm->_draw->_backSurface, left, top, right, bottom)) +		_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); +} + +void PreGob::drawAnim(ANIObject &anim) { +	int16 left, top, right, bottom; + +	if (anim.draw(*_vm->_draw->_backSurface, left, top, right, bottom)) +		_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); +	anim.advance(); +} + +void PreGob::redrawAnim(ANIObject &anim) { +	clearAnim(anim); +	drawAnim(anim); +} + +void PreGob::clearAnim(const ANIList &anims) { +	for (int i = (anims.size() - 1); i >= 0; i--) +		clearAnim(*anims[i]); +} + +void PreGob::drawAnim(const ANIList &anims) { +	for (ANIList::const_iterator a = anims.begin(); a != anims.end(); ++a) +		drawAnim(**a); +} + +void PreGob::redrawAnim(const ANIList &anims) { +	clearAnim(anims); +	drawAnim(anims); +} + +void PreGob::loadAnims(ANIList &anims, ANIFile &ani, uint count, const AnimProperties *props) const { +	freeAnims(anims); + +	anims.resize(count); +	for (uint i = 0; i < count; i++) { +		anims[i] = new ANIObject(ani); + +		setAnim(*anims[i], props[i]); +	} +} + +void PreGob::freeAnims(ANIList &anims) const { +	for (ANIList::iterator a = anims.begin(); a != anims.end(); ++a) +		delete *a; + +	anims.clear(); +} + +void PreGob::setAnim(ANIObject &anim, const AnimProperties &props) const { +	anim.setAnimation(props.animation); +	anim.setFrame(props.frame); +	anim.setMode(props.mode); +	anim.setPause(props.paused); +	anim.setVisible(props.visible); + +	if (props.hasPosition) +		anim.setPosition(props.x, props.y); +	else +		anim.setPosition(); +} + +Common::String PreGob::getLocFile(const Common::String &file) const { +	if (_vm->_global->_language >= ARRAYSIZE(kLanguageSuffixShort)) +		return file; + +	return file + kLanguageSuffixShort[_vm->_global->_language]; +} + +TXTFile *PreGob::loadTXT(const Common::String &txtFile, TXTFile::Format format) const { +	Common::SeekableReadStream *txtStream = _vm->_dataIO->getFile(txtFile); +	if (!txtStream) +		error("PreGob::loadTXT(): Failed to open \"%s\"", txtFile.c_str()); + +	TXTFile *txt = new TXTFile(*txtStream, format); + +	delete txtStream; + +	fixTXTStrings(*txt); + +	return txt; +} + +void PreGob::fixTXTStrings(TXTFile &txt) const { +} + +GCTFile *PreGob::loadGCT(const Common::String &gctFile) const { +	Common::SeekableReadStream *gctStream = _vm->_dataIO->getFile(gctFile); +	if (!gctStream) +		error("PreGob::loadGCT(): Failed to open \"%s\"", gctFile.c_str()); + +	GCTFile *gct = new GCTFile(*gctStream, _vm->_rnd); + +	delete gctStream; + +	return gct; +} + +} // End of namespace Gob diff --git a/engines/gob/pregob/pregob.h b/engines/gob/pregob/pregob.h new file mode 100644 index 0000000000..632f85b88e --- /dev/null +++ b/engines/gob/pregob/pregob.h @@ -0,0 +1,194 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GOB_PREGOB_PREGOB_H +#define GOB_PREGOB_PREGOB_H + +#include "common/str.h" +#include "common/array.h" + +#include "gob/util.h" +#include "gob/aniobject.h" + +#include "gob/sound/sounddesc.h" + +#include "gob/pregob/txtfile.h" + +namespace Gob { + +class GobEngine; +class Surface; + +class GCTFile; + +class PreGob { +public: +	PreGob(GobEngine *vm); +	virtual ~PreGob(); + +	virtual void run() = 0; + +	struct AnimProperties { +		uint16 animation; +		uint16 frame; + +		ANIObject::Mode mode; + +		bool visible; +		bool paused; + +		bool hasPosition; +		int16 x; +		int16 y; +	}; + +protected: +	typedef Common::Array<ANIObject *> ANIList; + +	static const char  kLanguageSuffixShort[5]; +	static const char *kLanguageSuffixLong [5]; + + +	GobEngine *_vm; + + +	// -- Graphics -- + +	/** Initialize the game screen. */ +	void initScreen(); + +	void fadeOut(); ///< Fade to black. +	void fadeIn();  ///< Fade to the current palette. + +	void clearScreen(); + +	/** Change the palette. +	 * +	 *  @param palette The palette to change to. +	 *  @param size Size of the palette in colors. +	 */ +	void setPalette(const byte *palette, uint16 size); ///< Change the palette + +	/** Add a new cursor that can be manipulated to the stack. */ +	void addCursor(); +	/** Remove the top-most cursor from the stack. */ +	void removeCursor(); + +	/** Set the current cursor. */ +	void setCursor(Surface &sprite, int16 hotspotX, int16 hotspotY); +	/** Set the current cursor. */ +	void setCursor(Surface &sprite, int16 left, int16 top, int16 right, int16 bottom, +	               int16 hotspotX, int16 hotspotY); + +	/** Show the cursor. */ +	void showCursor(); +	/** Hide the cursor. */ +	void hideCursor(); + +	/** Is the cursor currently visible? */ +	bool isCursorVisible() const; + +	/** Remove an animation from the screen. */ +	void clearAnim(ANIObject &anim); +	/** Draw an animation to the screen, advancing it. */ +	void drawAnim(ANIObject &anim); +	/** Clear and draw an animation to the screen, advancing it. */ +	void redrawAnim(ANIObject &anim); + +	/** Remove animations from the screen. */ +	void clearAnim(const ANIList &anims); +	/** Draw animations to the screen, advancing them. */ +	void drawAnim(const ANIList &anims); +	/** Clear and draw animations to the screen, advancing them. */ +	void redrawAnim(const ANIList &anims); + +	void loadAnims(ANIList &anims, ANIFile &ani, uint count, const AnimProperties *props) const; +	void freeAnims(ANIList &anims) const; + +	void setAnim(ANIObject &anim, const AnimProperties &props) const; + +	/** Wait for the frame to end, handling screen updates and optionally update input. */ +	void endFrame(bool doInput); + + +	// -- Sound -- + +	/** Load all sounds that can be played interactively in the game. */ +	void loadSounds(const char * const *sounds, uint soundCount); +	/** Free all loaded sound. */ +	void freeSounds(); + +	/** Play a loaded sound. */ +	void playSound(uint sound, int16 frequency = 0, int16 repCount = 0); +	/** Stop all sound playback. */ +	void stopSound(); + +	/** Play a sound until it ends or is interrupted by a keypress. */ +	void playSoundFile(const Common::String &file, int16 frequency = 0, int16 repCount = 0, bool interruptible = true); + +	/** Beep the PC speaker. */ +	void beep(int16 frequency, int32 length); + + +	// -- Input -- + +	/** Check mouse and keyboard input. */ +	int16 checkInput(int16 &mouseX, int16 &mouseY, MouseButtons &mouseButtons); +	/** Wait for mouse or keyboard input. */ +	int16 waitInput (int16 &mouseX, int16 &mouseY, MouseButtons &mouseButtons); +	/** Wait for mouse or keyboard input, but don't care about what was done with the mouse. */ +	int16 waitInput(); +	/** Did we have mouse or keyboard input? */ +	bool  hasInput(); + + +	// -- TXT helpers -- + +	/** Get the name of a localized file. */ +	Common::String getLocFile(const Common::String &file) const; +	/** Open a TXT file. */ +	TXTFile *loadTXT(const Common::String &txtFile, TXTFile::Format format) const; + +	/** Called by loadTXT() to fix strings within the TXT file. */ +	virtual void fixTXTStrings(TXTFile &txt) const; + + +	// -- GCT helpers -- + +	GCTFile *loadGCT(const Common::String &gctFile) const; + + +private: +	/** Did we fade out? */ +	bool _fadedOut; + +	/** All loaded sounds. */ +	Common::Array<SoundDesc> _sounds; + + +	/** Load a sound file. */ +	bool loadSound(SoundDesc &sound, const Common::String &file) const; +}; + +} // End of namespace Gob + +#endif // GOB_PREGOB_PREGOB_H diff --git a/engines/gob/pregob/seqfile.cpp b/engines/gob/pregob/seqfile.cpp new file mode 100644 index 0000000000..91973bbb85 --- /dev/null +++ b/engines/gob/pregob/seqfile.cpp @@ -0,0 +1,384 @@ +/* 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/str.h" +#include "common/stream.h" + +#include "gob/gob.h" +#include "gob/dataio.h" +#include "gob/draw.h" +#include "gob/decfile.h" +#include "gob/anifile.h" +#include "gob/aniobject.h" + +#include "gob/pregob/seqfile.h" + +namespace Gob { + +SEQFile::SEQFile(GobEngine *vm, const Common::String &fileName) : _vm(vm) { +	for (uint i = 0; i < kObjectCount; i++) +		_objects[i].object = 0; + +	Common::SeekableReadStream *seq = _vm->_dataIO->getFile(Util::setExtension(fileName, ".SEQ")); +	if (!seq) { +		warning("SEQFile::SEQFile(): No such file \"%s\"", fileName.c_str()); +		return; +	} + +	load(*seq); + +	delete seq; +} + +SEQFile::~SEQFile() { +	for (uint i = 0; i < kObjectCount; i++) +		delete _objects[i].object; + +	for (Backgrounds::iterator b = _backgrounds.begin(); b != _backgrounds.end(); ++b) +		delete *b; + +	for (Animations::iterator a = _animations.begin(); a != _animations.end(); ++a) +		delete *a; +} + +void SEQFile::load(Common::SeekableReadStream &seq) { +	const uint16 decCount = (uint16)seq.readByte() + 1; +	const uint16 aniCount = (uint16)seq.readByte() + 1; + +	// Load backgrounds +	_backgrounds.reserve(decCount); +	for (uint i = 0; i < decCount; i++) { +		const Common::String dec = Util::readString(seq, 13); + +		if (!_vm->_dataIO->hasFile(dec)) { +			warning("SEQFile::load(): No such background \"%s\"", dec.c_str()); +			return; +		} + +		_backgrounds.push_back(new DECFile(_vm, dec, 320, 200)); +	} + +	// Load animations +	_animations.reserve(aniCount); +	for (uint i = 0; i < aniCount; i++) { +		const Common::String ani = Util::readString(seq, 13); + +		if (!_vm->_dataIO->hasFile(ani)) { +			warning("SEQFile::load(): No such animation \"%s\"", ani.c_str()); +			return; +		} + +		_animations.push_back(new ANIFile(_vm, ani)); +	} + +	_frameRate = seq.readUint16LE(); + +	// Load background change keys + +	const uint16 bgKeyCount = seq.readUint16LE(); +	_bgKeys.resize(bgKeyCount); + +	for (uint16 i = 0; i < bgKeyCount; i++) { +		const uint16 frame = seq.readUint16LE(); +		const uint16 index = seq.readUint16LE(); + +		_bgKeys[i].frame      = frame; +		_bgKeys[i].background = index < _backgrounds.size() ? _backgrounds[index] : 0; +	} + +	// Load animation keys for all 4 objects + +	for (uint i = 0; i < kObjectCount; i++) { +		const uint16 animKeyCount = seq.readUint16LE(); +		_animKeys.reserve(_animKeys.size() + animKeyCount); + +		for (uint16 j = 0; j < animKeyCount; j++) { +			_animKeys.push_back(AnimationKey()); + +			const uint16 frame = seq.readUint16LE(); +			const uint16 index = seq.readUint16LE(); + +			uint16 animation; +			const ANIFile *ani = findANI(index, animation); + +			_animKeys.back().object    = i; +			_animKeys.back().frame     = frame; +			_animKeys.back().ani       = ani; +			_animKeys.back().animation = animation; +			_animKeys.back().x         = seq.readSint16LE(); +			_animKeys.back().y         = seq.readSint16LE(); +			_animKeys.back().order     = seq.readSint16LE(); +		} +	} + +} + +const ANIFile *SEQFile::findANI(uint16 index, uint16 &animation) { +	animation = 0xFFFF; + +	// 0xFFFF = remove animation +	if (index == 0xFFFF) +		return 0; + +	for (Animations::const_iterator a = _animations.begin(); a != _animations.end(); ++a) { +		if (index < (*a)->getAnimationCount()) { +			animation = index; +			return *a; +		} + +		index -= (*a)->getAnimationCount(); +	} + +	return 0; +} + +void SEQFile::play(bool abortable, uint16 endFrame, uint16 frameRate) { +	if (_bgKeys.empty() && _animKeys.empty()) +		// Nothing to do +		return; + +	// Init + +	_frame = 0; +	_abortPlay = false; + +	for (uint i = 0; i < kObjectCount; i++) { +		delete _objects[i].object; + +		_objects[i].object = 0; +		_objects[i].order  = 0; +	} + +	for (Loops::iterator l = _loops.begin(); l != _loops.end(); ++l) +		l->currentLoop = 0; + +	// Set the frame rate + +	int16 frameRateBack = _vm->_util->getFrameRate(); + +	if (frameRate == 0) +		frameRate = _frameRate; + +	_vm->_util->setFrameRate(frameRate); + +	_abortable = abortable; + +	while (!_vm->shouldQuit() && !_abortPlay) { +		// Handle the frame contents +		playFrame(); + +		// Handle extra frame events +		handleFrameEvent(); + +		// Wait for the frame to end +		_vm->_draw->blitInvalidated(); +		_vm->_util->waitEndFrame(); + +		// Handle input + +		_vm->_util->processInput(); + +		int16 key = _vm->_util->checkKey(); + +		int16 mouseX, mouseY; +		MouseButtons mouseButtons; +		_vm->_util->getMouseState(&mouseX, &mouseY, &mouseButtons); +		_vm->_util->forceMouseUp(); + +		handleInput(key, mouseX, mouseY, mouseButtons); + +		// Loop + +		bool looped = false; +		for (Loops::iterator l = _loops.begin(); l != _loops.end(); ++l) { +			if ((l->endFrame == _frame) && (l->currentLoop < l->loopCount)) { +				_frame = l->startFrame; + +				l->currentLoop++; +				looped = true; +			} +		} + +		// If we didn't loop, advance the frame and look if we should end here + +		if (!looped) { +			_frame++; +			if (_frame >= endFrame) +				break; +		} +	} + +	// Restore the frame rate +	_vm->_util->setFrameRate(frameRateBack); +} + +void SEQFile::playFrame() { +	// Remove the current animation frames +	clearAnims(); + +	// Handle background keys, directly updating the background +	for (BackgroundKeys::const_iterator b = _bgKeys.begin(); b != _bgKeys.end(); ++b) { +		if (!b->background || (b->frame != _frame)) +			continue; + +		b->background->draw(*_vm->_draw->_backSurface); + +		_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 0, 0, 319, 199); +	} + +	// Handle the animation keys, updating the objects +	for (AnimationKeys::const_iterator a = _animKeys.begin(); a != _animKeys.end(); ++a) { +		if (a->frame != _frame) +			continue; + +		Object &object = _objects[a->object]; + +		delete object.object; +		object.object = 0; + +		// No valid animation => remove +		if ((a->animation == 0xFFFF) || !a->ani) +			continue; + +		// Change the animation + +		object.object = new ANIObject(*a->ani); + +		object.object->setAnimation(a->animation); +		object.object->setPosition(a->x, a->y); +		object.object->setVisible(true); +		object.object->setPause(false); + +		object.order = a->order; +	} + +	// Draw the animations +	drawAnims(); +} + +// NOTE: This is really not at all efficient. However, since there's only a +//       small number of objects, it should matter. We really do need a stable +//       sort, though, so Common::sort() is out. +SEQFile::Objects SEQFile::getOrderedObjects() { +	int16 minOrder = (int16)0x7FFF; +	int16 maxOrder = (int16)0x8000; + +	Objects objects; + +	// Find the span of order values +	for (uint i = 0; i < kObjectCount; i++) { +		if (!_objects[i].object) +			continue; + +		minOrder = MIN(minOrder, _objects[i].order); +		maxOrder = MAX(maxOrder, _objects[i].order); +	} + +	// Stably sort the objects by order value +	for (int16 o = minOrder; o <= maxOrder; o++) +		for (uint i = 0; i < kObjectCount; i++) +			if (_objects[i].object && (_objects[i].order == o)) +				objects.push_back(_objects[i]); + +	return objects; +} + +void SEQFile::clearAnims() { +	Objects objects = getOrderedObjects(); + +	// Remove the animation frames, in reverse drawing order +	for (Objects::iterator o = objects.reverse_begin(); o != objects.end(); --o) { +		int16 left, top, right, bottom; + +		if (o->object->clear(*_vm->_draw->_backSurface, left, top, right, bottom)) +			_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); +	} +} + +void SEQFile::drawAnims() { +	Objects objects = getOrderedObjects(); + +	// Draw the animation frames and advance the animation +	for (Objects::iterator o = objects.begin(); o != objects.end(); ++o) { +		int16 left, top, right, bottom; + +		if (o->object->draw(*_vm->_draw->_backSurface, left, top, right, bottom)) +			_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + +		o->object->advance(); +	} +} + +uint16 SEQFile::getFrame() const { +	return _frame; +} + +void SEQFile::seekFrame(uint16 frame) { +	_frame = frame; +} + +uint SEQFile::addLoop(uint16 startFrame, uint16 endFrame, uint16 loopCount) { +	_loops.resize(_loops.size() + 1); + +	_loops.back().startFrame  = startFrame; +	_loops.back().endFrame    = endFrame; +	_loops.back().loopCount   = loopCount; +	_loops.back().currentLoop = 0; +	_loops.back().empty       = false; + +	return _loops.size() - 1; +} + +void SEQFile::skipLoop(uint loopID) { +	if (loopID >= _loops.size()) +		return; + +	_loops[loopID].currentLoop = 0xFFFF; +} + +void SEQFile::delLoop(uint loopID) { +	if (loopID >= _loops.size()) +		return; + +	_loops[loopID].empty = true; + +	cleanLoops(); +} + +void SEQFile::cleanLoops() { +	while (!_loops.empty() && _loops.back().empty) +		_loops.pop_back(); +} + +void SEQFile::abortPlay() { +	_abortPlay = true; +} + +void SEQFile::handleFrameEvent() { +} + +void SEQFile::handleInput(int16 key, int16 mouseX, int16 mouseY, MouseButtons mouseButtons) { +	if (_abortable && ((key != 0) || (mouseButtons != kMouseButtonsNone))) +		abortPlay(); +} + +} // End of namespace Gob diff --git a/engines/gob/pregob/seqfile.h b/engines/gob/pregob/seqfile.h new file mode 100644 index 0000000000..5e12962ef9 --- /dev/null +++ b/engines/gob/pregob/seqfile.h @@ -0,0 +1,193 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GOB_PREGOB_SEQFILE_H +#define GOB_PREGOB_SEQFILE_H + +#include "common/system.h" +#include "common/array.h" +#include "common/list.h" + +#include "gob/util.h" + +namespace Common { +	class String; +	class SeekableReadStream; +} + +namespace Gob { + +class GobEngine; + +class DECFile; +class ANIFile; +class ANIObject; + +/** A SEQ file, describing a complex animation sequence. + * + *  Used in early hardcoded gob games. + *  The principle is similar to the Mult class (see mult.h), but instead + *  of depending on all the externally loaded animations, backgrounds and + *  objects, a SEQ file references animation and background directly by + *  filename. + */ +class SEQFile { +public: +	SEQFile(GobEngine *vm, const Common::String &fileName); +	virtual ~SEQFile(); + +	/** Play the SEQ. +	 * +	 *  @param abortable If true, end playback on any user input. +	 *  @param endFrame  The frame on where to end, or 0xFFFF for infinite playback. +	 *  @param frameRate The frame rate at which to play the SEQ, or 0 for playing at +	 *                   the speed the SEQ itself wants to. +	 */ +	void play(bool abortable = true, uint16 endFrame = 0xFFFF, uint16 frameRate = 0); + + +protected: +	GobEngine *_vm; + + +	/** Returns the current frame number. */ +	uint16 getFrame() const; + +	/** Seek to a specific frame. */ +	void seekFrame(uint16 frame); + +	/** Add a frame loop. */ +	uint addLoop(uint16 startFrame, uint16 endFrame, uint16 loopCount); + +	/** Skip a frame loop. */ +	void skipLoop(uint loopID); + +	/** Delete a frame loop. */ +	void delLoop(uint loopID); + +	/** Ends SEQ playback. */ +	void abortPlay(); + +	/** Callback for special frame events. */ +	virtual void handleFrameEvent(); +	/** Callback for special user input handling. */ +	virtual void handleInput(int16 key, int16 mouseX, int16 mouseY, MouseButtons mouseButtons); + + +private: +	/** Number of animation objects that are visible at the same time. */ +	static const uint kObjectCount = 4; + +	/** A key for changing the background. */ +	struct BackgroundKey { +		uint16 frame; ///< Frame the change is to happen. + +		const DECFile *background; ///< The background to use. +	}; + +	/** A key for playing an object animation. */ +	struct AnimationKey { +		uint object; ///< The object this key belongs to. + +		uint16 frame; ///< Frame the change is to happen. + +		const ANIFile *ani; ///< The ANI to use. + +		uint16 animation; ///< The animation to use. + +		int16 x; ///< X position of the animation. +		int16 y; ///< Y position of the animation. + +		int16 order; ///< Used to determine in which order to draw the objects. +	}; + +	/** A managed animation object. */ +	struct Object { +		ANIObject *object; ///< The actual animation object. + +		int16 order; ///< The current drawing order. +	}; + +	/** A frame loop. */ +	struct Loop { +		uint16 startFrame; +		uint16 endFrame; + +		uint16 loopCount; +		uint16 currentLoop; + +		bool empty; +	}; + +	typedef Common::Array<DECFile *> Backgrounds; +	typedef Common::Array<ANIFile *> Animations; + +	typedef Common::Array<BackgroundKey> BackgroundKeys; +	typedef Common::Array<AnimationKey>  AnimationKeys; + +	typedef Common::List<Object> Objects; + +	typedef Common::Array<Loop> Loops; + + +	uint16 _frame;     ///< The current frame. +	bool   _abortPlay; ///< Was the end of the playback requested? + +	uint16 _frameRate; + +	Backgrounds _backgrounds; ///< All backgrounds in this SEQ. +	Animations  _animations;  ///< All animations in this SEQ. + +	BackgroundKeys _bgKeys;   ///< The background change keyframes. +	AnimationKeys  _animKeys; ///< The animation change keyframes. + +	Object _objects[kObjectCount]; ///< The managed animation objects. + +	Loops _loops; + +	/** Whether the playback should be abortable by user input. */ +	bool _abortable; + + +	// -- Loading helpers -- + +	void load(Common::SeekableReadStream &seq); + +	const ANIFile *findANI(uint16 index, uint16 &animation); + +	// -- Playback helpers -- + +	void playFrame(); + +	/** Get a list of objects ordered by drawing order. */ +	Objects getOrderedObjects(); + +	void clearAnims(); ///< Remove all animation frames. +	void drawAnims();  ///< Draw the animation frames. + +	/** Look if we can compact the loop array. */ +	void cleanLoops(); +}; + +} // End of namespace Gob + +#endif // GOB_PREGOB_SEQFILE_H diff --git a/engines/gob/pregob/txtfile.cpp b/engines/gob/pregob/txtfile.cpp new file mode 100644 index 0000000000..3ff0d4b039 --- /dev/null +++ b/engines/gob/pregob/txtfile.cpp @@ -0,0 +1,232 @@ +/* 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/stream.h" + +#include "gob/draw.h" + +#include "gob/pregob/txtfile.h" + +namespace Gob { + +TXTFile::TXTFile(Common::SeekableReadStream &txt, Format format) { +	load(txt, format); +} + +TXTFile::~TXTFile() { +} + +TXTFile::LineArray &TXTFile::getLines() { +	return _lines; +} + +void TXTFile::load(Common::SeekableReadStream &txt, Format format) { +	if (format == kFormatStringPositionColorFont) { +		int numLines = getInt(txt); + +		_lines.reserve(numLines); +	} + +	while (!txt.eos()) { +		Line line; + +		line.text  =                                              getStr(txt); +		line.x     = (format >= kFormatStringPosition)          ? getInt(txt) : 0; +		line.y     = (format >= kFormatStringPosition)          ? getInt(txt) : 0; +		line.color = (format >= kFormatStringPositionColor)     ? getInt(txt) : 0; +		line.font  = (format >= kFormatStringPositionColorFont) ? getInt(txt) : 0; + +		_lines.push_back(line); +	} + +	while (!_lines.empty() && _lines.back().text.empty()) +		_lines.pop_back(); +} + +bool TXTFile::draw(Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom, +                   const Font * const *fonts, uint fontCount, int color) { + +	trashBuffer(); + +	if (!getArea(left, top, right, bottom, fonts, fontCount)) +		return false; + +	resizeBuffer(right - left + 1, bottom - top + 1); +	saveScreen(surface, left, top, right, bottom); + +	for (LineArray::const_iterator l = _lines.begin(); l != _lines.end(); ++l) { +		if (l->font >= fontCount) +			continue; + +		fonts[l->font]->drawString(l->text, l->x, l->y, (color < 0) ? l->color : color, 0, true, surface); +	} + +	return true; +} + +bool TXTFile::draw(uint line, Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom, +                   const Font * const *fonts, uint fontCount, int color) { + +	trashBuffer(); + +	if (!getArea(line, left, top, right, bottom, fonts, fontCount)) +		return false; + +	resizeBuffer(right - left + 1, bottom - top + 1); +	saveScreen(surface, left, top, right, bottom); + +	const Line &l = _lines[line]; + +	fonts[l.font]->drawString(l.text, l.x, l.y, (color < 0) ? l.color : color, 0, true, surface); + +	return true; +} + +bool TXTFile::draw(Surface &surface, const Font * const *fonts, uint fontCount, int color) { +	int16 left, top, right, bottom; + +	return draw(surface, left, top, right, bottom, fonts, fontCount, color); +} + +bool TXTFile::draw(uint line, Surface &surface, const Font * const *fonts, uint fontCount, int color) { +	int16 left, top, right, bottom; + +	return draw(line, surface, left, top, right, bottom, fonts, fontCount, color); +} + +bool TXTFile::clear(Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom) { +	return restoreScreen(surface, left, top, right, bottom); +} + +bool TXTFile::getArea(int16 &left, int16 &top, int16 &right, int16 &bottom, +                      const Font * const *fonts, uint fontCount) const { + +	bool hasLine = false; + +	left   = 0x7FFF; +	top    = 0x7FFF; +	right  = 0x0000; +	bottom = 0x0000; + +	for (uint i = 0; i < _lines.size(); i++) { +		int16 lLeft, lTop, lRight, lBottom; + +		if (getArea(i, lLeft, lTop, lRight, lBottom, fonts, fontCount)) { +			left   = MIN(left  , lLeft  ); +			top    = MIN(top   , lTop   ); +			right  = MAX(right , lRight ); +			bottom = MAX(bottom, lBottom); + +			hasLine = true; +		} +	} + +	return hasLine; +} + +bool TXTFile::getArea(uint line, int16 &left, int16 &top, int16 &right, int16 &bottom, +                      const Font * const *fonts, uint fontCount) const { + + +	if ((line >= _lines.size()) || (_lines[line].font >= fontCount)) +		return false; + +	const Line &l = _lines[line]; + +	left   = l.x; +	top    = l.y; +	right  = l.x + l.text.size() * fonts[l.font]->getCharWidth()  - 1; +	bottom = l.y +                 fonts[l.font]->getCharHeight() - 1; + +	return true; +} + +Common::String TXTFile::getStr(Common::SeekableReadStream &txt) { +	// Skip all ' ', '\n' and '\r' +	while (!txt.eos()) { +		char c = txt.readByte(); +		if (txt.eos()) +			break; + +		if ((c != ' ') && (c != '\n') && (c != '\r')) { +			txt.seek(-1, SEEK_CUR); +			break; +		} +	} + +	if (txt.eos()) +		return ""; + +	// Read string until ' ', '\n' or '\r' +	Common::String string; +	while (!txt.eos()) { +		char c = txt.readByte(); +		if ((c == ' ') || (c == '\n') || (c == '\r')) +			break; + +		string += c; +	} + +	// Replace all '#' with ' ' and throw out non-printables +	Common::String cleanString; + +	for (uint i = 0; i < string.size(); i++) { +		if      (string[i] == '#') +			cleanString += ' '; +		else if ((unsigned char)string[i] >= ' ') +			cleanString += string[i]; +	} + +	return cleanString; +} + +int TXTFile::getInt(Common::SeekableReadStream &txt) { +	// Skip all [^-0-9] +	while (!txt.eos()) { +		char c = txt.readByte(); +		if (txt.eos()) +			break; + +		if ((c == '-') || ((c >= '0') && (c <= '9'))) { +			txt.seek(-1, SEEK_CUR); +			break; +		} +	} + +	if (txt.eos()) +		return 0; + +	// Read until [^-0-9] +	Common::String string; +	while (!txt.eos()) { +		char c = txt.readByte(); +		if ((c != '-') && ((c < '0') || (c > '9'))) +			break; + +		string += c; +	} + +	// Convert to integer +	return atoi(string.c_str()); +} + +} // End of namespace Gob diff --git a/engines/gob/pregob/txtfile.h b/engines/gob/pregob/txtfile.h new file mode 100644 index 0000000000..c623b58859 --- /dev/null +++ b/engines/gob/pregob/txtfile.h @@ -0,0 +1,91 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GOB_PREGOB_TXTFILE_H +#define GOB_PREGOB_TXTFILE_H + +#include "common/system.h" +#include "common/str.h" +#include "common/array.h" + +#include "gob/backbuffer.h" + +namespace Common { +	class SeekableReadStream; +} + +namespace Gob { + +class Surface; +class Font; + +class TXTFile : public BackBuffer { +public: +	enum Format { +		kFormatString, +		kFormatStringPosition, +		kFormatStringPositionColor, +		kFormatStringPositionColorFont +	}; + +	struct Line { +		Common::String text; +		int x, y; +		int color; +		uint font; +	}; + +	typedef Common::Array<Line> LineArray; + +	TXTFile(Common::SeekableReadStream &txt, Format format); +	~TXTFile(); + +	LineArray &getLines(); + +	bool draw(           Surface &surface, const Font * const *fonts, uint fontCount, int color = -1); +	bool draw(uint line, Surface &surface, const Font * const *fonts, uint fontCount, int color = -1); + +	bool draw(           Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom, +	          const Font * const *fonts, uint fontCount, int color = -1); +	bool draw(uint line, Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom, +	          const Font * const *fonts, uint fontCount, int color = -1); + +	bool clear(Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom); + +private: +	LineArray _lines; + +	void load(Common::SeekableReadStream &txt, Format format); + +	Common::String getStr(Common::SeekableReadStream &txt); +	int getInt(Common::SeekableReadStream &txt); + + +	bool getArea(           int16 &left, int16 &top, int16 &right, int16 &bottom, +	             const Font * const *fonts, uint fontCount) const; +	bool getArea(uint line, int16 &left, int16 &top, int16 &right, int16 &bottom, +	             const Font * const *fonts, uint fontCount) const; +}; + +} // End of namespace Gob + +#endif // GOB_PREGOB_TXTFILE_H diff --git a/engines/gob/rxyfile.cpp b/engines/gob/rxyfile.cpp index 9702dc8c7f..2ff8c121cd 100644 --- a/engines/gob/rxyfile.cpp +++ b/engines/gob/rxyfile.cpp @@ -21,12 +21,19 @@   */  #include "common/stream.h" +#include "common/substream.h"  #include "gob/rxyfile.h"  namespace Gob {  RXYFile::RXYFile(Common::SeekableReadStream &rxy) : _width(0), _height(0) { +	Common::SeekableSubReadStreamEndian sub(&rxy, 0, rxy.size(), false, DisposeAfterUse::NO); + +	load(sub); +} + +RXYFile::RXYFile(Common::SeekableSubReadStreamEndian &rxy) : _width(0), _height(0) {  	load(rxy);  } @@ -64,22 +71,22 @@ const RXYFile::Coordinates &RXYFile::operator[](uint i) const {  	return _coords[i];  } -void RXYFile::load(Common::SeekableReadStream &rxy) { +void RXYFile::load(Common::SeekableSubReadStreamEndian &rxy) {  	if (rxy.size() < 2)  		return;  	rxy.seek(0); -	_realCount = rxy.readUint16LE(); +	_realCount = rxy.readUint16();  	uint16 count = (rxy.size() - 2) / 8;  	_coords.resize(count);  	for (CoordArray::iterator c = _coords.begin(); c != _coords.end(); ++c) { -		c->left   = rxy.readUint16LE(); -		c->right  = rxy.readUint16LE(); -		c->top    = rxy.readUint16LE(); -		c->bottom = rxy.readUint16LE(); +		c->left   = rxy.readUint16(); +		c->right  = rxy.readUint16(); +		c->top    = rxy.readUint16(); +		c->bottom = rxy.readUint16();  		if (c->left != 0xFFFF) {  			_width  = MAX<uint16>(_width , c->right  + 1); diff --git a/engines/gob/rxyfile.h b/engines/gob/rxyfile.h index bc9600b5b0..4fd46c5e40 100644 --- a/engines/gob/rxyfile.h +++ b/engines/gob/rxyfile.h @@ -28,6 +28,7 @@  namespace Common {  	class SeekableReadStream; +	class SeekableSubReadStreamEndian;  }  namespace Gob { @@ -46,6 +47,7 @@ public:  	};  	RXYFile(Common::SeekableReadStream &rxy); +	RXYFile(Common::SeekableSubReadStreamEndian &rxy);  	RXYFile(uint16 width, uint16 height);  	~RXYFile(); @@ -71,7 +73,7 @@ private:  	uint16 _height; -	void load(Common::SeekableReadStream &rxy); +	void load(Common::SeekableSubReadStreamEndian &rxy);  };  } // End of namespace Gob diff --git a/engines/gob/sound/sound.cpp b/engines/gob/sound/sound.cpp index 403bd632a1..63af6aeef4 100644 --- a/engines/gob/sound/sound.cpp +++ b/engines/gob/sound/sound.cpp @@ -109,7 +109,7 @@ int Sound::sampleGetNextFreeSlot() const {  	return -1;  } -bool Sound::sampleLoad(SoundDesc *sndDesc, SoundType type, const char *fileName, bool tryExist) { +bool Sound::sampleLoad(SoundDesc *sndDesc, SoundType type, const char *fileName) {  	if (!sndDesc)  		return false; @@ -117,12 +117,15 @@ bool Sound::sampleLoad(SoundDesc *sndDesc, SoundType type, const char *fileName,  	int32 size;  	byte *data = _vm->_dataIO->getFile(fileName, size); -	if (!data) { -		warning("Can't open sample file \"%s\"", fileName); + +	if (!data || !sndDesc->load(type, data, size)) { +		delete data; + +		warning("Sound::sampleLoad(): Failed to load sound \"%s\"", fileName);  		return false;  	} -	return sndDesc->load(type, data, size); +	return true;  }  void Sound::sampleFree(SoundDesc *sndDesc, bool noteAdLib, int index) { @@ -458,7 +461,7 @@ void Sound::blasterStop(int16 fadeLength, SoundDesc *sndDesc) {  	_blaster->stopSound(fadeLength, sndDesc);  } -void Sound::blasterPlayComposition(int16 *composition, int16 freqVal, +void Sound::blasterPlayComposition(const int16 *composition, int16 freqVal,  		SoundDesc *sndDescs, int8 sndCount) {  	if (!_blaster)  		return; diff --git a/engines/gob/sound/sound.h b/engines/gob/sound/sound.h index 6ad0ec5483..bbc182d172 100644 --- a/engines/gob/sound/sound.h +++ b/engines/gob/sound/sound.h @@ -51,7 +51,7 @@ public:  	const SoundDesc *sampleGetBySlot(int slot) const;  	int sampleGetNextFreeSlot() const; -	bool sampleLoad(SoundDesc *sndDesc, SoundType type, const char *fileName, bool tryExist = true); +	bool sampleLoad(SoundDesc *sndDesc, SoundType type, const char *fileName);  	void sampleFree(SoundDesc *sndDesc, bool noteAdLib = false, int index = -1); @@ -60,7 +60,7 @@ public:  			int16 frequency, int16 fadeLength = 0);  	void blasterStop(int16 fadeLength, SoundDesc *sndDesc = 0); -	void blasterPlayComposition(int16 *composition, int16 freqVal, +	void blasterPlayComposition(const int16 *composition, int16 freqVal,  			SoundDesc *sndDescs = 0, int8 sndCount = kSoundsCount);  	void blasterStopComposition();  	void blasterRepeatComposition(int32 repCount); diff --git a/engines/gob/sound/soundblaster.cpp b/engines/gob/sound/soundblaster.cpp index 19c2346448..f267eee32d 100644 --- a/engines/gob/sound/soundblaster.cpp +++ b/engines/gob/sound/soundblaster.cpp @@ -88,7 +88,7 @@ void SoundBlaster::nextCompositionPos() {  	_compositionPos = -1;  } -void SoundBlaster::playComposition(int16 *composition, int16 freqVal, +void SoundBlaster::playComposition(const int16 *composition, int16 freqVal,  		SoundDesc *sndDescs, int8 sndCount) {  	_compositionSamples = sndDescs; diff --git a/engines/gob/sound/soundblaster.h b/engines/gob/sound/soundblaster.h index c740ba2269..3c4968d611 100644 --- a/engines/gob/sound/soundblaster.h +++ b/engines/gob/sound/soundblaster.h @@ -41,7 +41,7 @@ public:  			int16 frequency, int16 fadeLength = 0);  	void stopSound(int16 fadeLength, SoundDesc *sndDesc = 0); -	void playComposition(int16 *composition, int16 freqVal, +	void playComposition(const int16 *composition, int16 freqVal,  			SoundDesc *sndDescs = 0, int8 sndCount = 60);  	void stopComposition();  	void endComposition(); diff --git a/engines/gob/surface.cpp b/engines/gob/surface.cpp index 3eaf741be2..afbb7c3bae 100644 --- a/engines/gob/surface.cpp +++ b/engines/gob/surface.cpp @@ -684,6 +684,12 @@ void Surface::shadeRect(uint16 left, uint16 top, uint16 right, uint16 bottom,  } +void Surface::recolor(uint8 from, uint8 to) { +	for (Pixel p = get(); p.isValid(); ++p) +		if (p.get() == from) +			p.set(to); +} +  void Surface::putPixel(uint16 x, uint16 y, uint32 color) {  	if ((x >= _width) || (y >= _height))  		return; diff --git a/engines/gob/surface.h b/engines/gob/surface.h index 09be8d1a49..8f895a7910 100644 --- a/engines/gob/surface.h +++ b/engines/gob/surface.h @@ -156,6 +156,8 @@ public:  	void shadeRect(uint16 left, uint16 top, uint16 right, uint16 bottom,  			uint32 color, uint8 strength); +	void recolor(uint8 from, uint8 to); +  	void putPixel(uint16 x, uint16 y, uint32 color);  	void drawLine(uint16 x0, uint16 y0, uint16 x1, uint16 y1, uint32 color);  	void drawRect(uint16 left, uint16 top, uint16 right, uint16 bottom, uint32 color); diff --git a/engines/gob/util.cpp b/engines/gob/util.cpp index 64dfcf9b12..5ac4ef024e 100644 --- a/engines/gob/util.cpp +++ b/engines/gob/util.cpp @@ -189,12 +189,27 @@ bool Util::getKeyFromBuffer(Common::KeyState &key) {  	return true;  } +static const uint16 kLatin1ToCP850[] = { +	0xFF, 0xAD, 0xBD, 0x9C, 0xCF, 0xBE, 0xDD, 0xF5, 0xF9, 0xB8, 0xA6, 0xAE, 0xAA, 0xF0, 0xA9, 0xEE, +	0xF8, 0xF1, 0xFD, 0xFC, 0xEF, 0xE6, 0xF4, 0xFA, 0xF7, 0xFB, 0xA7, 0xAF, 0xAC, 0xAB, 0xF3, 0xA8, +	0xB7, 0xB5, 0xB6, 0xC7, 0x8E, 0x8F, 0x92, 0x80, 0xD4, 0x90, 0xD2, 0xD3, 0xDE, 0xD6, 0xD7, 0xD8, +	0xD1, 0xA5, 0xE3, 0xE0, 0xE2, 0xE5, 0x99, 0x9E, 0x9D, 0xEB, 0xE9, 0xEA, 0x9A, 0xED, 0xE8, 0xE1, +	0x85, 0xA0, 0x83, 0xC6, 0x84, 0x86, 0x91, 0x87, 0x8A, 0x82, 0x88, 0x89, 0x8D, 0xA1, 0x8C, 0x8B, +	0xD0, 0xA4, 0x95, 0xA2, 0x93, 0xE4, 0x94, 0xF6, 0x9B, 0x97, 0xA3, 0x96, 0x81, 0xEC, 0xE7, 0x98 +}; + +int16 Util::toCP850(uint16 latin1) { +	if ((latin1 < 0xA0) || ((latin1 - 0xA0) >= ARRAYSIZE(kLatin1ToCP850))) +		return 0; + +	return kLatin1ToCP850[latin1 - 0xA0]; +} +  int16 Util::translateKey(const Common::KeyState &key) {  	static struct keyS {  		int16 from;  		int16 to;  	} keys[] = { -		{Common::KEYCODE_INVALID,   kKeyNone     },  		{Common::KEYCODE_BACKSPACE, kKeyBackspace},  		{Common::KEYCODE_SPACE,     kKeySpace    },  		{Common::KEYCODE_RETURN,    kKeyReturn   }, @@ -216,20 +231,88 @@ int16 Util::translateKey(const Common::KeyState &key) {  		{Common::KEYCODE_F10,       kKeyF10      }  	}; +	// Translate special keys  	for (int i = 0; i < ARRAYSIZE(keys); i++)  		if (key.keycode == keys[i].from)  			return keys[i].to; -	if ((key.keycode >= Common::KEYCODE_SPACE) && -	    (key.keycode <= Common::KEYCODE_DELETE)) { - -		// Used as a user input in Gobliins 2 notepad, in the save dialog, ... +	// Return the ascii value, for text input +	if ((key.ascii >= 32) && (key.ascii <= 127))  		return key.ascii; -	} + +	// Translate international characters into CP850 characters +	if ((key.ascii >= 160) && (key.ascii <= 255)) +		return toCP850(key.ascii);  	return 0;  } +static const uint8 kLowerToUpper[][2] = { +	{0x81, 0x9A}, +	{0x82, 0x90}, +	{0x83, 0xB6}, +	{0x84, 0x8E}, +	{0x85, 0xB7}, +	{0x86, 0x8F}, +	{0x87, 0x80}, +	{0x88, 0xD2}, +	{0x89, 0xD3}, +	{0x8A, 0xD4}, +	{0x8B, 0xD8}, +	{0x8C, 0xD7}, +	{0x8D, 0xDE}, +	{0x91, 0x92}, +	{0x93, 0xE2}, +	{0x94, 0x99}, +	{0x95, 0xE3}, +	{0x96, 0xEA}, +	{0x97, 0xEB}, +	{0x95, 0xE3}, +	{0x96, 0xEA}, +	{0x97, 0xEB}, +	{0x9B, 0x9D}, +	{0xA0, 0xB5}, +	{0xA1, 0xD6}, +	{0xA2, 0xE0}, +	{0xA3, 0xE9}, +	{0xA4, 0xA5}, +	{0xC6, 0xC7}, +	{0xD0, 0xD1}, +	{0xE4, 0xE5}, +	{0xE7, 0xE8}, +	{0xEC, 0xED} +}; + +char Util::toCP850Lower(char cp850) { +	const uint8 cp = (unsigned char)cp850; +	if (cp <= 32) +		return cp850; + +	if (cp <= 127) +		return tolower(cp850); + +	for (uint i = 0; i < ARRAYSIZE(kLowerToUpper); i++) +		if (cp == kLowerToUpper[i][1]) +			return (char)kLowerToUpper[i][0]; + +	return cp850; +} + +char Util::toCP850Upper(char cp850) { +	const uint8 cp = (unsigned char)cp850; +	if (cp <= 32) +		return cp850; + +	if (cp <= 127) +		return toupper(cp850); + +	for (uint i = 0; i < ARRAYSIZE(kLowerToUpper); i++) +		if (cp == kLowerToUpper[i][0]) +			return (char)kLowerToUpper[i][1]; + +	return cp850; +} +  int16 Util::getKey() {  	Common::KeyState key; @@ -367,21 +450,29 @@ void Util::notifyNewAnim() {  	_startFrameTime = getTimeKey();  } -void Util::waitEndFrame() { +void Util::waitEndFrame(bool handleInput) {  	int32 time; -	_vm->_video->waitRetrace(); -  	time = getTimeKey() - _startFrameTime;  	if ((time > 1000) || (time < 0)) { +		_vm->_video->retrace();  		_startFrameTime = getTimeKey();  		return;  	} -	int32 toWait = _frameWaitTime - time; +	int32 toWait = 0; +	do { +		if (toWait > 0) +			delay(MIN<int>(toWait, 10)); + +		if (handleInput) +			processInput(); + +		_vm->_video->retrace(); -	if (toWait > 0) -		delay(toWait); +		time   = getTimeKey() - _startFrameTime; +		toWait = _frameWaitTime - time; +	} while (toWait > 0);  	_startFrameTime = getTimeKey();  } diff --git a/engines/gob/util.h b/engines/gob/util.h index b26a78ab2c..a4984c6207 100644 --- a/engines/gob/util.h +++ b/engines/gob/util.h @@ -124,7 +124,7 @@ public:  	int16 getFrameRate();  	void setFrameRate(int16 rate);  	void notifyNewAnim(); -	void waitEndFrame(); +	void waitEndFrame(bool handleInput = true);  	void setScrollOffset(int16 x = -1, int16 y = -1);  	static void insertStr(const char *str1, char *str2, int16 pos); @@ -143,6 +143,11 @@ public:  	/** Read a constant-length string out of a stream. */  	static Common::String readString(Common::SeekableReadStream &stream, int n); +	/** Convert a character in CP850 encoding to the equivalent lower case character. */ +	static char toCP850Lower(char cp850); +	/** Convert a character in CP850 encoding to the equivalent upper case character. */ +	static char toCP850Upper(char cp850); +  	Util(GobEngine *vm);  protected: @@ -166,6 +171,7 @@ protected:  	void addKeyToBuffer(const Common::KeyState &key);  	bool getKeyFromBuffer(Common::KeyState &key);  	int16 translateKey(const Common::KeyState &key); +	int16 toCP850(uint16 latin1);  	void checkJoystick();  	void keyDown(const Common::Event &event); diff --git a/engines/gob/video.cpp b/engines/gob/video.cpp index 3b1c6423bb..64af34cf62 100644 --- a/engines/gob/video.cpp +++ b/engines/gob/video.cpp @@ -84,6 +84,10 @@ uint16 Font::getCharCount() const {  	return _endItem - _startItem + 1;  } +bool Font::hasChar(uint8 c) const { +	return (c >= _startItem) && (c <= _endItem); +} +  bool Font::isMonospaced() const {  	return _charWidths == 0;  } @@ -134,6 +138,23 @@ void Font::drawLetter(Surface &surf, uint8 c, uint16 x, uint16 y,  	}  } +void Font::drawString(const Common::String &str, int16 x, int16 y, int16 color1, int16 color2, +                      bool transp, Surface &dest) const { + +	const char *s = str.c_str(); + +	while (*s != '\0') { +		const int16 charRight  = x + getCharWidth(*s); +		const int16 charBottom = y + getCharHeight(); + +		if ((x >= 0) && (y >= 0) && (charRight <= dest.getWidth()) && (charBottom <= dest.getHeight())) +			drawLetter(dest, *s, x, y, color1, color2, transp); + +		x += getCharWidth(*s); +		s++; +	} +} +  const byte *Font::getCharData(uint8 c) const {  	if (_endItem == 0) {  		warning("Font::getCharData(): _endItem == 0"); @@ -225,7 +246,7 @@ void Video::setSize(bool defaultTo1XScaler) {  void Video::retrace(bool mouse) {  	if (mouse) -		CursorMan.showMouse((_vm->_draw->_showCursor & 2) != 0); +		CursorMan.showMouse((_vm->_draw->_showCursor & 6) != 0);  	if (_vm->_global->_primarySurfDesc) {  		int screenX = _screenDeltaX; @@ -332,6 +353,10 @@ void Video::drawPackedSprite(byte *sprBuf, int16 width, int16 height,  void Video::drawPackedSprite(const char *path, Surface &dest, int width) {  	int32 size;  	byte *data = _vm->_dataIO->getFile(path, size); +	if (!data) { +		warning("Video::drawPackedSprite(): Failed to open sprite \"%s\"", path); +		return; +	}  	drawPackedSprite(data, width, dest.getHeight(), 0, 0, 0, dest);  	delete[] data; diff --git a/engines/gob/video.h b/engines/gob/video.h index ecbb579c5f..122c1e47d5 100644 --- a/engines/gob/video.h +++ b/engines/gob/video.h @@ -41,11 +41,16 @@ public:  	uint8  getCharWidth ()        const;  	uint8  getCharHeight()        const; +	bool hasChar(uint8 c) const; +  	bool isMonospaced() const;  	void drawLetter(Surface &surf, uint8 c, uint16 x, uint16 y,  	                uint32 color1, uint32 color2, bool transp) const; +	void drawString(const Common::String &str, int16 x, int16 y, int16 color1, int16 color2, +	                bool transp, Surface &dest) const; +  private:  	const byte *_dataPtr;  	const byte  *_data; | 
