[Scummvm-git-logs] scummvm master -> 4e7a5ab5eec20004ec96d8efdccddfdb75d16749

dreammaster noreply at scummvm.org
Mon Feb 13 03:45:16 UTC 2023


This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
1cc18af84c MM: Move File class into Shared
4e7a5ab5ee XEEN: Move SpriteResource into Shared namespace


Commit: 1cc18af84caf67910f80ec95a5ae268fbda92559
    https://github.com/scummvm/scummvm/commit/1cc18af84caf67910f80ec95a5ae268fbda92559
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2023-02-12T19:44:53-08:00

Commit Message:
MM: Move File class into Shared

Changed paths:
  A engines/mm/shared/xeen/file.cpp
  A engines/mm/shared/xeen/file.h
    engines/mm/module.mk
    engines/mm/xeen/debugger.cpp
    engines/mm/xeen/files.cpp
    engines/mm/xeen/files.h
    engines/mm/xeen/saves.cpp
    engines/mm/xeen/xeen.h


diff --git a/engines/mm/module.mk b/engines/mm/module.mk
index 4ad5c853d9a..f4ba0a97322 100644
--- a/engines/mm/module.mk
+++ b/engines/mm/module.mk
@@ -247,6 +247,7 @@ MODULE_OBJS := \
 	xeen/window.o \
 	xeen/xeen.o \
 	shared/xeen/cc_archive.o \
+	shared/xeen/file.o \
 	shared/xeen/xsurface.o
 
 # This module can be built as a plugin
diff --git a/engines/mm/shared/xeen/file.cpp b/engines/mm/shared/xeen/file.cpp
new file mode 100644
index 00000000000..5d50021d8b6
--- /dev/null
+++ b/engines/mm/shared/xeen/file.cpp
@@ -0,0 +1,187 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mm/shared/xeen/file.h"
+#include "mm/xeen/files.h"
+#include "mm/xeen/xeen.h"
+
+namespace MM {
+namespace Shared {
+namespace Xeen {
+
+
+File::File(const Common::String &filename) {
+	File::open(filename);
+}
+
+File::File(const Common::String &filename, Common::Archive &archive) {
+	File::open(filename, archive);
+}
+
+File::File(const Common::String &filename, int ccMode) {
+	File::open(filename, ccMode);
+}
+
+bool File::open(const Common::Path &filename) {
+	MM::Xeen::XeenEngine *engine = dynamic_cast<MM::Xeen::XeenEngine *>(g_engine);
+
+	if (engine) {
+		MM::Xeen::FileManager &fm = *engine->_files;
+
+		if (!fm._currentSave || !Common::File::open(filename, *fm._currentSave)) {
+			if (!fm._currentArchive || !Common::File::open(filename, *fm._currentArchive)) {
+				// Could not find in current archive, so try intro.cc or in folder
+				if (!Common::File::open(filename))
+					error("Could not open file - %s", filename.toString().c_str());
+			}
+		}
+	} else {
+		if (!Common::File::open(filename))
+			error("Could not open file - %s", filename.toString().c_str());
+	}
+
+	return true;
+}
+
+bool File::open(const Common::Path &filename, Common::Archive &archive) {
+	if (!Common::File::open(filename, archive))
+		error("Could not open file - %s", filename.toString().c_str());
+	return true;
+}
+
+bool File::open(const Common::String &filename, int ccMode) {
+	MM::Xeen::XeenEngine *engine = dynamic_cast<MM::Xeen::XeenEngine *>(g_engine);
+	assert(engine);
+
+	MM::Xeen::FileManager &fm = *engine->_files;
+	int oldNum = fm._ccNum;
+
+	fm.setGameCc(ccMode);
+	if (File::exists(filename, *fm._currentArchive))
+		File::open(filename, *fm._currentArchive);
+	else
+		File::open(filename);
+
+	fm.setGameCc(oldNum);
+
+	return true;
+}
+
+void File::setCurrentArchive(int ccMode) {
+	MM::Xeen::XeenEngine *engine = dynamic_cast<MM::Xeen::XeenEngine *>(g_engine);
+	assert(engine);
+	MM::Xeen::FileManager &fm = *engine->_files;
+
+	switch (ccMode) {
+	case 0:
+		fm._currentArchive = fm._xeenCc;
+		fm._currentSave = fm._xeenSave;
+		break;
+
+	case 1:
+		fm._currentArchive = fm._darkCc;
+		fm._currentSave = fm._darkSave;
+		break;
+
+	case 2:
+		fm._currentArchive = fm._introCc;
+		fm._currentSave = nullptr;
+		break;
+
+	default:
+		break;
+	}
+
+	assert(fm._currentArchive);
+}
+
+Common::String File::readString() {
+	Common::String result;
+	char c;
+
+	while (pos() < size() && (c = (char)readByte()) != '\0')
+		result += c;
+
+	return result;
+}
+
+bool File::exists(const Common::String &filename) {
+	MM::Xeen::XeenEngine *engine = dynamic_cast<MM::Xeen::XeenEngine *>(g_engine);
+
+	if (engine) {
+		MM::Xeen::FileManager &fm = *engine->_files;
+
+		if (!fm._currentSave || !fm._currentSave->hasFile(filename)) {
+			if (!fm._currentArchive->hasFile(filename)) {
+				// Could not find in current archive, so try intro.cc or in folder
+				return Common::File::exists(filename);
+			}
+		}
+
+		return true;
+	} else {
+		return Common::File::exists(filename);
+	}
+}
+
+bool File::exists(const Common::String &filename, int ccMode) {
+	MM::Xeen::XeenEngine *engine = dynamic_cast<MM::Xeen::XeenEngine *>(g_engine);
+	assert(engine);
+	MM::Xeen::FileManager &fm = *engine->_files;
+	int oldNum = fm._ccNum;
+
+	fm.setGameCc(ccMode);
+	bool result = exists(filename);
+	fm.setGameCc(oldNum);
+
+	return result;
+}
+
+bool File::exists(const Common::String &filename, Common::Archive &archive) {
+	return archive.hasFile(filename);
+}
+
+void File::syncBitFlags(Common::Serializer &s, bool *startP, bool *endP) {
+	byte data = 0;
+
+	int bitCounter = 0;
+	for (bool *p = startP; p < endP; ++p, bitCounter = (bitCounter + 1) % 8) {
+		if (bitCounter == 0) {
+			if (s.isLoading() || p != startP)
+				s.syncAsByte(data);
+
+			if (s.isSaving())
+				data = 0;
+		}
+
+		if (s.isLoading())
+			*p = ((data >> bitCounter) & 1) != 0;
+		else if (*p)
+			data |= 1 << bitCounter;
+	}
+
+	if (s.isSaving())
+		s.syncAsByte(data);
+}
+
+} // namespace Xeen
+} // namespace Shared
+} // namespace MM
diff --git a/engines/mm/shared/xeen/file.h b/engines/mm/shared/xeen/file.h
new file mode 100644
index 00000000000..d2dc5b7aaf9
--- /dev/null
+++ b/engines/mm/shared/xeen/file.h
@@ -0,0 +1,122 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MM_SHARED_XEEN_FILE_H
+#define MM_SHARED_XEEN_FILE_H
+
+#include "common/file.h"
+#include "common/serializer.h"
+
+namespace MM {
+namespace Shared {
+namespace Xeen {
+
+
+/**
+ * Derived file class
+ */
+class File : public Common::File {
+private:
+public:
+	/**
+	 * Sets which archive is used by default
+	 */
+	static void setCurrentArchive(int ccMode);
+
+	/**
+	 * Synchronizes a boolean array as a bitfield set
+	 */
+	static void syncBitFlags(Common::Serializer &s, bool *startP, bool *endP);
+public:
+	File() : Common::File() {
+	}
+	File(const Common::String &filename);
+	File(const Common::String &filename, int ccMode);
+	File(const Common::String &filename, Common::Archive &archive);
+	~File() override {
+	}
+
+	/**
+	 * Opens the given file, throwing an error if it can't be opened
+	 */
+	bool open(const Common::Path &filename) override;
+
+	/**
+	 * Opens the given file, throwing an error if it can't be opened
+	 */
+	bool open(const Common::Path &filename, Common::Archive &archive) override;
+
+	/**
+	 * Opens the given file, throwing an error if it can't be opened
+	 */
+	virtual bool open(const Common::String &filename, int ccMode);
+
+	/**
+	 * Opens the given file
+	 */
+	bool open(const Common::FSNode &node) override {
+		return Common::File::open(node);
+	}
+
+	/**
+	 * Opens the given file
+	 */
+	bool open(SeekableReadStream *stream, const Common::String &name) override {
+		return Common::File::open(stream, name);
+	}
+
+	/**
+	 * Reads in a null terminated string
+	 */
+	Common::String readString();
+
+	/**
+	 * Checks if a given file exists
+	 *
+	 * @param	filename	the file to check for
+	 * @return	true if the file exists, false otherwise
+	 */
+	static bool exists(const Common::String &filename);
+
+	/**
+	 * Checks if a given file exists
+	 *
+	 * @param	filename	the file to check for
+	 * @param	ccMode		Archive to use
+	 * @return	true if the file exists, false otherwise
+	 */
+	static bool exists(const Common::String &filename, int ccMode);
+
+	/**
+	 * Checks if a given file exists
+	 *
+	 * @param	filename	the file to check for
+	 * @param	archive		Archive to use
+	 * @return	true if the file exists, false otherwise
+	 */
+	static bool exists(const Common::String &filename, Common::Archive &archive);
+};
+
+} // namespace Xeen
+} // namespace Shared
+} // namespace MM
+
+#endif
diff --git a/engines/mm/xeen/debugger.cpp b/engines/mm/xeen/debugger.cpp
index 97d5ea49d56..ec19c21ae91 100644
--- a/engines/mm/xeen/debugger.cpp
+++ b/engines/mm/xeen/debugger.cpp
@@ -215,8 +215,8 @@ bool Debugger::cmdLoadOriginal(int argc, const char **argv) {
 	}
 
 	// Loop through loading the sides' save archives
-	SaveArchive *archives[2] = { File::_xeenSave, File::_darkSave };
-	CCArchive *cc[2] = { File::_xeenCc, File::_darkCc };
+	SaveArchive *archives[2] = { files._xeenSave, files._darkSave };
+	CCArchive *cc[2] = { files._xeenCc, files._darkCc };
 	const char *prefix[2] = { "XEEN", "DARK" };
 
 	Common::FSNode folder(argv[1]);
@@ -238,7 +238,7 @@ bool Debugger::cmdLoadOriginal(int argc, const char **argv) {
 	files.setGameCc(_vm->getGameID() == GType_DarkSide ? 1 : 0);
 
 	// Load the character roster and party
-	File::_currentSave->loadParty();
+	files._currentSave->loadParty();
 
 	// Reset any combat information from the previous game
 	combat.reset();
diff --git a/engines/mm/xeen/files.cpp b/engines/mm/xeen/files.cpp
index b26c48eb43c..05c1e7ea775 100644
--- a/engines/mm/xeen/files.cpp
+++ b/engines/mm/xeen/files.cpp
@@ -34,38 +34,38 @@ namespace Xeen {
 
 FileManager::FileManager(XeenEngine *vm) {
 	_ccNum = vm->getGameID() == GType_DarkSide;
-	File::_xeenCc = File::_darkCc = File::_introCc = nullptr;
-	File::_xeenSave = File::_darkSave = nullptr;
-	File::_currentSave = nullptr;
-	File::_currentArchive = nullptr;
+	_xeenCc = _darkCc = _introCc = nullptr;
+	_xeenSave = _darkSave = nullptr;
+	_currentSave = nullptr;
+	_currentArchive = nullptr;
 }
 
 FileManager::~FileManager() {
 	SearchMan.remove("intro");
 	SearchMan.remove("data");
-	delete File::_xeenCc;
-	delete File::_darkCc;
+	delete _xeenCc;
+	delete _darkCc;
 }
 
 bool FileManager::setup() {
 	if (g_vm->getGameID() == GType_Swords) {
-		File::_xeenCc = nullptr;
-		File::_darkCc = new CCArchive("swrd.cc", "xeen", true);
+		_xeenCc = nullptr;
+		_darkCc = new CCArchive("swrd.cc", "xeen", true);
 	} else {
-		File::_xeenCc = (g_vm->getGameID() == GType_DarkSide) ? nullptr :
+		_xeenCc = (g_vm->getGameID() == GType_DarkSide) ? nullptr :
 			new CCArchive("xeen.cc", "xeen", true);
-		File::_darkCc = (g_vm->getGameID() == GType_Clouds) ? nullptr :
+		_darkCc = (g_vm->getGameID() == GType_Clouds) ? nullptr :
 			new CCArchive("dark.cc", "dark", true);
 	}
 
 	if (Common::File::exists("intro.cc")) {
-		File::_introCc = new CCArchive("intro.cc", "intro", true);
-		SearchMan.add("intro", File::_introCc);
+		_introCc = new CCArchive("intro.cc", "intro", true);
+		SearchMan.add("intro", _introCc);
 	}
 
-	File::_currentArchive = g_vm->getGameID() == GType_DarkSide || g_vm->getGameID() == GType_Swords ?
-		File::_darkCc : File::_xeenCc;
-	assert(File::_currentArchive);
+	_currentArchive = g_vm->getGameID() == GType_DarkSide || g_vm->getGameID() == GType_Swords ?
+		_darkCc : _xeenCc;
+	assert(_currentArchive);
 
 	// Set up the engine data file
 	Common::U32String errMsg;
@@ -97,144 +97,6 @@ void FileManager::save(Common::WriteStream &s) {
 
 /*------------------------------------------------------------------------*/
 
-CCArchive *File::_xeenCc;
-CCArchive *File::_darkCc;
-CCArchive *File::_introCc;
-SaveArchive *File::_xeenSave;
-SaveArchive *File::_darkSave;
-BaseCCArchive *File::_currentArchive;
-SaveArchive *File::_currentSave;
-
-File::File(const Common::String &filename) {
-	File::open(filename);
-}
-
-File::File(const Common::String &filename, Common::Archive &archive) {
-	File::open(filename, archive);
-}
-
-File::File(const Common::String &filename, int ccMode) {
-	File::open(filename, ccMode);
-}
-
-bool File::open(const Common::Path &filename) {
-	if (!_currentSave || !Common::File::open(filename, *_currentSave)) {
-		if (!_currentArchive || !Common::File::open(filename, *_currentArchive)) {
-			// Could not find in current archive, so try intro.cc or in folder
-			if (!Common::File::open(filename))
-				error("Could not open file - %s", filename.toString().c_str());
-		}
-	}
-
-	return true;
-}
-
-bool File::open(const Common::Path &filename, Common::Archive &archive) {
-	if (!Common::File::open(filename, archive))
-		error("Could not open file - %s", filename.toString().c_str());
-	return true;
-}
-
-bool File::open(const Common::String &filename, int ccMode) {
-	FileManager &files = *g_vm->_files;
-	int oldNum = files._ccNum;
-
-	files.setGameCc(ccMode);
-	if (File::exists(filename, *_currentArchive))
-		File::open(filename, *_currentArchive);
-	else
-		File::open(filename);
-
-	files.setGameCc(oldNum);
-
-	return true;
-}
-
-void File::setCurrentArchive(int ccMode) {
-	switch (ccMode) {
-	case 0:
-		_currentArchive = _xeenCc;
-		_currentSave = _xeenSave;
-		break;
-
-	case 1:
-		_currentArchive = _darkCc;
-		_currentSave = _darkSave;
-		break;
-
-	case 2:
-		_currentArchive = _introCc;
-		_currentSave = nullptr;
-		break;
-
-	default:
-		break;
-	}
-
-	assert(_currentArchive);
-}
-
-Common::String File::readString() {
-	Common::String result;
-	char c;
-
-	while (pos() < size() && (c = (char)readByte()) != '\0')
-		result += c;
-
-	return result;
-}
-
-bool File::exists(const Common::String &filename) {
-	if (!_currentSave || !_currentSave->hasFile(filename)) {
-		if (!_currentArchive->hasFile(filename)) {
-			// Could not find in current archive, so try intro.cc or in folder
-			return Common::File::exists(filename);
-		}
-	}
-
-	return true;
-}
-
-bool File::exists(const Common::String &filename, int ccMode) {
-	FileManager &files = *g_vm->_files;
-	int oldNum = files._ccNum;
-
-	files.setGameCc(ccMode);
-	bool result = exists(filename);
-	files.setGameCc(oldNum);
-
-	return result;
-}
-
-bool File::exists(const Common::String &filename, Common::Archive &archive) {
-	return archive.hasFile(filename);
-}
-
-void File::syncBitFlags(Common::Serializer &s, bool *startP, bool *endP) {
-	byte data = 0;
-
-	int bitCounter = 0;
-	for (bool *p = startP; p < endP; ++p, bitCounter = (bitCounter + 1) % 8) {
-		if (bitCounter == 0) {
-			if (s.isLoading() || p != startP)
-				s.syncAsByte(data);
-
-			if (s.isSaving())
-				data = 0;
-		}
-
-		if (s.isLoading())
-			*p = ((data >> bitCounter) & 1) != 0;
-		else if (*p)
-			data |= 1 << bitCounter;
-	}
-
-	if (s.isSaving())
-		s.syncAsByte(data);
-}
-
-/*------------------------------------------------------------------------*/
-
 SaveArchive::SaveArchive(Party *party) : BaseCCArchive(), _party(party), _data(nullptr), _dataSize(0) {
 }
 
@@ -402,7 +264,8 @@ void SaveArchive::replaceEntry(uint16 id, const byte *data, size_t size) {
 
 OutFile::OutFile(const Common::String &filename) :
 		_filename(filename), _backingStream(DisposeAfterUse::YES) {
-	_archive = File::_currentSave;
+	FileManager &files = *g_vm->_files;
+	_archive = files._currentSave;
 }
 
 OutFile::OutFile(const Common::String &filename, SaveArchive *archive) :
@@ -411,8 +274,9 @@ OutFile::OutFile(const Common::String &filename, SaveArchive *archive) :
 
 OutFile::OutFile(const Common::String &filename, int ccMode) :
 		_filename(filename), _backingStream(DisposeAfterUse::YES) {
+	FileManager &files = *g_vm->_files;
 	g_vm->_files->setGameCc(ccMode);
-	_archive = File::_currentSave;
+	_archive = files._currentSave;
 }
 
 uint32 OutFile::write(const void *dataPtr, uint32 dataSize) {
diff --git a/engines/mm/xeen/files.h b/engines/mm/xeen/files.h
index f5702a74aa7..f8a8c872d55 100644
--- a/engines/mm/xeen/files.h
+++ b/engines/mm/xeen/files.h
@@ -31,6 +31,7 @@
 #include "common/str-array.h"
 #include "graphics/surface.h"
 #include "mm/shared/xeen/cc_archive.h"
+#include "mm/shared/xeen/file.h"
 
 namespace MM {
 namespace Xeen {
@@ -38,9 +39,9 @@ namespace Xeen {
 using Shared::Xeen::BaseCCArchive;
 using Shared::Xeen::CCArchive;
 using Shared::Xeen::CCEntry;
+using Shared::Xeen::File;
 
 class XeenEngine;
-class File;
 class SaveArchive;
 class Party;
 class OutFile;
@@ -65,7 +66,13 @@ class SavesManager;
  */
 class FileManager {
 public:
-	int _ccNum;
+	int _ccNum = 0;
+	CCArchive *_xeenCc = nullptr, *_darkCc = nullptr,
+		*_introCc = nullptr;
+	SaveArchive *_xeenSave = nullptr, *_darkSave = nullptr;
+	BaseCCArchive *_currentArchive = nullptr;
+	SaveArchive *_currentSave = nullptr;
+
 public:
 	/**
 	 * Constructor
@@ -100,97 +107,6 @@ public:
 	void save(Common::WriteStream &s);
 };
 
-/**
- * Derived file class
- */
-class File : public Common::File {
-	friend class FileManager;
-	friend class OutFile;
-	friend class SavesManager;
-	friend class Debugger;
-private:
-	static CCArchive *_xeenCc, *_darkCc, *_introCc;
-	static SaveArchive *_xeenSave, *_darkSave;
-	static BaseCCArchive *_currentArchive;
-	static SaveArchive *_currentSave;
-public:
-	/**
-	 * Sets which archive is used by default
-	 */
-	static void setCurrentArchive(int ccMode);
-
-	/**
-	 * Synchronizes a boolean array as a bitfield set
-	 */
-	static void syncBitFlags(Common::Serializer &s, bool *startP, bool *endP);
-public:
-	File() : Common::File() {}
-	File(const Common::String &filename);
-	File(const Common::String &filename, int ccMode);
-	File(const Common::String &filename, Common::Archive &archive);
-	~File() override {}
-
-	/**
-	 * Opens the given file, throwing an error if it can't be opened
-	 */
-	bool open(const Common::Path &filename) override;
-
-	/**
-	 * Opens the given file, throwing an error if it can't be opened
-	 */
-	bool open(const Common::Path &filename, Common::Archive &archive) override;
-
-	/**
-	 * Opens the given file, throwing an error if it can't be opened
-	 */
-	virtual bool open(const Common::String &filename, int ccMode);
-
-	/**
-	 * Opens the given file
-	 */
-	bool open(const Common::FSNode &node) override {
-		return Common::File::open(node);
-	}
-
-	/**
-	 * Opens the given file
-	 */
-	bool open(SeekableReadStream *stream, const Common::String &name) override {
-		return Common::File::open(stream, name);
-	}
-
-	/**
-	 * Reads in a null terminated string
-	 */
-	Common::String readString();
-
-	/**
-	 * Checks if a given file exists
-	 *
-	 * @param	filename	the file to check for
-	 * @return	true if the file exists, false otherwise
-	 */
-	static bool exists(const Common::String &filename);
-
-	/**
-	 * Checks if a given file exists
-	 *
-	 * @param	filename	the file to check for
-	 * @param	ccMode		Archive to use
-	 * @return	true if the file exists, false otherwise
-	 */
-	static bool exists(const Common::String &filename, int ccMode);
-
-	/**
-	 * Checks if a given file exists
-	 *
-	 * @param	filename	the file to check for
-	 * @param	archive		Archive to use
-	 * @return	true if the file exists, false otherwise
-	 */
-	static bool exists(const Common::String &filename, Common::Archive &archive);
-};
-
 /**
  * SubWriteStream provides a way of compartmentalizing writing to a subsection of
  * a file. This is primarily useful for the pos() function which can, for example,
diff --git a/engines/mm/xeen/saves.cpp b/engines/mm/xeen/saves.cpp
index 1584c7b9a27..5afab0d1c8c 100644
--- a/engines/mm/xeen/saves.cpp
+++ b/engines/mm/xeen/saves.cpp
@@ -36,13 +36,15 @@ namespace Xeen {
 
 SavesManager::SavesManager(const Common::String &targetName) : _targetName(targetName),
 _wonWorld(false), _wonDarkSide(false) {
-	File::_xeenSave = nullptr;
-	File::_darkSave = nullptr;
+	FileManager &files = *g_vm->_files;
+	files._xeenSave = nullptr;
+	files._darkSave = nullptr;
 }
 
 SavesManager::~SavesManager() {
-	delete File::_xeenSave;
-	delete File::_darkSave;
+	FileManager &files = *g_vm->_files;
+	delete files._xeenSave;
+	delete files._darkSave;
 }
 
 static const char *const SAVEGAME_STR = "XEEN";
@@ -121,6 +123,7 @@ Common::Error SavesManager::saveGameState(int slot, const Common::String &desc,
 		return Common::kCreatingFileFailed;
 
 	// Push map and party data to the save archives
+	FileManager &files = *g_vm->_files;
 	Map &map = *g_vm->_map;
 	map.saveMaze();
 
@@ -130,7 +133,7 @@ Common::Error SavesManager::saveGameState(int slot, const Common::String &desc,
 	writeSavegameHeader(out, header);
 
 	// Loop through saving the sides' save archives
-	SaveArchive *archives[2] = { File::_xeenSave, File::_darkSave };
+	SaveArchive *archives[2] = { files._xeenSave, files._darkSave };
 	for (int idx = 0; idx < 2; ++idx) {
 		if (archives[idx]) {
 			archives[idx]->save(*out);
@@ -141,7 +144,6 @@ Common::Error SavesManager::saveGameState(int slot, const Common::String &desc,
 	}
 
 	// Write out miscellaneous
-	FileManager &files = *g_vm->_files;
 	files.save(*out);
 
 	out->finalize();
@@ -171,7 +173,7 @@ Common::Error SavesManager::loadGameState(int slot) {
 	events.setPlayTime(header._totalFrames);
 
 	// Loop through loading the sides' save archives
-	SaveArchive *archives[2] = { File::_xeenSave, File::_darkSave };
+	SaveArchive *archives[2] = { files._xeenSave, files._darkSave };
 	for (int idx = 0; idx < 2; ++idx) {
 		uint fileSize = saveFile->readUint32LE();
 
@@ -181,7 +183,7 @@ Common::Error SavesManager::loadGameState(int slot) {
 					saveFile->pos() + fileSize);
 				archives[idx]->load(arcStream);
 			} else {
-				archives[idx]->reset((idx == 1) ? File::_darkCc : File::_xeenCc);
+				archives[idx]->reset((idx == 1) ? files._darkCc : files._xeenCc);
 			}
 		} else {
 			assert(!fileSize);
@@ -192,7 +194,7 @@ Common::Error SavesManager::loadGameState(int slot) {
 	files.load(*saveFile);
 
 	// Load the character roster and party
-	File::_currentSave->loadParty();
+	files._currentSave->loadParty();
 
 	// Reset any combat information from the previous game
 	combat.reset();
@@ -208,30 +210,31 @@ Common::Error SavesManager::loadGameState(int slot) {
 }
 
 void SavesManager::newGame() {
-	delete File::_xeenSave;
-	delete File::_darkSave;
-	File::_xeenSave = nullptr;
-	File::_darkSave = nullptr;
+	FileManager &files = *g_vm->_files;
+	delete files._xeenSave;
+	delete files._darkSave;
+	files._xeenSave = nullptr;
+	files._darkSave = nullptr;
 
 	// Reset any combat information from the previous game
 	g_vm->_combat->reset();
 
 	// Reset the game states
 	if (g_vm->getGameID() != GType_Clouds) {
-		File::_darkSave = new SaveArchive(g_vm->_party);
-		File::_darkSave->reset(File::_darkCc);
+		files._darkSave = new SaveArchive(g_vm->_party);
+		files._darkSave->reset(files._darkCc);
 	}
 	if (g_vm->getGameID() != GType_DarkSide && g_vm->getGameID() != GType_Swords) {
-		File::_xeenSave = new SaveArchive(g_vm->_party);
-		File::_xeenSave->reset(File::_xeenCc);
+		files._xeenSave = new SaveArchive(g_vm->_party);
+		files._xeenSave->reset(files._xeenCc);
 	}
 
-	File::_currentSave = g_vm->getGameID() == GType_DarkSide || g_vm->getGameID() == GType_Swords ?
-		File::_darkSave : File::_xeenSave;
-	assert(File::_currentSave);
+	files._currentSave = g_vm->getGameID() == GType_DarkSide || g_vm->getGameID() == GType_Swords ?
+		files._darkSave : files._xeenSave;
+	assert(files._currentSave);
 
 	// Load the character roster and party
-	File::_currentSave->loadParty();
+	files._currentSave->loadParty();
 
 	// Set any final initial values
 	Party &party = *g_vm->_party;
diff --git a/engines/mm/xeen/xeen.h b/engines/mm/xeen/xeen.h
index 2f6b52fed3d..16a59760602 100644
--- a/engines/mm/xeen/xeen.h
+++ b/engines/mm/xeen/xeen.h
@@ -177,6 +177,12 @@ public:
 	bool _gameWon[3];
 	uint _finalScore;
 	ExtendedOptions _extOptions;
+
+	CCArchive *_xeenCc = nullptr, *_darkCc = nullptr,
+		*_introCc = nullptr;
+	SaveArchive *_xeenSave = nullptr, *_darkSave = nullptr;
+	BaseCCArchive *_currentArchive = nullptr;
+	SaveArchive *_currentSave = nullptr;
 public:
 	XeenEngine(OSystem *syst, const MM::MightAndMagicGameDescription *gameDesc);
 	~XeenEngine() override;


Commit: 4e7a5ab5eec20004ec96d8efdccddfdb75d16749
    https://github.com/scummvm/scummvm/commit/4e7a5ab5eec20004ec96d8efdccddfdb75d16749
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2023-02-12T19:44:54-08:00

Commit Message:
XEEN: Move SpriteResource into Shared namespace

All the Xeen specific parts of SpriteResource has been
split off into a derived SpriteResource class, so that
MM1 won't accidentally use methods it shouldn't

Changed paths:
  A engines/mm/shared/xeen/sprites.cpp
  A engines/mm/shared/xeen/sprites.h
    engines/mm/mm.h
    engines/mm/mm1/data/roster.cpp
    engines/mm/mm1/globals.h
    engines/mm/mm1/mm1.cpp
    engines/mm/mm1/utils/mouse.cpp
    engines/mm/mm1/utils/mouse.h
    engines/mm/mm1/views_enh/button_container.cpp
    engines/mm/mm1/views_enh/button_container.h
    engines/mm/mm1/views_enh/character_info.h
    engines/mm/mm1/views_enh/game_commands.cpp
    engines/mm/mm1/views_enh/scroll_view.cpp
    engines/mm/mm1/views_enh/scroll_view.h
    engines/mm/module.mk
    engines/mm/xeen/sprites.cpp
    engines/mm/xeen/sprites.h


diff --git a/engines/mm/mm.h b/engines/mm/mm.h
index dec644e669e..137d4cf2d08 100644
--- a/engines/mm/mm.h
+++ b/engines/mm/mm.h
@@ -67,6 +67,12 @@ public:
 	 */
 	uint32 getGameID() const;
 
+	/**
+	 * Get a random number
+	 */
+	uint getRandomNumber(int max) {
+		return _randomSource.getRandomNumber(max);
+	}
 };
 
 } // namespace MM
diff --git a/engines/mm/mm1/data/roster.cpp b/engines/mm/mm1/data/roster.cpp
index 2b0ca105b67..10d1ce4fe02 100644
--- a/engines/mm/mm1/data/roster.cpp
+++ b/engines/mm/mm1/data/roster.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/file.h"
+#include "common/memstream.h"
 #include "common/savefile.h"
 #include "common/system.h"
 #include "mm/mm1/data/roster.h"
diff --git a/engines/mm/mm1/globals.h b/engines/mm/mm1/globals.h
index c267f0b86f0..e8ec9e20665 100644
--- a/engines/mm/mm1/globals.h
+++ b/engines/mm/mm1/globals.h
@@ -66,10 +66,10 @@ public:
 
 public:
 	// Enhanced mode globals
-	Xeen::SpriteResource _mainIcons;
-	Xeen::SpriteResource _confirmIcons;
-	Xeen::SpriteResource _globalSprites;
-	Xeen::SpriteResource _tileSprites;
+	Shared::Xeen::SpriteResource _mainIcons;
+	Shared::Xeen::SpriteResource _confirmIcons;
+	Shared::Xeen::SpriteResource _globalSprites;
+	Shared::Xeen::SpriteResource _tileSprites;
 	byte SYMBOLS[20][64];
 	XeenFont _fontNormal;
 	XeenFont _fontReduced;
diff --git a/engines/mm/mm1/mm1.cpp b/engines/mm/mm1/mm1.cpp
index 5e3932793db..e1e3d5b16ad 100644
--- a/engines/mm/mm1/mm1.cpp
+++ b/engines/mm/mm1/mm1.cpp
@@ -31,6 +31,7 @@
 #include "mm/mm1/gfx/gfx.h"
 #include "mm/mm1/views/game.h"
 #include "mm/mm1/views_enh/game.h"
+#include "mm/shared/xeen/cc_archive.h"
 
 namespace MM {
 namespace MM1 {
@@ -93,7 +94,7 @@ bool MM1Engine::setupEnhanced() {
 	}
 
 	// Add the Xeen cc archives
-	::MM::Xeen::CCArchive *xeenCC = new ::MM::Xeen::CCArchive(
+	Shared::Xeen::CCArchive *xeenCC = new Shared::Xeen::CCArchive(
 		"xeen.cc", "xeen", true);
 	SearchMan.add("xeen", xeenCC);
 
diff --git a/engines/mm/mm1/utils/mouse.cpp b/engines/mm/mm1/utils/mouse.cpp
index 7759b59e3c3..b99dad2fe40 100644
--- a/engines/mm/mm1/utils/mouse.cpp
+++ b/engines/mm/mm1/utils/mouse.cpp
@@ -30,8 +30,8 @@ void Mouse::loadCursors() {
 }
 
 void Mouse::setCursor(int cursorId) {
-	Xeen::XSurface cursor;
-	_sprites.draw(cursor, cursorId, Common::Point(0, 0), Xeen::SPRFLAG_RESIZE);
+	Shared::Xeen::XSurface cursor;
+	_sprites.draw(cursor, cursorId, Common::Point(0, 0), Shared::Xeen::SPRFLAG_RESIZE);
 
 	CursorMan.replaceCursor(cursor.getPixels(), cursor.w, cursor.h, 0, 0, 0);
 	showCursor();
diff --git a/engines/mm/mm1/utils/mouse.h b/engines/mm/mm1/utils/mouse.h
index 119a9bb6ec4..124cdb7fdbb 100644
--- a/engines/mm/mm1/utils/mouse.h
+++ b/engines/mm/mm1/utils/mouse.h
@@ -22,14 +22,14 @@
 #ifndef MM1_UTILS_MOUSE_H
 #define MM1_UTILS_MOUSE_H
 
-#include "mm/xeen/sprites.h"
+#include "mm/shared/xeen/sprites.h"
 
 namespace MM {
 namespace MM1 {
 
 class Mouse {
 private:
-	Xeen::SpriteResource _sprites;
+	Shared::Xeen::SpriteResource _sprites;
 public:
 	Mouse() {}
 
diff --git a/engines/mm/mm1/views_enh/button_container.cpp b/engines/mm/mm1/views_enh/button_container.cpp
index 45dbe3a5d4d..c72ecfbe2c0 100644
--- a/engines/mm/mm1/views_enh/button_container.cpp
+++ b/engines/mm/mm1/views_enh/button_container.cpp
@@ -51,12 +51,12 @@ void ButtonContainer::restoreButtons() {
 }
 
 void ButtonContainer::addButton(const Common::Rect &bounds, KeybindingAction action,
-	Xeen::SpriteResource *sprites) {
+	Shared::Xeen::SpriteResource *sprites) {
 	_buttons.push_back(UIButton(this, bounds, action, _buttons.size() * 2, sprites, sprites != nullptr));
 }
 
 void ButtonContainer::addButton(const Common::Rect &bounds, KeybindingAction action,
-	int frameNum, Xeen::SpriteResource *sprites) {
+	int frameNum, Shared::Xeen::SpriteResource *sprites) {
 	_buttons.push_back(UIButton(this, bounds, action, frameNum, sprites, sprites != nullptr));
 }
 
diff --git a/engines/mm/mm1/views_enh/button_container.h b/engines/mm/mm1/views_enh/button_container.h
index f451a37a049..aa4ee8814d3 100644
--- a/engines/mm/mm1/views_enh/button_container.h
+++ b/engines/mm/mm1/views_enh/button_container.h
@@ -25,7 +25,7 @@
 #include "common/array.h"
 #include "common/stack.h"
 #include "common/rect.h"
-#include "mm/xeen/sprites.h"
+#include "mm/shared/xeen/sprites.h"
 #include "mm/mm1/views/text_view.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/metaengine.h"
@@ -39,7 +39,7 @@ class ButtonContainer;
 class UIButton {
 public:
 	Common::Rect _bounds;
-	Xeen::SpriteResource *_sprites;
+	Shared::Xeen::SpriteResource *_sprites;
 	KeybindingAction _action;
 	uint _frameNum, _selectedFrame;
 	bool _draw;
@@ -48,7 +48,7 @@ public:
 	 * Constructor
 	 */
 	UIButton(ButtonContainer *owner, const Common::Rect &bounds,
-			KeybindingAction action, uint frameNum, Xeen::SpriteResource *sprites,
+			KeybindingAction action, uint frameNum, Shared::Xeen::SpriteResource *sprites,
 			bool draw) :
 			_bounds(bounds), _action(action), _frameNum(frameNum),
 			_selectedFrame(frameNum | 1), _sprites(sprites), _draw(draw) {
@@ -104,9 +104,9 @@ public:
 	void restoreButtons();
 
 	void addButton(const Common::Rect &bounds, KeybindingAction action,
-		Xeen::SpriteResource *sprites = nullptr);
+		Shared::Xeen::SpriteResource *sprites = nullptr);
 	void addButton(const Common::Rect &bounds, KeybindingAction action,
-		int frameNum, Xeen::SpriteResource *sprites = nullptr);
+		int frameNum, Shared::Xeen::SpriteResource *sprites = nullptr);
 
 	void draw() override;
 	bool msgMouseDown(const MouseDownMessage &msg) override;
diff --git a/engines/mm/mm1/views_enh/character_info.h b/engines/mm/mm1/views_enh/character_info.h
index 7d72f8463ed..876af592b47 100644
--- a/engines/mm/mm1/views_enh/character_info.h
+++ b/engines/mm/mm1/views_enh/character_info.h
@@ -24,7 +24,7 @@
 
 #include "mm/mm1/views_enh/scroll_view.h"
 #include "mm/mm1/views_enh/scroll_popup.h"
-#include "mm/xeen/sprites.h"
+#include "mm/shared/xeen/sprites.h"
 
 namespace MM {
 namespace MM1 {
@@ -37,7 +37,7 @@ class CharacterInfo : public ScrollView {
 		int _frame; int _x; int _y;
 	};
 private:
-	Xeen::SpriteResource _viewIcon;
+	Shared::Xeen::SpriteResource _viewIcon;
 	static const IconPos ICONS[CHAR_ICONS_COUNT];
 	const char *ICONS_TEXT[CHAR_ICONS_COUNT];
 	int _cursorCell = 0;
diff --git a/engines/mm/mm1/views_enh/game_commands.cpp b/engines/mm/mm1/views_enh/game_commands.cpp
index d4192680768..cfe8a832927 100644
--- a/engines/mm/mm1/views_enh/game_commands.cpp
+++ b/engines/mm/mm1/views_enh/game_commands.cpp
@@ -29,7 +29,7 @@ namespace ViewsEnh {
 GameCommands::GameCommands(UIElement *owner) :
 		ButtonContainer("GameCommands", owner),
 		_minimap(this) {
-	Xeen::SpriteResource *spr = &g_globals->_mainIcons;
+	Shared::Xeen::SpriteResource *spr = &g_globals->_mainIcons;
 	addButton(Common::Rect(286, 75, 310, 95), KEYBIND_NONE, spr);       // Unlock
 	addButton(Common::Rect(235, 75, 259, 95), KEYBIND_PROTECT, spr);    // Protect
 	addButton(Common::Rect(260, 75, 284, 95), KEYBIND_REST, spr);       // Rest
diff --git a/engines/mm/mm1/views_enh/scroll_view.cpp b/engines/mm/mm1/views_enh/scroll_view.cpp
index c46fdc4efcf..5311d00a3fd 100644
--- a/engines/mm/mm1/views_enh/scroll_view.cpp
+++ b/engines/mm/mm1/views_enh/scroll_view.cpp
@@ -41,7 +41,7 @@ ScrollView::ScrollView(const Common::String &name,
 	_bounds.setBorderSize(FRAME_BORDER_SIZE);
 }
 
-void ScrollView::addButton(Xeen::SpriteResource *sprites,
+void ScrollView::addButton(Shared::Xeen::SpriteResource *sprites,
 		const Common::Point &pos, int frame,
 		const Common::KeyState &key) {
 	_buttons.push_back(Button(sprites, pos, frame, key));
diff --git a/engines/mm/mm1/views_enh/scroll_view.h b/engines/mm/mm1/views_enh/scroll_view.h
index 99045447b2e..f8ba6eb5e7b 100644
--- a/engines/mm/mm1/views_enh/scroll_view.h
+++ b/engines/mm/mm1/views_enh/scroll_view.h
@@ -32,12 +32,12 @@ namespace ViewsEnh {
 
 class ScrollView : public TextView {
 	struct Button {
-		Xeen::SpriteResource *_sprites;
+		Shared::Xeen::SpriteResource *_sprites;
 		Common::Point _pos;
 		int _frame;
 		Common::KeyState _key;
 
-		Button(Xeen::SpriteResource *sprites,
+		Button(Shared::Xeen::SpriteResource *sprites,
 			const Common::Point &pos, int frame,
 			const Common::KeyState &key) :
 			_sprites(sprites), _pos(pos), _frame(frame), _key(key) {
@@ -83,7 +83,7 @@ public:
 	/**
 	 * Add a button for display
 	 */
-	void addButton(Xeen::SpriteResource *sprites,
+	void addButton(Shared::Xeen::SpriteResource *sprites,
 		const Common::Point &pos, int frame,
 		const Common::KeyState &key);
 
diff --git a/engines/mm/module.mk b/engines/mm/module.mk
index f4ba0a97322..69969865f3b 100644
--- a/engines/mm/module.mk
+++ b/engines/mm/module.mk
@@ -248,6 +248,7 @@ MODULE_OBJS := \
 	xeen/xeen.o \
 	shared/xeen/cc_archive.o \
 	shared/xeen/file.o \
+	shared/xeen/sprites.o \
 	shared/xeen/xsurface.o
 
 # This module can be built as a plugin
diff --git a/engines/mm/shared/xeen/sprites.cpp b/engines/mm/shared/xeen/sprites.cpp
new file mode 100644
index 00000000000..1e40aa6bba6
--- /dev/null
+++ b/engines/mm/shared/xeen/sprites.cpp
@@ -0,0 +1,595 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "common/archive.h"
+#include "common/memstream.h"
+#include "common/textconsole.h"
+#include "graphics/palette.h"
+#include "mm/shared/xeen/sprites.h"
+#include "mm/mm.h"
+
+namespace MM {
+namespace Shared {
+namespace Xeen {
+
+#define SCREEN_WIDTH 320
+#define SCREEN_HEIGHT 200
+#define PALETTE_COUNT 256
+#define PALETTE_SIZE (256 * 3)
+#define SCENE_CLIP_LEFT 8
+#define SCENE_CLIP_RIGHT 223
+
+int SpriteResource::_clippedBottom;
+
+SpriteResource::SpriteResource() {
+	_filesize = 0;
+	_data = nullptr;
+}
+
+SpriteResource::SpriteResource(const Common::String &filename) {
+	_data = nullptr;
+	load(filename);
+}
+
+SpriteResource::SpriteResource(const SpriteResource &src) {
+	copy(src);
+}
+
+SpriteResource::~SpriteResource() {
+	clear();
+}
+
+void SpriteResource::copy(const SpriteResource &src) {
+	_filesize = src._filesize;
+	_data = new byte[_filesize];
+	Common::copy(src._data, src._data + _filesize, _data);
+
+	_index.resize(src._index.size());
+	for (uint i = 0; i < src._index.size(); ++i)
+		_index[i] = src._index[i];
+}
+
+SpriteResource &SpriteResource::operator=(const SpriteResource &src) {
+	delete[] _data;
+	_index.clear();
+
+	copy(src);
+
+	return *this;
+}
+
+void SpriteResource::load(const Common::String &filename) {
+	_filename = filename;
+	Common::File f;
+	if (f.open(filename)) {
+		load(f);
+	} else {
+		File f2(filename);
+		load(f2);
+	}
+}
+
+void SpriteResource::load(Common::SeekableReadStream &f) {
+	// Read in a copy of the file
+	_filesize = f.size();
+	delete[] _data;
+	_data = new byte[_filesize];
+	f.read(_data, _filesize);
+
+	// Read in the index
+	f.seek(0);
+	int count = f.readUint16LE();
+	_index.resize(count);
+
+	for (int i = 0; i < count; ++i) {
+		_index[i]._offset1 = f.readUint16LE();
+		_index[i]._offset2 = f.readUint16LE();
+	}
+}
+
+void SpriteResource::clear() {
+	delete[] _data;
+	_data = nullptr;
+	_filesize = 0;
+	_index.clear();
+}
+
+void SpriteResource::draw(XSurface &dest, int frame, const Common::Point &destPos,
+	uint flags, int scale) {
+	draw(dest, frame, destPos, Common::Rect(0, 0, dest.w, dest.h), flags, scale);
+}
+
+void SpriteResource::draw(XSurface &dest, int frame, const Common::Point &destPos,
+	const Common::Rect &bounds, uint flags, int scale) {
+	Common::Rect r = bounds;
+	if (flags & SPRFLAG_BOTTOM_CLIPPED)
+		r.clip(SCREEN_WIDTH, _clippedBottom);
+
+	// Create drawer to handle the rendering
+	SpriteDrawer *drawer;
+	switch (flags & SPRFLAG_MODE_MASK) {
+	case SPRFLAG_DRAWER1:
+		drawer = new SpriteDrawer1(_data, _filesize, flags & 0x1F);
+		break;
+	case SPRFLAG_DRAWER2:
+		drawer = new SpriteDrawer2(_data, _filesize, flags & 0x1F);
+		break;
+	case SPRFLAG_DRAWER3:
+		drawer = new SpriteDrawer3(_data, _filesize, flags & 0x1F);
+		break;
+	case SPRFLAG_DRAWER5:
+		drawer = new SpriteDrawer5(_data, _filesize, flags & 0x1F);
+		break;
+	case SPRFLAG_DRAWER6:
+		drawer = new SpriteDrawer6(_data, _filesize, flags & 0x1F);
+		break;
+	default:
+		drawer = new SpriteDrawer(_data, _filesize);
+		break;
+	}
+
+	// WORKAROUND: Crash clicking Vertigo well in Clouds
+	if (frame < (int)_index.size()) {
+		// Sprites can consist of separate background & foreground
+		drawer->draw(dest, _index[frame]._offset1, destPos, r, flags, scale);
+		if (_index[frame]._offset2)
+			drawer->draw(dest, _index[frame]._offset2, destPos, r, flags, scale);
+	}
+
+	delete drawer;
+}
+
+void SpriteResource::draw(XSurface &dest, int frame) {
+	draw(dest, frame, Common::Point());
+}
+
+void SpriteResource::draw(Graphics::ManagedSurface *dest, int frame, const Common::Point &destPos) {
+	XSurface tmp;
+	tmp.w = dest->w;
+	tmp.h = dest->h;
+	tmp.pitch = dest->pitch;
+	tmp.format = dest->format;
+	tmp.setPixels(dest->getPixels());
+
+	draw(tmp, frame, destPos);
+}
+
+
+Common::Point SpriteResource::getFrameSize(int frame) const {
+	Common::MemoryReadStream f(_data, _filesize);
+	Common::Point frameSize;
+
+	for (int idx = 0; idx < (_index[frame]._offset2 ? 2 : 1); ++idx) {
+		f.seek((idx == 0) ? _index[frame]._offset1 : _index[frame]._offset2);
+		int xOffset = f.readUint16LE();
+		int width = f.readUint16LE();
+		int yOffset = f.readUint16LE();
+		int height = f.readUint16LE();
+
+		frameSize.x = MAX((int)frameSize.x, xOffset + width);
+		frameSize.y = MAX((int)frameSize.y, yOffset + height);
+	}
+
+	return frameSize;
+}
+
+/*------------------------------------------------------------------------*/
+
+void SpriteDrawer::draw(XSurface &dest, uint16 offset, const Common::Point &pt,
+	const Common::Rect &clipRect, uint flags, int scale) {
+	static const uint SCALE_TABLE[] = {
+		0xFFFF, 0xFFEF, 0xEFEF, 0xEFEE, 0xEEEE, 0xEEAE, 0xAEAE, 0xAEAA,
+		0xAAAA, 0xAA8A, 0x8A8A, 0x8A88, 0x8888, 0x8880, 0x8080, 0x8000
+	};
+	static const int PATTERN_STEPS[] = { 0, 1, 1, 1, 2, 2, 3, 3, 0, -1, -1, -1, -2, -2, -3, -3 };
+
+	assert((scale & SCALE_MASK) < 16);
+	uint16 scaleMask = SCALE_TABLE[scale & SCALE_MASK];
+	uint16 scaleMaskX = scaleMask, scaleMaskY = scaleMask;
+	bool flipped = (flags & SPRFLAG_HORIZ_FLIPPED) != 0;
+	int xInc = flipped ? -1 : 1;
+	bool enlarge = (scale & SCALE_ENLARGE) != 0;
+
+	_destTop = (byte *)dest.getBasePtr(clipRect.left, clipRect.top);
+	_destBottom = (byte *)dest.getBasePtr(clipRect.right, clipRect.bottom - 1);
+	_pitch = dest.pitch;
+
+	// Get cell header
+	Common::MemoryReadStream f(_data, _filesize);
+	f.seek(offset);
+	int xOffset = f.readUint16LE();
+	int width = f.readUint16LE();
+	int yOffset = f.readUint16LE();
+	int height = f.readUint16LE();
+
+	// Figure out drawing x, y
+	Common::Point destPos;
+	destPos.x = pt.x + getScaledVal(xOffset, scaleMaskX);
+	destPos.x += (width - getScaledVal(width, scaleMaskX)) / 2;
+
+	destPos.y = pt.y + getScaledVal(yOffset, scaleMaskY);
+
+	// If the flags allow the dest surface to be resized, ensure dest surface is big enough
+	Common::Rect bounds = clipRect;
+	if (flags & SPRFLAG_RESIZE) {
+		if (dest.w < (xOffset + width) || dest.h < (yOffset + height))
+			dest.create(xOffset + width, yOffset + height);
+		bounds = Common::Rect(0, 0, dest.w, dest.h);
+	}
+	if (flags & SPRFLAG_SCENE_CLIPPED) {
+		bounds.clip(Common::Rect(8, 8, 223, 141));
+	}
+
+	uint16 scaleMaskXCopy = scaleMaskX;
+	Common::Rect drawBounds;
+	drawBounds.left = SCREEN_WIDTH;
+	drawBounds.top = SCREEN_HEIGHT;
+	drawBounds.right = drawBounds.bottom = 0;
+
+	// Main loop
+	for (int yCtr = height; yCtr > 0; --yCtr) {
+		// The number of bytes in this scan line
+		int lineLength = f.readByte();
+
+		if (lineLength == 0) {
+			// Skip the specified number of scan lines
+			int numLines = f.readByte();
+			destPos.y += getScaledVal(numLines + 1, scaleMaskY);
+			yCtr -= numLines;
+			continue;
+		}
+
+		// Roll the scale mask
+		uint bit = (scaleMaskY >> 15) & 1;
+		scaleMaskY = ((scaleMaskY & 0x7fff) << 1) + bit;
+
+		if (!bit) {
+			// Not a line to be drawn due to scaling down
+			f.skip(lineLength);
+		} else if (destPos.y < bounds.top || destPos.y >= bounds.bottom) {
+			// Skip over the bytes of the line
+			f.skip(lineLength);
+			destPos.y++;
+		} else {
+			scaleMaskX = scaleMaskXCopy;
+			xOffset = f.readByte();
+
+			// Initialize the array to hold the temporary data for the line. We do this to make it simpler
+			// to handle both deciding which pixels to draw in a scaled image, as well as when images
+			// have been horizontally flipped. Note that we allocate an extra line for before and after our
+			// work line, just in case the sprite is screwed up and overruns the line
+			int tempLine[SCREEN_WIDTH * 3];
+			Common::fill(&tempLine[SCREEN_WIDTH], &tempLine[SCREEN_WIDTH * 3], -1);
+			int *lineP = flipped ? &tempLine[SCREEN_WIDTH + width - 1 - xOffset] : &tempLine[SCREEN_WIDTH + xOffset];
+
+			// Build up the line
+			int byteCount, opr1, opr2;
+			int32 pos;
+			for (byteCount = 1; byteCount < lineLength; ) {
+				// The next byte is an opcode that determines what operators are to follow and how to interpret them.
+				int opcode = f.readByte(); ++byteCount;
+
+				// Decode the opcode
+				int len = opcode & 0x1F;
+				int cmd = (opcode & 0xE0) >> 5;
+
+				switch (cmd) {
+				case 0:   // The following len + 1 bytes are stored as indexes into the color table.
+				case 1:   // The following len + 33 bytes are stored as indexes into the color table.
+					for (int i = 0; i < opcode + 1; ++i, ++byteCount) {
+						byte b = f.readByte();
+						*lineP = b;
+						lineP += xInc;
+					}
+					break;
+
+				case 2:   // The following byte is an index into the color table, draw it len + 3 times.
+					opr1 = f.readByte(); ++byteCount;
+					for (int i = 0; i < len + 3; ++i) {
+						*lineP = opr1;
+						lineP += xInc;
+					}
+					break;
+
+				case 3:   // Stream copy command.
+					opr1 = f.readUint16LE(); byteCount += 2;
+					pos = f.pos();
+					f.seek(-opr1, SEEK_CUR);
+
+					for (int i = 0; i < len + 4; ++i) {
+						*lineP = f.readByte();
+						lineP += xInc;
+					}
+
+					f.seek(pos, SEEK_SET);
+					break;
+
+				case 4:   // The following two bytes are indexes into the color table, draw the pair len + 2 times.
+					opr1 = f.readByte(); ++byteCount;
+					opr2 = f.readByte(); ++byteCount;
+					for (int i = 0; i < len + 2; ++i) {
+						*lineP = opr1;
+						lineP += xInc;
+						*lineP = opr2;
+						lineP += xInc;
+					}
+					break;
+
+				case 5:   // Skip len + 1 pixels
+					lineP += (len + 1) * xInc;
+					break;
+
+				case 6:   // Pattern command.
+				case 7:
+					// The pattern command has a different opcode format
+					len = opcode & 0x07;
+					cmd = (opcode >> 2) & 0x0E;
+
+					opr1 = f.readByte(); ++byteCount;
+					for (int i = 0; i < len + 3; ++i) {
+						*lineP = opr1;
+						lineP += xInc;
+						opr1 += PATTERN_STEPS[cmd + (i % 2)];
+					}
+					break;
+
+				default:
+					break;
+				}
+			}
+			assert(byteCount == lineLength);
+
+			drawBounds.top = MIN(drawBounds.top, destPos.y);
+			drawBounds.bottom = MAX((int)drawBounds.bottom, destPos.y + 1);
+
+			// Handle drawing out the line
+			byte *destP = (byte *)dest.getBasePtr(destPos.x, destPos.y);
+			_destLeft = (byte *)dest.getBasePtr(
+				(flags & SPRFLAG_SCENE_CLIPPED) ? SCENE_CLIP_LEFT : clipRect.left, destPos.y);
+			_destRight = (byte *)dest.getBasePtr(
+				(flags & SPRFLAG_SCENE_CLIPPED) ? SCENE_CLIP_RIGHT : clipRect.right, destPos.y);
+			int16 xp = destPos.x;
+			lineP = &tempLine[SCREEN_WIDTH];
+
+			for (int xCtr = 0; xCtr < width; ++xCtr, ++lineP) {
+				bit = (scaleMaskX >> 15) & 1;
+				scaleMaskX = ((scaleMaskX & 0x7fff) << 1) + bit;
+
+				if (bit) {
+					// Check whether there's a pixel to write, and we're within the allowable bounds. Note that for
+					// the SPRFLAG_SCENE_CLIPPED or when enlarging, we also have an extra horizontal bounds check
+					if (*lineP != -1 && xp >= bounds.left && xp < bounds.right) {
+						drawBounds.left = MIN(drawBounds.left, xp);
+						drawBounds.right = MAX((int)drawBounds.right, xp + 1);
+						drawPixel(destP, (byte)*lineP);
+						if (enlarge) {
+							drawPixel(destP + SCREEN_WIDTH, (byte)*lineP);
+							drawPixel(destP + 1, (byte)*lineP);
+							drawPixel(destP + 1 + SCREEN_WIDTH, (byte)*lineP);
+						}
+					}
+
+					++xp;
+					++destP;
+					if (enlarge) {
+						++destP;
+						++xp;
+					}
+				}
+			}
+
+			++destPos.y;
+			if (enlarge)
+				++destPos.y;
+		}
+	}
+
+	if (drawBounds.isValidRect()) {
+		drawBounds.clip(Common::Rect(0, 0, dest.w, dest.h));
+		if (!drawBounds.isEmpty())
+			dest.addDirtyRect(drawBounds);
+	}
+}
+
+uint SpriteDrawer::getScaledVal(int xy, uint16 &scaleMask) {
+	if (!xy)
+		return 0;
+
+	uint result = 0;
+	for (int idx = 0; idx < xy; ++idx) {
+		uint bit = (scaleMask >> 15) & 1;
+		scaleMask = ((scaleMask & 0x7fff) << 1) + bit;
+		result += bit;
+	}
+
+	return result;
+}
+
+void SpriteDrawer::drawPixel(byte *dest, byte pixel) {
+	*dest = pixel;
+}
+
+void SpriteDrawer::rcr(uint16 &val, bool &cf) {
+	bool newCf = (val & 1);
+	val = (val >> 1) | (cf ? 0x8000 : 0);
+	cf = newCf;
+}
+
+/*------------------------------------------------------------------------*/
+
+static const byte DRAWER1_OFFSET[24] = {
+	0x30, 0xC0, 0xB0, 0x10, 0x41, 0x20, 0x40, 0x21, 0x48, 0x46, 0x43, 0x40,
+	0xD0, 0xD3, 0xD6, 0xD8, 0x01, 0x04, 0x07, 0x0A, 0xEA, 0xEE, 0xF2, 0xF6
+};
+
+static const byte DRAWER1_MASK[24] = {
+	0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x0F, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x0F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x07, 0x07
+};
+
+SpriteDrawer1::SpriteDrawer1(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) {
+	_offset = DRAWER1_OFFSET[index];
+	_mask = DRAWER1_MASK[index];
+}
+
+void SpriteDrawer1::drawPixel(byte *dest, byte pixel) {
+	*dest = (pixel & _mask) + _offset;
+}
+
+/*------------------------------------------------------------------------*/
+
+static const byte DRAWER2_MASK1[32] = {
+	3, 0, 3, 0, 3, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0,
+	1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const byte DRAWER2_MASK2[16] = {
+	0x7E, 0x7E, 0x7E, 0x7E, 0x3E, 0x3E, 0x3E, 0x3E,
+	0x1E, 0x1E, 0x1E, 0x1E, 0x0E, 0x0E, 0x0E, 0x0E
+};
+
+static const int8 DRAWER2_DELTA[64] = {
+	-3, 3, 0, 0, 0, 0, 0, 0,
+	-5, 5, 0, 0, 0, 0, 0, 0,
+	-7, 7, 0, 0, 0, 0, 0, 0,
+	-9, 9, 0, 0, 0, 0, 0, 0,
+	-7, 7, 0, 0, 0, 0, 0, 0,
+	-9, 9, 0, 0, 0, 0, 0, 0,
+	-11, 11, 0, 0, 0, 0, 0, 0,
+	-13, 13, 0, 0, 0, 0, 0, 0
+};
+
+SpriteDrawer2::SpriteDrawer2(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) {
+	_mask1 = DRAWER2_MASK1[index];
+	_mask2 = DRAWER2_MASK2[index];
+
+	MM::MMEngine *engine = static_cast<MM::MMEngine *>(g_engine);
+	_random1 = engine->getRandomNumber(0xffff);
+	_random2 = engine->getRandomNumber(0xffff);
+}
+
+void SpriteDrawer2::drawPixel(byte *dest, byte pixel) {
+	bool flag = (_random1 & 0x8000) != 0;
+	_random1 = (int)((uint16)_random1 << 1) - _random2 - (flag ? 1 : 0);
+
+	rcr(_random2, flag);
+	rcr(_random2, flag);
+	_random2 ^= _random1;
+
+	dest += DRAWER2_DELTA[(_random2 & _mask1 & _mask2) / 2];
+	if (dest >= _destLeft && dest < _destRight) {
+		dest += _pitch * DRAWER2_DELTA[((_random2 >> 8) &_mask1 &_mask2) / 2];
+
+		if (dest >= _destTop && dest < _destBottom) {
+			*dest = pixel;
+		}
+	}
+}
+
+/*------------------------------------------------------------------------*/
+
+static const uint16 DRAWER3_MASK[4] = { 1, 3, 7, 15 };
+static const uint16 DRAWER3_OFFSET[4] = { 1, 2, 4, 8 };
+
+SpriteDrawer3::SpriteDrawer3(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) {
+	_offset = DRAWER3_OFFSET[index];
+	_mask = DRAWER3_MASK[index];
+
+	g_system->getPaletteManager()->grabPalette(_palette, 0, PALETTE_COUNT);
+	_hasPalette = false;
+	for (byte *pal = _palette; pal < _palette + PALETTE_SIZE && !_hasPalette; ++pal)
+		_hasPalette = *pal != 0;
+}
+
+void SpriteDrawer3::drawPixel(byte *dest, byte pixel) {
+	// WORKAROUND: This is slightly different then the original:
+	// 1) The original has bunches of black pixels appearing. This does index increments to avoid such pixels
+	// 2) It also prevents any pixels being drawn in the single initial frame until the palette is set
+	if (_hasPalette) {
+		byte level = (pixel & _mask) - _offset + (*dest & 0xf);
+
+		if (level >= 0x80) {
+			*dest &= 0xf0;
+		} else if (level <= 0xf) {
+			*dest = (*dest & 0xf0) | level;
+		} else {
+			*dest |= 0xf;
+		}
+
+		//
+		while (*dest < 0xff && !_palette[*dest * 3] && !_palette[*dest * 3 + 1] && !_palette[*dest * 3 + 2])
+			++ *dest;
+	}
+}
+
+/*------------------------------------------------------------------------*/
+
+static const byte DRAWER4_THRESHOLD[4] = { 4, 7, 10, 13 };
+
+SpriteDrawer4::SpriteDrawer4(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) {
+	_threshold = DRAWER4_THRESHOLD[index];
+}
+
+void SpriteDrawer4::drawPixel(byte *dest, byte pixel) {
+	if ((pixel & 0xf) >= _threshold)
+		*dest = pixel;
+}
+
+/*------------------------------------------------------------------------*/
+
+static const uint16 DRAWER5_THRESHOLD[4] = { 0x3333, 0x6666, 0x999A, 0xCCCD };
+
+SpriteDrawer5::SpriteDrawer5(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) {
+	_threshold = DRAWER5_THRESHOLD[index];
+
+	MM::MMEngine *engine = static_cast<MM::MMEngine *>(g_engine);
+	_random1 = engine->getRandomNumber(0xffff);
+	_random2 = engine->getRandomNumber(0xffff);
+}
+
+void SpriteDrawer5::drawPixel(byte *dest, byte pixel) {
+	bool flag = (_random1 & 0x8000) != 0;
+	_random1 = (int)((uint16)_random1 << 1) - _random2 - (flag ? 1 : 0);
+
+	rcr(_random2, flag);
+	rcr(_random2, flag);
+	_random2 ^= _random1;
+
+	if (_random2 > _threshold)
+		*dest = pixel;
+}
+
+/*------------------------------------------------------------------------*/
+
+static const byte DRAWER6_MASK[16] = { 1, 2, 4, 8, 1, 3, 7, 15, 8, 12, 14, 15, 1, 2, 1, 2 };
+
+SpriteDrawer6::SpriteDrawer6(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) {
+	_mask = DRAWER6_MASK[index];
+}
+
+void SpriteDrawer6::drawPixel(byte *dest, byte pixel) {
+	*dest = pixel ^ _mask;
+}
+
+} // End of namespace Xeen
+} // End of namespace Shared
+} // End of namespace MM
diff --git a/engines/mm/shared/xeen/sprites.h b/engines/mm/shared/xeen/sprites.h
new file mode 100644
index 00000000000..c75bea695aa
--- /dev/null
+++ b/engines/mm/shared/xeen/sprites.h
@@ -0,0 +1,317 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MM_SHARED_XEEN_SPRITES_H
+#define MM_SHARED_XEEN_SPRITES_H
+
+#include "common/scummsys.h"
+#include "common/array.h"
+#include "common/file.h"
+#include "graphics/surface.h"
+#include "mm/shared/xeen/file.h"
+#include "mm/shared/xeen/xsurface.h"
+
+namespace MM {
+namespace Xeen {
+class XeenEngine;
+class Window;
+} // namespace Xeen
+
+namespace Shared {
+namespace Xeen {
+
+enum {
+	SCALE_MASK = 0x7FFF, SCALE_ENLARGE = 0x8000
+};
+
+enum SpriteFlags {
+	SPRFLAG_MODE_MASK = 0xF00, SPRFLAG_DRAWER1 = 0x100, SPRFLAG_DRAWER2 = 0x200,
+	SPRFLAG_DRAWER3 = 0x300, SPRFLAG_DRAWER4 = 0x400, SPRFLAG_DRAWER5 = 0x500, SPRFLAG_DRAWER6 = 0x600,
+	SPRFLAG_DRAWER7 = 0x700, SPRFLAG_800 = 0x800, SPRFLAG_SCENE_CLIPPED = 0x2000,
+	SPRFLAG_BOTTOM_CLIPPED = 0x4000, SPRFLAG_HORIZ_FLIPPED = 0x8000, SPRFLAG_RESIZE = 0x10000
+};
+
+class SpriteResource {
+protected:
+	struct IndexEntry {
+		uint16 _offset1, _offset2;
+	};
+	Common::Array<IndexEntry> _index;
+	size_t _filesize;
+	byte *_data;
+	Common::String _filename;
+	static int _clippedBottom;
+
+	/**
+	 * Load a sprite resource from a stream
+	 */
+	void load(Common::SeekableReadStream &f);
+
+	/**
+	 * Draw the sprite onto the given surface
+	 */
+	void draw(XSurface &dest, int frame, const Common::Point &destPos,
+		const Common::Rect &bounds, uint flags = 0, int scale = 0);
+
+	/**
+	 * Deep copy assuming that the current instance is clean
+	 */
+	void copy(const SpriteResource &src);
+public:
+	SpriteResource();
+	SpriteResource(const Common::String &filename);
+	SpriteResource(const SpriteResource &src);
+
+	virtual ~SpriteResource();
+
+	/**
+	 * Copy operator for duplicating a sprite resource
+	 */
+	SpriteResource &operator=(const SpriteResource &src);
+
+	/**
+	 * Load a sprite resource from a given file
+	 */
+	void load(const Common::String &filename);
+
+	/**
+	 * Clears the sprite resource
+	 */
+	void clear();
+
+	/**
+	 * Draw a sprite onto a surface
+	 * @param dest		Destination surface
+	 * @param frame		Frame number
+	 * @param destPos	Destination position
+	 * @param flags		Flags
+	 * @param scale		Scale: 0=No scale, SCALE_ENLARGE=Enlarge it
+	 *					1..15   -> reduces the sprite: the higher, the smaller it'll be
+	 */
+	void draw(XSurface &dest, int frame, const Common::Point &destPos,
+		uint flags = 0, int scale = 0);
+
+	/**
+	 * Draw a sprite onto a given window
+	 * @param windowIndex	Destination window number
+	 * @param frame		Frame number
+	 * @param destPos	Destination position
+	 * @param flags		Flags
+	 * @param scale		Scale: 0=No scale, SCALE_ENLARGE=Enlarge it
+	 *					1..15   -> reduces the sprite: the higher, the smaller it'll be
+	 */
+	void draw(int windowIndex, int frame, const Common::Point &destPos,
+		uint flags = 0, int scale = 0);
+
+	/**
+	 * Draw the sprite onto the given surface
+	 * @param dest		Destination surface
+	 * @param frame		Frame number
+	 */
+	void draw(XSurface &dest, int frame);
+
+	/**
+	 * Draw the sprite onto a given surface
+	 */
+	void draw(Graphics::ManagedSurface *dest, int frame, const Common::Point &destPos);
+
+	/**
+	 * Gets the size of a sprite
+	 */
+	Common::Point getFrameSize(int frame) const;
+
+	/**
+	 * Returns the number of frames the sprite resource has
+	 */
+	size_t size() const {
+		return _index.size();
+	}
+
+	/**
+	 * Returns true if the sprite resource is empty (ie. nothing is loaded)
+	 */
+	bool empty() const {
+		return _index.size() == 0;
+	}
+
+	/**
+	 * Set the bottom Y position where sprites are clipped if SPRFLAG_BOTTOM_CLIPPED
+	 * is applied
+	 */
+	static void setClippedBottom(int y) {
+		_clippedBottom = y;
+	}
+};
+
+/**
+ * Basic sprite drawer
+ */
+class SpriteDrawer {
+private:
+	byte *_data = nullptr;
+	size_t _filesize = 0;
+protected:
+	byte *_destTop = nullptr, *_destBottom = nullptr;
+	byte *_destLeft = nullptr, *_destRight = nullptr;
+	int _pitch = 0;
+private:
+	/**
+	 * Scale a co-ordinate value based on the passed scaling mask
+	 */
+	static uint getScaledVal(int xy, uint16 &scaleMask);
+protected:
+	/**
+	 * Roll carry right opcode emulation
+	 */
+	void rcr(uint16 &val, bool &cf);
+
+	/**
+	 * Output a pixel
+	 */
+	virtual void drawPixel(byte *dest, byte pixel);
+public:
+	/**
+	 * Constructor
+	 */
+	SpriteDrawer(byte *data, size_t filesize) : _data(data), _filesize(filesize) {
+	}
+
+	/**
+	 * Destructor
+	 */
+	virtual ~SpriteDrawer() {
+	}
+
+	/**
+	 * Draw a sprite frame based on a passed offset into the data stream
+	 */
+	void draw(XSurface &dest, uint16 offset, const Common::Point &pt,
+		const Common::Rect &clipRect, uint flags, int scale);
+};
+
+class SpriteDrawer1 : public SpriteDrawer {
+private:
+	byte _offset = 0, _mask = 0;
+protected:
+	/**
+	 * Output a pixel
+	 */
+	void drawPixel(byte *dest, byte pixel) override;
+public:
+	/**
+	 * Constructor
+	 */
+	SpriteDrawer1(byte *data, size_t filesize, int index);
+};
+
+/**
+ * Scrambles up the sprite by drawing many of the pixels randomly
+ * at a horizontal or vertical offset
+ */
+class SpriteDrawer2 : public SpriteDrawer {
+private:
+	uint16 _mask1 = 0, _mask2 = 0;
+	uint16 _random1 = 0, _random2 = 0;
+private:
+	/**
+	 * Output a pixel
+	 */
+	void drawPixel(byte *dest, byte pixel) override;
+public:
+	/**
+	 * Constructor
+	 */
+	SpriteDrawer2(byte *data, size_t filesize, int index);
+};
+
+/**
+ * Draws the sprite as faint ghostly, see-through.
+ */
+class SpriteDrawer3 : public SpriteDrawer {
+private:
+	uint16 _offset = 0, _mask = 0;
+	byte _palette[256 * 3];
+	bool _hasPalette = false;
+private:
+	/**
+	 * Output a pixel
+	 */
+	void drawPixel(byte *dest, byte pixel) override;
+public:
+	/**
+	 * Constructor
+	 */
+	SpriteDrawer3(byte *data, size_t filesize, int index);
+};
+
+class SpriteDrawer4 : public SpriteDrawer {
+private:
+	byte _threshold = 0;
+protected:
+	/**
+	 * Output a pixel
+	 */
+	void drawPixel(byte *dest, byte pixel) override;
+public:
+	/**
+	 * Constructor
+	 */
+	SpriteDrawer4(byte *data, size_t filesize, int index);
+};
+
+/**
+ * Draws a sprite with a fuzziness effect where only some pixels of the sprite are randomly drawn
+ */
+class SpriteDrawer5 : public SpriteDrawer {
+private:
+	uint16 _threshold = 0, _random1 = 0, _random2 = 0;
+protected:
+	/**
+	 * Output a pixel
+	 */
+	void drawPixel(byte *dest, byte pixel) override;
+public:
+	/**
+	 * Constructor
+	 */
+	SpriteDrawer5(byte *data, size_t filesize, int index);
+};
+
+class SpriteDrawer6 : public SpriteDrawer {
+private:
+	byte _mask = 0;
+protected:
+	/**
+	 * Output a pixel
+	 */
+	void drawPixel(byte *dest, byte pixel) override;
+public:
+	/**
+	 * Constructor
+	 */
+	SpriteDrawer6(byte *data, size_t filesize, int index);
+};
+
+} // End of namespace Xeen
+} // End of namespace Shared
+} // End of namespace MM
+
+#endif
diff --git a/engines/mm/xeen/sprites.cpp b/engines/mm/xeen/sprites.cpp
index 43e9158944f..8d92648fc73 100644
--- a/engines/mm/xeen/sprites.cpp
+++ b/engines/mm/xeen/sprites.cpp
@@ -8,127 +8,37 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
  *
  */
 
-#include "common/scummsys.h"
-#include "common/archive.h"
-#include "common/memstream.h"
-#include "common/textconsole.h"
-#include "mm/xeen/xeen.h"
-#include "mm/xeen/screen.h"
 #include "mm/xeen/sprites.h"
-
-#include "graphics/palette.h"
+#include "mm/xeen/xeen.h"
 
 namespace MM {
 namespace Xeen {
 
-#define SCENE_CLIP_LEFT 8
-#define SCENE_CLIP_RIGHT 223
-
-int SpriteResource::_clippedBottom;
-
-SpriteResource::SpriteResource() {
-	_filesize = 0;
-	_data = nullptr;
-}
-
-SpriteResource::SpriteResource(const Common::String &filename) {
-	_data = nullptr;
-	load(filename);
-}
-
 SpriteResource::SpriteResource(const Common::String &filename, int ccMode) {
 	_data = nullptr;
 	load(filename, ccMode);
 }
 
-SpriteResource::SpriteResource(const SpriteResource &src) {
-	copy(src);
-}
-
-SpriteResource::~SpriteResource() {
-	clear();
-}
-
-void SpriteResource::copy(const SpriteResource &src) {
-	_filesize = src._filesize;
-	_data = new byte[_filesize];
-	Common::copy(src._data, src._data + _filesize, _data);
-
-	_index.resize(src._index.size());
-	for (uint i = 0; i < src._index.size(); ++i)
-		_index[i] = src._index[i];
-}
-
-SpriteResource &SpriteResource::operator=(const SpriteResource &src) {
-	delete[] _data;
-	_index.clear();
-
-	copy(src);
-
-	return *this;
-}
-
-void SpriteResource::load(const Common::String &filename) {
-	_filename = filename;
-	Common::File f;
-	if (f.open(filename)) {
-		load(f);
-	} else {
-		File f2(filename);
-		load(f2);
-	}
-}
-
 void SpriteResource::load(const Common::String &filename, int ccMode) {
 	_filename = filename;
 	File f(filename, ccMode);
-	load(f);
-}
-
-void SpriteResource::load(Common::SeekableReadStream &f) {
-	// Read in a copy of the file
-	_filesize = f.size();
-	delete[] _data;
-	_data = new byte[_filesize];
-	f.read(_data, _filesize);
-
-	// Read in the index
-	f.seek(0);
-	int count = f.readUint16LE();
-	_index.resize(count);
-
-	for (int i = 0; i < count; ++i) {
-		_index[i]._offset1 = f.readUint16LE();
-		_index[i]._offset2 = f.readUint16LE();
-	}
-}
-
-void SpriteResource::clear() {
-	delete[] _data;
-	_data = nullptr;
-	_filesize = 0;
-	_index.clear();
-}
-
-void SpriteResource::draw(XSurface &dest, int frame, const Common::Point &destPos,
-	uint flags, int scale) {
-	draw(dest, frame, destPos, Common::Rect(0, 0, dest.w, dest.h), flags, scale);
+	Shared::Xeen::SpriteResource::load(f);
 }
 
 void SpriteResource::draw(Window &dest, int frame, const Common::Point &destPos,
 	uint flags, int scale) {
-	draw(dest, frame, destPos, dest.getBounds(), flags, scale);
+	Shared::Xeen::SpriteResource::draw(dest, frame, destPos, dest.getBounds(), flags, scale);
 }
 
 void SpriteResource::draw(int windowIndex, int frame, const Common::Point &destPos,
@@ -137,478 +47,9 @@ void SpriteResource::draw(int windowIndex, int frame, const Common::Point &destP
 	draw(win, frame, destPos, flags, scale);
 }
 
-void SpriteResource::draw(XSurface &dest, int frame, const Common::Point &destPos,
-	const Common::Rect &bounds, uint flags, int scale) {
-	Common::Rect r = bounds;
-	if (flags & SPRFLAG_BOTTOM_CLIPPED)
-		r.clip(SCREEN_WIDTH, _clippedBottom);
-
-	// Create drawer to handle the rendering
-	SpriteDrawer *drawer;
-	switch (flags & SPRFLAG_MODE_MASK) {
-	case SPRFLAG_DRAWER1:
-		drawer = new SpriteDrawer1(_data, _filesize, flags & 0x1F);
-		break;
-	case SPRFLAG_DRAWER2:
-		drawer = new SpriteDrawer2(_data, _filesize, flags & 0x1F);
-		break;
-	case SPRFLAG_DRAWER3:
-		drawer = new SpriteDrawer3(_data, _filesize, flags & 0x1F);
-		break;
-	case SPRFLAG_DRAWER5:
-		drawer = new SpriteDrawer5(_data, _filesize, flags & 0x1F);
-		break;
-	case SPRFLAG_DRAWER6:
-		drawer = new SpriteDrawer6(_data, _filesize, flags & 0x1F);
-		break;
-	default:
-		drawer = new SpriteDrawer(_data, _filesize);
-		break;
-	}
-
-	// WORKAROUND: Crash clicking Vertigo well in Clouds
-	if (frame < (int)_index.size()) {
-		// Sprites can consist of separate background & foreground
-		drawer->draw(dest, _index[frame]._offset1, destPos, r, flags, scale);
-		if (_index[frame]._offset2)
-			drawer->draw(dest, _index[frame]._offset2, destPos, r, flags, scale);
-	}
-
-	delete drawer;
-}
-
-void SpriteResource::draw(XSurface &dest, int frame) {
-	draw(dest, frame, Common::Point());
-}
-
 void SpriteResource::draw(int windowIndex, int frame) {
 	draw((*g_vm->_windows)[windowIndex], frame, Common::Point());
 }
 
-void SpriteResource::draw(Graphics::ManagedSurface *dest, int frame, const Common::Point &destPos) {
-	XSurface tmp;
-	tmp.w = dest->w;
-	tmp.h = dest->h;
-	tmp.pitch = dest->pitch;
-	tmp.format = dest->format;
-	tmp.setPixels(dest->getPixels());
-
-	draw(tmp, frame, destPos);
-}
-
-
-Common::Point SpriteResource::getFrameSize(int frame) const {
-	Common::MemoryReadStream f(_data, _filesize);
-	Common::Point frameSize;
-
-	for (int idx = 0; idx < (_index[frame]._offset2 ? 2 : 1); ++idx) {
-		f.seek((idx == 0) ? _index[frame]._offset1 : _index[frame]._offset2);
-		int xOffset = f.readUint16LE();
-		int width = f.readUint16LE();
-		int yOffset = f.readUint16LE();
-		int height = f.readUint16LE();
-
-		frameSize.x = MAX((int)frameSize.x, xOffset + width);
-		frameSize.y = MAX((int)frameSize.y, yOffset + height);
-	}
-
-	return frameSize;
-}
-
-/*------------------------------------------------------------------------*/
-
-void SpriteDrawer::draw(XSurface &dest, uint16 offset, const Common::Point &pt,
-	const Common::Rect &clipRect, uint flags, int scale) {
-	static const uint SCALE_TABLE[] = {
-		0xFFFF, 0xFFEF, 0xEFEF, 0xEFEE, 0xEEEE, 0xEEAE, 0xAEAE, 0xAEAA,
-		0xAAAA, 0xAA8A, 0x8A8A, 0x8A88, 0x8888, 0x8880, 0x8080, 0x8000
-	};
-	static const int PATTERN_STEPS[] = { 0, 1, 1, 1, 2, 2, 3, 3, 0, -1, -1, -1, -2, -2, -3, -3 };
-
-	assert((scale & SCALE_MASK) < 16);
-	uint16 scaleMask = SCALE_TABLE[scale & SCALE_MASK];
-	uint16 scaleMaskX = scaleMask, scaleMaskY = scaleMask;
-	bool flipped = (flags & SPRFLAG_HORIZ_FLIPPED) != 0;
-	int xInc = flipped ? -1 : 1;
-	bool enlarge = (scale & SCALE_ENLARGE) != 0;
-
-	_destTop = (byte *)dest.getBasePtr(clipRect.left, clipRect.top);
-	_destBottom = (byte *)dest.getBasePtr(clipRect.right, clipRect.bottom - 1);
-	_pitch = dest.pitch;
-
-	// Get cell header
-	Common::MemoryReadStream f(_data, _filesize);
-	f.seek(offset);
-	int xOffset = f.readUint16LE();
-	int width = f.readUint16LE();
-	int yOffset = f.readUint16LE();
-	int height = f.readUint16LE();
-
-	// Figure out drawing x, y
-	Common::Point destPos;
-	destPos.x = pt.x + getScaledVal(xOffset, scaleMaskX);
-	destPos.x += (width - getScaledVal(width, scaleMaskX)) / 2;
-
-	destPos.y = pt.y + getScaledVal(yOffset, scaleMaskY);
-
-	// If the flags allow the dest surface to be resized, ensure dest surface is big enough
-	Common::Rect bounds = clipRect;
-	if (flags & SPRFLAG_RESIZE) {
-		if (dest.w < (xOffset + width) || dest.h < (yOffset + height))
-			dest.create(xOffset + width, yOffset + height);
-		bounds = Common::Rect(0, 0, dest.w, dest.h);
-	}
-	if (flags & SPRFLAG_SCENE_CLIPPED) {
-		bounds.clip(Common::Rect(8, 8, 223, 141));
-	}
-
-	uint16 scaleMaskXCopy = scaleMaskX;
-	Common::Rect drawBounds;
-	drawBounds.left = SCREEN_WIDTH;
-	drawBounds.top = SCREEN_HEIGHT;
-	drawBounds.right = drawBounds.bottom = 0;
-
-	// Main loop
-	for (int yCtr = height; yCtr > 0; --yCtr) {
-		// The number of bytes in this scan line
-		int lineLength = f.readByte();
-
-		if (lineLength == 0) {
-			// Skip the specified number of scan lines
-			int numLines = f.readByte();
-			destPos.y += getScaledVal(numLines + 1, scaleMaskY);
-			yCtr -= numLines;
-			continue;
-		}
-
-		// Roll the scale mask
-		uint bit = (scaleMaskY >> 15) & 1;
-		scaleMaskY = ((scaleMaskY & 0x7fff) << 1) + bit;
-
-		if (!bit) {
-			// Not a line to be drawn due to scaling down
-			f.skip(lineLength);
-		} else if (destPos.y < bounds.top || destPos.y >= bounds.bottom) {
-			// Skip over the bytes of the line
-			f.skip(lineLength);
-			destPos.y++;
-		} else {
-			scaleMaskX = scaleMaskXCopy;
-			xOffset = f.readByte();
-
-			// Initialize the array to hold the temporary data for the line. We do this to make it simpler
-			// to handle both deciding which pixels to draw in a scaled image, as well as when images
-			// have been horizontally flipped. Note that we allocate an extra line for before and after our
-			// work line, just in case the sprite is screwed up and overruns the line
-			int tempLine[SCREEN_WIDTH * 3];
-			Common::fill(&tempLine[SCREEN_WIDTH], &tempLine[SCREEN_WIDTH * 3], -1);
-			int *lineP = flipped ? &tempLine[SCREEN_WIDTH + width - 1 - xOffset] : &tempLine[SCREEN_WIDTH + xOffset];
-
-			// Build up the line
-			int byteCount, opr1, opr2;
-			int32 pos;
-			for (byteCount = 1; byteCount < lineLength; ) {
-				// The next byte is an opcode that determines what operators are to follow and how to interpret them.
-				int opcode = f.readByte(); ++byteCount;
-
-				// Decode the opcode
-				int len = opcode & 0x1F;
-				int cmd = (opcode & 0xE0) >> 5;
-
-				switch (cmd) {
-				case 0:   // The following len + 1 bytes are stored as indexes into the color table.
-				case 1:   // The following len + 33 bytes are stored as indexes into the color table.
-					for (int i = 0; i < opcode + 1; ++i, ++byteCount) {
-						byte b = f.readByte();
-						*lineP = b;
-						lineP += xInc;
-					}
-					break;
-
-				case 2:   // The following byte is an index into the color table, draw it len + 3 times.
-					opr1 = f.readByte(); ++byteCount;
-					for (int i = 0; i < len + 3; ++i) {
-						*lineP = opr1;
-						lineP += xInc;
-					}
-					break;
-
-				case 3:   // Stream copy command.
-					opr1 = f.readUint16LE(); byteCount += 2;
-					pos = f.pos();
-					f.seek(-opr1, SEEK_CUR);
-
-					for (int i = 0; i < len + 4; ++i) {
-						*lineP = f.readByte();
-						lineP += xInc;
-					}
-
-					f.seek(pos, SEEK_SET);
-					break;
-
-				case 4:   // The following two bytes are indexes into the color table, draw the pair len + 2 times.
-					opr1 = f.readByte(); ++byteCount;
-					opr2 = f.readByte(); ++byteCount;
-					for (int i = 0; i < len + 2; ++i) {
-						*lineP = opr1;
-						lineP += xInc;
-						*lineP = opr2;
-						lineP += xInc;
-					}
-					break;
-
-				case 5:   // Skip len + 1 pixels
-					lineP += (len + 1) * xInc;
-					break;
-
-				case 6:   // Pattern command.
-				case 7:
-					// The pattern command has a different opcode format
-					len = opcode & 0x07;
-					cmd = (opcode >> 2) & 0x0E;
-
-					opr1 = f.readByte(); ++byteCount;
-					for (int i = 0; i < len + 3; ++i) {
-						*lineP = opr1;
-						lineP += xInc;
-						opr1 += PATTERN_STEPS[cmd + (i % 2)];
-					}
-					break;
-
-				default:
-					break;
-				}
-			}
-			assert(byteCount == lineLength);
-
-			drawBounds.top = MIN(drawBounds.top, destPos.y);
-			drawBounds.bottom = MAX((int)drawBounds.bottom, destPos.y + 1);
-
-			// Handle drawing out the line
-			byte *destP = (byte *)dest.getBasePtr(destPos.x, destPos.y);
-			_destLeft = (byte *)dest.getBasePtr(
-				(flags & SPRFLAG_SCENE_CLIPPED) ? SCENE_CLIP_LEFT : clipRect.left, destPos.y);
-			_destRight = (byte *)dest.getBasePtr(
-				(flags & SPRFLAG_SCENE_CLIPPED) ? SCENE_CLIP_RIGHT : clipRect.right, destPos.y);
-			int16 xp = destPos.x;
-			lineP = &tempLine[SCREEN_WIDTH];
-
-			for (int xCtr = 0; xCtr < width; ++xCtr, ++lineP) {
-				bit = (scaleMaskX >> 15) & 1;
-				scaleMaskX = ((scaleMaskX & 0x7fff) << 1) + bit;
-
-				if (bit) {
-					// Check whether there's a pixel to write, and we're within the allowable bounds. Note that for
-					// the SPRFLAG_SCENE_CLIPPED or when enlarging, we also have an extra horizontal bounds check
-					if (*lineP != -1 && xp >= bounds.left && xp < bounds.right) {
-						drawBounds.left = MIN(drawBounds.left, xp);
-						drawBounds.right = MAX((int)drawBounds.right, xp + 1);
-						drawPixel(destP, (byte)*lineP);
-						if (enlarge) {
-							drawPixel(destP + SCREEN_WIDTH, (byte)*lineP);
-							drawPixel(destP + 1, (byte)*lineP);
-							drawPixel(destP + 1 + SCREEN_WIDTH, (byte)*lineP);
-						}
-					}
-
-					++xp;
-					++destP;
-					if (enlarge) {
-						++destP;
-						++xp;
-					}
-				}
-			}
-
-			++destPos.y;
-			if (enlarge)
-				++destPos.y;
-		}
-	}
-
-	if (drawBounds.isValidRect()) {
-		drawBounds.clip(Common::Rect(0, 0, dest.w, dest.h));
-		if (!drawBounds.isEmpty())
-			dest.addDirtyRect(drawBounds);
-	}
-}
-
-uint SpriteDrawer::getScaledVal(int xy, uint16 &scaleMask) {
-	if (!xy)
-		return 0;
-
-	uint result = 0;
-	for (int idx = 0; idx < xy; ++idx) {
-		uint bit = (scaleMask >> 15) & 1;
-		scaleMask = ((scaleMask & 0x7fff) << 1) + bit;
-		result += bit;
-	}
-
-	return result;
-}
-
-void SpriteDrawer::drawPixel(byte *dest, byte pixel) {
-	*dest = pixel;
-}
-
-void SpriteDrawer::rcr(uint16 &val, bool &cf) {
-	bool newCf = (val & 1);
-	val = (val >> 1) | (cf ? 0x8000 : 0);
-	cf = newCf;
-}
-
-/*------------------------------------------------------------------------*/
-
-static const byte DRAWER1_OFFSET[24] = {
-	0x30, 0xC0, 0xB0, 0x10, 0x41, 0x20, 0x40, 0x21, 0x48, 0x46, 0x43, 0x40,
-	0xD0, 0xD3, 0xD6, 0xD8, 0x01, 0x04, 0x07, 0x0A, 0xEA, 0xEE, 0xF2, 0xF6
-};
-
-static const byte DRAWER1_MASK[24] = {
-	0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x0F, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x0F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x07, 0x07
-};
-
-SpriteDrawer1::SpriteDrawer1(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) {
-	_offset = DRAWER1_OFFSET[index];
-	_mask = DRAWER1_MASK[index];
-}
-
-void SpriteDrawer1::drawPixel(byte *dest, byte pixel) {
-	*dest = (pixel & _mask) + _offset;
-}
-
-/*------------------------------------------------------------------------*/
-
-static const byte DRAWER2_MASK1[32] = {
-	3, 0, 3, 0, 3, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0,
-	1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
-};
-
-static const byte DRAWER2_MASK2[16] = {
-	0x7E, 0x7E, 0x7E, 0x7E, 0x3E, 0x3E, 0x3E, 0x3E,
-	0x1E, 0x1E, 0x1E, 0x1E, 0x0E, 0x0E, 0x0E, 0x0E
-};
-
-static const int8 DRAWER2_DELTA[64] = {
-	-3, 3, 0, 0, 0, 0, 0, 0,
-	-5, 5, 0, 0, 0, 0, 0, 0,
-	-7, 7, 0, 0, 0, 0, 0, 0,
-	-9, 9, 0, 0, 0, 0, 0, 0,
-	-7, 7, 0, 0, 0, 0, 0, 0,
-	-9, 9, 0, 0, 0, 0, 0, 0,
-	-11, 11, 0, 0, 0, 0, 0, 0,
-	-13, 13, 0, 0, 0, 0, 0, 0
-};
-
-SpriteDrawer2::SpriteDrawer2(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) {
-	_mask1 = DRAWER2_MASK1[index];
-	_mask2 = DRAWER2_MASK2[index];
-
-	_random1 = g_vm->getRandomNumber(0xffff);
-	_random2 = g_vm->getRandomNumber(0xffff);
-}
-
-void SpriteDrawer2::drawPixel(byte *dest, byte pixel) {
-	bool flag = (_random1 & 0x8000) != 0;
-	_random1 = (int)((uint16)_random1 << 1) - _random2 - (flag ? 1 : 0);
-
-	rcr(_random2, flag);
-	rcr(_random2, flag);
-	_random2 ^= _random1;
-
-	dest += DRAWER2_DELTA[(_random2 & _mask1 & _mask2) / 2];
-	if (dest >= _destLeft && dest < _destRight) {
-		dest += _pitch * DRAWER2_DELTA[((_random2 >> 8) &_mask1 &_mask2) / 2];
-
-		if (dest >= _destTop && dest < _destBottom) {
-			*dest = pixel;
-		}
-	}
-}
-
-/*------------------------------------------------------------------------*/
-
-static const uint16 DRAWER3_MASK[4] = { 1, 3, 7, 15 };
-static const uint16 DRAWER3_OFFSET[4] = { 1, 2, 4, 8 };
-
-SpriteDrawer3::SpriteDrawer3(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) {
-	_offset = DRAWER3_OFFSET[index];
-	_mask = DRAWER3_MASK[index];
-
-	g_system->getPaletteManager()->grabPalette(_palette, 0, PALETTE_COUNT);
-	_hasPalette = false;
-	for (byte *pal = _palette; pal < _palette + PALETTE_SIZE && !_hasPalette; ++pal)
-		_hasPalette = *pal != 0;
-}
-
-void SpriteDrawer3::drawPixel(byte *dest, byte pixel) {
-	// WORKAROUND: This is slightly different then the original:
-	// 1) The original has bunches of black pixels appearing. This does index increments to avoid such pixels
-	// 2) It also prevents any pixels being drawn in the single initial frame until the palette is set
-	if (_hasPalette) {
-		byte level = (pixel & _mask) - _offset + (*dest & 0xf);
-
-		if (level >= 0x80) {
-			*dest &= 0xf0;
-		} else if (level <= 0xf) {
-			*dest = (*dest & 0xf0) | level;
-		} else {
-			*dest |= 0xf;
-		}
-
-		//
-		while (*dest < 0xff && !_palette[*dest * 3] && !_palette[*dest * 3 + 1] && !_palette[*dest * 3 + 2])
-			++ *dest;
-	}
-}
-
-/*------------------------------------------------------------------------*/
-
-static const byte DRAWER4_THRESHOLD[4] = { 4, 7, 10, 13 };
-
-SpriteDrawer4::SpriteDrawer4(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) {
-	_threshold = DRAWER4_THRESHOLD[index];
-}
-
-void SpriteDrawer4::drawPixel(byte *dest, byte pixel) {
-	if ((pixel & 0xf) >= _threshold)
-		*dest = pixel;
-}
-
-/*------------------------------------------------------------------------*/
-
-static const uint16 DRAWER5_THRESHOLD[4] = { 0x3333, 0x6666, 0x999A, 0xCCCD };
-
-SpriteDrawer5::SpriteDrawer5(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) {
-	_threshold = DRAWER5_THRESHOLD[index];
-	_random1 = g_vm->getRandomNumber(0xffff);
-	_random2 = g_vm->getRandomNumber(0xffff);
-}
-
-void SpriteDrawer5::drawPixel(byte *dest, byte pixel) {
-	bool flag = (_random1 & 0x8000) != 0;
-	_random1 = (int)((uint16)_random1 << 1) - _random2 - (flag ? 1 : 0);
-
-	rcr(_random2, flag);
-	rcr(_random2, flag);
-	_random2 ^= _random1;
-
-	if (_random2 > _threshold)
-		*dest = pixel;
-}
-
-/*------------------------------------------------------------------------*/
-
-static const byte DRAWER6_MASK[16] = { 1, 2, 4, 8, 1, 3, 7, 15, 8, 12, 14, 15, 1, 2, 1, 2 };
-
-SpriteDrawer6::SpriteDrawer6(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) {
-	_mask = DRAWER6_MASK[index];
-}
-
-void SpriteDrawer6::drawPixel(byte *dest, byte pixel) {
-	*dest = pixel ^ _mask;
-}
-
 } // End of namespace Xeen
 } // End of namespace MM
diff --git a/engines/mm/xeen/sprites.h b/engines/mm/xeen/sprites.h
index 8a173d51c35..c426b6e937e 100644
--- a/engines/mm/xeen/sprites.h
+++ b/engines/mm/xeen/sprites.h
@@ -8,12 +8,12 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
  *
@@ -22,92 +22,42 @@
 #ifndef XEEN_SPRITES_H
 #define XEEN_SPRITES_H
 
-#include "common/scummsys.h"
-#include "common/array.h"
-#include "common/file.h"
-#include "graphics/surface.h"
-#include "mm/xeen/files.h"
+#include "mm/shared/xeen/sprites.h"
 #include "mm/shared/xeen/xsurface.h"
 
 namespace MM {
 namespace Xeen {
 
-using Shared::Xeen::XSurface;
-
-class XeenEngine;
-class Window;
+using namespace Shared::Xeen;
 
-enum {
-	SCALE_MASK = 0x7FFF, SCALE_ENLARGE = 0x8000
-};
-
-enum SpriteFlags {
-	SPRFLAG_MODE_MASK = 0xF00, SPRFLAG_DRAWER1 = 0x100, SPRFLAG_DRAWER2 = 0x200,
-	SPRFLAG_DRAWER3 = 0x300, SPRFLAG_DRAWER4 = 0x400, SPRFLAG_DRAWER5 = 0x500, SPRFLAG_DRAWER6 = 0x600,
-	SPRFLAG_DRAWER7 = 0x700, SPRFLAG_800 = 0x800, SPRFLAG_SCENE_CLIPPED = 0x2000,
-	SPRFLAG_BOTTOM_CLIPPED = 0x4000, SPRFLAG_HORIZ_FLIPPED = 0x8000, SPRFLAG_RESIZE = 0x10000
-};
-
-class SpriteResource {
+class SpriteResource : public MM::Shared::Xeen::SpriteResource {
 private:
-	struct IndexEntry {
-		uint16 _offset1, _offset2;
-	};
-	Common::Array<IndexEntry> _index;
-	size_t _filesize;
-	byte *_data;
-	Common::String _filename;
-	static int _clippedBottom;
-
-	/**
-	 * Load a sprite resource from a stream
-	 */
-	void load(Common::SeekableReadStream &f);
-
-	/**
-	 * Draw the sprite onto the given surface
-	 */
-	void draw(XSurface &dest, int frame, const Common::Point &destPos,
-		const Common::Rect &bounds, uint flags = 0, int scale = 0);
-
 	/**
 	 * Draw the sprite onto a given window
 	 */
 	void draw(int windowNum, int frame, const Common::Point &destPos,
 		const Common::Rect &bounds, uint flags = 0, int scale = 0);
 
-	/**
-	 * Deep copy assuming that the current instance is clean
-	 */
-	void copy(const SpriteResource &src);
 public:
-	SpriteResource();
-	SpriteResource(const Common::String &filename);
+	SpriteResource() : Shared::Xeen::SpriteResource() {}
+	SpriteResource(const Common::String &filename) :
+		Shared::Xeen::SpriteResource(filename) {}
+	SpriteResource(const SpriteResource &src) :
+		Shared::Xeen::SpriteResource(src) {}
 	SpriteResource(const Common::String &filename, int ccMode);
-	SpriteResource(const SpriteResource &src);
-
-	virtual ~SpriteResource();
-
-	/**
-	 * Copy operator for duplicating a sprite resource
-	 */
-	SpriteResource &operator=(const SpriteResource &src);
 
 	/**
 	 * Load a sprite resource from a given file
 	 */
-	void load(const Common::String &filename);
+	void load(const Common::String &filename) {
+		Shared::Xeen::SpriteResource::load(filename);
+	}
 
 	/**
 	 * Load a sprite resource from a given file and archive
 	 */
 	void load(const Common::String &filename, int ccMode);
 
-	/**
-	 * Clears the sprite resource
-	 */
-	void clear();
-
 	/**
 	 * Draw a sprite onto a surface
 	 * @param dest		Destination surface
@@ -117,8 +67,26 @@ public:
 	 * @param scale		Scale: 0=No scale, SCALE_ENLARGE=Enlarge it
 	 *					1..15   -> reduces the sprite: the higher, the smaller it'll be
 	 */
-	void draw(XSurface &dest, int frame, const Common::Point &destPos,
-		uint flags = 0, int scale = 0);
+	void draw(Shared::Xeen::XSurface &dest, int frame, const Common::Point &destPos,
+		uint flags = 0, int scale = 0) {
+		Shared::Xeen::SpriteResource::draw(dest, frame, destPos, flags, 0);
+	}
+
+	/**
+	 * Draw the sprite onto the given surface
+	 * @param dest		Destination surface
+	 * @param frame		Frame number
+	 */
+	void draw(Shared::Xeen::XSurface &dest, int frame) {
+		Shared::Xeen::SpriteResource::draw(dest, frame);
+	}
+
+	/**
+	 * Draw the sprite onto a given surface
+	 */
+	void draw(Graphics::ManagedSurface *dest, int frame, const Common::Point &destPos) {
+		Shared::Xeen::SpriteResource::draw(dest, frame, destPos);
+	}
 
 	/**
 	 * Draw a sprite onto a specific window
@@ -132,6 +100,13 @@ public:
 	void draw(Window &dest, int frame, const Common::Point &destPos,
 		uint flags = 0, int scale = 0);
 
+	/**
+	 * Draw the sprite onto the given window
+	 * @param windowIndex	Destination window number
+	 * @param frame			Frame number
+	 */
+	void draw(int windowIndex, int frame);
+
 	/**
 	 * Draw a sprite onto a given window
 	 * @param windowIndex	Destination window number
@@ -143,202 +118,9 @@ public:
 	 */
 	void draw(int windowIndex, int frame, const Common::Point &destPos,
 		uint flags = 0, int scale = 0);
-
-	/**
-	 * Draw the sprite onto the given surface
-	 * @param dest		Destination surface
-	 * @param frame		Frame number
-	 */
-	void draw(XSurface &dest, int frame);
-
-	/**
-	 * Draw the sprite onto the given window
-	 * @param windowIndex	Destination window number
-	 * @param frame			Frame number
-	 */
-	void draw(int windowIndex, int frame);
-
-	/**
-	 * Draw the sprite onto a given surface
-	 */
-	void draw(Graphics::ManagedSurface *dest, int frame, const Common::Point &destPos);
-
-	/**
-	 * Gets the size of a sprite
-	 */
-	Common::Point getFrameSize(int frame) const;
-
-	/**
-	 * Returns the number of frames the sprite resource has
-	 */
-	size_t size() const {
-		return _index.size();
-	}
-
-	/**
-	 * Returns true if the sprite resource is empty (ie. nothing is loaded)
-	 */
-	bool empty() const {
-		return _index.size() == 0;
-	}
-
-	/**
-	 * Set the bottom Y position where sprites are clipped if SPRFLAG_BOTTOM_CLIPPED
-	 * is applied
-	 */
-	static void setClippedBottom(int y) {
-		_clippedBottom = y;
-	}
-};
-
-/**
- * Basic sprite drawer
- */
-class SpriteDrawer {
-private:
-	byte *_data = nullptr;
-	size_t _filesize = 0;
-protected:
-	byte *_destTop = nullptr, *_destBottom = nullptr;
-	byte *_destLeft = nullptr, *_destRight = nullptr;
-	int _pitch = 0;
-private:
-	/**
-	 * Scale a co-ordinate value based on the passed scaling mask
-	 */
-	static uint getScaledVal(int xy, uint16 &scaleMask);
-protected:
-	/**
-	 * Roll carry right opcode emulation
-	 */
-	void rcr(uint16 &val, bool &cf);
-
-	/**
-	 * Output a pixel
-	 */
-	virtual void drawPixel(byte *dest, byte pixel);
-public:
-	/**
-	 * Constructor
-	 */
-	SpriteDrawer(byte *data, size_t filesize) : _data(data), _filesize(filesize) {
-	}
-
-	/**
-	 * Destructor
-	 */
-	virtual ~SpriteDrawer() {
-	}
-
-	/**
-	 * Draw a sprite frame based on a passed offset into the data stream
-	 */
-	void draw(XSurface &dest, uint16 offset, const Common::Point &pt,
-		const Common::Rect &clipRect, uint flags, int scale);
-};
-
-class SpriteDrawer1 : public SpriteDrawer {
-private:
-	byte _offset = 0, _mask = 0;
-protected:
-	/**
-	 * Output a pixel
-	 */
-	void drawPixel(byte *dest, byte pixel) override;
-public:
-	/**
-	 * Constructor
-	 */
-	SpriteDrawer1(byte *data, size_t filesize, int index);
-};
-
-/**
- * Scrambles up the sprite by drawing many of the pixels randomly
- * at a horizontal or vertical offset
- */
-class SpriteDrawer2 : public SpriteDrawer {
-private:
-	uint16 _mask1 = 0, _mask2 = 0;
-	uint16 _random1 = 0, _random2 = 0;
-private:
-	/**
-	 * Output a pixel
-	 */
-	void drawPixel(byte *dest, byte pixel) override;
-public:
-	/**
-	 * Constructor
-	 */
-	SpriteDrawer2(byte *data, size_t filesize, int index);
-};
-
-/**
- * Draws the sprite as faint ghostly, see-through.
- */
-class SpriteDrawer3 : public SpriteDrawer {
-private:
-	uint16 _offset = 0, _mask = 0;
-	byte _palette[256 * 3];
-	bool _hasPalette = false;
-private:
-	/**
-	 * Output a pixel
-	 */
-	void drawPixel(byte *dest, byte pixel) override;
-public:
-	/**
-	 * Constructor
-	 */
-	SpriteDrawer3(byte *data, size_t filesize, int index);
 };
 
-class SpriteDrawer4 : public SpriteDrawer {
-private:
-	byte _threshold = 0;
-protected:
-	/**
-	 * Output a pixel
-	 */
-	void drawPixel(byte *dest, byte pixel) override;
-public:
-	/**
-	 * Constructor
-	 */
-	SpriteDrawer4(byte *data, size_t filesize, int index);
-};
-
-/**
- * Draws a sprite with a fuzziness effect where only some pixels of the sprite are randomly drawn
- */
-class SpriteDrawer5 : public SpriteDrawer {
-private:
-	uint16 _threshold = 0, _random1 = 0, _random2 = 0;
-protected:
-	/**
-	 * Output a pixel
-	 */
-	void drawPixel(byte *dest, byte pixel) override;
-public:
-	/**
-	 * Constructor
-	 */
-	SpriteDrawer5(byte *data, size_t filesize, int index);
-};
-
-class SpriteDrawer6 : public SpriteDrawer {
-private:
-	byte _mask = 0;
-protected:
-	/**
-	 * Output a pixel
-	 */
-	void drawPixel(byte *dest, byte pixel) override;
-public:
-	/**
-	 * Constructor
-	 */
-	SpriteDrawer6(byte *data, size_t filesize, int index);
-};
+using namespace MM::Shared;
 
 } // End of namespace Xeen
 } // End of namespace MM




More information about the Scummvm-git-logs mailing list