[Scummvm-git-logs] scummvm schr -> a70a7ec5a0d1427bcc286d9d52bb2f1620ff4b67

athrxx noreply at scummvm.org
Thu Jul 14 13:33:26 UTC 2022


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

Summary:
531351c239 SNATCHER: new engine initial commit
1e2e3afded GRAPHICS: SegaCD rendering code - refactor and move to Common
6d90e0f57d SNATCHER: start work on graphics code
a8e31cbd25 SNATCHER: cnt
d6026ab1e4 KYRA: (EOB II/PC98) - add detection entry
9d60e444a6 KYRA: (EOB II/PC98) - add static resources
cc0b6bad94 KYRA: (EOB II/PC98) - fix startup
2f1190a473 KYRA: (EOB II/PC98) - fix intro and main menu text
13573911f5 KYRA: (EOB II/PC98) - adapt file formats
2d38105396 KYRA: (EOB II/PC98) - font/layout fixes
0aafa70a5c KYRA: (EOB II/PC98) - more work on the text display
ffa5a4b8fb KYRA: (EOBII) - improve intro scrolling
9ff6d2cf89 temp
ee96d7b6ee EOBIIWIP1
2cf4ce91f3 EOBIIWIPSTATRES
6c370164dc TTS: (Windows) - improve Windows text-to-speech
663ab6b012 tts
4bf96009da dfds
a70a7ec5a0 c


Commit: 531351c23932fc69cb9dc133dc40bb94ed7e9a91
    https://github.com/scummvm/scummvm/commit/531351c23932fc69cb9dc133dc40bb94ed7e9a91
Author: athrxx (athrxx at scummvm.org)
Date: 2022-07-14T14:59:09+02:00

Commit Message:
SNATCHER: new engine initial commit

(detection and other skeleton code, no actual game code)

Changed paths:
  A engines/snatcher/POTFILES
  A engines/snatcher/configure.engine
  A engines/snatcher/detection.cpp
  A engines/snatcher/detection.h
  A engines/snatcher/detection_tables.h
  A engines/snatcher/graphics.cpp
  A engines/snatcher/graphics.h
  A engines/snatcher/metaengine.cpp
  A engines/snatcher/module.mk
  A engines/snatcher/render.h
  A engines/snatcher/render_scd.cpp
  A engines/snatcher/resource.cpp
  A engines/snatcher/resource.h
  A engines/snatcher/snatcher.cpp
  A engines/snatcher/snatcher.h
  A engines/snatcher/sound.cpp
  A engines/snatcher/sound.h
  A engines/snatcher/sound_device.h
  A engines/snatcher/sound_device_null.cpp
  A engines/snatcher/sound_device_scd.cpp


diff --git a/engines/snatcher/POTFILES b/engines/snatcher/POTFILES
new file mode 100644
index 00000000000..1b090beb384
--- /dev/null
+++ b/engines/snatcher/POTFILES
@@ -0,0 +1,2 @@
+engines/snatcher/detection.cpp
+
diff --git a/engines/snatcher/configure.engine b/engines/snatcher/configure.engine
new file mode 100644
index 00000000000..ad1b48f45de
--- /dev/null
+++ b/engines/snatcher/configure.engine
@@ -0,0 +1,3 @@
+# This file is included from the main "configure" script
+# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
+add_engine snatcher "Snatcher" no
diff --git a/engines/snatcher/detection.cpp b/engines/snatcher/detection.cpp
new file mode 100644
index 00000000000..a020f8cc0c3
--- /dev/null
+++ b/engines/snatcher/detection.cpp
@@ -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.
+ *
+ */
+
+#include "common/translation.h"
+#include "engines/advancedDetector.h"
+#include "base/plugins.h"
+#include "audio/mididrv.h"
+
+#include "snatcher/detection.h"
+#include "snatcher/detection_tables.h"
+
+namespace {
+
+const ADExtraGuiOptionsMap gameGuiOptions[] = {
+	AD_EXTRA_GUI_OPTIONS_TERMINATOR
+};
+
+} // End of anonymous namespace
+
+class SnatcherMetaEngineDetection : public AdvancedMetaEngineDetection {
+public:
+	SnatcherMetaEngineDetection() : AdvancedMetaEngineDetection(adGameDescs, sizeof(SnatcherGameDescription), gameList, gameGuiOptions) {
+		_md5Bytes = 1024 * 1024;
+		_maxScanDepth = 2;
+		_directoryGlobs = 0;
+	}
+
+	const char *getEngineId() const override {
+		return "snatcher";
+	}
+
+	const char *getName() const override {
+		return "snatcher";
+	}
+
+	const char *getOriginalCopyright() const override {
+		return "Snatcher (C) Konami";
+	}
+};
+
+REGISTER_PLUGIN_STATIC(SNATCHER_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, SnatcherMetaEngineDetection);
diff --git a/engines/snatcher/detection.h b/engines/snatcher/detection.h
new file mode 100644
index 00000000000..87355bfc830
--- /dev/null
+++ b/engines/snatcher/detection.h
@@ -0,0 +1,57 @@
+/* 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 SNATCHER_DETECTION_H
+#define SNATCHER_DETECTION_H
+
+#include "engines/advancedDetector.h"
+
+namespace Snatcher {
+
+enum {
+	GI_SNATCHER = 0
+};
+
+struct GameDescription {
+	byte gameID;
+	int soundOptions;
+	Common::Language lang;
+	Common::Platform platform;
+
+	// language overwrites of fan translations (only needed for multilingual games)
+	Common::Language fanLang;
+	Common::Language replacedLang;
+	
+};
+
+} // End of namespace Snatcher
+
+namespace {
+
+struct SnatcherGameDescription {
+	ADGameDescription ad_desc;
+	Snatcher::GameDescription ex_desc;
+};
+
+} // End of anonymous namespace
+
+#endif // SNATCHER_DETECTION_H
diff --git a/engines/snatcher/detection_tables.h b/engines/snatcher/detection_tables.h
new file mode 100644
index 00000000000..355d9ef4fdb
--- /dev/null
+++ b/engines/snatcher/detection_tables.h
@@ -0,0 +1,53 @@
+/* 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.
+ *
+ */
+
+namespace {
+
+#define FLAGS(id, sound) { id, sound, Common::UNK_LANG, Common::kPlatformUnknown, Common::UNK_LANG, Common::UNK_LANG }
+#define FLAGS_FAN(id, sound, fanLang, repLang) { id, sound, Common::UNK_LANG, Common::kPlatformUnknown, fanLang, repLang }
+
+#define SNATCHER_FLAGS(sound) FLAGS(Snatcher::GI_SNATCHER, sound)
+
+const SnatcherGameDescription adGameDescs[] = {
+	{
+		{
+			"snatcher",
+			0,
+			AD_ENTRY2s(	"DATA_B0.BIN",	"63170d11da5ebc3a66ecf0fc2d23e78a", -1,
+						"DATA_J0.BIN",	"4cd2c1d16b116c1cf1f38f8114c9c2ff", -1),
+			Common::EN_ANY,
+			Common::kPlatformSegaCD,
+			ADGF_TESTING,
+			GUIO2(GUIO_NOSPEECHVOLUME, GUIO_MIDISEGACD)
+		},
+		SNATCHER_FLAGS(MDT_SEGACD)
+	},
+
+	{ AD_TABLE_END_MARKER, FLAGS(0, MDT_NONE) }
+};
+
+const PlainGameDescriptor gameList[] = {
+	{ "snatcher", "Snatcher" },
+	{ 0, 0 }
+};
+
+} // End of anonymous namespace
diff --git a/engines/snatcher/graphics.cpp b/engines/snatcher/graphics.cpp
new file mode 100644
index 00000000000..b6feb62a4e1
--- /dev/null
+++ b/engines/snatcher/graphics.cpp
@@ -0,0 +1,37 @@
+/* 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 "snatcher/render.h"
+#include "snatcher/graphics.h"
+
+namespace Snatcher {
+
+GraphicsEngine::GraphicsEngine(Common::Platform platform) : _renderer(0) {
+	_renderer = Renderer::create(platform);
+	assert(_renderer);
+}
+
+GraphicsEngine::~GraphicsEngine() {
+	delete _renderer;
+}
+
+} // End of namespace Snatcher
diff --git a/engines/snatcher/graphics.h b/engines/snatcher/graphics.h
new file mode 100644
index 00000000000..1964a00b5a8
--- /dev/null
+++ b/engines/snatcher/graphics.h
@@ -0,0 +1,43 @@
+/* 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 SNATCHER_GRAPHICS_H
+#define SNATCHER_GRAPHICS_H
+
+#include "common/platform.h"
+
+namespace Snatcher {
+
+class Renderer;
+
+class GraphicsEngine {
+public:
+	GraphicsEngine(Common::Platform platform);
+	~GraphicsEngine();
+
+private:
+	Renderer *_renderer;
+};
+
+} // End of namespace Snatcher
+
+#endif // SNATCHER_GRAPHICS_H
diff --git a/engines/snatcher/metaengine.cpp b/engines/snatcher/metaengine.cpp
new file mode 100644
index 00000000000..a66e8be2a61
--- /dev/null
+++ b/engines/snatcher/metaengine.cpp
@@ -0,0 +1,190 @@
+/* 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 "snatcher/detection.h"
+#include "snatcher/snatcher.h"
+#include "common/config-manager.h"
+#include "common/system.h"
+#include "common/savefile.h"
+/*#include "common/translation.h"*/
+
+#include "engines/advancedDetector.h"
+
+#include "base/plugins.h"
+
+class SnatcherMetaEngine : public AdvancedMetaEngine {
+public:
+    const char *getName() const override {
+		return "snatcher";
+	}
+
+    bool hasFeature(MetaEngineFeature f) const override;
+	Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
+
+	SaveStateList listSaves(const char *target) const override;
+	int getMaximumSaveSlot() const override;
+	void removeSaveState(const char *target, int slot) const override;
+	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
+	virtual int getAutosaveSlot() const override { return 999; }
+
+	Common::KeymapArray initKeymaps(const char *target) const override;
+};
+
+bool SnatcherMetaEngine::hasFeature(MetaEngineFeature f) const {
+	return
+	    (f == kSupportsListSaves) ||
+	    (f == kSupportsLoadingDuringStartup) ||
+	    (f == kSupportsDeleteSave) ||
+	    (f == kSavesSupportMetaInfo) ||
+	    (f == kSavesSupportThumbnail) ||
+		(f == kSimpleSavesNames);
+}
+
+bool Snatcher::SnatcherEngine::hasFeature(EngineFeature f) const {
+	return
+	    (f == kSupportsReturnToLauncher) ||
+	    (f == kSupportsLoadingDuringRuntime) ||
+	    (f == kSupportsSavingDuringRuntime) ||
+	    (f == kSupportsSubtitleOptions);
+}
+
+Common::Error SnatcherMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+	const SnatcherGameDescription *gd = (const SnatcherGameDescription*)desc;
+	Snatcher::GameDescription dsc = gd->ex_desc;
+
+	Common::Platform platform = Common::parsePlatform(ConfMan.get("platform"));
+	if (platform != Common::kPlatformUnknown)
+		dsc.platform = platform;
+
+	if (dsc.lang == Common::UNK_LANG) {
+		Common::Language lang = Common::parseLanguage(ConfMan.get("language"));
+		if (lang != Common::UNK_LANG)
+			dsc.lang = lang;
+		else
+			dsc.lang = Common::EN_ANY;
+	}
+
+	switch (dsc.gameID) {
+	case Snatcher::GI_SNATCHER:
+		*engine = new Snatcher::SnatcherEngine(syst, dsc);
+		break;
+	default:
+		return Common::kUnsupportedGameidError;
+	}
+
+	return Common::kNoError;
+}
+
+SaveStateList SnatcherMetaEngine::listSaves(const char *target) const {
+	/*Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+	Snatcher::SnatcherEngine::SaveHeader header;
+	Common::String pattern = target;
+	pattern += ".###";
+
+	Common::StringArray filenames;
+	filenames = saveFileMan->listSavefiles(pattern);
+	*/
+	SaveStateList saveList;
+	/*for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
+		// Obtain the last 3 digits of the filename, since they correspond to the save slot
+		int slotNum = atoi(file->c_str() + file->size() - 3);
+
+		if (slotNum >= 0 && slotNum <= 999) {
+			Common::InSaveFile *in = saveFileMan->openForLoading(*file);
+			if (in) {
+				if (Kyra::KyraEngine_v1::readSaveHeader(in, header) == Kyra::KyraEngine_v1::kRSHENoError) {
+					// WORKAROUND: Old savegames are using 'German' as description for kyra3 restart game save (slot 0),
+					// since that looks odd we replace it by "New Game".
+					if (slotNum == 0 && header.gameID == Kyra::GI_KYRA3)
+						header.description = "New Game";
+
+					saveList.push_back(SaveStateDescriptor(slotNum, header.description));
+				}
+				delete in;
+			}
+		}
+	}*/
+
+	// Sort saves based on slot number.
+	Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
+	return saveList;
+}
+
+int SnatcherMetaEngine::getMaximumSaveSlot() const {
+	return 999;
+}
+
+void SnatcherMetaEngine::removeSaveState(const char *target, int slot) const {
+	//Common::String filename = Snatcher::SnatcherEngine::getSavegameFilename(target, slot);
+	//g_system->getSavefileManager()->removeSavefile(filename);
+}
+
+SaveStateDescriptor SnatcherMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+	/*Common::String filename = Snatcher::SnatcherEngine::getSavegameFilename(target, slot);
+	Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename);
+	const bool nonKyraGame = ConfMan.getDomain(target)->getVal("gameid").equalsIgnoreCase("lol") || ConfMan.getDomain(target)->getVal("gameid").equalsIgnoreCase("eob") || ConfMan.getDomain(target)->getVal("gameid").equalsIgnoreCase("eob2");
+
+	if (in) {
+		Kyra::KyraEngine_v1::SaveHeader header;
+		Kyra::KyraEngine_v1::ReadSaveHeaderError error;
+
+		error = Kyra::KyraEngine_v1::readSaveHeader(in, header, false);
+		delete in;
+
+		if (error == Kyra::KyraEngine_v1::kRSHENoError) {
+			SaveStateDescriptor desc(slot, header.description);
+
+			// Slot 0 is used for the 'restart game' save in all three Kyrandia games, thus
+			// we prevent it from being deleted.
+			desc.setDeletableFlag(slot != 0 || nonKyraGame);
+
+			// We don't allow quick saves (slot 990 till 998) to be overwritten.
+			// The same goes for the 'Autosave', which is slot 999. Slot 0 will also
+			// be protected in Kyra 1-3, since it's the 'restart game' save.
+			desc.setWriteProtectedFlag((slot == 0 && !nonKyraGame) || slot >= 990);
+			desc.setThumbnail(header.thumbnail);
+
+			return desc;
+		}
+	}*/
+
+	SaveStateDescriptor desc(slot, Common::String());
+
+	// We don't allow quick saves (slot 990 till 998) to be overwritten.
+	// The same goes for the 'Autosave', which is slot 999. Slot 0 will also
+	// be protected in Kyra 1-3, since it's the 'restart game' save.
+
+
+	//desc.setWriteProtectedFlag((slot == 0 && !nonKyraGame) || slot >= 990);
+
+	return desc;
+}
+
+Common::KeymapArray SnatcherMetaEngine::initKeymaps(const char *target) const {
+	return AdvancedMetaEngine::initKeymaps(target);
+}
+
+#if PLUGIN_ENABLED_DYNAMIC(SNATCHER)
+	REGISTER_PLUGIN_DYNAMIC(SNATCHER, PLUGIN_TYPE_ENGINE, SnatcherMetaEngine);
+#else
+	REGISTER_PLUGIN_STATIC(SNATCHER, PLUGIN_TYPE_ENGINE, SnatcherMetaEngine);
+#endif
diff --git a/engines/snatcher/module.mk b/engines/snatcher/module.mk
new file mode 100644
index 00000000000..fdce0b469e6
--- /dev/null
+++ b/engines/snatcher/module.mk
@@ -0,0 +1,22 @@
+MODULE := engines/snatcher
+
+MODULE_OBJS := \
+	graphics.o \
+	metaengine.o \
+	render_scd.o \
+	resource.o \
+	snatcher.o \
+	sound.o \
+	sound_device_null.o \
+	sound_device_scd.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_SNATCHER), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
+
+# Detection objects
+DETECT_OBJS += $(MODULE)/detection.o
diff --git a/engines/snatcher/render.h b/engines/snatcher/render.h
new file mode 100644
index 00000000000..9d72d402089
--- /dev/null
+++ b/engines/snatcher/render.h
@@ -0,0 +1,54 @@
+/* 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 SNATCHER_RENDER_H
+#define SNATCHER_RENDER_H
+
+#include "common/platform.h"
+
+namespace Snatcher {
+
+class Renderer {
+public:
+	virtual ~Renderer() {}
+
+protected:
+	Renderer() {}
+
+private:
+	static Renderer *createSegaRenderer();
+
+public:
+	static Renderer *create(Common::Platform platform) {
+		switch (platform) {
+		case Common::kPlatformSegaCD:
+			return createSegaRenderer();
+		default:
+			break;
+		};
+		return 0;
+	}
+};
+
+} // End of namespace Snatcher
+
+#endif // SNATCHER_RENDER_H
diff --git a/engines/snatcher/render_scd.cpp b/engines/snatcher/render_scd.cpp
new file mode 100644
index 00000000000..e392f73cc84
--- /dev/null
+++ b/engines/snatcher/render_scd.cpp
@@ -0,0 +1,38 @@
+/* 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 "snatcher/render.h"
+
+namespace Snatcher {
+
+class SegaRenderer : public Renderer {
+public:
+	SegaRenderer() : Renderer() {}
+	~SegaRenderer() override {}
+private:
+};
+
+Renderer *Renderer::createSegaRenderer() {
+	return new SegaRenderer();
+}
+
+} // End of namespace Snatcher
diff --git a/engines/snatcher/resource.cpp b/engines/snatcher/resource.cpp
new file mode 100644
index 00000000000..d34cfa1a767
--- /dev/null
+++ b/engines/snatcher/resource.cpp
@@ -0,0 +1,29 @@
+/* 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 "snatcher/resource.h"
+
+namespace Snatcher {
+
+///
+
+} // End of namespace Snatcher
diff --git a/engines/snatcher/resource.h b/engines/snatcher/resource.h
new file mode 100644
index 00000000000..88023a577bf
--- /dev/null
+++ b/engines/snatcher/resource.h
@@ -0,0 +1,32 @@
+/* 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 SNATCHER_RESOURCE_H
+#define SNATCHER_RESOURCE_H
+
+namespace Snatcher {
+
+///
+
+} // End of namespace Snatcher
+
+#endif // SNATCHER_RESOURCE_H
diff --git a/engines/snatcher/snatcher.cpp b/engines/snatcher/snatcher.cpp
new file mode 100644
index 00000000000..5aef53b9128
--- /dev/null
+++ b/engines/snatcher/snatcher.cpp
@@ -0,0 +1,84 @@
+/* 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 "snatcher/graphics.h"
+#include "snatcher/snatcher.h"
+#include "snatcher/sound.h"
+#include "common/error.h"
+
+namespace Snatcher {
+
+SnatcherEngine::SnatcherEngine(OSystem *system, GameDescription &dsc) : Engine(system), _game(dsc), _gfx(0), _snd(0) {
+
+}
+
+SnatcherEngine::~SnatcherEngine() {
+	delete _gfx;
+	delete _snd;
+}
+
+Common::Error SnatcherEngine::run() {
+	registerDefaultSettings();
+
+	if (!initResource())
+		return Common::Error(Common::kReadingFailed);
+
+	if (!initSound(_game.platform, _game.soundOptions))
+		return Common::Error(Common::kAudioDeviceInitFailed);
+
+	if (!initGraphics(_game.platform))
+		return Common::Error(Common::kUnknownError);
+
+	return Common::Error(start() ? Common::kNoError : Common::kUnknownError);
+}
+
+bool SnatcherEngine::initResource() {
+	return true;
+}
+
+bool SnatcherEngine::initGraphics(Common::Platform platform) {
+	_gfx = new GraphicsEngine(platform);
+	return _gfx;
+}
+
+bool SnatcherEngine::initSound(Common::Platform platform, int soundOptions) {
+	_snd = new SoundEngine(platform, soundOptions);
+	return _snd;
+}
+
+bool SnatcherEngine::start() {
+	return true;
+}
+
+void SnatcherEngine::registerDefaultSettings() {
+	//ConfMan.registerDefault("cdaudio", true);
+}
+
+void SnatcherEngine::syncSoundSettings() {
+
+}
+
+void SnatcherEngine::pauseEngineIntern(bool pause) {
+
+}
+
+} // End of namespace Snatcher
diff --git a/engines/snatcher/snatcher.h b/engines/snatcher/snatcher.h
new file mode 100644
index 00000000000..6660f71dbb8
--- /dev/null
+++ b/engines/snatcher/snatcher.h
@@ -0,0 +1,70 @@
+/* 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 SNATCHER_H
+#define SNATCHER_H
+
+#include "snatcher/detection.h"
+
+#include "engines/engine.h"
+
+class SnatcherMetaEngine;
+
+namespace Snatcher {
+
+class GraphicsEngine;
+class SoundEngine;
+
+class SnatcherEngine : public Engine {
+public:
+	SnatcherEngine(OSystem *system, GameDescription &dsc);
+	~SnatcherEngine() override;
+
+private:
+	// Startup
+	Common::Error run() override;
+	bool initResource();
+	bool initGraphics(Common::Platform platform);
+	bool initSound(Common::Platform platform, int soundOptions);
+	bool start();	
+
+	// ConfigManager sync
+	void registerDefaultSettings();
+	void syncSoundSettings() override;
+
+	// GMM
+	void pauseEngineIntern(bool pause) override;
+
+	// MetaEngine
+	bool hasFeature(EngineFeature f) const override;
+	GameDescription _game;
+
+	// Graphics
+	GraphicsEngine *_gfx;
+
+	// Sound
+	SoundEngine *_snd;
+};
+
+} // End of namespace Snatcher
+
+#endif // SNATCHER_H
diff --git a/engines/snatcher/sound.cpp b/engines/snatcher/sound.cpp
new file mode 100644
index 00000000000..70d4813a605
--- /dev/null
+++ b/engines/snatcher/sound.cpp
@@ -0,0 +1,37 @@
+/* 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 "snatcher/sound_device.h"
+#include "snatcher/sound.h"
+
+namespace Snatcher {
+
+SoundEngine::SoundEngine(Common::Platform platform, int soundOptions) : _dev(0) {
+	_dev = SoundDevice::create(platform, soundOptions);
+	assert(_dev);
+}
+
+SoundEngine::~SoundEngine() {
+	delete _dev;
+}
+
+} // End of namespace Snatcher
diff --git a/engines/snatcher/sound.h b/engines/snatcher/sound.h
new file mode 100644
index 00000000000..4a69aa693bc
--- /dev/null
+++ b/engines/snatcher/sound.h
@@ -0,0 +1,43 @@
+/* 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 SNATCHER_SOUND_H
+#define SNATCHER_SOUND_H
+
+#include "common/platform.h"
+
+namespace Snatcher {
+
+class SoundDevice;
+
+class SoundEngine {
+public:
+	SoundEngine(Common::Platform platform, int soundOptions);
+	~SoundEngine();
+
+private:
+	SoundDevice *_dev;
+};
+
+} // End of namespace Snatcher
+
+#endif // SNATCHER_SOUND_H
diff --git a/engines/snatcher/sound_device.h b/engines/snatcher/sound_device.h
new file mode 100644
index 00000000000..f3cf908eb5e
--- /dev/null
+++ b/engines/snatcher/sound_device.h
@@ -0,0 +1,63 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SNATCHER_SOUND_DEVICE_H
+#define SNATCHER_SOUND_DEVICE_H
+
+#include "audio//mididrv.h"
+#include "common/platform.h"
+
+namespace Snatcher {
+
+class SoundDevice {
+public:
+	virtual ~SoundDevice() {}
+
+protected:
+	SoundDevice() {}
+
+private:
+	static SoundDevice *createNullSoundDevice();
+	static SoundDevice *createSegaSoundDevice();
+
+public:
+	static SoundDevice *create(Common::Platform platform, int soundOptions) {
+		MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(soundOptions);
+		MusicType musicType = MidiDriver::getMusicType(dev);
+
+		if (musicType == MT_INVALID || musicType == MT_NULL)
+			return createNullSoundDevice();
+
+		switch (platform) {
+		case Common::kPlatformSegaCD:
+			return createSegaSoundDevice();
+		default:
+			break;
+		};
+
+		return 0;
+	}
+};
+
+} // End of namespace Snatcher
+
+#endif // SNATCHER_SOUND_DEVICE_H
diff --git a/engines/snatcher/sound_device_null.cpp b/engines/snatcher/sound_device_null.cpp
new file mode 100644
index 00000000000..87da19208f3
--- /dev/null
+++ b/engines/snatcher/sound_device_null.cpp
@@ -0,0 +1,38 @@
+/* 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 "snatcher/sound_device.h"
+
+namespace Snatcher {
+
+class NullSoundDevice : public SoundDevice {
+public:
+	NullSoundDevice() : SoundDevice() {}
+	~NullSoundDevice() override {}
+private:
+};
+
+SoundDevice *SoundDevice::createNullSoundDevice() {
+	return new NullSoundDevice();
+}
+
+} // End of namespace Snatcher
diff --git a/engines/snatcher/sound_device_scd.cpp b/engines/snatcher/sound_device_scd.cpp
new file mode 100644
index 00000000000..5ec812e9d4f
--- /dev/null
+++ b/engines/snatcher/sound_device_scd.cpp
@@ -0,0 +1,38 @@
+/* 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 "snatcher/sound_device.h"
+
+namespace Snatcher {
+
+class SegaSoundDevice : public SoundDevice {
+public:
+	SegaSoundDevice() : SoundDevice() {}
+	~SegaSoundDevice() override {}
+private:
+};
+
+SoundDevice *SoundDevice::createSegaSoundDevice() {
+	return new SegaSoundDevice();
+}
+
+} // End of namespace Snatcher


Commit: 1e2e3afded9849237abaf7ec88ce606e2aaebe1e
    https://github.com/scummvm/scummvm/commit/1e2e3afded9849237abaf7ec88ce606e2aaebe1e
Author: athrxx (athrxx at scummvm.org)
Date: 2022-07-14T14:59:10+02:00

Commit Message:
GRAPHICS: SegaCD rendering code - refactor and move to Common

(make it available for other engines)

Changed paths:
  A graphics/segagfx.cpp
  A graphics/segagfx.h
    engines/kyra/engine/chargen.cpp
    engines/kyra/engine/eob.cpp
    engines/kyra/graphics/screen_eob.h
    engines/kyra/graphics/screen_eob_segacd.cpp
    engines/kyra/graphics/screen_eob_segacd.h
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/gui/gui_eob_segacd.cpp
    engines/kyra/sequence/seqplayer_eob_segacd.cpp
    engines/kyra/sequence/seqplayer_eob_segacd.h
    engines/kyra/sequence/sequences_eob.cpp
    engines/kyra/text/text_eob_segacd.cpp
    engines/kyra/text/text_eob_segacd.h
    engines/snatcher/render_scd.cpp
    graphics/module.mk


diff --git a/engines/kyra/engine/chargen.cpp b/engines/kyra/engine/chargen.cpp
index 130e01cacfc..28416673ce4 100644
--- a/engines/kyra/engine/chargen.cpp
+++ b/engines/kyra/engine/chargen.cpp
@@ -289,7 +289,7 @@ void CharacterGenerator::init(bool defaultParty) {
 		_screen->sega_getRenderer()->loadToVRAM(&cgb[0x21E0], 0x1400, 0x8220);
 		for (int i = 0; i < 10; i++)
 			_screen->sega_getRenderer()->fillRectWithTiles(1, i << 2, 0, 4, 4, 0x4411 + (i << 4), true);
-		_screen->sega_getRenderer()->render(2);
+		_screen->sega_getRenderer()->renderToPage(2);
 		_screen->_curPage = 2;
 		_chargenMagicShapes = new uint8*[10];
 		for (int i = 0; i < 10; i++)
@@ -303,7 +303,7 @@ void CharacterGenerator::init(bool defaultParty) {
 		_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 40, 28, 0);
 		_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 0);
 		_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 26, 1, true);
-		_screen->sega_getRenderer()->render(0);
+		_screen->sega_getRenderer()->renderToPage(0);
 
 		for (int i = 0; i < 4; ++i)
 			_screen->copyRegion(_chargenBoxX[i], _chargenBoxY[i] + 1, i << 5, 128, 32, 32, 0, 2, Screen::CR_NO_P_CHECK);
@@ -510,7 +510,7 @@ void CharacterGenerator::checkForCompleteParty() {
 
 	if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
 		_screen->setFontStyles(_screen->_currentFont, cs);
-		_screen->sega_getRenderer()->render(0, 18, 8, 20, 16);
+		_screen->sega_getRenderer()->renderToPage(0, 18, 8, 20, 16);
 	}
 
 	_screen->updateScreen();
@@ -529,13 +529,13 @@ void CharacterGenerator::drawButton(int index, int buttonState, int pageNum) {
 				// since it seems weird to have the button still there, even when it is dysfunctional.
 				_screen->sega_getRenderer()->fillRectWithTiles(0, 3, 23, 11, 1, 0x39C, true);
 				_screen->sega_getRenderer()->fillRectWithTiles(0, 3, 24, 11, 1, 0x3C4, true);
-				_screen->sega_getRenderer()->render(0, 3, 23, 11, 2);
+				_screen->sega_getRenderer()->renderToPage(0, 3, 23, 11, 2);
 			}
 			return;
 		}
 		const uint8 *bt = &_chargenSegaButtonCoords[index * 5];
 		_screen->sega_getRenderer()->fillRectWithTiles(0, bt[0], bt[1], bt[2], bt[3], (index > 9 ? 0x24BC : 0x2411) + bt[4] + (buttonState ? (bt[2] * bt[3]) : 0), true);
-		_screen->sega_getRenderer()->render(0, bt[0], bt[1], bt[2], bt[3]);
+		_screen->sega_getRenderer()->renderToPage(0, bt[0], bt[1], bt[2], bt[3]);
 		_screen->updateScreen();
 		return;
 	}
@@ -610,7 +610,7 @@ int CharacterGenerator::viewDeleteCharacter() {
 					_characters[_activeBox].faceShape = 0;
 					if (_vm->_flags.platform == Common::kPlatformSegaCD) {
 						_screen->sega_getRenderer()->memsetVRAM((((_chargenBoxY[_activeBox] + 41) >> 3) * 40 + (_chargenBoxX[_activeBox] >> 3)) << 5, 0, 224);
-						_screen->sega_getRenderer()->render(0, (_chargenBoxX[_activeBox] >> 3) - 1, (_chargenBoxY[_activeBox] + 41) >> 3, 7, 1);
+						_screen->sega_getRenderer()->renderToPage(0, (_chargenBoxX[_activeBox] >> 3) - 1, (_chargenBoxY[_activeBox] + 41) >> 3, 7, 1);
 					} else {
 						processNameInput(_activeBox, _vm->guiSettings()->colors.guiColorBlack);
 					}
@@ -677,7 +677,7 @@ void CharacterGenerator::createPartyMember() {
 			if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
 				_screen->sega_loadTextBackground(_wndBackgrnd, 10240);
 				_vm->_txt->printShadedText(_chargenStrings2[11], 0, 0);
-				_screen->sega_getRenderer()->render(0, 18, 8, 20, 2);
+				_screen->sega_getRenderer()->renderToPage(0, 18, 8, 20, 2);
 				if (!_vm->shouldQuit())
 					_vm->_gui->getTextInput(_characters[_activeBox].name, (_chargenBoxX[_activeBox] >> 3) - 1, _chargenBoxY[_activeBox] + 41, 7, 0xFF, 0x00, 0xFF);
 			} else {
@@ -706,14 +706,14 @@ int CharacterGenerator::raceSexMenu() {
 
 	_vm->_gui->simpleMenu_setup(1, 0, _chargenRaceSexStrings, -1, 0, 0, _menuColor1, _menuColor2, _menuColor3);
 	if (_vm->_flags.platform == Common::kPlatformSegaCD)
-		_screen->sega_getRenderer()->render(0, 18, 8, 20, 16);
+		_screen->sega_getRenderer()->renderToPage(0, 18, 8, 20, 16);
 	_screen->updateScreen();
 	int16 res = -1;
 
 	while (res == -1 && !_vm->shouldQuit()) {
 		res = _vm->_gui->simpleMenu_process(1, _chargenRaceSexStrings, 0, -1, 0);
 		if (_vm->_flags.platform == Common::kPlatformSegaCD)
-			_screen->sega_getRenderer()->render(0, 18, 8, 20, 16);
+			_screen->sega_getRenderer()->renderToPage(0, 18, 8, 20, 16);
 		_screen->updateScreen();
 		updateMagicShapes();
 	}
@@ -746,7 +746,7 @@ int CharacterGenerator::classMenu(int raceSex) {
 	itemsMask &= _classMenuMasks[raceSex / 2];
 	_vm->_gui->simpleMenu_setup(2, 15, _chargenClassStrings, itemsMask, 0, 0, _menuColor1, _menuColor2, _menuColor3);
 	if (_vm->_flags.platform == Common::kPlatformSegaCD)
-		_screen->sega_getRenderer()->render(0, 18, 8, 20, 16);
+		_screen->sega_getRenderer()->renderToPage(0, 18, 8, 20, 16);
 	_screen->updateScreen();
 
 	_vm->_mouseX = _vm->_mouseY = 0;
@@ -767,7 +767,7 @@ int CharacterGenerator::classMenu(int raceSex) {
 		} else {
 			res = _vm->_gui->simpleMenu_process(2, _chargenClassStrings, 0, itemsMask, 0);
 			if (_vm->_flags.platform == Common::kPlatformSegaCD)
-				_screen->sega_getRenderer()->render(0, 18, 8, 20, 16);
+				_screen->sega_getRenderer()->renderToPage(0, 18, 8, 20, 16);
 			_screen->updateScreen();
 		}
 	}
@@ -806,7 +806,7 @@ int CharacterGenerator::alignmentMenu(int cClass) {
 	itemsMask &= _alignmentMenuMasks[cClass];
 	_vm->_gui->simpleMenu_setup(3, 9, _chargenAlignmentStrings, itemsMask, 0, 0, _menuColor1, _menuColor2, _menuColor3);
 	if (_vm->_flags.platform == Common::kPlatformSegaCD)
-		_screen->sega_getRenderer()->render(0, 18, 8, 20, 16);
+		_screen->sega_getRenderer()->renderToPage(0, 18, 8, 20, 16);
 	_screen->updateScreen();
 
 	_vm->_mouseX = _vm->_mouseY = 0;
@@ -827,7 +827,7 @@ int CharacterGenerator::alignmentMenu(int cClass) {
 		} else {
 			res = _vm->_gui->simpleMenu_process(3, _chargenAlignmentStrings, 0, itemsMask, 0);
 			if (_vm->_flags.platform == Common::kPlatformSegaCD)
-				_screen->sega_getRenderer()->render(0, 18, 9, 20, 16);
+				_screen->sega_getRenderer()->renderToPage(0, 18, 9, 20, 16);
 			_screen->updateScreen();
 		}
 	}
@@ -1188,7 +1188,7 @@ void CharacterGenerator::printStats(int index, int mode) {
 	}
 
 	if (_vm->_flags.platform == Common::kPlatformSegaCD) {
-		_screen->sega_getRenderer()->render(0, 18, 8, 20, 16);
+		_screen->sega_getRenderer()->renderToPage(0, 18, 8, 20, 16);
 		if (mode != 4)
 			_screen->drawShape(0, c->faceShape, 208, 66, 0);
 	} else
@@ -1238,7 +1238,7 @@ int CharacterGenerator::modifyStat(int index, int8 *stat1, int8 *stat2) {
 	if (_vm->_flags.platform == Common::kPlatformSegaCD) {
 		Common::String statStr = index ? Common::String::format("%02d", *s1) : _vm->getCharStrength(*s1, *s2, true);
 		_vm->_txt->printShadedText(statStr.c_str(), b->x - 112, b->y - 64, 0x55);
-		_screen->sega_getRenderer()->render(0, (b->x + 32) >> 3, b->y >> 3, 5, 1);
+		_screen->sega_getRenderer()->renderToPage(0, (b->x + 32) >> 3, b->y >> 3, 5, 1);
 	} else {
 		Common::String statStr = index ? Common::String::format("%d", *s1) : _vm->getCharStrength(*s1, *s2);
 		_screen->copyRegion(b->x - 112, b->y - 64, b->x + 32, b->y, 40, b->height, 2, 0, Screen::CR_NO_P_CHECK);
@@ -1338,7 +1338,7 @@ int CharacterGenerator::modifyStat(int index, int8 *stat1, int8 *stat2) {
 			Common::String statStr = index ? Common::String::format("%02d", *s1) : _vm->getCharStrength(*s1, *s2, true);
 			printStats(_activeBox, 3);
 			_vm->_txt->printShadedText(statStr.c_str(), b->x - 112, b->y - 64, 0x55);
-			_screen->sega_getRenderer()->render(0, (b->x + 32) >> 3, b->y >> 3, 5, 1);
+			_screen->sega_getRenderer()->renderToPage(0, (b->x + 32) >> 3, b->y >> 3, 5, 1);
 		} else {
 			Common::String statStr = index ? Common::String::format("%d", *s1) : _vm->getCharStrength(*s1, *s2);
 			_screen->copyRegion(b->x - 112, b->y - 64, b->x + 32, b->y, 40, b->height, 2, 0, Screen::CR_NO_P_CHECK);
diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index fc3cd2b0904..9006af24d64 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -184,7 +184,7 @@ Common::Error EoBEngine::init() {
 	const uint8 **shapeBuffer = new const uint8 *[numSprites]; \
 	_screen->sega_encodeShapesFromSprites(shapeBuffer, in + (resOffset), numSprites, spriteWidth, spriteHeight, 3, false); \
 	releaseShpArr(shapeBuffer, numSprites); \
-	_screen->sega_getRenderer()->render(Screen_EoB::kSegaInitShapesPage, -1, -1, -1, -1, true); \
+	_screen->sega_getRenderer()->renderToPage(Screen_EoB::kSegaInitShapesPage, -1, -1, -1, -1, true); \
 	_screen->sega_getAnimator()->clearSprites(); \
 	int cp = _screen->setCurPage(Screen_EoB::kSegaInitShapesPage); \
 	singleShape = _screen->encodeShape(0, 0, numSprites  * (spriteWidth >> 3), spriteHeight); \
@@ -261,7 +261,7 @@ void EoBEngine::loadItemsAndDecorationsShapes() {
 	for (int i = 0; i < 4; ++i)
 		_screen->sega_getRenderer()->loadToVRAM(_redGridTile, 8, 0x52A0 + i * 8);
 	_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 4, 4, 4, 0x6295);
-	_screen->sega_getRenderer()->render(Screen_EoB::kSegaInitShapesPage, 0, 4, 4, 4);
+	_screen->sega_getRenderer()->renderToPage(Screen_EoB::kSegaInitShapesPage, 0, 4, 4, 4);
 	_screen->drawShape(Screen_EoB::kSegaInitShapesPage, _weaponSlotShapes[1], 0, 0, 0);
 	_screen->drawShape(Screen_EoB::kSegaInitShapesPage, _weaponSlotShapes[1], 0, 16, 0);
 	_weaponSlotGrid = _screen->encodeShape(0, 0, 4, 16);
@@ -1038,7 +1038,7 @@ void EoBEngine::displayParchment(int id) {
 		gui_drawCharPortraitWithStats(i);
 	}
 
-	SegaRenderer *r = _screen->sega_getRenderer();
+	SCDRenderer *r = _screen->sega_getRenderer();
 	r->fillRectWithTiles(0, 0, 0, 40, 28, 0);
 	r->fillRectWithTiles(1, 0, 0, 40, 28, 0);
 	_screen->sega_getAnimator()->clearSprites();
@@ -1056,7 +1056,7 @@ void EoBEngine::displayParchment(int id) {
 		_txt->printShadedText(strings[curPage++], 16, 16, 0x22, 0, 208, 216, 16, false);
 		_screen->sega_loadTextBufferToVRAM(0, 0x20, 22464);
 		r->fillRectWithTiles(0, 7, 0, 26, 27, 0x4001, true);
-		r->render(0);
+		r->renderToPage(0);
 
 		_screen->sega_fadeToNeutral(1);
 
@@ -1127,7 +1127,7 @@ bool EoBEngine::checkPartyStatusExtra() {
 		snd_stopSound();
 
 		Common::SeekableReadStreamEndian *in = _res->createEndianAwareReadStream("GO");
-		SegaRenderer *r = _screen->sega_getRenderer();
+		SCDRenderer *r = _screen->sega_getRenderer();
 		r->loadStreamToVRAM(in, 0x20);
 		delete in;
 
@@ -1151,7 +1151,7 @@ bool EoBEngine::checkPartyStatusExtra() {
 		_screen->setFontStyles(_screen->_currentFont, cs);
 		_screen->sega_loadTextBufferToVRAM(0, 0xA3A0, 7296);
 		r->fillRectWithTiles(0, 1, 20, 38, 6, 0x651D, true);
-		r->render(0);
+		r->renderToPage(0);
 
 		snd_playSoundEffect(0x5086);
 
@@ -1173,7 +1173,7 @@ bool EoBEngine::checkPartyStatusExtra() {
 		for (int i = 0; i < 7; ++i)
 			_screen->sega_getAnimator()->initSprite(i, 104 + (i << 4), 80, 0x4501 + (i << 2), 5);
 		_screen->sega_getAnimator()->update();
-		r->render(0);
+		r->renderToPage(0);
 
 		_screen->sega_paletteOps(2, 0, 5);
 
diff --git a/engines/kyra/graphics/screen_eob.h b/engines/kyra/graphics/screen_eob.h
index 00c727d019c..3292558079b 100644
--- a/engines/kyra/graphics/screen_eob.h
+++ b/engines/kyra/graphics/screen_eob.h
@@ -29,10 +29,10 @@
 namespace Kyra {
 
 class EoBCoreEngine;
-class SegaRenderer;
+class SCDRenderer;
 class SegaAnimator;
 class Screen_EoB : public Screen {
-friend class SegaRenderer;
+friend class SCDRenderer;
 public:
 	// The purpose of this enum is to keep better track of which page is used
 	// when and for which purpose. We use the pages for more backup operations
@@ -153,7 +153,7 @@ public:
 	uint8 *sega_convertShape(const uint8 *src, int w, int h, int pal, int hOffs = 0);
 	void sega_encodeShapesFromSprites(const uint8 **dst, const uint8 *src, int numShapes, int w, int h, int pal, bool removeSprites = true);
 
-	SegaRenderer *sega_getRenderer() const { return _segaRenderer; }
+	SCDRenderer *sega_getRenderer() const { return _segaRenderer; }
 	SegaAnimator *sega_getAnimator() const { return _segaAnimator; }
 
 private:
@@ -213,7 +213,7 @@ private:
 
 	PaletteFader *_palFaders;
 	bool _specialColorReplace;
-	SegaRenderer *_segaRenderer;
+	SCDRenderer *_segaRenderer;
 	SegaAnimator *_segaAnimator;
 	uint16 _segaCurPalette[64];
 	uint16 *_segaCustomPalettes;
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index 0a85a757d8f..486594dce78 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -30,13 +30,13 @@
 namespace Kyra {
 
 void Screen_EoB::sega_initGraphics() {
-	_segaRenderer = new SegaRenderer(this);
+	_segaRenderer = new SCDRenderer(this);
 	_segaRenderer->setResolution(320, 224);
-	_segaRenderer->setPlaneTableLocation(SegaRenderer::kPlaneA, 0xC000);
-	_segaRenderer->setPlaneTableLocation(SegaRenderer::kPlaneB, 0xE000);
-	_segaRenderer->setPlaneTableLocation(SegaRenderer::kWindowPlane, 0xF000);
+	_segaRenderer->setPlaneTableLocation(SCDRenderer::kPlaneA, 0xC000);
+	_segaRenderer->setPlaneTableLocation(SCDRenderer::kPlaneB, 0xE000);
+	_segaRenderer->setPlaneTableLocation(SCDRenderer::kWindowPlane, 0xF000);
 	_segaRenderer->setupPlaneAB(512, 512);
-	_segaRenderer->setupWindowPlane(0, 0, SegaRenderer::kWinToLeft, SegaRenderer::kWinToTop);
+	_segaRenderer->setupWindowPlane(0, 0, SCDRenderer::kWinToLeft, SCDRenderer::kWinToTop);
 	_segaRenderer->setHScrollTableLocation(0xD800);
 	_segaRenderer->setSpriteTableLocation(0xDC00);
 	_segaAnimator = new SegaAnimator(_segaRenderer);
@@ -343,7 +343,7 @@ void Screen_EoB::sega_encodeShapesFromSprites(const uint8 **dst, const uint8 *sr
 		}
 
 		_segaAnimator->update();
-		_segaRenderer->render(Screen_EoB::kSegaInitShapesPage, -1, -1, -1, -1, true);
+		_segaRenderer->renderToPage(Screen_EoB::kSegaInitShapesPage, -1, -1, -1, -1, true);
 
 		for (int i = l; i < s; ++i)
 			dst[i] = encodeShape((((i % 80) * w) % SCREEN_W) >> 3, ((i % 80) / (SCREEN_W / w)) * h, w >> 3, h);
@@ -360,148 +360,14 @@ void Screen_EoB::sega_encodeShapesFromSprites(const uint8 **dst, const uint8 *sr
 	setCurPage(cp);
 }
 
-#if SEGA_PERFORMANCE
-#define mRenderLineFragment(hFlip, oddStart, oddEnd, useMask, dst, mask, src, start, end, pal) \
-{ \
-	int rlfOffs = 0; \
-	if (hFlip) \
-		rlfOffs |= 4; \
-	if (oddStart) \
-		rlfOffs |= 2; \
-	if (oddEnd) \
-		rlfOffs |= 1; \
-	if (useMask) \
-		(this->*_renderLineFragmentM[rlfOffs])(dst, mask, src, start, end, pal); \
-	else \
-		(this->*_renderLineFragmentD[rlfOffs])(dst, src, start, end, pal); \
-}
-#else
-#define mRenderLineFragment(hFlip, oddStart, oddEnd, useMask, dst, mask, src, start, end, pal) \
-{ \
-	if (hFlip) \
-		renderLineFragment<true>(dst, mask, src, start, end, pal); \
-	else \
-		renderLineFragment<false>(dst, mask, src, start, end, pal); \
-}
-#endif
-
-SegaRenderer::SegaRenderer(Screen_EoB *screen) : _screen(screen), _prioChainStart(0), _prioChainEnd(0), _pitch(64), _hScrollMode(0), _hScrollTable(0), _vScrollMode(0), _spriteTable(0), _numSpritesMax(0), _spriteMask(0)
-#if SEGA_PERFORMANCE
-, _renderLineFragmentD(0), _renderLineFragmentM(0)
-#endif
-{
-	_vram = new uint8[0x10000]();
-	assert(_vram);
-	_vsram = new uint16[40]();
-	assert(_vsram);
-
-#if SEGA_PERFORMANCE
-	static const SegaRenderer::renderFuncD funcD[8] = {
-		&SegaRenderer::renderLineFragmentD<false, false, false>,
-		&SegaRenderer::renderLineFragmentD<false, false, true>,
-		&SegaRenderer::renderLineFragmentD<false, true, false>,
-		&SegaRenderer::renderLineFragmentD<false, true, true>,
-		&SegaRenderer::renderLineFragmentD<true, false, false>,
-		&SegaRenderer::renderLineFragmentD<true, false, true>,
-		&SegaRenderer::renderLineFragmentD<true, true, false>,
-		&SegaRenderer::renderLineFragmentD<true, true, true>
-	};
-
-	static const SegaRenderer::renderFuncM funcM[8] = {
-		&SegaRenderer::renderLineFragmentM<false, false, false>,
-		&SegaRenderer::renderLineFragmentM<false, false, true>,
-		&SegaRenderer::renderLineFragmentM<false, true, false>,
-		&SegaRenderer::renderLineFragmentM<false, true, true>,
-		&SegaRenderer::renderLineFragmentM<true, false, false>,
-		&SegaRenderer::renderLineFragmentM<true, false, true>,
-		&SegaRenderer::renderLineFragmentM<true, true, false>,
-		&SegaRenderer::renderLineFragmentM<true, true, true>
-	};
-
-	_renderLineFragmentD = funcD;
-	_renderLineFragmentM = funcM;
-#endif
-
+SCDRenderer::SCDRenderer(Screen_EoB *screen) : Graphics::SegaRenderer(), _screen(screen) {
 	setResolution(320, 224);
 }
 
-SegaRenderer::~SegaRenderer() {
-	delete[] _vram;
-	delete[] _vsram;
-	delete[] _spriteMask;
-}
-
-void SegaRenderer::setResolution(int w, int h) {
-	assert(w == 320 || w == 256);
-	assert(h == 224 || h == 240);
-
-	_screenW = w;
-	_screenH = h;
-	_blocksW = w >> 3;
-	_blocksH = h >> 3;
-	_numSpritesMax = w >> 2;
-
-	delete[] _spriteMask;
-	_spriteMask = new uint8[w * h]();
-	assert(_spriteMask);
-}
-
-void SegaRenderer::setPlaneTableLocation(int plane, uint16 addr) {
-	assert(plane >= kPlaneA && plane <= kWindowPlane);
-	_planes[plane].nameTable = (uint16*)(&_vram[addr]);
+SCDRenderer::~SCDRenderer() {
 }
 
-void SegaRenderer::setupPlaneAB(int pixelWidth, int pixelHeigth) {
-	for (int i = 0; i < 2; ++i) {
-		if (pixelWidth != -1)
-			_planes[i].w = pixelWidth >> 3;
-		if (pixelHeigth != -1)
-			_planes[i].h = pixelHeigth >> 3;
-		_planes[i].mod = _planes[i].h;
-		_planes[i].nameTableSize = _planes[i].w * _planes[i].h;
-	}
-}
-
-void SegaRenderer::setupWindowPlane(int blockX, int blockY, int horizontalMode, int verticalMode) {
-	if (blockX != -1)
-		_planes[kWindowPlane].blockX = horizontalMode ? blockX : 0;
-	if (blockY != -1)
-		_planes[kWindowPlane].blockY = verticalMode ? blockY : 0;
-	_planes[kWindowPlane].w = horizontalMode ? _blocksW - blockX : blockX;
-	_planes[kWindowPlane].h = verticalMode ? _blocksH - blockY : blockY;
-	_planes[kWindowPlane].mod = _planes[kWindowPlane].blockY + _planes[kWindowPlane].h;
-	_planes[kWindowPlane].nameTableSize = _planes[kWindowPlane].w * _planes[kWindowPlane].h;
-}
-
-void SegaRenderer::setHScrollTableLocation(int addr) {
-	assert(addr <= 0xFFFF);
-	_hScrollTable = (uint16*)(&_vram[addr]);
-}
-
-void SegaRenderer::setSpriteTableLocation(int addr) {
-	assert(addr <= 0xFFFF);
-	_spriteTable = (uint16*)(&_vram[addr]);
-}
-
-void SegaRenderer::setPitch(int pitch) {
-	_pitch = pitch;
-}
-
-void SegaRenderer::setHScrollMode(int mode) {
-	_hScrollMode = mode;
-}
-
-void SegaRenderer::setVScrollMode(int mode) {
-	_vScrollMode = mode;
-}
-
-void SegaRenderer::loadToVRAM(const void *data, uint16 dataSize, uint16 addr) {
-	assert(data);
-	assert(addr + dataSize <= 0x10000);
-	memcpy(_vram + addr, data, dataSize);
-}
-
-void SegaRenderer::loadStreamToVRAM(Common::SeekableReadStream *in, uint16 addr, bool compressedData) {
+void SCDRenderer::loadStreamToVRAM(Common::SeekableReadStream *in, uint16 addr, bool compressedData) {
 	assert(in);
 	uint8 *dst = _vram + addr;
 
@@ -520,12 +386,7 @@ void SegaRenderer::loadStreamToVRAM(Common::SeekableReadStream *in, uint16 addr,
 	}
 }
 
-void SegaRenderer::memsetVRAM(int addr, uint8 val, int len) {
-	assert(addr + len <= 0x10000);
-	memset(_vram + addr, val, len);
-}
-
-void SegaRenderer::fillRectWithTiles(int vramArea, int x, int y, int w, int h, uint16 nameTblEntry, bool incr, bool topToBottom, const uint16 *patternTable) {
+void SCDRenderer::fillRectWithTiles(int vramArea, int x, int y, int w, int h, uint16 nameTblEntry, bool incr, bool topToBottom, const uint16 *patternTable) {
 	uint16 addr = vramArea ? (vramArea == 1 ? 0xE000 : 0xF000) : 0xC000;
 	if (y & 0x8000) {
 		y &= ~0x8000;
@@ -583,30 +444,7 @@ void SegaRenderer::fillRectWithTiles(int vramArea, int x, int y, int w, int h, u
 	}
 }
 
-void SegaRenderer::writeUint16VSRAM(int addr, uint16 value) {
-	assert(addr < 80);
-	assert(!(addr & 1));
-	_vsram[addr >> 1] = value;
-}
-
-void SegaRenderer::writeUint8VRAM(int addr, uint8 value) {
-	assert(addr < 0x10000);
-	_vram[addr] = value;
-}
-
-void SegaRenderer::writeUint16VRAM(int addr, uint16 value) {
-	assert(addr < 0x10000);
-	*((uint16*)(_vram + addr)) = value;
-}
-
-void SegaRenderer::clearPlanes() {
-	for (int i = 0; i < 3; ++i) {
-		if (_planes[i].nameTableSize)
-			memset(_planes[i].nameTable, 0, _planes[i].nameTableSize * sizeof(uint16));
-	}
-}
-
-void SegaRenderer::render(int destPageNum, int renderBlockX, int renderBlockY, int renderBlockWidth, int renderBlockHeight, bool spritesOnly) {
+void SCDRenderer::renderToPage(int destPageNum, int renderBlockX, int renderBlockY, int renderBlockWidth, int renderBlockHeight, bool spritesOnly) {
 	if (renderBlockX == -1)
 		renderBlockX = 0;
 	if (renderBlockY == -1)
@@ -616,343 +454,12 @@ void SegaRenderer::render(int destPageNum, int renderBlockX, int renderBlockY, i
 	if (renderBlockHeight == -1)
 		renderBlockHeight = _blocksH;
 
-	uint8 *renderBuffer = _screen->getPagePtr(destPageNum);
-	// This also ensures that a dirty rect is created if necessary
-	_screen->fillRect(renderBlockX << 3, renderBlockY << 3, ((renderBlockX + renderBlockWidth) << 3) - 1, ((renderBlockY + renderBlockHeight) << 3) - 1, 0, destPageNum);
-
-	// Plane B
-	if (!spritesOnly)
-		renderPlanePart(kPlaneB, renderBuffer, renderBlockX, renderBlockY, renderBlockX + renderBlockWidth, renderBlockY + renderBlockHeight);
-
-	// Plane A (only draw if the nametable is not identical to that of plane B)
-	if (_planes[kPlaneA].nameTable != _planes[kPlaneB].nameTable && !spritesOnly) {
-		// If the window plane is active the rendering of plane A becomes more tedious because the window plane
-		// kind of replaces plane A in the space that is covered by it.
-		if (_planes[kWindowPlane].nameTableSize) {
-			SegaPlane *p = &_planes[kWindowPlane];
-			renderPlanePart(kPlaneA, renderBuffer, MAX<int>(0, renderBlockX), MAX<int>(0, renderBlockY), MIN<int>(p->blockX, renderBlockX + renderBlockWidth), MIN<int>(_blocksH, renderBlockY + renderBlockHeight));
-			renderPlanePart(kPlaneA, renderBuffer, MAX<int>(0, renderBlockX), MAX<int>(0, renderBlockY), MIN<int>(_blocksW, renderBlockX + renderBlockWidth), MIN<int>(p->blockY, renderBlockY + renderBlockHeight));
-			renderPlanePart(kPlaneA, renderBuffer, MAX<int>(p->blockX + p->w, renderBlockX), MAX<int>(0, renderBlockY), MIN<int>(_blocksW, renderBlockX + renderBlockWidth), MIN<int>(_blocksH, renderBlockY + renderBlockHeight));
-			renderPlanePart(kPlaneA, renderBuffer, MAX<int>(0, renderBlockX), MAX<int>(p->blockY + p->h, renderBlockY), MIN<int>(_blocksW, renderBlockX + renderBlockWidth), MIN<int>(_blocksH, renderBlockY + renderBlockHeight));
-		} else {
-			renderPlanePart(kPlaneA, renderBuffer, renderBlockX, renderBlockY, renderBlockX + renderBlockWidth, renderBlockY + renderBlockHeight);
-		}
-	}
-
-	// Window Plane
-	if (_planes[kWindowPlane].nameTableSize && !spritesOnly) {
-		SegaPlane *p = &_planes[kWindowPlane];
-		renderPlanePart(kWindowPlane, renderBuffer, MIN<int>(p->blockX, renderBlockX + renderBlockWidth), MIN<int>(p->blockY, renderBlockY + renderBlockHeight), MAX<int>(p->blockX + p->w, renderBlockX), MAX<int>(p->blockY + p->h, renderBlockY));
-	}
-
-	// Sprites
-	memset(_spriteMask, 0xFF, _screenW * _screenH * sizeof(uint8));
-	const uint16 *pos = _spriteTable;
-	for (int i = 0; i < _numSpritesMax && pos; ++i) {
-		int y = *pos++ & 0x3FF;
-		uint8 bH = ((*pos >> 8) & 3) + 1;
-		uint8 bW = ((*pos >> 10) & 3) + 1;
-		uint8 next = *pos++ & 0x7F;
-		uint16 pal = ((*pos >> 13) & 3) << 4;
-		bool prio = (*pos & 0x8000);
-		bool hflip = (*pos & 0x800);
-		bool vflip = (*pos & 0x1000);
-		uint16 tile = *pos++ & 0x7FF;
-		int x = *pos & 0x3FF;
-
-		// Sprite masking. Can't happen really, since the animator automatically adds 128 to x and y coords for all sprites.
-		assert(!(x == 0 && y >= 128));
-
-		assert(!hflip);
-		assert(!vflip);
-
-		x -= 128;
-		y -= 128;
-
-		/*if ((x >> 3) < renderBlockX) {
-			bW = MIN<int>(0, (int)bW - (renderBlockX - (x >> 3)));
-			x = (renderBlockX << 3);
-
-		}
-
-		if ((y >> 3) < renderBlockY) {
-			bH = MIN<int>(0, (int)bH - (renderBlockY - (y >> 3)));
-			y = (renderBlockY << 3);
-		}
-
-		bW = MIN<int>(bW, renderBlockWidth);
-		bH = MIN<int>(bH, renderBlockHeight);*/
-
-		uint8 *dst = renderBuffer + y * _screenW + x;
-		uint8 *msk = _spriteMask + y * _screenW + x;
-
-		for (int blX = 0; blX < bW; ++blX) {
-			uint8 *dst2 = dst;
-			uint8 *msk2 = msk;
-			for (int blY = 0; blY < bH; ++blY) {
-				renderSpriteTile(dst, msk, x + (blX << 3), y + (blY << 3), tile++, pal, vflip, hflip, prio);
-				dst += (_screenW << 3);
-				msk += (_screenW << 3);
-			}
-			dst = dst2 + 8;
-			msk = msk2 + 8;
-		}
-
-		pos = next ? &_spriteTable[next << 2] : 0;
-	}
-
-	// Priority Tiles
-	// Instead of going through all rendering passes for all planes again (only now drawing the
-	// prio tiles instead of the non-priority tiles) I have collected the data for the priority
-	// tiles on the way and put that data into a chain. Should be faster...
-	for (PrioTileRenderObj *e = _prioChainStart; e; e = e->_next)
-		mRenderLineFragment(e->_hflip, e->_start & 1, e->_end & 1, e->_mask, e->_dst, e->_mask, e->_src, e->_start, e->_end, e->_pal)
-
-	clearPrioChain();
-}
-
-void SegaRenderer::renderPlanePart(int plane, uint8 *dstBuffer, int x1, int y1, int x2, int y2) {
-	SegaPlane *p = &_planes[plane];
-	uint8 *dst = dstBuffer + (y1 << 3) * _screenW + (x1 << 3);
-
-	for (int y = y1; y < y2; ++y) {
-		int hScrollTableIndex = (plane == kWindowPlane) ? -1 : (_hScrollMode == kHScrollFullScreen) ? plane : (y1 << 4) + plane;
-		uint8 *dst2 = dst;
-		for (int x = x1; x < x2; ++x) {
-			int vScrollTableIndex = (plane == kWindowPlane) ? -1 : (_vScrollMode == kVScrollFullScreen) ? plane : (x & ~1) + plane;
-			uint16 vscrNt = 0;
-			uint16 vscrPxStart = 0;
-			uint16 vscrPxEnd = 8;
-
-			if (vScrollTableIndex != -1) {
-				vscrNt = _vsram[vScrollTableIndex] & 0x3FF;
-				vscrPxStart = vscrNt & 7;
-				vscrNt >>= 3;
-			}
-
-			int ty = (vscrNt + y) % p->mod;
-
-			renderPlaneTile(dst, x, &p->nameTable[ty * _pitch], vscrPxStart, vscrPxEnd, hScrollTableIndex, _pitch);
-
-			if (vscrPxStart) {
-				ty = (ty + 1) % p->mod;
-				uint16 dstOffs = (vscrPxEnd - vscrPxStart) * _screenW;
-				vscrPxEnd = vscrPxStart;
-				vscrPxStart = 0;
-				renderPlaneTile(dst + dstOffs, x, &p->nameTable[ty * _pitch], vscrPxStart, vscrPxEnd, hScrollTableIndex, _pitch);
-			}
-			dst += 8;
-		}
-		dst = dst2 + (_screenW << 3);
-	}
-}
-
-void SegaRenderer::renderPlaneTile(uint8 *dst, int ntblX, const uint16 *ntblLine, int vScrollLSBStart, int vScrollLSBEnd, int hScrollTableIndex, uint16 pitch) {
-	for (int bY = vScrollLSBStart; bY < vScrollLSBEnd; ++bY) {
-		uint8 *dst2 = dst;
-		uint16 hscrNt = 0;
-		uint16 hscrPx = 0;
-
-		if (hScrollTableIndex != -1) {
-			hscrNt = (-_hScrollTable[hScrollTableIndex]) & 0x3FF;
-			hscrPx = hscrNt & 7;
-			hscrNt >>= 3;
-		}
-
-		const uint16 *pNt = &ntblLine[(ntblX + hscrNt) % pitch];
-		if (pNt < (const uint16*)(&_vram[0x10000])) {
-			uint16 nt = *pNt;
-			uint16 pal = ((nt >> 13) & 3) << 4;
-			bool hflip = (nt & 0x800);
-			int y = bY % 8;
-			if (nt & 0x1000) // vflip
-				y = 7 - y;
-
-			// We skip the priority tiles here and draw them later
-			if (nt & 0x8000)
-				initPrioRenderTask(dst, 0, &_vram[((nt & 0x7FF) << 5) + (y << 2) + (hscrPx >> 1)], hscrPx, 8, pal, hflip);
-			else
-				mRenderLineFragment(hflip, hscrPx & 1, 0, 0, dst, 0, &_vram[((nt & 0x7FF) << 5) + (y << 2) + (hscrPx >> 1)], hscrPx, 8, pal);
-		}
-
-		if (hscrPx) {
-			dst += (8 - hscrPx);
-			pNt = &ntblLine[(ntblX + hscrNt + 1) % pitch];
-			if (pNt < (const uint16*)(&_vram[0x10000])) {
-				uint16 nt = *pNt;
-				uint16 pal = ((nt >> 13) & 3) << 4;
-				bool hflip = (nt & 0x800);
-				int y = bY % 8;
-				if (nt & 0x1000) // vflip
-					y = 7 - y;
-
-				// We skip the priority tiles here and draw them later
-				if (nt & 0x8000)
-					initPrioRenderTask(dst, 0, &_vram[((nt & 0x7FF) << 5) + (y << 2)], 0, hscrPx, pal, hflip);
-				else
-					mRenderLineFragment(hflip, 0, hscrPx & 1, 0, dst, 0, &_vram[((nt & 0x7FF) << 5) + (y << 2)], 0, hscrPx, pal)
-			}
-		}
-
-		if (hScrollTableIndex != -1 && _hScrollMode == kHScroll1PixelRows)
-			hScrollTableIndex += 2;
-		dst = dst2 + _screenW;
-	}
-}
-
-#undef vflip
-
-void SegaRenderer::renderSpriteTile(uint8 *dst, uint8 *mask, int x, int y, uint16 tile, uint8 pal, bool vflip, bool hflip, bool prio) {
-	if (y <= -8 || y >= _screenH || x <= -8 || x >= _screenW)
-		return;
-
-	const uint8 *src = &_vram[tile << 5];
-	if (vflip)
-		src += 31;
-
-	if (y < 0) {
-		dst -= (y * _screenW);
-		mask -= (y * _screenW);
-	} if (x < 0) {
-		dst -= x;
-		mask -= x;
-	}
-
-	int xstart = CLIP<int>(-x, 0, 7);
-	int xend = CLIP<int>(_screenW - x, 0, 8);
-	src += (xstart >> 1);
-
-	int ystart = CLIP<int>(-y, 0, 7);
-	int yend = CLIP<int>(_screenH - y, 0, 8);
-	src += (ystart << 2);
-
-	for (int bY = ystart; bY < yend; ++bY) {
-		uint8 *dst2 = dst;
-		uint8 *msk2 = mask;
-
-		if (prio)
-			initPrioRenderTask(dst, mask, src, xstart, xend, pal, hflip);
-		else
-			mRenderLineFragment(hflip, xstart & 1, xend & 1, 1, dst, mask, src, xstart, xend, pal);
-
-		src += 4;
-		dst = dst2 + _screenW;
-		mask = msk2 + _screenW;
-	}
-}
-
-#if SEGA_PERFORMANCE
-template<bool hflip, bool oddStart, bool oddEnd> void SegaRenderer::renderLineFragmentM(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal) {
-	if (hflip)
-		src += ((end - 1 - start) >> 1);
-
-	for (int i = (end - start) >> 1; i; --i) {
-		uint8 col = hflip ? (oddEnd ? *src-- >> 4 : *src & 0x0F) : (oddStart ? *src++ & 0x0F : *src >> 4);
-		uint8 col2 = hflip ? (oddEnd ? *src & 0x0F : *src-- >> 4) : (oddStart ? *src >> 4 : *src++ & 0x0F);
-		if (col & *mask) {
-			*dst = pal | col;
-			*mask = 0;
-		}
-		dst++;
-		mask++;
-		if (col2 & *mask) {
-			*dst = pal | col2;
-			*mask = 0;
-		}
-		dst++;
-		mask++;
-	}
-	if (oddStart != oddEnd) {
-		uint8 col = hflip ? (oddEnd ? *src-- >> 4 : *src & 0x0F) : (oddStart ? *src++ & 0x0F : *src >> 4);
-		if (col & *mask) {
-			*dst = pal | col;
-			*mask = 0;
-		}
-		dst++;
-		mask++;
-	}
-}
-
-template<bool hflip, bool oddStart, bool oddEnd> void SegaRenderer::renderLineFragmentD(uint8 *dst, const uint8 *src, int start, int end, uint8 pal) {
-	if (hflip)
-		src += ((end - 1 - start) >> 1);
-
-	for (int i = (end - start) >> 1; i; --i) {
-		uint8 col = hflip ? (oddEnd ? *src-- >> 4 : *src & 0x0F) : (oddStart ? *src++ & 0x0F : *src >> 4);
-		uint8 col2 = hflip ? (oddEnd ? *src & 0x0F : *src-- >> 4) : (oddStart ? *src >> 4 : *src++ & 0x0F);
-		if (col)
-			*dst = pal | col;
-		dst++;
-		if (col2)
-			*dst = pal | col2;
-		dst++;
-	}
-	if (oddStart != oddEnd) {
-		uint8 col = hflip ? (oddEnd ? *src-- >> 4 : *src & 0x0F) : (oddStart ? *src++ & 0x0F : *src >> 4);
-		if (col)
-			*dst = pal | col;
-		dst++;
-	}
-}
-#else
-template<bool hflip> void SegaRenderer::renderLineFragment(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal) {
-	if (hflip) {
-		src += ((end - 1 - start) >> 1);
-		if (end & 1) {
-			start++;
-			end++;
-		}
-	}
-
-	if (mask) {
-		for (int bX = start; bX < end; ++bX) {
-			uint8 col = hflip ? ((bX & 1) ? *src-- >> 4 : *src & 0x0F) : ((bX & 1) ? *src++ & 0x0F : *src >> 4);
-			if (col & *mask) {
-				*dst = pal | col;
-				*mask = 0;
-			}
-			dst++;
-			mask++;
-		}
-	} else {
-		for (int bX = start; bX < end; ++bX) {
-			uint8 col = hflip ? ((bX & 1) ? *src-- >> 4 : *src & 0x0F) : ((bX & 1) ? *src++ & 0x0F : *src >> 4);
-			if (col)
-				*dst = pal | col;
-			dst++;
-		}
-	}
-}
-#endif
-
-#undef mRenderLineFragment
-
-void SegaRenderer::initPrioRenderTask(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal, bool hflip) {
-#if SEGA_USE_MEMPOOL
-	_prioChainEnd =	new (_prioRenderMemPool) PrioTileRenderObj(_prioChainEnd, dst, mask, src, start, end, pal, hflip);
-#else
-	_prioChainEnd = new PrioTileRenderObj(_prioChainEnd, dst, mask, src, start, end, pal, hflip);
-#endif
-	if (!_prioChainStart)
-		_prioChainStart = _prioChainEnd;
-}
-
-void SegaRenderer::clearPrioChain() {
-	while (_prioChainEnd) {
-		_prioChainEnd->_next = 0;
-		PrioTileRenderObj *e = _prioChainEnd->_pred;
-#if SEGA_USE_MEMPOOL
-		_prioRenderMemPool.deleteChunk(_prioChainEnd);
-#else
-		delete _prioChainEnd;
-#endif
-		_prioChainEnd = e;
-	}
-	_prioChainStart = 0;
+	_screen->addDirtyRect(renderBlockX << 3, renderBlockY << 3, ((renderBlockX + renderBlockWidth) << 3) - 1, ((renderBlockY + renderBlockHeight) << 3) - 1);
+	Graphics::SegaRenderer::render(_screen->getPagePtr(destPageNum), renderBlockX, renderBlockY, renderBlockWidth, renderBlockHeight, spritesOnly);
 }
 
-SegaAnimator::SegaAnimator(SegaRenderer *renderer) : _renderer(renderer), _needUpdate(false) {
-	_sprites = new Sprite[80]();
+SegaAnimator::SegaAnimator(SCDRenderer *renderer) : _renderer(renderer), _needUpdate(false) {
+	_sprites = new Sprite[80];
 	assert(_sprites);
 	_tempBuffer = new uint16[320]();
 	assert(_tempBuffer);
@@ -1214,7 +721,7 @@ const uint8 *SegaCDFont::getGlyphData(uint16 c, uint8 &charWidth, uint8 &charHei
 	return res;
 }
 
-ScrollManager::ScrollManager(SegaRenderer *renderer) : _renderer(renderer) {
+ScrollManager::ScrollManager(SCDRenderer *renderer) : _renderer(renderer) {
 	_vScrollTimers = new ScrollTimer[2];
 	assert(_vScrollTimers);
 	_hScrollTimers = new ScrollTimer[2];
diff --git a/engines/kyra/graphics/screen_eob_segacd.h b/engines/kyra/graphics/screen_eob_segacd.h
index 7c70e40c0bc..863f623c7d5 100644
--- a/engines/kyra/graphics/screen_eob_segacd.h
+++ b/engines/kyra/graphics/screen_eob_segacd.h
@@ -24,138 +24,28 @@
 
 #ifdef ENABLE_EOB
 
-#define SEGA_PERFORMANCE		true
-#define SEGA_USE_MEMPOOL		true
-
 #include "kyra/graphics/screen_eob.h"
-
-#if SEGA_USE_MEMPOOL
-#include "common/memorypool.h"
-#endif
+#include "graphics/segagfx.h"
 
 namespace Kyra {
 
-class SegaRenderer {
+class SCDRenderer : public Graphics::SegaRenderer {
 public:
-	enum Plane {
-		kPlaneA = 0,
-		kPlaneB = 1,
-		kWindowPlane = 2
-	};
-
-	enum WindowMode {
-		kWinToLeft = 0,
-		kWinToTop = 0,
-		kWinToRight = 1,
-		kWinToBottom = 1
-	};
-
-	enum HScrollMode {
-		kHScrollFullScreen = 0,
-		kHScroll8PixelRows,
-		kHScroll1PixelRows
-	};
-
-	enum VScrollMode {
-		kVScrollFullScreen = 0,
-		kVScroll16PixelStrips
-	};
+	SCDRenderer(Screen_EoB *screen);
+	~SCDRenderer();
 
-public:
-	SegaRenderer(Screen_EoB *screen);
-	~SegaRenderer();
-
-	void setResolution(int w, int h);
-	void setPlaneTableLocation(int plane, uint16 addr);
-	// The hardware allows/demands separate modification of the vertical and horizontal properties.
-	// To allow this without making another function the w/h parameters can be set to -1 which will
-	// keep the existing value for that property.
-	void setupPlaneAB(int pixelWidth, int pixelHeigth);
-	// The hardware allows/demands separate modification of the vertical and horizontal properties.
-	// To allow this without making another function the blockX/Y parameters can be set to -1 which
-	// will keep the existing value for that property.
-	void setupWindowPlane(int blockX, int blockY, int horizontalMode, int verticalMode);
-	void setHScrollTableLocation(int addr);
-	void setSpriteTableLocation(int addr);
-	void setPitch(int pitch);
-	void setHScrollMode(int mode);
-	void setVScrollMode(int mode);
-
-	void loadToVRAM(const void *data, uint16 dataSize, uint16 addr);
 	void loadStreamToVRAM(Common::SeekableReadStream *in, uint16 addr, bool compressedData = false);
-	void memsetVRAM(int addr, uint8 val, int len);
 	void fillRectWithTiles(int vramArea, int x, int y, int w, int h, uint16 nameTblEntry, bool incr = false, bool topToBottom = false, const uint16 *patternTable = 0);
-	void writeUint16VSRAM(int addr, uint16 value);
-	void writeUint8VRAM(int addr, uint8 value);
-	void writeUint16VRAM(int addr, uint16 value);
-	void clearPlanes();
-
-	void render(int destPageNum, int renderLeft = -1, int renderTop = -1, int renderWidth = -1, int renderHeight = -1, bool spritesOnly = false);
-private:
-	void renderPlanePart(int plane, uint8 *dstBuffer, int x1, int y1, int x2, int y2);
-	void renderPlaneTile(uint8 *dst, int destX, const uint16 *nameTable, int vScrollLSBStart, int vScrollLSBEnd, int hScrollTableIndex, uint16 pitch);
-	void renderSpriteTile(uint8 *dst, uint8 *mask, int x, int y, uint16 tile, uint8 pal, bool vflip, bool hflip, bool prio);
-#if SEGA_PERFORMANCE
-	template<bool hflip, bool oddStart, bool oddEnd> void renderLineFragmentM(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal);
-	template<bool hflip, bool oddStart, bool oddEnd> void renderLineFragmentD(uint8 *dst, const uint8 *src, int start, int end, uint8 pal);
-	typedef void(SegaRenderer::*renderFuncM)(uint8*, uint8*, const uint8*, int, int, uint8);
-	typedef void(SegaRenderer::*renderFuncD)(uint8*, const uint8*, int, int, uint8);
-	const renderFuncM *_renderLineFragmentM;
-	const renderFuncD *_renderLineFragmentD;
-#else
-	template<bool hflip> void renderLineFragment(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal);
-#endif
-
-	void initPrioRenderTask(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal, bool hflip);
-	void clearPrioChain();
 
-	struct SegaPlane {
-		SegaPlane() : blockX(0), blockY(0), w(0), h(0), mod(0), nameTable(0), nameTableSize(0) {}
-		int blockX, blockY;
-		uint16 w, h, mod;
-		uint16 *nameTable;
-		uint16 nameTableSize;
-	};
-
-	SegaPlane _planes[3];
-	uint8 *_vram;
-	uint16 *_vsram;
-	uint16 *_hScrollTable;
-	uint16 *_spriteTable;
-	uint8 *_spriteMask;
-	uint8 _hScrollMode;
-	uint8 _vScrollMode;
-	uint16 _pitch;
-	uint16 _numSpritesMax;
-
-	struct PrioTileRenderObj {
-		PrioTileRenderObj(PrioTileRenderObj *chainEnd, uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal, bool hflip) :
-			_pred(chainEnd), _next(0), _dst(dst), _mask(mask), _src(src), _start(start), _end(end), _pal(pal), _hflip(hflip) {
-			if (_pred)
-				_pred->_next = this;
-		}
-		uint8 *_dst;
-		uint8 *_mask;
-		const uint8 *_src;
-		int _start;
-		int _end;
-		uint8 _pal;
-		bool _hflip;
-		PrioTileRenderObj *_pred;
-		PrioTileRenderObj *_next;
-	};
+	void renderToPage(int destPageNum, int renderBlockX = -1, int renderBlockY = -1, int renderBlockWidth = -1, int renderBlockHeight = -1, bool spritesOnly = false);
 
-#if SEGA_USE_MEMPOOL
-	Common::ObjectPool<PrioTileRenderObj> _prioRenderMemPool;
-#endif
-	PrioTileRenderObj *_prioChainStart, *_prioChainEnd;
-	uint16 _screenW, _screenH, _blocksW, _blocksH;
+private:
 	Screen_EoB *_screen;
 };
 
 class SegaAnimator {
 public:
-	SegaAnimator(SegaRenderer *renderer);
+	SegaAnimator(SCDRenderer *renderer);
 	~SegaAnimator();
 
 	void initSprite(int id, int16 x, int16 y, uint16 nameTbl, uint16 hw);
@@ -176,13 +66,13 @@ private:
 
 	uint16 *_tempBuffer;
 	Sprite *_sprites;
-	SegaRenderer *_renderer;
+	SCDRenderer *_renderer;
 	bool _needUpdate;
 };
 
 class ScrollManager {
 public:
-	ScrollManager(SegaRenderer *renderer);
+	ScrollManager(SCDRenderer *renderer);
 	~ScrollManager();
 
 	void setVScrollTimers(uint16 destA, int incrA, int delayA, uint16 destB, int incrB, int delayB);
@@ -202,7 +92,7 @@ private:
 
 	ScrollTimer *_vScrollTimers;
 	ScrollTimer *_hScrollTimers;
-	SegaRenderer *_renderer;
+	SCDRenderer *_renderer;
 };
 
 } // End of namespace Kyra
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 5aff48f6405..ef260d36c4a 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -2236,7 +2236,7 @@ void GUI_EoB::runCampMenu() {
 					prevHighlightButton = 0;
 				}
 			} else if (_vm->gameFlags().platform == Common::kPlatformSegaCD && newMenu == 2) {
-				_screen->sega_getRenderer()->render(0, 0, 0, 22, 21);
+				_screen->sega_getRenderer()->renderToPage(0, 0, 0, 22, 21);
 			}
 
 			lastMenu = newMenu;
@@ -2748,7 +2748,7 @@ int GUI_EoB::getTextInput(char *dest, int x, int y, int destMaxLen, int textColo
 					_screen->sega_setTextBuffer(segaCharBuf, 32);
 					_vm->_txt->printShadedText(sufx, 0, 0, textColor1, 0, -1, -1, 0, false);
 					_screen->sega_loadTextBufferToVRAM(0, ((y >> 3) * 40 + x + pos + 1) << 5, 32);
-					_screen->sega_getRenderer()->render(0, x + pos, y >> 3, 1, 1);
+					_screen->sega_getRenderer()->renderToPage(0, x + pos, y >> 3, 1, 1);
 					_screen->sega_setTextBuffer(0, 0);
 				} else if (cursorState) {
 					_screen->copyRegion((pos + 1) << 3, 191, (x + pos) << 3, y, 8, 9, 2, 0, Screen::CR_NO_P_CHECK);
@@ -2875,7 +2875,7 @@ int GUI_EoB::getTextInput(char *dest, int x, int y, int destMaxLen, int textColo
 			_vm->_txt->printShadedText(dest, 0, 0, textColor1, 0, -1, -1, 0, false);
 			_screen->setFontStyles(_screen->_currentFont, cs);
 			_screen->sega_loadTextBufferToVRAM(0, ((y >> 3) * 40 + x + 1) << 5, destMaxLen << 5);
-			_screen->sega_getRenderer()->render(0, x, y >> 3, destMaxLen, 1);
+			_screen->sega_getRenderer()->renderToPage(0, x, y >> 3, destMaxLen, 1);
 			_screen->sega_setTextBuffer(0, 0);
 		} else {
 			_screen->copyRegion(0, 191, (x - 1) << 3, y, (destMaxLen + 2) << 3, 9, 2, 0, Screen::CR_NO_P_CHECK);
@@ -2899,7 +2899,7 @@ int GUI_EoB::getTextInput(char *dest, int x, int y, int destMaxLen, int textColo
 			_screen->sega_setTextBuffer(segaCharBuf, 32);
 			_vm->_txt->printShadedText(sufx, 0, 0, textColor1, 0, -1, -1, 0, false);
 			_screen->sega_loadTextBufferToVRAM(0, ((y >> 3) * 40 + x + pos + 1) << 5, 32);
-			_screen->sega_getRenderer()->render(0, x + pos, y >> 3, 1, 1);
+			_screen->sega_getRenderer()->renderToPage(0, x + pos, y >> 3, 1, 1);
 			_screen->sega_setTextBuffer(0, 0);
 		} else if (cursorState) {
 			_screen->printText(sufx, (x + pos) << 3, y, textColor1, cursorColor);
@@ -3302,7 +3302,7 @@ int GUI_EoB::selectSaveSlotDialog(int x, int y, int id) {
 				_screen->sega_clearTextBuffer(0);
 				_vm->_txt->printShadedText(Common::String::format("%03d/989", sli).c_str(), 0, 0, 0xFF, 0xCC, -1, -1, 0, false);
 				_screen->sega_loadTextBufferToVRAM(0, 64, 224);
-				_screen->sega_getRenderer()->render(0, (_saveSlotX + 8) >> 3, (_saveSlotY + 152) >> 3, 7, 1);
+				_screen->sega_getRenderer()->renderToPage(0, (_saveSlotX + 8) >> 3, (_saveSlotY + 152) >> 3, 7, 1);
 			} else {
 				Screen::FontId of = _screen->setFont(Screen::FID_6_FNT);
 				_screen->set16bitShadingLevel(4);
@@ -3477,7 +3477,7 @@ void GUI_EoB::runMemorizePrayMenu(int charIndex, int spellType) {
 
 			_screen->setCurPage(0);
 			if (_vm->gameFlags().platform == Common::kPlatformSegaCD)
-				_screen->sega_getRenderer()->render(0, 0, 10, 22, 9);
+				_screen->sega_getRenderer()->renderToPage(0, 0, 10, 22, 9);
 			else
 				_screen->copyRegion(0, 50, 0, 50, 176, 72, 2, 0, Screen::CR_NO_P_CHECK);
 			lastHighLightText = -1;
@@ -3489,7 +3489,7 @@ void GUI_EoB::runMemorizePrayMenu(int charIndex, int spellType) {
 				_screen->sega_clearTextBuffer(0);
 				_vm->_txt->printShadedText(Common::String::format(_vm->_menuStringsMgc[1], (char)(np[lastHighLightButton] - numAssignedSpellsPerBookPage[lastHighLightButton] + 79), (char)(np[lastHighLightButton] + 79)).c_str(), 0, 2, 0x55, 0xCC, 160, 16, 0, false);
 				_screen->sega_loadTextBufferToVRAM(0, 0x5560, 1280);
-				_screen->sega_getRenderer()->render(0, 1, 8, 20, 2);
+				_screen->sega_getRenderer()->renderToPage(0, 1, 8, 20, 2);
 			} else {
 				_screen->set16bitShadingLevel(4);
 				_screen->printShadedText(Common::String::format(_vm->_menuStringsMgc[1], np[lastHighLightButton] - numAssignedSpellsPerBookPage[lastHighLightButton], np[lastHighLightButton]).c_str(), 8, 38, _vm->guiSettings()->colors.guiColorLightBlue, _vm->guiSettings()->colors.fill, _vm->guiSettings()->colors.guiColorBlack);
@@ -4364,7 +4364,7 @@ Button *GUI_EoB::initMenu(int id) {
 	}
 
 	if (_vm->gameFlags().platform == Common::kPlatformSegaCD)
-		_screen->sega_getRenderer()->render(0, 0, 0, 22, 21);
+		_screen->sega_getRenderer()->renderToPage(0, 0, 0, 22, 21);
 	else
 		_screen->copyRegion(_screen->_curDim->sx << 3, _screen->_curDim->sy, _screen->_curDim->sx << 3, _screen->_curDim->sy, _screen->_curDim->w << 3, _screen->_curDim->h, 2, 0, Screen::CR_NO_P_CHECK);
 
diff --git a/engines/kyra/gui/gui_eob_segacd.cpp b/engines/kyra/gui/gui_eob_segacd.cpp
index 075384fd15e..c7597f1b9ef 100644
--- a/engines/kyra/gui/gui_eob_segacd.cpp
+++ b/engines/kyra/gui/gui_eob_segacd.cpp
@@ -63,7 +63,7 @@ void EoBEngine::gui_drawPlayField(bool refresh) {
 	_txt->clearDim(0);
 	_screen->sega_getAnimator()->clearSprites();
 	_screen->sega_getAnimator()->update();
-	SegaRenderer *r = _screen->sega_getRenderer();
+	SCDRenderer *r = _screen->sega_getRenderer();
 	r->fillRectWithTiles(0, 0, 0, 40, 28, 0);
 	r->fillRectWithTiles(1, 0, 0, 40, 28, 0);
 
@@ -110,7 +110,7 @@ void EoBEngine::gui_drawPlayField(bool refresh) {
 	_screen->copyRegionToBuffer(0, 173, 0, 6, 120, _shakeBackBuffer1);
 	_screen->copyRegionToBuffer(0, 0, 117, 179, 6, _shakeBackBuffer2);
 
-	// Since we're not going to draw the character portrait boxes with the SegaRenderer but rather with our "normal" code, we have to backup
+	// Since we're not going to draw the character portrait boxes with the SCDRenderer but rather with our "normal" code, we have to backup
 	// some parts of the background between the character portraits. Unlike in the other versions the red splat shapes overlaps with that space.
 	for (int i = 0; i < 6; ++i) {
 		delete[] _redSplatBG[i];
@@ -137,7 +137,7 @@ void EoBEngine::gui_setupPlayFieldHelperPages(bool keepText) {
 	if (!keepText)
 		_txt->clearDim(0);
 
-	SegaRenderer *r = _screen->sega_getRenderer();
+	SCDRenderer *r = _screen->sega_getRenderer();
 	r->loadToVRAM(_scrYellow, 4992, 0x3CE0);
 	r->fillRectWithTiles(0, 0, 0, 22, 21, 0);
 	r->fillRectWithTiles(0, 22, 0, 18, 21, 0);
@@ -149,11 +149,11 @@ void EoBEngine::gui_setupPlayFieldHelperPages(bool keepText) {
 	// r->fillRectWithTiles(0, 0, 1, 22, 14, 0xE295, true, true);
 	// Text field tiles
 	r->fillRectWithTiles(0, 1, 22, 35, 3, 0x2597, true);
-	r->render(0);
+	r->renderToPage(0);
 	r->fillRectWithTiles(1, 22, 0, 18, 21, 0x6444, true, true, _invPattern);
-	r->render(2);
+	r->renderToPage(2);
 	r->fillRectWithTiles(1, 22, 0, 18, 21, 0x6444, true, true, _statsPattern);
-	r->render(Screen_EoB::kSegaRenderPage);
+	r->renderToPage(Screen_EoB::kSegaRenderPage);
 	_screen->copyRegion(184, 1, 176, 168, guiSettings()->charBoxCoords.boxWidth, 24, 0, 2, Screen::CR_NO_P_CHECK);
 	_screen->copyRegion(184, 25, 240, 168, guiSettings()->charBoxCoords.boxWidth, guiSettings()->charBoxCoords.boxHeight - 24, 0, 2, Screen::CR_NO_P_CHECK);
 }
@@ -187,7 +187,7 @@ void EoBEngine::gui_drawCharacterStatsPage() {
 		return;
 	}
 
-	SegaRenderer *r = _screen->sega_getRenderer();
+	SCDRenderer *r = _screen->sega_getRenderer();
 	EoBCharacter *c = &_characters[_updateCharNum];
 
 	memset(_tempPattern, 0, 792);
@@ -225,7 +225,7 @@ void EoBEngine::gui_drawCharacterStatsPage() {
 	}
 
 	r->fillRectWithTiles(0, 22, 0, 18, 21, 0, true, true, _tempPattern);
-	r->render(2, 22, 5, 18, 16);
+	r->renderToPage(2, 22, 5, 18, 16);
 }
 
 void EoBEngine::gui_displayMap() {
@@ -243,7 +243,7 @@ void EoBEngine::gui_displayMap() {
 		gui_drawCharPortraitWithStats(i);
 	}
 
-	SegaRenderer *r = _screen->sega_getRenderer();
+	SCDRenderer *r = _screen->sega_getRenderer();
 	r->fillRectWithTiles(0, 0, 0, 40, 28, 0);
 	r->fillRectWithTiles(1, 0, 0, 40, 28, 0);
 	_screen->sega_getAnimator()->clearSprites();
@@ -276,7 +276,7 @@ void EoBEngine::gui_displayMap() {
 	r->fillRectWithTiles(0, 31, 16, 8, 2, 0x63E1, true);
 
 	drawMapPage(_currentLevel);
-	r->render(0);
+	r->renderToPage(0);
 	_screen->sega_fadeToNeutral(3);
 
 	gui_resetButtonList();
@@ -303,7 +303,7 @@ void EoBEngine::gui_displayMap() {
 		}
 
 		if (update) {
-			r->render(0);
+			r->renderToPage(0);
 			_screen->updateScreen();
 		}
 		delayUntil(del);
@@ -327,7 +327,7 @@ void EoBEngine::gui_drawSpellbook() {
 		return;
 	}
 
-	SegaRenderer *r = _screen->sega_getRenderer();
+	SCDRenderer *r = _screen->sega_getRenderer();
 	r->fillRectWithTiles(0, 10, 15, 12, 7, 0);
 	r->fillRectWithTiles(1, 10, 15, 12, 7, 0x6429);
 	memset(_tempPattern, 0, 168);
@@ -347,7 +347,7 @@ void EoBEngine::gui_drawSpellbook() {
 	}
 
 	r->fillRectWithTiles(0, 10, 15, 12, 6, 0, true, false, _tempPattern);
-	r->render(Screen_EoB::kSegaRenderPage, 10, 15, 12, 7);
+	r->renderToPage(Screen_EoB::kSegaRenderPage, 10, 15, 12, 7);
 
 	// The original SegaCD version actually doesn't disable the spell book after use but closes it instead.
 	if (!_closeSpellbookAfterUse) {
@@ -415,7 +415,7 @@ void EoBEngine::gui_updateAnimations() {
 	}
 	if (redrawCompass) {
 		_screen->sega_getRenderer()->loadToVRAM(_compassData + (_compassAnimPhase & 0x0F) * 0x500, 0x500, 0xEE00);
-		_screen->sega_getRenderer()->render(0, 11, 15, 10, 6);
+		_screen->sega_getRenderer()->renderToPage(0, 11, 15, 10, 6);
 		updScreen = true;
 	}
 
@@ -510,7 +510,7 @@ void EoBEngine::makeNameShapes(int charId) {
 	}
 	delete[] in;
 
-	_screen->sega_getRenderer()->render(_screen->_curPage, 0, 0, 8, 12);
+	_screen->sega_getRenderer()->renderToPage(_screen->_curPage, 0, 0, 8, 12);
 	for (int i = first; i <= last; ++i) {
 		if (!_characters[i].flags)
 			continue;
@@ -609,7 +609,7 @@ void EoBEngine::drawMapPage(int level) {
 	_txt->printShadedText(_mapStrings3[level - 1], 0, 0, 0xCC, 0, 48, 16, 0, false);
 	_screen->setFontStyles(_screen->_currentFont, cs);
 	_screen->sega_loadTextBufferToVRAM(0, 0x7920, 384);
-	SegaRenderer *r = _screen->sega_getRenderer();
+	SCDRenderer *r = _screen->sega_getRenderer();
 	r->fillRectWithTiles(0, 23, 8, 6, 2, 0x63C9, true);
 
 	Common::SeekableReadStreamEndian *in = _sres->resStreamEndian(hasLevelMap(level) ? 2 + level : 2);
@@ -663,7 +663,7 @@ void EoBEngine::drawDialogueButtons() {
 	}
 
 	_screen->sega_loadTextBufferToVRAM(0, 0xA380, 7296);
-	_screen->sega_getRenderer()->render(0);
+	_screen->sega_getRenderer()->renderToPage(0);
 }
 
 GUI_EoB_SegaCD::GUI_EoB_SegaCD(EoBEngine *vm) : GUI_EoB(vm), _vm(vm), _clickableCharactersNumPages(vm->_textInputCharacterLinesSize) {
@@ -706,7 +706,7 @@ void GUI_EoB_SegaCD::initMemorizePrayMenu(int spellType) {
 	_screen->sega_clearTextBuffer(0);
 	_vm->_txt->printShadedText(_vm->_menuStringsSpells[spellType ? 17 : 14], 0, 2, 0xFF, 0xCC, 160, 16, 0, false);
 	_screen->sega_loadTextBufferToVRAM(0, 0x5060, 2560);
-	_screen->sega_getRenderer()->render(0, 1, 4, 20, 2);
+	_screen->sega_getRenderer()->renderToPage(0, 1, 4, 20, 2);
 }
 
 void GUI_EoB_SegaCD::initScribeScrollMenu() {
@@ -716,7 +716,7 @@ void GUI_EoB_SegaCD::initScribeScrollMenu() {
 	_screen->sega_clearTextBuffer(0);
 	_vm->_txt->printShadedText(getMenuString(48), 0, 3, 0xFF, 0xCC, 160, 16, 0, false);
 	_screen->sega_loadTextBufferToVRAM(0, 0x5060, 2560);
-	_screen->sega_getRenderer()->render(0, 1, 4, 20, 2);
+	_screen->sega_getRenderer()->renderToPage(0, 1, 4, 20, 2);
 }
 
 void GUI_EoB_SegaCD::printScribeScrollSpellString(const int16 *menuItems, int id, bool highlight) {
@@ -725,7 +725,7 @@ void GUI_EoB_SegaCD::printScribeScrollSpellString(const int16 *menuItems, int id
 	memset(buf, 0, sizeof(buf));
 	_vm->printSpellbookString(&buf[1], _vm->_mageSpellList[menuItems[id]], highlight ? 0x6223 : 0x63C9);
 	_screen->sega_getRenderer()->fillRectWithTiles(0, 1, 10 + id, 20, 1, 0,  true, false, buf);
-	_screen->sega_getRenderer()->render(0, 1, 10 + id, 20, 1);
+	_screen->sega_getRenderer()->renderToPage(0, 1, 10 + id, 20, 1);
 }
 
 void GUI_EoB_SegaCD::drawSaveSlotDialog(int x, int y, int id) {
@@ -742,7 +742,7 @@ void GUI_EoB_SegaCD::drawSaveSlotDialog(int x, int y, int id) {
 	_vm->_txt->printShadedText(_vm->_saveLoadStrings[2 + id], 0, 3, 0xFF, 0xCC, 160, 16, 0, false);
 	_screen->setFontStyles(_screen->_currentFont, cs);
 	_screen->sega_loadTextBufferToVRAM(0, 0x5060, 1280);
-	_screen->sega_getRenderer()->render(0, x >> 3, (y >> 3) + 1, 22, 21);
+	_screen->sega_getRenderer()->renderToPage(0, x >> 3, (y >> 3) + 1, 22, 21);
 }
 
 bool GUI_EoB_SegaCD::confirmDialogue(int id) {
@@ -761,7 +761,7 @@ bool GUI_EoB_SegaCD::confirmDialogue(int id) {
 	_screen->sega_loadTextBufferToVRAM(0, 0x5060, 10240);
 	_screen->sega_getRenderer()->fillRectWithTiles(0, 1, 0, 20, 20, 0);
 	_screen->sega_getRenderer()->fillRectWithTiles(0, 1, 5, 20, 8, 0x6283, true);
-	_screen->sega_getRenderer()->render(0, 1, 0, 22, 20);
+	_screen->sega_getRenderer()->renderToPage(0, 1, 0, 22, 20);
 	_screen->updateScreen();
 
 	Button *buttonList = initMenu(5);
@@ -828,7 +828,7 @@ void GUI_EoB_SegaCD::displayTextBox(int id, int textColor, bool wait) {
 	_screen->sega_loadTextBufferToVRAM(0, 0x5060, 3200);
 	_screen->setFontStyles(_screen->_currentFont, cs);
 	_screen->sega_getRenderer()->fillRectWithTiles(0, 1, 6, 20, 5, 0x6283, true);
-	_screen->sega_getRenderer()->render(0, 0, 1, 22, 19);
+	_screen->sega_getRenderer()->renderToPage(0, 0, 1, 22, 19);
 	_screen->updateScreen();
 	if (!wait)
 		return;
@@ -849,7 +849,7 @@ void GUI_EoB_SegaCD::drawMenuButton(Button *b, bool clicked, bool highlight, boo
 
 	_screen->sega_getRenderer()->loadToVRAM(&_campMenu[(0x1CE + t.srcOffs + (clicked ? 1 : 0) * ((b->width * b->height) >> 6)) << 5], (b->width * b->height) >> 1, t.nameTbl << 5);
 	_screen->sega_getRenderer()->fillRectWithTiles(0, b->x >> 3, b->y >> 3, b->width >> 3, b->height >> 3, 0x4000 + t.nameTbl, true);
-	_screen->sega_getRenderer()->render(0, b->x >> 3, b->y >> 3, b->width >> 3, b->height >> 3);
+	_screen->sega_getRenderer()->renderToPage(0, b->x >> 3, b->y >> 3, b->width >> 3, b->height >> 3);
 }
 
 void GUI_EoB_SegaCD::drawSaveSlotButton(int slot, int redrawBox, bool highlight) {
@@ -890,7 +890,7 @@ void GUI_EoB_SegaCD::drawSaveSlotButton(int slot, int redrawBox, bool highlight)
 	}
 
 	_screen->sega_loadTextBufferToVRAM(0, 0x5560, 4800);
-	_screen->sega_getRenderer()->render(0, (_saveSlotX >> 3) + (_saveSlotX ? 1 : 2), (_saveSlotY >> 3) + (_saveSlotY ? 6 : 7) + (slot << 1), 21, 2);
+	_screen->sega_getRenderer()->renderToPage(0, (_saveSlotX >> 3) + (_saveSlotX ? 1 : 2), (_saveSlotY >> 3) + (_saveSlotY ? 6 : 7) + (slot << 1), 21, 2);
 }
 
 int GUI_EoB_SegaCD::getHighlightSlot() {
@@ -929,14 +929,14 @@ void GUI_EoB_SegaCD::memorizePrayMenuPrintString(int spellId, int bookPageIndex,
 	} else {
 		_screen->sega_getRenderer()->fillRectWithTiles(0, 1, 10 + bookPageIndex, 20, 1, 0);
 	}
-	_screen->sega_getRenderer()->render(0, 1, 10 + bookPageIndex, 20, 1);
+	_screen->sega_getRenderer()->renderToPage(0, 1, 10 + bookPageIndex, 20, 1);
 }
 
 void GUI_EoB_SegaCD::updateOptionsStrings() {
 	uint16 ntblInputMode[3] = { 0x34C, 0x360, 0x30C };
 	int speed = _vm->_configMouse ? _vm->_mouseSpeed : _vm->_padSpeed;
 
-	SegaRenderer *r = _screen->sega_getRenderer();
+	SCDRenderer *r = _screen->sega_getRenderer();
 	r->loadToVRAM(&_campMenu[(0x1CE + (_vm->_configMouse ? 0x240 : 0x24C)) << 5], 0x180, 0x42E0);
 	r->loadToVRAM(&_campMenu[(0x1CE + (_vm->_configMusic ? 0x258 : 0x264)) << 5], 0x180, 0x4460);
 	r->loadToVRAM(&_campMenu[(0x1CE + (_vm->_configSounds ? 0x258 : 0x264)) << 5], 0x180, 0x45E0);
@@ -951,7 +951,7 @@ void GUI_EoB_SegaCD::updateOptionsStrings() {
 }
 
 void GUI_EoB_SegaCD::restParty_updateRestTime(int hours, bool init) {
-	SegaRenderer *r = _screen->sega_getRenderer();
+	SCDRenderer *r = _screen->sega_getRenderer();
 	if (init)
 		r->fillRectWithTiles(0, 1, 4, 20, 17, 0);
 	_screen->sega_clearTextBuffer(0);
@@ -965,7 +965,7 @@ void GUI_EoB_SegaCD::restParty_updateRestTime(int hours, bool init) {
 	_screen->sega_loadTextBufferToVRAM(0, 0x5060, 5120);
 	r->fillRectWithTiles(0, 1, 4, 20, 2, 0x6000);
 	r->fillRectWithTiles(0, 1, 6, 20, 6, 0x6283, true);
-	r->render(0, 0, 0, 22, 16);
+	r->renderToPage(0, 0, 0, 22, 16);
 	_screen->updateScreen();
 	_vm->delay(160);
 }
@@ -997,7 +997,7 @@ uint16 GUI_EoB_SegaCD::checkClickableCharactersSelection() {
 		printClickableCharacters(_clickableCharactersPage);
 		if (highlight != -1)
 			printClickableCharacter(highlight, 0x55);
-		_screen->sega_getRenderer()->render(0, 18, 10, 20, 14);
+		_screen->sega_getRenderer()->renderToPage(0, 18, 10, 20, 14);
 		_menuCur = highlight;
 	}
 
@@ -1040,7 +1040,7 @@ void GUI_EoB_SegaCD::printClickableCharacters(int page) {
 		printClickableCharacter(i, 0xFF);
 	for (int i = 200; i < 203; ++i)
 		printClickableCharacter(i, 0xFF);
-	_screen->sega_getRenderer()->render(0, 18, 10, 20, 14);
+	_screen->sega_getRenderer()->renderToPage(0, 18, 10, 20, 14);
 }
 
 void GUI_EoB_SegaCD::printClickableCharacter(int id, int col) {
diff --git a/engines/kyra/sequence/seqplayer_eob_segacd.cpp b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
index d08455149ff..da18e6ed544 100644
--- a/engines/kyra/sequence/seqplayer_eob_segacd.cpp
+++ b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
@@ -120,7 +120,7 @@ SegaSequencePlayer::~SegaSequencePlayer() {
 }
 
 bool SegaSequencePlayer::play(int id) {
-	_renderer->render(0);
+	_renderer->renderToPage(0);
 	_screen->sega_fadeToBlack(2);
 	_screen->clearPage(0);
 
@@ -273,7 +273,7 @@ void SegaSequencePlayer::run(const uint8 *data) {
 			_scrollManager->fastForward();
 		} else {
 			_scrollManager->updateScrollTimers();
-			_renderer->render(0);
+			_renderer->renderToPage(0);
 			_screen->sega_updatePaletteFaders(-1);
 			_screen->updateScreen();
 		}
@@ -557,7 +557,7 @@ void SegaSequencePlayer::s_orbZoomEffect(const uint8*) {
 		_renderer->loadToVRAM(_scaleOutBuffer, 0x5800, 0x2AA0);
 
 		if (!_fastForward) {
-			_renderer->render(0);
+			_renderer->renderToPage(0);
 			_screen->updateScreen();
 			_vm->delayUntil(nextFrame);
 		}
diff --git a/engines/kyra/sequence/seqplayer_eob_segacd.h b/engines/kyra/sequence/seqplayer_eob_segacd.h
index 4ff68836fcb..4ddd8e1f7cc 100644
--- a/engines/kyra/sequence/seqplayer_eob_segacd.h
+++ b/engines/kyra/sequence/seqplayer_eob_segacd.h
@@ -32,7 +32,7 @@ namespace Common {
 namespace Kyra {
 class EoBEngine;
 class Screen_EoB;
-class SegaRenderer;
+class SCDRenderer;
 class SegaCDResource;
 class SegaSequencePlayer {
 public:
@@ -92,7 +92,7 @@ private:
 
 	EoBEngine *_vm;
 	Screen_EoB *_screen;
-	SegaRenderer *_renderer;
+	SCDRenderer *_renderer;
 	SegaAnimator *_animator;
 	SegaCDResource *_res;
 	ScrollManager *_scrollManager;
diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp
index 900cd1f56cb..7a70f87bc98 100644
--- a/engines/kyra/sequence/sequences_eob.cpp
+++ b/engines/kyra/sequence/sequences_eob.cpp
@@ -2282,11 +2282,15 @@ int EoBEngine::mainMenuLoop() {
 		_screen->setScreenDim(28);
 		_gui->simpleMenu_setup(8, 0, _mainMenuStrings, -1, 0, 0, col1, col2, col3);
 		if (_flags.platform == Common::kPlatformSegaCD)
-			_screen->sega_getRenderer()->render(0);
+			_screen->sega_getRenderer()->renderToPage(0);
 		_screen->updateScreen();
 
 		while (sel == -1 && !shouldQuit())
 			sel = _gui->simpleMenu_process(8, _mainMenuStrings, 0, -1, 0);
+			if (_flags.platform == Common::kPlatformSegaCD)
+				_screen->sega_getRenderer()->renderToPage(0, 6, 20, 26, 5);
+			_screen->updateScreen();
+		}
 	} while ((sel < 0 || sel > 5) && !shouldQuit());
 
 	return sel + 1;
@@ -2460,9 +2464,9 @@ void EoBEngine::seq_segaOpeningCredits(bool jumpToTitle) {
 	SegaRenderer *r = _screen->sega_getRenderer();
 
 	r->setPitch(128);
-	r->setPlaneTableLocation(SegaRenderer::kPlaneA, 0xE000);
+	r->setPlaneTableLocation(SCDRenderer::kPlaneA, 0xE000);
 	r->setupPlaneAB(1024, 256);
-	r->setHScrollMode(SegaRenderer::kHScroll1PixelRows);
+	r->setHScrollMode(SCDRenderer::kHScroll1PixelRows);
 
 	r->fillRectWithTiles(0, 0, 0, 40, 28, 0);
 	r->fillRectWithTiles(1, 0, 0, 128, 28, 1);
@@ -2478,7 +2482,7 @@ void EoBEngine::seq_segaOpeningCredits(bool jumpToTitle) {
 	delete in;
 
 	_screen->sega_selectPalette(50, 0, 0);
-	r->render(0);
+	r->renderToPage(0);
 
 	_allowSkip = true;
 	resetSkipFlag();
@@ -2496,7 +2500,7 @@ void EoBEngine::seq_segaOpeningCredits(bool jumpToTitle) {
 		r->loadStreamToVRAM(in, 32, !containerAlt);
 		delete in;
 
-		r->render(0);
+		r->renderToPage(0);
 		_screen->updateScreen();
 
 		_screen->sega_paletteOps(6, 0, 0);
@@ -2506,7 +2510,7 @@ void EoBEngine::seq_segaOpeningCredits(bool jumpToTitle) {
 			uint32 end = _system->getMillis() + 16;
 			updateScrollState(scrollTable, ii / 30);
 			r->loadToVRAM(scrollTable, 0x400, 0xD800);
-			r->render(0);
+			r->renderToPage(0);
 			_screen->updateScreen();
 			mod--;
 			delayUntil(end);
@@ -2522,7 +2526,7 @@ void EoBEngine::seq_segaOpeningCredits(bool jumpToTitle) {
 			uint32 end = _system->getMillis() + 16;
 			updateScrollState(scrollTable, ii / 10);
 			r->loadToVRAM(scrollTable, 0x400, 0xD800);
-			r->render(0);
+			r->renderToPage(0);
 			_screen->updateScreen();
 			mod++;
 			delayUntil(end);
@@ -2533,9 +2537,9 @@ void EoBEngine::seq_segaOpeningCredits(bool jumpToTitle) {
 
 	delete[] scrollTable;
 	_screen->sega_fadeToBlack(0);
-	r->setPlaneTableLocation(SegaRenderer::kPlaneA, 0xC000);
+	r->setPlaneTableLocation(SCDRenderer::kPlaneA, 0xC000);
 	r->setupPlaneAB(512, 512);
-	r->setHScrollMode(SegaRenderer::kHScrollFullScreen);
+	r->setHScrollMode(SCDRenderer::kHScrollFullScreen);
 	r->memsetVRAM(0xD800, 0, 0x400);
 	r->setPitch(64);
 	_screen->sega_selectPalette(0, 0);
@@ -2553,7 +2557,7 @@ void EoBEngine::seq_segaOpeningCredits(bool jumpToTitle) {
 	}
 	r->fillRectWithTiles(1, 0, 0, 40, 28, 1, true);
 	r->fillRectWithTiles(0, 0, 0, 40, 28, 0);
-	r->render(0);
+	r->renderToPage(0);
 	if (!(jumpToTitle || shouldQuit() || skipFlag()))
 		_screen->sega_fadeToNeutral(3);
 
@@ -2564,7 +2568,7 @@ void EoBEngine::seq_segaOpeningCredits(bool jumpToTitle) {
 	resetSkipFlag();
 
 	r->fillRectWithTiles(1, 0, 19, 40, 9, 1);
-	r->render(0);
+	r->renderToPage(0);
 	_screen->sega_fadeToNeutral(3);
 }
 
@@ -2575,7 +2579,7 @@ void EoBEngine::seq_segaFinalCredits() {
 	int temp = 0;
 	const uint8 *grid = _staticres->loadRawData(kEoB1CreditsTileGrid, temp);
 	const char *const *strings = _staticres->loadStrings(kEoB1CreditsStrings2, temp);
-	SegaRenderer *r = _screen->sega_getRenderer();
+	SCDRenderer *r = _screen->sega_getRenderer();
 	_screen->sega_fadeToBlack(0);
 	_screen->sega_selectPalette(7, 3, true);
 	_txt->clearDim(4);
@@ -2591,7 +2595,7 @@ void EoBEngine::seq_segaFinalCredits() {
 	r->memsetVRAM(32, 0xCC, 32);
 	r->loadToVRAM(grid, 64, 64);
 	r->memsetVRAM(0x140, 0, 0x7800);
-	r->render(0);
+	r->renderToPage(0);
 
 	delay(320);
 
@@ -2610,7 +2614,7 @@ void EoBEngine::seq_segaFinalCredits() {
 		for (int i = 0; i < 32; ++i) {
 			uint32 del = _system->getMillis() + 16;
 			scrMan->updateScrollTimers();
-			r->render(0);
+			r->renderToPage(0);
 			_screen->updateScreen();
 			delayUntil(del);
 		}
@@ -2668,7 +2672,7 @@ void EoBEngine::seq_segaFinalCredits() {
 	r->fillRectWithTiles(0, 0, 0, 40, 28, 0);
 	r->fillRectWithTiles(1, 0, 0, 40, 28, 0);
 	r->fillRectWithTiles(0, 14, 9, 12, 8, 0x45A0, true);
-	r->render(0);
+	r->renderToPage(0);
 
 	_screen->sega_fadeToNeutral(3);
 
@@ -2685,7 +2689,7 @@ void EoBEngine::seq_segaShowStats() {
 	if (shouldQuit())
 		return;
 
-	SegaRenderer *r = _screen->sega_getRenderer();
+	SCDRenderer *r = _screen->sega_getRenderer();
 	_txt->clearDim(5);
 
 	int styles = Font::kStyleFullWidth;
@@ -2752,7 +2756,7 @@ void EoBEngine::seq_segaShowStats() {
 	r->fillRectWithTiles(0, 0, 0, 40, 28, 0);
 	r->fillRectWithTiles(1, 0, 0, 40, 28, 0);
 	r->fillRectWithTiles(0, 0, 3, 40, 22, 0x4001, true);
-	r->render(0);
+	r->renderToPage(0);
 
 	// This is a custom palette that gets loaded at the beginning of the ending sequence.
 	// Aborting that sequence too early might lead to wrong colors here...
@@ -2789,7 +2793,7 @@ void EoBEngine::seq_segaSetupSequence(int sequenceId) {
 	_screen->clearPage(0);
 
 	// transposeScreenOutputY(0);
-	_screen->sega_getRenderer()->setupWindowPlane(0, (sequenceId == 53 || sequenceId == 54) ? 23 : 18, SegaRenderer::kWinToRight, SegaRenderer::kWinToBottom);
+	_screen->sega_getRenderer()->setupWindowPlane(0, (sequenceId == 53 || sequenceId == 54) ? 23 : 18, SCDRenderer::kWinToRight, SCDRenderer::kWinToBottom);
 	_screen->sega_getRenderer()->memsetVRAM(0xD840, 0xEE, 512);
 	_screen->sega_getAnimator()->clearSprites();
 	_screen->setScreenDim(2);
@@ -2799,11 +2803,11 @@ void EoBEngine::seq_segaRestoreAfterSequence() {
 	if (_flags.platform != Common::kPlatformSegaCD)
 		return;
 
-	SegaRenderer *r = _screen->sega_getRenderer();
+	SCDRenderer *r = _screen->sega_getRenderer();
 	_screen->sega_fadeToBlack(1);
 	_screen->sega_getAnimator()->clearSprites();
 	_screen->sega_getAnimator()->update();
-	r->setupWindowPlane(0, 0, SegaRenderer::kWinToLeft, SegaRenderer::kWinToTop);
+	r->setupWindowPlane(0, 0, SCDRenderer::kWinToLeft, SCDRenderer::kWinToTop);
 	r->fillRectWithTiles(0, 0, 0, 40, 28, 0x2000);
 	r->fillRectWithTiles(1, 0, 0, 40, 28, 0x2000);
 	r->writeUint16VSRAM(0, 0);
diff --git a/engines/kyra/text/text_eob_segacd.cpp b/engines/kyra/text/text_eob_segacd.cpp
index a9fbee28a74..e1e49b47d4a 100644
--- a/engines/kyra/text/text_eob_segacd.cpp
+++ b/engines/kyra/text/text_eob_segacd.cpp
@@ -56,12 +56,12 @@ void TextDisplayer_SegaCD::printDialogueText(const char *str, bool wait) {
 	if (wait) {
 		printShadedText(str, 32, 12);
 		_engine->resetSkipFlag();
-		_renderer->render(0);
+		_renderer->renderToPage(0);
 		_screen->updateScreen();
 		_engine->delay(500);
 	} else {
 		printShadedText(str, 0, 0);
-		_renderer->render(0);
+		_renderer->renderToPage(0);
 		_screen->updateScreen();
 	}
 
@@ -168,7 +168,7 @@ void TextDisplayer_SegaCD::displayText(char *str, ...) {
 	if (tc != -1)
 		SWAP(_textColor, tc);
 
-	_renderer->render(Screen_EoB::kSegaRenderPage);
+	_renderer->renderToPage(Screen_EoB::kSegaRenderPage);
 	_screen->setFontStyles(Screen::FID_8_FNT, cs);
 	_screen->copyRegion(8, 176, 8, 176, 280, 24, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
 	_screen->sega_setTextBuffer(0, 0);
diff --git a/engines/kyra/text/text_eob_segacd.h b/engines/kyra/text/text_eob_segacd.h
index 4bc7cd8c143..66bb8bcb364 100644
--- a/engines/kyra/text/text_eob_segacd.h
+++ b/engines/kyra/text/text_eob_segacd.h
@@ -50,7 +50,7 @@ private:
 	void copyTextBufferLine(uint16 srcY, uint16 dstY, uint16 lineHeight, uint16 pitch);
 
 	Screen_EoB *_screen;
-	SegaRenderer *_renderer;
+	SCDRenderer *_renderer;
 	EoBEngine *_engine;
 	uint8 *_msgRenderBuffer;
 	uint32 _msgRenderBufferSize;
diff --git a/engines/snatcher/render_scd.cpp b/engines/snatcher/render_scd.cpp
index e392f73cc84..8fa59097fa6 100644
--- a/engines/snatcher/render_scd.cpp
+++ b/engines/snatcher/render_scd.cpp
@@ -24,15 +24,15 @@
 
 namespace Snatcher {
 
-class SegaRenderer : public Renderer {
+class SCDRenderer : public Renderer {
 public:
-	SegaRenderer() : Renderer() {}
-	~SegaRenderer() override {}
+	SCDRenderer() : Renderer() {}
+	~SCDRenderer() override {}
 private:
 };
 
 Renderer *Renderer::createSegaRenderer() {
-	return new SegaRenderer();
+	return new SCDRenderer();
 }
 
 } // End of namespace Snatcher
diff --git a/graphics/module.mk b/graphics/module.mk
index d512f4859af..9b79d9e7a0f 100644
--- a/graphics/module.mk
+++ b/graphics/module.mk
@@ -41,6 +41,7 @@ MODULE_OBJS := \
 	scaler/thumbnail_intern.o \
 	screen.o \
 	scaler/normal.o \
+	segagfx.o \
 	sjis.o \
 	surface.o \
 	svg.o \
diff --git a/graphics/segagfx.cpp b/graphics/segagfx.cpp
new file mode 100644
index 00000000000..3934819942b
--- /dev/null
+++ b/graphics/segagfx.cpp
@@ -0,0 +1,575 @@
+/* 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/segagfx.h"
+#include "graphics/surface.h"
+#include "common/rect.h"
+
+namespace Graphics {
+
+#if SEGA_PERFORMANCE
+#define mRenderLineFragment(hFlip, oddStart, oddEnd, useMask, dst, mask, src, start, end, pal) \
+{ \
+	int rlfOffs = 0; \
+	if (hFlip) \
+		rlfOffs |= 4; \
+	if (oddStart) \
+		rlfOffs |= 2; \
+	if (oddEnd) \
+		rlfOffs |= 1; \
+	if (useMask) \
+		(this->*_renderLineFragmentM[rlfOffs])(dst, mask, src, start, end, pal); \
+	else \
+		(this->*_renderLineFragmentD[rlfOffs])(dst, src, start, end, pal); \
+}
+#else
+#define mRenderLineFragment(hFlip, oddStart, oddEnd, useMask, dst, mask, src, start, end, pal) \
+{ \
+	if (hFlip) \
+		renderLineFragment<true>(dst, mask, src, start, end, pal); \
+	else \
+		renderLineFragment<false>(dst, mask, src, start, end, pal); \
+}
+#endif
+
+SegaRenderer::SegaRenderer() : _prioChainStart(0), _prioChainEnd(0), _pitch(64), _hScrollMode(0), _hScrollTable(0), _vScrollMode(0), _spriteTable(0), _numSpritesMax(0), _spriteMask(0), _backgroundColor(0)
+#if SEGA_PERFORMANCE
+, _renderLineFragmentD(0), _renderLineFragmentM(0)
+#endif
+{
+	_vram = new uint8[0x10000];
+	assert(_vram);
+	memset(_vram, 0, 0x10000 * sizeof(uint8));
+	_vsram = new uint16[40];
+	assert(_vsram);
+	memset(_vsram, 0, 40 * sizeof(uint16));
+
+#if SEGA_PERFORMANCE
+	static const SegaRenderer::renderFuncD funcD[8] = {
+		&SegaRenderer::renderLineFragmentD<false, false, false>,
+		&SegaRenderer::renderLineFragmentD<false, false, true>,
+		&SegaRenderer::renderLineFragmentD<false, true, false>,
+		&SegaRenderer::renderLineFragmentD<false, true, true>,
+		&SegaRenderer::renderLineFragmentD<true, false, false>,
+		&SegaRenderer::renderLineFragmentD<true, false, true>,
+		&SegaRenderer::renderLineFragmentD<true, true, false>,
+		&SegaRenderer::renderLineFragmentD<true, true, true>
+	};
+
+	static const SegaRenderer::renderFuncM funcM[8] = {
+		&SegaRenderer::renderLineFragmentM<false, false, false>,
+		&SegaRenderer::renderLineFragmentM<false, false, true>,
+		&SegaRenderer::renderLineFragmentM<false, true, false>,
+		&SegaRenderer::renderLineFragmentM<false, true, true>,
+		&SegaRenderer::renderLineFragmentM<true, false, false>,
+		&SegaRenderer::renderLineFragmentM<true, false, true>,
+		&SegaRenderer::renderLineFragmentM<true, true, false>,
+		&SegaRenderer::renderLineFragmentM<true, true, true>
+	};
+
+	_renderLineFragmentD = funcD;
+	_renderLineFragmentM = funcM;
+#endif
+
+	setResolution(320, 224);
+}
+
+SegaRenderer::~SegaRenderer() {
+	delete[] _vram;
+	delete[] _vsram;
+	delete[] _spriteMask;
+}
+
+void SegaRenderer::setResolution(int w, int h) {
+	assert(w == 320 || w == 256);
+	assert(h == 224 || h == 240);
+
+	_screenW = w;
+	_screenH = h;
+	_blocksW = w >> 3;
+	_blocksH = h >> 3;
+	_numSpritesMax = w >> 2;
+
+	delete[] _spriteMask;
+	_spriteMask = new uint8[w * h];
+	assert(_spriteMask);
+	memset(_spriteMask, 0, w * h * sizeof(uint8));
+}
+
+void SegaRenderer::setPlaneTableLocation(int plane, uint16 addr) {
+	assert(plane >= kPlaneA && plane <= kWindowPlane);
+	_planes[plane].nameTable = (uint16*)(&_vram[addr]);
+}
+
+void SegaRenderer::setupPlaneAB(int pixelWidth, int pixelHeigth) {
+	for (int i = 0; i < 2; ++i) {
+		if (pixelWidth != -1)
+			_planes[i].w = pixelWidth >> 3;
+		if (pixelHeigth != -1)
+			_planes[i].h = pixelHeigth >> 3;
+		_planes[i].mod = _planes[i].h;
+		_planes[i].nameTableSize = _planes[i].w * _planes[i].h;
+	}
+}
+
+void SegaRenderer::setupWindowPlane(int blockX, int blockY, int horizontalMode, int verticalMode) {
+	if (blockX != -1)
+		_planes[kWindowPlane].blockX = horizontalMode ? blockX : 0;
+	if (blockY != -1)
+		_planes[kWindowPlane].blockY = verticalMode ? blockY : 0;
+	_planes[kWindowPlane].w = horizontalMode ? _blocksW - blockX : blockX;
+	_planes[kWindowPlane].h = verticalMode ? _blocksH - blockY : blockY;
+	_planes[kWindowPlane].mod = _planes[kWindowPlane].blockY + _planes[kWindowPlane].h;
+	_planes[kWindowPlane].nameTableSize = _planes[kWindowPlane].w * _planes[kWindowPlane].h;
+}
+
+void SegaRenderer::setHScrollTableLocation(int addr) {
+	assert(addr <= 0xFFFF);
+	_hScrollTable = (uint16*)(&_vram[addr]);
+}
+
+void SegaRenderer::setSpriteTableLocation(int addr) {
+	assert(addr <= 0xFFFF);
+	_spriteTable = (uint16*)(&_vram[addr]);
+}
+
+void SegaRenderer::setPitch(int pitch) {
+	_pitch = pitch;
+}
+
+void SegaRenderer::setHScrollMode(int mode) {
+	_hScrollMode = mode;
+}
+
+void SegaRenderer::setVScrollMode(int mode) {
+	_vScrollMode = mode;
+}
+
+void SegaRenderer::loadToVRAM(const void *data, uint16 len, uint16 addr) {
+	assert(data);
+	assert(addr + len <= 0x10000);
+	memcpy(_vram + addr, data, len);
+}
+
+void SegaRenderer::memsetVRAM(int addr, uint8 val, int len) {
+	assert(addr + len <= 0x10000);
+	memset(_vram + addr, val, len);
+}
+
+void SegaRenderer::writeUint16VSRAM(int addr, uint16 value) {
+	assert(addr < 80);
+	assert(!(addr & 1));
+	_vsram[addr >> 1] = value;
+}
+
+void SegaRenderer::writeUint8VRAM(int addr, uint8 value) {
+	assert(addr < 0x10000);
+	_vram[addr] = value;
+}
+
+void SegaRenderer::writeUint16VRAM(int addr, uint16 value) {
+	assert(addr < 0x10000);
+	*((uint16*)(_vram + addr)) = value;
+}
+
+void SegaRenderer::clearPlanes() {
+	for (int i = 0; i < 3; ++i) {
+		if (_planes[i].nameTableSize)
+			memset(_planes[i].nameTable, 0, _planes[i].nameTableSize * sizeof(uint16));
+	}
+}
+
+void SegaRenderer::render(Surface *surf, int renderBlockX, int renderBlockY, int renderBlockWidth, int renderBlockHeight, bool spritesOnly) {
+	if ((_screenW != surf->w) || (_screenH != surf->h))
+		error("SegaRenderer::render(): Render target surface needs to be have the same width and height as passed to SegaRenderer::setResolution().");
+
+	if (surf->format.bpp() > 1)
+		error("SegaRenderer::render(): Unsupported color mode in render target surface.");
+
+	render((uint8*)surf->getPixels(), renderBlockX, renderBlockY, renderBlockWidth, renderBlockHeight, spritesOnly);
+}
+
+void SegaRenderer::render(uint8 *dest, int renderBlockX, int renderBlockY, int renderBlockWidth, int renderBlockHeight, bool spritesOnly) {
+	if (renderBlockX == -1)
+		renderBlockX = 0;
+	if (renderBlockY == -1)
+		renderBlockY = 0;
+	if (renderBlockWidth == -1)
+		renderBlockWidth = _blocksW;
+	if (renderBlockHeight == -1)
+		renderBlockHeight = _blocksH;
+
+	assert(dest);
+	uint8 *renderBuffer = dest;
+
+	fillBackground(dest, renderBlockX, renderBlockY, renderBlockWidth, renderBlockHeight);
+
+	// Plane B
+	if (!spritesOnly)
+		renderPlanePart(kPlaneB, renderBuffer, renderBlockX, renderBlockY, renderBlockX + renderBlockWidth, renderBlockY + renderBlockHeight);
+
+	// Plane A (only draw if the nametable is not identical to that of plane B)
+	if (_planes[kPlaneA].nameTable != _planes[kPlaneB].nameTable && !spritesOnly) {
+		// If the window plane is active the rendering of plane A becomes more tedious because the window plane
+		// kind of replaces plane A in the space that is covered by it.
+		if (_planes[kWindowPlane].nameTableSize) {
+			SegaPlane *p = &_planes[kWindowPlane];
+			renderPlanePart(kPlaneA, renderBuffer, MAX<int>(0, renderBlockX), MAX<int>(0, renderBlockY), MIN<int>(p->blockX, renderBlockX + renderBlockWidth), MIN<int>(_blocksH, renderBlockY + renderBlockHeight));
+			renderPlanePart(kPlaneA, renderBuffer, MAX<int>(0, renderBlockX), MAX<int>(0, renderBlockY), MIN<int>(_blocksW, renderBlockX + renderBlockWidth), MIN<int>(p->blockY, renderBlockY + renderBlockHeight));
+			renderPlanePart(kPlaneA, renderBuffer, MAX<int>(p->blockX + p->w, renderBlockX), MAX<int>(0, renderBlockY), MIN<int>(_blocksW, renderBlockX + renderBlockWidth), MIN<int>(_blocksH, renderBlockY + renderBlockHeight));
+			renderPlanePart(kPlaneA, renderBuffer, MAX<int>(0, renderBlockX), MAX<int>(p->blockY + p->h, renderBlockY), MIN<int>(_blocksW, renderBlockX + renderBlockWidth), MIN<int>(_blocksH, renderBlockY + renderBlockHeight));
+		} else {
+			renderPlanePart(kPlaneA, renderBuffer, renderBlockX, renderBlockY, renderBlockX + renderBlockWidth, renderBlockY + renderBlockHeight);
+		}
+	}
+
+	// Window Plane
+	if (_planes[kWindowPlane].nameTableSize && !spritesOnly) {
+		SegaPlane *p = &_planes[kWindowPlane];
+		renderPlanePart(kWindowPlane, renderBuffer, MIN<int>(p->blockX, renderBlockX + renderBlockWidth), MIN<int>(p->blockY, renderBlockY + renderBlockHeight), MAX<int>(p->blockX + p->w, renderBlockX), MAX<int>(p->blockY + p->h, renderBlockY));
+	}
+
+	// Sprites
+	memset(_spriteMask, 0xFF, _screenW * _screenH * sizeof(uint8));
+	const uint16 *pos = _spriteTable;
+	for (int i = 0; i < _numSpritesMax && pos; ++i) {
+		int y = *pos++ & 0x3FF;
+		uint8 bH = ((*pos >> 8) & 3) + 1;
+		uint8 bW = ((*pos >> 10) & 3) + 1;
+		uint8 next = *pos++ & 0x7F;
+		uint16 pal = ((*pos >> 13) & 3) << 4;
+		bool prio = (*pos & 0x8000);
+		bool hflip = (*pos & 0x800);
+		bool vflip = (*pos & 0x1000);
+		uint16 tile = *pos++ & 0x7FF;
+		int x = *pos & 0x3FF;
+
+		// Sprite masking. Can't happen in EOB at least, since the animator automatically adds 128 to x and y coords for all sprites.
+		// If this triggers anywhere else it will need to be added...
+		assert(!(x == 0 && y >= 128));
+
+		assert(!hflip);
+		assert(!vflip);
+
+		x -= 128;
+		y -= 128;
+
+		/*if ((x >> 3) < renderBlockX) {
+			bW = MIN<int>(0, (int)bW - (renderBlockX - (x >> 3)));
+			x = (renderBlockX << 3);
+
+		}
+
+		if ((y >> 3) < renderBlockY) {
+			bH = MIN<int>(0, (int)bH - (renderBlockY - (y >> 3)));
+			y = (renderBlockY << 3);
+		}
+
+		bW = MIN<int>(bW, renderBlockWidth);
+		bH = MIN<int>(bH, renderBlockHeight);*/
+
+		uint8 *dst = renderBuffer + y * _screenW + x;
+		uint8 *msk = _spriteMask + y * _screenW + x;
+
+		for (int blX = 0; blX < bW; ++blX) {
+			uint8 *dst2 = dst;
+			uint8 *msk2 = msk;
+			for (int blY = 0; blY < bH; ++blY) {
+				renderSpriteTile(dst, msk, x + (blX << 3), y + (blY << 3), tile++, pal, vflip, hflip, prio);
+				dst += (_screenW << 3);
+				msk += (_screenW << 3);
+			}
+			dst = dst2 + 8;
+			msk = msk2 + 8;
+		}
+
+		pos = next ? &_spriteTable[next << 2] : 0;
+	}
+
+	// Priority Tiles
+	// Instead of going through all rendering passes for all planes again (only now drawing the
+	// prio tiles instead of the non-priority tiles) I have collected the data for the priority
+	// tiles on the way and put that data into a chain. Should be faster...
+	for (PrioTileRenderObj *e = _prioChainStart; e; e = e->_next)
+		mRenderLineFragment(e->_hflip, e->_start & 1, e->_end & 1, e->_mask, e->_dst, e->_mask, e->_src, e->_start, e->_end, e->_pal)
+
+	clearPrioChain();
+}
+
+void SegaRenderer::fillBackground(uint8 *dst, int blockX, int blockY, int blockW, int blockH) {
+	uint32 *pos = (uint32*)(dst + (blockY << 3) * _screenW + (blockX << 3));
+	blockW <<= 1;
+	blockH <<= 3;
+	int pitch = _screenW >> 2;
+
+	uint32 fill = (_backgroundColor << 24) | (_backgroundColor << 16) | (_backgroundColor << 8) | _backgroundColor;
+	while (blockH--) {
+		uint32 *pos2 = pos;
+		for (int x = 0; x < blockW; ++x)
+			*pos2++ = fill;
+		pos += pitch;
+	}
+}
+
+void SegaRenderer::renderPlanePart(int plane, uint8 *dstBuffer, int x1, int y1, int x2, int y2) {
+	SegaPlane *p = &_planes[plane];
+	uint8 *dst = dstBuffer + (y1 << 3) * _screenW + (x1 << 3);
+
+	for (int y = y1; y < y2; ++y) {
+		int hScrollTableIndex = (plane == kWindowPlane) ? -1 : (_hScrollMode == kHScrollFullScreen) ? plane : (y1 << 4) + plane;
+		uint8 *dst2 = dst;
+		for (int x = x1; x < x2; ++x) {
+			int vScrollTableIndex = (plane == kWindowPlane) ? -1 : (_vScrollMode == kVScrollFullScreen) ? plane : (x & ~1) + plane;
+			uint16 vscrNt = 0;
+			uint16 vscrPxStart = 0;
+			uint16 vscrPxEnd = 8;
+
+			if (vScrollTableIndex != -1) {
+				vscrNt = _vsram[vScrollTableIndex] & 0x3FF;
+				vscrPxStart = vscrNt & 7;
+				vscrNt >>= 3;
+			}
+
+			int ty = (vscrNt + y) % p->mod;
+
+			renderPlaneTile(dst, x, &p->nameTable[ty * _pitch], vscrPxStart, vscrPxEnd, hScrollTableIndex, _pitch);
+
+			if (vscrPxStart) {
+				ty = (ty + 1) % p->mod;
+				uint16 dstOffs = (vscrPxEnd - vscrPxStart) * _screenW;
+				vscrPxEnd = vscrPxStart;
+				vscrPxStart = 0;
+				renderPlaneTile(dst + dstOffs, x, &p->nameTable[ty * _pitch], vscrPxStart, vscrPxEnd, hScrollTableIndex, _pitch);
+			}
+			dst += 8;
+		}
+		dst = dst2 + (_screenW << 3);
+	}
+}
+
+void SegaRenderer::renderPlaneTile(uint8 *dst, int ntblX, const uint16 *ntblLine, int vScrollLSBStart, int vScrollLSBEnd, int hScrollTableIndex, uint16 pitch) {
+	for (int bY = vScrollLSBStart; bY < vScrollLSBEnd; ++bY) {
+		uint8 *dst2 = dst;
+		uint16 hscrNt = 0;
+		uint16 hscrPx = 0;
+
+		if (hScrollTableIndex != -1) {
+			hscrNt = (-_hScrollTable[hScrollTableIndex]) & 0x3FF;
+			hscrPx = hscrNt & 7;
+			hscrNt >>= 3;
+		}
+
+		const uint16 *pNt = &ntblLine[(ntblX + hscrNt) % pitch];
+		if (pNt < (const uint16*)(&_vram[0x10000])) {
+			uint16 nt = *pNt;
+			uint16 pal = ((nt >> 13) & 3) << 4;
+			bool hflip = (nt & 0x800);
+			int y = bY % 8;
+			if (nt & 0x1000) // vflip
+				y = 7 - y;
+
+			// We skip the priority tiles here and draw them later
+			if (nt & 0x8000)
+				initPrioRenderTask(dst, 0, &_vram[((nt & 0x7FF) << 5) + (y << 2) + (hscrPx >> 1)], hscrPx, 8, pal, hflip);
+			else
+				mRenderLineFragment(hflip, hscrPx & 1, 0, 0, dst, 0, &_vram[((nt & 0x7FF) << 5) + (y << 2) + (hscrPx >> 1)], hscrPx, 8, pal);
+		}
+
+		if (hscrPx) {
+			dst += (8 - hscrPx);
+			pNt = &ntblLine[(ntblX + hscrNt + 1) % pitch];
+			if (pNt < (const uint16*)(&_vram[0x10000])) {
+				uint16 nt = *pNt;
+				uint16 pal = ((nt >> 13) & 3) << 4;
+				bool hflip = (nt & 0x800);
+				int y = bY % 8;
+				if (nt & 0x1000) // vflip
+					y = 7 - y;
+
+				// We skip the priority tiles here and draw them later
+				if (nt & 0x8000)
+					initPrioRenderTask(dst, 0, &_vram[((nt & 0x7FF) << 5) + (y << 2)], 0, hscrPx, pal, hflip);
+				else
+					mRenderLineFragment(hflip, 0, hscrPx & 1, 0, dst, 0, &_vram[((nt & 0x7FF) << 5) + (y << 2)], 0, hscrPx, pal)
+			}
+		}
+
+		if (hScrollTableIndex != -1 && _hScrollMode == kHScroll1PixelRows)
+			hScrollTableIndex += 2;
+		dst = dst2 + _screenW;
+	}
+}
+
+#undef vflip
+
+void SegaRenderer::renderSpriteTile(uint8 *dst, uint8 *mask, int x, int y, uint16 tile, uint8 pal, bool vflip, bool hflip, bool prio) {
+	if (y <= -8 || y >= _screenH || x <= -8 || x >= _screenW)
+		return;
+
+	const uint8 *src = &_vram[tile << 5];
+	if (vflip)
+		src += 31;
+
+	if (y < 0) {
+		dst -= (y * _screenW);
+		mask -= (y * _screenW);
+	} if (x < 0) {
+		dst -= x;
+		mask -= x;
+	}
+
+	int xstart = CLIP<int>(-x, 0, 7);
+	int xend = CLIP<int>(_screenW - x, 0, 8);
+	src += (xstart >> 1);
+
+	int ystart = CLIP<int>(-y, 0, 7);
+	int yend = CLIP<int>(_screenH - y, 0, 8);
+	src += (ystart << 2);
+
+	for (int bY = ystart; bY < yend; ++bY) {
+		uint8 *dst2 = dst;
+		uint8 *msk2 = mask;
+
+		if (prio)
+			initPrioRenderTask(dst, mask, src, xstart, xend, pal, hflip);
+		else
+			mRenderLineFragment(hflip, xstart & 1, xend & 1, 1, dst, mask, src, xstart, xend, pal);
+
+		src += 4;
+		dst = dst2 + _screenW;
+		mask = msk2 + _screenW;
+	}
+}
+
+#if SEGA_PERFORMANCE
+template<bool hflip, bool oddStart, bool oddEnd> void SegaRenderer::renderLineFragmentM(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal) {
+	if (hflip)
+		src += ((end - 1 - start) >> 1);
+
+	for (int i = (end - start) >> 1; i; --i) {
+		uint8 col = hflip ? (oddEnd ? *src-- >> 4 : *src & 0x0F) : (oddStart ? *src++ & 0x0F : *src >> 4);
+		uint8 col2 = hflip ? (oddEnd ? *src & 0x0F : *src-- >> 4) : (oddStart ? *src >> 4 : *src++ & 0x0F);
+		if (col & *mask) {
+			*dst = pal | col;
+			*mask = 0;
+		}
+		dst++;
+		mask++;
+		if (col2 & *mask) {
+			*dst = pal | col2;
+			*mask = 0;
+		}
+		dst++;
+		mask++;
+	}
+	if (oddStart != oddEnd) {
+		uint8 col = hflip ? (oddEnd ? *src-- >> 4 : *src & 0x0F) : (oddStart ? *src++ & 0x0F : *src >> 4);
+		if (col & *mask) {
+			*dst = pal | col;
+			*mask = 0;
+		}
+		dst++;
+		mask++;
+	}
+}
+
+template<bool hflip, bool oddStart, bool oddEnd> void SegaRenderer::renderLineFragmentD(uint8 *dst, const uint8 *src, int start, int end, uint8 pal) {
+	if (hflip)
+		src += ((end - 1 - start) >> 1);
+
+	for (int i = (end - start) >> 1; i; --i) {
+		uint8 col = hflip ? (oddEnd ? *src-- >> 4 : *src & 0x0F) : (oddStart ? *src++ & 0x0F : *src >> 4);
+		uint8 col2 = hflip ? (oddEnd ? *src & 0x0F : *src-- >> 4) : (oddStart ? *src >> 4 : *src++ & 0x0F);
+		if (col)
+			*dst = pal | col;
+		dst++;
+		if (col2)
+			*dst = pal | col2;
+		dst++;
+	}
+	if (oddStart != oddEnd) {
+		uint8 col = hflip ? (oddEnd ? *src-- >> 4 : *src & 0x0F) : (oddStart ? *src++ & 0x0F : *src >> 4);
+		if (col)
+			*dst = pal | col;
+		dst++;
+	}
+}
+#else
+template<bool hflip> void SegaRenderer::renderLineFragment(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal) {
+	if (hflip) {
+		src += ((end - 1 - start) >> 1);
+		if (end & 1) {
+			start++;
+			end++;
+		}
+	}
+
+	if (mask) {
+		for (int bX = start; bX < end; ++bX) {
+			uint8 col = hflip ? ((bX & 1) ? *src-- >> 4 : *src & 0x0F) : ((bX & 1) ? *src++ & 0x0F : *src >> 4);
+			if (col & *mask) {
+				*dst = pal | col;
+				*mask = 0;
+			}
+			dst++;
+			mask++;
+		}
+	} else {
+		for (int bX = start; bX < end; ++bX) {
+			uint8 col = hflip ? ((bX & 1) ? *src-- >> 4 : *src & 0x0F) : ((bX & 1) ? *src++ & 0x0F : *src >> 4);
+			if (col)
+				*dst = pal | col;
+			dst++;
+		}
+	}
+}
+#endif
+
+#undef mRenderLineFragment
+
+void SegaRenderer::initPrioRenderTask(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal, bool hflip) {
+#if SEGA_USE_MEMPOOL
+	_prioChainEnd =	new (_prioRenderMemPool) PrioTileRenderObj(_prioChainEnd, dst, mask, src, start, end, pal, hflip);
+#else
+	_prioChainEnd = new PrioTileRenderObj(_prioChainEnd, dst, mask, src, start, end, pal, hflip);
+#endif
+	if (!_prioChainStart)
+		_prioChainStart = _prioChainEnd;
+}
+
+void SegaRenderer::clearPrioChain() {
+	while (_prioChainEnd) {
+		_prioChainEnd->_next = 0;
+		PrioTileRenderObj *e = _prioChainEnd->_pred;
+#if SEGA_USE_MEMPOOL
+		_prioRenderMemPool.deleteChunk(_prioChainEnd);
+#else
+		delete _prioChainEnd;
+#endif
+		_prioChainEnd = e;
+	}
+	_prioChainStart = 0;
+}
+
+} // End of namespace Graphics
+
diff --git a/graphics/segagfx.h b/graphics/segagfx.h
new file mode 100644
index 00000000000..79cea424388
--- /dev/null
+++ b/graphics/segagfx.h
@@ -0,0 +1,265 @@
+/* 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 GRAPHICS_SEGAGFX_H
+#define GRAPHICS_SEGAGFX_H
+
+#define SEGA_PERFORMANCE		true
+#define SEGA_USE_MEMPOOL		true
+
+#if SEGA_USE_MEMPOOL
+#include "common/memorypool.h"
+#endif
+
+namespace Graphics {
+
+struct Surface;
+
+/**
+ * SegaRenderer
+ *
+ * This class allows rendering the content of a virtual Sega VDP video ram (including all planes with their respective
+ * coords and scroll states and including sprites) to a "normal" CLUT8 buffer. It can be used as a base class to have
+ * better access to the vram (see e. g. EOB I SegaCD). The renderer handles the 4 palettes of the VDP (with 16 colors
+ * each) by flagging the pixels with (paletteNo << 4), e. g. a pixel of palette 1, color 5 will be rendered as 0x15.
+ * This has to be kept in mind when writing the palette code for your engine. Also, there is no parsing of control
+ * register opcodes to set up plane dimensions, table locations etc., since I didn't see any need for that. This has
+ * to be done through the public setup methods of the renderer.
+ */
+class SegaRenderer {
+public:
+	enum Plane {
+		kPlaneA = 0,
+		kPlaneB = 1,
+		kWindowPlane = 2
+	};
+
+	enum WindowMode {
+		kWinToLeft = 0,
+		kWinToTop = 0,
+		kWinToRight = 1,
+		kWinToBottom = 1
+	};
+
+	enum HScrollMode {
+		kHScrollFullScreen = 0,
+		kHScroll8PixelRows,
+		kHScroll1PixelRows
+	};
+
+	enum VScrollMode {
+		kVScrollFullScreen = 0,
+		kVScroll16PixelStrips
+	};
+
+public:
+	SegaRenderer();
+	~SegaRenderer();
+
+	/**
+	 * Set target render buffer resolution for internal purposes of this renderer only. It will not interact in any way with the ScummVM backend.
+	 * @param w:		pixel width, 320 or 256 allowed
+	 * @param h:		pixel height, 224 or 240 allowed
+	 */
+	void setResolution(int w, int h);
+
+	/**
+	 * Set address for a plane's nametable.
+	 * @param plane:	plane id (from Plane enum)
+	 * @param addr:		address in the vdp vram
+	 */
+	void setPlaneTableLocation(int plane, uint16 addr);
+
+	/**
+	 * Set pixel width and/or height for planes A and B.
+	 * @param pixelWidth:	1024, 512 or 256 allowed
+	 * @param pixelHeight:	1024, 512 or 256 allowed
+	 *
+	 * The hardware allows/demands separate modification of the vertical and horizontal properties.
+	 * To allow this without making another function one of the pixelWidth/pixelHeight parameters
+	 * can be set to -1 which will keep the existing value for that property.
+	 */
+	void setupPlaneAB(int pixelWidth, int pixelHeigth);
+
+	/**
+	 * Set pixel width and/or height and drawing mode for the window plane.
+	 * @param pixelWidth:		1024, 512 or 256 allowed
+	 * @param pixelHeight:		1024, 512 or 256 allowed
+	 * @param horizontalMode:	kWinToLeft or kWinToRight
+	 * @param verticalMode:		kWinToTop or kWinToBottom
+	 *
+	 * The hardware allows/demands separate modification of the vertical and horizontal properties.
+	 * To allow this without making another function one of the pixelWidth/pixelHeight parameters
+	 * can be set to -1 which will keep the existing value for that property.
+	 */
+	void setupWindowPlane(int blockX, int blockY, int horizontalMode, int verticalMode);
+
+	/**
+	 * Set address for the horizontal scroll table.
+	 * @param addr:		address in the vdp vram
+	 */
+	void setHScrollTableLocation(int addr);
+
+	/**
+	 * Set address for the sprite table.
+	 * @param addr:		address in the vdp vram
+	 */
+	void setSpriteTableLocation(int addr);
+
+	/**
+	 * Set plane pitch in (8 pixels wide) blocks.
+	 * @param pitch:	pitch
+	 */
+	void setPitch(int pitch);
+
+	/**
+	 * Set horizontal scroll mode.
+	 * @param mode:		see enum HScrollMode
+	 */
+	void setHScrollMode(int mode);
+
+	/**
+	 * Set vertical scroll mode.
+	 * @param mode:		see enum VScrollMode
+	 */
+	void setVScrollMode(int mode);
+
+	/**
+	 * Load data from a buffer to a vram address.
+	 * @param data:		source data ptr
+	 * @param len:		byte size of data
+	 * @param addr:		target address in the vdp vram
+	 */
+	void loadToVRAM(const void *data, uint16 len, uint16 addr);
+
+	/**
+	 * Fill vram area with bytes of the specified value.
+	 * @param addr:		target address in the vdp vram
+	 * @param val:		fill value
+	 * @param len:		byte size of fill area
+	 */
+	void memsetVRAM(int addr, uint8 val, int len);
+
+	/**
+	 * Write uint16 value into vs ram. No endianness corrections take place here.
+	 * @param addr:		target address in the vdp vs ram
+	 * @param val:		value
+	 */
+	void writeUint16VSRAM(int addr, uint16 value);
+
+	/**
+	 * Write uint8 value into vram.
+	 * @param addr:		target address in the vdp vram
+	 * @param val:		value
+	 */
+	void writeUint8VRAM(int addr, uint8 value);
+
+	/**
+	 * Write uint16 value into vram. No endianness corrections take place here.
+	 * @param addr:		target address in the vdp vram
+	 * @param val:		value
+	 */
+	void writeUint16VRAM(int addr, uint16 value);
+
+	/**
+	 * Fills all planes with 0.
+	 */
+	void clearPlanes();
+
+	/**
+	 * Render vram in its current state to the target buffer/surface.
+	 * @param surf/dest:		target surface or buffer
+	 * @param renderBlockX, renderBlockY, renderBlockWidth, renderBlockHeight:
+								block (8x8 pixels) coordinates of target render area. Values of -1 for maximum range / full screen rendering.
+	 * @param spritesOnly:		skip rendering of the planes and only render the sprites.
+	 */
+	void render(Surface *surf, int renderBlockX = -1, int renderBlockY = -1, int renderBlockWidth = -1, int renderBlockHeight = -1, bool spritesOnly = false);
+	void render(uint8 *dest, int renderBlockX = -1, int renderBlockY = -1, int renderBlockWidth = -1, int renderBlockHeight = -1, bool spritesOnly = false);
+
+protected:
+	uint16 _screenW, _screenH, _blocksW, _blocksH;
+	uint8 *_vram;
+	uint16 *_vsram;
+	uint16 _pitch;
+
+private:
+	void fillBackground(uint8 *dst, int x, int y, int w, int h);
+	void renderPlanePart(int plane, uint8 *dstBuffer, int x1, int y1, int x2, int y2);
+	void renderPlaneTile(uint8 *dst, int destX, const uint16 *nameTable, int vScrollLSBStart, int vScrollLSBEnd, int hScrollTableIndex, uint16 pitch);
+	void renderSpriteTile(uint8 *dst, uint8 *mask, int x, int y, uint16 tile, uint8 pal, bool vflip, bool hflip, bool prio);
+#if SEGA_PERFORMANCE
+	template<bool hflip, bool oddStart, bool oddEnd> void renderLineFragmentM(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal);
+	template<bool hflip, bool oddStart, bool oddEnd> void renderLineFragmentD(uint8 *dst, const uint8 *src, int start, int end, uint8 pal);
+	typedef void(SegaRenderer::*renderFuncM)(uint8*, uint8*, const uint8*, int, int, uint8);
+	typedef void(SegaRenderer::*renderFuncD)(uint8*, const uint8*, int, int, uint8);
+	const renderFuncM *_renderLineFragmentM;
+	const renderFuncD *_renderLineFragmentD;
+#else
+	template<bool hflip> void renderLineFragment(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal);
+#endif
+
+	void initPrioRenderTask(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal, bool hflip);
+	void clearPrioChain();
+
+	struct SegaPlane {
+		SegaPlane() : blockX(0), blockY(0), w(0), h(0), mod(0), nameTable(0), nameTableSize(0) {}
+		int blockX, blockY;
+		uint16 w, h, mod;
+		uint16 *nameTable;
+		uint16 nameTableSize;
+	};
+
+	SegaPlane _planes[3];
+	uint16 *_hScrollTable;
+	uint16 *_spriteTable;
+	uint8 *_spriteMask;
+	uint8 _hScrollMode;
+	uint8 _vScrollMode;
+	uint16 _numSpritesMax;
+	uint8 _backgroundColor;
+
+	struct PrioTileRenderObj {
+		PrioTileRenderObj(PrioTileRenderObj *chainEnd, uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal, bool hflip) :
+			_pred(chainEnd), _next(0), _dst(dst), _mask(mask), _src(src), _start(start), _end(end), _pal(pal), _hflip(hflip) {
+			if (_pred)
+				_pred->_next = this;
+		}
+		uint8 *_dst;
+		uint8 *_mask;
+		const uint8 *_src;
+		int _start;
+		int _end;
+		uint8 _pal;
+		bool _hflip;
+		PrioTileRenderObj *_pred;
+		PrioTileRenderObj *_next;
+	};
+
+#if SEGA_USE_MEMPOOL
+	Common::ObjectPool<PrioTileRenderObj> _prioRenderMemPool;
+#endif
+	PrioTileRenderObj *_prioChainStart, *_prioChainEnd;
+};
+
+} // End of namespace Graphics
+
+#endif // GRAPHICS_SEGAGFX_H


Commit: 6d90e0f57dede9a062c401c6bd90d4ce5c72538c
    https://github.com/scummvm/scummvm/commit/6d90e0f57dede9a062c401c6bd90d4ce5c72538c
Author: athrxx (athrxx at scummvm.org)
Date: 2022-07-14T14:59:10+02:00

Commit Message:
SNATCHER: start work on graphics code

More skeleton code. This is still in the early research/experiment stage. Add palette handling (this should actually be quite usable already).

Changed paths:
  A engines/snatcher/palette.h
  A engines/snatcher/palette_scd.cpp
  A engines/snatcher/palevent_scd.h
  A engines/snatcher/scene.h
  A engines/snatcher/scene/scene_d0.cpp
  A engines/snatcher/scene/scene_d1.cpp
  A engines/snatcher/scene/scene_d2.cpp
  A engines/snatcher/scene_imp.h
    engines/snatcher/detection.h
    engines/snatcher/detection_tables.h
    engines/snatcher/graphics.cpp
    engines/snatcher/graphics.h
    engines/snatcher/module.mk
    engines/snatcher/render.h
    engines/snatcher/render_scd.cpp
    engines/snatcher/resource.cpp
    engines/snatcher/resource.h
    engines/snatcher/snatcher.cpp
    engines/snatcher/snatcher.h


diff --git a/engines/snatcher/detection.h b/engines/snatcher/detection.h
index 87355bfc830..fe48b041113 100644
--- a/engines/snatcher/detection.h
+++ b/engines/snatcher/detection.h
@@ -36,6 +36,7 @@ struct GameDescription {
 	int soundOptions;
 	Common::Language lang;
 	Common::Platform platform;
+	bool isBigEndian;
 
 	// language overwrites of fan translations (only needed for multilingual games)
 	Common::Language fanLang;
diff --git a/engines/snatcher/detection_tables.h b/engines/snatcher/detection_tables.h
index 355d9ef4fdb..9aac02775d1 100644
--- a/engines/snatcher/detection_tables.h
+++ b/engines/snatcher/detection_tables.h
@@ -22,10 +22,10 @@
 
 namespace {
 
-#define FLAGS(id, sound) { id, sound, Common::UNK_LANG, Common::kPlatformUnknown, Common::UNK_LANG, Common::UNK_LANG }
-#define FLAGS_FAN(id, sound, fanLang, repLang) { id, sound, Common::UNK_LANG, Common::kPlatformUnknown, fanLang, repLang }
+#define FLAGS(id, a, b) { id, a, Common::UNK_LANG, Common::kPlatformUnknown, b, Common::UNK_LANG, Common::UNK_LANG }
+#define FLAGS_FAN(id, a, b, fanLang, repLang) { id, a, Common::UNK_LANG, Common::kPlatformUnknown, b, fanLang, repLang }
 
-#define SNATCHER_FLAGS(sound) FLAGS(Snatcher::GI_SNATCHER, sound)
+#define SNATCHER_FLAGS(a, b) FLAGS(Snatcher::GI_SNATCHER, a, b)
 
 const SnatcherGameDescription adGameDescs[] = {
 	{
@@ -39,10 +39,10 @@ const SnatcherGameDescription adGameDescs[] = {
 			ADGF_TESTING,
 			GUIO2(GUIO_NOSPEECHVOLUME, GUIO_MIDISEGACD)
 		},
-		SNATCHER_FLAGS(MDT_SEGACD)
+		SNATCHER_FLAGS(MDT_SEGACD, true)
 	},
 
-	{ AD_TABLE_END_MARKER, FLAGS(0, MDT_NONE) }
+	{ AD_TABLE_END_MARKER, FLAGS(0, MDT_NONE, false) }
 };
 
 const PlainGameDescriptor gameList[] = {
diff --git a/engines/snatcher/graphics.cpp b/engines/snatcher/graphics.cpp
index b6feb62a4e1..8fc493cefcb 100644
--- a/engines/snatcher/graphics.cpp
+++ b/engines/snatcher/graphics.cpp
@@ -20,18 +20,35 @@
  *
  */
 
+#include "snatcher/palette.h"
 #include "snatcher/render.h"
 #include "snatcher/graphics.h"
 
+#include "common/system.h"
+
 namespace Snatcher {
 
-GraphicsEngine::GraphicsEngine(Common::Platform platform) : _renderer(0) {
+GraphicsEngine::GraphicsEngine(OSystem *system, Common::Platform platform) : _system(system), _renderer(0) {
 	_renderer = Renderer::create(platform);
+	_palette = Palette::create(_system->getPaletteManager(), platform);
 	assert(_renderer);
 }
 
 GraphicsEngine::~GraphicsEngine() {
 	delete _renderer;
+	delete _palette;
+}
+
+void GraphicsEngine::enqueuePaletteEvent(const uint8 *data, uint32 curPos) {
+	_palette->enqueueEvent(data, curPos);
+}
+
+void GraphicsEngine::nextFrame() {
+	//if (!_skipManyEvents) {
+		//_blockPalEvent = false;
+		_palette->processEventQueue();
+	//}
+	_system->updateScreen();
 }
 
 } // End of namespace Snatcher
diff --git a/engines/snatcher/graphics.h b/engines/snatcher/graphics.h
index 1964a00b5a8..f9750182ec8 100644
--- a/engines/snatcher/graphics.h
+++ b/engines/snatcher/graphics.h
@@ -23,19 +23,31 @@
 #ifndef SNATCHER_GRAPHICS_H
 #define SNATCHER_GRAPHICS_H
 
+#include "snatcher/render.h"
 #include "common/platform.h"
 
+class OSystem;
+
 namespace Snatcher {
 
-class Renderer;
+class Palette;
 
 class GraphicsEngine {
 public:
-	GraphicsEngine(Common::Platform platform);
+	GraphicsEngine(OSystem *system, Common::Platform platform);
 	~GraphicsEngine();
 
+	void enqueuePaletteEvent(const uint8 *data, uint32 curPos);
+
+	void nextFrame();
+
+	uint16 screenWidth() const { return _renderer ? _renderer->screenWidth() : 0; }
+	uint16 screenHeight() const { return _renderer ? _renderer->screenHeight() : 0; }
+
 private:
 	Renderer *_renderer;
+	Palette *_palette;
+	OSystem *_system;
 };
 
 } // End of namespace Snatcher
diff --git a/engines/snatcher/module.mk b/engines/snatcher/module.mk
index fdce0b469e6..47240329921 100644
--- a/engines/snatcher/module.mk
+++ b/engines/snatcher/module.mk
@@ -3,8 +3,12 @@ MODULE := engines/snatcher
 MODULE_OBJS := \
 	graphics.o \
 	metaengine.o \
+	palette_scd.o \
 	render_scd.o \
 	resource.o \
+	scene/scene_d0.o \
+	scene/scene_d1.o \
+	scene/scene_d2.o \
 	snatcher.o \
 	sound.o \
 	sound_device_null.o \
diff --git a/engines/snatcher/palette.h b/engines/snatcher/palette.h
new file mode 100644
index 00000000000..a85863d3368
--- /dev/null
+++ b/engines/snatcher/palette.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 SNATCHER_PALETTE_H
+#define SNATCHER_PALETTE_H
+
+#include "common/platform.h"
+
+class PaletteManager;
+
+namespace Snatcher {
+
+class Palette {
+public:
+	Palette(PaletteManager *pm) : _palMan(pm) {}
+	virtual ~Palette() {}
+
+	virtual bool enqueueEvent(const uint8*, uint32) { return true; }
+	virtual void processEventQueue() {}
+
+protected:
+	PaletteManager *_palMan;
+
+private:
+	static Palette *createSegaPalette(PaletteManager *pm);
+
+public:
+	static Palette *create(PaletteManager *pm, Common::Platform platform) {
+		switch (platform) {
+		case Common::kPlatformSegaCD:
+			return createSegaPalette(pm);
+		default:
+			break;
+		};
+		return 0;
+	}
+};
+
+} // End of namespace Snatcher
+
+#endif // SNATCHER_PALETTE_H
diff --git a/engines/snatcher/palette_scd.cpp b/engines/snatcher/palette_scd.cpp
new file mode 100644
index 00000000000..b63c88d4ed0
--- /dev/null
+++ b/engines/snatcher/palette_scd.cpp
@@ -0,0 +1,391 @@
+/* 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 "snatcher/palette.h"
+#include "snatcher/palevent_scd.h"
+
+#include "common/algorithm.h"
+#include "common/array.h"
+#include "common/endian.h"
+#include "common/func.h"
+#include "common/textconsole.h"
+#include "graphics/palette.h"
+
+namespace Snatcher {
+
+class SCDPalette : public Palette {
+public:
+	SCDPalette(PaletteManager *pm);
+	~SCDPalette() override;
+
+	bool enqueueEvent(const uint8 *data, uint32 curPos) override;
+	void processEventQueue() override;
+
+private:
+	typedef Common::Functor1Mem<PalEventSCD*, void, SCDPalette> EventProc;
+	Common::Array<EventProc*> _eventProcs;
+
+	void event_palSet(PalEventSCD *evt);
+	void event_palFadeFromBlack(PalEventSCD *evt);
+	void event_palFadeToBlack(PalEventSCD *evt);
+	void event_palFadeFromWhite(PalEventSCD *evt);
+	void event_palFadeToWhite(PalEventSCD *evt);
+	void event_palCycle(PalEventSCD *evt);
+	void event_palFadeToColor(PalEventSCD *evt);
+	void event_palFade2(PalEventSCD *evt);
+	void event_palFadeFrom(PalEventSCD *evt);
+	void event_palClear(PalEventSCD *evt);
+
+	bool updateDelay(PalEventSCD *evt);
+	void fadeStep(uint16 *modColor, uint16 toR, uint16 toG, uint16 toB);
+
+	void updateSystemPalette();
+
+	PalEventSCD *_eventQueue;
+	PalEventSCD *_eventCurPos;
+
+	uint16 *_colors;
+	uint8 *_internalPalette;
+	uint8 _updateFlags;
+};
+
+SCDPalette::SCDPalette(PaletteManager *pm) : Palette(pm), _eventProcs(), _eventQueue(0), _eventCurPos(0), _colors(0), _internalPalette(0), _updateFlags(0) {
+#define P_OP(a)	_eventProcs.push_back(new EventProc(this, &SCDPalette::event_##a));
+	_eventProcs.push_back(0);
+	P_OP(palSet);
+	P_OP(palFadeFromBlack);
+	P_OP(palFadeToBlack);
+	P_OP(palFadeFromWhite);
+	P_OP(palFadeToWhite);
+	P_OP(palCycle);
+	P_OP(palFadeToColor);
+	P_OP(palFade2);
+	P_OP(palFadeFrom);
+	P_OP(palClear);
+#undef P_OP
+
+	_eventQueue = _eventCurPos = new PalEventSCD[12];
+	memset(_eventQueue, 0, 12 * sizeof(PalEventSCD));
+	_colors = new uint16[128];
+	memset(_colors, 0, 128 * sizeof(uint16));
+	_internalPalette = new uint8[256];
+	memset(_internalPalette, 0, 256 * sizeof(uint8));
+}
+
+SCDPalette::~SCDPalette() {
+	delete[] _eventQueue;
+	delete[] _colors;
+	delete[] _internalPalette;
+
+	for (Common::Array<EventProc*>::iterator i = _eventProcs.begin(); i != _eventProcs.end(); ++i)
+		delete *i;
+}
+
+bool SCDPalette::enqueueEvent(const uint8 *data, uint32 curPos) {
+	const uint8 *dataStart = data;
+	data = &data[curPos];
+
+	for (int i = 0; i < 12; ++i) {
+		if (++_eventCurPos > &_eventQueue[11])
+			_eventCurPos = _eventQueue;
+		if (_eventCurPos->cmd)
+			continue;
+
+		_eventCurPos->cmd = *data++;
+		_eventCurPos->delay = *data++;
+		_eventCurPos->countDown = 0;
+		_eventCurPos->destOffset = *data++;
+		_eventCurPos->len = (*data++) + 1;
+		_eventCurPos->srcOffsets = _eventCurPos->srcOffsetCur = (uint32*)data;
+		_eventCurPos->srcOrig = dataStart;
+		_eventCurPos->progress = 0;
+
+		if (_eventCurPos->cmd & 0x80) {
+			_eventCurPos->cmd &= ~0x80;
+			for (int ii = 0; ii < 12; ++ii) {
+				if (_eventQueue[ii].cmd == 6)
+					_eventQueue[ii].cmd = 0;
+			}
+		}
+		return true;
+	}
+
+	return false;
+}
+
+void SCDPalette::processEventQueue() {
+	PalEventSCD *p = _eventCurPos;
+	for (int i = 0; i < 12; ++i) {
+		if (++p > &_eventQueue[11])
+			p = _eventQueue;
+
+		if (!p->cmd)
+			continue;
+
+		if (p->cmd > _eventProcs.size())
+			error("SCDPalette::processEventQueue(): Invalid opcode 0x%02x", p->cmd);
+
+		if (_eventProcs[p->cmd]->isValid())
+			(*_eventProcs[p->cmd])(p);
+	}
+
+	//if (_updateFlags & 1)
+		updateSystemPalette();
+
+	//if (_updateFlags & 2)
+		updateSystemPalette();
+	_updateFlags = 0;
+}
+
+void SCDPalette::event_palSet(PalEventSCD *evt) {
+	if (updateDelay(evt))
+		return;
+
+	uint16 *dst = &_colors[evt->destOffset >> 1];
+	uint32 srcOffs = READ_BE_UINT32(evt->srcOffsetCur);
+	assert(srcOffs >= 0x28000);
+	const uint16 *src = (const uint16*)&evt->srcOrig[srcOffs - 0x28000];
+
+	for (int i = 0; i < evt->len; ++i)
+		*dst++ = READ_BE_UINT16(src++);
+
+	if (READ_BE_UINT16(++evt->srcOffsetCur) == 0xFFFF)
+		evt->cmd = 0;	
+}
+
+void SCDPalette::event_palFadeFromBlack(PalEventSCD *evt) {
+	uint16 *dst = &_colors[evt->destOffset >> 1];
+	Common::fill<uint16*, uint16>(dst, &dst[evt->len], 0);
+	evt->cmd = 9;
+	event_palFadeFrom(evt);
+}
+
+void SCDPalette::event_palFadeToBlack(PalEventSCD *evt) {
+	if (updateDelay(evt))
+		return;
+
+	uint16 *dst = &_colors[evt->destOffset >> 1];
+	for (int i = 0; i < evt->len; ++i) {
+		if (*dst & 0xF)
+			*dst -= 0x2;
+		if (*dst & 0xF0)
+			*dst -= 0x20;
+		if (*dst & 0xF00)
+			*dst -= 0x200;
+	}
+	if (++evt->progress == 8)
+		evt->cmd = 0;
+}
+
+void SCDPalette::event_palFadeFromWhite(PalEventSCD *evt) {
+	uint16 *dst = &_colors[evt->destOffset >> 1];
+	Common::fill<uint16*, uint16>(dst, &dst[evt->len], 0xEEE);
+	evt->cmd = 9;
+	event_palFadeFrom(evt);
+}
+
+void SCDPalette::event_palFadeToWhite(PalEventSCD *evt) {
+	if (updateDelay(evt))
+		return;
+
+	uint16 *dst = &_colors[evt->destOffset >> 1];
+	for (int i = 0; i < evt->len; ++i) {
+		if ((*dst & 0xF) < 0xE)
+			*dst += 0x2;
+		if ((*dst & 0xF0) < 0xE0)
+			*dst += 0x20;
+		if ((*dst & 0xF00) < 0xE00)
+			*dst += 0x200;
+	}
+	if (++evt->progress == 8)
+		evt->cmd = 0;
+}
+
+void SCDPalette::event_palCycle(PalEventSCD *evt) {
+	if (updateDelay(evt))
+		return;
+
+	uint16 *dst = &_colors[evt->destOffset >> 1];
+	uint32 srcOffs = READ_BE_UINT32(evt->srcOffsetCur);
+	assert(srcOffs >= 0x28000);
+	const uint16 *src = (const uint16*)&evt->srcOrig[srcOffs - 0x28000];
+
+	for (int i = 0; i < evt->len; ++i)
+		*dst++ = READ_BE_UINT16(src++);
+
+	if (READ_BE_UINT16(++evt->srcOffsetCur) == 0xFFFF)
+		evt->srcOffsetCur = evt->srcOffsets;
+}
+
+void SCDPalette::event_palFadeToColor(PalEventSCD *evt) {
+	if (updateDelay(evt))
+		return;
+
+	uint16 *dst = &_colors[evt->destOffset >> 1];
+	uint32 srcOffs = READ_BE_UINT32(evt->srcOffsetCur);
+	assert(srcOffs >= 0x28000);
+	uint16 col = READ_BE_UINT16(&evt->srcOrig[srcOffs - 0x28000]);
+
+	for (int i = 0; i < evt->len; ++i)
+		fadeStep(dst++, col & 0xF, col & 0xF0, col & 0xF00);
+
+	if (++evt->progress == 8)
+		evt->cmd = 0;
+}
+
+void SCDPalette::event_palFade2(PalEventSCD *evt) {
+	if (updateDelay(evt))
+		return;
+
+	uint16 *dst = &_colors[evt->destOffset >> 1];
+	uint32 srcOffs = READ_BE_UINT32(evt->srcOffsetCur);
+	assert(srcOffs >= 0x28000);
+	const uint16 *src = (const uint16*)&evt->srcOrig[srcOffs - 0x28000];
+
+	for (int i = 0; i < evt->len; ++i) {
+		uint16 col = READ_BE_UINT16(src++);
+		uint8 r = col & 0xF;
+		uint8 g = (col & 0xF0) >> 4;
+		uint8 b = (col & 0xF00) >> 8;
+		col = ((r + g + b) / 3) & 0xFFFE;		
+		fadeStep(dst++, col & 0xF, (col << 4) & 0xF0, (col << 8) & 0xF00);
+	}
+
+	if (++evt->progress == 8)
+		evt->cmd = 0;
+}
+
+void SCDPalette::event_palFadeFrom(PalEventSCD *evt) {
+	if (updateDelay(evt))
+		return;
+
+	uint16 *dst = &_colors[evt->destOffset >> 1];
+	uint32 srcOffs = READ_BE_UINT32(evt->srcOffsetCur);
+	assert(srcOffs >= 0x28000);
+	const uint16 *src = (const uint16*)&evt->srcOrig[srcOffs - 0x28000];
+
+	for (int i = 0; i < evt->len; ++i) {
+		uint16 col = READ_BE_UINT16(src++);
+		fadeStep(dst++, col & 0xF, col & 0xF0, col & 0xF00);
+	}
+
+	if (++evt->progress == 8)
+		evt->cmd = 0;
+}
+
+
+void SCDPalette::event_palClear(PalEventSCD *evt) {
+	if (updateDelay(evt))
+		return;
+
+	uint16 *dst = &_colors[evt->destOffset >> 1];
+	Common::fill<uint16*, uint16>(dst, &dst[evt->len], 0);
+	evt->cmd = 0;
+}
+
+bool SCDPalette::updateDelay(PalEventSCD *evt) {
+	if (evt->countDown) {
+		evt->countDown--;
+		return true;
+	}
+
+	evt->countDown = evt->delay;
+	_updateFlags |= ((evt->destOffset & 0x80) ? 2 : 1);
+	return false;
+}
+
+void SCDPalette::fadeStep(uint16 *modColor, uint16 toR, uint16 toG, uint16 toB) {
+	uint16 r = *modColor & 0xF;
+	uint16 g = *modColor & 0xF0;
+	uint16 b = *modColor & 0xF00;
+
+	if (r < toR)
+		r += 0x2;
+	else if (r > toR)
+		r -= 0x2;
+
+	if (g < toG)
+		g += 0x20;
+	else if (g > toG)
+		g -= 0x20;
+
+	if (b < toB)
+		b += 0x200;
+	else if (b > toB)
+		b -= 0x200;
+
+	*modColor = (r | g | b);
+}
+
+void SCDPalette::updateSystemPalette() {
+	const uint16 *src = &_colors[0 << 6];
+	uint8 *dst = _internalPalette;
+
+
+	/*uint8 rgbColors[48];
+	uint8 *dst = rgbColors;
+
+	if (srcPalID >= 31 && srcPalID <= 38) {
+		src = &_segaCustomPalettes[(srcPalID - 31) << 4];
+	} else if (srcPalID >= 0) {
+		int temp = 0;
+		const uint16 *palettes = _vm->staticres()->loadRawDataBe16(kEoB1PalettesSega, temp);
+		if (!palettes)
+			return;
+		src = &palettes[srcPalID << 4];
+	}
+	*/
+	// R: bits 1, 2, 3   G: bits 5, 6, 7   B: bits 9, 10, 11
+	for (int i = 0; i < 64; ++i) {
+		uint16 in = *src++;
+		//_segaCurPalette[dstPalID << 4 | i] = in;
+#if 0
+		static const uint8 col[8] = { 0, 52, 87, 116, 144, 172, 206, 255 };
+		*dst++ = col[(in & 0x00F) >> 1];
+		*dst++ = col[(in & 0x0F0) >> 5];
+		*dst++ = col[(in & 0xF00) >> 9];
+#else
+		*dst++ = ((in & 0x00F) >> 1) * 255 / 7;
+		*dst++ = ((in & 0x0F0) >> 5) * 255 / 7;
+		*dst++ = ((in & 0xF00) >> 9) * 255 / 7;
+#endif
+	}
+
+	/*getPalette(0).copy(rgbColors, 0, 16, dstPalID << 4);
+
+	if (_specialColorReplace) {
+		const uint8 swapColors[6] = { 0x08, 0x09, 0x0C, 0x0D, 0x0E, 0x0F };
+		for (int i = 0; i < 6; ++i)
+			getPalette(0).copy(getPalette(0), 0x10 | swapColors[i], 1, swapColors[i]);
+	}
+
+	if (set)
+		setScreenPalette(getPalette(0));
+		*/
+	_palMan->setPalette(_internalPalette, 0, 256);
+}
+
+Palette *Palette::createSegaPalette(PaletteManager *pm) {
+	return new SCDPalette(pm);
+}
+
+} // End of namespace Snatcher
diff --git a/engines/snatcher/palevent_scd.h b/engines/snatcher/palevent_scd.h
new file mode 100644
index 00000000000..fd162bbf908
--- /dev/null
+++ b/engines/snatcher/palevent_scd.h
@@ -0,0 +1,47 @@
+/* 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 SNATCHER_PALEVENT_SCD_H
+#define SNATCHER_PALEVENT_SCD_H
+
+#include "common/scummsys.h"
+
+namespace Snatcher {
+
+struct PalEventSCD {
+	uint8 cmd;
+	uint8 delay;
+	uint8 countDown;
+	uint8 destOffset;
+	uint8 len;
+	const uint32 *srcOffsetCur;
+	const uint32 *srcOffsets;
+	const uint8 *srcOrig;
+	uint8 progress;
+	uint8 fD;
+	uint8 fE;
+	uint8 fF;
+};
+
+} // End of namespace Snatcher
+
+#endif // SNATCHER_PALEVENT_SCD_H
diff --git a/engines/snatcher/render.h b/engines/snatcher/render.h
index 9d72d402089..58cb065e9ab 100644
--- a/engines/snatcher/render.h
+++ b/engines/snatcher/render.h
@@ -31,6 +31,8 @@ class Renderer {
 public:
 	virtual ~Renderer() {}
 
+	virtual uint16 screenWidth() const = 0;
+	virtual uint16 screenHeight() const = 0;
 protected:
 	Renderer() {}
 
diff --git a/engines/snatcher/render_scd.cpp b/engines/snatcher/render_scd.cpp
index 8fa59097fa6..a762f34c262 100644
--- a/engines/snatcher/render_scd.cpp
+++ b/engines/snatcher/render_scd.cpp
@@ -26,9 +26,14 @@ namespace Snatcher {
 
 class SCDRenderer : public Renderer {
 public:
-	SCDRenderer() : Renderer() {}
+	SCDRenderer() : Renderer(), _screenWidth(320), _screenHeight(224) {}
 	~SCDRenderer() override {}
+
+	uint16 screenWidth() const { return _screenWidth; }
+	uint16 screenHeight() const { return _screenHeight; }
+
 private:
+	const uint16 _screenWidth, _screenHeight;
 };
 
 Renderer *Renderer::createSegaRenderer() {
diff --git a/engines/snatcher/resource.cpp b/engines/snatcher/resource.cpp
index d34cfa1a767..a391dcb1c0d 100644
--- a/engines/snatcher/resource.cpp
+++ b/engines/snatcher/resource.cpp
@@ -21,9 +21,191 @@
  */
 
 #include "snatcher/resource.h"
+#include "snatcher/snatcher.h"
+#include "snatcher/scene.h"
+
+#include "common/stream.h"
 
 namespace Snatcher {
 
-///
+class EndianWrapper : public Common::SeekableReadStreamEndian {
+public:
+	EndianWrapper(Common::SeekableReadStream *stream, bool bigEndian, bool disposeAfterUse = true) : Common::SeekableReadStreamEndian(bigEndian), Common::ReadStreamEndian(bigEndian), _stream(stream), _dispose(disposeAfterUse) {}
+	~EndianWrapper() override { if (_dispose) delete _stream; }
+
+	// Common::Stream interface
+	bool err() const override { return _stream->err(); }
+
+	// Common::ReadStream interface
+	bool eos() const override { return _stream->eos(); }
+	uint32 read(void *dataPtr, uint32 dataSize) override { return _stream->read(dataPtr, dataSize); }
+
+	// Common::SeekableReadStream interface
+	int32 pos() const override { return _stream->pos(); }
+	int32 size() const override { return _stream->size(); }
+	bool seek(int32 offset, int whence = SEEK_SET) override { return _stream->seek(offset, whence); }
+
+private:
+	Common::SeekableReadStream *_stream;
+	bool _dispose;
+};
+
+FIO::FIO(SnatcherEngine *vm, bool isBigEndian) : _vm(vm), _bigEndianTarget(isBigEndian) {
+	_files.add("global_search", &SearchMan, 0, false);
+}
+
+FIO::~FIO() {
+}
+
+SceneResource *FIO::createSceneResource(int index) {
+	return (index >= 0 && index < _resFileListSize)  ? new SceneResource(_vm, this, _resFileList[index], index) : 0;
+}
+
+Common::SeekableReadStream *FIO::readStream(Common::String &file) {
+	return _files.createReadStreamForMember(file);
+}
+
+Common::SeekableReadStreamEndian *FIO::readStreamEndian(Common::String &file, EndianMode em) {
+	Common::SeekableReadStream *stream = readStream(file);
+	return stream ? new EndianWrapper(stream, (em == kForceBE) ? true : (em == kForceLE ? false : _bigEndianTarget)) : 0;
+}
+
+uint8 *FIO::fileData(Common::String &file, uint32 *fileSize) {
+	Common::SeekableReadStream *s = readStream(file);
+
+	uint32 size = s ? s->size() : 0;
+	if (fileSize)
+		*fileSize = size;
+
+	if (!s)
+		return 0;
+
+	uint8 *data = new uint8[size];
+	s->read(data, size);
+	delete s;
+
+	return data;
+}
+
+const char *FIO::_resFileList[97] = {
+	"DATA_A0.BIN",	"DATA_B0.BIN",	"DATA_D0.BIN",	"DATA_D1.BIN",	"DATA_D2.BIN",	"DATA_F0.BIN",	"DATA_F4.BIN",	"DATA_G0.BIN",
+	"DATA_H00.BIN",	"DATA_H01.BIN",	"DATA_H02.BIN",	"DATA_H03.BIN",	"DATA_H04.BIN",	"DATA_H05.BIN",	"DATA_H06.BIN",	"DATA_H07.BIN",
+	"DATA_H08.BIN",	"DATA_H09.BIN",	"DATA_H11.BIN",	"DATA_H12.BIN",	"DATA_H13.BIN",	"DATA_H14.BIN",	"DATA_H15.BIN",	"DATA_I0.BIN",
+	"DATA_J0.BIN",	"DATA_K1.BIN",	"DATA_M0.BIN",	"DATA_O0.BIN",	"DATA_P0.BIN",	"DATA_Q3.BIN",	"DATA_Q5.BIN",	"DATA_Q8.BIN",
+	"DATA_S0.BIN",	"DATA_S1.BIN",	"DATA_S2.BIN",	"DATA_T0.BIN",	"DATA_U0.BIN",	"DATA_Y00.BIN",	"DATA_Y01.BIN",	"DATA_Y03.BIN",
+	"DATA_Y04.BIN",	"DATA_Y05.BIN",	"DATA_Y06.BIN",	"DATA_Y07.BIN",	"DATA_Y08.BIN",	"DATA_Y09.BIN",	"DATA_Y10.BIN",	"DATA_Y11.BIN",
+	"DATA_Y12.BIN",	"DATA_Y13.BIN",	"DATA_Y14.BIN",	"DATA_Y15.BIN",	"DATA_Y16.BIN",	"FMWR_1.BIN",	"PCMDRMDT.BIN",	"PCMLD_01.BIN",
+	"PCMLT_01.BIN",	"SP00.BIN",		"SP01.BIN",		"SP02.BIN",		"SP03.BIN",		"SP04.BIN",		"SP05.BIN",		"SP06.BIN",
+	"SP07.BIN",		"SP08.BIN",		"SP09.BIN",		"SP10.BIN",		"SP11.BIN",		"SP12.BIN",		"SP13.BIN",		"SP14.BIN",
+	"SP15.BIN",		"SP16.BIN",		"SP17.BIN",		"SP18.BIN",		"SP19.BIN",		"SP20.BIN",		"SP21.BIN",		"SP22.BIN",
+	"SP23.BIN",		"SP24.BIN",		"SP25.BIN",		"SP26.BIN",		"SP27.BIN",		"SP28.BIN",		"SP29.BIN",		"SP30.BIN",
+	"SP31.BIN",		"SP32.BIN",		"SP33.BIN",		"SP34.BIN",		"SP35.BIN",		"SP36.BIN",		"SP37.BIN",		"SP38.BIN",
+	"SUBCODE.BIN"
+};
+
+const int FIO::_resFileListSize = ARRAYSIZE(_resFileList);
+
+SceneResource::SceneResource(SnatcherEngine *vm, FIO *fio, const char *resFile, int index) : _vm(vm), _fio(fio), _resFile(resFile), _resIndex(index), _data(0), _dataSize(0), _handler(0) {
+	_handler = _shList[_resIndex] ? (_shList[_resIndex])(_vm, this, _fio) : 0;
+
+	assert(!_resFile.empty());
+	_data = _fio->fileData(_resFile, &_dataSize);
+
+	/*Common::SeekableReadStreamEndian *s = _fio->readStreamEndian(_resFile);
+	if (!s)
+		error("SceneResource::loadData(): Failed to load file '%s', _resFile");
+	_data = _fio->*/
+
+	//s->skip(4);
+
+	/*uint32 tableStart = s->readUint32();
+	assert(tableStart > 0x28000);
+	tableStart -= 0x28000;
+	s->seek(tableStart);
+
+	uint16 dataStart = 0xFFFF;
+	uint16 pos = 0;
+	while (pos < dataStart) {
+		uint16 val = s->readUint16();
+		if (val < pos || (val + tableStart) >= s->size())
+			break;
+		pos += 2;
+		dataStart = MIN<int>(val, dataStart);
+	}
+	_tableSize = pos >> 1;
+	_table = new const uint8*[_tableSize];
+	*/
+	/*for (int i = 0; i < _tableSize; ++i) {
+		s->seek(tableStart + (i << 1));
+		s->seek(tableStart + s->readUint16());
+
+		int32 cmdStart = s->pos();
+
+		for (bool checkLen = (s->readSByte() >= 0); checkLen; checkLen = (s->readSByte() >= 0))
+			s->skip(s->readByte());
+
+		uint32 size = s->pos() - cmdStart + 1;
+		uint8 *data = new uint8[size];
+
+		s->seek(cmdStart);
+		s->read(data, size);
+		_table[i] = data;
+	}*/
+
+	//delete s;
+}
+
+SceneResource::~SceneResource() {
+	/*if (_table) {
+		for (uint16 i = 0; i < _tableSize; ++i)
+			delete _table[i];
+		delete[] _table;
+		_table = 0;
+		_tableSize = 0;
+	}*/
+
+	delete[] _data;
+	delete _handler;
+}
+
+const uint8 *SceneResource::getData(int offset) const {
+	assert(offset >= 0x28000);
+	return _data + offset - 0x28000;
+}
+
+const uint8 *SceneResource::getDataFromTable(int offset, int tableEntry) const {
+	const uint8 *data = getData(offset);
+	return data + READ_BE_UINT16(data + (tableEntry << 1));
+}
+
+const uint8 *SceneResource::getDataFromMainTable(int tableEntry) const {
+	return getDataFromTable(READ_BE_UINT32(getData(0x28004)), tableEntry);
+}
+
+void SceneResource::startScene() {
+	if (_handler)
+		(*_handler)();
+}
+
+#define S(id) &createSceneHandler_##id
+#define INVALD 0
+
+SceneResource::SHFactory *const SceneResource::_shList[97] = {
+	INVALD, INVALD, S(D0 ), S(D1 ), S(D2 ), INVALD, INVALD, INVALD,
+	INVALD, INVALD, INVALD, INVALD, INVALD, INVALD, INVALD, INVALD,
+	INVALD, INVALD, INVALD, INVALD, INVALD, INVALD, INVALD, INVALD,
+	INVALD, INVALD, INVALD, INVALD, INVALD, INVALD, INVALD, INVALD,
+	INVALD, INVALD, INVALD, INVALD, INVALD, INVALD, INVALD, INVALD,
+	INVALD, INVALD, INVALD, INVALD, INVALD, INVALD, INVALD, INVALD,
+	INVALD, INVALD, INVALD, INVALD, INVALD, INVALD, INVALD, INVALD,
+	INVALD, INVALD, INVALD, INVALD, INVALD, INVALD, INVALD, INVALD,
+	INVALD, INVALD, INVALD, INVALD, INVALD, INVALD, INVALD, INVALD,
+	INVALD, INVALD, INVALD, INVALD, INVALD, INVALD, INVALD, INVALD,
+	INVALD, INVALD, INVALD, INVALD, INVALD, INVALD, INVALD, INVALD,
+	INVALD, INVALD, INVALD, INVALD
+};
+
+#undef INVALD
+#undef S
 
 } // End of namespace Snatcher
diff --git a/engines/snatcher/resource.h b/engines/snatcher/resource.h
index 88023a577bf..915c1757e75 100644
--- a/engines/snatcher/resource.h
+++ b/engines/snatcher/resource.h
@@ -23,9 +23,88 @@
 #ifndef SNATCHER_RESOURCE_H
 #define SNATCHER_RESOURCE_H
 
+#include "common/fs.h"
+#include "common/scummsys.h"
+#include "common/str.h"
+
+namespace Common {
+class SeekableReadStream;
+class SeekableReadStreamEndian;
+} // End of namespace Common
+
 namespace Snatcher {
 
-///
+class SnatcherEngine;
+class SceneResource;
+class FIO;
+
+class SceneHandler {
+public:
+	SceneHandler(SnatcherEngine *vm, SceneResource *scn, FIO *fio) : _vm(vm), _scene(scn), _fio(fio) {}
+	virtual ~SceneHandler() {}
+	virtual void operator()() = 0;
+protected:
+	SnatcherEngine *_vm;
+	SceneResource *_scene;
+	FIO *_fio;
+};
+
+class SceneResource {
+	friend class FIO;
+public:
+	~SceneResource();
+
+	const uint8 *getData(int offset) const;
+	const uint8 *getDataFromTable(int offset, int tableEntry) const;
+	const uint8 *getDataFromMainTable(int tableEntry) const;
+
+	void startScene();
+
+private:
+	SceneResource(SnatcherEngine *vm, FIO *fio, const char *resFile, int index);
+
+	const uint8 *_data;
+	uint32 _dataSize;
+	//const uint8 **_table;
+	//uint16 _tableSize;
+
+	SnatcherEngine *_vm;
+	FIO *_fio;
+	const int _resIndex;
+	Common::String _resFile;
+	SceneHandler *_handler;
+
+	typedef SceneHandler*(SHFactory)(SnatcherEngine*, SceneResource*, FIO*);
+	static SHFactory *const _shList[97];
+};
+
+class FIO {
+public:
+	FIO(SnatcherEngine *vm, bool isBigEndian);
+	~FIO();
+
+	SceneResource *createSceneResource(int index);
+
+	enum EndianMode {
+		kPlatformEndianness = 0,
+		kForceLE,
+		kForceBE
+	};
+
+	Common::SeekableReadStream *readStream(Common::String &file);
+	Common::SeekableReadStreamEndian *readStreamEndian(Common::String &file, EndianMode em = kPlatformEndianness);
+	uint8 *fileData(Common::String &file, uint32 *fileSize);
+
+private:
+	Common::SearchSet _files;
+
+	SnatcherEngine *_vm;
+	bool _bigEndianTarget;
+
+private:
+	static const char *_resFileList[97];
+	static const int _resFileListSize;
+};
 
 } // End of namespace Snatcher
 
diff --git a/engines/snatcher/scene.h b/engines/snatcher/scene.h
new file mode 100644
index 00000000000..7a63f0e71e9
--- /dev/null
+++ b/engines/snatcher/scene.h
@@ -0,0 +1,44 @@
+/* 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 SNATCHER_SCENE_H
+#define SNATCHER_SCENE_H
+
+namespace Snatcher {
+
+class SceneHandler;
+class SnatcherEngine;
+class SceneResource;
+class FIO;
+
+#define declSHF(id) \
+	SceneHandler *createSceneHandler_##id(SnatcherEngine*, SceneResource*, FIO*)
+
+declSHF(D0);
+declSHF(D1);
+declSHF(D2);
+
+#undef declSHF
+
+} // End of namespace Snatcher
+
+#endif // SNATCHER_SCENE_H
diff --git a/engines/snatcher/scene/scene_d0.cpp b/engines/snatcher/scene/scene_d0.cpp
new file mode 100644
index 00000000000..ad502b28fbc
--- /dev/null
+++ b/engines/snatcher/scene/scene_d0.cpp
@@ -0,0 +1,50 @@
+/* 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 "snatcher/resource.h"
+#include "snatcher/scene_imp.h"
+
+namespace Snatcher {
+
+#define SCImp Scene_D0
+
+SH_HEAD_BEGIN(D0)
+// declarations
+SH_HEAD_END(D0)
+
+SH_IMP_CTOR(D0) {
+
+}
+
+SCImp::~SCImp() {
+
+}
+
+void SCImp::operator()() {
+
+}
+
+// functions
+
+#undef SCImp
+
+} // End of namespace Snatcher
diff --git a/engines/snatcher/scene/scene_d1.cpp b/engines/snatcher/scene/scene_d1.cpp
new file mode 100644
index 00000000000..2325ad519ca
--- /dev/null
+++ b/engines/snatcher/scene/scene_d1.cpp
@@ -0,0 +1,50 @@
+/* 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 "snatcher/resource.h"
+#include "snatcher/scene_imp.h"
+
+namespace Snatcher {
+
+#define SCImp Scene_D1
+
+SH_HEAD_BEGIN(D1)
+// declarations
+SH_HEAD_END(D1)
+
+SH_IMP_CTOR(D1) {
+
+}
+
+SCImp::~SCImp() {
+
+}
+
+void SCImp::operator()() {
+
+}
+
+// functions
+
+#undef SCImp
+
+} // End of namespace Snatcher
diff --git a/engines/snatcher/scene/scene_d2.cpp b/engines/snatcher/scene/scene_d2.cpp
new file mode 100644
index 00000000000..ce8e2158c8d
--- /dev/null
+++ b/engines/snatcher/scene/scene_d2.cpp
@@ -0,0 +1,50 @@
+/* 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 "snatcher/resource.h"
+#include "snatcher/scene_imp.h"
+
+namespace Snatcher {
+
+#define SCImp Scene_D2
+
+SH_HEAD_BEGIN(D2)
+// declarations
+SH_HEAD_END(D2)
+
+SH_IMP_CTOR(D2) {
+
+}
+
+SCImp::~SCImp() {
+
+}
+
+void SCImp::operator()() {
+
+}
+
+// functions
+
+#undef SCImp
+
+} // End of namespace Snatcher
diff --git a/engines/snatcher/scene_imp.h b/engines/snatcher/scene_imp.h
new file mode 100644
index 00000000000..540e0c91f97
--- /dev/null
+++ b/engines/snatcher/scene_imp.h
@@ -0,0 +1,43 @@
+/* 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 SNATCHER_SCENE_IMP_H
+#define SNATCHER_SCENE_IMP_H
+
+namespace Snatcher {
+
+#define SH_HEAD_BEGIN(id) \
+	class Scene_##id : public SceneHandler { \
+	public: \
+		Scene_##id(SnatcherEngine *vm, SceneResource *scn, FIO *fio); \
+		~Scene_##id() override; \
+		void operator()() override; \
+	private:
+
+#define SH_HEAD_END(id) }; \
+	SceneHandler *createSceneHandler_##id(SnatcherEngine *vm, SceneResource *scn, FIO *fio) { return new Scene_##id(vm, scn, fio); }
+#define SH_IMP_CTOR(id) \
+	Scene_##id::Scene_##id(SnatcherEngine *vm, SceneResource *scn, FIO *fio) : SceneHandler(vm, scn, fio)
+
+} // End of namespace Snatcher
+
+#endif // SNATCHER_SCENE_IMP_H
diff --git a/engines/snatcher/snatcher.cpp b/engines/snatcher/snatcher.cpp
index 5aef53b9128..92a1c93cffe 100644
--- a/engines/snatcher/snatcher.cpp
+++ b/engines/snatcher/snatcher.cpp
@@ -21,19 +21,26 @@
  */
 
 #include "snatcher/graphics.h"
+#include "snatcher/resource.h"
 #include "snatcher/snatcher.h"
 #include "snatcher/sound.h"
-#include "common/error.h"
+
+#include "common/events.h"
+#include "common/system.h"
+
+#include "engines/util.h"
 
 namespace Snatcher {
 
-SnatcherEngine::SnatcherEngine(OSystem *system, GameDescription &dsc) : Engine(system), _game(dsc), _gfx(0), _snd(0) {
+SnatcherEngine::SnatcherEngine(OSystem *system, GameDescription &dsc) : Engine(system), _game(dsc), _fio(0), _scene(0), _gfx(0), _snd(0) {
 
 }
 
 SnatcherEngine::~SnatcherEngine() {
+	delete _scene;
 	delete _gfx;
 	delete _snd;
+	delete _fio;
 }
 
 Common::Error SnatcherEngine::run() {
@@ -45,30 +52,198 @@ Common::Error SnatcherEngine::run() {
 	if (!initSound(_game.platform, _game.soundOptions))
 		return Common::Error(Common::kAudioDeviceInitFailed);
 
-	if (!initGraphics(_game.platform))
+	if (!initGfx(_game.platform))
 		return Common::Error(Common::kUnknownError);
 
 	return Common::Error(start() ? Common::kNoError : Common::kUnknownError);
 }
 
 bool SnatcherEngine::initResource() {
-	return true;
+	return (_fio = new FIO(this, _game.isBigEndian));
 }
 
-bool SnatcherEngine::initGraphics(Common::Platform platform) {
-	_gfx = new GraphicsEngine(platform);
-	return _gfx;
+bool SnatcherEngine::initGfx(Common::Platform platform) {
+	if (_gfx = new GraphicsEngine(_system, platform)) {
+		initGraphics(_gfx->screenWidth(), _gfx->screenHeight());
+		return true;
+	}
+	return false;
 }
 
 bool SnatcherEngine::initSound(Common::Platform platform, int soundOptions) {
-	_snd = new SoundEngine(platform, soundOptions);
-	return _snd;
+	return (_snd = new SoundEngine(platform, soundOptions));
 }
 
+struct Struct80 {
+	uint16 cmd;
+	uint8 f2;
+	uint8 f3;
+	uint8 f4;
+	uint8 f5;
+	uint32 f6;
+	uint32 fa;
+
+	uint16 fe;
+	uint8 f10;
+	uint8 f11;
+	uint16 f12;
+	uint8 f14;
+	uint8 f15;
+	uint8 f16;
+	uint8 f1c;
+
+	uint8 f25;
+	uint8 f26;
+	uint8 f27;
+	uint16 f28;
+	uint16 f2a;
+	uint8 f2c;
+	uint8 f2d;
+
+	uint32 offs_2e;
+	const uint8 *ptr_32;
+	const uint8 *ptr_36;
+
+	const uint8 *ptr_40;
+	const uint8 *ptr_44;
+	uint16 prev;
+	uint16 next;
+	uint16 next2;
+	uint8 f4e;
+	uint8 f4f;
+};
+
+Struct80 _s80[64];
+//memset(_s80, 0, 64 * sizeof(Struct80));
+uint8 _vc1_var_788A_flags;
+uint8 _vc1_var_78BC;
+uint8 _vc1_var_78BD;
+uint16 _vc1_var_78BE;
+const uint8 *_cpos_7880;
+bool _var_7886;
+uint8 _var_7893;
+
+uint16 _word_B6406;
+
 bool SnatcherEngine::start() {
+	_scene = _fio->createSceneResource(2);
+
+	const uint8 *scstart = _scene->getData(0x28000);
+	const uint8 *sc = _scene->getDataFromMainTable(0);
+	//
+	memset(_s80, 0, 64 * sizeof(Struct80));
+	//
+	for (uint8 cmd = *sc++; cmd != 0xFF; cmd = *sc++) {
+		uint8 len = *sc++;
+		const uint8 *next = sc + len;
+
+		switch (cmd) {
+		case 0:
+			_gfx->enqueuePaletteEvent(scstart, sc - scstart);
+			break;
+
+		case 1:
+			_vc1_var_788A_flags &= ~1;
+			_vc1_var_78BD = _vc1_var_78BC;
+			_vc1_var_78BE = 0;
+			_cpos_7880 = sc;
+			_var_7886 = true;
+			break;
+
+		case 2: {
+			while (sc < next) {
+				assert(*sc < ARRAYSIZE(_s80));
+				Struct80 *s = &_s80[*sc++];
+				if (s->cmd)
+					return 0;
+
+				s->cmd = 1;
+				s->f25 = *sc++;
+				s->f6 = READ_BE_UINT16(sc);
+				sc += 2;
+				s->fa = READ_BE_UINT16(sc);
+				sc += 2;
+				s->offs_2e = READ_BE_UINT32(sc);
+				sc += 4;
+				s->ptr_32 = sc + READ_BE_UINT16(sc) + 2;
+				sc += 2;
+				s->f28 = 0xFFFF;
+
+				uint8 v = *s->ptr_32;
+				for (bool l = true; l; ) {
+					if (v == 0xA1 || v == 0xA7) {
+						s->f16 = s->ptr_32[1];
+						v = s->ptr_32[4];
+					} else {
+						if (!(v & 0x80))
+							s->ptr_36 = _scene->getDataFromTable(s->offs_2e, v);
+						l = false;
+					}
+				}
+			}
+		} break;
+
+		case 3: 
+		case 4: {
+			while (sc < next) {
+				uint16 i1 = *sc++;
+				uint16 i2 = *sc++;
+				assert(i1 < ARRAYSIZE(_s80) && i2 < ARRAYSIZE(_s80));
+				Struct80 *s1 = &_s80[i1];
+				Struct80 *s2 = &_s80[i2];
+				s2->next2 = s1->next;
+				s1->next = i2;
+				s2->prev = i1;
+			}
+		} break;
+
+		case 5:
+		case 6:
+		case 7:
+			_var_7893 = sc[1];
+			break;
+		case 8:
+			_word_B6406 = READ_BE_UINT16(sc);
+			break;
+		case 9:
+			_vc1_var_78BC = sc[1];
+			break;
+		default:
+			error("Unknown opcode 0x%02x", cmd);
+		}
+
+		sc = next;
+	}
+
+	while (!shouldQuit()) {
+		uint32 nextFrame = _system->getMillis() + 16;
+
+		_gfx->nextFrame();
+
+		delayUntil(nextFrame);
+	}
+
 	return true;
 }
 
+void SnatcherEngine::delayUntil(uint32 end) {
+	uint32 cur = _system->getMillis();
+	while (!shouldQuit() && cur < end) {
+		updateEvents();
+		int ms = MIN<int>(end - cur, 4);
+		_system->delayMillis(ms);
+		cur = _system->getMillis();
+	} 
+}
+
+void SnatcherEngine::updateEvents() {
+	Common::Event evt;
+	while (_eventMan->pollEvent(evt)) {
+		/*
+		*/
+	}
+}
+
 void SnatcherEngine::registerDefaultSettings() {
 	//ConfMan.registerDefault("cdaudio", true);
 }
diff --git a/engines/snatcher/snatcher.h b/engines/snatcher/snatcher.h
index 6660f71dbb8..6beb1b8b7c7 100644
--- a/engines/snatcher/snatcher.h
+++ b/engines/snatcher/snatcher.h
@@ -32,7 +32,9 @@ class SnatcherMetaEngine;
 namespace Snatcher {
 
 class GraphicsEngine;
+class FIO;
 class SoundEngine;
+class SceneResource;
 
 class SnatcherEngine : public Engine {
 public:
@@ -43,9 +45,13 @@ private:
 	// Startup
 	Common::Error run() override;
 	bool initResource();
-	bool initGraphics(Common::Platform platform);
+	bool initGfx(Common::Platform platform);
 	bool initSound(Common::Platform platform, int soundOptions);
-	bool start();	
+
+	// Main loop
+	bool start();
+	void delayUntil(uint32 end);
+	void updateEvents();
 
 	// ConfigManager sync
 	void registerDefaultSettings();
@@ -58,6 +64,10 @@ private:
 	bool hasFeature(EngineFeature f) const override;
 	GameDescription _game;
 
+	// Resource
+	FIO *_fio;
+	SceneResource *_scene;
+
 	// Graphics
 	GraphicsEngine *_gfx;
 


Commit: a8e31cbd25419232672b28757bb4ddc49e9bcc1d
    https://github.com/scummvm/scummvm/commit/a8e31cbd25419232672b28757bb4ddc49e9bcc1d
Author: athrxx (athrxx at scummvm.org)
Date: 2022-07-14T14:59:10+02:00

Commit Message:
SNATCHER: cnt

Changed paths:
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/sequence/sequences_eob.cpp
    engines/snatcher/graphics.cpp
    engines/snatcher/metaengine.cpp
    engines/snatcher/palette.h
    engines/snatcher/palette_scd.cpp
    engines/snatcher/resource.cpp
    engines/snatcher/snatcher.cpp


diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index ef260d36c4a..f5641608ad7 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -2146,7 +2146,7 @@ int GUI_EoB::simpleMenu_process(int sd, const char *const *strings, void *b, int
 		if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
 			_vm->_txt->printShadedText(strings[simpleMenu_getMenuItem(currentItem, menuItemsMask, itemOffset)], 4, (sd == 8 ? 2 : 20) + currentItem * lineH, _menuTextColor, _menuShadowColor);
 			_vm->_txt->printShadedText(strings[simpleMenu_getMenuItem(newItem, menuItemsMask, itemOffset)], 4, (sd == 8 ? 2 : 20) + newItem * lineH, _menuHighlightColor, _menuShadowColor);
-			_screen->sega_getRenderer()->render(0, 6, 20, 26, 5);
+			_screen->sega_getRenderer()->renderToPage(0, 6, 20, 26, 5);
 		} else {
 			_screen->printText(strings[simpleMenu_getMenuItem(currentItem, menuItemsMask, itemOffset)], x, y + currentItem * lineH, _menuTextColor, 0);
 			_screen->printText(strings[simpleMenu_getMenuItem(newItem, menuItemsMask, itemOffset)], x, y + newItem * lineH, _menuHighlightColor, 0);
diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp
index 7a70f87bc98..d75bf6ffdfb 100644
--- a/engines/kyra/sequence/sequences_eob.cpp
+++ b/engines/kyra/sequence/sequences_eob.cpp
@@ -2287,10 +2287,6 @@ int EoBEngine::mainMenuLoop() {
 
 		while (sel == -1 && !shouldQuit())
 			sel = _gui->simpleMenu_process(8, _mainMenuStrings, 0, -1, 0);
-			if (_flags.platform == Common::kPlatformSegaCD)
-				_screen->sega_getRenderer()->renderToPage(0, 6, 20, 26, 5);
-			_screen->updateScreen();
-		}
 	} while ((sel < 0 || sel > 5) && !shouldQuit());
 
 	return sel + 1;
@@ -2461,7 +2457,7 @@ void EoBEngine::seq_xdeath() {
 
 void EoBEngine::seq_segaOpeningCredits(bool jumpToTitle) {
 	uint16 *scrollTable = new uint16[0x200]();
-	SegaRenderer *r = _screen->sega_getRenderer();
+	SCDRenderer *r = _screen->sega_getRenderer();
 
 	r->setPitch(128);
 	r->setPlaneTableLocation(SCDRenderer::kPlaneA, 0xE000);
diff --git a/engines/snatcher/graphics.cpp b/engines/snatcher/graphics.cpp
index 8fc493cefcb..b30a9c31de2 100644
--- a/engines/snatcher/graphics.cpp
+++ b/engines/snatcher/graphics.cpp
@@ -44,6 +44,10 @@ void GraphicsEngine::enqueuePaletteEvent(const uint8 *data, uint32 curPos) {
 }
 
 void GraphicsEngine::nextFrame() {
+	//if (!_skipManyEvents) {
+		//_renderer->processEventQueue();
+	//}
+
 	//if (!_skipManyEvents) {
 		//_blockPalEvent = false;
 		_palette->processEventQueue();
diff --git a/engines/snatcher/metaengine.cpp b/engines/snatcher/metaengine.cpp
index a66e8be2a61..c688cf400e1 100644
--- a/engines/snatcher/metaengine.cpp
+++ b/engines/snatcher/metaengine.cpp
@@ -167,7 +167,8 @@ SaveStateDescriptor SnatcherMetaEngine::querySaveMetaInfos(const char *target, i
 		}
 	}*/
 
-	SaveStateDescriptor desc(slot, Common::String());
+	Common::String dmy;
+	SaveStateDescriptor desc(this, slot, dmy);
 
 	// We don't allow quick saves (slot 990 till 998) to be overwritten.
 	// The same goes for the 'Autosave', which is slot 999. Slot 0 will also
diff --git a/engines/snatcher/palette.h b/engines/snatcher/palette.h
index a85863d3368..0d2da1b6385 100644
--- a/engines/snatcher/palette.h
+++ b/engines/snatcher/palette.h
@@ -36,6 +36,7 @@ public:
 
 	virtual bool enqueueEvent(const uint8*, uint32) { return true; }
 	virtual void processEventQueue() {}
+	virtual void clearEvents() {}
 
 protected:
 	PaletteManager *_palMan;
diff --git a/engines/snatcher/palette_scd.cpp b/engines/snatcher/palette_scd.cpp
index b63c88d4ed0..ce9fe82c783 100644
--- a/engines/snatcher/palette_scd.cpp
+++ b/engines/snatcher/palette_scd.cpp
@@ -39,6 +39,7 @@ public:
 
 	bool enqueueEvent(const uint8 *data, uint32 curPos) override;
 	void processEventQueue() override;
+	void clearEvents() override;
 
 private:
 	typedef Common::Functor1Mem<PalEventSCD*, void, SCDPalette> EventProc;
@@ -156,6 +157,10 @@ void SCDPalette::processEventQueue() {
 	_updateFlags = 0;
 }
 
+void SCDPalette::clearEvents() {
+	memset(_eventQueue, 0, 12 * sizeof(PalEventSCD));
+}
+
 void SCDPalette::event_palSet(PalEventSCD *evt) {
 	if (updateDelay(evt))
 		return;
diff --git a/engines/snatcher/resource.cpp b/engines/snatcher/resource.cpp
index a391dcb1c0d..08255b0fb12 100644
--- a/engines/snatcher/resource.cpp
+++ b/engines/snatcher/resource.cpp
@@ -41,9 +41,9 @@ public:
 	uint32 read(void *dataPtr, uint32 dataSize) override { return _stream->read(dataPtr, dataSize); }
 
 	// Common::SeekableReadStream interface
-	int32 pos() const override { return _stream->pos(); }
-	int32 size() const override { return _stream->size(); }
-	bool seek(int32 offset, int whence = SEEK_SET) override { return _stream->seek(offset, whence); }
+	int64 pos() const override { return _stream->pos(); }
+	int64 size() const override { return _stream->size(); }
+	bool seek(int64 offset, int whence = SEEK_SET) override { return _stream->seek(offset, whence); }
 
 private:
 	Common::SeekableReadStream *_stream;
diff --git a/engines/snatcher/snatcher.cpp b/engines/snatcher/snatcher.cpp
index 92a1c93cffe..72609f96ffe 100644
--- a/engines/snatcher/snatcher.cpp
+++ b/engines/snatcher/snatcher.cpp
@@ -25,6 +25,7 @@
 #include "snatcher/snatcher.h"
 #include "snatcher/sound.h"
 
+#include "common/endian.h"
 #include "common/events.h"
 #include "common/system.h"
 
@@ -91,7 +92,8 @@ struct Struct80 {
 	uint8 f15;
 	uint8 f16;
 	uint8 f1c;
-
+	uint32 f20;
+	uint8 f24;
 	uint8 f25;
 	uint8 f26;
 	uint8 f27;
@@ -125,6 +127,13 @@ uint8 _var_7893;
 
 uint16 _word_B6406;
 
+
+int16 _gg_A__, _gg_A__cntDwn;
+uint8 _gg_B;
+uint32 _gg_dword_787A;
+bool gfx1_sub1() { return true; }
+void gfx1_sub2() {}
+
 bool SnatcherEngine::start() {
 	_scene = _fio->createSceneResource(2);
 
@@ -220,6 +229,41 @@ bool SnatcherEngine::start() {
 
 		_gfx->nextFrame();
 
+
+	
+
+		for (Struct80 *p = _s80; p < &_s80[64]; ++p) {
+			_gg_A__cntDwn = _gg_A__;
+			if ((p->f1c || p->f26) && _gg_B)
+				continue;
+			for (bool lp = true; lp; ) {
+				if (p->cmd != 1) {
+					lp = false;
+				} else {
+					if (p->f25 & 4) {
+						if (p->f20 > _gg_dword_787A)
+							lp = false;
+						else
+							p->f25 &= ~4;
+					}
+					if (lp) {
+						if (p->f25 & 1) {
+							lp = false;
+						} else {
+							gfx1_sub2();
+							--_gg_A__cntDwn;
+							if (_gg_A__cntDwn < 0 || !p->f26)
+								lp = false;
+						}
+					}
+				}
+			} 
+		}
+
+		for (Struct80 *p = _s80; p < &_s80[64]; ++p) {
+
+		}
+
 		delayUntil(nextFrame);
 	}
 


Commit: d6026ab1e4d8a044a9a62b85e2fe00db17f0326f
    https://github.com/scummvm/scummvm/commit/d6026ab1e4d8a044a9a62b85e2fe00db17f0326f
Author: athrxx (athrxx at scummvm.org)
Date: 2022-07-14T14:59:10+02:00

Commit Message:
KYRA: (EOB II/PC98) - add detection entry

Changed paths:
    engines/kyra/detection_tables.h


diff --git a/engines/kyra/detection_tables.h b/engines/kyra/detection_tables.h
index cd1b18e4aac..5b89b29f786 100644
--- a/engines/kyra/detection_tables.h
+++ b/engines/kyra/detection_tables.h
@@ -66,6 +66,7 @@ namespace {
 #define EOB_PC98_FLAGS FLAGS(false, false, false, false, true, true, false, false, false, Kyra::GI_EOB1)
 #define EOB2_FLAGS FLAGS(false, false, false, false, false, false, false, false, false, Kyra::GI_EOB2)
 #define EOB2_FMTOWNS_FLAGS FLAGS(false, false, false, false, true, false, true, false, false, Kyra::GI_EOB2)
+#define EOB2_PC98_FLAGS FLAGS(false, false, false, false, true, false, false, false, false, Kyra::GI_EOB2)
 
 #define GAMEOPTION_KYRA3_AUDIENCE GUIO_GAMEOPTIONS1
 #define GAMEOPTION_KYRA3_SKIP     GUIO_GAMEOPTIONS2
@@ -2096,6 +2097,22 @@ const KYRAGameDescription adGameDescs[] = {
 		EOB2_FLAGS
 	},
 
+	{
+		{
+			"eob2",
+			0,
+			{
+				{ "AAD_LOGO.CPS", 0, "a0951ff3cce7fcbd57b8152278eac3eb", -1 },
+				{ 0, 0, 0, 0 }
+			},
+				Common::JA_JPN,
+				Common::kPlatformPC98,
+				ADGF_NO_FLAGS,
+				GUIO7(GUIO_NOSPEECH, GUIO_MIDIPC98, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_RENDERPC9821, GUIO_RENDERPC9801, GAMEOPTION_EOB_HPGRAPHS)
+		},
+		EOB2_PC98_FLAGS
+	},
+
 	{ AD_TABLE_END_MARKER, FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0) }
 };
 


Commit: 9d60e444a64ef72217abe0253ad29b82bac0b5c5
    https://github.com/scummvm/scummvm/commit/9d60e444a64ef72217abe0253ad29b82bac0b5c5
Author: athrxx (athrxx at scummvm.org)
Date: 2022-07-14T14:59:11+02:00

Commit Message:
KYRA: (EOB II/PC98) - add static resources

(also make some initial adjustments to the code, but it will throw an assert for now)

Changed paths:
  A devtools/create_kyradat/resources/eob2_pc98.h
  A devtools/create_kyradat/resources/eob2_pc98_japanese.h
    devtools/create_kyradat/create_kyradat.cpp
    devtools/create_kyradat/create_kyradat.h
    devtools/create_kyradat/games.cpp
    devtools/create_kyradat/resources.cpp
    devtools/create_kyradat/resources/eob1_segacd_english.h
    devtools/create_kyradat/resources/eob1_segacd_japanese.h
    devtools/create_kyradat/resources/eob2_fmtowns.h
    dists/engine-data/kyra.dat
    engines/kyra/engine/chargen.cpp
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/resource/resource.h
    engines/kyra/resource/staticres.cpp
    engines/kyra/sequence/sequences_darkmoon.cpp


diff --git a/devtools/create_kyradat/create_kyradat.cpp b/devtools/create_kyradat/create_kyradat.cpp
index 995ef535cc2..9b88f4f29c1 100644
--- a/devtools/create_kyradat/create_kyradat.cpp
+++ b/devtools/create_kyradat/create_kyradat.cpp
@@ -38,7 +38,7 @@
 
 
 enum {
-	kKyraDatVersion = 118
+	kKyraDatVersion = 119
 };
 
 const ExtractFilename extractFilenames[] = {
@@ -601,6 +601,10 @@ const ExtractFilename extractFilenames[] = {
 	{ kEoB2IntroAnimData41, kEoB2SequenceData, false },
 	{ kEoB2IntroAnimData42, kEoB2SequenceData, false },
 	{ kEoB2IntroAnimData43, kEoB2SequenceData, false },
+	{ kEoB2IntroAnimData44, kEoB2SequenceData, false },
+	{ kEoB2IntroAnimData45, kEoB2SequenceData, false },
+	{ kEoB2IntroAnimData46, kEoB2SequenceData, false },
+	{ kEoB2IntroAnimData47, kEoB2SequenceData, false },
 	{ kEoB2IntroShapes00, kEoB2ShapeData, false },
 	{ kEoB2IntroShapes01, kEoB2ShapeData, false },
 	{ kEoB2IntroShapes04, kEoB2ShapeData, false },
@@ -1049,7 +1053,8 @@ const ExtractFilename extractFilenames[] = {
 	{ kEoB2Config2431Strings, kStringList, true },
 	{ kEoBBaseTextInputCharacterLines, kStringList, true },
 	{ kEoBBaseTextInputSelectStrings, kStringList, true },
-	{ kEoB2FontDmpSearchTbl, kRawDataBe16, false },
+	{ kEoB2FontLookupTbl, kRawDataBe16, false },
+	{ kEoB2FontConvertTbl, kRawData, false },
 	{ kEoB2Ascii2SjisTables, kStringList, false },
 	{ kEoB2Ascii2SjisTables2, kStringList, false },
 	{ kEoBBaseSaveNamePatterns, kStringList, true },
diff --git a/devtools/create_kyradat/create_kyradat.h b/devtools/create_kyradat/create_kyradat.h
index 0c4e80d9271..e01ff61909b 100644
--- a/devtools/create_kyradat/create_kyradat.h
+++ b/devtools/create_kyradat/create_kyradat.h
@@ -756,6 +756,11 @@ enum kExtractID {
 	kEoB2IntroAnimData42,
 	kEoB2IntroAnimData43,
 
+	kEoB2IntroAnimData44,
+	kEoB2IntroAnimData45,
+	kEoB2IntroAnimData46,
+	kEoB2IntroAnimData47,
+
 	kEoB2IntroShapes00,
 	kEoB2IntroShapes01,
 	kEoB2IntroShapes04,
@@ -1052,7 +1057,8 @@ enum kExtractID {
 
 	kEoB2UtilMenuStrings,
 	kEoB2Config2431Strings,
-	kEoB2FontDmpSearchTbl,
+	kEoB2FontLookupTbl,
+	kEoB2FontConvertTbl,
 	kEoB2Ascii2SjisTables,
 	kEoB2Ascii2SjisTables2,
 	kEoB2PcmSoundEffectsIngame,
diff --git a/devtools/create_kyradat/games.cpp b/devtools/create_kyradat/games.cpp
index 4f0c9c8552c..5db4c989495 100644
--- a/devtools/create_kyradat/games.cpp
+++ b/devtools/create_kyradat/games.cpp
@@ -135,7 +135,7 @@ const Game eob2Games[] = {
 	{ kEoB2, kPlatformAmiga, kNoSpecial, EN_ANY },
 	{ kEoB2, kPlatformAmiga, kNoSpecial, DE_DEU },
 
-	//{ kEoB2, kPlatformPC98, kNoSpecial, JA_JPN },
+	{ kEoB2, kPlatformPC98, kNoSpecial, JA_JPN },
 
 	{ kEoB2, kPlatformFMTowns, kNoSpecial, JA_JPN },
 
@@ -3578,7 +3578,7 @@ const int eob2AmigaNeed[] = {
 
 	-1
 };
-/*
+
 const int eob2PC98Need[] = {
 	kEoBBaseChargenStrings1,
 	kEoBBaseChargenStrings2,
@@ -3746,9 +3746,6 @@ const int eob2PC98Need[] = {
 	kEoBBaseDscTelptrShpCoords,
 
 	kEoBBasePortalSeqData,
-	kEoBBaseManDef,
-	kEoBBaseManWord,
-	kEoBBaseManPrompt,
 
 	kEoBBaseDscMonsterFrmOffsTbl1,
 	kEoBBaseDscMonsterFrmOffsTbl2,
@@ -3821,14 +3818,18 @@ const int eob2PC98Need[] = {
 	kEoB2IntroAnimData41,
 	kEoB2IntroAnimData42,
 	kEoB2IntroAnimData43,
+	kEoB2IntroAnimData44,
+	kEoB2IntroAnimData45,
+	kEoB2IntroAnimData46,
+	kEoB2IntroAnimData47,
 
 	kEoB2IntroShapes00,
 	kEoB2IntroShapes01,
 	kEoB2IntroShapes04,
 	kEoB2IntroShapes07,
+	kEoB2IntroShapes13,
 
 	kEoB2FinaleStrings,
-	kEoB2CreditsData,
 	kEoB2FinaleCPSFiles,
 	kEoB2FinaleAnimData00,
 	kEoB2FinaleAnimData01,
@@ -3901,11 +3902,14 @@ const int eob2PC98Need[] = {
 	kRpgCommonDscDimMap,
 	kRpgCommonDscBlockIndex,
 
+	kEoB2Ascii2SjisTables,
+	kEoB2FontConvertTbl,
+
 	kEoBBaseSoundFilesIntro,
 	kEoBBaseSoundFilesFinale,
 
 	-1
-};*/
+};
 
 const int eob2FMTownsNeed[] = {
 	kEoBBaseChargenStrings1,
@@ -4459,7 +4463,7 @@ const int eob2FMTownsNeed[] = {
 	kEoB2Config2431Strings,
 	kEoBBaseTextInputCharacterLines,
 	kEoBBaseTextInputSelectStrings,
-	kEoB2FontDmpSearchTbl,
+	kEoB2FontLookupTbl,
 	kEoB2Ascii2SjisTables,
 	kEoB2Ascii2SjisTables2,
 	kEoBBaseSaveNamePatterns,
@@ -4529,7 +4533,7 @@ const GameNeed gameNeedTable[] = {
 
 	{ kEoB2, kPlatformDOS, kNoSpecial, eob2FloppyNeed },
 	{ kEoB2, kPlatformAmiga, kNoSpecial, eob2AmigaNeed },
-	//{ kEoB2, kPlatformPC98, kNoSpecial, eob2PC98Need },
+	{ kEoB2, kPlatformPC98, kNoSpecial, eob2PC98Need },
 	{ kEoB2, kPlatformFMTowns, kNoSpecial, eob2FMTownsNeed },
 
 	{ -1, -1, -1, nullptr }
diff --git a/devtools/create_kyradat/resources.cpp b/devtools/create_kyradat/resources.cpp
index 306a6c2ecb7..a9f275abb2c 100644
--- a/devtools/create_kyradat/resources.cpp
+++ b/devtools/create_kyradat/resources.cpp
@@ -131,8 +131,8 @@
 #include "resources/eob2_amiga.h"
 #include "resources/eob2_amiga_english.h"
 #include "resources/eob2_amiga_german.h"
-//#include "resources/eob2_pc98.h"
-//#include "resources/eob2_pc98_japanese.h"
+#include "resources/eob2_pc98.h"
+#include "resources/eob2_pc98_japanese.h"
 #include "resources/eob2_fmtowns.h"
 #include "resources/eob2_fmtowns_japanese.h"
 
@@ -4338,13 +4338,306 @@ static const ResourceProvider resourceProviders[] = {
 	{ kEoB2Config2431Strings, kEoB2, kPlatformFMTowns, kNoSpecial, JA_JPN, &kEoB2Config2431StringsFMTownsJapaneseProvider },
 	{ kEoBBaseTextInputCharacterLines, kEoB2, kPlatformFMTowns, kNoSpecial, JA_JPN, &kEoB2TextInputCharacterLinesFMTownsJapaneseProvider },
 	{ kEoBBaseTextInputSelectStrings, kEoB2, kPlatformFMTowns, kNoSpecial, JA_JPN, &kEoB2TextInputSelectStringsFMTownsJapaneseProvider },
-	{ kEoB2FontDmpSearchTbl, kEoB2, kPlatformFMTowns, kNoSpecial, UNK_LANG, &kEoB2FontDmpSearchTblFMTownsProvider },
+	{ kEoB2FontLookupTbl, kEoB2, kPlatformFMTowns, kNoSpecial, UNK_LANG, &kEoB2FontLookupTblFMTownsProvider },
 	{ kEoB2Ascii2SjisTables, kEoB2, kPlatformFMTowns, kNoSpecial, UNK_LANG, &kEoB2Ascii2SjisTablesFMTownsProvider },
 	{ kEoB2Ascii2SjisTables2, kEoB2, kPlatformFMTowns, kNoSpecial, UNK_LANG, &kEoB2Ascii2SjisTables2FMTownsProvider },
 	{ kEoBBaseSaveNamePatterns, kEoB2, kPlatformFMTowns, kNoSpecial, JA_JPN, &kEoB2SaveNamePatternsFMTownsJapaneseProvider },
 	{ kEoB2PcmSoundEffectsIngame, kEoB2, kPlatformFMTowns, kNoSpecial, UNK_LANG, &kEoB2PcmSoundEffectsIngameFMTownsProvider },
 	{ kEoB2PcmSoundEffectsIntro, kEoB2, kPlatformFMTowns, kNoSpecial, UNK_LANG, &kEoB2PcmSoundEffectsIntroFMTownsProvider },
 	{ kEoB2PcmSoundEffectsFinale, kEoB2, kPlatformFMTowns, kNoSpecial, UNK_LANG, &kEoB2PcmSoundEffectsFinaleFMTownsProvider },
+	{ kEoBBaseNpcPresetsNames, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2NpcPresetsNamesPC98JapaneseProvider },
+	{ kEoBBaseChargenStrings1, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2ChargenStrings1PC98JapaneseProvider },
+	{ kEoBBaseChargenStrings2, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2ChargenStrings2PC98JapaneseProvider },
+	{ kEoBBaseChargenStartLevels, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ChargenStartLevelsPC98Provider },
+	{ kEoBBaseChargenStatStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2ChargenStatStringsPC98JapaneseProvider },
+	{ kEoBBaseChargenRaceSexStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2ChargenRaceSexStringsPC98JapaneseProvider },
+	{ kEoBBaseChargenClassStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2ChargenClassStringsPC98JapaneseProvider },
+	{ kEoBBaseChargenAlignmentStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2ChargenAlignmentStringsPC98JapaneseProvider },
+	{ kEoBBaseChargenEnterGameStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2ChargenEnterGameStringsPC98JapaneseProvider },
+	{ kEoBBaseChargenClassMinStats, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ChargenClassMinStatsPC98Provider },
+	{ kEoBBaseChargenRaceMinStats, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ChargenRaceMinStatsPC98Provider },
+	{ kEoBBaseChargenRaceMaxStats, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ChargenRaceMaxStatsPC98Provider },
+	{ kEoBBaseSaveThrowTable1, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SaveThrowTable1PC98Provider },
+	{ kEoBBaseSaveThrowTable2, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SaveThrowTable2PC98Provider },
+	{ kEoBBaseSaveThrowTable3, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SaveThrowTable3PC98Provider },
+	{ kEoBBaseSaveThrowTable4, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SaveThrowTable4PC98Provider },
+	{ kEoBBaseSaveThrwLvlIndex, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SaveThrwLvlIndexPC98Provider },
+	{ kEoBBaseSaveThrwModDiv, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SaveThrwModDivPC98Provider },
+	{ kEoBBaseSaveThrwModExt, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SaveThrwModExtPC98Provider },
+	{ kEoBBasePryDoorStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2PryDoorStringsPC98JapaneseProvider },
+	{ kEoBBaseWarningStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2WarningStringsPC98JapaneseProvider },
+	{ kEoBBaseItemSuffixStringsRings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2ItemSuffixStringsRingsPC98JapaneseProvider },
+	{ kEoBBaseItemSuffixStringsPotions, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2ItemSuffixStringsPotionsPC98JapaneseProvider },
+	{ kEoBBaseItemSuffixStringsWands, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2ItemSuffixStringsWandsPC98JapaneseProvider },
+	{ kEoBBaseRipItemStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2RipItemStringsPC98JapaneseProvider },
+	{ kEoBBaseCursedString, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2CursedStringPC98JapaneseProvider },
+	{ kEoBBaseEnchantedString, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2EnchantedStringPC98Provider },
+	{ kEoBBaseMagicObjectStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MagicObjectStringsPC98JapaneseProvider },
+	{ kEoBBaseMagicObjectString5, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MagicObjectString5PC98JapaneseProvider },
+	{ kEoBBasePatternSuffix, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2PatternSuffixPC98JapaneseProvider },
+	{ kEoBBasePatternGrFix1, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2PatternGrFix1PC98JapaneseProvider },
+	{ kEoBBasePatternGrFix2, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2PatternGrFix2PC98JapaneseProvider },
+	{ kEoBBaseValidateArmorString, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2ValidateArmorStringPC98JapaneseProvider },
+	{ kEoBBaseValidateCursedString, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2ValidateCursedStringPC98JapaneseProvider },
+	{ kEoBBaseValidateNoDropString, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2ValidateNoDropStringPC98JapaneseProvider },
+	{ kEoBBasePotionStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2PotionStringsPC98JapaneseProvider },
+	{ kEoBBaseWandStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2WandStringsPC98JapaneseProvider },
+	{ kEoBBaseItemMisuseStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2ItemMisuseStringsPC98JapaneseProvider },
+	{ kEoBBaseTakenStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2TakenStringsPC98JapaneseProvider },
+	{ kEoBBasePotionEffectStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2PotionEffectStringsPC98JapaneseProvider },
+	{ kEoBBaseYesNoStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2YesNoStringsPC98JapaneseProvider },
+	{ kRpgCommonMoreStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MoreStringsPC98JapaneseProvider },
+	{ kEoBBaseNpcMaxStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2NpcMaxStringsPC98JapaneseProvider },
+	{ kEoBBaseOkStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2OkStringsPC98JapaneseProvider },
+	{ kEoBBaseNpcJoinStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2NpcJoinStringsPC98JapaneseProvider },
+	{ kEoBBaseCancelStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2CancelStringsPC98JapaneseProvider },
+	{ kEoBBaseAbortStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2AbortStringsPC98JapaneseProvider },
+	{ kEoBBaseMenuStringsMain, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MenuStringsMainPC98JapaneseProvider },
+	{ kEoBBaseMenuStringsSaveLoad, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MenuStringsSaveLoadPC98JapaneseProvider },
+	{ kEoBBaseMenuStringsOnOff, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MenuStringsOnOffPC98JapaneseProvider },
+	{ kEoBBaseMenuStringsSpells, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MenuStringsSpellsPC98JapaneseProvider },
+	{ kEoBBaseMenuStringsRest, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MenuStringsRestPC98JapaneseProvider },
+	{ kEoBBaseMenuStringsDrop, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MenuStringsDropPC98JapaneseProvider },
+	{ kEoBBaseMenuStringsExit, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MenuStringsExitPC98JapaneseProvider },
+	{ kEoBBaseMenuStringsStarve, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MenuStringsStarvePC98JapaneseProvider },
+	{ kEoBBaseMenuStringsScribe, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MenuStringsScribePC98JapaneseProvider },
+	{ kEoBBaseMenuStringsDrop2, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MenuStringsDrop2PC98JapaneseProvider },
+	{ kEoBBaseMenuStringsHead, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MenuStringsHeadPC98JapaneseProvider },
+	{ kEoBBaseMenuStringsPoison, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MenuStringsPoisonPC98JapaneseProvider },
+	{ kEoBBaseMenuStringsMgc, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MenuStringsMgcPC98JapaneseProvider },
+	{ kEoBBaseMenuStringsPrefs, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MenuStringsPrefsPC98JapaneseProvider },
+	{ kEoBBaseMenuStringsRest2, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MenuStringsRest2PC98JapaneseProvider },
+	{ kEoBBaseMenuStringsRest3, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MenuStringsRest3PC98JapaneseProvider },
+	{ kEoBBaseMenuStringsRest4, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MenuStringsRest4PC98JapaneseProvider },
+	{ kEoBBaseMenuStringsDefeat, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MenuStringsDefeatPC98JapaneseProvider },
+	{ kEoBBaseMenuStringsTransfer, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MenuStringsTransferPC98JapaneseProvider },
+	{ kEoBBaseMenuStringsSpec, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MenuStringsSpecPC98JapaneseProvider },
+	{ kEoBBaseMenuStringsSpellNo, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2MenuStringsSpellNoPC98Provider },
+	{ kEoBBaseMenuYesNoStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MenuYesNoStringsPC98JapaneseProvider },
+	{ kEoBBaseSpellLevelsMage, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SpellLevelsMagePC98Provider },
+	{ kEoBBaseSpellLevelsCleric, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SpellLevelsClericPC98Provider },
+	{ kEoBBaseNumSpellsCleric, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2NumSpellsClericPC98Provider },
+	{ kEoBBaseNumSpellsWisAdj, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2NumSpellsWisAdjPC98Provider },
+	{ kEoBBaseNumSpellsPal, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2NumSpellsPalPC98Provider },
+	{ kEoBBaseNumSpellsMage, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2NumSpellsMagePC98Provider },
+	{ kEoBBaseCharGuiStringsHp, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2CharGuiStringsHpPC98JapaneseProvider },
+	{ kEoBBaseCharGuiStringsWp2, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2CharGuiStringsWp2PC98JapaneseProvider },
+	{ kEoBBaseCharGuiStringsWr, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2CharGuiStringsWrPC98JapaneseProvider },
+	{ kEoBBaseCharGuiStringsSt2, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2CharGuiStringsSt2PC98JapaneseProvider },
+	{ kEoBBaseCharGuiStringsIn, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2CharGuiStringsInPC98JapaneseProvider },
+	{ kEoBBaseCharStatusStrings7, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2CharStatusStrings7PC98JapaneseProvider },
+	{ kEoBBaseCharStatusStrings82, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2CharStatusStrings82PC98JapaneseProvider },
+	{ kEoBBaseCharStatusStrings9, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2CharStatusStrings9PC98JapaneseProvider },
+	{ kEoBBaseCharStatusStrings12, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2CharStatusStrings12PC98JapaneseProvider },
+	{ kEoBBaseCharStatusStrings132, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2CharStatusStrings132PC98JapaneseProvider },
+	{ kEoBBaseLevelGainStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2LevelGainStringsPC98JapaneseProvider },
+	{ kEoBBaseExperienceTable0, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ExperienceTable0PC98Provider },
+	{ kEoBBaseExperienceTable1, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ExperienceTable1PC98Provider },
+	{ kEoBBaseExperienceTable2, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ExperienceTable2PC98Provider },
+	{ kEoBBaseExperienceTable3, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ExperienceTable3PC98Provider },
+	{ kEoBBaseExperienceTable4, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ExperienceTable4PC98Provider },
+	{ kEoBBaseBookNumbers, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2BookNumbersPC98JapaneseProvider },
+	{ kEoBBaseMageSpellsList, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MageSpellsListPC98JapaneseProvider },
+	{ kEoBBaseClericSpellsList, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2ClericSpellsListPC98JapaneseProvider },
+	{ kEoBBaseSpellNames, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2SpellNamesPC98JapaneseProvider },
+	{ kEoBBaseMagicStrings1, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MagicStrings1PC98JapaneseProvider },
+	{ kEoBBaseMagicStrings2, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MagicStrings2PC98JapaneseProvider },
+	{ kEoBBaseMagicStrings3, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MagicStrings3PC98JapaneseProvider },
+	{ kEoBBaseMagicStrings4, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MagicStrings4PC98JapaneseProvider },
+	{ kEoBBaseMagicStrings6, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MagicStrings6PC98JapaneseProvider },
+	{ kEoBBaseMagicStrings7, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MagicStrings7PC98JapaneseProvider },
+	{ kEoBBaseMagicStrings8, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MagicStrings8PC98JapaneseProvider },
+	{ kEoBBaseExpObjectTlMode, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ExpObjectTlModePC98Provider },
+	{ kEoBBaseExpObjectTblIndex, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ExpObjectTblIndexPC98Provider },
+	{ kEoBBaseExpObjectShpStart, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ExpObjectShpStartPC98Provider },
+	{ kEoBBaseExpObjectTbl1, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ExpObjectTbl1PC98Provider },
+	{ kEoBBaseExpObjectTbl2, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ExpObjectTbl2PC98Provider },
+	{ kEoBBaseExpObjectTbl3, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ExpObjectTbl3PC98Provider },
+	{ kEoBBaseExpObjectY, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ExpObjectYPC98Provider },
+	{ kEoBBaseSparkDefSteps, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SparkDefStepsPC98Provider },
+	{ kEoBBaseSparkDefSubSteps, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SparkDefSubStepsPC98Provider },
+	{ kEoBBaseSparkDefShift, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SparkDefShiftPC98Provider },
+	{ kEoBBaseSparkDefAdd, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SparkDefAddPC98Provider },
+	{ kEoBBaseSparkDefX, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SparkDefXPC98Provider },
+	{ kEoBBaseSparkDefY, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SparkDefYPC98Provider },
+	{ kEoBBaseSparkOfFlags1, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SparkOfFlags1PC98Provider },
+	{ kEoBBaseSparkOfFlags2, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SparkOfFlags2PC98Provider },
+	{ kEoBBaseSparkOfShift, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SparkOfShiftPC98Provider },
+	{ kEoBBaseSparkOfX, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SparkOfXPC98Provider },
+	{ kEoBBaseSparkOfY, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SparkOfYPC98Provider },
+	{ kEoBBaseSpellProperties, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SpellPropertiesPC98Provider },
+	{ kEoBBaseMagicFlightProps, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2MagicFlightPropsPC98Provider },
+	{ kEoBBaseTurnUndeadEffect, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2TurnUndeadEffectPC98Provider },
+	{ kEoBBaseBurningHandsDest, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2BurningHandsDestPC98Provider },
+	{ kEoBBaseConeOfColdDest1, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ConeOfColdDest1PC98Provider },
+	{ kEoBBaseConeOfColdDest2, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ConeOfColdDest2PC98Provider },
+	{ kEoBBaseConeOfColdDest3, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ConeOfColdDest3PC98Provider },
+	{ kEoBBaseConeOfColdDest4, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ConeOfColdDest4PC98Provider },
+	{ kEoBBaseConeOfColdGfxTbl, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ConeOfColdGfxTblPC98Provider },
+	{ kRpgCommonDscDoorShapeIndex, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscDoorShapeIndexPC98Provider },
+	{ kEoBBaseWllFlagPreset, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2WllFlagPresetPC98Provider },
+	{ kEoBBaseDscShapeCoords, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscShapeCoordsPC98Provider },
+	{ kRpgCommonDscDoorScaleOffs, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscDoorScaleOffsPC98Provider },
+	{ kEoBBaseDscDoorScaleMult1, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscDoorScaleMult1PC98Provider },
+	{ kEoBBaseDscDoorScaleMult2, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscDoorScaleMult2PC98Provider },
+	{ kEoBBaseDscDoorScaleMult3, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscDoorScaleMult3PC98Provider },
+	{ kEoBBaseDscDoorType5Offs, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscDoorType5OffsPC98Provider },
+	{ kEoBBaseDscDoorY1, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscDoorY1PC98Provider },
+	{ kRpgCommonDscDoorY2, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscDoorY2PC98Provider },
+	{ kRpgCommonDscDoorFrameY1, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscDoorFrameY1PC98Provider },
+	{ kRpgCommonDscDoorFrameY2, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscDoorFrameY2PC98Provider },
+	{ kEoBBaseDscItemPosIndex, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscItemPosIndexPC98Provider },
+	{ kEoBBaseDscItemShpX, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscItemShpXPC98Provider },
+	{ kEoBBaseDscItemScaleIndex, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscItemScaleIndexPC98Provider },
+	{ kEoBBaseDscItemTileIndex, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscItemTileIndexPC98Provider },
+	{ kEoBBaseDscItemShapeMap, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscItemShapeMapPC98Provider },
+	{ kEoBBaseDscTelptrShpCoords, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscTelptrShpCoordsPC98Provider },
+	{ kEoBBasePortalSeqData, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2PortalSeqDataPC98Provider },
+	{ kEoBBaseDscMonsterFrmOffsTbl1, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscMonsterFrmOffsTbl1PC98Provider },
+	{ kEoBBaseDscMonsterFrmOffsTbl2, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscMonsterFrmOffsTbl2PC98Provider },
+	{ kEoBBaseInvSlotX, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2InvSlotXPC98Provider },
+	{ kEoBBaseInvSlotY, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2InvSlotYPC98Provider },
+	{ kEoBBaseSlotValidationFlags, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SlotValidationFlagsPC98Provider },
+	{ kEoBBaseProjectileWeaponTypes, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ProjectileWeaponTypesPC98Provider },
+	{ kEoBBaseWandTypes, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2WandTypesPC98Provider },
+	{ kEoBBaseDrawObjPosIndex, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DrawObjPosIndexPC98Provider },
+	{ kEoBBaseFlightObjFlipIndex, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FlightObjFlipIndexPC98Provider },
+	{ kEoBBaseFlightObjShpMap, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FlightObjShpMapPC98Provider },
+	{ kEoBBaseFlightObjSclIndex, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FlightObjSclIndexPC98Provider },
+	{ kEoB2MainMenuStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MainMenuStringsPC98JapaneseProvider },
+	{ kEoB2TransferPortraitFrames, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2TransferPortraitFramesPC98Provider },
+	{ kEoB2TransferConvertTable, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2TransferConvertTablePC98Provider },
+	{ kEoB2TransferItemTable, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2TransferItemTablePC98Provider },
+	{ kEoB2TransferExpTable, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2TransferExpTablePC98Provider },
+	{ kEoB2TransferStrings1, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2TransferStrings1PC98JapaneseProvider },
+	{ kEoB2TransferStrings2, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2TransferStrings2PC98JapaneseProvider },
+	{ kEoB2TransferLabels, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2TransferLabelsPC98JapaneseProvider },
+	{ kEoB2IntroStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2IntroStringsPC98JapaneseProvider },
+	{ kEoB2IntroCPSFiles, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2IntroCPSFilesPC98JapaneseProvider },
+	{ kEoB2IntroAnimData00, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData00PC98Provider },
+	{ kEoB2IntroAnimData01, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData01PC98Provider },
+	{ kEoB2IntroAnimData02, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData02PC98Provider },
+	{ kEoB2IntroAnimData03, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData03PC98Provider },
+	{ kEoB2IntroAnimData04, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData04PC98Provider },
+	{ kEoB2IntroAnimData05, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData05PC98Provider },
+	{ kEoB2IntroAnimData06, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData06PC98Provider },
+	{ kEoB2IntroAnimData07, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData07PC98Provider },
+	{ kEoB2IntroAnimData08, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData08PC98Provider },
+	{ kEoB2IntroAnimData09, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData09PC98Provider },
+	{ kEoB2IntroAnimData10, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData10PC98Provider },
+	{ kEoB2IntroAnimData11, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData11PC98Provider },
+	{ kEoB2IntroAnimData12, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData12PC98Provider },
+	{ kEoB2IntroAnimData13, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData13PC98Provider },
+	{ kEoB2IntroAnimData14, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData14PC98Provider },
+	{ kEoB2IntroAnimData15, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData15PC98Provider },
+	{ kEoB2IntroAnimData16, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData16PC98Provider },
+	{ kEoB2IntroAnimData17, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData17PC98Provider },
+	{ kEoB2IntroAnimData18, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData18PC98Provider },
+	{ kEoB2IntroAnimData19, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData19PC98Provider },
+	{ kEoB2IntroAnimData20, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData20PC98Provider },
+	{ kEoB2IntroAnimData21, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData21PC98Provider },
+	{ kEoB2IntroAnimData22, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData22PC98Provider },
+	{ kEoB2IntroAnimData23, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData23PC98Provider },
+	{ kEoB2IntroAnimData24, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData24PC98Provider },
+	{ kEoB2IntroAnimData25, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData25PC98Provider },
+	{ kEoB2IntroAnimData26, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData26PC98Provider },
+	{ kEoB2IntroAnimData27, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData27PC98Provider },
+	{ kEoB2IntroAnimData28, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData28PC98Provider },
+	{ kEoB2IntroAnimData29, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData29PC98Provider },
+	{ kEoB2IntroAnimData30, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData30PC98Provider },
+	{ kEoB2IntroAnimData31, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData31PC98Provider },
+	{ kEoB2IntroAnimData32, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData32PC98Provider },
+	{ kEoB2IntroAnimData33, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData33PC98Provider },
+	{ kEoB2IntroAnimData34, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData34PC98Provider },
+	{ kEoB2IntroAnimData35, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData35PC98Provider },
+	{ kEoB2IntroAnimData36, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData36PC98Provider },
+	{ kEoB2IntroAnimData37, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData37PC98Provider },
+	{ kEoB2IntroAnimData38, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData38PC98Provider },
+	{ kEoB2IntroAnimData39, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData39PC98Provider },
+	{ kEoB2IntroAnimData40, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData40PC98Provider },
+	{ kEoB2IntroAnimData41, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData41PC98Provider },
+	{ kEoB2IntroAnimData42, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData42PC98Provider },
+	{ kEoB2IntroAnimData43, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData43PC98Provider },
+	{ kEoB2IntroAnimData44, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData44PC98Provider },
+	{ kEoB2IntroAnimData45, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData45PC98Provider },
+	{ kEoB2IntroAnimData46, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData46PC98Provider },
+	{ kEoB2IntroAnimData47, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroAnimData47PC98Provider },
+	{ kEoB2IntroShapes00, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroShapes00PC98Provider },
+	{ kEoB2IntroShapes01, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroShapes01PC98Provider },
+	{ kEoB2IntroShapes04, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroShapes04PC98Provider },
+	{ kEoB2IntroShapes07, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroShapes07PC98Provider },
+	{ kEoB2IntroShapes13, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2IntroShapes13PC98Provider },
+	{ kEoB2FinaleStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2FinaleStringsPC98JapaneseProvider },
+	{ kEoB2FinaleCPSFiles, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2FinaleCPSFilesPC98JapaneseProvider },
+	{ kEoB2FinaleAnimData00, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleAnimData00PC98Provider },
+	{ kEoB2FinaleAnimData01, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleAnimData01PC98Provider },
+	{ kEoB2FinaleAnimData02, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleAnimData02PC98Provider },
+	{ kEoB2FinaleAnimData03, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleAnimData03PC98Provider },
+	{ kEoB2FinaleAnimData04, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleAnimData04PC98Provider },
+	{ kEoB2FinaleAnimData05, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleAnimData05PC98Provider },
+	{ kEoB2FinaleAnimData06, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleAnimData06PC98Provider },
+	{ kEoB2FinaleAnimData07, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleAnimData07PC98Provider },
+	{ kEoB2FinaleAnimData08, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleAnimData08PC98Provider },
+	{ kEoB2FinaleAnimData09, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleAnimData09PC98Provider },
+	{ kEoB2FinaleAnimData10, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleAnimData10PC98Provider },
+	{ kEoB2FinaleAnimData11, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleAnimData11PC98Provider },
+	{ kEoB2FinaleAnimData12, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleAnimData12PC98Provider },
+	{ kEoB2FinaleAnimData13, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleAnimData13PC98Provider },
+	{ kEoB2FinaleAnimData14, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleAnimData14PC98Provider },
+	{ kEoB2FinaleAnimData15, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleAnimData15PC98Provider },
+	{ kEoB2FinaleAnimData16, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleAnimData16PC98Provider },
+	{ kEoB2FinaleAnimData17, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleAnimData17PC98Provider },
+	{ kEoB2FinaleAnimData18, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleAnimData18PC98Provider },
+	{ kEoB2FinaleAnimData19, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleAnimData19PC98Provider },
+	{ kEoB2FinaleAnimData20, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleAnimData20PC98Provider },
+	{ kEoB2FinaleShapes00, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleShapes00PC98Provider },
+	{ kEoB2FinaleShapes03, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleShapes03PC98Provider },
+	{ kEoB2FinaleShapes07, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleShapes07PC98Provider },
+	{ kEoB2FinaleShapes09, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleShapes09PC98Provider },
+	{ kEoB2FinaleShapes10, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FinaleShapes10PC98Provider },
+	{ kEoB2NpcShapeData, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2NpcShapeDataPC98Provider },
+	{ kEoBBaseClassModifierFlags, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2ClassModifierFlagsPC98Provider },
+	{ kEoBBaseMonsterStepTable02, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2MonsterStepTable02PC98Provider },
+	{ kEoBBaseMonsterStepTable1, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2MonsterStepTable1PC98Provider },
+	{ kEoBBaseMonsterStepTable2, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2MonsterStepTable2PC98Provider },
+	{ kEoBBaseMonsterStepTable3, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2MonsterStepTable3PC98Provider },
+	{ kEoBBaseMonsterCloseAttPosTable1, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2MonsterCloseAttPosTable1PC98Provider },
+	{ kEoBBaseMonsterCloseAttPosTable22, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2MonsterCloseAttPosTable22PC98Provider },
+	{ kEoBBaseMonsterCloseAttUnkTable, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2MonsterCloseAttUnkTablePC98Provider },
+	{ kEoBBaseMonsterCloseAttChkTable1, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2MonsterCloseAttChkTable1PC98Provider },
+	{ kEoBBaseMonsterCloseAttChkTable2, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2MonsterCloseAttChkTable2PC98Provider },
+	{ kEoBBaseMonsterCloseAttDstTable1, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2MonsterCloseAttDstTable1PC98Provider },
+	{ kEoBBaseMonsterCloseAttDstTable2, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2MonsterCloseAttDstTable2PC98Provider },
+	{ kEoBBaseMonsterProximityTable, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2MonsterProximityTablePC98Provider },
+	{ kEoBBaseFindBlockMonstersTable, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FindBlockMonstersTablePC98Provider },
+	{ kEoBBaseMonsterDirChangeTable, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2MonsterDirChangeTablePC98Provider },
+	{ kEoBBaseMonsterDistAttStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MonsterDistAttStringsPC98JapaneseProvider },
+	{ kEoBBaseEncodeMonsterDefs, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2EncodeMonsterDefsPC98Provider },
+	{ kEoBBaseNpcPresets, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2NpcPresetsPC98Provider },
+	{ kEoBBaseSoundFilesIntro, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SoundFilesIntroPC98Provider },
+	{ kEoBBaseSoundFilesFinale, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2SoundFilesFinalePC98Provider },
+	{ kEoB2Npc1Strings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2Npc1StringsPC98JapaneseProvider },
+	{ kEoB2Npc2Strings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2Npc2StringsPC98JapaneseProvider },
+	{ kEoB2MonsterDustStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2MonsterDustStringsPC98JapaneseProvider },
+	{ kEoB2DreamSteps, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DreamStepsPC98Provider },
+	{ kEoB2KheldranStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2KheldranStringsPC98JapaneseProvider },
+	{ kEoB2HornStrings, kEoB2, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB2HornStringsPC98JapaneseProvider },
+	{ kEoB2HornSounds, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2HornSoundsPC98Provider },
+	{ kEoB2WallOfForceDsX, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2WallOfForceDsXPC98Provider },
+	{ kEoB2WallOfForceDsY, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2WallOfForceDsYPC98Provider },
+	{ kEoB2WallOfForceNumW, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2WallOfForceNumWPC98Provider },
+	{ kEoB2WallOfForceNumH, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2WallOfForceNumHPC98Provider },
+	{ kEoB2WallOfForceShpId, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2WallOfForceShpIdPC98Provider },
+	{ kRpgCommonDscShapeIndex, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscShapeIndexPC98Provider },
+	{ kRpgCommonDscX, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscXPC98Provider },
+	{ kRpgCommonDscTileIndex, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscTileIndexPC98Provider },
+	{ kRpgCommonDscDimData1, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscDimData1PC98Provider },
+	{ kRpgCommonDscDimData2, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscDimData2PC98Provider },
+	{ kRpgCommonDscBlockMap, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscBlockMapPC98Provider },
+	{ kRpgCommonDscDimMap, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscDimMapPC98Provider },
+	{ kRpgCommonDscBlockIndex, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2DscBlockIndexPC98Provider },
+	{ kEoB2Ascii2SjisTables, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2Ascii2SjisTablesPC98Provider },
+	{ kEoB2FontConvertTbl, kEoB2, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB2FontConvertTblPC98Provider },
 	{ kLoLIngamePakFiles, kLoL, kPlatformDOS, kNoSpecial, UNK_LANG, &kLoLIngamePakFilesDOSProvider },
 	{ kLoLCharacterDefs, kLoL, kPlatformDOS, kNoSpecial, EN_ANY, &kLoLCharacterDefsDOSEnglishProvider },
 	{ kLoLIngameSfxFiles, kLoL, kPlatformDOS, kNoSpecial, UNK_LANG, &kLoLIngameSfxFilesDOSProvider },
diff --git a/devtools/create_kyradat/resources/eob1_segacd_english.h b/devtools/create_kyradat/resources/eob1_segacd_english.h
index 34c9d413426..4f83bc10d9b 100644
--- a/devtools/create_kyradat/resources/eob1_segacd_english.h
+++ b/devtools/create_kyradat/resources/eob1_segacd_english.h
@@ -1173,7 +1173,7 @@ static const char *const kEoB1TextInputSelectStringsSegaCDEnglish[5] = {
 static const StringListProvider kEoB1TextInputSelectStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1TextInputSelectStringsSegaCDEnglish), kEoB1TextInputSelectStringsSegaCDEnglish };
 
 static const char *const kEoB1SaveNamePatternsSegaCDEnglish[1] = {
-	"%s\r FLOOR % -2u % u: % 02u"
+	"%s\r FLOOR %-2u %u:%02u"
 };
 
 static const StringListProvider kEoB1SaveNamePatternsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1SaveNamePatternsSegaCDEnglish), kEoB1SaveNamePatternsSegaCDEnglish };
diff --git a/devtools/create_kyradat/resources/eob1_segacd_japanese.h b/devtools/create_kyradat/resources/eob1_segacd_japanese.h
index 9725b9625c1..57e700f0e16 100644
--- a/devtools/create_kyradat/resources/eob1_segacd_japanese.h
+++ b/devtools/create_kyradat/resources/eob1_segacd_japanese.h
@@ -1168,7 +1168,7 @@ static const char *const kEoB1TextInputSelectStringsSegaCDJapanese[5] = {
 static const StringListProvider kEoB1TextInputSelectStringsSegaCDJapaneseProvider = { ARRAYSIZE(kEoB1TextInputSelectStringsSegaCDJapanese), kEoB1TextInputSelectStringsSegaCDJapanese };
 
 static const char *const kEoB1SaveNamePatternsSegaCDJapanese[1] = {
-	"%s\r FLOOR % -2u % u: % 02u"
+	"%s\r FLOOR %-2u %u:%02u"
 };
 
 static const StringListProvider kEoB1SaveNamePatternsSegaCDJapaneseProvider = { ARRAYSIZE(kEoB1SaveNamePatternsSegaCDJapanese), kEoB1SaveNamePatternsSegaCDJapanese };
diff --git a/devtools/create_kyradat/resources/eob2_fmtowns.h b/devtools/create_kyradat/resources/eob2_fmtowns.h
index cb9688654d0..b332c2c2973 100644
--- a/devtools/create_kyradat/resources/eob2_fmtowns.h
+++ b/devtools/create_kyradat/resources/eob2_fmtowns.h
@@ -73603,7 +73603,7 @@ static const byte kEoB2PcmSoundEffectsFinaleFMTowns[36768] = {
 
 static const ByteProvider kEoB2PcmSoundEffectsFinaleFMTownsProvider = { ARRAYSIZE(kEoB2PcmSoundEffectsFinaleFMTowns), kEoB2PcmSoundEffectsFinaleFMTowns };
 
-static const uint16 kEoB2FontDmpSearchTblFMTowns[149] = {
+static const uint16 kEoB2FontLookupTblFMTowns[149] = {
 	0x4083, 0x4283, 0x4483, 0x4683, 0x4883, 0x8383, 0x8583, 0x8783, 0x6283, 0x4183, 0x4383, 0x4583, 0x4783, 0x4983, 0x4a83, 0x4c83,
 	0x4e83, 0x5083, 0x5283, 0x5483, 0x5683, 0x5883, 0x5a83, 0x5c83, 0x5e83, 0x6083, 0x6383, 0x6583, 0x6783, 0x6983, 0x6a83, 0x6b83,
 	0x6c83, 0x6d83, 0x6e83, 0x7183, 0x7483, 0x7783, 0x7a83, 0x7d83, 0x7e83, 0x8083, 0x8183, 0x8283, 0x8483, 0x8683, 0x8883, 0x8983,
@@ -73616,7 +73616,7 @@ static const uint16 kEoB2FontDmpSearchTblFMTowns[149] = {
 	0x9681, 0x4881, 0x8381, 0x8481, 0x0000
 };
 
-static const Uint16Provider kEoB2FontDmpSearchTblFMTownsProvider = { ARRAYSIZE(kEoB2FontDmpSearchTblFMTowns), kEoB2FontDmpSearchTblFMTowns };
+static const Uint16Provider kEoB2FontLookupTblFMTownsProvider = { ARRAYSIZE(kEoB2FontLookupTblFMTowns), kEoB2FontLookupTblFMTowns };
 
 static const char *const kEoB2SoundFilesIntroFMTowns[1] = {
 	"INTRO"
diff --git a/devtools/create_kyradat/resources/eob2_pc98.h b/devtools/create_kyradat/resources/eob2_pc98.h
new file mode 100644
index 00000000000..130c22ca039
--- /dev/null
+++ b/devtools/create_kyradat/resources/eob2_pc98.h
@@ -0,0 +1,2592 @@
+static const byte kEoB2ChargenStartLevelsPC98[60] = {
+	0x07, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x01,
+	0x06, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x01,
+	0x07, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x01,
+	0x06, 0x06, 0x00, 0x02, 0x06, 0x06, 0x00, 0x02,
+	0x06, 0x05, 0x00, 0x02, 0x05, 0x05, 0x06, 0x03,
+	0x06, 0x05, 0x00, 0x02, 0x06, 0x06, 0x00, 0x02,
+	0x05, 0x05, 0x05, 0x03, 0x05, 0x06, 0x00, 0x02,
+	0x06, 0x05, 0x00, 0x02
+};
+
+static const ByteProvider kEoB2ChargenStartLevelsPC98Provider = { ARRAYSIZE(kEoB2ChargenStartLevelsPC98), kEoB2ChargenStartLevelsPC98 };
+
+static const byte kEoB2ChargenClassMinStatsPC98[90] = {
+	0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00,
+	0x0E, 0x0D, 0x0E, 0x00, 0x0C, 0x00, 0x0D, 0x00,
+	0x09, 0x11, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x09, 0x00,
+	0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00,
+	0x09, 0x09, 0x00, 0x00, 0x00, 0x00, 0x09, 0x09,
+	0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x09,
+	0x00, 0x00, 0x00, 0x00, 0x09, 0x09, 0x00, 0x00,
+	0x09, 0x09, 0x09, 0x00, 0x00, 0x00, 0x0D, 0x00,
+	0x0E, 0x0D, 0x0E, 0x00, 0x00, 0x09, 0x09, 0x00,
+	0x00, 0x00
+};
+
+static const ByteProvider kEoB2ChargenClassMinStatsPC98Provider = { ARRAYSIZE(kEoB2ChargenClassMinStatsPC98), kEoB2ChargenClassMinStatsPC98 };
+
+static const byte kEoB2ChargenRaceMinStatsPC98[36] = {
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x08,
+	0x03, 0x07, 0x06, 0x08, 0x03, 0x04, 0x03, 0x06,
+	0x06, 0x03, 0x08, 0x03, 0x03, 0x03, 0x0C, 0x02,
+	0x06, 0x07, 0x02, 0x03, 0x08, 0x03, 0x07, 0x06,
+	0x03, 0x08, 0x0A, 0x06
+};
+
+static const ByteProvider kEoB2ChargenRaceMinStatsPC98Provider = { ARRAYSIZE(kEoB2ChargenRaceMinStatsPC98), kEoB2ChargenRaceMinStatsPC98 };
+
+static const uint16 kEoB2ChargenRaceMaxStatsPC98[36] = {
+	0x6412, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x6412, 0x0012,
+	0x0012, 0x0013, 0x0011, 0x0012, 0x6412, 0x0012, 0x0012, 0x0012,
+	0x0012, 0x0012, 0x6412, 0x0012, 0x0012, 0x0011, 0x0013, 0x0010,
+	0x6412, 0x0013, 0x0011, 0x0012, 0x0012, 0x0012, 0x0011, 0x0012,
+	0x0011, 0x0013, 0x0012, 0x0012
+};
+
+static const Uint16Provider kEoB2ChargenRaceMaxStatsPC98Provider = { ARRAYSIZE(kEoB2ChargenRaceMaxStatsPC98), kEoB2ChargenRaceMaxStatsPC98 };
+
+static const byte kEoB2SaveThrowTable1PC98[50] = {
+	0x10, 0x0E, 0x0D, 0x0B, 0x0A, 0x08, 0x07, 0x05,
+	0x04, 0x03, 0x12, 0x10, 0x0F, 0x0D, 0x0C, 0x0A,
+	0x09, 0x07, 0x06, 0x05, 0x11, 0x0F, 0x0E, 0x0C,
+	0x0B, 0x09, 0x08, 0x06, 0x05, 0x04, 0x14, 0x11,
+	0x10, 0x0D, 0x0C, 0x09, 0x08, 0x05, 0x04, 0x04,
+	0x13, 0x11, 0x10, 0x0E, 0x0D, 0x0B, 0x0A, 0x08,
+	0x07, 0x06
+};
+
+static const ByteProvider kEoB2SaveThrowTable1PC98Provider = { ARRAYSIZE(kEoB2SaveThrowTable1PC98), kEoB2SaveThrowTable1PC98 };
+
+static const byte kEoB2SaveThrowTable2PC98[25] = {
+	0x0E, 0x0D, 0x0B, 0x0A, 0x08, 0x0B, 0x09, 0x07,
+	0x05, 0x03, 0x0D, 0x0B, 0x09, 0x07, 0x05, 0x0F,
+	0x0D, 0x0B, 0x09, 0x07, 0x0C, 0x0A, 0x08, 0x06,
+	0x04
+};
+
+static const ByteProvider kEoB2SaveThrowTable2PC98Provider = { ARRAYSIZE(kEoB2SaveThrowTable2PC98), kEoB2SaveThrowTable2PC98 };
+
+static const byte kEoB2SaveThrowTable3PC98[35] = {
+	0x0A, 0x09, 0x07, 0x06, 0x05, 0x04, 0x02, 0x0E,
+	0x0D, 0x0B, 0x0A, 0x09, 0x08, 0x06, 0x0D, 0x0C,
+	0x0A, 0x09, 0x08, 0x07, 0x05, 0x10, 0x0F, 0x0D,
+	0x0C, 0x0B, 0x0A, 0x08, 0x0F, 0x0E, 0x0C, 0x0B,
+	0x0A, 0x09, 0x07
+};
+
+static const ByteProvider kEoB2SaveThrowTable3PC98Provider = { ARRAYSIZE(kEoB2SaveThrowTable3PC98), kEoB2SaveThrowTable3PC98 };
+
+static const byte kEoB2SaveThrowTable4PC98[30] = {
+	0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x0E, 0x0C,
+	0x0A, 0x08, 0x06, 0x07, 0x0C, 0x0B, 0x0A, 0x09,
+	0x08, 0x04, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B,
+	0x0F, 0x0D, 0x0B, 0x09, 0x07, 0x05
+};
+
+static const ByteProvider kEoB2SaveThrowTable4PC98Provider = { ARRAYSIZE(kEoB2SaveThrowTable4PC98), kEoB2SaveThrowTable4PC98 };
+
+static const byte kEoB2SaveThrwLvlIndexPC98[6] = {
+	0x11, 0x15, 0x13, 0x15, 0x11, 0x11
+};
+
+static const ByteProvider kEoB2SaveThrwLvlIndexPC98Provider = { ARRAYSIZE(kEoB2SaveThrwLvlIndexPC98), kEoB2SaveThrwLvlIndexPC98 };
+
+static const byte kEoB2SaveThrwModDivPC98[6] = {
+	0x02, 0x05, 0x03, 0x04, 0x02, 0x02
+};
+
+static const ByteProvider kEoB2SaveThrwModDivPC98Provider = { ARRAYSIZE(kEoB2SaveThrwModDivPC98), kEoB2SaveThrwModDivPC98 };
+
+static const byte kEoB2SaveThrwModExtPC98[6] = {
+	0x0A, 0x05, 0x07, 0x06, 0x0A, 0x0A
+};
+
+static const ByteProvider kEoB2SaveThrwModExtPC98Provider = { ARRAYSIZE(kEoB2SaveThrwModExtPC98), kEoB2SaveThrwModExtPC98 };
+
+static const char *const kEoB2EnchantedStringPC98[1] = {
+	"+%d %s"
+};
+
+static const StringListProvider kEoB2EnchantedStringPC98Provider = { ARRAYSIZE(kEoB2EnchantedStringPC98), kEoB2EnchantedStringPC98 };
+
+static const char *const kEoB2MenuStringsSpellNoPC98[3] = {
+	"\x82""U",
+	"\x82""V",
+	"\x82""W"
+};
+
+static const StringListProvider kEoB2MenuStringsSpellNoPC98Provider = { ARRAYSIZE(kEoB2MenuStringsSpellNoPC98), kEoB2MenuStringsSpellNoPC98 };
+
+static const byte kEoB2SpellLevelsMagePC98[35] = {
+	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
+	0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03,
+	0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05,
+	0x05, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x08,
+	0x00, 0x01, 0x01
+};
+
+static const ByteProvider kEoB2SpellLevelsMagePC98Provider = { ARRAYSIZE(kEoB2SpellLevelsMagePC98), kEoB2SpellLevelsMagePC98 };
+
+static const byte kEoB2SpellLevelsClericPC98[29] = {
+	0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02,
+	0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04,
+	0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+	0x06, 0x06, 0x08, 0x08, 0x00
+};
+
+static const ByteProvider kEoB2SpellLevelsClericPC98Provider = { ARRAYSIZE(kEoB2SpellLevelsClericPC98), kEoB2SpellLevelsClericPC98 };
+
+static const byte kEoB2NumSpellsClericPC98[136] = {
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x03, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x03, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00,
+	0x03, 0x03, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00,
+	0x04, 0x04, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00,
+	0x04, 0x04, 0x03, 0x02, 0x02, 0x00, 0x00, 0x00,
+	0x05, 0x04, 0x04, 0x03, 0x02, 0x01, 0x00, 0x00,
+	0x06, 0x05, 0x05, 0x03, 0x02, 0x02, 0x00, 0x00,
+	0x06, 0x06, 0x06, 0x04, 0x02, 0x02, 0x00, 0x00,
+	0x06, 0x06, 0x06, 0x05, 0x03, 0x02, 0x01, 0x00,
+	0x06, 0x06, 0x06, 0x06, 0x04, 0x02, 0x01, 0x00,
+	0x07, 0x07, 0x07, 0x06, 0x04, 0x03, 0x01, 0x00,
+	0x07, 0x07, 0x07, 0x07, 0x05, 0x03, 0x02, 0x00
+};
+
+static const ByteProvider kEoB2NumSpellsClericPC98Provider = { ARRAYSIZE(kEoB2NumSpellsClericPC98), kEoB2NumSpellsClericPC98 };
+
+static const byte kEoB2NumSpellsWisAdjPC98[64] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
+	0x03, 0x02, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00
+};
+
+static const ByteProvider kEoB2NumSpellsWisAdjPC98Provider = { ARRAYSIZE(kEoB2NumSpellsWisAdjPC98), kEoB2NumSpellsWisAdjPC98 };
+
+static const byte kEoB2NumSpellsPalPC98[136] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
+	0x03, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00,
+	0x03, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00
+};
+
+static const ByteProvider kEoB2NumSpellsPalPC98Provider = { ARRAYSIZE(kEoB2NumSpellsPalPC98), kEoB2NumSpellsPalPC98 };
+
+static const byte kEoB2NumSpellsMagePC98[276] = {
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x04, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x04, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x04, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00,
+	0x04, 0x03, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00,
+	0x04, 0x03, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00,
+	0x04, 0x04, 0x03, 0x02, 0x02, 0x00, 0x00, 0x00,
+	0x04, 0x04, 0x04, 0x03, 0x03, 0x00, 0x00, 0x00,
+	0x04, 0x04, 0x04, 0x04, 0x04, 0x01, 0x00, 0x00,
+	0x05, 0x05, 0x05, 0x04, 0x04, 0x02, 0x00, 0x00,
+	0x05, 0x05, 0x05, 0x04, 0x04, 0x02, 0x01, 0x00,
+	0x05, 0x05, 0x05, 0x05, 0x05, 0x02, 0x01, 0x00,
+	0x05, 0x05, 0x05, 0x05, 0x05, 0x03, 0x02, 0x01,
+	0x05, 0x05, 0x05, 0x05, 0x05, 0x03, 0x03, 0x02,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00
+};
+
+static const ByteProvider kEoB2NumSpellsMagePC98Provider = { ARRAYSIZE(kEoB2NumSpellsMagePC98), kEoB2NumSpellsMagePC98 };
+
+static const uint32 kEoB2ExperienceTable0PC98[14] = {
+	0x00000000, 0x000007D0, 0x00000FA0, 0x00001F40, 0x00003E80, 0x00007D00, 0x0000FA00, 0x0001E848,
+	0x0003D090, 0x0007A120, 0x000B71B0, 0x000F4240, 0x001312D0, 0xFFFFFFFF
+};
+
+static const Uint32Provider kEoB2ExperienceTable0PC98Provider = { ARRAYSIZE(kEoB2ExperienceTable0PC98), kEoB2ExperienceTable0PC98 };
+
+static const uint32 kEoB2ExperienceTable1PC98[14] = {
+	0x00000000, 0x000009C4, 0x00001388, 0x00002710, 0x00004E20, 0x00009C40, 0x0000EA60, 0x00015F90,
+	0x00020F58, 0x0003D090, 0x0005B8D8, 0x000B71B0, 0x00112A88, 0xFFFFFFFF
+};
+
+static const Uint32Provider kEoB2ExperienceTable1PC98Provider = { ARRAYSIZE(kEoB2ExperienceTable1PC98), kEoB2ExperienceTable1PC98 };
+
+static const uint32 kEoB2ExperienceTable2PC98[14] = {
+	0x00000000, 0x000005DC, 0x00000BB8, 0x00001770, 0x000032C8, 0x00006B6C, 0x0000D6D8, 0x0001ADB0,
+	0x00036EE8, 0x0006DDD0, 0x000A4CB8, 0x000DBBA0, 0x00112A88, 0xFFFFFFFF
+};
+
+static const Uint32Provider kEoB2ExperienceTable2PC98Provider = { ARRAYSIZE(kEoB2ExperienceTable2PC98), kEoB2ExperienceTable2PC98 };
+
+static const uint32 kEoB2ExperienceTable3PC98[14] = {
+	0x00000000, 0x000004E2, 0x000009C4, 0x00001388, 0x00002710, 0x00004E20, 0x00009C40, 0x00011170,
+	0x0001ADB0, 0x00027100, 0x00035B60, 0x0006B6C0, 0x000A1220, 0xFFFFFFFF
+};
+
+static const Uint32Provider kEoB2ExperienceTable3PC98Provider = { ARRAYSIZE(kEoB2ExperienceTable3PC98), kEoB2ExperienceTable3PC98 };
+
+static const uint32 kEoB2ExperienceTable4PC98[14] = {
+	0x00000000, 0x000008CA, 0x00001194, 0x00002328, 0x00004650, 0x00008CA0, 0x000124F8, 0x000249F0,
+	0x000493E0, 0x000927C0, 0x000DBBA0, 0x00124F80, 0x0016E360, 0xFFFFFFFF
+};
+
+static const Uint32Provider kEoB2ExperienceTable4PC98Provider = { ARRAYSIZE(kEoB2ExperienceTable4PC98), kEoB2ExperienceTable4PC98 };
+
+static const byte kEoB2ExpObjectTlModePC98[18] = {
+	0x00, 0x00, 0x01, 0x02, 0x01, 0x00, 0x00, 0x00,
+	0x01, 0x02, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0x02, 0x00
+};
+
+static const ByteProvider kEoB2ExpObjectTlModePC98Provider = { ARRAYSIZE(kEoB2ExpObjectTlModePC98), kEoB2ExpObjectTlModePC98 };
+
+static const byte kEoB2ExpObjectTblIndexPC98[14] = {
+	0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const ByteProvider kEoB2ExpObjectTblIndexPC98Provider = { ARRAYSIZE(kEoB2ExpObjectTblIndexPC98), kEoB2ExpObjectTblIndexPC98 };
+
+static const byte kEoB2ExpObjectShpStartPC98[4] = {
+	0x07, 0x0C, 0x0F, 0x12
+};
+
+static const ByteProvider kEoB2ExpObjectShpStartPC98Provider = { ARRAYSIZE(kEoB2ExpObjectShpStartPC98), kEoB2ExpObjectShpStartPC98 };
+
+static const byte kEoB2ExpObjectTbl1PC98[13] = {
+	0x0F, 0x05, 0x0F, 0x05, 0x06, 0x05, 0x06, 0x08,
+	0x06, 0x08, 0x06, 0x08, 0x00
+};
+
+static const ByteProvider kEoB2ExpObjectTbl1PC98Provider = { ARRAYSIZE(kEoB2ExpObjectTbl1PC98), kEoB2ExpObjectTbl1PC98 };
+
+static const byte kEoB2ExpObjectTbl2PC98[10] = {
+	0x0F, 0x09, 0x0F, 0x09, 0x02, 0x0A, 0x0B, 0x0A,
+	0x0B, 0x00
+};
+
+static const ByteProvider kEoB2ExpObjectTbl2PC98Provider = { ARRAYSIZE(kEoB2ExpObjectTbl2PC98), kEoB2ExpObjectTbl2PC98 };
+
+static const byte kEoB2ExpObjectTbl3PC98[11] = {
+	0x05, 0x03, 0x05, 0x03, 0x03, 0x03, 0x03, 0x0B,
+	0x03, 0x0B, 0x00
+};
+
+static const ByteProvider kEoB2ExpObjectTbl3PC98Provider = { ARRAYSIZE(kEoB2ExpObjectTbl3PC98), kEoB2ExpObjectTbl3PC98 };
+
+static const byte kEoB2ExpObjectYPC98[4] = {
+	0x77, 0x67, 0x4F, 0x3F
+};
+
+static const ByteProvider kEoB2ExpObjectYPC98Provider = { ARRAYSIZE(kEoB2ExpObjectYPC98), kEoB2ExpObjectYPC98 };
+
+static const byte kEoB2SparkDefStepsPC98[8] = {
+	0x40, 0x90, 0xE4, 0xB9, 0x6E, 0x1B, 0x06, 0x01
+};
+
+static const ByteProvider kEoB2SparkDefStepsPC98Provider = { ARRAYSIZE(kEoB2SparkDefStepsPC98), kEoB2SparkDefStepsPC98 };
+
+static const byte kEoB2SparkDefSubStepsPC98[4] = {
+	0xC0, 0x30, 0x0C, 0x03
+};
+
+static const ByteProvider kEoB2SparkDefSubStepsPC98Provider = { ARRAYSIZE(kEoB2SparkDefSubStepsPC98), kEoB2SparkDefSubStepsPC98 };
+
+static const byte kEoB2SparkDefShiftPC98[4] = {
+	0x06, 0x04, 0x02, 0x00
+};
+
+static const ByteProvider kEoB2SparkDefShiftPC98Provider = { ARRAYSIZE(kEoB2SparkDefShiftPC98), kEoB2SparkDefShiftPC98 };
+
+static const byte kEoB2SparkDefAddPC98[8] = {
+	0x08, 0x06, 0x1C, 0x14, 0x0D, 0x18, 0x0D, 0x0F
+};
+
+static const ByteProvider kEoB2SparkDefAddPC98Provider = { ARRAYSIZE(kEoB2SparkDefAddPC98), kEoB2SparkDefAddPC98 };
+
+static const byte kEoB2SparkDefXPC98[6] = {
+	0x17, 0x20, 0x17, 0x20, 0x17, 0x20
+};
+
+static const ByteProvider kEoB2SparkDefXPC98Provider = { ARRAYSIZE(kEoB2SparkDefXPC98), kEoB2SparkDefXPC98 };
+
+static const byte kEoB2SparkDefYPC98[6] = {
+	0x02, 0x02, 0x34, 0x34, 0x66, 0x66
+};
+
+static const ByteProvider kEoB2SparkDefYPC98Provider = { ARRAYSIZE(kEoB2SparkDefYPC98), kEoB2SparkDefYPC98 };
+
+static const uint32 kEoB2SparkOfFlags1PC98[11] = {
+	0x40000000, 0x95000000, 0xEA550000, 0xBFAA5400, 0x6AFFA954, 0x15AAFEA9, 0x0055ABFE, 0x000056AB,
+	0x00000156, 0x00000001, 0x00000000
+};
+
+static const Uint32Provider kEoB2SparkOfFlags1PC98Provider = { ARRAYSIZE(kEoB2SparkOfFlags1PC98), kEoB2SparkOfFlags1PC98 };
+
+static const uint32 kEoB2SparkOfFlags2PC98[16] = {
+	0xC0000000, 0x30000000, 0x0C000000, 0x03000000, 0x00C00000, 0x00300000, 0x000C0000, 0x00030000,
+	0x0000C000, 0x00003000, 0x00000C00, 0x00000300, 0x000000C0, 0x00000030, 0x0000000C, 0x00000003
+};
+
+static const Uint32Provider kEoB2SparkOfFlags2PC98Provider = { ARRAYSIZE(kEoB2SparkOfFlags2PC98), kEoB2SparkOfFlags2PC98 };
+
+static const byte kEoB2SparkOfShiftPC98[16] = {
+	0x1E, 0x1C, 0x1A, 0x18, 0x16, 0x14, 0x12, 0x10,
+	0x0E, 0x0C, 0x0A, 0x08, 0x06, 0x04, 0x02, 0x00
+};
+
+static const ByteProvider kEoB2SparkOfShiftPC98Provider = { ARRAYSIZE(kEoB2SparkOfShiftPC98), kEoB2SparkOfShiftPC98 };
+
+static const byte kEoB2SparkOfXPC98[16] = {
+	0x50, 0x70, 0x30, 0x68, 0x20, 0x60, 0x38, 0x78,
+	0x80, 0x48, 0x58, 0x28, 0x60, 0x40, 0x70, 0x48
+};
+
+static const ByteProvider kEoB2SparkOfXPC98Provider = { ARRAYSIZE(kEoB2SparkOfXPC98), kEoB2SparkOfXPC98 };
+
+static const byte kEoB2SparkOfYPC98[16] = {
+	0x31, 0x2B, 0x48, 0x17, 0x16, 0x48, 0x35, 0x1B,
+	0x43, 0x2E, 0x24, 0x28, 0x38, 0x1C, 0x16, 0x44
+};
+
+static const ByteProvider kEoB2SparkOfYPC98Provider = { ARRAYSIZE(kEoB2SparkOfYPC98), kEoB2SparkOfYPC98 };
+
+static const byte kEoB2SpellPropertiesPC98[1750] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0xFE, 0x2B, 0x16, 0x2B, 0x60, 0x00, 0xA7,
+	0x13, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x5C, 0x01, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x04, 0x2C, 0x16, 0x2B, 0xC3, 0x00,
+	0xA7, 0x13, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00,
+	0x00, 0x21, 0x00, 0x12, 0x2C, 0x16, 0x2B, 0x9D,
+	0x01, 0xA7, 0x13, 0x4C, 0x00, 0x00, 0x00, 0x95,
+	0x29, 0xBB, 0x01, 0xA7, 0x13, 0x5F, 0x02, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x1F, 0x2C, 0x16, 0x2B,
+	0xD9, 0x01, 0xA7, 0x13, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0xFA, 0x01, 0xA7, 0x13, 0x55, 0x00,
+	0x00, 0x00, 0x00, 0x01, 0x00, 0x2D, 0x2C, 0x16,
+	0x2B, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00,
+	0x00, 0x96, 0x29, 0x00, 0x00, 0x00, 0x00, 0x5C,
+	0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x2C,
+	0x16, 0x2B, 0x2D, 0x02, 0xA7, 0x13, 0x88, 0x04,
+	0x00, 0x00, 0x97, 0x29, 0xE4, 0x02, 0xA7, 0x13,
+	0x58, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x43,
+	0x2C, 0x16, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x89,
+	0x00, 0x00, 0x00, 0x98, 0x29, 0x00, 0x00, 0x00,
+	0x00, 0x4B, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x48, 0x2C, 0x16, 0x2B, 0x00, 0x00, 0x00, 0x00,
+	0x4C, 0x00, 0x00, 0x00, 0x9A, 0x29, 0x00, 0x00,
+	0x00, 0x00, 0x5F, 0x20, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x5C, 0x2C, 0x16, 0x2B, 0x2C, 0x00, 0xA7,
+	0x13, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x14, 0x40, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x6E, 0x2C, 0x16, 0x2B, 0x00, 0x00,
+	0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x5E, 0x40, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x7B, 0x2C, 0x16, 0x2B, 0x2B,
+	0x03, 0xA7, 0x13, 0x00, 0x01, 0x00, 0x00, 0x00,
+	0x00, 0x4D, 0x03, 0xA7, 0x13, 0x60, 0x00, 0x00,
+	0x00, 0x00, 0x11, 0x00, 0x8D, 0x2C, 0x16, 0x2B,
+	0x02, 0x00, 0xA7, 0x13, 0x00, 0x10, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0x2C, 0x16,
+	0x2B, 0x82, 0x03, 0xA7, 0x13, 0x00, 0x01, 0x00,
+	0x00, 0x00, 0x00, 0xA4, 0x03, 0xA7, 0x13, 0x63,
+	0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0xA3, 0x2C,
+	0x16, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x48, 0x02,
+	0x00, 0x00, 0x99, 0x29, 0x00, 0x00, 0x00, 0x00,
+	0x64, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xA9,
+	0x2C, 0x16, 0x2B, 0xD3, 0x03, 0xA7, 0x13, 0x00,
+	0x01, 0x00, 0x00, 0x00, 0x00, 0xF5, 0x03, 0xA7,
+	0x13, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0xB5, 0x2C, 0x16, 0x2B, 0x00, 0x00, 0x00, 0x00,
+	0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x5E, 0x40, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0xCD, 0x2C, 0x16, 0x2B, 0xFA, 0x04, 0xA7,
+	0x13, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x1C,
+	0x05, 0xA7, 0x13, 0x47, 0x00, 0x00, 0x00, 0x00,
+	0x03, 0x00, 0xDC, 0x2C, 0x16, 0x2B, 0x4B, 0x05,
+	0xA7, 0x13, 0x88, 0x04, 0x00, 0x00, 0x9B, 0x29,
+	0x04, 0x06, 0xA7, 0x13, 0x66, 0x00, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0xEB, 0x2C, 0x16, 0x2B, 0x2B,
+	0x06, 0xA7, 0x13, 0x00, 0x01, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0xF0, 0x2C, 0x16, 0x2B,
+	0x89, 0x06, 0xA7, 0x13, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0xAB, 0x06, 0xA7, 0x13, 0x59, 0x00,
+	0x00, 0x00, 0x00, 0x41, 0x00, 0xFA, 0x2C, 0x16,
+	0x2B, 0x00, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00,
+	0x00, 0x9C, 0x29, 0x00, 0x00, 0x00, 0x00, 0x08,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2D,
+	0x16, 0x2B, 0x3C, 0x07, 0xA7, 0x13, 0x20, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D,
+	0x2D, 0x16, 0x2B, 0x9F, 0x07, 0xA7, 0x13, 0x00,
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x76, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00,
+	0x2A, 0x2D, 0x16, 0x2B, 0x94, 0x08, 0xA7, 0x13,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xB6, 0x08,
+	0xA7, 0x13, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x37, 0x2D, 0x16, 0x2B, 0xD0, 0x09, 0xA7,
+	0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x45, 0x2D, 0x16, 0x2B, 0x25, 0x0B,
+	0xA7, 0x13, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x52, 0x2D, 0x16, 0x2B, 0x89,
+	0x0B, 0xA7, 0x13, 0x00, 0x01, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x00, 0x61, 0x2D, 0x16, 0x2B,
+	0xF1, 0x0B, 0xA7, 0x13, 0x20, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x2D, 0x16,
+	0x2B, 0x0D, 0x0C, 0xA7, 0x13, 0x4C, 0x00, 0x00,
+	0x00, 0x9D, 0x29, 0x1C, 0x0C, 0xA7, 0x13, 0x49,
+	0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x2D,
+	0x16, 0x2B, 0x2B, 0x0C, 0xA7, 0x13, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x8C,
+	0x2D, 0x16, 0x2B, 0x98, 0x0C, 0xA7, 0x13, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x9C, 0x2D, 0x16, 0x2B, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xB2, 0x2D, 0x16, 0x2B, 0x00, 0x00,
+	0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x9E, 0x29,
+	0x00, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x04, 0x00,
+	0x00, 0x00, 0x00, 0xB8, 0x2D, 0x16, 0x2B, 0xDE,
+	0x0C, 0xA7, 0x13, 0x00, 0x01, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x00, 0xCB, 0x2D, 0x16, 0x2B,
+	0xF6, 0x0C, 0xA7, 0x13, 0x20, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xDD, 0x2D, 0x16,
+	0x2B, 0x9D, 0x01, 0xA7, 0x13, 0x4C, 0x00, 0x00,
+	0x00, 0x95, 0x29, 0xBB, 0x01, 0xA7, 0x13, 0x5F,
+	0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0x2D,
+	0x16, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x29, 0x00,
+	0x00, 0x00, 0x9F, 0x29, 0x00, 0x00, 0x00, 0x00,
+	0x6E, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0xFF,
+	0x2D, 0x16, 0x2B, 0x1A, 0x0D, 0xA7, 0x13, 0x39,
+	0x00, 0x00, 0x00, 0xA0, 0x29, 0xC9, 0x0D, 0xA7,
+	0x13, 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x03, 0x2E, 0x16, 0x2B, 0xEF, 0x0D, 0xA7, 0x13,
+	0x08, 0x24, 0x00, 0x00, 0xA1, 0x29, 0xE4, 0x02,
+	0xA7, 0x13, 0x63, 0x00, 0x00, 0x00, 0x00, 0x21,
+	0x00, 0x0F, 0x2E, 0x16, 0x2B, 0xD3, 0x03, 0xA7,
+	0x13, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xF5,
+	0x03, 0xA7, 0x13, 0x65, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x1B, 0x2E, 0x16, 0x2B, 0x99, 0x0E,
+	0xA7, 0x13, 0x28, 0x00, 0x00, 0x00, 0xA2, 0x29,
+	0xF2, 0x0E, 0xA7, 0x13, 0x6F, 0x00, 0x20, 0x00,
+	0x00, 0x00, 0x00, 0x27, 0x2E, 0x16, 0x2B, 0x08,
+	0x0F, 0xA7, 0x13, 0x40, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x33, 0x2E, 0x16, 0x2B,
+	0x02, 0x00, 0xA7, 0x13, 0x00, 0x10, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x2E, 0x16,
+	0x2B, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00,
+	0x00, 0xA3, 0x29, 0x00, 0x00, 0x00, 0x00, 0x71,
+	0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x51, 0x2E,
+	0x16, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x00,
+	0x00, 0x00, 0xA4, 0x29, 0x00, 0x00, 0x00, 0x00,
+	0x5B, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x58,
+	0x2E, 0x16, 0x2B, 0x38, 0x0F, 0xA7, 0x13, 0x40,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x69, 0x2E, 0x16, 0x2B, 0x95, 0x0F, 0xA7, 0x13,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x01,
+	0x00, 0x7E, 0x2E, 0x16, 0x2B, 0x71, 0x0F, 0xA7,
+	0x13, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x92, 0x2E, 0x16, 0x2B, 0xAE, 0x0F,
+	0xA7, 0x13, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0xA4, 0x2E, 0x16, 0x2B, 0x00,
+	0x00, 0x00, 0x00, 0x48, 0x02, 0x00, 0x00, 0x9F,
+	0x29, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x08,
+	0x00, 0x00, 0x00, 0x00, 0xC4, 0x2E, 0x16, 0x2B,
+	0xD1, 0x0F, 0xA7, 0x13, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6D, 0x00,
+	0x00, 0x00, 0x00, 0x01, 0x00, 0xDA, 0x2E, 0x16,
+	0x2B, 0xEA, 0x0F, 0xA7, 0x13, 0x20, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6A,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0x2E,
+	0x16, 0x2B, 0x0E, 0x10, 0xA7, 0x13, 0x00, 0x01,
+	0x00, 0x00, 0x00, 0x00, 0x30, 0x10, 0xA7, 0x13,
+	0x62, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0xFC,
+	0x2E, 0x16, 0x2B, 0x4F, 0x10, 0xA7, 0x13, 0x20,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x07, 0x2F, 0x16, 0x2B, 0x2B, 0x0C, 0xA7, 0x13,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x01,
+	0x00, 0x13, 0x2F, 0x16, 0x2B, 0x0D, 0x0C, 0xA7,
+	0x13, 0x4C, 0x00, 0x00, 0x00, 0x9D, 0x29, 0x1C,
+	0x0C, 0xA7, 0x13, 0x49, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x1F, 0x2F, 0x16, 0x2B, 0x84, 0x10,
+	0xA7, 0x13, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x24, 0x2F, 0x16, 0x2B, 0x97,
+	0x10, 0xA7, 0x13, 0x20, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x29, 0x2F, 0x16, 0x2B,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2F, 0x16,
+	0x2B, 0xA8, 0x13, 0xA7, 0x13, 0x20, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5B,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x2F,
+	0x16, 0x2B, 0xE6, 0x10, 0xA7, 0x13, 0x00, 0x10,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F,
+	0x2F, 0x16, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xCD, 0x13, 0xA7,
+	0x13, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x50, 0x2F, 0x16, 0x2B, 0x00, 0x00, 0x00, 0x00,
+	0x4C, 0x00, 0x00, 0x00, 0xA5, 0x29, 0x00, 0x00,
+	0x00, 0x00, 0x5B, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x5F, 0x2F, 0x16, 0x2B, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D,
+	0x14, 0xA7, 0x13, 0x62, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x60, 0x2F, 0x16, 0x2B, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x6A, 0x12, 0xA7, 0x13, 0x65, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x61, 0x2F, 0x16, 0x2B, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0xF6, 0x11, 0xA7, 0x13, 0x65, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x62, 0x2F, 0x16, 0x2B,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xE1, 0x12, 0xA7, 0x13, 0x65, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x2F, 0x16,
+	0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x43, 0x13, 0xA7, 0x13, 0x65,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const ByteProvider kEoB2SpellPropertiesPC98Provider = { ARRAYSIZE(kEoB2SpellPropertiesPC98), kEoB2SpellPropertiesPC98 };
+
+static const byte kEoB2MagicFlightPropsPC98[56] = {
+	0x04, 0xFF, 0x41, 0x0C, 0x0B, 0xFF, 0x04, 0x06,
+	0x0D, 0xFF, 0x7A, 0x09, 0x0F, 0xFF, 0x4A, 0x08,
+	0x11, 0x06, 0x38, 0x0A, 0x14, 0x06, 0x7A, 0x0B,
+	0x18, 0xFF, 0x4A, 0x08, 0x36, 0xFF, 0x4B, 0x09,
+	0x3F, 0xFF, 0x4B, 0x09, 0x41, 0x06, 0x4B, 0x09,
+	0x42, 0x06, 0x4B, 0x08, 0x43, 0x06, 0x4B, 0x08,
+	0x44, 0x06, 0x4B, 0x08, 0x45, 0x06, 0x4B, 0x08
+};
+
+static const ByteProvider kEoB2MagicFlightPropsPC98Provider = { ARRAYSIZE(kEoB2MagicFlightPropsPC98), kEoB2MagicFlightPropsPC98 };
+
+static const byte kEoB2TurnUndeadEffectPC98[140] = {
+	0x0A, 0x07, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0D, 0x0A,
+	0x07, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0x10, 0x0D, 0x0A, 0x07,
+	0x04, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0x13, 0x10, 0x0D, 0x0A, 0x07, 0x04,
+	0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0x14, 0x13, 0x10, 0x0D, 0x0A, 0x07, 0x04, 0x00,
+	0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x63, 0x14,
+	0x13, 0x10, 0x0D, 0x0A, 0x07, 0x04, 0x00, 0x00,
+	0x00, 0xFF, 0xFF, 0x00, 0x63, 0x63, 0x14, 0x13,
+	0x10, 0x0D, 0x0A, 0x07, 0x04, 0x00, 0x00, 0x00,
+	0x00, 0xFF, 0x63, 0x63, 0x63, 0x14, 0x13, 0x10,
+	0x0D, 0x0A, 0x07, 0x04, 0x04, 0x00, 0x00, 0x00,
+	0x63, 0x63, 0x63, 0x63, 0x14, 0x13, 0x10, 0x0D,
+	0x0A, 0x07, 0x07, 0x04, 0x04, 0x00, 0x63, 0x63,
+	0x63, 0x63, 0x63, 0x14, 0x13, 0x10, 0x0D, 0x0A,
+	0x0A, 0x07, 0x07, 0x04
+};
+
+static const ByteProvider kEoB2TurnUndeadEffectPC98Provider = { ARRAYSIZE(kEoB2TurnUndeadEffectPC98), kEoB2TurnUndeadEffectPC98 };
+
+static const byte kEoB2BurningHandsDestPC98[32] = {
+	0x02, 0x03, 0x04, 0x01, 0x02, 0x05, 0x00, 0x00,
+	0x00, 0x02, 0x04, 0x01, 0x03, 0x05, 0x00, 0x00,
+	0x00, 0x01, 0x04, 0x02, 0x03, 0x05, 0x00, 0x00,
+	0x01, 0x03, 0x04, 0x00, 0x02, 0x05, 0x00, 0x00
+};
+
+static const ByteProvider kEoB2BurningHandsDestPC98Provider = { ARRAYSIZE(kEoB2BurningHandsDestPC98), kEoB2BurningHandsDestPC98 };
+
+static const byte kEoB2ConeOfColdDest1PC98[7] = {
+	0xE0, 0xC0, 0xC1, 0xBF, 0xA0, 0x9F, 0xA1
+};
+
+static const ByteProvider kEoB2ConeOfColdDest1PC98Provider = { ARRAYSIZE(kEoB2ConeOfColdDest1PC98), kEoB2ConeOfColdDest1PC98 };
+
+static const byte kEoB2ConeOfColdDest2PC98[7] = {
+	0x01, 0x02, 0xE2, 0x22, 0x03, 0xE3, 0x23
+};
+
+static const ByteProvider kEoB2ConeOfColdDest2PC98Provider = { ARRAYSIZE(kEoB2ConeOfColdDest2PC98), kEoB2ConeOfColdDest2PC98 };
+
+static const byte kEoB2ConeOfColdDest3PC98[7] = {
+	0x20, 0x40, 0x3F, 0x41, 0x60, 0x5F, 0x61
+};
+
+static const ByteProvider kEoB2ConeOfColdDest3PC98Provider = { ARRAYSIZE(kEoB2ConeOfColdDest3PC98), kEoB2ConeOfColdDest3PC98 };
+
+static const byte kEoB2ConeOfColdDest4PC98[7] = {
+	0xFF, 0xFE, 0x1E, 0xDE, 0xFD, 0x1D, 0xDD
+};
+
+static const ByteProvider kEoB2ConeOfColdDest4PC98Provider = { ARRAYSIZE(kEoB2ConeOfColdDest4PC98), kEoB2ConeOfColdDest4PC98 };
+
+static const byte kEoB2ConeOfColdGfxTblPC98[8] = {
+	0x0F, 0x09, 0x0F, 0x09, 0x02, 0x0A, 0x02, 0x00
+};
+
+static const ByteProvider kEoB2ConeOfColdGfxTblPC98Provider = { ARRAYSIZE(kEoB2ConeOfColdGfxTblPC98), kEoB2ConeOfColdGfxTblPC98 };
+
+static const byte kEoB2DscDoorShapeIndexPC98[53] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x01
+};
+
+static const ByteProvider kEoB2DscDoorShapeIndexPC98Provider = { ARRAYSIZE(kEoB2DscDoorShapeIndexPC98), kEoB2DscDoorShapeIndexPC98 };
+
+static const byte kEoB2WllFlagPresetPC98[25] = {
+	0x07, 0x00, 0x40, 0xA8, 0x88, 0x88, 0x88, 0x9F,
+	0xA8, 0x88, 0x88, 0x88, 0x9F, 0xA8, 0x88, 0x88,
+	0x88, 0x9F, 0xA8, 0x88, 0x88, 0x88, 0x9F, 0x03,
+	0x03
+};
+
+static const ByteProvider kEoB2WllFlagPresetPC98Provider = { ARRAYSIZE(kEoB2WllFlagPresetPC98), kEoB2WllFlagPresetPC98 };
+
+static const uint16 kEoB2DscShapeCoordsPC98[180] = {
+	0xFF91, 0xFFC1, 0xFFA1, 0xFFC1, 0xFF75, 0xFFC5, 0xFF8B, 0xFFC5,
+	0xFF88, 0xFFC3, 0xFFB4, 0xFFC1, 0xFFC4, 0xFFC1, 0xFFA1, 0xFFC5,
+	0xFFB6, 0xFFC5, 0xFFB0, 0xFFC3, 0xFFD5, 0xFFC1, 0xFFE5, 0xFFC1,
+	0xFFCB, 0xFFC5, 0xFFE1, 0xFFC5, 0xFFD8, 0xFFC3, 0xFFF8, 0xFFC1,
+	0x0008, 0xFFC1, 0xFFF6, 0xFFC5, 0x000A, 0xFFC5, 0x0000, 0xFFC3,
+	0x001B, 0xFFC1, 0x002B, 0xFFC1, 0x001F, 0xFFC5, 0x0035, 0xFFC5,
+	0x0028, 0xFFC3, 0x003C, 0xFFC1, 0x004C, 0xFFC1, 0x004A, 0xFFC5,
+	0x005F, 0xFFC5, 0x0050, 0xFFC3, 0x005F, 0xFFC1, 0x006F, 0xFFC1,
+	0x0075, 0xFFC5, 0x008B, 0xFFC5, 0x0078, 0xFFC3, 0xFF8A, 0xFFCB,
+	0xFFA4, 0xFFCB, 0xFF68, 0xFFD3, 0xFF88, 0xFFD3, 0xFF8A, 0xFFCE,
+	0xFFBE, 0xFFCB, 0xFFD8, 0xFFCB, 0xFFAC, 0xFFD3, 0xFFCD, 0xFFD3,
+	0xFFC5, 0xFFCE, 0xFFF3, 0xFFCB, 0x000D, 0xFFCB, 0xFFF0, 0xFFD3,
+	0x0010, 0xFFD3, 0x0000, 0xFFCE, 0x0028, 0xFFCB, 0x0042, 0xFFCB,
+	0x0033, 0xFFD3, 0x0054, 0xFFD3, 0x003B, 0xFFCE, 0x005C, 0xFFCB,
+	0x0076, 0xFFCB, 0x0078, 0xFFD3, 0x0098, 0xFFD3, 0x0076, 0xFFCE,
+	0xFF92, 0xFFDD, 0xFFBD, 0xFFDD, 0xFF74, 0xFFEA, 0xFFAD, 0xFFEA,
+	0xFF9E, 0xFFE2, 0xFFEA, 0xFFDD, 0x0016, 0xFFDD, 0xFFE5, 0xFFEA,
+	0x001B, 0xFFEA, 0x0000, 0xFFE2, 0x0043, 0xFFDD, 0x006E, 0xFFDD,
+	0x0053, 0xFFEA, 0x008C, 0xFFEA, 0x0062, 0xFFE2, 0xFF80, 0xFFFC,
+	0x0080, 0xFFFC, 0xFF80, 0xFFBE, 0x0080, 0xFFBE, 0x0080, 0x0000,
+	0xFFDA, 0xFFFC, 0x0026, 0xFFFC, 0xFFDA, 0xFFBE, 0x0026, 0xFFBE,
+	0x0000, 0x0000, 0xFF80, 0xFFFC, 0x0080, 0xFFFC, 0xFF80, 0xFFBE,
+	0x0080, 0xFFBE, 0x0080, 0x0000
+};
+
+static const Uint16Provider kEoB2DscShapeCoordsPC98Provider = { ARRAYSIZE(kEoB2DscShapeCoordsPC98), kEoB2DscShapeCoordsPC98 };
+
+static const byte kEoB2DscDoorScaleOffsPC98[53] = {
+	0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03,
+	0x08, 0x08, 0x08, 0x08, 0x08, 0x0D, 0x0D, 0x0D,
+	0x0D, 0x0D, 0x12, 0x12, 0x12, 0x12, 0x12, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x33, 0x34
+};
+
+static const ByteProvider kEoB2DscDoorScaleOffsPC98Provider = { ARRAYSIZE(kEoB2DscDoorScaleOffsPC98), kEoB2DscDoorScaleOffsPC98 };
+
+static const byte kEoB2DscDoorScaleMult1PC98[4] = {
+	0x08, 0x0C, 0x12, 0x00
+};
+
+static const ByteProvider kEoB2DscDoorScaleMult1PC98Provider = { ARRAYSIZE(kEoB2DscDoorScaleMult1PC98), kEoB2DscDoorScaleMult1PC98 };
+
+static const byte kEoB2DscDoorScaleMult2PC98[4] = {
+	0x00, 0x02, 0x04, 0x00
+};
+
+static const ByteProvider kEoB2DscDoorScaleMult2PC98Provider = { ARRAYSIZE(kEoB2DscDoorScaleMult2PC98), kEoB2DscDoorScaleMult2PC98 };
+
+static const byte kEoB2DscDoorScaleMult3PC98[4] = {
+	0x05, 0x08, 0x0C, 0x00
+};
+
+static const ByteProvider kEoB2DscDoorScaleMult3PC98Provider = { ARRAYSIZE(kEoB2DscDoorScaleMult3PC98), kEoB2DscDoorScaleMult3PC98 };
+
+static const byte kEoB2DscDoorType5OffsPC98[6] = {
+	0x05, 0x03, 0x01, 0x05, 0x03, 0x01
+};
+
+static const ByteProvider kEoB2DscDoorType5OffsPC98Provider = { ARRAYSIZE(kEoB2DscDoorType5OffsPC98), kEoB2DscDoorType5OffsPC98 };
+
+static const byte kEoB2DscDoorY1PC98[4] = {
+	0x3B, 0x47, 0x56, 0x00
+};
+
+static const ByteProvider kEoB2DscDoorY1PC98Provider = { ARRAYSIZE(kEoB2DscDoorY1PC98), kEoB2DscDoorY1PC98 };
+
+static const byte kEoB2DscDoorY2PC98[4] = {
+	0x1F, 0x18, 0x0F, 0x00
+};
+
+static const ByteProvider kEoB2DscDoorY2PC98Provider = { ARRAYSIZE(kEoB2DscDoorY2PC98), kEoB2DscDoorY2PC98 };
+
+static const byte kEoB2DscDoorFrameY1PC98[4] = {
+	0x1E, 0x18, 0x10, 0x00
+};
+
+static const ByteProvider kEoB2DscDoorFrameY1PC98Provider = { ARRAYSIZE(kEoB2DscDoorFrameY1PC98), kEoB2DscDoorFrameY1PC98 };
+
+static const byte kEoB2DscDoorFrameY2PC98[4] = {
+	0x3B, 0x47, 0x56, 0x78
+};
+
+static const ByteProvider kEoB2DscDoorFrameY2PC98Provider = { ARRAYSIZE(kEoB2DscDoorFrameY2PC98), kEoB2DscDoorFrameY2PC98 };
+
+static const byte kEoB2DscItemPosIndexPC98[16] = {
+	0x00, 0x01, 0x02, 0x03, 0x02, 0x00, 0x03, 0x01,
+	0x03, 0x02, 0x01, 0x00, 0x01, 0x03, 0x00, 0x02
+};
+
+static const ByteProvider kEoB2DscItemPosIndexPC98Provider = { ARRAYSIZE(kEoB2DscItemPosIndexPC98), kEoB2DscItemPosIndexPC98 };
+
+static const uint16 kEoB2DscItemShpXPC98[18] = {
+	0xFFC8, 0xFFF8, 0x0028, 0x0058, 0x0088, 0x00B8, 0x00E8, 0xFFB8,
+	0x0008, 0x0058, 0x00A8, 0x00F8, 0xFFD8, 0x0058, 0x00D8, 0xFFA8,
+	0x0058, 0x0108
+};
+
+static const Uint16Provider kEoB2DscItemShpXPC98Provider = { ARRAYSIZE(kEoB2DscItemShpXPC98), kEoB2DscItemShpXPC98 };
+
+static const byte kEoB2DscItemScaleIndexPC98[18] = {
+	0xFF, 0xFF, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02,
+	0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xFF, 0xFF,
+	0x25, 0x00
+};
+
+static const ByteProvider kEoB2DscItemScaleIndexPC98Provider = { ARRAYSIZE(kEoB2DscItemScaleIndexPC98), kEoB2DscItemScaleIndexPC98 };
+
+static const byte kEoB2DscItemTileIndexPC98[18] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0x09, 0xFF, 0x0B,
+	0x0C, 0x0D
+};
+
+static const ByteProvider kEoB2DscItemTileIndexPC98Provider = { ARRAYSIZE(kEoB2DscItemTileIndexPC98), kEoB2DscItemTileIndexPC98 };
+
+static const byte kEoB2DscItemShapeMapPC98[113] = {
+	0x00, 0x00, 0x00, 0x05, 0x01, 0x02, 0x03, 0x04,
+	0x03, 0x05, 0x06, 0x06, 0x12, 0x05, 0x0F, 0x12,
+	0x14, 0x08, 0x13, 0x11, 0x15, 0x18, 0x07, 0x07,
+	0x07, 0x1F, 0x09, 0x23, 0x09, 0x09, 0x09, 0x09,
+	0x08, 0x1C, 0x1C, 0x1A, 0x1B, 0x21, 0x1D, 0x1D,
+	0x22, 0x22, 0x22, 0x00, 0x16, 0x00, 0x00, 0x17,
+	0x17, 0x17, 0x17, 0x17, 0x22, 0x21, 0x19, 0x23,
+	0x10, 0x1E, 0x17, 0x25, 0x17, 0x26, 0x12, 0x21,
+	0x17, 0x23, 0x1C, 0x00, 0x20, 0x25, 0x12, 0x18,
+	0x1F, 0x07, 0x07, 0x15, 0x15, 0x0F, 0x03, 0x09,
+	0x1E, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B,
+	0x1B, 0x17, 0x17, 0x16, 0x16, 0x21, 0x08, 0x25,
+	0x25, 0x25, 0x25, 0x28, 0x03, 0x04, 0x21, 0x00,
+	0x17, 0x00, 0x20, 0x24, 0x27, 0x27, 0x1C, 0x27,
+	0x00
+};
+
+static const ByteProvider kEoB2DscItemShapeMapPC98Provider = { ARRAYSIZE(kEoB2DscItemShapeMapPC98), kEoB2DscItemShapeMapPC98 };
+
+static const byte kEoB2DscTelptrShpCoordsPC98[156] = {
+	0x0C, 0x07, 0x1A, 0x01, 0x3E, 0x03, 0x0C, 0x1A,
+	0x2A, 0x13, 0x40, 0x18, 0x02, 0x2D, 0x16, 0x25,
+	0x28, 0x32, 0x36, 0x27, 0x0A, 0x3E, 0x16, 0x49,
+	0x3E, 0x44, 0x06, 0x06, 0x2A, 0x04, 0x37, 0x0A,
+	0x04, 0x1B, 0x1A, 0x16, 0x37, 0x1D, 0x0E, 0x2A,
+	0x1B, 0x35, 0x2E, 0x28, 0x42, 0x30, 0x06, 0x47,
+	0x06, 0x47, 0x2D, 0x4C, 0x0A, 0x04, 0x14, 0x00,
+	0x2E, 0x01, 0x0C, 0x10, 0x1F, 0x10, 0x2F, 0x10,
+	0x12, 0x18, 0x28, 0x1D, 0x01, 0x21, 0x08, 0x2A,
+	0x11, 0x32, 0x2F, 0x2E, 0x1F, 0x25, 0x02, 0x02,
+	0x01, 0x11, 0x01, 0x2F, 0x08, 0x1E, 0x11, 0x0E,
+	0x11, 0x26, 0x1C, 0x01, 0x1E, 0x19, 0x1F, 0x33,
+	0x24, 0x11, 0x26, 0x05, 0x28, 0x2B, 0x2F, 0x22,
+	0x00, 0x13, 0x05, 0x01, 0x06, 0x08, 0x09, 0x0C,
+	0x04, 0x1A, 0x08, 0x1F, 0x12, 0x05, 0x12, 0x15,
+	0x16, 0x10, 0x1A, 0x08, 0x1A, 0x1D, 0x0A, 0x00,
+	0x0A, 0x00, 0x00, 0x09, 0x00, 0x1E, 0x04, 0x11,
+	0x08, 0x16, 0x08, 0x06, 0x10, 0x00, 0x11, 0x0D,
+	0x12, 0x20, 0x15, 0x02, 0x14, 0x09, 0x16, 0x1B,
+	0x1A, 0x14, 0x1A, 0x14
+};
+
+static const ByteProvider kEoB2DscTelptrShpCoordsPC98Provider = { ARRAYSIZE(kEoB2DscTelptrShpCoordsPC98), kEoB2DscTelptrShpCoordsPC98 };
+
+static const byte kEoB2PortalSeqDataPC98[126] = {
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
+	0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00,
+	0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00,
+	0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x01, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04,
+	0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x03, 0x02,
+	0x02, 0x03, 0x01, 0x04, 0x00, 0x02, 0x01, 0x03,
+	0x02, 0x04, 0x03, 0x02, 0x04, 0x03, 0x03, 0x04,
+	0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03,
+	0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x03, 0x07,
+	0x02, 0x08, 0x02, 0x08, 0x01, 0x09, 0x01, 0x09,
+	0x01, 0x09, 0x00, 0x0A, 0xFF, 0xFF
+};
+
+static const ByteProvider kEoB2PortalSeqDataPC98Provider = { ARRAYSIZE(kEoB2PortalSeqDataPC98), kEoB2PortalSeqDataPC98 };
+
+static const byte kEoB2DscMonsterFrmOffsTbl1PC98[32] = {
+	0x04, 0xFE, 0x01, 0x03, 0x03, 0x04, 0xFE, 0x01,
+	0x01, 0x03, 0x04, 0xFE, 0xFE, 0x01, 0x03, 0x04,
+	0xFC, 0xFD, 0xFF, 0x02, 0x02, 0xFC, 0xFD, 0xFF,
+	0xFF, 0x02, 0xFC, 0xFD, 0xFD, 0xFF, 0x02, 0xFC
+};
+
+static const ByteProvider kEoB2DscMonsterFrmOffsTbl1PC98Provider = { ARRAYSIZE(kEoB2DscMonsterFrmOffsTbl1PC98), kEoB2DscMonsterFrmOffsTbl1PC98 };
+
+static const byte kEoB2DscMonsterFrmOffsTbl2PC98[32] = {
+	0x04, 0xFE, 0x01, 0x03, 0x03, 0x04, 0xFE, 0x01,
+	0x01, 0x03, 0x04, 0xFE, 0xFE, 0x01, 0x03, 0x04,
+	0x04, 0xFD, 0x01, 0x02, 0x02, 0x04, 0xFD, 0x01,
+	0x01, 0x02, 0x04, 0xFD, 0xFD, 0x01, 0x02, 0x04
+};
+
+static const ByteProvider kEoB2DscMonsterFrmOffsTbl2PC98Provider = { ARRAYSIZE(kEoB2DscMonsterFrmOffsTbl2PC98), kEoB2DscMonsterFrmOffsTbl2PC98 };
+
+static const uint16 kEoB2InvSlotXPC98[27] = {
+	0x00E6, 0x0116, 0x00B5, 0x00C7, 0x00B5, 0x00C7, 0x00B5, 0x00C7,
+	0x00B5, 0x00C7, 0x00B5, 0x00C7, 0x00B5, 0x00C7, 0x00B5, 0x00C7,
+	0x00E1, 0x00E0, 0x00E1, 0x012A, 0x011F, 0x0115, 0x012C, 0x012C,
+	0x012C, 0x00E4, 0x00F0
+};
+
+static const Uint16Provider kEoB2InvSlotXPC98Provider = { ARRAYSIZE(kEoB2InvSlotXPC98), kEoB2InvSlotXPC98 };
+
+static const byte kEoB2InvSlotYPC98[27] = {
+	0x74, 0x74, 0x28, 0x28, 0x3A, 0x3A, 0x4C, 0x4C,
+	0x5E, 0x5E, 0x70, 0x70, 0x82, 0x82, 0x94, 0x94,
+	0x38, 0x4C, 0x60, 0x37, 0x4B, 0x89, 0x5E, 0x70,
+	0x82, 0x88, 0x88
+};
+
+static const ByteProvider kEoB2InvSlotYPC98Provider = { ARRAYSIZE(kEoB2InvSlotYPC98), kEoB2InvSlotYPC98 };
+
+static const uint16 kEoB2SlotValidationFlagsPC98[27] = {
+	0x0008, 0x0008, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+	0x0001, 0x0002, 0x0004, 0x0020, 0x0040, 0x0010, 0xFFFF, 0x0080,
+	0x0080, 0x0100, 0x0100
+};
+
+static const Uint16Provider kEoB2SlotValidationFlagsPC98Provider = { ARRAYSIZE(kEoB2SlotValidationFlagsPC98), kEoB2SlotValidationFlagsPC98 };
+
+static const byte kEoB2ProjectileWeaponTypesPC98[15] = {
+	0xFF, 0x01, 0xFF, 0x02, 0x04, 0x03, 0x05, 0x10,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x12
+};
+
+static const ByteProvider kEoB2ProjectileWeaponTypesPC98Provider = { ARRAYSIZE(kEoB2ProjectileWeaponTypesPC98), kEoB2ProjectileWeaponTypesPC98 };
+
+static const byte kEoB2WandTypesPC98[8] = {
+	0x00, 0x11, 0x17, 0x31, 0x0D, 0xFF, 0x04, 0x0C
+};
+
+static const ByteProvider kEoB2WandTypesPC98Provider = { ARRAYSIZE(kEoB2WandTypesPC98), kEoB2WandTypesPC98 };
+
+static const byte kEoB2DrawObjPosIndexPC98[20] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x02, 0x00, 0x03,
+	0x01, 0x04, 0x02, 0x03, 0x00, 0x01, 0x04, 0x01,
+	0x03, 0x00, 0x02, 0x04
+};
+
+static const ByteProvider kEoB2DrawObjPosIndexPC98Provider = { ARRAYSIZE(kEoB2DrawObjPosIndexPC98), kEoB2DrawObjPosIndexPC98 };
+
+static const byte kEoB2FlightObjFlipIndexPC98[16] = {
+	0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01,
+	0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00
+};
+
+static const ByteProvider kEoB2FlightObjFlipIndexPC98Provider = { ARRAYSIZE(kEoB2FlightObjFlipIndexPC98), kEoB2FlightObjFlipIndexPC98 };
+
+static const byte kEoB2FlightObjShpMapPC98[45] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x02,
+	0xFF, 0xFF, 0x04, 0xFF, 0x06, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0x00, 0x00, 0x00, 0x00
+};
+
+static const ByteProvider kEoB2FlightObjShpMapPC98Provider = { ARRAYSIZE(kEoB2FlightObjShpMapPC98), kEoB2FlightObjShpMapPC98 };
+
+static const byte kEoB2FlightObjSclIndexPC98[72] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03,
+	0xFF, 0xFF, 0x03, 0x03, 0xFF, 0xFF, 0x03, 0x03,
+	0xFF, 0xFF, 0x03, 0x03, 0xFF, 0xFF, 0x03, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+	0x02, 0x02, 0x02, 0x02, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0x01, 0xFF, 0x01, 0x01, 0x01, 0x01, 0x01,
+	0x01, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static const ByteProvider kEoB2FlightObjSclIndexPC98Provider = { ARRAYSIZE(kEoB2FlightObjSclIndexPC98), kEoB2FlightObjSclIndexPC98 };
+
+static const uint16 kEoB2TransferPortraitFramesPC98[32] = {
+	0x0004, 0x0018, 0x009D, 0x0038, 0x00A2, 0x0018, 0x013C, 0x0039,
+	0x0004, 0x0040, 0x009D, 0x0060, 0x00A2, 0x0040, 0x013C, 0x0060,
+	0x0004, 0x0068, 0x009D, 0x0089, 0x00A2, 0x0068, 0x013C, 0x0089,
+	0x0004, 0x0094, 0x002E, 0x009E, 0x0110, 0x0094, 0x013A, 0x009E
+};
+
+static const Uint16Provider kEoB2TransferPortraitFramesPC98Provider = { ARRAYSIZE(kEoB2TransferPortraitFramesPC98), kEoB2TransferPortraitFramesPC98 };
+
+static const byte kEoB2TransferConvertTablePC98[49] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0x05, 0x06,
+	0x0A, 0x00, 0x0B, 0x00, 0x0C, 0x0D, 0x00, 0x0E,
+	0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x00, 0x00,
+	0x17, 0x18, 0x22, 0x24, 0x23, 0x25, 0x26, 0x27,
+	0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
+	0x31, 0x30, 0x32, 0x33, 0x00, 0x35, 0x34, 0x36,
+	0x37
+};
+
+static const ByteProvider kEoB2TransferConvertTablePC98Provider = { ARRAYSIZE(kEoB2TransferConvertTablePC98), kEoB2TransferConvertTablePC98 };
+
+static const byte kEoB2TransferItemTablePC98[60] = {
+	0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+	0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+	0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
+	0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+	0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01,
+	0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01,
+	0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00
+};
+
+static const ByteProvider kEoB2TransferItemTablePC98Provider = { ARRAYSIZE(kEoB2TransferItemTablePC98), kEoB2TransferItemTablePC98 };
+
+static const uint32 kEoB2TransferExpTablePC98[6] = {
+	0x000F423F, 0x000B71AF, 0x000DBB9F, 0x0006B6BF, 0x000DBB9F, 0x000DBB9F
+};
+
+static const Uint32Provider kEoB2TransferExpTablePC98Provider = { ARRAYSIZE(kEoB2TransferExpTablePC98), kEoB2TransferExpTablePC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData00PC98[5] = {
+	{ 0x01,   0, 208,  16,   1,   1,   0,   0,   0,   0 },
+	{ 0x01,   0, 208,  16,   1,   1,   0,   0,   0,   0 },
+	{ 0x00,   0, 216,  51,   8,   0,   0,   0,   0,   0 },
+	{ 0x06,   5,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData00PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData00PC98), kEoB2IntroAnimData00PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData01PC98[5] = {
+	{ 0x01,   1, 184,   8,   1,   1,   0,   0,   0,   0 },
+	{ 0x01,   1, 184,   8,   1,   1,   0,   0,   0,   0 },
+	{ 0x00,   0, 216,  51,   7,   0,   0,   0,   0,   0 },
+	{ 0x06,   6,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData01PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData01PC98), kEoB2IntroAnimData01PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData02PC98[5] = {
+	{ 0x01,   2, 216,  51,   1,   0,   0,   0,   0,   0 },
+	{ 0x01,   2, 216,  51,   1,   0,   0,   0,   0,   0 },
+	{ 0x00,   0, 216,  51,  18,   0,   0,   0,   0,   0 },
+	{ 0x06,   8,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData02PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData02PC98), kEoB2IntroAnimData02PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData03PC98[4] = {
+	{ 0x00,   0,   0,   0,   1,   2,   0,   0,   0,   0 },
+	{ 0x06,   5,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x00,   0,   0,   0,   1,   2,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData03PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData03PC98), kEoB2IntroAnimData03PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData04PC98[3] = {
+	{ 0x01,   0, 282,   8,   1,   0,   0,   0,   0,   0 },
+	{ 0x01,   0, 282,   8,   1,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData04PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData04PC98), kEoB2IntroAnimData04PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData05PC98[4] = {
+	{ 0x00,   0,   0,   0,   1,   1,   0,   0,   0,   0 },
+	{ 0x00,   0,   0,   0,   1,   1,   0,   0,   0,   0 },
+	{ 0x06,   7,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData05PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData05PC98), kEoB2IntroAnimData05PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData06PC98[16] = {
+	{ 0x01,  12, 170,  11,   2,   0,   0,   0,   0,   0 },
+	{ 0x01,  12, 188,  38,   2,   0,   0,   0,   0,   0 },
+	{ 0x01,  12, 203,  63,   2,   0,   0,   0,   0,   0 },
+	{ 0x01,  12, 218,  85,   2,   0,   0,   0,   0,   0 },
+	{ 0x01,  12, 232, 107,   2,   0,   0,   0,   0,   0 },
+	{ 0x06,   9,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x01,   3, 242, 125,   2,   0,   0,   0,   0,   0 },
+	{ 0x01,   4, 240, 125,   3,   0,   0,   0,   0,   0 },
+	{ 0x01,   5, 238, 124,   3,   0,   0,   0,   0,   0 },
+	{ 0x01,   6, 236, 123,   3,   0,   0,   0,   0,   0 },
+	{ 0x01,   7, 232, 121,   3,   0,   0,   0,   0,   0 },
+	{ 0x01,   8, 226, 120,   4,   0,   0,   0,   0,   0 },
+	{ 0x01,   9, 220, 119,   4,   0,   0,   0,   0,   0 },
+	{ 0x01,  10, 241, 115,   4,   0,   0,   0,   0,   0 },
+	{ 0x01,  11, 241, 115,   4,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData06PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData06PC98), kEoB2IntroAnimData06PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData07PC98[5] = {
+	{ 0x01,  12,  54,  17,   2,   0,   0,   0,   0,   0 },
+	{ 0x01,  12,  80,  53,   2,   0,   0,   0,   0,   0 },
+	{ 0x01,  12, 104,  87,   2,   0,   0,   0,   0,   0 },
+	{ 0x01,  12, 130, 124,   2,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData07PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData07PC98), kEoB2IntroAnimData07PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData08PC98[6] = {
+	{ 0x01,  12, 112,   8,   2,   0,   0,   0,   0,   0 },
+	{ 0x01,  12, 126,  34,   2,   0,   0,   0,   0,   0 },
+	{ 0x01,  12, 142,  60,   2,   0,   0,   0,   0,   0 },
+	{ 0x01,  12, 170, 109,   2,   0,   0,   0,   0,   0 },
+	{ 0x06,   9,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData08PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData08PC98), kEoB2IntroAnimData08PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData09PC98[4] = {
+	{ 0x05,   0, 184,  64,   6,   0,  30,   8,   3,  16 },
+	{ 0x05,   0, 184,  64,   6,   0,  33,   8,   3,  16 },
+	{ 0x05,   0, 184,  64,   6,   0,  36,   8,   3,  16 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData09PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData09PC98), kEoB2IntroAnimData09PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData10PC98[10] = {
+	{ 0x05,   0, 184,  64,   0,   0,  30,   8,   3,  16 },
+	{ 0x05,   0, 128,  88,   3,   0,  30,  32,   4,  24 },
+	{ 0x05,   0, 128,  88,   3,   0,  34,  32,   4,  24 },
+	{ 0x05,   0, 184,  64,   0,   0,  33,   8,   3,  16 },
+	{ 0x05,   0, 128,  88,   3,   0,  30,  32,   4,  24 },
+	{ 0x05,   0, 128,  88,   3,   0,  34,  32,   4,  24 },
+	{ 0x05,   0, 184,  64,   0,   0,  36,   8,   3,  16 },
+	{ 0x05,   0, 128,  88,   3,   0,  30,  32,   4,  24 },
+	{ 0x05,   0, 128,  88,   3,   0,  34,  32,   4,  24 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData10PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData10PC98), kEoB2IntroAnimData10PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData11PC98[4] = {
+	{ 0x06,  10,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   0, 104,  24,   5,   0,   0,   8,  15, 112 },
+	{ 0x05,   0, 104,  24,   5,   0,  15,   8,  15, 112 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData11PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData11PC98), kEoB2IntroAnimData11PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData12PC98[6] = {
+	{ 0x05,   0, 184,  64,   0,   0,  30,   8,   3,  16 },
+	{ 0x05,   0, 104,  72,   5,   0,  10, 120,  10,  64 },
+	{ 0x05,   0, 184,  64,   0,   0,  33,   8,   3,  16 },
+	{ 0x05,   0, 104,  72,   5,   0,  20, 120,  10,  64 },
+	{ 0x05,   0, 184,  64,   0,   0,  36,   8,   3,  16 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData12PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData12PC98), kEoB2IntroAnimData12PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData13PC98[4] = {
+	{ 0x05,   0, 104,   0,   8,   0,   0,   0,  13, 200 },
+	{ 0x05,   0, 104,   0,   8,   0,  13,   0,  13, 200 },
+	{ 0x05,   0, 104,   0,   8,   0,  26,   0,  13, 200 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData13PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData13PC98), kEoB2IntroAnimData13PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData14PC98[10] = {
+	{ 0x02,   9, 224,  72,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   3, 240,  40,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   6, 112,  72,   6,   0,   0,   0,   0,   0 },
+	{ 0x02,   1, 224,  72,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   4, 240,  40,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   7, 112,  72,   6,   0,   0,   0,   0,   0 },
+	{ 0x02,   2, 224,  72,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   5, 240,  40,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   8, 112,  72,   6,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData14PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData14PC98), kEoB2IntroAnimData14PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData15PC98[11] = {
+	{ 0x02,   9, 224,  72,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   3, 240,  40,   6,   0,   0,   0,   0,   0 },
+	{ 0x06,   7,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x00,   0,   0,   0,   1,   3,   0,   0,   0,   0 },
+	{ 0x00,   0,   0,   0,   1,   3,   0,   0,   0,   0 },
+	{ 0x00,   0,   0,   0,   1,   3,   0,   0,   0,   0 },
+	{ 0x02,   1, 224,  72,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   4, 240,  40,   6,   0,   0,   0,   0,   0 },
+	{ 0x02,   2, 224,  72,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   5, 240,  40,   6,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData15PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData15PC98), kEoB2IntroAnimData15PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData16PC98[8] = {
+	{ 0x06, 255,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   9, 224,  72,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   3, 240,  40,   6,   0,   0,   0,   0,   0 },
+	{ 0x02,   1, 224,  72,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   4, 240,  40,   6,   0,   0,   0,   0,   0 },
+	{ 0x02,   2, 224,  72,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   5, 240,  40,   6,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData16PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData16PC98), kEoB2IntroAnimData16PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData17PC98[14] = {
+	{ 0x06, 255,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,  10, 136,  40,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   9, 224,  72,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   3, 240,  40,   3,   0,   0,   0,   0,   0 },
+	{ 0x02,  11, 136,  40,   3,   0,   0,   0,   0,   0 },
+	{ 0x02,  12, 136,  40,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   1, 224,  72,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   4, 240,  40,   3,   0,   0,   0,   0,   0 },
+	{ 0x02,  13, 136,  40,   3,   0,   0,   0,   0,   0 },
+	{ 0x02,  10, 136,  40,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   2, 224,  72,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   5, 240,  40,   3,   0,   0,   0,   0,   0 },
+	{ 0x02,  13, 136,  40,   3,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData17PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData17PC98), kEoB2IntroAnimData17PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData18PC98[14] = {
+	{ 0x06, 255,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,  11, 136,  40,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   9, 224,  72,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   3, 240,  40,   3,   0,   0,   0,   0,   0 },
+	{ 0x02,  10, 136,  40,   3,   0,   0,   0,   0,   0 },
+	{ 0x02,  13, 136,  40,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   1, 224,  72,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   4, 240,  40,   3,   0,   0,   0,   0,   0 },
+	{ 0x02,  12, 136,  40,   3,   0,   0,   0,   0,   0 },
+	{ 0x02,  11, 136,  40,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   2, 224,  72,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   5, 240,  40,   3,   0,   0,   0,   0,   0 },
+	{ 0x02,  13, 136,  40,   3,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData18PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData18PC98), kEoB2IntroAnimData18PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData19PC98[14] = {
+	{ 0x06, 255,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,  14, 136,  40,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   9, 224,  72,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   3, 240,  40,   3,   0,   0,   0,   0,   0 },
+	{ 0x02,  15, 136,  40,   3,   0,   0,   0,   0,   0 },
+	{ 0x02,  16, 136,  40,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   1, 224,  72,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   4, 240,  40,   3,   0,   0,   0,   0,   0 },
+	{ 0x02,  17, 136,  40,   3,   0,   0,   0,   0,   0 },
+	{ 0x02,  14, 136,  40,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   2, 224,  72,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   5, 240,  40,   3,   0,   0,   0,   0,   0 },
+	{ 0x02,  17, 136,  40,   3,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData19PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData19PC98), kEoB2IntroAnimData19PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData20PC98[2] = {
+	{ 0x05,   0, 104,  32,   0,   0,  10,   0,  10,  96 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData20PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData20PC98), kEoB2IntroAnimData20PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData21PC98[2] = {
+	{ 0x05,   0, 104,  32,   3,   0,  20,   0,  10,  96 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData21PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData21PC98), kEoB2IntroAnimData21PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData22PC98[2] = {
+	{ 0x05,   0, 104,  32,   0,   0,  30,   0,  10,  96 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData22PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData22PC98), kEoB2IntroAnimData22PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData23PC98[2] = {
+	{ 0x05,   0, 104,  32,   0,   0,   0,  96,  10,  96 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData23PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData23PC98), kEoB2IntroAnimData23PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData24PC98[2] = {
+	{ 0x05,   0, 104,  32,   3,   0,  10,  96,  10,  96 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData24PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData24PC98), kEoB2IntroAnimData24PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData25PC98[2] = {
+	{ 0x05,   0, 104,  32,   0,   0,  20,  96,  10,  96 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData25PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData25PC98), kEoB2IntroAnimData25PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData26PC98[2] = {
+	{ 0x05,   0, 104,  32,   0,   0,   0,   0,  10,  96 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData26PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData26PC98), kEoB2IntroAnimData26PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData27PC98[2] = {
+	{ 0x05,   0, 104,  32,   3,   0,   0,   0,  10,  96 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData27PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData27PC98), kEoB2IntroAnimData27PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData28PC98[2] = {
+	{ 0x05,   0, 104,  32,   4,   0,  10,   0,  10,  96 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData28PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData28PC98), kEoB2IntroAnimData28PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData29PC98[2] = {
+	{ 0x05,   0, 104,  32,   8,   0,  20,   0,  10,  96 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData29PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData29PC98), kEoB2IntroAnimData29PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData30PC98[5] = {
+	{ 0x06, 255,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   0, 176,  56,   3,   0,  19, 136,   2,   8 },
+	{ 0x05,   0, 176,  56,   3,   0,  21, 136,   2,   8 },
+	{ 0x05,   0, 176,  56,   3,   0,  23, 136,   2,   8 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData30PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData30PC98), kEoB2IntroAnimData30PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData31PC98[14] = {
+	{ 0x06, 255,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   0, 176,  56,   0,   0,  19, 136,   2,   8 },
+	{ 0x05,   0, 144,  88,   5,   0,   1, 136,   6,  40 },
+	{ 0x05,   0, 176,  56,   0,   0,  21, 136,   2,   8 },
+	{ 0x05,   0, 144,  88,   3,   0,   7, 136,   6,  40 },
+	{ 0x05,   0, 176,  56,   2,   0,  23, 136,   2,   8 },
+	{ 0x05,   0, 144,  88,   3,   0,  13, 136,   6,  40 },
+	{ 0x05,   0, 176,  56,   0,   0,  19, 136,   2,   8 },
+	{ 0x05,   0, 144,  88,   6,   0,   1, 136,   6,  40 },
+	{ 0x05,   0, 176,  56,   0,   0,  21, 136,   2,   8 },
+	{ 0x05,   0, 144,  88,   3,   0,   7, 136,   6,  40 },
+	{ 0x05,   0, 176,  56,   0,   0,  23, 136,   2,   8 },
+	{ 0x05,   0, 144,  88,   3,   0,   1, 136,   6,  40 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData31PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData31PC98), kEoB2IntroAnimData31PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData32PC98[8] = {
+	{ 0x06, 255,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   0,  80,  64,   0,   0,   1, 136,   2,  16 },
+	{ 0x05,   0, 248,  64,   3,   0,   7, 136,   2,  16 },
+	{ 0x05,   0,  80,  64,   0,   0,   3, 136,   2,  16 },
+	{ 0x05,   0, 248,  64,   3,   0,   9, 136,   2,  16 },
+	{ 0x05,   0,  80,  64,   0,   0,   5, 136,   2,  16 },
+	{ 0x05,   0, 248,  64,   3,   0,  11, 136,   2,  16 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData32PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData32PC98), kEoB2IntroAnimData32PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData33PC98[5] = {
+	{ 0x06, 255,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   0,  16,  48,   0,   0,   0, 152,  14,  48 },
+	{ 0x05,   0, 192,  48,   4,   0,  14, 128,  13,  48 },
+	{ 0x05,   0,  16,  48,   0,   0,   1,  40,  35,  48 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData33PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData33PC98), kEoB2IntroAnimData33PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData34PC98[6] = {
+	{ 0x06,   3,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   0, 168,  24,   3,   0,   0, 128,   2,  32 },
+	{ 0x05,   0, 152,  16,   3,   0,   0,  88,   5,  40 },
+	{ 0x05,   0, 128,   0,   3,   0,   0,   0,  10,  88 },
+	{ 0x05,   0, 104,   0,   3,   0,  13,   0,  14,  88 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData34PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData34PC98), kEoB2IntroAnimData34PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData35PC98[2] = {
+	{ 0x05,   0, 104,   0,   0,   0,  13,   0,  13, 200 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData35PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData35PC98), kEoB2IntroAnimData35PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData36PC98[15] = {
+	{ 0x03,  16,   8,  63,   1,   1,   0,   0,   0,   0 },
+	{ 0x03,  16,   8,  63,   1,   2,   0,   0,   0,   0 },
+	{ 0x03,  16,   8,  63,   1,   3,   0,   0,   0,   0 },
+	{ 0x03,  16,   8,  63,   1,   4,   0,   0,   0,   0 },
+	{ 0x03,  16,   8,  63,   1,   5,   0,   0,   0,   0 },
+	{ 0x03,  16,   8,  63,   1,   6,   0,   0,   0,   0 },
+	{ 0x03,  16,   8,  63,  54,   7,   0,   0,   0,   0 },
+	{ 0x03,  16,   8,  63,   1,   6,   0,   0,   0,   0 },
+	{ 0x03,  16,   8,  63,   1,   5,   0,   0,   0,   0 },
+	{ 0x03,  16,   8,  63,   1,   4,   0,   0,   0,   0 },
+	{ 0x03,  16,   8,  63,   1,   3,   0,   0,   0,   0 },
+	{ 0x03,  16,   8,  63,   1,   2,   0,   0,   0,   0 },
+	{ 0x03,  16,   8,  63,   1,   1,   0,   0,   0,   0 },
+	{ 0x03,  16,   8,  63,   1,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData36PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData36PC98), kEoB2IntroAnimData36PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData37PC98[3] = {
+	{ 0x02,  16,   8,  63,  54,   0,   0,   0,   0,   0 },
+	{ 0x07,  16,   8,  63,   0,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData37PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData37PC98), kEoB2IntroAnimData37PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData38PC98[32] = {
+	{ 0x03,  21,  21,  32,   0,   1,   0,   0,   0,   0 },
+	{ 0x03,  22,  67,  80,   1,   1,   0,   0,   0,   0 },
+	{ 0x03,  21,  21,  32,   0,   2,   0,   0,   0,   0 },
+	{ 0x03,  22,  67,  80,   1,   1,   0,   0,   0,   0 },
+	{ 0x03,  21,  21,  32,   0,   3,   0,   0,   0,   0 },
+	{ 0x03,  22,  67,  80,   1,   2,   0,   0,   0,   0 },
+	{ 0x03,  21,  21,  32,   0,   4,   0,   0,   0,   0 },
+	{ 0x03,  22,  67,  80,   1,   3,   0,   0,   0,   0 },
+	{ 0x06,  11,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x03,  21,  21,  32,   0,   5,   0,   0,   0,   0 },
+	{ 0x03,  22,  67,  80,   1,   4,   0,   0,   0,   0 },
+	{ 0x03,  21,  21,  32,   0,   6,   0,   0,   0,   0 },
+	{ 0x03,  22,  67,  80,   1,   5,   0,   0,   0,   0 },
+	{ 0x03,  21,  21,  32,   0,   7,   0,   0,   0,   0 },
+	{ 0x03,  22,  67,  80,  18,   6,   0,   0,   0,   0 },
+	{ 0x03,  21,  21,  32,   0,   7,   0,   0,   0,   0 },
+	{ 0x03,  22,  67,  80,  90,   6,   0,   0,   0,   0 },
+	{ 0x03,  21,  21,  32,   0,   6,   0,   0,   0,   0 },
+	{ 0x03,  22,  67,  80,   1,   5,   0,   0,   0,   0 },
+	{ 0x03,  21,  21,  32,   0,   5,   0,   0,   0,   0 },
+	{ 0x03,  22,  67,  80,   1,   4,   0,   0,   0,   0 },
+	{ 0x03,  21,  21,  32,   0,   4,   0,   0,   0,   0 },
+	{ 0x03,  22,  67,  80,   1,   3,   0,   0,   0,   0 },
+	{ 0x03,  21,  21,  32,   0,   3,   0,   0,   0,   0 },
+	{ 0x03,  22,  67,  80,   1,   2,   0,   0,   0,   0 },
+	{ 0x03,  21,  21,  32,   0,   2,   0,   0,   0,   0 },
+	{ 0x03,  22,  67,  80,   1,   1,   0,   0,   0,   0 },
+	{ 0x03,  21,  21,  32,   0,   1,   0,   0,   0,   0 },
+	{ 0x03,  22,  67,  80,   1,   1,   0,   0,   0,   0 },
+	{ 0x03,  21,  21,  32,   0,   0,   0,   0,   0,   0 },
+	{ 0x03,  22,  67,  80,   1,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData38PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData38PC98), kEoB2IntroAnimData38PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData39PC98[5] = {
+	{ 0x02,  21,  21,  32,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,  22,  67,  80,  90,   0,   0,   0,   0,   0 },
+	{ 0x07,  21,  21,  32,   0,   0,   0,   0,   0,   0 },
+	{ 0x07,  22,  67,  80,   0,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData39PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData39PC98), kEoB2IntroAnimData39PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData40PC98[3] = {
+	{ 0x06,   5,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x00,   0,   0,   0,   1,   2,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData40PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData40PC98), kEoB2IntroAnimData40PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData41PC98[5] = {
+	{ 0x06,   7,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x00,   0,   0,   0,   1,   3,   0,   0,   0,   0 },
+	{ 0x00,   0,   0,   0,   1,   3,   0,   0,   0,   0 },
+	{ 0x00,   0,   0,   0,   1,   3,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData41PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData41PC98), kEoB2IntroAnimData41PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData42PC98[15] = {
+	{ 0x03,  15, 100,  65,   1,   1,   0,   0,   0,   0 },
+	{ 0x03,  15, 100,  65,   1,   2,   0,   0,   0,   0 },
+	{ 0x03,  15, 100,  65,   1,   3,   0,   0,   0,   0 },
+	{ 0x03,  15, 100,  65,   1,   4,   0,   0,   0,   0 },
+	{ 0x03,  15, 100,  65,   1,   5,   0,   0,   0,   0 },
+	{ 0x03,  15, 100,  65,   1,   6,   0,   0,   0,   0 },
+	{ 0x03,  15, 100,  65,  90,   7,   0,   0,   0,   0 },
+	{ 0x03,  15, 100,  65,   1,   6,   0,   0,   0,   0 },
+	{ 0x03,  15, 100,  65,   1,   5,   0,   0,   0,   0 },
+	{ 0x03,  15, 100,  65,   1,   4,   0,   0,   0,   0 },
+	{ 0x03,  15, 100,  65,   1,   3,   0,   0,   0,   0 },
+	{ 0x03,  15, 100,  65,   1,   2,   0,   0,   0,   0 },
+	{ 0x03,  15, 100,  65,   1,   1,   0,   0,   0,   0 },
+	{ 0x03,  15, 100,  65,   1,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData42PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData42PC98), kEoB2IntroAnimData42PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData43PC98[3] = {
+	{ 0x02,  15, 100,  65,  90,   0,   0,   0,   0,   0 },
+	{ 0x07,  15, 100,  65,   0,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData43PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData43PC98), kEoB2IntroAnimData43PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData44PC98[43] = {
+	{ 0x03,  18,  44,  64,   0,   1,   0,   0,   0,   0 },
+	{ 0x03,  19, 142,  96,   0,   1,   0,   0,   0,   0 },
+	{ 0x03,  17, 151,  40,   1,   1,   0,   0,   0,   0 },
+	{ 0x03,  18,  44,  64,   0,   2,   0,   0,   0,   0 },
+	{ 0x03,  19, 142,  96,   0,   2,   0,   0,   0,   0 },
+	{ 0x03,  17, 151,  40,   1,   2,   0,   0,   0,   0 },
+	{ 0x03,  18,  44,  64,   0,   3,   0,   0,   0,   0 },
+	{ 0x03,  19, 142,  96,   0,   3,   0,   0,   0,   0 },
+	{ 0x03,  17, 151,  40,   1,   3,   0,   0,   0,   0 },
+	{ 0x03,  18,  44,  64,   0,   4,   0,   0,   0,   0 },
+	{ 0x03,  19, 142,  96,   0,   4,   0,   0,   0,   0 },
+	{ 0x03,  17, 151,  40,   1,   4,   0,   0,   0,   0 },
+	{ 0x03,  18,  44,  64,   0,   5,   0,   0,   0,   0 },
+	{ 0x03,  19, 142,  96,   0,   5,   0,   0,   0,   0 },
+	{ 0x03,  17, 151,  40,   1,   5,   0,   0,   0,   0 },
+	{ 0x03,  18,  44,  64,   0,   6,   0,   0,   0,   0 },
+	{ 0x03,  19, 142,  96,   0,   6,   0,   0,   0,   0 },
+	{ 0x03,  17, 151,  40,   1,   6,   0,   0,   0,   0 },
+	{ 0x03,  18,  44,  64,   0,   7,   0,   0,   0,   0 },
+	{ 0x03,  19, 142,  96,   0,   7,   0,   0,   0,   0 },
+	{ 0x03,  17, 151,  40,  54,   7,   0,   0,   0,   0 },
+	{ 0x03,  18,  44,  64,   0,   6,   0,   0,   0,   0 },
+	{ 0x03,  19, 142,  96,   0,   6,   0,   0,   0,   0 },
+	{ 0x03,  17, 151,  40,   1,   6,   0,   0,   0,   0 },
+	{ 0x03,  18,  44,  64,   0,   5,   0,   0,   0,   0 },
+	{ 0x03,  19, 142,  96,   0,   5,   0,   0,   0,   0 },
+	{ 0x03,  17, 151,  40,   1,   5,   0,   0,   0,   0 },
+	{ 0x03,  18,  44,  64,   0,   4,   0,   0,   0,   0 },
+	{ 0x03,  19, 142,  96,   0,   4,   0,   0,   0,   0 },
+	{ 0x03,  17, 151,  40,   1,   4,   0,   0,   0,   0 },
+	{ 0x03,  18,  44,  64,   0,   3,   0,   0,   0,   0 },
+	{ 0x03,  19, 142,  96,   0,   3,   0,   0,   0,   0 },
+	{ 0x03,  17, 151,  40,   1,   3,   0,   0,   0,   0 },
+	{ 0x03,  18,  44,  64,   0,   2,   0,   0,   0,   0 },
+	{ 0x03,  19, 142,  96,   0,   2,   0,   0,   0,   0 },
+	{ 0x03,  17, 151,  40,   1,   2,   0,   0,   0,   0 },
+	{ 0x03,  18,  44,  64,   0,   1,   0,   0,   0,   0 },
+	{ 0x03,  19, 142,  96,   0,   1,   0,   0,   0,   0 },
+	{ 0x03,  17, 151,  40,   1,   1,   0,   0,   0,   0 },
+	{ 0x03,  18,  44,  64,   0,   0,   0,   0,   0,   0 },
+	{ 0x03,  19, 142,  96,   0,   0,   0,   0,   0,   0 },
+	{ 0x03,  17, 151,  40,   1,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData44PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData44PC98), kEoB2IntroAnimData44PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData45PC98[7] = {
+	{ 0x02,  18,  44,  64,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,  19, 142,  96,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,  17, 151,  40,  54,   0,   0,   0,   0,   0 },
+	{ 0x07,  18,  44,  64,   0,   0,   0,   0,   0,   0 },
+	{ 0x07,  19, 142,  96,   0,   0,   0,   0,   0,   0 },
+	{ 0x07,  17, 151,  40,   0,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData45PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData45PC98), kEoB2IntroAnimData45PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData46PC98[15] = {
+	{ 0x03,  20,  12,  35,   1,   1,   0,   0,   0,   0 },
+	{ 0x03,  20,  12,  35,   1,   2,   0,   0,   0,   0 },
+	{ 0x03,  20,  12,  35,   1,   3,   0,   0,   0,   0 },
+	{ 0x03,  20,  12,  35,   1,   4,   0,   0,   0,   0 },
+	{ 0x03,  20,  12,  35,   1,   5,   0,   0,   0,   0 },
+	{ 0x03,  20,  12,  35,   1,   6,   0,   0,   0,   0 },
+	{ 0x03,  20,  12,  35,  54,   7,   0,   0,   0,   0 },
+	{ 0x03,  20,  12,  35,   1,   6,   0,   0,   0,   0 },
+	{ 0x03,  20,  12,  35,   1,   5,   0,   0,   0,   0 },
+	{ 0x03,  20,  12,  35,   1,   4,   0,   0,   0,   0 },
+	{ 0x03,  20,  12,  35,   1,   3,   0,   0,   0,   0 },
+	{ 0x03,  20,  12,  35,   1,   2,   0,   0,   0,   0 },
+	{ 0x03,  20,  12,  35,   1,   1,   0,   0,   0,   0 },
+	{ 0x03,  20,  12,  35,   1,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData46PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData46PC98), kEoB2IntroAnimData46PC98 };
+
+static const DarkMoonAnimCommand kEoB2IntroAnimData47PC98[3] = {
+	{ 0x02,  20,  12,  35,  54,   0,   0,   0,   0,   0 },
+	{ 0x07,  20,  12,  35,   0,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2IntroAnimData47PC98Provider = { ARRAYSIZE(kEoB2IntroAnimData47PC98), kEoB2IntroAnimData47PC98 };
+
+static const DarkMoonShapeDef kEoB2IntroShapes00PC98[15] = {
+	{    0,   1, 137,   4,  43 },
+	{    1,   5, 137,   4,  54 },
+	{    2,   9, 140,   4,   8 },
+	{    3,   1, 181,   1,   3 },
+	{    4,   9, 154,   1,   3 },
+	{    5,   9, 158,   2,   5 },
+	{    6,   9, 164,   3,   7 },
+	{    7,   9, 172,   3,  11 },
+	{    8,   9, 184,   5,  13 },
+	{    9,  15, 137,   6,  15 },
+	{   10,  15, 153,   2,  21 },
+	{   11,  17, 153,   2,  21 },
+	{   12,   1, 185,   1,   4 },
+	{   13,   1, 190,   1,   1 },
+	{    0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonShapeDefProvider kEoB2IntroShapes00PC98Provider = { ARRAYSIZE(kEoB2IntroShapes00PC98), kEoB2IntroShapes00PC98 };
+
+static const DarkMoonShapeDef kEoB2IntroShapes01PC98[2] = {
+	{    0,   1, 137,   1,  16 },
+	{    0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonShapeDefProvider kEoB2IntroShapes01PC98Provider = { ARRAYSIZE(kEoB2IntroShapes01PC98), kEoB2IntroShapes01PC98 };
+
+static const DarkMoonShapeDef kEoB2IntroShapes04PC98[5] = {
+	{   17,  21,  16,   3,   8 },
+	{   19,   5,  16,   5,   8 },
+	{   21,   0,  71,  35,  39 },
+	{   22,   0, 119,  24,  15 },
+	{    0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonShapeDefProvider kEoB2IntroShapes04PC98Provider = { ARRAYSIZE(kEoB2IntroShapes04PC98), kEoB2IntroShapes04PC98 };
+
+static const DarkMoonShapeDef kEoB2IntroShapes07PC98[18] = {
+	{   -9,   1, 136,   6,  40 },
+	{   -1,   7, 136,   6,  40 },
+	{   -2,  13, 136,   6,  40 },
+	{    3,  19, 136,   2,  16 },
+	{    4,  21, 136,   2,  16 },
+	{    5,  23, 136,   2,  16 },
+	{    6,  19, 152,   1,   8 },
+	{    7,  20, 152,   1,   8 },
+	{    8,  21, 152,   1,   8 },
+	{  -10,  25, 136,   2,  16 },
+	{  -11,  27, 136,   2,  16 },
+	{  -12,  29, 136,   2,  16 },
+	{  -13,  31, 136,   2,  16 },
+	{  -14,  25, 152,   2,  16 },
+	{  -15,  27, 152,   2,  16 },
+	{  -16,  29, 152,   2,  16 },
+	{  -17,  31, 152,   2,  16 },
+	{    0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonShapeDefProvider kEoB2IntroShapes07PC98Provider = { ARRAYSIZE(kEoB2IntroShapes07PC98), kEoB2IntroShapes07PC98 };
+
+static const DarkMoonShapeDef kEoB2IntroShapes13PC98[5] = {
+	{   15,  13,   0,  15,  16 },
+	{   16,   1,  32,  39,  18 },
+	{   18,   5,  64,  29,  16 },
+	{   20,   2,  96,  37,  74 },
+	{    0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonShapeDefProvider kEoB2IntroShapes13PC98Provider = { ARRAYSIZE(kEoB2IntroShapes13PC98), kEoB2IntroShapes13PC98 };
+
+static const DarkMoonAnimCommand kEoB2FinaleAnimData00PC98[11] = {
+	{ 0x06,   3,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   0, 136,   8,   8,   0,   5, 136,  11,  48 },
+	{ 0x02,   1, 136,   8,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   0,  80,   8,   0,   0,   0, 136,   5,  40 },
+	{ 0x06,   4,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   0, 232,  88,   4,   0,   0,  88,   8,  48 },
+	{ 0x02,   3,  80,   8,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   0, 232,  88,   4,   0,   8,  88,   8,  48 },
+	{ 0x05,   0, 232,  88,   4,   0,  16,  88,   8,  48 },
+	{ 0x02,   2, 232,  88,   4,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2FinaleAnimData00PC98Provider = { ARRAYSIZE(kEoB2FinaleAnimData00PC98), kEoB2FinaleAnimData00PC98 };
+
+static const DarkMoonAnimCommand kEoB2FinaleAnimData01PC98[9] = {
+	{ 0x06,   3,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   0, 136,   8,  12,   0,   5, 136,  11,  48 },
+	{ 0x02,   1, 136,   8,   0,   0,   0,   0,   0,   0 },
+	{ 0x06,   4,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   0, 232,  88,   6,   0,   0,  88,   8,  48 },
+	{ 0x05,   0, 232,  88,   6,   0,   8,  88,   8,  48 },
+	{ 0x05,   0, 232,  88,   6,   0,  16,  88,   8,  48 },
+	{ 0x02,   2, 232,  88,   6,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2FinaleAnimData01PC98Provider = { ARRAYSIZE(kEoB2FinaleAnimData01PC98), kEoB2FinaleAnimData01PC98 };
+
+static const DarkMoonAnimCommand kEoB2FinaleAnimData02PC98[4] = {
+	{ 0x05,   0, 232, 112,   6,   0,  24, 176,   3,  16 },
+	{ 0x05,   0, 232, 112,   6,   0,  27, 176,   3,  16 },
+	{ 0x05,   0, 232, 112,   6,   0,  30, 176,   3,  16 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2FinaleAnimData02PC98Provider = { ARRAYSIZE(kEoB2FinaleAnimData02PC98), kEoB2FinaleAnimData02PC98 };
+
+static const DarkMoonAnimCommand kEoB2FinaleAnimData03PC98[6] = {
+	{ 0x05,   0,  80,   8,   0,   0,   0, 136,   5,  40 },
+	{ 0x05,   0, 232, 112,   3,   0,  24, 176,   3,  16 },
+	{ 0x02,   3,  80,   8,   3,   0,   0,   0,   0,   0 },
+	{ 0x05,   0, 232, 112,   3,   0,  27, 176,   3,  16 },
+	{ 0x05,   0, 232, 112,   3,   0,  30, 176,   3,  16 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2FinaleAnimData03PC98Provider = { ARRAYSIZE(kEoB2FinaleAnimData03PC98), kEoB2FinaleAnimData03PC98 };
+
+static const DarkMoonAnimCommand kEoB2FinaleAnimData04PC98[8] = {
+	{ 0x06,   5,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   0, 104,  40,   3,   0,  32,  88,   8,  88 },
+	{ 0x05,   0, 104,  40,   3,   0,  32,   0,   8,  88 },
+	{ 0x05,   0, 104,  40,   3,   0,  24,   0,   8,  88 },
+	{ 0x05,   0, 104,  40,   3,   0,  16,   0,   8,  88 },
+	{ 0x05,   0, 104,  40,   3,   0,   0,   0,   8,  88 },
+	{ 0x05,   0, 104,  40,   3,   0,  24,  88,   8,  88 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2FinaleAnimData04PC98Provider = { ARRAYSIZE(kEoB2FinaleAnimData04PC98), kEoB2FinaleAnimData04PC98 };
+
+static const DarkMoonAnimCommand kEoB2FinaleAnimData05PC98[7] = {
+	{ 0x05,   0, 232, 112,   0,   0,  24, 176,   3,  16 },
+	{ 0x05,   0, 128,  40,   4,   0,  33, 176,   2,  16 },
+	{ 0x05,   0, 232, 112,   0,   0,  27, 176,   3,  16 },
+	{ 0x05,   0, 128,  40,   4,   0,  35, 176,   2,  16 },
+	{ 0x05,   0, 232, 112,   0,   0,  30, 176,   3,  16 },
+	{ 0x05,   0, 128,  40,   4,   0,  37, 176,   2,  16 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2FinaleAnimData05PC98Provider = { ARRAYSIZE(kEoB2FinaleAnimData05PC98), kEoB2FinaleAnimData05PC98 };
+
+static const DarkMoonAnimCommand kEoB2FinaleAnimData06PC98[2] = {
+	{ 0x05,   0, 104,  40,   0,   0,  16, 136,   8,  48 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2FinaleAnimData06PC98Provider = { ARRAYSIZE(kEoB2FinaleAnimData06PC98), kEoB2FinaleAnimData06PC98 };
+
+static const DarkMoonAnimCommand kEoB2FinaleAnimData07PC98[6] = {
+	{ 0x05,   0, 208,  80,   4,   0,   0, 128,   6,  56 },
+	{ 0x05,   0, 208,  80,   4,   0,   6, 128,   6,  56 },
+	{ 0x05,   0, 208,  80,   4,   0,  12, 128,   6,  56 },
+	{ 0x05,   0, 208,  80,   4,   0,  18, 128,   6,  56 },
+	{ 0x05,   0, 208,  80,   4,   0,  24, 128,   6,  56 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2FinaleAnimData07PC98Provider = { ARRAYSIZE(kEoB2FinaleAnimData07PC98), kEoB2FinaleAnimData07PC98 };
+
+static const DarkMoonAnimCommand kEoB2FinaleAnimData08PC98[3] = {
+	{ 0x05,   0, 224,  56,   3,   0,   0, 184,   4,  16 },
+	{ 0x05,   0, 224,  56,   0,   0,   4, 184,   4,  16 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2FinaleAnimData08PC98Provider = { ARRAYSIZE(kEoB2FinaleAnimData08PC98), kEoB2FinaleAnimData08PC98 };
+
+static const DarkMoonAnimCommand kEoB2FinaleAnimData09PC98[8] = {
+	{ 0x01,   1, -10,  40,   2,   0,   0,   0,   0,   0 },
+	{ 0x01,   1,   0,  40,   2,   0,   0,   0,   0,   0 },
+	{ 0x01,   1,  10,  40,   2,   0,   0,   0,   0,   0 },
+	{ 0x01,   1,  20,  40,   2,   0,   0,   0,   0,   0 },
+	{ 0x01,   1,  30,  40,   2,   0,   0,   0,   0,   0 },
+	{ 0x01,   1,  40,  40,   2,   0,   0,   0,   0,   0 },
+	{ 0x02,   2,  48,  40,   2,   0,   0,   0,   0,   0 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2FinaleAnimData09PC98Provider = { ARRAYSIZE(kEoB2FinaleAnimData09PC98), kEoB2FinaleAnimData09PC98 };
+
+static const DarkMoonAnimCommand kEoB2FinaleAnimData10PC98[14] = {
+	{ 0x05,   1,   8,  40,   0,   0,  32,   0,   8,  80 },
+	{ 0x05,   1, 280,  96,   0,   0,  24, 160,   4,  40 },
+	{ 0x05,   1,  96,  96,   3,   0,  12,  80,   1,  16 },
+	{ 0x05,   1,   8,  40,   0,   0,  32,  80,   8,  80 },
+	{ 0x05,   1, 280,  96,   0,   0,  28, 160,   4,  40 },
+	{ 0x05,   1,  96,  96,   3,   0,  13,  80,   1,  16 },
+	{ 0x05,   1,   8,  40,   0,   0,  24,  80,   8,  80 },
+	{ 0x05,   1, 280,  96,   0,   0,  32, 160,   4,  40 },
+	{ 0x05,   1,  96,  96,   3,   0,  14,  80,   1,  16 },
+	{ 0x06,   7,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   1,  40,  32,   0,   0,   0,  40,  16,  40 },
+	{ 0x05,   1, 248,  88,   0,   0,  12, 176,   6,  24 },
+	{ 0x05,   1,  96,  80,   3,   0,   0, 128,  12,  24 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2FinaleAnimData10PC98Provider = { ARRAYSIZE(kEoB2FinaleAnimData10PC98), kEoB2FinaleAnimData10PC98 };
+
+static const DarkMoonAnimCommand kEoB2FinaleAnimData11PC98[18] = {
+	{ 0x05,   1,  40,  32,   0,   0,  16,  40,  16,  40 },
+	{ 0x05,   1, 248,  88,   0,   0,  18, 176,   6,  24 },
+	{ 0x05,   1,  96,  80,   0,   0,   0, 152,  12,  24 },
+	{ 0x05,   1,   8,  40,   0,   0,  24,  80,   8,  80 },
+	{ 0x05,   1, 280,  96,   0,   0,  32, 160,   4,  40 },
+	{ 0x05,   1,  96,  96,   0,   0,  14,  80,   1,  16 },
+	{ 0x05,   0, 168,  16,   0,   0,  32,   0,   8,  80 },
+	{ 0x05,   0, 232,  16,   0,   0,  32,  80,   8,  80 },
+	{ 0x05,   1,   8,  40,   0,   0,  24,  80,   8,  80 },
+	{ 0x05,   1, 280,  96,   0,   0,  32, 160,   4,  40 },
+	{ 0x05,   1,  96,  96,   3,   0,  14,  80,   1,  16 },
+	{ 0x05,   1,   8,  40,   0,   0,  32,   0,   8,  80 },
+	{ 0x05,   1, 280,  96,   0,   0,  24, 160,   4,  40 },
+	{ 0x05,   1,  96,  96,   3,   0,  12,  80,   1,  16 },
+	{ 0x05,   1,   8,  40,   0,   0,  16,  80,   8,  80 },
+	{ 0x05,   1, 280,  96,   0,   0,  36, 160,   4,  40 },
+	{ 0x05,   1,  96,  96,   3,   0,  15,  80,   1,  16 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2FinaleAnimData11PC98Provider = { ARRAYSIZE(kEoB2FinaleAnimData11PC98), kEoB2FinaleAnimData11PC98 };
+
+static const DarkMoonAnimCommand kEoB2FinaleAnimData12PC98[23] = {
+	{ 0x05,   1,  40,  32,   0,   0,  16,  40,  16,  40 },
+	{ 0x05,   1, 248,  88,   0,   0,  18, 176,   6,  24 },
+	{ 0x05,   1,  96,  80,   0,   0,   0, 152,  12,  24 },
+	{ 0x05,   1,   8,  40,   0,   0,  24,  80,   8,  80 },
+	{ 0x05,   1, 280,  96,   0,   0,  32, 160,   4,  40 },
+	{ 0x05,   1,  96,  96,   0,   0,  14,  80,   1,  16 },
+	{ 0x05,   0, 168,  16,   0,   0,  32,   0,   8,  80 },
+	{ 0x05,   0, 232,  16,   0,   0,  32,  80,   8,  80 },
+	{ 0x06,   9,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   2, 168,  16,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   1,   8,  40,   0,   0,  32,   0,   8,  80 },
+	{ 0x05,   1, 280,  96,   0,   0,  24, 160,   4,  40 },
+	{ 0x05,   1,  96,  96,   3,   0,  12,  80,   1,  16 },
+	{ 0x05,   0, 168,  16,   0,   0,  32,   0,   8,  80 },
+	{ 0x05,   0, 232,  16,   0,   0,  32,  80,   8,  80 },
+	{ 0x06,   9,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   3, 168,  16,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   1,   8,  40,   0,   0,  16,  80,   8,  80 },
+	{ 0x05,   1, 280,  96,   0,   0,  36, 160,   4,  40 },
+	{ 0x05,   1,  96,  96,   3,   0,  15,  80,   1,  16 },
+	{ 0x05,   0, 168,  16,   0,   0,  32,   0,   8,  80 },
+	{ 0x05,   0, 232,  16,   0,   0,  32,  80,   8,  80 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2FinaleAnimData12PC98Provider = { ARRAYSIZE(kEoB2FinaleAnimData12PC98), kEoB2FinaleAnimData12PC98 };
+
+static const DarkMoonAnimCommand kEoB2FinaleAnimData13PC98[23] = {
+	{ 0x06,   9,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   1, 168,  16,   0,   0,   0,   0,   0,   0 },
+	{ 0x06,   8,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   1,  40,  32,   0,   0,   0,   0,  16,  40 },
+	{ 0x05,   1, 248,  88,   0,   0,   0, 176,   6,  24 },
+	{ 0x05,   1,  96,  80,   3,   0,   0,  80,  12,  24 },
+	{ 0x05,   0, 168,  16,   0,   0,  32,   0,   8,  80 },
+	{ 0x05,   0, 232,  16,   0,   0,  32,  80,   8,  80 },
+	{ 0x06,   9,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   3, 168,  16,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   1,  40,  32,   0,   0,  16,   0,  16,  40 },
+	{ 0x05,   1, 248,  88,   0,   0,   6, 176,   6,  24 },
+	{ 0x05,   1,  96,  80,   3,   0,   0, 104,  12,  24 },
+	{ 0x05,   0, 168,  16,   0,   0,  32,   0,   8,  80 },
+	{ 0x05,   0, 232,  16,   0,   0,  32,  80,   8,  80 },
+	{ 0x06,   9,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   2, 168,  16,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   1,  40,  32,   0,   0,   0,  40,  16,  40 },
+	{ 0x05,   1, 248,  88,   0,   0,  12, 176,   6,  24 },
+	{ 0x05,   1,  96,  80,   3,   0,   0, 128,  12,  24 },
+	{ 0x05,   0, 168,  16,   0,   0,  32,   0,   8,  80 },
+	{ 0x05,   0, 232,  16,   0,   0,  32,  80,   8,  80 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2FinaleAnimData13PC98Provider = { ARRAYSIZE(kEoB2FinaleAnimData13PC98), kEoB2FinaleAnimData13PC98 };
+
+static const DarkMoonAnimCommand kEoB2FinaleAnimData14PC98[24] = {
+	{ 0x06,   9,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   1, 168,  16,   0,   0,   0,   0,   0,   0 },
+	{ 0x06,   8,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   1,  40,  32,   0,   0,   0,   0,  16,  40 },
+	{ 0x05,   1, 248,  88,   0,   0,   0, 176,   6,  24 },
+	{ 0x05,   1,  96,  80,   3,   0,   0,  80,  12,  24 },
+	{ 0x05,   0, 168,  16,   0,   0,  32,   0,   8,  80 },
+	{ 0x05,   0, 232,  16,   0,   0,  32,  80,   8,  80 },
+	{ 0x06,   9,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   3, 168,  16,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   1,  40,  32,   0,   0,  16,   0,  16,  40 },
+	{ 0x05,   1, 248,  88,   0,   0,   6, 176,   6,  24 },
+	{ 0x05,   1,  96,  80,   3,   0,   0, 104,  12,  24 },
+	{ 0x05,   0, 168,  16,   0,   0,  32,   0,   8,  80 },
+	{ 0x05,   0, 232,  16,   0,   0,  32,  80,   8,  80 },
+	{ 0x06,   9,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   2, 168,  16,   0,   0,   0,   0,   0,   0 },
+	{ 0x06,   8,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   1,  40,  32,   0,   0,   0,  40,  16,  40 },
+	{ 0x05,   1, 248,  88,   0,   0,  12, 176,   6,  24 },
+	{ 0x05,   1,  96,  80,   3,   0,   0, 128,  12,  24 },
+	{ 0x05,   0, 168,  16,   0,   0,  32,   0,   8,  80 },
+	{ 0x05,   0, 232,  16,   0,   0,  32,  80,   8,  80 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2FinaleAnimData14PC98Provider = { ARRAYSIZE(kEoB2FinaleAnimData14PC98), kEoB2FinaleAnimData14PC98 };
+
+static const DarkMoonAnimCommand kEoB2FinaleAnimData15PC98[19] = {
+	{ 0x05,   1,  40,  32,   0,   0,   0,   0,  16,  40 },
+	{ 0x05,   1, 248,  88,   0,   0,   0, 176,   6,  24 },
+	{ 0x05,   1,  96,  80,   3,   0,   0,  80,  12,  24 },
+	{ 0x05,   0, 168,  16,   0,   0,  32,   0,   8,  80 },
+	{ 0x05,   0, 232,  16,   0,   0,  32,  80,   8,  80 },
+	{ 0x06,   9,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   3, 168,  16,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   1,  40,  32,   0,   0,  16,   0,  16,  40 },
+	{ 0x05,   1, 248,  88,   0,   0,   6, 176,   6,  24 },
+	{ 0x05,   1,  96,  80,   3,   0,   0, 104,  12,  24 },
+	{ 0x05,   0, 168,  16,   0,   0,  32,   0,   8,  80 },
+	{ 0x05,   0, 232,  16,   0,   0,  32,  80,   8,  80 },
+	{ 0x06,   8,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   1,  40,  32,   0,   0,   0,  40,  16,  40 },
+	{ 0x05,   1, 248,  88,   0,   0,  12, 176,   6,  24 },
+	{ 0x05,   1,  96,  80,   3,   0,   0, 128,  12,  24 },
+	{ 0x05,   0, 168,  16,   0,   0,  32,   0,   8,  80 },
+	{ 0x05,   0, 232,  16,   0,   0,  32,  80,   8,  80 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2FinaleAnimData15PC98Provider = { ARRAYSIZE(kEoB2FinaleAnimData15PC98), kEoB2FinaleAnimData15PC98 };
+
+static const DarkMoonAnimCommand kEoB2FinaleAnimData16PC98[13] = {
+	{ 0x06,   9,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   1, 168,  16,   3,   0,   0,   0,   0,   0 },
+	{ 0x05,   0, 168,  16,   0,   0,  32,   0,   8,  80 },
+	{ 0x05,   0, 232,  16,   0,   0,  32,  80,   8,  80 },
+	{ 0x06,   9,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   2, 168,  16,   3,   0,   0,   0,   0,   0 },
+	{ 0x05,   0, 168,  16,   0,   0,  32,   0,   8,  80 },
+	{ 0x05,   0, 232,  16,   0,   0,  32,  80,   8,  80 },
+	{ 0x06,   9,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x02,   3, 168,  16,   3,   0,   0,   0,   0,   0 },
+	{ 0x05,   0, 168,  16,   0,   0,  32,   0,   8,  80 },
+	{ 0x05,   0, 232,  16,   0,   0,  32,  80,   8,  80 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2FinaleAnimData16PC98Provider = { ARRAYSIZE(kEoB2FinaleAnimData16PC98), kEoB2FinaleAnimData16PC98 };
+
+static const DarkMoonAnimCommand kEoB2FinaleAnimData17PC98[20] = {
+	{ 0x05,   0, 168,  16,   1,   0,   0,   0,  16,  80 },
+	{ 0x05,   0, 168,  16,   2,   0,  16,   0,  16,  80 },
+	{ 0x05,   0, 168,  16,   3,   0,   0,   0,  16,  80 },
+	{ 0x05,   0, 168,  16,   1,   0,   0,   0,  16,  80 },
+	{ 0x05,   0, 168,  16,   2,   0,  16,   0,  16,  80 },
+	{ 0x05,   0, 168,  16,   3,   0,   0,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   1,   0,  16,   0,  16,  80 },
+	{ 0x05,   0, 168,  16,   2,   0,   0,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   3,   0,  16,   0,  16,  80 },
+	{ 0x05,   0, 168,  16,   1,   0,   0,   0,  16,  80 },
+	{ 0x05,   0, 168,  16,   2,   0,  16,   0,  16,  80 },
+	{ 0x06,  12,   0,   0,   0,   0,   0,   0,   0,   0 },
+	{ 0x05,   0, 168,  16,   3,   0,   0,   0,  16,  80 },
+	{ 0x05,   0, 168,  16,   1,   0,  16,   0,  16,  80 },
+	{ 0x05,   0, 168,  16,   2,   0,   0,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   3,   0,  16,   0,  16,  80 },
+	{ 0x05,   0, 168,  16,   1,   0,   0,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   2,   0,  16,   0,  16,  80 },
+	{ 0x05,   0, 168,  16,   3,   0,  16,   0,  16,  80 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2FinaleAnimData17PC98Provider = { ARRAYSIZE(kEoB2FinaleAnimData17PC98), kEoB2FinaleAnimData17PC98 };
+
+static const DarkMoonAnimCommand kEoB2FinaleAnimData18PC98[28] = {
+	{ 0x05,   0, 168,  16,   1,   0,   0,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   2,   0,   0,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   3,   0,  16,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   1,   0,   0,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   2,   0,  16,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   3,   0,   0,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   1,   0,  16,   0,  16,  80 },
+	{ 0x05,   0, 168,  16,   2,   0,   0,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   3,   0,  16,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   1,   0,   0,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   2,   0,  16,   0,  16,  80 },
+	{ 0x05,   0, 168,  16,   3,   0,   0,   0,  16,  80 },
+	{ 0x05,   0, 168,  16,   1,   0,   0,   0,  16,  80 },
+	{ 0x05,   0, 168,  16,   2,   0,  16,   0,  16,  80 },
+	{ 0x05,   0, 168,  16,   3,   0,   0,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   1,   0,  16,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   2,   0,   0,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   3,   0,  16,   0,  16,  80 },
+	{ 0x05,   0, 168,  16,   1,   0,   0,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   2,   0,  16,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   3,   0,   0,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   1,   0,  16,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   2,   0,   0,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   3,   0,  16,   0,  16,  80 },
+	{ 0x05,   0, 168,  16,   1,   0,   0,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   2,   0,  16,  80,  16,  80 },
+	{ 0x05,   0, 168,  16,   0,   0,  16,  80,  16,  80 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2FinaleAnimData18PC98Provider = { ARRAYSIZE(kEoB2FinaleAnimData18PC98), kEoB2FinaleAnimData18PC98 };
+
+static const DarkMoonAnimCommand kEoB2FinaleAnimData19PC98[7] = {
+	{ 0x05,   0,  80,  80,   4,   0,   0, 128,   6,  56 },
+	{ 0x05,   0,  80,  80,   4,   0,   6, 128,   6,  56 },
+	{ 0x05,   0,  80,  80,   4,   0,  12, 128,   6,  56 },
+	{ 0x05,   0,  80,  80,   4,   0,  18, 128,   6,  56 },
+	{ 0x05,   0,  80,  80,   4,   0,  24, 128,   6,  56 },
+	{ 0x05,   0,  80,  80,   4,   0,  30, 128,   6,  56 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2FinaleAnimData19PC98Provider = { ARRAYSIZE(kEoB2FinaleAnimData19PC98), kEoB2FinaleAnimData19PC98 };
+
+static const DarkMoonAnimCommand kEoB2FinaleAnimData20PC98[3] = {
+	{ 0x05,   0,  96,  56,   3,   0,   0, 184,   4,  16 },
+	{ 0x05,   0,  96,  56,   0,   0,   4, 184,   4,  16 },
+	{ 0xff,   0,   0,   0,   0,   0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonAnimCommandProvider kEoB2FinaleAnimData20PC98Provider = { ARRAYSIZE(kEoB2FinaleAnimData20PC98), kEoB2FinaleAnimData20PC98 };
+
+static const DarkMoonShapeDef kEoB2FinaleShapes00PC98[4] = {
+	{   -1,  16,   0,  11,  48 },
+	{   -2,  28,  80,   8,  48 },
+	{   -3,   9,   0,   5,  40 },
+	{    0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonShapeDefProvider kEoB2FinaleShapes00PC98Provider = { ARRAYSIZE(kEoB2FinaleShapes00PC98), kEoB2FinaleShapes00PC98 };
+
+static const DarkMoonShapeDef kEoB2FinaleShapes03PC98[3] = {
+	{   -1,  30,   0,   8,  96 },
+	{   -2,  30, 104,  10,  96 },
+	{    0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonShapeDefProvider kEoB2FinaleShapes03PC98Provider = { ARRAYSIZE(kEoB2FinaleShapes03PC98), kEoB2FinaleShapes03PC98 };
+
+static const DarkMoonShapeDef kEoB2FinaleShapes07PC98[4] = {
+	{    1,   0,   0,  16,  72 },
+	{    2,  16,   0,  16,  72 },
+	{    3,   0,  72,  16,  72 },
+	{    0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonShapeDefProvider kEoB2FinaleShapes07PC98Provider = { ARRAYSIZE(kEoB2FinaleShapes07PC98), kEoB2FinaleShapes07PC98 };
+
+static const DarkMoonShapeDef kEoB2FinaleShapes09PC98[9] = {
+	{    0,   0,   0,  32,  16 },
+	{    2,   0,  36,  35,  41 },
+	{    3,   0,  77,  24,  17 },
+	{    4,   0,  94,  15,  33 },
+	{    5,  24,  77,  10,  17 },
+	{    6,  16,  99,  23,  69 },
+	{  -10,   0, 136,   8,  64 },
+	{  -11,   8, 136,   8,  64 },
+	{    0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonShapeDefProvider kEoB2FinaleShapes09PC98Provider = { ARRAYSIZE(kEoB2FinaleShapes09PC98), kEoB2FinaleShapes09PC98 };
+
+static const DarkMoonShapeDef kEoB2FinaleShapes10PC98[5] = {
+	{    1,   0,   0,  40,  30 },
+	{   15,   9,  37,  21,  48 },
+	{   16,  16,  88,   6,  56 },
+	{   17,  12, 152,  14,  24 },
+	{    0,   0,   0,   0,   0 }
+};
+
+static const DarkMoonShapeDefProvider kEoB2FinaleShapes10PC98Provider = { ARRAYSIZE(kEoB2FinaleShapes10PC98), kEoB2FinaleShapes10PC98 };
+
+static const byte kEoB2NpcShapeDataPC98[24] = {
+	0x00, 0x00, 0x00, 0x05, 0x39, 0x00, 0x00, 0xFF,
+	0x00, 0x00, 0x64, 0x05, 0x4F, 0x00, 0x00, 0xFF,
+	0x00, 0x00, 0x39, 0x06, 0x2B, 0x00, 0x00, 0xFF
+};
+
+static const ByteProvider kEoB2NpcShapeDataPC98Provider = { ARRAYSIZE(kEoB2NpcShapeDataPC98), kEoB2NpcShapeDataPC98 };
+
+static const byte kEoB2ClassModifierFlagsPC98[15] = {
+	0x01, 0x20, 0x10, 0x02, 0x04, 0x08, 0x05, 0x09,
+	0x03, 0x0B, 0x0A, 0x0C, 0x07, 0x05, 0x06
+};
+
+static const ByteProvider kEoB2ClassModifierFlagsPC98Provider = { ARRAYSIZE(kEoB2ClassModifierFlagsPC98), kEoB2ClassModifierFlagsPC98 };
+
+static const byte kEoB2MonsterStepTable02PC98[8] = {
+	0xE0, 0xE1, 0x01, 0x21, 0x20, 0x1F, 0xFF, 0xDF
+};
+
+static const ByteProvider kEoB2MonsterStepTable02PC98Provider = { ARRAYSIZE(kEoB2MonsterStepTable02PC98), kEoB2MonsterStepTable02PC98 };
+
+static const byte kEoB2MonsterStepTable1PC98[16] = {
+	0x01, 0xE0, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00,
+	0x00, 0x00, 0xFF, 0x20, 0xFF, 0x00, 0x00, 0xE0
+};
+
+static const ByteProvider kEoB2MonsterStepTable1PC98Provider = { ARRAYSIZE(kEoB2MonsterStepTable1PC98), kEoB2MonsterStepTable1PC98 };
+
+static const byte kEoB2MonsterStepTable2PC98[8] = {
+	0x07, 0xFA, 0x05, 0xFC, 0x03, 0xFE, 0x01, 0x00
+};
+
+static const ByteProvider kEoB2MonsterStepTable2PC98Provider = { ARRAYSIZE(kEoB2MonsterStepTable2PC98), kEoB2MonsterStepTable2PC98 };
+
+static const byte kEoB2MonsterStepTable3PC98[8] = {
+	0xF9, 0x06, 0xFB, 0x04, 0xFD, 0x02, 0xFF, 0x00
+};
+
+static const ByteProvider kEoB2MonsterStepTable3PC98Provider = { ARRAYSIZE(kEoB2MonsterStepTable3PC98), kEoB2MonsterStepTable3PC98 };
+
+static const byte kEoB2MonsterCloseAttPosTable1PC98[4] = {
+	0x00, 0x01, 0x03, 0x02
+};
+
+static const ByteProvider kEoB2MonsterCloseAttPosTable1PC98Provider = { ARRAYSIZE(kEoB2MonsterCloseAttPosTable1PC98), kEoB2MonsterCloseAttPosTable1PC98 };
+
+static const byte kEoB2MonsterCloseAttPosTable22PC98[16] = {
+	0x00, 0x01, 0x02, 0x03, 0x00, 0x02, 0x01, 0x03,
+	0x00, 0x01, 0x02, 0x03, 0x03, 0x01, 0x02, 0x00
+};
+
+static const ByteProvider kEoB2MonsterCloseAttPosTable22PC98Provider = { ARRAYSIZE(kEoB2MonsterCloseAttPosTable22PC98), kEoB2MonsterCloseAttPosTable22PC98 };
+
+static const byte kEoB2MonsterCloseAttUnkTablePC98[12] = {
+	0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0x00, 0x02, 0xFF, 0x01
+};
+
+static const ByteProvider kEoB2MonsterCloseAttUnkTablePC98Provider = { ARRAYSIZE(kEoB2MonsterCloseAttUnkTablePC98), kEoB2MonsterCloseAttUnkTablePC98 };
+
+static const byte kEoB2MonsterCloseAttChkTable1PC98[16] = {
+	0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
+	0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00
+};
+
+static const ByteProvider kEoB2MonsterCloseAttChkTable1PC98Provider = { ARRAYSIZE(kEoB2MonsterCloseAttChkTable1PC98), kEoB2MonsterCloseAttChkTable1PC98 };
+
+static const byte kEoB2MonsterCloseAttChkTable2PC98[16] = {
+	0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01,
+	0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00
+};
+
+static const ByteProvider kEoB2MonsterCloseAttChkTable2PC98Provider = { ARRAYSIZE(kEoB2MonsterCloseAttChkTable2PC98), kEoB2MonsterCloseAttChkTable2PC98 };
+
+static const byte kEoB2MonsterCloseAttDstTable1PC98[16] = {
+	0x02, 0x03, 0x00, 0x01, 0x01, 0x02, 0x03, 0x00,
+	0x00, 0x01, 0x02, 0x03, 0x03, 0x00, 0x01, 0x02
+};
+
+static const ByteProvider kEoB2MonsterCloseAttDstTable1PC98Provider = { ARRAYSIZE(kEoB2MonsterCloseAttDstTable1PC98), kEoB2MonsterCloseAttDstTable1PC98 };
+
+static const byte kEoB2MonsterCloseAttDstTable2PC98[48] = {
+	0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x00, 0x01,
+	0x02, 0x03, 0x04, 0x05, 0x05, 0x03, 0x01, 0x04,
+	0x02, 0x00, 0x01, 0x03, 0x05, 0x00, 0x02, 0x04,
+	0x04, 0x05, 0x02, 0x03, 0x00, 0x01, 0x05, 0x04,
+	0x03, 0x02, 0x01, 0x00, 0x00, 0x02, 0x04, 0x01,
+	0x03, 0x05, 0x04, 0x02, 0x00, 0x05, 0x03, 0x01
+};
+
+static const ByteProvider kEoB2MonsterCloseAttDstTable2PC98Provider = { ARRAYSIZE(kEoB2MonsterCloseAttDstTable2PC98), kEoB2MonsterCloseAttDstTable2PC98 };
+
+static const byte kEoB2MonsterProximityTablePC98[32] = {
+	0x02, 0x03, 0x00, 0x01, 0x03, 0x02, 0x01, 0x00,
+	0x00, 0x02, 0x01, 0x03, 0x02, 0x00, 0x03, 0x01,
+	0x01, 0x00, 0x03, 0x02, 0x00, 0x01, 0x02, 0x03,
+	0x03, 0x01, 0x00, 0x02, 0x01, 0x03, 0x02, 0x00
+};
+
+static const ByteProvider kEoB2MonsterProximityTablePC98Provider = { ARRAYSIZE(kEoB2MonsterProximityTablePC98), kEoB2MonsterProximityTablePC98 };
+
+static const byte kEoB2FindBlockMonstersTablePC98[64] = {
+	0x04, 0x02, 0x01, 0x03, 0x04, 0x03, 0x00, 0x02,
+	0x04, 0x00, 0x03, 0x01, 0x04, 0x01, 0x02, 0x00,
+	0x04, 0x01, 0x02, 0x03, 0x04, 0x00, 0x03, 0x02,
+	0x04, 0x03, 0x00, 0x01, 0x04, 0x02, 0x01, 0x00,
+	0x04, 0x02, 0x01, 0x03, 0x04, 0x03, 0x00, 0x02,
+	0x04, 0x00, 0x03, 0x01, 0x04, 0x01, 0x02, 0x00,
+	0x04, 0x01, 0x02, 0x03, 0x04, 0x00, 0x03, 0x02,
+	0x04, 0x03, 0x00, 0x01, 0x04, 0x02, 0x01, 0x00
+};
+
+static const ByteProvider kEoB2FindBlockMonstersTablePC98Provider = { ARRAYSIZE(kEoB2FindBlockMonstersTablePC98), kEoB2FindBlockMonstersTablePC98 };
+
+static const byte kEoB2MonsterDirChangeTablePC98[48] = {
+	0xFF, 0x06, 0x02, 0xFF, 0x04, 0x05, 0x03, 0xFF,
+	0x00, 0x07, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xDF, 0xFF, 0xE0, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF,
+	0x01, 0x00, 0x1F, 0x00, 0x20, 0x00, 0x21, 0x00,
+	0xC0, 0xFF, 0xA0, 0xFF, 0x02, 0x00, 0x03, 0x00,
+	0x40, 0x00, 0x60, 0x00, 0xFE, 0xFF, 0xFD, 0xFF
+};
+
+static const ByteProvider kEoB2MonsterDirChangeTablePC98Provider = { ARRAYSIZE(kEoB2MonsterDirChangeTablePC98), kEoB2MonsterDirChangeTablePC98 };
+
+static const uint16 kEoB2EncodeMonsterDefsPC98[168] = {
+	0x0000, 0x0000, 0x0007, 0x0060, 0x0007, 0x0028, 0x000C, 0x0038,
+	0x0013, 0x0028, 0x000C, 0x0038, 0x001F, 0x0000, 0x0007, 0x0060,
+	0x0000, 0x0060, 0x0007, 0x0060, 0x0007, 0x0060, 0x0007, 0x0060,
+	0x000E, 0x0060, 0x0005, 0x0038, 0x0007, 0x0000, 0x0007, 0x0028,
+	0x000E, 0x0000, 0x0007, 0x0028, 0x0013, 0x0060, 0x0005, 0x0038,
+	0x0018, 0x0060, 0x0005, 0x0038, 0x001D, 0x0060, 0x0005, 0x0038,
+	0x000E, 0x0098, 0x0003, 0x0020, 0x0011, 0x00A0, 0x0004, 0x0018,
+	0x0015, 0x00A0, 0x0004, 0x0018, 0x0019, 0x0098, 0x0003, 0x0020,
+	0x001C, 0x0098, 0x0003, 0x0020, 0x001F, 0x0098, 0x0003, 0x0020,
+	0x0000, 0x0000, 0x0007, 0x0060, 0x0007, 0x0000, 0x0007, 0x0060,
+	0x000E, 0x0000, 0x0007, 0x0060, 0x0015, 0x0000, 0x0007, 0x0060,
+	0x001C, 0x0000, 0x0007, 0x0060, 0x0000, 0x0060, 0x0007, 0x0060,
+	0x0007, 0x0060, 0x0005, 0x0038, 0x000C, 0x0060, 0x0005, 0x0038,
+	0x0011, 0x0060, 0x0005, 0x0038, 0x0016, 0x0060, 0x0005, 0x0038,
+	0x001B, 0x0060, 0x0005, 0x0038, 0x0020, 0x0060, 0x0005, 0x0038,
+	0x0007, 0x0098, 0x0003, 0x0020, 0x000A, 0x0098, 0x0003, 0x0020,
+	0x000D, 0x0098, 0x0003, 0x0020, 0x0010, 0x0098, 0x0003, 0x0020,
+	0x0013, 0x0098, 0x0003, 0x0020, 0x0016, 0x0098, 0x0003, 0x0020,
+	0x0000, 0x0000, 0x000A, 0x0058, 0x000A, 0x0000, 0x000A, 0x0058,
+	0x0014, 0x0000, 0x000A, 0x0058, 0x001E, 0x0000, 0x000A, 0x0058,
+	0x0000, 0x0058, 0x000A, 0x0058, 0x000A, 0x0058, 0x000A, 0x0058
+};
+
+static const Uint16Provider kEoB2EncodeMonsterDefsPC98Provider = { ARRAYSIZE(kEoB2EncodeMonsterDefsPC98), kEoB2EncodeMonsterDefsPC98 };
+
+static const EoBCharacter kEoB2NpcPresetsPC98[6] = {
+	{ 0x00, 0x01,
+	  15, 15,  0,  0, 13, 13, 11, 11, 17, 17, 16, 16,  9,  9,
+	     3,   39, 10, 0, 10, 5, 5, -1, 8, { 6, 0, 0 },
+	  { 0x00006ADA, 0x00000000, 0x00000000 }, 0x00000000,
+	  { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0000, 0x0000 } },
+	{ 0x00, 0x01,
+	  18, 18, 36, 36, 13, 13,  8,  8, 15, 15, 16, 16, 14, 14,
+	     4,   76, 10, 0, 1, 0, 2, -2, 12, { 9, 0, 0 },
+	  { 0x0003DF35, 0x00000000, 0x00000000 }, 0x00000000,
+	  { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0000, 0x0000 } },
+	{ 0x00, 0x01,
+	  15, 15,  0,  0, 14, 14, 13, 13, 14, 14, 13, 13, 16, 16,
+	    40,   40, 10, 0, 6, 4, 3, -3, 100, { 8, 0, 0 },
+	  { 0x00021730, 0x00000000, 0x00000000 }, 0x00000000,
+	  { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0000, 0x0000 } },
+	{ 0x00, 0x01,
+	  11, 11,  0,  0, 18, 18, 13, 13, 14, 14, 16, 16,  9,  9,
+	    28,   28, 10, 0, 2, 3, 5, -4, 100, { 8, 0, 0 },
+	  { 0x00017AF0, 0x00000000, 0x00000000 }, 0x096B4566,
+	  { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0000, 0x0000 } },
+	{ 0x00, 0x01,
+	  16, 16,  0,  0, 13, 13, 16, 16, 15, 15, 11, 11, 12, 12,
+	    53,   53,  9, 0, 4, 6, 1, -5, 100, { 7, 7, 0 },
+	  { 0x00010FC2, 0x00010FC2, 0x00000000 }, 0xFFFFFFFF,
+	  { 0x0004, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0024, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0000, 0x0000 } },
+	{ 0x00, 0x01,
+	  14, 14,  0,  0, 17, 17, 12, 12, 18, 18,  9,  9, 17, 17,
+	    36,   36, 10, 0, 3, 10, 1, -6, 100, { 7, 7, 0 },
+	  { 0x0000FAE6, 0x0000FAE6, 0x00000000 }, 0x00294566,
+	  { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0000, 0x0000 } }
+};
+
+static const EoBCharacterProvider kEoB2NpcPresetsPC98Provider = { ARRAYSIZE(kEoB2NpcPresetsPC98), kEoB2NpcPresetsPC98 };
+
+static const byte kEoB2DreamStepsPC98[14] = {
+	0x01, 0x02, 0x03, 0x02, 0x01, 0x00, 0x01, 0x02,
+	0x03, 0x03, 0x02, 0x01, 0x00, 0xFF
+};
+
+static const ByteProvider kEoB2DreamStepsPC98Provider = { ARRAYSIZE(kEoB2DreamStepsPC98), kEoB2DreamStepsPC98 };
+
+static const byte kEoB2HornSoundsPC98[4] = {
+	0x40, 0x41, 0x42, 0x43
+};
+
+static const ByteProvider kEoB2HornSoundsPC98Provider = { ARRAYSIZE(kEoB2HornSoundsPC98), kEoB2HornSoundsPC98 };
+
+static const uint16 kEoB2WallOfForceDsXPC98[18] = {
+	0xFFCC, 0xFFF4, 0x001C, 0x0044, 0x006C, 0x0094, 0x00BC, 0xFFB8,
+	0xFFF8, 0x0038, 0x0078, 0x00B8, 0xFFC8, 0x0028, 0x0088, 0x0000,
+	0x0000, 0x0000
+};
+
+static const Uint16Provider kEoB2WallOfForceDsXPC98Provider = { ARRAYSIZE(kEoB2WallOfForceDsXPC98), kEoB2WallOfForceDsXPC98 };
+
+static const byte kEoB2WallOfForceDsYPC98[3] = {
+	0x20, 0x18, 0x10
+};
+
+static const ByteProvider kEoB2WallOfForceDsYPC98Provider = { ARRAYSIZE(kEoB2WallOfForceDsYPC98), kEoB2WallOfForceDsYPC98 };
+
+static const byte kEoB2WallOfForceNumWPC98[3] = {
+	0x01, 0x02, 0x03
+};
+
+static const ByteProvider kEoB2WallOfForceNumWPC98Provider = { ARRAYSIZE(kEoB2WallOfForceNumWPC98), kEoB2WallOfForceNumWPC98 };
+
+static const byte kEoB2WallOfForceNumHPC98[3] = {
+	0x02, 0x06, 0x09
+};
+
+static const ByteProvider kEoB2WallOfForceNumHPC98Provider = { ARRAYSIZE(kEoB2WallOfForceNumHPC98), kEoB2WallOfForceNumHPC98 };
+
+static const byte kEoB2WallOfForceShpIdPC98[3] = {
+	0x04, 0x02, 0x00
+};
+
+static const ByteProvider kEoB2WallOfForceShpIdPC98Provider = { ARRAYSIZE(kEoB2WallOfForceShpIdPC98), kEoB2WallOfForceShpIdPC98 };
+
+static const byte kEoB2DscShapeIndexPC98[36] = {
+	0x01, 0x01, 0x04, 0x0A, 0x04, 0x08, 0x04, 0x01,
+	0x04, 0xF8, 0x04, 0xF6, 0x00, 0x00, 0x03, 0x09,
+	0x03, 0x07, 0x03, 0x01, 0x03, 0xF9, 0x03, 0xF7,
+	0x02, 0x06, 0x02, 0x01, 0x02, 0xFA, 0x01, 0x05,
+	0x01, 0x01, 0x01, 0xFB
+};
+
+static const ByteProvider kEoB2DscShapeIndexPC98Provider = { ARRAYSIZE(kEoB2DscShapeIndexPC98), kEoB2DscShapeIndexPC98 };
+
+static const uint16 kEoB2DscXPC98[18] = {
+	0xFF70, 0xFFA0, 0xFFD0, 0x0000, 0x0030, 0x0060, 0x0090, 0xFF60,
+	0xFFB0, 0x0000, 0x0050, 0x00A0, 0xFF80, 0x0000, 0x0080, 0x0000,
+	0x0000, 0x0000
+};
+
+static const Uint16Provider kEoB2DscXPC98Provider = { ARRAYSIZE(kEoB2DscXPC98), kEoB2DscXPC98 };
+
+static const byte kEoB2DscTileIndexPC98[18] = {
+	0x00, 0x06, 0x01, 0x05, 0x02, 0x04, 0x03, 0x07,
+	0x0B, 0x08, 0x0A, 0x09, 0x0C, 0x0E, 0x0D, 0x0F,
+	0x11, 0x10
+};
+
+static const ByteProvider kEoB2DscTileIndexPC98Provider = { ARRAYSIZE(kEoB2DscTileIndexPC98), kEoB2DscTileIndexPC98 };
+
+static const byte kEoB2DscDimData1PC98[324] = {
+	0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7,
+	0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7,
+	0xD8, 0xD7, 0xD8, 0xD8, 0x02, 0xD8, 0xD8, 0xD8,
+	0xD8, 0xFE, 0xD7, 0xD8, 0xD8, 0xD8, 0xD7, 0x03,
+	0xD8, 0xFD, 0xD8, 0xD8, 0xD8, 0xFE, 0xD8, 0x08,
+	0xD8, 0xD8, 0xD8, 0xFE, 0xD7, 0x06, 0xD8, 0xD8,
+	0xFA, 0x03, 0xD8, 0xFD, 0xD8, 0xD8, 0xD8, 0xD8,
+	0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xFA, 0xD7,
+	0x10, 0xD8, 0xFD, 0xD7, 0x13, 0xD8, 0xD8, 0xD8,
+	0xD8, 0xD8, 0xD8, 0xF2, 0xD8, 0x14, 0xD8, 0xD8,
+	0xD8, 0xF0, 0xD7, 0x14, 0xD8, 0xED, 0x10, 0xD8,
+	0xD8, 0x13, 0xD8, 0xD8, 0xD8, 0xD8, 0xEC, 0xD8,
+	0xD8, 0xD8, 0xD8, 0xD8, 0xD7, 0x14, 0xD8, 0xED,
+	0xD7, 0xD8, 0xD8, 0x13, 0xD7, 0xD7, 0xD7, 0xD7,
+	0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7,
+	0xD7, 0xD7, 0xD7, 0xD7, 0xD8, 0xD7, 0xD7, 0xD7,
+	0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7,
+	0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD8, 0xD7,
+	0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8,
+	0xD8, 0x06, 0xD8, 0xD8, 0xFA, 0x03, 0xD8, 0xFD,
+	0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8,
+	0xD8, 0xD8, 0xFA, 0xD8, 0x10, 0xD8, 0xFD, 0xD7,
+	0x13, 0xFD, 0xD8, 0x13, 0xD8, 0xD8, 0xD8, 0xD8,
+	0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xF0, 0xD8, 0xD8,
+	0xD8, 0xED, 0x10, 0xD8, 0xD8, 0x13, 0xD7, 0xD7,
+	0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7,
+	0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD8, 0xD7,
+	0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8,
+	0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0x03, 0xD8, 0xFD,
+	0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8,
+	0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xFD, 0xD8,
+	0x13, 0xFD, 0xD8, 0x13, 0xD8, 0xD8, 0xD8, 0xD8,
+	0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8,
+	0xD8, 0xED, 0xD8, 0xD8, 0xD8, 0x13, 0xD8, 0xD8,
+	0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8,
+	0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8,
+	0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8,
+	0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8,
+	0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8,
+	0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8,
+	0xD8, 0xD8, 0xD8, 0xD8
+};
+
+static const ByteProvider kEoB2DscDimData1PC98Provider = { ARRAYSIZE(kEoB2DscDimData1PC98), kEoB2DscDimData1PC98 };
+
+static const byte kEoB2DscDimData2PC98[648] = {
+	0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+	0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+	0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+	0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+	0x00, 0x16, 0x16, 0x00, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x02, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x02, 0x16, 0x00, 0x04, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x16, 0x00, 0x00, 0x03,
+	0x00, 0x16, 0x03, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x02, 0x16, 0x00, 0x16, 0x00, 0x08,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x02,
+	0x16, 0x00, 0x00, 0x06, 0x00, 0x16, 0x00, 0x16,
+	0x06, 0x16, 0x00, 0x03, 0x00, 0x16, 0x03, 0x00,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x06, 0x16, 0x08, 0x0E,
+	0x00, 0x10, 0x00, 0x16, 0x03, 0x16, 0x06, 0x10,
+	0x00, 0x13, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x0E, 0x16,
+	0x00, 0x16, 0x00, 0x14, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x10, 0x16, 0x16, 0x00, 0x00, 0x14,
+	0x00, 0x16, 0x13, 0x16, 0x00, 0x10, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x13, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x14, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x12, 0x16, 0x14, 0x16, 0x00, 0x16, 0x13, 0x16,
+	0x16, 0x00, 0x00, 0x16, 0x00, 0x16, 0x00, 0x13,
+	0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+	0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+	0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+	0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+	0x00, 0x16, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+	0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+	0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+	0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+	0x16, 0x00, 0x16, 0x00, 0x00, 0x16, 0x16, 0x00,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x06, 0x00, 0x16, 0x00, 0x16,
+	0x06, 0x16, 0x00, 0x03, 0x00, 0x16, 0x03, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x06, 0x16, 0x00, 0x16,
+	0x00, 0x10, 0x00, 0x16, 0x03, 0x16, 0x07, 0x0F,
+	0x00, 0x13, 0x03, 0x16, 0x00, 0x16, 0x00, 0x13,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x10, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x13, 0x16, 0x00, 0x10, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x13, 0x16, 0x00, 0x16, 0x00,
+	0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+	0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+	0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+	0x16, 0x00, 0x16, 0x00, 0x00, 0x16, 0x16, 0x00,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x03, 0x00, 0x16, 0x03, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x03, 0x16, 0x00, 0x16,
+	0x00, 0x13, 0x03, 0x16, 0x00, 0x16, 0x00, 0x13,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x13, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x13, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16,
+	0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16
+};
+
+static const ByteProvider kEoB2DscDimData2PC98Provider = { ARRAYSIZE(kEoB2DscDimData2PC98), kEoB2DscDimData2PC98 };
+
+static const byte kEoB2DscBlockMapPC98[12] = {
+	0x02, 0x03, 0x00, 0x01, 0x01, 0x02, 0x03, 0x00,
+	0x03, 0x00, 0x01, 0x02
+};
+
+static const ByteProvider kEoB2DscBlockMapPC98Provider = { ARRAYSIZE(kEoB2DscBlockMapPC98), kEoB2DscBlockMapPC98 };
+
+static const byte kEoB2DscDimMapPC98[18] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+	0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03,
+	0x03, 0x03
+};
+
+static const ByteProvider kEoB2DscDimMapPC98Provider = { ARRAYSIZE(kEoB2DscDimMapPC98), kEoB2DscDimMapPC98 };
+
+static const byte kEoB2DscBlockIndexPC98[72] = {
+	0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xBE,
+	0xBF, 0xC0, 0xC1, 0xC2, 0xDF, 0xE0, 0xE1, 0xFF,
+	0x00, 0x01, 0xA3, 0xC3, 0xE3, 0x03, 0x23, 0x43,
+	0x63, 0xC2, 0xE2, 0x02, 0x22, 0x42, 0xE1, 0x01,
+	0x21, 0xE0, 0x00, 0x20, 0x63, 0x62, 0x61, 0x60,
+	0x5F, 0x5E, 0x5D, 0x42, 0x41, 0x40, 0x3F, 0x3E,
+	0x21, 0x20, 0x1F, 0x01, 0x00, 0xFF, 0x5D, 0x3D,
+	0x1D, 0xFD, 0xDD, 0xBD, 0x9D, 0x3E, 0x1E, 0xFE,
+	0xDE, 0xBE, 0x1F, 0xFF, 0xDF, 0x20, 0x00, 0xE0
+};
+
+static const ByteProvider kEoB2DscBlockIndexPC98Provider = { ARRAYSIZE(kEoB2DscBlockIndexPC98), kEoB2DscBlockIndexPC98 };
+
+static const char *const kEoB2Ascii2SjisTablesPC98[4] = {
+	"\x81""@""\x81""I""\x81""h""\x81\x94\x81\x90\x81\x93\x81\x95\x81""f""\x81""i""\x81""j""\x81\x96\x81""{""\x81""C""\x81""|""\x81""D""\x81""^""\x82""O""\x82""P""\x82""Q""\x82""R""\x82""S""\x82""T""\x82""U""\x82""V""\x82""W""\x82""X""\x81""F""\x81""G""\x81\x83\x81\x81\x81\x84\x81""H""\x81\x97\x82""`""\x82""a""\x82""b""\x82""c""\x82""d""\x82""e""\x82""f""\x82""g""\x82""h""\x82""i""\x82""j""\x82""k""\x82""l""\x82""m""\x82""n""\x82""o""\x82""p""\x82""q""\x82""r""\x82""s""\x82""t""\x82""u""\x82""v""\x82""w""\x82""x""\x82""y""\x81""m""\x81\x8f\x81""n""\x81""O""\x81""Q""\x81""M""\x82\x81\x82\x82\x82\x83\x82\x84\x82\x85\x82\x86\x82\x87\x82\x88\x82\x89\x82\x8a\x82\x8b\x82\x8c\x82\x8d\x82\x8e\x82\x8f\x82\x90\x82\x91\x82\x92\x82\x93\x82\x94\x82\x95\x82\x96\x82\x97\x82\x98\x82\x99\x82\x9a\x81""o""\x81""b""\x81""p""\x81""P""\x81""A""\x81""B""\x81""E""\x81""[",
+	"\x83""n""\x83""q""\x83""t""\x83""w""\x83""z",
+	"\x83""J""\x83""L""\x83""N""\x83""P""\x83""R""\x83""T""\x83""V""\x83""X""\x83""Z""\x83""\\""\x83""^""\x83""`""\x83""c""\x83""e""\x83""g""\x83""n""\x83""q""\x83""t""\x83""w""\x83""z",
+	"\x83\x92\x83""@""\x83""B""\x83""D""\x83""F""\x83""H""\x83\x83\x83\x85\x83\x87\x83""b""\x81""[""\x83""A""\x83""C""\x83""E""\x83""G""\x83""I""\x83""J""\x83""L""\x83""N""\x83""P""\x83""R""\x83""T""\x83""V""\x83""X""\x83""Z""\x83""\\""\x83""^""\x83""`""\x83""c""\x83""e""\x83""g""\x83""i""\x83""j""\x83""k""\x83""l""\x83""m""\x83""n""\x83""q""\x83""t""\x83""w""\x83""z""\x83""}""\x83""~""\x83\x80\x83\x81\x83\x82\x83\x84\x83\x86\x83\x88\x83\x89\x83\x8a\x83\x8b\x83\x8c\x83\x8d\x83\x8f\x83\x93",
+};
+
+static const StringListProvider kEoB2Ascii2SjisTablesPC98Provider = { ARRAYSIZE(kEoB2Ascii2SjisTablesPC98), kEoB2Ascii2SjisTablesPC98 };
+
+static const byte kEoB2FontConvertTblPC98[109] = {
+	0x20, 0xd4, 0xd5, 0x2c, 0x2e, 0xd6, 0x3a, 0x3b,
+	0x3f, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e,
+	0x7e, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2f, 0x00,
+	0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x27, 0x00,
+	0x22, 0x28, 0x29, 0x00, 0x00, 0x5b, 0x5d, 0x7b,
+	0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x2b, 0x2d, 0x00, 0x00, 0x00,
+	0x00, 0x3d, 0x00, 0x3c, 0x3e, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c,
+	0x24, 0x00, 0x00, 0x25, 0x23, 0x26, 0x2a, 0x40,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const ByteProvider kEoB2FontConvertTblPC98Provider = { ARRAYSIZE(kEoB2FontConvertTblPC98), kEoB2FontConvertTblPC98 };
+
+static const char *const kEoB2SoundFilesIntroPC98[1] = {
+	"INTRO"
+};
+
+static const StringListProvider kEoB2SoundFilesIntroPC98Provider = { ARRAYSIZE(kEoB2SoundFilesIntroPC98), kEoB2SoundFilesIntroPC98 };
+
+static const char *const kEoB2SoundFilesFinalePC98[2] = {
+	"FINALE1",
+	"FINALE2"
+};
+
+static const StringListProvider kEoB2SoundFilesFinalePC98Provider = { ARRAYSIZE(kEoB2SoundFilesFinalePC98), kEoB2SoundFilesFinalePC98 };
diff --git a/devtools/create_kyradat/resources/eob2_pc98_japanese.h b/devtools/create_kyradat/resources/eob2_pc98_japanese.h
new file mode 100644
index 00000000000..a15bdbf1bc0
--- /dev/null
+++ b/devtools/create_kyradat/resources/eob2_pc98_japanese.h
@@ -0,0 +1,971 @@
+static const char *const kEoB2ChargenStrings1PC98Japanese[9] = {
+	"\x83""p""\x81""[""\x83""e""\x83""B""\x82\xcc\x83\x81\x83\x93\x83""o""\x81""[""\x82\xaa\x91""S""\x88\xf5""\r""\x91\xb5\x82\xa2\x82\xdc\x82\xb5\x82\xbd\x81""B""\x82""o""\x82""k""\x82""`""\x82""x""\x83""{""\x83""^""\x83\x93""\r""\x82\xa9\x81""A""\x83""L""\x81""[""\x83""{""\x81""[""\x83""h""\x82\xcc\x82""o""\x83""L""\x81""[""\x82\xf0""\r""\x89\x9f\x82\xb7\x82\xc6\x83""Q""\x81""[""\x83\x80\x82\xaa\x8e""n""\x82\xdc\x82\xe8\x82\xdc\x82\xb7\x81""B",
+	"          ",
+	"AC\rHP\rLVL",
+	"%s\r%d\r%d\r%d\r%d\r%d",
+	"%d\r%d",
+	"%d",
+	"%d/%d",
+	"%d/%d/%d",
+	"\x83""L""\x83\x83\x83\x89\x83""N""\x83""^""\x81""[""\x82\xf0\x8d\xec\x82\xe9\x82\xbd\x82\xdf\x82\xcc""\r""\x83""{""\x83""b""\x83""N""\x83""X""\x82\xf0\x8e""w""\x92\xe8\x82\xb5\x82\xc4\x82\xad\x82\xbe\x82\xb3\x82\xa2\x81""B"
+};
+
+static const StringListProvider kEoB2ChargenStrings1PC98JapaneseProvider = { ARRAYSIZE(kEoB2ChargenStrings1PC98Japanese), kEoB2ChargenStrings1PC98Japanese };
+
+static const char *const kEoB2ChargenStrings2PC98Japanese[12] = {
+	"%s",
+	"%d",
+	"%s",
+	"%d",
+	"%d",
+	"%d",
+	"%s",
+	"%d",
+	"SELECT RACE:",
+	"SELECT CLASS:",
+	"SELECT ALIGNMENT:",
+	"\x96\xbc\x91""O""\x81""F"
+};
+
+static const StringListProvider kEoB2ChargenStrings2PC98JapaneseProvider = { ARRAYSIZE(kEoB2ChargenStrings2PC98Japanese), kEoB2ChargenStrings2PC98Japanese };
+
+static const char *const kEoB2ChargenStatStringsPC98Japanese[12] = {
+	"STR",
+	"INT",
+	"WIS",
+	"DEX",
+	"CON",
+	"CHA",
+	"STR",
+	"INT",
+	"WIS",
+	"DEX",
+	"CON",
+	"CHA"
+};
+
+static const StringListProvider kEoB2ChargenStatStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2ChargenStatStringsPC98Japanese), kEoB2ChargenStatStringsPC98Japanese };
+
+static const char *const kEoB2ChargenRaceSexStringsPC98Japanese[12] = {
+	"HUMAN MALE",
+	"HUMAN FEMALE",
+	"ELF MALE",
+	"ELF FEMALE",
+	"HALF-ELF MALE",
+	"HALF-ELF FEMALE",
+	"DWARF MALE",
+	"DWARF FEMALE",
+	"GNOME MALE",
+	"GNOME FEMALE",
+	"HALFLING MALE",
+	"HALFLING FEMALE"
+};
+
+static const StringListProvider kEoB2ChargenRaceSexStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2ChargenRaceSexStringsPC98Japanese), kEoB2ChargenRaceSexStringsPC98Japanese };
+
+static const char *const kEoB2ChargenClassStringsPC98Japanese[21] = {
+	"FIGHTER",
+	"RANGER",
+	"PALADIN",
+	"MAGE",
+	"CLERIC",
+	"THIEF",
+	"FIGHTER/CLERIC",
+	"FIGHTER/THIEF",
+	"FIGHTER/MAGE",
+	"FIGHTER/MAGE/THIEF",
+	"THIEF/MAGE",
+	"CLERIC/THIEF",
+	"FIGHTER/CLERIC/MAGE",
+	"RANGER/CLERIC",
+	"CLERIC/MAGE",
+	"FIGHTER",
+	"MAGE",
+	"CLERIC",
+	"THIEF",
+	"PALADIN",
+	"RANGER"
+};
+
+static const StringListProvider kEoB2ChargenClassStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2ChargenClassStringsPC98Japanese), kEoB2ChargenClassStringsPC98Japanese };
+
+static const char *const kEoB2ChargenAlignmentStringsPC98Japanese[9] = {
+	"LAWFUL GOOD",
+	"NEUTRAL GOOD",
+	"CHAOTIC GOOD",
+	"LAWFUL NEUTRAL",
+	"TRUE NEUTRAL",
+	"CHAOTIC NEUTRAL",
+	"LAWFUL EVIL",
+	"NEUTRAL EVIL",
+	"CHAOTIC EVIL"
+};
+
+static const StringListProvider kEoB2ChargenAlignmentStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2ChargenAlignmentStringsPC98Japanese), kEoB2ChargenAlignmentStringsPC98Japanese };
+
+static const char *const kEoB2ChargenEnterGameStringsPC98Japanese[1] = {
+	"\x83""Q""\x81""[""\x83\x80\x82\xf0\x8e""n""\x82\xdf\x82\xdc\x82\xb7\x81""B""\r""\x8f\xad\x81""X""\x82\xa8\x91\xd2\x82\xbf\x82\xad\x82\xbe\x82\xb3\x82\xa2\x81""B"
+};
+
+static const StringListProvider kEoB2ChargenEnterGameStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2ChargenEnterGameStringsPC98Japanese), kEoB2ChargenEnterGameStringsPC98Japanese };
+
+static const char *const kEoB2PryDoorStringsPC98Japanese[8] = {
+	"\r",
+	"\x97\xcd\x82\xb8\x82\xad\x82\xc5\x83""h""\x83""A""\x82\xf0\x8a""J""\x82\xaf\x82\xe9\x82\xb1\x82\xc6\x82\xcd\x82\xc5\x82\xab\x82\xbb\x82\xa4\x82\xc9\x82\xc8\x82\xa2\x81""B\r",
+	"\x06\x04\x83""h""\x83""A""\x82\xf0\x82\xb1\x82\xb6\x8a""J""\x82\xaf\x82\xbd\x81""B\r",
+	"\x06\x06\x83""h""\x83""A""\x82\xf0\x97\xcd\x82\xb8\x82\xad\x82\xc5\x8a""J""\x82\xaf\x82\xe6\x82\xa4\x82\xc6\x82\xb5\x82\xbd\x82\xaa\x8e\xb8\x94""s""\x82\xb5\x82\xbd\x81""B\r",
+	"\x83""A""\x83""C""\x83""e""\x83\x80\x82\xf0\x82\xbb\x82\xb1\x82\xc9\x92""u""\x82\xad\x82\xb1\x82\xc6\x82\xcd\x82\xc5\x82\xab\x82\xc8\x82\xa2\x81""B\r",
+	"\x82\xbb\x82\xcc\x83""A""\x83""C""\x83""e""\x83\x80\x82\xcd\x91\xe5\x82\xab\x82\xb7\x82\xac\x82\xc4\x93\xfc\x82\xe7\x82\xc8\x82\xa2\x81""B\r",
+	"\x92""N""\x82\xe0\x83""h""\x83""A""\x82\xf0\x82\xb1\x82\xb6\x8a""J""\x82\xaf\x82\xe9\x82\xb1\x82\xc6\x82\xaa\x82\xc5\x82\xab\x82\xc8\x82\xa2\x81""B\r",
+	"\r"
+};
+
+static const StringListProvider kEoB2PryDoorStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2PryDoorStringsPC98Japanese), kEoB2PryDoorStringsPC98Japanese };
+
+static const char *const kEoB2WarningStringsPC98Japanese[4] = {
+	"\x82\xbb\x82\xbf\x82\xe7\x82\xd6\x82\xcd\x8d""s""\x82\xaf\x82\xc8\x82\xa2\x81""B\r",
+	"%s""\x82\xcd\x95\xa8\x82\xf0\x90""H""\x82\xd7\x82\xe7\x82\xea\x82\xe9\x8f\xf3\x91\xd4\x82\xc5\x82\xcd\x82\xc8\x82\xa2\x81""I\r",
+	"\x82\xbb\x82\xcc\x90""H""\x97\xbf\x82\xcd\x95\x85\x82\xc1\x82\xc4\x82\xa2\x82\xe9\x81""I""\x90""H""\x82\xd7\x82\xe9\x8b""C""\x82\xaa\x82\xb5\x82\xc8\x82\xa2\x81""B\r",
+	"\x90""H""\x82\xd7\x95\xa8\x88\xc8\x8a""O""\x82\xcd\x90""H""\x82\xd7\x82\xe7\x82\xea\x82\xc8\x82\xa2\x81""I\r"
+};
+
+static const StringListProvider kEoB2WarningStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2WarningStringsPC98Japanese), kEoB2WarningStringsPC98Japanese };
+
+static const char *const kEoB2ItemSuffixStringsRingsPC98Japanese[4] = {
+	"\x83""A""\x83""h""\x81""[""\x83\x93\x83\x81\x83\x93\x83""g",
+	"\x83""E""\x83""B""\x83""U""\x81""[""\x83""h""\x83\x8a\x81""[",
+	"\x83""T""\x83""X""\x83""e""\x83""B""\x83""i""\x83\x93\x83""X",
+	"\x83""t""\x83""F""\x83""U""\x81""[""\x83""t""\x83""H""\x81""[""\x83\x8b"
+};
+
+static const StringListProvider kEoB2ItemSuffixStringsRingsPC98JapaneseProvider = { ARRAYSIZE(kEoB2ItemSuffixStringsRingsPC98Japanese), kEoB2ItemSuffixStringsRingsPC98Japanese };
+
+static const char *const kEoB2ItemSuffixStringsPotionsPC98Japanese[8] = {
+	"\x83""W""\x83\x83\x83""C""\x83""A""\x83\x93\x83""g""\x81""E""\x83""X""\x83""g""\x83\x8c\x83\x93\x83""O""\x83""X",
+	"\x83""q""\x81""[""\x83\x8a\x83\x93\x83""O",
+	"\x83""G""\x83""L""\x83""X""\x83""g""\x83\x89\x81""E""\x83""q""\x81""[""\x83\x8a\x83\x93\x83""O",
+	"\x83""|""\x83""C""\x83""Y""\x83\x93",
+	"\x83""o""\x83""C""\x83""^""\x83\x8a\x83""e""\x83""B",
+	"\x83""X""\x83""s""\x81""[""\x83""h",
+	"\x83""C""\x83\x93\x83""r""\x83""W""\x83""r""\x83\x8a\x83""e""\x83""B",
+	"\x83""L""\x83\x85\x83""A""\x81""E""\x83""|""\x83""C""\x83""Y""\x83\x93"
+};
+
+static const StringListProvider kEoB2ItemSuffixStringsPotionsPC98JapaneseProvider = { ARRAYSIZE(kEoB2ItemSuffixStringsPotionsPC98Japanese), kEoB2ItemSuffixStringsPotionsPC98Japanese };
+
+static const char *const kEoB2ItemSuffixStringsWandsPC98Japanese[8] = {
+	" Stick ",
+	" Lightning ",
+	" Frost ",
+	" Curing ",
+	" Fireball ",
+	" Starfire ",
+	" Magic Missile ",
+	" Dispel Magic "
+};
+
+static const StringListProvider kEoB2ItemSuffixStringsWandsPC98JapaneseProvider = { ARRAYSIZE(kEoB2ItemSuffixStringsWandsPC98Japanese), kEoB2ItemSuffixStringsWandsPC98Japanese };
+
+static const char *const kEoB2RipItemStringsPC98Japanese[3] = {
+	"%s""\x82\xcc",
+	"%s""\x82\xcc",
+	"\x82\xcd\x96\xb3\x82\xad\x82\xc8\x82\xc1\x82\xbd""\r"
+};
+
+static const StringListProvider kEoB2RipItemStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2RipItemStringsPC98Japanese), kEoB2RipItemStringsPC98Japanese };
+
+static const char *const kEoB2CursedStringPC98Japanese[1] = {
+	"%d""\x82\xcc\x8e\xf4\x82\xed\x82\xea\x82\xbd""%s"
+};
+
+static const StringListProvider kEoB2CursedStringPC98JapaneseProvider = { ARRAYSIZE(kEoB2CursedStringPC98Japanese), kEoB2CursedStringPC98Japanese };
+
+static const char *const kEoB2MagicObjectStringsPC98Japanese[5] = {
+	"\x83\x81\x83""C""\x83""W""\x83""X""\x83""N""\x83\x8d\x81""[""\x83\x8b",
+	"\x83""N""\x83\x8c\x83\x8a\x83""b""\x83""N""\x83""X""\x83""N""\x83\x8d\x81""[""\x83\x8b",
+	"\x83\x8a\x83\x93\x83""O",
+	"\x83""|""\x81""[""\x83""V""\x83\x87\x83\x93",
+	"\x83\x8f\x83\x93\x83""h"
+};
+
+static const StringListProvider kEoB2MagicObjectStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2MagicObjectStringsPC98Japanese), kEoB2MagicObjectStringsPC98Japanese };
+
+static const char *const kEoB2MagicObjectString5PC98Japanese[1] = {
+	"Stick"
+};
+
+static const StringListProvider kEoB2MagicObjectString5PC98JapaneseProvider = { ARRAYSIZE(kEoB2MagicObjectString5PC98Japanese), kEoB2MagicObjectString5PC98Japanese };
+
+static const char *const kEoB2PatternSuffixPC98Japanese[1] = {
+	"%s""\x82\xcc""%s"
+};
+
+static const StringListProvider kEoB2PatternSuffixPC98JapaneseProvider = { ARRAYSIZE(kEoB2PatternSuffixPC98Japanese), kEoB2PatternSuffixPC98Japanese };
+
+static const char *const kEoB2PatternGrFix1PC98Japanese[1] = {
+	"%s""\x82\xcc""%s"
+};
+
+static const StringListProvider kEoB2PatternGrFix1PC98JapaneseProvider = { ARRAYSIZE(kEoB2PatternGrFix1PC98Japanese), kEoB2PatternGrFix1PC98Japanese };
+
+static const char *const kEoB2PatternGrFix2PC98Japanese[1] = {
+	"%s""\x82\xcc""%s"
+};
+
+static const StringListProvider kEoB2PatternGrFix2PC98JapaneseProvider = { ARRAYSIZE(kEoB2PatternGrFix2PC98Japanese), kEoB2PatternGrFix2PC98Japanese };
+
+static const char *const kEoB2ValidateArmorStringPC98Japanese[1] = {
+	"%s""\x82\xcd\x82\xbb\x82\xcc\x83""^""\x83""C""\x83""v""\x82\xcc\x96""h""\x8b\xef\x82\xf0\x90""g""\x82\xc9\x82\xc2\x82\xaf\x82\xe7\x82\xea\x82\xc8\x82\xa2\x81""B\r"
+};
+
+static const StringListProvider kEoB2ValidateArmorStringPC98JapaneseProvider = { ARRAYSIZE(kEoB2ValidateArmorStringPC98Japanese), kEoB2ValidateArmorStringPC98Japanese };
+
+static const char *const kEoB2ValidateCursedStringPC98Japanese[1] = {
+	"%s""\x82\xcd\x95\x90\x8a\xed\x82\xf0\x8a""O""\x82\xb7\x82\xb1\x82\xc6\x82\xaa\x82\xc5\x82\xab\x82\xc8\x82\xa2\x81""I""\x82\xbb\x82\xcc\x95\x90\x8a\xed\x82\xcd\x8e\xf4\x82\xed\x82\xea\x82\xc4\x82\xa2\x82\xe9\x81""I\r"
+};
+
+static const StringListProvider kEoB2ValidateCursedStringPC98JapaneseProvider = { ARRAYSIZE(kEoB2ValidateCursedStringPC98Japanese), kEoB2ValidateCursedStringPC98Japanese };
+
+static const char *const kEoB2ValidateNoDropStringPC98Japanese[1] = {
+	"\x82\xbb\x82\xcc\x83""A""\x83""C""\x83""e""\x83\x80\x82\xcd\x82\xb1\x82\xb1\x82\xc9\x92""u""\x82\xaf\x82\xc8\x82\xa2\x81""B\r"
+};
+
+static const StringListProvider kEoB2ValidateNoDropStringPC98JapaneseProvider = { ARRAYSIZE(kEoB2ValidateNoDropStringPC98Japanese), kEoB2ValidateNoDropStringPC98Japanese };
+
+static const char *const kEoB2PotionStringsPC98Japanese[2] = {
+	"\x93\xC5\x82\xF0\x8E\xF3\x82\xAF\x82\xBD",
+	"%s""\x82\xcd""%s""\x81""I\r"
+};
+
+static const StringListProvider kEoB2PotionStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2PotionStringsPC98Japanese), kEoB2PotionStringsPC98Japanese };
+
+static const char *const kEoB2WandStringsPC98Japanese[1] = {
+	"\x82\xbb\x82\xcc\x83\x8f\x83\x93\x83""h""\x82\xc9\x82\xcd\x96\x82\x97\xcd\x82\xaa\x8e""c""\x82\xc1\x82\xc4\x82\xa2\x82\xc8\x82\xa2\x81""B\r"
+};
+
+static const StringListProvider kEoB2WandStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2WandStringsPC98Japanese), kEoB2WandStringsPC98Japanese };
+
+static const char *const kEoB2ItemMisuseStringsPC98Japanese[3] = {
+	"\x82\xcd\x82\xbb\x82\xcc\x83""A""\x83""C""\x83""e""\x83\x80\x82\xf0\x8e""g""\x82\xa6\x82\xc8\x82\xa2\x81""B\r",
+	"\x82\xb1\x82\xcc\x83""A""\x83""C""\x83""e""\x83\x80\x82\xcd\x90""g""\x82\xc9\x82\xc2\x82\xaf\x82\xc4\x82\xa2\x82\xea\x82\xce\x8e\xa9\x93\xae\x93""I""\x82\xc9\x8c\xf8\x89\xca\x82\xf0\x94\xad\x8a\xf6\x82\xb7\x82\xe9\x81""B\r",
+	"\x83""A""\x83""C""\x83""e""\x83\x80\x82\xcc\x8e""g""\x82\xa2\x95\xfb\x82\xaa\x8a\xd4\x88\xe1\x82\xc1\x82\xc4\x82\xa2\x82\xe9\x81""B\r"
+};
+
+static const StringListProvider kEoB2ItemMisuseStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2ItemMisuseStringsPC98Japanese), kEoB2ItemMisuseStringsPC98Japanese };
+
+static const char *const kEoB2TakenStringsPC98Japanese[1] = {
+	"\x82\xf0\x8e\xe6\x82\xc1\x82\xbd\x81""B\r"
+};
+
+static const StringListProvider kEoB2TakenStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2TakenStringsPC98Japanese), kEoB2TakenStringsPC98Japanese };
+
+static const char *const kEoB2PotionEffectStringsPC98Japanese[8] = {
+	"\x97\xcd\x82\xaa\x82\xdd\x82\xc8\x82\xac\x82\xc1\x82\xc4\x82\xab\x82\xbd",
+	"\x8b""C""\x95\xaa\x82\xaa\x82\xe6\x82\xad\x82\xc8\x82\xc1\x82\xbd",
+	"\x8b""C""\x95\xaa\x82\xaa\x82\xa9\x82\xc8\x82\xe8\x82\xe6\x82\xad\x82\xc8\x82\xc1\x82\xbd",
+	"\x8b""C""\x95\xaa\x82\xaa\x88\xab\x82\xad\x82\xc8\x82\xc1\x82\xbd",
+	"\x8b\xf3\x95\xa0\x82\xc5\x82\xc8\x82\xad\x82\xc8\x82\xc1\x82\xbd",
+	"\x91""f""\x91\x81\x82\xb3\x82\xaa\x91\x9d\x82\xb5\x82\xbd",
+	"\x91\xcc\x82\xaa\x93\xa7\x96\xbe\x82\xc9\x82\xc8\x82\xc1\x82\xbd",
+	"\x8b""C""\x95\xaa\x82\xaa\x82\xe6\x82\xad\x82\xc8\x82\xc1\x82\xbd"
+};
+
+static const StringListProvider kEoB2PotionEffectStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2PotionEffectStringsPC98Japanese), kEoB2PotionEffectStringsPC98Japanese };
+
+static const char *const kEoB2YesNoStringsPC98Japanese[2] = {
+	" ""\x82\xcd\x82\xa2"" ",
+	"\x82\xa2\x82\xa2\x82\xa6"
+};
+
+static const StringListProvider kEoB2YesNoStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2YesNoStringsPC98Japanese), kEoB2YesNoStringsPC98Japanese };
+
+static const char *const kEoB2MoreStringsPC98Japanese[1] = {
+	"\x82""l""\x82""n""\x82""q""\x82""d"
+};
+
+static const StringListProvider kEoB2MoreStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2MoreStringsPC98Japanese), kEoB2MoreStringsPC98Japanese };
+
+static const char *const kEoB2NpcMaxStringsPC98Japanese[1] = {
+		"\x83""p""\x81""[""\x83""e""\x83""B""\x82\xcc\x95\xd2\x90\xac\x82\xcd\x82""U""\x90""l""\x82\xdc\x82\xc5\x82\xc5\x82\xb7\x81""B""\x83""p""\x81""[""\x83""e""\x83""B""\x82\xa9\x82\xe7\x8a""O""\x82\xb7\x90""l""\x82\xf0\x82""P""\x90""l""\x91""I""\x82\xf1\x82\xc5\x89\xba\x82\xb3\x82\xa2\x81""B"
+};
+
+static const StringListProvider kEoB2NpcMaxStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2NpcMaxStringsPC98Japanese), kEoB2NpcMaxStringsPC98Japanese };
+
+static const char *const kEoB2OkStringsPC98Japanese[1] = {
+	"\x82""n""\x82""j"
+};
+
+static const StringListProvider kEoB2OkStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2OkStringsPC98Japanese), kEoB2OkStringsPC98Japanese };
+
+static const char *const kEoB2NpcJoinStringsPC98Japanese[1] = {
+	"%s""\x82\xaa\x83""p""\x81""[""\x83""e""\x83""B""\x82\xc9\x89\xc1\x82\xed\x82\xc1\x82\xbd\x81""B\r"
+};
+
+static const StringListProvider kEoB2NpcJoinStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2NpcJoinStringsPC98Japanese), kEoB2NpcJoinStringsPC98Japanese };
+
+static const char *const kEoB2CancelStringsPC98Japanese[1] = {
+	"\x82""b""\x82""`""\x82""m""\x82""b""\x82""d""\x82""k"
+};
+
+static const StringListProvider kEoB2CancelStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2CancelStringsPC98Japanese), kEoB2CancelStringsPC98Japanese };
+
+static const char *const kEoB2AbortStringsPC98Japanese[1] = {
+	"\x92\x86\x8e""~"
+};
+
+static const StringListProvider kEoB2AbortStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2AbortStringsPC98Japanese), kEoB2AbortStringsPC98Japanese };
+
+static const char *const kEoB2MenuStringsMainPC98Japanese[8] = {
+	"\x81""@""\x83""I""\x83""v""\x83""V""\x83\x87\x83\x93\x91""I""\x91\xf0"":",
+	"\x8b""x""\x91\xa7\x82\xb7\x82\xe9",
+	"\x8e\xf4\x95\xb6\x82\xf0\x8b""L""\x89\xaf\x82\xb7\x82\xe9",
+	"\x8b""F""\x82\xe8\x82\xf0\x95\xf9\x82\xb0\x82\xe9",
+	"\x83""X""\x83""N""\x83\x8d\x81""[""\x83\x8b\x82\xf0\x8f\x91\x82\xab\x8e\xca\x82\xb7",
+	"\x8a\xc2\x8b\xab\x90\xdd\x92\xe8",
+	"\x83""I""\x83""v""\x83""V""\x83\x87\x83\x93",
+	"\x82""d""\x82""w""\x82""h""\x82""s"
+};
+
+static const StringListProvider kEoB2MenuStringsMainPC98JapaneseProvider = { ARRAYSIZE(kEoB2MenuStringsMainPC98Japanese), kEoB2MenuStringsMainPC98Japanese };
+
+static const char *const kEoB2MenuStringsSaveLoadPC98Japanese[8] = {
+	"\x83""Q""\x81""[""\x83\x80\x83\x8d\x81""[""\x83""h",
+	"\x83""Q""\x81""[""\x83\x80\x83""Z""\x81""[""\x83""u",
+	"\x83""L""\x83\x83\x83\x89\x83""N""\x83""^""\x82\xf0\x8a""O""\x82\xb7",
+	"\x83""Q""\x81""[""\x83\x80\x82\xf0\x8f""I""\x97\xb9\x82\xb7\x82\xe9",
+	"\x81""@""\x83""I""\x83""v""\x83""V""\x83\x87\x83\x93"":",
+	"\r\r""\x81""@""\x81""@""\x81""@""\x81""@""\x83""Z""\x81""[""\x83""u""\x8f""I""\x97\xb9",
+	"\r""\x81""@""\x83""f""\x81""[""\x83""^""\x82\xcc\x83""Z""\x81""[""\x83""u""\x82\xc9\x8e\xb8\x94""s""\x82\xb5\x82\xdc\x82\xb5\x82\xbd\x81""I",
+	"\r""\x81""@""\x83""Z""\x81""[""\x83""u""\x83""f""\x81""[""\x83""^""\x82\xaa\x93\xc7\x82\xdd\x8d\x9e\x82\xdf\x82\xdc\x82\xb9\x82\xf1\x81""B\r""\x81""@""\x83""t""\x83""@""\x83""C""\x83\x8b\x82\xaa\x89\xf3\x82\xea\x82\xc4\x82\xa2\x82\xdc\x82\xb7\x81""I"
+};
+
+static const StringListProvider kEoB2MenuStringsSaveLoadPC98JapaneseProvider = { ARRAYSIZE(kEoB2MenuStringsSaveLoadPC98Japanese), kEoB2MenuStringsSaveLoadPC98Japanese };
+
+static const char *const kEoB2MenuStringsOnOffPC98Japanese[2] = {
+	"\x82""n""\x82""m",
+	"\x82""n""\x82""e""\x82""e"
+};
+
+static const StringListProvider kEoB2MenuStringsOnOffPC98JapaneseProvider = { ARRAYSIZE(kEoB2MenuStringsOnOffPC98Japanese), kEoB2MenuStringsOnOffPC98Japanese };
+
+static const char *const kEoB2MenuStringsSpellsPC98Japanese[17] = {
+	"\r\r  ""\x8e\xf4\x95\xb6\x82\xf0\x8b""L""\x89\xaf\x82\xb3\x82\xb9\x82\xbd\x82\xa2\x83""L""\x83\x83\x83\x89\x83""N""\x83""^""\x81""[""\x82\xf0""\r  ""\x91""I""\x82\xf1\x82\xc5\x82\xad\x82\xbe\x82\xb3\x82\xa2\x81""B",
+	"\r  ""\x82\xbb\x82\xcc\x83""p""\x83\x89\x83""f""\x83""B""\x83\x93\x82\xcd\x83\x8c\x83""x""\x83\x8b\x82\xaa\x92\xe1\x82\xb7\x82\xac\x82\xc4\x8e\xf4\x95\xb6""\r  ""\x82\xf0\x8a""o""\x82\xa6\x82\xe9\x82\xb1\x82\xc6\x82\xaa\x82\xc5\x82\xab\x82\xc8\x82\xa2\x81""B",
+	"\r  ""\x82\xbb\x82\xcc\x83\x81\x83""C""\x83""W""\x82\xcd\x83""X""\x83""y""\x83\x8b\x83""u""\x83""b""\x83""N""\x82\xf0\x8e\x9d\x82\xc1\x82\xc4\x82\xa2\x82\xc8""\r""\x81""@""\x82\xa2\x81""I",
+	"\r\r\r  ""\x8e\xf4\x95\xb6\x82\xcc\x82\xbd\x82\xdf\x82\xc9\x8b""F""\x82\xe8\x82\xf0\x95\xf9\x82\xb0\x82\xe9\x83""L""\x83\x83\x83\x89\x83""N""\x83""^""\x81""[\r  ""\x82\xf0\x91""I""\x82\xf1\x82\xc5\x89\xba\x82\xb3\x82\xa2\x81""B",
+	"\r  ""\x83""p""\x81""[""\x83""e""\x83""B""\x82\xcc\x92\x86\x82\xc9\x8b""F""\x82\xe8\x82\xf0\x95\xf9\x82\xb0\x82\xe7\x82\xea\x82\xe9\x83""L""\x83\x83\x83\x89""\r""\x81""@""\x83""N""\x83""^""\x81""[""\x82\xaa\x82\xa2\x82\xc8\x82\xa2\x81""B",
+	"\r  ""\x83""p""\x81""[""\x83""e""\x83""B""\x82\xcc\x92\x86\x82\xc9\x82\xcd\x90""V""\x82\xb5\x82\xa2\x8e\xf4\x95\xb6\x82\xf0\x8a""o""\x82\xa6\x82\xe9\x82\xb1""\r""\x81""@""\x82\xc6\x82\xaa\x82\xc5\x82\xab\x82\xe9\x8e\xd2\x82\xaa\x82\xa2\x82\xc8\x82\xa2\x81""B",
+	"\r  ""\x8b""C""\x90\xe2\x82\xdc\x82\xbd\x82\xcd\x8e\x80\x82\xf1\x82\xbe\x83\x81\x83""C""\x83""W""\x82\xcd\x8e\xf4\x95\xb6\x82\xf0\x8b""L""\x89\xaf""\r  ""\x82\xb7\x82\xe9\x82\xb1\x82\xc6\x82\xaa\x82\xc5\x82\xab\x82\xc8\x82\xa2\x81""B",
+	"\r  ""\x8b""C""\x90\xe2\x82\xdc\x82\xbd\x82\xcd\x8e\x80\x82\xf1\x82\xbe\x83""N""\x83\x8c\x83\x8a\x83""b""\x83""N""\x82\xcd\x8b""F""\x82\xe8\x82\xf0""\r  ""\x95\xf9\x82\xb0\x82\xe9\x82\xb1\x82\xc6\x82\xcd\x82\xc5\x82\xab\x82\xc8\x82\xa2\x81""B",
+	"\x82""P",
+	"\x82""Q",
+	"\x82""R",
+	"\x82""S",
+	"\x82""T",
+	"\x83""N""\x83\x8a\x83""A",
+	"\x81""@""\x8e""g""\x97""p""\x89\xc2\x94""\\""\x82\xc8\x8e\xf4\x95\xb6"":",
+	"\x82\xcd\x82\xa2",
+	"\x82\xa2\x82\xa2\x82\xa6"
+};
+
+static const StringListProvider kEoB2MenuStringsSpellsPC98JapaneseProvider = { ARRAYSIZE(kEoB2MenuStringsSpellsPC98Japanese), kEoB2MenuStringsSpellsPC98Japanese };
+
+static const char *const kEoB2MenuStringsRestPC98Japanese[5] = {
+	"\r""\x81""@""\x83""N""\x83\x8c\x83\x8a\x83""b""\x83""N""\x82\xc9\x83""p""\x81""[""\x83""e""\x83""B""\x82\xcc\x8e\xa1\x97\xc3\x82\xf0""\r  ""\x82\xb3\x82\xb9\x82\xdc\x82\xb7\x82\xa9\x81""H",
+	"\r  ""\x82\xdc\x82\xbe\x83""P""\x83""K""\x82\xf0\x82\xb5\x82\xc4\x82\xa2\x82\xe9\x90""l""\x82\xaa\x82\xa2\x82\xdc\x82\xb7\x81""B\r  ""\x8b""x""\x91\xa7\x82\xf0\x91\xb1\x82\xaf\x82\xdc\x82\xb7\x82\xa9\x81""H",
+	"  ""\x83""p""\x81""[""\x83""e""\x83""B""\x82\xcd\x8b""x""\x8c""e""\x82\xb5\x82\xc4\x82\xa2\x82\xdc\x82\xb7""..",
+	"\r\r  ""\x83""p""\x81""[""\x83""e""\x83""B""\x82\xcd\x91""S""\x88\xf5\x8a\xae\x91""S""\x82\xc9\x89\xf1\x95\x9c\x82\xb5\x82\xdc\x82\xb5\x82\xbd\x81""B",
+	"\r  ""\x8e\xf4\x95\xb6\x82\xf0\x8e""g""\x82\xa6\x82\xe9\x97""l""\x82\xc9\x82\xc8\x82\xe9\x82\xbd\x82\xdf\x82\xc9\x82\xcd\x81""A""\x8b""x""\x91\xa7\x82\xaa""\r""\x81""@""\x95""K""\x97""v""\x82\xc5\x82\xb7\x81""B"
+};
+
+static const StringListProvider kEoB2MenuStringsRestPC98JapaneseProvider = { ARRAYSIZE(kEoB2MenuStringsRestPC98Japanese), kEoB2MenuStringsRestPC98Japanese };
+
+static const char *const kEoB2MenuStringsDropPC98Japanese[1] = {
+	"\r  ""\x83""p""\x81""[""\x83""e""\x83""B""\x82\xcc\x83\x81\x83\x93\x83""o""\x81""[""\x82\xf0\x82""S""\x90""l""\x96\xa2\x96\x9e\x82\xc9\x82\xb7\x82\xe9\x82\xb1""\r""\x81""@""\x82\xc6\x82\xcd\x82\xc5\x82\xab\x82\xdc\x82\xb9\x82\xf1\x81""B"
+};
+
+static const StringListProvider kEoB2MenuStringsDropPC98JapaneseProvider = { ARRAYSIZE(kEoB2MenuStringsDropPC98Japanese), kEoB2MenuStringsDropPC98Japanese };
+
+static const char *const kEoB2MenuStringsExitPC98Japanese[1] = {
+	"\r""\x81""@""\x83""Q""\x81""[""\x83\x80\x82\xf0\x8f""I""\x97\xb9\x82\xb5\x82\xc4\x82\xe0\x82\xa2\x82\xa2\x82\xc5\x82\xb7\x82\xa9\x81""H",
+};
+
+static const StringListProvider kEoB2MenuStringsExitPC98JapaneseProvider = { ARRAYSIZE(kEoB2MenuStringsExitPC98Japanese), kEoB2MenuStringsExitPC98Japanese };
+
+static const char *const kEoB2MenuStringsStarvePC98Japanese[1] = {
+	"\x81""@""\x83""p""\x81""[""\x83""e""\x83""B""\x82\xcc\x92\x86\x82\xc9\x8b\xf3\x95\xa0\x82\xcc\x83\x81\x83\x93\x83""o""\x81""[""\x82\xaa""\r  ""\x82\xa2\x82\xdc\x82\xb7\x81""B\r  ""\x82\xbb\x82\xea\x82\xc5\x82\xe0\x8b""x""\x91\xa7\x82\xf0\x91\xb1\x82\xaf\x82\xdc\x82\xb7\x82\xa9\x81""H"
+};
+
+static const StringListProvider kEoB2MenuStringsStarvePC98JapaneseProvider = { ARRAYSIZE(kEoB2MenuStringsStarvePC98Japanese), kEoB2MenuStringsStarvePC98Japanese };
+
+static const char *const kEoB2MenuStringsScribePC98Japanese[5] = {
+	"  ""\x8f\x91\x82\xab\x8e\xca\x82\xb5\x82\xbd\x82\xa2\x8e\xf4\x95\xb6\x82\xcc\x83""X""\x83""N""\x83\x8d\x81""[""\x83\x8b\x82\xf0""\r  ""\x91""I""\x82\xf1\x82\xc5\x82\xad\x82\xbe\x82\xb3\x82\xa2\x81""B",
+	"\r\r\r\r  ""\x83""p""\x81""[""\x83""e""\x83""B""\x82\xcc\x92\x86\x82\xa9\x82\xe7\x8e\xf4\x95\xb6\x82\xf0\x8f\x91\x82\xab\x8e\xca\x82\xb5\x82\xbd\x82\xa2""\r  ""\x83\x81\x83""C""\x83""W""\x82\xf0\x91""I""\x82\xf1\x82\xc5\x82\xad\x82\xbe\x82\xb3\x82\xa2\x81""B",
+	"\r""\x81""@""\x8e\xf4\x95\xb6\x82\xf0\x8f\xa5\x82\xa6\x82\xe9\x82\xbd\x82\xdf\x82\xcc\x83""X""\x83""N""\x83\x8d\x81""[""\x83\x8b\x82\xf0\x8e\x9d\x82\xc1\x82\xc4""\r""\x81""@""\x82\xa2\x82\xc8\x82\xa2\x81""B",
+	"\r  ""\x82\xb1\x82\xcc\x83\x81\x83""C""\x83""W""\x82\xaa\x95""K""\x97""v""\x82\xc6\x82\xb7\x82\xe9\x83""X""\x83""N""\x83\x8d\x81""[""\x83\x8b\x82\xf0\x8e\x9d""\r""\x81""@""\x82\xc1\x82\xc4\x82\xa2\x82\xc8\x82\xa2\x81""B",
+	"\r  ""\x90""V""\x82\xb5\x82\xa2\x8e\xf4\x95\xb6\x82\xf0\x8f\x91\x82\xab\x8e\xca\x82\xb7\x82\xb1\x82\xc6\x82\xcc\x82\xc5\x82\xab\x82\xe9\x83\x81\x83""C\r""\x81""@""\x83""W""\x82\xaa\x82\xa2\x82\xc8\x82\xa2\x81""B"
+};
+
+static const StringListProvider kEoB2MenuStringsScribePC98JapaneseProvider = { ARRAYSIZE(kEoB2MenuStringsScribePC98Japanese), kEoB2MenuStringsScribePC98Japanese };
+
+static const char *const kEoB2MenuStringsDrop2PC98Japanese[3] = {
+	"\r\r\r""\x81""@""\x95\xca\x82\xea\x82\xe9\x83""L""\x83\x83\x83\x89\x83""N""\x83""^""\x82\xf0\x91""I""\x82\xf1\x82\xc5\x82\xad\x82\xbe\x82\xb3\x82\xa2\x81""B",
+	"\r  ""\x83""Z""\x81""[""\x83""u""\x83""t""\x83""@""\x83""C""\x83\x8b\x82\xc9\x82\xcd\x96\xbc\x91""O""\x82\xaa\x95""K""\x97""v""\x82\xc5\x82\xb7\x81""B",
+	"\r""\x91""O""\x82\xcc\x83""Z""\x81""[""\x83""u""\x83""f""\x81""[""\x83""^""\x82\xc9\x8f\xe3\x8f\x91\x82\xab\x82\xb5\x82\xc4\x82\xe0""\r""\x82\xa2\x82\xa2\x82\xc5\x82\xb7\x82\xa9\x81""H"
+};
+
+static const StringListProvider kEoB2MenuStringsDrop2PC98JapaneseProvider = { ARRAYSIZE(kEoB2MenuStringsDrop2PC98Japanese), kEoB2MenuStringsDrop2PC98Japanese };
+
+static const char *const kEoB2MenuStringsHeadPC98Japanese[3] = {
+	"\x81""@""\x83""L""\x83\x83\x83\x93\x83""v:",
+	"\x81""@""\x8a\xc2\x8b\xab\x90\xdd\x92\xe8"":",
+	"\x81""@""\x83""I""\x83""v""\x83""V""\x83\x87\x83\x93"":"
+};
+
+static const StringListProvider kEoB2MenuStringsHeadPC98JapaneseProvider = { ARRAYSIZE(kEoB2MenuStringsHeadPC98Japanese), kEoB2MenuStringsHeadPC98Japanese };
+
+static const char *const kEoB2MenuStringsPoisonPC98Japanese[1] = {
+	"  ""\x93\xc5\x82\xf0\x8e\xf3\x82\xaf\x82\xbd\x83""L""\x83\x83\x83\x89\x83""N""\x83""^""\x81""[""\x82\xaa\x8e\x80\x82\xf1\x82\xc5\x82\xb5\x82\xdc""\r""\x81""@""\x82\xa2\x82\xdc\x82\xb7\x81""I\r  ""\x82\xbb\x82\xea\x82\xc5\x82\xe0\x8b""x""\x82\xdd\x82\xdc\x82\xb7\x82\xa9\x81""H"
+};
+
+static const StringListProvider kEoB2MenuStringsPoisonPC98JapaneseProvider = { ARRAYSIZE(kEoB2MenuStringsPoisonPC98Japanese), kEoB2MenuStringsPoisonPC98Japanese };
+
+static const char *const kEoB2MenuStringsMgcPC98Japanese[2] = {
+	"%-18s %1d",
+	" %d""\x8c\xc2\x92\x86""  %d""\x8c\xc2\x8e""g""\x97""p""\x89\xc2\x94""\\",
+};
+
+static const StringListProvider kEoB2MenuStringsMgcPC98JapaneseProvider = { ARRAYSIZE(kEoB2MenuStringsMgcPC98Japanese), kEoB2MenuStringsMgcPC98Japanese };
+
+static const char *const kEoB2MenuStringsPrefsPC98Japanese[4] = {
+	"\x82""a""\x82""f""\x82""l %-6s",
+	"\x8c\xf8\x89\xca\x89\xb9"" %-6s",
+	"\x83""o""\x81""[""\x83""O""\x83\x89\x83""t""\x95""\\""\x8e\xa6"" %-6s",
+	""
+};
+
+static const StringListProvider kEoB2MenuStringsPrefsPC98JapaneseProvider = { ARRAYSIZE(kEoB2MenuStringsPrefsPC98Japanese), kEoB2MenuStringsPrefsPC98Japanese };
+
+static const char *const kEoB2MenuStringsRest2PC98Japanese[5] = {
+	"%s""\x82\xcd""%s""\x82\xcc\x8e\xf4\x95\xb6\x82\xf0\x8e\xf6\x82\xa9\x82\xc1\x82\xbd\x81""B\r",
+	"%s""\x82\xcd""%s""\x82\xcc\x8e\xf4\x95\xb6\x82\xf0\x8b""L""\x89\xaf\x82\xb5\x82\xbd\x81""B\r",
+	"%s""\x82\xcd""%s""\x82\xc9\x8e\xa1\x96\xfc\x82\xcc\x8e\xf4\x95\xb6\x82\xf0\x82\xc6\x82\xc8\x82\xa6\x82\xbd\x81""B\r",
+	"\x8b""x""\x91\xa7\x82\xb5\x82\xbd\x8e\x9e\x8a\xd4"": %-4d",
+	"\r%s\r"
+};
+
+static const StringListProvider kEoB2MenuStringsRest2PC98JapaneseProvider = { ARRAYSIZE(kEoB2MenuStringsRest2PC98Japanese), kEoB2MenuStringsRest2PC98Japanese };
+
+static const char *const kEoB2MenuStringsRest3PC98Japanese[1] = {
+	"\x06\x06\x82\xb1\x82\xb1\x82\xc5\x82\xcd\x88\xc0\x90""S""\x82\xb5\x82\xc4\x8b""x""\x82\xdf\x82\xbb\x82\xa4\x82\xc9\x82\xc8\x82\xa2\x81""B"
+};
+
+static const StringListProvider kEoB2MenuStringsRest3PC98JapaneseProvider = { ARRAYSIZE(kEoB2MenuStringsRest3PC98Japanese), kEoB2MenuStringsRest3PC98Japanese };
+
+static const char *const kEoB2MenuStringsRest4PC98Japanese[1] = {
+	"\x06\x06\x8b""x""\x82\xde\x82\xc9\x82\xcd\x8a\xeb\x8c\xaf\x89\xdf\x82\xac\x82\xe9\x81""B"
+};
+
+static const StringListProvider kEoB2MenuStringsRest4PC98JapaneseProvider = { ARRAYSIZE(kEoB2MenuStringsRest4PC98Japanese), kEoB2MenuStringsRest4PC98Japanese };
+
+static const char *const kEoB2MenuStringsDefeatPC98Japanese[1] = {
+	"\r""\x81""@""\x83""p""\x81""[""\x83""e""\x83""B""\x82\xcd\x91""S""\x96\xc5\x82\xb5\x82\xbd\x81""I\r  ""\x83""Z""\x81""[""\x83""u""\x83""f""\x81""[""\x83""^""\x82\xf0\x93\xc7\x82\xdd\x8d\x9e\x82\xf1\x82\xc5\x91\xb1\x82\xab\x82\xf0""\r  ""\x82\xb5\x82\xdc\x82\xb7\x82\xa9\x81""H"
+};
+
+static const StringListProvider kEoB2MenuStringsDefeatPC98JapaneseProvider = { ARRAYSIZE(kEoB2MenuStringsDefeatPC98Japanese), kEoB2MenuStringsDefeatPC98Japanese };
+
+static const char *const kEoB2MenuStringsTransferPC98Japanese[5] = {
+	"\x88\xf8\x82\xab\x8c""p""\x82\xae\x83\x81\x83\x93\x83""o""\x81""[""\x82\xf0\x82""S""\x90""l""\x8c\x88\x82\xdf\x82\xc4\x89\xba\x82\xb3\x82\xa2\x81""I",
+	"\x88\xf8\x82\xab\x8c""p""\x82\xac\x82\xc5\x82\xab\x82\xe9\x82\xcc\x82\xcd\x82""S""\x90""l""\x82\xdc\x82\xc5\x82\xc5\x82\xb7\x81""I",
+	"\x93""]""\x91\x97\x82\xc5\x82\xab\x82\xc8\x82\xa2\x83""A""\x83""C""\x83""e""\x83\x80\x82\xf0\x88\xea\x95\x94\x8d\xed\x8f\x9c\x82\xb5\x82\xdc\x82\xb5\x82\xbd\x81""B",
+	"\r ""\x83""p""\x81""[""\x83""e""\x83""B""\x82\xc9\x83\x81\x83""C""\x83""W""\x82\xaa\x82\xa2\x82\xdc\x82\xb9\x82\xf1\x81""B",
+	"\r  ""\x83""p""\x81""[""\x83""e""\x83""B""\x82\xc9\x83""N""\x83\x8c\x83\x8a\x83""b""\x83""N""\x82\xe0\x83""p""\x83\x89\x83""f""\x83""B""\x83\x93\x82\xe0\x82\xa2""\r""\x81""@""\x82\xdc\x82\xb9\x82\xf1\x81""B"
+};
+
+static const StringListProvider kEoB2MenuStringsTransferPC98JapaneseProvider = { ARRAYSIZE(kEoB2MenuStringsTransferPC98Japanese), kEoB2MenuStringsTransferPC98Japanese };
+
+static const char *const kEoB2MenuStringsSpecPC98Japanese[2] = {
+	"  ""\x96\xda\x82\xaa\x8a""o""\x82\xdf\x82\xe9\x82\xc6\x81""A""\x83""V""\x81""[""\x83""t""\x82\xcc\x83""C""\x83\x93\x83""T""\x83\x8b\x82\xaa""\r  ""\x82\xa2\x82\xc8\x82\xad\x82\xc8\x82\xc1\x82\xc4\x82\xa2\x82\xbd\x81""I",
+	"  ""\x8e\x9d\x82\xbf\x95\xa8\x82\xaa\x82\xc8\x82\xad\x82\xc8\x82\xc1\x82\xc4\x82\xa2\x82\xe9\x81""I"
+};
+
+static const StringListProvider kEoB2MenuStringsSpecPC98JapaneseProvider = { ARRAYSIZE(kEoB2MenuStringsSpecPC98Japanese), kEoB2MenuStringsSpecPC98Japanese };
+
+static const char *const kEoB2MenuYesNoStringsPC98Japanese[2] = {
+	"\x82\xcd\x82\xa2",
+	"\x82\xa2\x82\xa2\x82\xa6"
+};
+
+static const StringListProvider kEoB2MenuYesNoStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2MenuYesNoStringsPC98Japanese), kEoB2MenuYesNoStringsPC98Japanese };
+
+static const char *const kEoB2CharGuiStringsHpPC98Japanese[2] = {
+	"HP",
+	"%3d of %-3d"
+};
+
+static const StringListProvider kEoB2CharGuiStringsHpPC98JapaneseProvider = { ARRAYSIZE(kEoB2CharGuiStringsHpPC98Japanese), kEoB2CharGuiStringsHpPC98Japanese };
+
+static const char *const kEoB2CharGuiStringsWp2PC98Japanese[3] = {
+	"MISS",
+	"HACK",
+	"BASH"
+};
+
+static const StringListProvider kEoB2CharGuiStringsWp2PC98JapaneseProvider = { ARRAYSIZE(kEoB2CharGuiStringsWp2PC98Japanese), kEoB2CharGuiStringsWp2PC98Japanese };
+
+static const char *const kEoB2CharGuiStringsWrPC98Japanese[4] = {
+	"CAN'T",
+	"REACH",
+	"NO",
+	"AMMO"
+};
+
+static const StringListProvider kEoB2CharGuiStringsWrPC98JapaneseProvider = { ARRAYSIZE(kEoB2CharGuiStringsWrPC98Japanese), kEoB2CharGuiStringsWrPC98Japanese };
+
+static const char *const kEoB2CharGuiStringsSt2PC98Japanese[7] = {
+	"Swapping",
+	"\x81""@""\x81""@""\x8e\x80\x96""S""\x81""@""\x81""@",
+	"\x81""@""\x81""@""\x8b""C""\x90\xe2\x81""@""\x81""@",
+	" ""\x93\xc5\x81""i""\x8e\xe3\x82\xa2\x81""j ",
+	"\x81""@""\x81""@ ""\x93\xc5"" ""\x81""@""\x81""@",
+	"\x81""@""\x81""@""\x96\x83\xe1\x83\x81""@""\x81""@",
+	"\x81""@""\x81""@""\x90\xce\x89\xbb\x81""@""\x81""@"
+};
+
+static const StringListProvider kEoB2CharGuiStringsSt2PC98JapaneseProvider = { ARRAYSIZE(kEoB2CharGuiStringsSt2PC98Japanese), kEoB2CharGuiStringsSt2PC98Japanese };
+
+static const char *const kEoB2CharGuiStringsInPC98Japanese[4] = {
+	"CHARACTER INFO",
+	"ARMOR CLASS",
+	"EXP",
+	"LVL"
+};
+
+static const StringListProvider kEoB2CharGuiStringsInPC98JapaneseProvider = { ARRAYSIZE(kEoB2CharGuiStringsInPC98Japanese), kEoB2CharGuiStringsInPC98Japanese };
+
+static const char *const kEoB2CharStatusStrings7PC98Japanese[1] = {
+	"%s""\x82\xcc\x83""W""\x83\x83\x83""C""\x83""A""\x83\x93\x83""g""\x81""E""\x83""X""\x83""g""\x83\x8c\x83\x93\x83""O""\x83""X""\x82\xcc\x8c\xf8\x89\xca\x82\xaa\x90\xd8\x82\xea\x82\xbd\x81""B\r"
+};
+
+static const StringListProvider kEoB2CharStatusStrings7PC98JapaneseProvider = { ARRAYSIZE(kEoB2CharStatusStrings7PC98Japanese), kEoB2CharStatusStrings7PC98Japanese };
+
+static const char *const kEoB2CharStatusStrings82PC98Japanese[1] = {
+	"\x06\x06""%s""\x82\xcd\x93\xc5\x82\xaa\x89\xf1\x82\xc1\x82\xbd\x81""I\r"
+};
+
+static const StringListProvider kEoB2CharStatusStrings82PC98JapaneseProvider = { ARRAYSIZE(kEoB2CharStatusStrings82PC98Japanese), kEoB2CharStatusStrings82PC98Japanese };
+
+static const char *const kEoB2CharStatusStrings9PC98Japanese[1] = {
+	"\x06\x04""%s""\x82\xcc\x96\x83\xe1\x83\x82\xcd\x8e\xa1\x82\xc1\x82\xbd\x81""I\r"
+};
+
+static const StringListProvider kEoB2CharStatusStrings9PC98JapaneseProvider = { ARRAYSIZE(kEoB2CharStatusStrings9PC98Japanese), kEoB2CharStatusStrings9PC98Japanese };
+
+static const char *const kEoB2CharStatusStrings12PC98Japanese[1] = {
+	"%s""\x82\xcc\x93\xae\x82\xab\x82\xcd\x93\xdd\x82\xad\x82\xc8\x82\xc1\x82\xbd\x81""B\r"
+};
+
+static const StringListProvider kEoB2CharStatusStrings12PC98JapaneseProvider = { ARRAYSIZE(kEoB2CharStatusStrings12PC98Japanese), kEoB2CharStatusStrings12PC98Japanese };
+
+static const char *const kEoB2CharStatusStrings132PC98Japanese[1] = {
+	"\x06\x06""%s""\x82\xcd""%s""\x81""I\r"
+};
+
+static const StringListProvider kEoB2CharStatusStrings132PC98JapaneseProvider = { ARRAYSIZE(kEoB2CharStatusStrings132PC98Japanese), kEoB2CharStatusStrings132PC98Japanese };
+
+static const char *const kEoB2LevelGainStringsPC98Japanese[1] = {
+	"\x06\x01""%s""\x82\xcd\x83\x8c\x83""x""\x83\x8b\x82\xaa\x8f\xe3\x82\xaa\x82\xc1\x82\xbd\x81""B""\x06\x0f""\r" //!
+};
+
+static const StringListProvider kEoB2LevelGainStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2LevelGainStringsPC98Japanese), kEoB2LevelGainStringsPC98Japanese };
+
+static const char *const kEoB2BookNumbersPC98Japanese[5] = {
+	" ""\x82""P",
+	" ""\x82""Q",
+	" ""\x82""R",
+	" ""\x82""S",
+	" ""\x82""T"
+};
+
+static const StringListProvider kEoB2BookNumbersPC98JapaneseProvider = { ARRAYSIZE(kEoB2BookNumbersPC98Japanese), kEoB2BookNumbersPC98Japanese };
+
+static const char *const kEoB2MageSpellsListPC98Japanese[33] = {
+	"",
+	"Armor",
+	"Burning Hands",
+	"Detect Magic",
+	"Magic Missile",
+	"Shield",
+	"Shocking Grasp",
+	"Blur",
+	"Detect Invisible",
+	"Imp Identify",
+	"Invisibility",
+	"M's Acid Arrow",
+	"Dispel Magic",
+	"Fireball",
+	"Haste",
+	"Hold Person",
+	"Invisibility 10'",
+	"Lightning Bolt",
+	"Vampiric Touch",
+	"Fear",
+	"Ice Storm",
+	"Imp Invisibility",
+	"Remove Curse",
+	"Cone of Cold",
+	"Hold Monster",
+	"Wall of Force",
+	"Disintegrate",
+	"Flesh to Stone",
+	"Stone to Flesh",
+	"True Seeing",
+	"Finger of Death",
+	"Power Word Stun",
+	"Bigby's Fist"
+};
+
+static const StringListProvider kEoB2MageSpellsListPC98JapaneseProvider = { ARRAYSIZE(kEoB2MageSpellsListPC98Japanese), kEoB2MageSpellsListPC98Japanese };
+
+static const char *const kEoB2ClericSpellsListPC98Japanese[30] = {
+	"",
+	"Bless",
+	"Cause Light Wnds",
+	"Cure Light Wnds",
+	"Detect Magic",
+	"Protect-Evil",
+	"Aid",
+	"Flame Blade",
+	"Hold Person",
+	"Slow Poison",
+	"Create Food",
+	"Dispel Magic",
+	"Magical Vestment",
+	"Prayer",
+	"Remove Paralysis",
+	"Cause Serious",
+	"Cure Serious",
+	"Neutral-Poison",
+	"Protect-Evil 10'",
+	"Cause Critical",
+	"Cure Critical",
+	"Flame Strike",
+	"Raise Dead",
+	"Slay Living",
+	"True Seeing",
+	"Harm",
+	"Heal",
+	"Ressurection",
+	"Lay on Hands",
+	"Turn undead"
+};
+
+static const StringListProvider kEoB2ClericSpellsListPC98JapaneseProvider = { ARRAYSIZE(kEoB2ClericSpellsListPC98Japanese), kEoB2ClericSpellsListPC98Japanese };
+
+static const char *const kEoB2SpellNamesPC98Japanese[68] = {
+	"Armor",
+	"Burning Hands",
+	"Detect Magic",
+	"Magic Missile",
+	"Shield",
+	"Shocking Grasp",
+	"Blur",
+	"Detect Invisibility",
+	"Improved Identify",
+	"Invisibility",
+	"Melf's Acid Arrow",
+	"Dispel Magic",
+	"Fireball",
+	"Haste",
+	"Hold Person",
+	"Invisibility 10' Radius",
+	"Lightning Bolt",
+	"Vampiric Touch",
+	"Fear",
+	"Ice Storm",
+	"Improved Invisibility",
+	"Remove Curse",
+	"Cone of Cold",
+	"Hold Monster",
+	"Wall of Force",
+	"Disintegrate",
+	"Flesh to Stone",
+	"Stone to Flesh",
+	"True Seeing",
+	"Finger of Death",
+	"Power Word Stun",
+	"Bigby's Clenched Fist",
+	"Bless",
+	"Cause Light Wounds",
+	"Cure Light Wounds",
+	"Detect Magic",
+	"Protection from Evil",
+	"Aid",
+	"Flame Blade",
+	"Hold Person",
+	"Slow Poison",
+	"Create Food",
+	"Dispel Magic",
+	"Magical Vestment",
+	"Prayer",
+	"Remove Paralysis",
+	"Cause Serious Wounds",
+	"Cure Serious Wounds",
+	"Neutralize Poison",
+	"Protection from Evil 10' Radius",
+	"Cause Critical Wounds",
+	"Cure Critical Wounds",
+	"Flame Strike",
+	"Raise Dead",
+	"Slay Living",
+	"True Seeing",
+	"Harm",
+	"Heal",
+	"Ressurection",
+	"Lay on Hands",
+	"Turn Undead",
+	"",
+	"Mystic Defense",
+	"",
+	"",
+	"",
+	"",
+	""
+};
+
+static const StringListProvider kEoB2SpellNamesPC98JapaneseProvider = { ARRAYSIZE(kEoB2SpellNamesPC98Japanese), kEoB2SpellNamesPC98Japanese };
+
+static const char *const kEoB2MagicStrings1PC98Japanese[6] = {
+	"ABORT SPELL",
+	"ABORT SPELL",
+	"\x82\xb1\x82\xcc\x8e\xf4\x95\xb6\x82\xcd\x8e\xe8\x82\xaa\x8b\xf3\x82\xa2\x82\xc4\x82\xa2\x82\xc8\x82\xaf\x82\xea\x82\xce\x82\xc8\x82\xe7\x82\xc8\x82\xa2\x81""B\r",
+	"\x82\xb1\x82\xcc\x83""^""\x83""C""\x83""v""\x82\xcc\x96\x82\x96""@""\x82\xcd\x82""Q""\x82\xc2\x93\xaf\x8e\x9e\x82\xc9\x82\xa9\x82\xaf\x82\xe7\x82\xea\x82\xc8\x82\xa2\x81""B\r",
+	"%s""\x82\xcd""%s""\x82\xcc\x8e\xf4\x95\xb6\x82\xf0\x8f\xa5\x82\xa6\x82\xbd\x81""B\r",
+	"\r""\x82""n""\x82""j\r"
+};
+
+static const StringListProvider kEoB2MagicStrings1PC98JapaneseProvider = { ARRAYSIZE(kEoB2MagicStrings1PC98Japanese), kEoB2MagicStrings1PC98Japanese };
+
+static const char *const kEoB2MagicStrings2PC98Japanese[5] = {
+	"\x8e\xf4\x95\xb6\x82\xcd\x8e\xb8\x94""s""\x82\xb5\x82\xbd\x81""B\r",
+	"%s""\x82\xcd\x95\xaa\x89\xf0\x82\xb3\x82\xea\x82\xbd\x81""I\r",
+	"\x06\x06\x83""p""\x81""[""\x83""e""\x83""B""\x82\xcd""Death""\x82\xcc\x8e\xf4\x95\xb6\x82\xf0\x8e\xf3\x82\xaf\x82\xbd\x81""I\r",
+	"\x06\x06""%s""\x82\xcd""Cause Serious Wounds""\x82\xcc\x8e\xf4\x95\xb6\x82\xf0\x8e\xf3\x82\xaf\x82\xbd\x81""I\r",
+	"\x90\xce\x89\xbb\x82\xb5\x82\xbd"
+};
+
+static const StringListProvider kEoB2MagicStrings2PC98JapaneseProvider = { ARRAYSIZE(kEoB2MagicStrings2PC98Japanese), kEoB2MagicStrings2PC98Japanese };
+
+static const char *const kEoB2MagicStrings3PC98Japanese[5] = {
+	"\x92""N""\x82\xc9\x8e\xf4\x95\xb6\x82\xf0\x8f\xa5\x82\xa6\x82\xdc\x82\xb7\x82\xa9\x81""H ",
+	"\r""\x8e\xf4\x95\xb6\x82\xcd\x92\x86\x92""f""\x82\xb3\x82\xea\x82\xdc\x82\xb5\x82\xbd\x81""B\r",
+	"%s""\x82\xcc""%s""\x82\xcc\x8e\xf4\x95\xb6\x82\xcc\x8c\xf8\x89\xca\x82\xaa\x90\xd8\x82\xea\x82\xbd\x81""B\r",
+	"%s""\x82\xcd\x83\x82\x83\x93\x83""X""\x83""^""\x81""[""\x82\xc9\x93\x96\x82\xbd\x82\xe7\x82\xc8\x82\xa9\x82\xc1\x82\xbd\x81""B\r",
+	"%s""\x82\xcd\x91""O""\x97\xf1\x82\xc9\x82\xa2\x82\xc8\x82\xaf\x82\xea\x82\xce\x8c\xf8\x89\xca\x82\xaa\x82\xc8\x82\xa2\x81""I\r"
+};
+
+static const StringListProvider kEoB2MagicStrings3PC98JapaneseProvider = { ARRAYSIZE(kEoB2MagicStrings3PC98Japanese), kEoB2MagicStrings3PC98Japanese };
+
+static const char *const kEoB2MagicStrings4PC98Japanese[1] = {
+	"\x8c\xf8\x89\xca\x82\xaa\x82\xc8\x82\xa9\x82\xc1\x82\xbd\x81""B\r"
+};
+
+static const StringListProvider kEoB2MagicStrings4PC98JapaneseProvider = { ARRAYSIZE(kEoB2MagicStrings4PC98Japanese), kEoB2MagicStrings4PC98Japanese };
+
+static const char *const kEoB2MagicStrings6PC98Japanese[1] = {
+	"%s""\x82\xcc\x83""A""\x81""[""\x83""}""\x81""[""\x82\xcc\x83""N""\x83\x89\x83""X""\x82\xcd\x8a\xf9\x82\xc9\x82""U""\x88\xc8\x89\xba\x82\xbe\x81""B"
+};
+
+static const StringListProvider kEoB2MagicStrings6PC98JapaneseProvider = { ARRAYSIZE(kEoB2MagicStrings6PC98Japanese), kEoB2MagicStrings6PC98Japanese };
+
+static const char *const kEoB2MagicStrings7PC98Japanese[2] = {
+	"%s""\x82\xc9\x82\xcd\x8a\xf9\x82\xc9""%s""\x82\xcc\x8c\xf8\x89\xca\x82\xaa\x82\xa0\x82\xe9\x81""B\r",
+	"\x83""p""\x81""[""\x83""e""\x83""B""\x82\xcd\x8a\xf9\x82\xc9""%s""\x82\xcc\x8c\xf8\x89\xca\x82\xaa\x82\xa0\x82\xe9\x81""B\r"
+};
+
+static const StringListProvider kEoB2MagicStrings7PC98JapaneseProvider = { ARRAYSIZE(kEoB2MagicStrings7PC98Japanese), kEoB2MagicStrings7PC98Japanese };
+
+static const char *const kEoB2MagicStrings8PC98Japanese[6] = {
+	"\x8e\xf4\x95\xb6\x82\xcd\x8e\xb8\x94""s""\x82\xb5\x82\xbd\x81""B\r",
+	"\x90""g""\x93\xae\x82\xab\x82\xaa\x82\xc6\x82\xea\x82\xc8\x82\xa2",
+	"\x8e\xf4\x95\xb6\x82\xcd\x8e\xb8\x94""s""\x82\xb5\x82\xbd\x81""B\r",
+	"\x8e\xf4\x95\xb6\x82\xcd\x8e\xb8\x94""s""\x82\xb5\x82\xbd\x81""B\r",
+	"\x93\xcd\x82\xad\x94\xcd\x88\xcd\x82\xc9\x83\x82\x83\x93\x83""X""\x83""^""\x81""[""\x82\xaa\x82\xa2\x82\xc8\x82\xa2\x81""I\r",
+	"%s""\x82\xc9\x82\xcd\x8a\xf9\x82\xc9""Aid""\x82\xcc\x8e\xf4\x95\xb6\x82\xcc\x8c\xf8\x89\xca\x82\xaa\x82\xa0\x82\xe9\x81""B\r"
+};
+
+static const StringListProvider kEoB2MagicStrings8PC98JapaneseProvider = { ARRAYSIZE(kEoB2MagicStrings8PC98Japanese), kEoB2MagicStrings8PC98Japanese };
+
+static const char *const kEoB2MainMenuStringsPC98Japanese[5] = {
+	"\x83""Z""\x81""[""\x83""u""\x83""Q""\x81""[""\x83\x80\x82\xa9\x82\xe7\x8e""n""\x82\xdf\x82\xe9""\r",
+	"\x90""V""\x82\xb5\x82\xad\x83""p""\x81""[""\x83""e""\x83""B""\x82\xf0\x8d\xec\x82\xc1\x82\xc4\x8e""n""\x82\xdf\x82\xe9""\r",
+	"\x82""d""\x82""n""\x82""a""\x82""h""\x82\xa9\x82\xe7\x82\xcc\x83""R""\x83\x93\x83""o""\x81""[""\x83""g\r",
+	"\x83""C""\x83\x93\x83""g""\x83\x8d\x83""_""\x83""N""\x83""V""\x83\x87\x83\x93""\r",
+	"\x82""c""\x82""n""\x82""r""\x82\xd6\x96\xdf\x82\xe9""\r"
+};
+
+static const StringListProvider kEoB2MainMenuStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2MainMenuStringsPC98Japanese), kEoB2MainMenuStringsPC98Japanese };
+
+static const char *const kEoB2TransferStrings1PC98Japanese[2] = {
+	"Level: %d",
+	" / %d"
+};
+
+static const StringListProvider kEoB2TransferStrings1PC98JapaneseProvider = { ARRAYSIZE(kEoB2TransferStrings1PC98Japanese), kEoB2TransferStrings1PC98Japanese };
+
+static const char *const kEoB2TransferStrings2PC98Japanese[2] = {
+	"\x88\xf8\x82\xab\x8c""p""\x82\xae\x83""L""\x83\x83\x83\x89\x83""N""\x83""^""\x81""[""\x82\xf0\x82""S""\x90""l""\x91""I""\x82\xf1\x82\xc5\x82\xad\x82\xbe\x82\xb3\x82\xa2\x81""B",
+	"\x83\x81\x83\x93\x83""o""\x81""[""\x82\xaa\x8c\x88\x92\xe8\x82\xb5\x82\xbd\x82\xe7\x82""n""\x82""j""\x82\xf0\x83""N""\x83\x8a\x83""b""\x83""N""\x82\xb5\x82\xc4\x82\xad\x82\xbe\x82\xb3\x82\xa2\x81""B"
+};
+
+static const StringListProvider kEoB2TransferStrings2PC98JapaneseProvider = { ARRAYSIZE(kEoB2TransferStrings2PC98Japanese), kEoB2TransferStrings2PC98Japanese };
+
+static const char *const kEoB2TransferLabelsPC98Japanese[2] = {
+	"\x82""b""\x82""`""\x82""m""\x82""b""\x82""d""\x82""k",
+	"\x82""n""\x82""j"
+};
+
+static const StringListProvider kEoB2TransferLabelsPC98JapaneseProvider = { ARRAYSIZE(kEoB2TransferLabelsPC98Japanese), kEoB2TransferLabelsPC98Japanese };
+
+static const char *const kEoB2IntroStringsPC98Japanese[20] = {
+	"\x82\xbb\x82\xcc\x94\xd3\x81""A""\x89\xe4\x81""X""\x82\xcd\x92""g""\x98""F""\x82\xcc\x82\xbb\x82\xce\x82\xc5""\r""\x81""@""\x82\xa2\x82\xc2\x82\xe0\x82\xcc\x82\xe6\x82\xa4\x82\xc9\x82\xad\x82\xc2\x82\xeb\x82\xa2\x82\xc5\x82\xa2\x82\xbd\x81""B",
+	"\x82\xbb\x82\xb1\x82\xd6\x93\xcb\x91""R""\x82\xcc\x92""m""\x82\xe7\x82\xb9\x82\xaa""\r""\x81""@""\x95\x91\x82\xa2\x8d\x9e\x82\xf1\x82\xc5\x82\xab\x82\xbd\x81""B",
+	"\x82\xbb\x82\xea\x82\xcd\x81""A""\x89\xe4\x81""X""\x82\xcc\x93\xaf\x8e""u""\x82\xc5\x82\xa0\x82\xe9""\r""\x83""E""\x83""H""\x81""[""\x83""^""\x81""[""\x83""f""\x83""B""\x81""[""\x83""v""\x82\xcc\x83""A""\x81""[""\x83""`""\x83\x81\x83""C""\x83""W\r""\x83""P""\x83\x8b\x83""x""\x83\x93\x81\x81\x83""u""\x83\x89\x83""b""\x83""N""\x83""X""\x83""^""\x83""b""\x83""t""\x82\xa9\x82\xe7\x82\xbe\x82\xc1\x82\xbd\x81""B",
+	"\x8b\xd9\x8b""}""\x82\xcc\x97""p""\x8c\x8f\x82\xc6\x82\xcc\x82\xb1\x82\xc6\x82\xbe\x82\xaa\x81""E""\x81""E",
+	"\x82\xa2\x82\xc1\x82\xbd\x82\xa2\x83""P""\x83\x8b\x83""x""\x83\x93\x82\xcd""\r""\x81""@""\x89\xbd\x82\xf0\x96""]""\x82\xf1\x82\xc5\x82\xa2\x82\xe9\x82\xcc\x82\xa9\x81""B",
+	"\x82\xc7\x82\xa4\x82\xbc\x82\xa8\x93\xfc\x82\xe8\x82\xad\x82\xbe\x82\xb3\x82\xa2\x81""B",
+	"\x83""P""\x83\x8b\x83""x""\x83\x93\x97""l""\x82\xaa\x8f\x91\x8d\xd6\x82\xc5\x82\xa8\x91\xd2\x82\xbf\x82\xc5\x82\xb7\x81""B",
+	"\x91""f""\x91\x81\x82\xa2\x93\x9e\x92\x85\x82\xc9\x8a\xb4\x8e\xd3\x82\xb7\x82\xe9\x81""B",
+	"\x8d\xa2\x82\xc1\x82\xbd\x8e\x96\x82\xaa\x8b""N""\x82\xab\x82\xc4\x82\xb5\x82\xdc\x82\xc1\x82\xbd\x81""B",
+	"\x83""_""\x81""[""\x83""N""\x83\x80\x81""[""\x83\x93\x8e\x9b\x89""@""\x82\xc5""\r""\x8a\xf5\x82\xdc\x82\xed\x82\xb5\x82\xab\x82\xe0\x82\xcc\x82\xaa""\r""\x81""@""\x93\xae\x82\xab\x8f""o""\x82\xb5\x82\xbd\x82\xe6\x82\xa4\x82\xbe\x81""B",
+	"\x83""E""\x83""H""\x81""[""\x83""^""\x81""[""\x83""f""\x83""B""\x81""[""\x83""v""\x82\xcc\x88\xc0\x91""S""\x82\xaa""\r""\x81""@""\x8b\xba\x82\xa9\x82\xb3\x82\xea\x82\xc4\x82\xa2\x82\xe9\x81""B",
+	"\x8c""N""\x92""B""\x82\xcc\x8f\x95\x82\xaf\x82\xaa\x95""K""\x97""v""\x82\xbe\x81""B",
+	"\x8e""O""\x93\xfa\x91""O""\x81""A""\x92\xe3\x8e""@""\x82\xf0\x88\xea\x90""l""\x94""h""\x8c\xad\x82\xb5\x82\xbd\x82\xcc\x82\xbe\x82\xaa\x81""A",
+	"\x94\xde\x8f\x97\x82\xcd\x82\xa2\x82\xdc\x82\xbe\x82\xc9\x96\xdf\x82\xc1\x82\xc4\x82\xab\x82\xc4\x82\xa2\x82\xc8\x82\xa2\x81""B",
+	"\x94\xde\x8f\x97\x82\xcc\x82\xb1\x82\xc6\x82\xe0\x90""S""\x94""z""\x82\xbe\x81""B",
+	"\x82\xb1\x82\xcc\x83""R""\x83""C""\x83\x93\x82\xf0\x8e\x9d\x82\xc1\x82\xc4\x82\xa2\x82\xc1\x82\xc4\x82\xad\x82\xea\x81""B",
+	"\x8c""N""\x92""B""\x82\xc6\x98""A""\x97\x8d\x82\xf0\x8e\xe6\x82\xe9\x8e\x9e\x82\xc9\x8e""g""\x82\xa4\x82\xe0\x82\xcc\x82\xbe\x81""B",
+	"\x8e\x96\x82\xcd\x88\xea\x8d\x8f\x82\xf0\x91\x88\x82\xa4\x81""B",
+	"\x83""_""\x81""[""\x83""N""\x83\x80\x81""[""\x83\x93\x82\xcc\x8b\xdf\x82\xad\x82\xdc\x82\xc5""\r""\x81""@""\x8c""N""\x92""B""\x82\xf0\x83""e""\x83\x8c\x83""|""\x81""[""\x83""g""\x82\xb7\x82\xe9\x81""B",
+	"\x93\xaf\x8e""u""\x82\xc9\x90""_""\x82\xcc\x82\xb2\x89\xc1\x8c\xec\x82\xaa\x82\xa0\x82\xe7\x82\xf1\x82\xb1\x82\xc6\x82\xf0\x81""B"
+};
+
+static const StringListProvider kEoB2IntroStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2IntroStringsPC98Japanese), kEoB2IntroStringsPC98Japanese };
+
+static const char *const kEoB2IntroCPSFilesPC98Japanese[14] = {
+	"STREET1.CPS",
+	"STREET2.CPS",
+	"DOORWAY1.CPS",
+	"DOORWAY2.CPS",
+	"WESTWOOD.CPS",
+	"WINDING.CPS",
+	"KHELBAN2.CPS",
+	"KHELBAN1.CPS",
+	"KHELBAN3.CPS",
+	"KHELBAN4.CPS",
+	"COIN.CPS",
+	"KHELBAN5.CPS",
+	"KHELBAN6.CPS",
+	"AAD_LOGO.CPS"
+};
+
+static const StringListProvider kEoB2IntroCPSFilesPC98JapaneseProvider = { ARRAYSIZE(kEoB2IntroCPSFilesPC98Japanese), kEoB2IntroCPSFilesPC98Japanese };
+
+static const char *const kEoB2FinaleStringsPC98Japanese[20] = {
+	"\x82\xc2\x82\xa2\x82\xc9\x81""A""\x83""h""\x83\x89\x83\x93\x82\xcd\x97\xcd\x82\xc2\x82\xab\x82\xbd\x81""B",
+	"\x82\xbb\x82\xcc\x8e\x9e\x81""A""\x93\xaf\x8e""u""\x83""P""\x83\x8b\x83""x""\x83\x93\x82\xaa\x96\xda\x82\xcc\x91""O""\x82\xc9\x8c\xbb\x82\xea\x82\xbd\x81""B",
+	"\x82\xa8\x82\xdf\x82\xc5\x82\xc6\x82\xa4\x81""A""\x8c\xd6\x82\xe8\x8d\x82\x82\xab\x93\xaf\x8e""u""\x92""B""\x82\xe6\x81""B",
+	"\x8c""N""\x92""B""\x82\xc9\x82\xe6\x82\xc1\x82\xc4\x83""h""\x83\x89\x83\x93\x82\xcd\x93""|""\x82\xb3\x82\xea\x82\xbd\x81""I",
+	"\x83""h""\x83\x89\x83\x93\x82\xaa\x83""h""\x83\x89\x83""S""\x83\x93\x82\xbe\x82\xc1\x82\xbd\x82\xc6\x82\xcd\x8e\x84\x82\xe0\x92""m""\x82\xe7\x82\xc8\x82\xa9\x82\xc1\x82\xbd\x81""B",
+	"\x82\xb1\x82\xa2\x82\xc2\x82\xcd\x82""R""\x82""O""\x82""O""\x94""N""\x88\xc8\x8f\xe3\x90\xb6\x82\xab\x82\xc4\x82\xa2\x82\xbd\x82\xc9\x88\xe1\x82\xa2\x82\xc8\x82\xa2\x81""I",
+	"\x83""h""\x83\x89\x83\x93\x82\xcc\x97\xcd\x82\xcd\x8f\xc1\x82\xa6\x82\xbd\x81""B",
+	"\x82\xb5\x82\xa9\x82\xb5\x81""A""\x83""_""\x81""[""\x83""N""\x83\x80\x81""[""\x83\x93\x82\xcd\x96\xa2\x82\xbe\x82\xc9\x8b\x90\x91\xe5\x82\xc8\x88\xab\x82\xcc\x97\xcd\x82\xcc\x89\xf2\x82\xbe\x81""B",
+	"\x82\xbb\x82\xea\x82\xc9\x81""A""\x94\xde\x82\xcc\x89\xba\x96""l""\x92""B""\x82\xaa\x8e""c""\x82\xc1\x82\xc4\x82\xa2\x82\xe9\x81""B",
+	"\x82\xb3\x82\xa0\x81""A""\x82\xb1\x82\xcc\x8f\xea\x8f\x8a\x82\xf0\x97\xa3\x82\xea\x82\xe9\x82\xc6\x82\xb5\x82\xe6\x82\xa4\x81""B",
+	"\x8e\x84\x82\xcc\x97\xcd\x82\xc5\x94""j""\x89\xf3\x82\xb7\x82\xea\x82\xce\x81""A""\x8f""I""\x8e""~""\x95\x84\x82\xf0\x91\xc5\x82\xc2\x82\xb1\x82\xc6\x82\xaa\x82\xc5\x82\xab\x82\xe9\x82\xbe\x82\xeb\x82\xa4\x81""B",
+	"\x82\xc2\x82\xa2\x82\xc4\x97\x88\x82\xc8\x82\xb3\x82\xa2\x81""B",
+	"\x8b\xad\x97\xcd\x82\xc8\x83\x81\x83""C""\x83""W""\x92""B""\x82\xaa\x81""A""\x8d\xc5\x8c\xe3\x82\xcc\x8c\x88\x90\xed\x82\xcc\x88\xd7\x82\xc9\x83""_""\x81""[""\x83""N""\x83\x80\x81""[""\x83\x93\x82\xcc\x89\xf1\x82\xe8\x82\xc9\x97\xa7\x82\xc1\x82\xc4\x82\xa2\x82\xe9\x81""B",
+	"\x83""_""\x81""[""\x83""N""\x83\x80\x81""[""\x83\x93\x82\xcc\x88\xab\x82\xcc\x97\xcd\x82\xcd\x8b\xad\x97\xcd\x82\xbe\x81""B",
+	"\x90\xb6\x82\xa9\x82\xb5\x82\xc4\x82\xa8\x82\xa2\x82\xc4\x82\xcd\x82\xc8\x82\xe7\x82\xc8\x82\xa2\x81""I",
+	"\x83""_""\x81""[""\x83""N""\x83\x80\x81""[""\x83\x93\x82\xcd\x8f\xc1\x82\xa6\x8b\x8e\x82\xc1\x82\xc4\x82\xa2\x82\xc1\x82\xbd\x81""B",
+	"\x93\xaf\x8e""u""\x82\xe6\x81""B""\x82\xb1\x82\xea\x82\xc5\x81""A""\x91""S""\x82\xc4\x82\xaa\x8f""I""\x82\xed\x82\xc1\x82\xbd\x81""B",
+	"\x90""S""\x82\xa9\x82\xe7\x8a\xb4\x8e\xd3\x82\xb7\x82\xe9\x81""B",
+	"\x8c""N""\x92""B""\x82\xc9\x8e\x84\x82\xcc\x8d\xc5\x8d\x82\x82\xcc\x8c""h""\x88\xd3\x82\xf0\x95\xa5\x82\xa8\x82\xa4\x81""B",
+	"\x89\xe4\x81""X""\x82\xcd\x8c""N""\x92""B""\x82\xcc\x82\xb1\x82\xc6\x82\xf0\x82\xa2\x82\xc2\x82\xe0\x96""Y""\x82\xea\x82\xc8\x82\xa2\x82\xbe\x82\xeb\x82\xa4\x81""B"
+};
+
+static const StringListProvider kEoB2FinaleStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2FinaleStringsPC98Japanese), kEoB2FinaleStringsPC98Japanese };
+
+static const char *const kEoB2FinaleCPSFilesPC98Japanese[13] = {
+	"DRAGON1.CPS",
+	"DRAGON2.CPS",
+	"HURRY1.CPS",
+	"HURRY2.CPS",
+	"DESTROY0.CPS",
+	"DESTROY1.CPS",
+	"DESTROY2.CPS",
+	"MAGIC.CPS",
+	"DESTROY3.CPS",
+	"CREDITS2.CPS",
+	"CREDITS3.CPS",
+	"HEROES.CPS",
+	"THANKS.CPS"
+};
+
+static const StringListProvider kEoB2FinaleCPSFilesPC98JapaneseProvider = { ARRAYSIZE(kEoB2FinaleCPSFilesPC98Japanese), kEoB2FinaleCPSFilesPC98Japanese };
+
+static const char *const kEoB2MonsterDistAttStringsPC98Japanese[5] = {
+	"\x83""p""\x81""[""\x83""e""\x83""B""\x82\xcd""Psychic Mind Blast""\x82\xf0\x8e\xf3\x82\xaf\x82\xbd\x81""I\r",
+	"\x96\x83\xe1\x83\x82\xb5\x82\xbd",
+	"\x93\xc5\x82\xf0\x8e\xf3\x82\xaf\x82\xbd",
+	"\x96\x83\xe1\x83\x82\xb5\x82\xbd",
+	"\x90\xce\x89\xbb\x82\xb5\x82\xbd"
+};
+
+static const StringListProvider kEoB2MonsterDistAttStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2MonsterDistAttStringsPC98Japanese), kEoB2MonsterDistAttStringsPC98Japanese };
+
+static const char *const kEoB2Npc1StringsPC98Japanese[2] = {
+	"\x98""b""\x82\xb7",
+	"\x8b\x8e\x82\xe9"
+};
+
+static const StringListProvider kEoB2Npc1StringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2Npc1StringsPC98Japanese), kEoB2Npc1StringsPC98Japanese };
+
+static const char *const kEoB2Npc2StringsPC98Japanese[2] = {
+	"\x8d\xbd\x82\xf0\x8a""O""\x82\xb7",
+	"\x8b\x8e\x82\xe9"
+};
+
+static const StringListProvider kEoB2Npc2StringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2Npc2StringsPC98Japanese), kEoB2Npc2StringsPC98Japanese };
+
+static const char *const kEoB2MonsterDustStringsPC98Japanese[1] = {
+	"\x83\x82\x83\x93\x83""X""\x83""^""\x81""|""\x82\xcd\x8d\xd3\x82\xaf\x8e""U""\x82\xc1\x82\xbd\x81""I\r"
+};
+
+static const StringListProvider kEoB2MonsterDustStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2MonsterDustStringsPC98Japanese), kEoB2MonsterDustStringsPC98Japanese };
+
+static const char *const kEoB2KheldranStringsPC98Japanese[1] = {
+	"    ""\x82\xb1\x82\xcc\x8f\xac\x91""m""\x82\xdf\x82\xaa\x81""I"
+};
+
+static const StringListProvider kEoB2KheldranStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2KheldranStringsPC98Japanese), kEoB2KheldranStringsPC98Japanese };
+
+static const char *const kEoB2HornStringsPC98Japanese[4] = {
+	"\x83""z""\x81""[""\x83\x93\x82\xa9\x82\xe7\x82\xa4\x82\xc8\x82\xe9\x82\xe6\x82\xa4\x82\xc8\x89\xb9\x82\xaa\x8b\xbf\x82\xa2\x82\xbd\x81""B\r",
+	"\x83""z""\x81""[""\x83\x93\x82\xa9\x82\xe7\x82\xa4\x82\xc2\x82\xeb\x82\xc8\x89\xb9\x82\xaa\x95\xb7\x82\xb1\x82\xa6\x82\xe9\x81""B\r",
+	"\x83""z""\x81""[""\x83\x93\x82\xa9\x82\xe7\x94\xfc\x82\xb5\x82\xa2\x89\xb9\x82\xaa\x97\xac\x82\xea\x82\xe9\x81""B\r",
+	"\x83""z""\x81""[""\x83\x93\x82\xa9\x82\xe7\x95""s""\x8b""C""\x96\xa1\x82\xc8\x89\xb9\x82\xaa\x82\xb7\x82\xe9\x81""B\r"
+};
+
+static const StringListProvider kEoB2HornStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB2HornStringsPC98Japanese), kEoB2HornStringsPC98Japanese };
+
+static const char *const kEoB2NpcPresetsNamesPC98Japanese[6] = {
+	"Insal",
+	"Calandra",
+	"Shorn",
+	"San-raal",
+	"Tanglor",
+	"Amber"
+};
+
+static const StringListProvider kEoB2NpcPresetsNamesPC98JapaneseProvider = { ARRAYSIZE(kEoB2NpcPresetsNamesPC98Japanese), kEoB2NpcPresetsNamesPC98Japanese };
diff --git a/dists/engine-data/kyra.dat b/dists/engine-data/kyra.dat
index a1b79aab2ef..e885fe039e4 100644
Binary files a/dists/engine-data/kyra.dat and b/dists/engine-data/kyra.dat differ
diff --git a/engines/kyra/engine/chargen.cpp b/engines/kyra/engine/chargen.cpp
index 28416673ce4..2e7da98e3ba 100644
--- a/engines/kyra/engine/chargen.cpp
+++ b/engines/kyra/engine/chargen.cpp
@@ -475,6 +475,8 @@ void CharacterGenerator::checkForCompleteParty() {
 	_screen->copyRegion(0, 0, 160, 0, 160, 128, 2, 2, Screen::CR_NO_P_CHECK);
 	int cp = _screen->setCurPage(2);
 	int x = (_vm->gameFlags().platform == Common::kPlatformFMTowns) ? 184 : 168;
+	int y1 = (_vm->game() == GI_EOB2 && _vm->gameFlags().platform == Common::kPlatformPC98) ? 80 : 16;
+	int y2 = (_vm->game() == GI_EOB2 && _vm->gameFlags().platform == Common::kPlatformPC98) ? 112 : 61;
 	int cs = 0;
 
 	if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
@@ -483,7 +485,7 @@ void CharacterGenerator::checkForCompleteParty() {
 		cs = _screen->setFontStyles(_screen->_currentFont, _vm->gameFlags().lang == Common::JA_JPN ? Font::kStyleNone : Font::kStyleFullWidth);
 		_vm->_txt->printShadedText(_chargenStrings1[8], 0, 0, -1, 0x99);
 	} else {
-		_screen->printShadedText(_chargenStrings1[8], x, 16, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+		_screen->printShadedText(_chargenStrings1[8], x, y1, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
 		_screen->copyRegion(160, 0, 144, 64, 160, 128, 2, 0, Screen::CR_NO_P_CHECK);
 	}
 	_screen->setCurPage(cp);
@@ -499,7 +501,7 @@ void CharacterGenerator::checkForCompleteParty() {
 			_vm->_txt->printShadedText(_chargenStrings1[0], 0, 60, -1, 0x99);
 		} else {
 			_screen->setCurPage(2);
-			_screen->printShadedText(_chargenStrings1[0], x, 61, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+			_screen->printShadedText(_chargenStrings1[0], x, y2, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
 			_screen->setCurPage(0);
 			_screen->copyRegion(168, 61, 152, 125, 136, 40, 2, 0, Screen::CR_NO_P_CHECK);
 		}
@@ -1416,7 +1418,18 @@ void CharacterGenerator::finish() {
 	_screen->copyRegion(0, 0, 160, 0, 160, 128, 2, 2, Screen::CR_NO_P_CHECK);
 	if (_chargenEnterGameStrings) {
 		int cp = _screen->setCurPage(2);
-		_screen->printShadedText(_chargenEnterGameStrings[0], (_vm->gameFlags().platform == Common::kPlatformFMTowns) ? 184 : 168, 32, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+		int tx = 168;
+		int ty = 32;
+
+		if (_vm->game() == GI_EOB2) {
+			if (_vm->gameFlags().platform == Common::kPlatformPC98) {
+				tx = 184;
+				ty = 96;
+			} else if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
+				tx = 184;
+			}
+		}
+		_screen->printShadedText(_chargenEnterGameStrings[0], tx, ty, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
 		_screen->setCurPage(cp);
 	}
 	_screen->copyRegion(160, 0, 144, 64, 160, 128, 2, 0, Screen::CR_NO_P_CHECK);
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 1f89bc2b2cc..b3549a639b9 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -1317,6 +1317,9 @@ void EoBCoreEngine::npcSequence(int npcIndex) {
 		drawNpcScene(npcIndex);
 
 		Common::SeekableReadStream *s = _res->createReadStream("TEXT.DAT");
+		if (!s)
+			s = _res->createReadStream("JTEXT.DAT");
+
 		if (s) {
 			_screen->loadFileDataToPage(s, 5, 32000);
 		} else {
@@ -1576,6 +1579,9 @@ void EoBCoreEngine::initDialogueSequence() {
 		snd_stopSound();
 
 	Common::SeekableReadStream *s = _res->createReadStream("TEXT.DAT");
+	if (!s)
+		s = _res->createReadStream("JTEXT.DAT");
+
 	if (s) {
 		_screen->loadFileDataToPage(s, 5, 32000);
 	} else {
@@ -1821,6 +1827,9 @@ void EoBCoreEngine::displayParchment(int id) {
 	if (id >= 0) {
 		// display text
 		Common::SeekableReadStream *s = _res->createReadStream("TEXT.DAT");
+		if (!s)
+			s = _res->createReadStream("JTEXT.DAT");
+
 		if (s) {
 			_screen->loadFileDataToPage(s, 5, 32000);
 		} else {
diff --git a/engines/kyra/resource/resource.h b/engines/kyra/resource/resource.h
index 11604872489..770a1d5c85e 100644
--- a/engines/kyra/resource/resource.h
+++ b/engines/kyra/resource/resource.h
@@ -843,6 +843,12 @@ enum KyraResources {
 	kEoB2IntroAnimData42,
 	kEoB2IntroAnimData43,
 
+	// extra entries for PC-98
+	kEoB2IntroAnimData44,
+	kEoB2IntroAnimData45,
+	kEoB2IntroAnimData46,
+	kEoB2IntroAnimData47,
+
 	kEoB2IntroShapes00,
 	kEoB2IntroShapes01,
 	kEoB2IntroShapes04,
diff --git a/engines/kyra/resource/staticres.cpp b/engines/kyra/resource/staticres.cpp
index 8e6cfa07461..fceb05f19eb 100644
--- a/engines/kyra/resource/staticres.cpp
+++ b/engines/kyra/resource/staticres.cpp
@@ -38,7 +38,7 @@
 
 namespace Kyra {
 
-#define RESFILE_VERSION 118
+#define RESFILE_VERSION 119
 
 namespace {
 bool checkKyraDat(Common::SeekableReadStream *file) {
diff --git a/engines/kyra/sequence/sequences_darkmoon.cpp b/engines/kyra/sequence/sequences_darkmoon.cpp
index 86103823ac7..c2424a87c72 100644
--- a/engines/kyra/sequence/sequences_darkmoon.cpp
+++ b/engines/kyra/sequence/sequences_darkmoon.cpp
@@ -245,6 +245,10 @@ void DarkMoonEngine::seq_playIntro() {
 
 	uint8 textColor1 = 16;
 	uint8 textColor2 = 15;
+	int songCurPos = 0;
+
+	if (_flags.platform == Common::kPlatformPC98)
+		sq.loadScene(13, 2);
 
 	if (_flags.platform == Common::kPlatformAmiga) {
 		textColor1 = textColor2 = 31;
@@ -258,6 +262,8 @@ void DarkMoonEngine::seq_playIntro() {
 	sq.loadScene(0, 2);
 	sq.delay(1);
 
+	// PC-98 --- SFX 0
+
 	if (!skipFlag() && !shouldQuit())
 		snd_playSong(12);
 
@@ -276,29 +282,40 @@ void DarkMoonEngine::seq_playIntro() {
 	sq.animCommand(6, 18);
 	sq.animCommand(0);
 
-	sq.waitForSongNotifier(1);
+	sq.waitForSongNotifier(++songCurPos);
 
-	sq.animCommand(_configRenderMode == Common::kRenderEGA ? 12 : 11);
+	sq.animCommand(_flags.platform == Common::kPlatformPC98 ? (_configRenderMode == Common::kRenderEGA ? 43 : 42) : (_configRenderMode == Common::kRenderEGA ? 12 : 11));
 	sq.animCommand(7, 6);
 	sq.animCommand(2, 6);
 
-	sq.waitForSongNotifier(2);
+	sq.waitForSongNotifier(++songCurPos);
+
+	if (_flags.platform == Common::kPlatformPC98) {
+		sq.animCommand(_configRenderMode == Common::kRenderEGA ? 37 : 36);
+		sq.animCommand(7, 6);
+		sq.animCommand(2, 6);
+		sq.waitForSongNotifier(++songCurPos);
+		sq.animCommand(_configRenderMode == Common::kRenderEGA ? 45 : 44);
+		sq.animCommand(7, 6);
+		sq.animCommand(2, 6);
+		sq.waitForSongNotifier(++songCurPos);
+	}
 
-	sq.animCommand(_flags.platform == Common::kPlatformAmiga ? 37 : (_configRenderMode == Common::kRenderEGA ? 39 : 38));
+	sq.animCommand(_flags.platform == Common::kPlatformAmiga ? 37 : (_flags.platform == Common::kPlatformPC98 ? (_configRenderMode == Common::kRenderEGA ? 47 : 46) : (_configRenderMode == Common::kRenderEGA ? 39 : 38)));
 	sq.animCommand(3);
 	sq.animCommand(8);
 	sq.animCommand(1, 10);
 	sq.animCommand(0, 6);
 	sq.animCommand(2);
 
-	sq.waitForSongNotifier(3);
+	sq.waitForSongNotifier(++songCurPos);
 
 	_screen->setClearScreenDim(17);
 	_screen->setCurPage(2);
 	_screen->setClearScreenDim(17);
 	_screen->setCurPage(0);
 
-	sq.animCommand(_flags.platform == Common::kPlatformAmiga ? 38 : (_configRenderMode == Common::kRenderEGA ? 41 : 40));
+	sq.animCommand(_flags.platform == Common::kPlatformAmiga ? 38 : (_flags.platform == Common::kPlatformPC98 ? (_configRenderMode == Common::kRenderEGA ? 39 : 38) : (_configRenderMode == Common::kRenderEGA ? 41 : 40)));
 	sq.animCommand(7, 18);
 
 	if (_flags.platform == Common::kPlatformAmiga)
@@ -321,8 +338,10 @@ void DarkMoonEngine::seq_playIntro() {
 
 	sq.printText(3, textColor1);    // The message was urgent.
 
+	// PC-98 --- SFX 1
+
 	sq.loadScene(1, 2);
-	sq.waitForSongNotifier(4);
+	sq.waitForSongNotifier(++songCurPos);
 
 	// intro scroll
 	if (!skipFlag() && !shouldQuit()) {
@@ -333,7 +352,7 @@ void DarkMoonEngine::seq_playIntro() {
 				_screen->copyRegion(i << 3, 0, 304, 8, 8, 128, 2, 0, Screen::CR_NO_P_CHECK);
 				_screen->updateScreen();
 				if (i == 12)
-					sq.animCommand(42);
+					sq.animCommand(_flags.platform == Common::kPlatformPC98 ? 40 : 42);
 				else if (i == 25)
 					snd_playSoundEffect(11);
 				delayUntil(endtime);
@@ -348,7 +367,7 @@ void DarkMoonEngine::seq_playIntro() {
 					if (i == 4 || i == 24 || i == 36)
 						sq.animCommand(39);
 				} else if (i == 96) {
-					sq.animCommand(42);
+					sq.animCommand(_flags.platform == Common::kPlatformPC98 ? 40 : 42);
 				}
 				delayUntil(endtime);
 			}
@@ -369,7 +388,7 @@ void DarkMoonEngine::seq_playIntro() {
 
 	sq.loadScene(3, 2);
 	sq.delay(54);
-	sq.animCommand(_flags.platform == Common::kPlatformAmiga ? 12 : 13);
+	sq.animCommand(_flags.platform == Common::kPlatformAmiga ? 12 : (_flags.platform == Common::kPlatformPC98 ? 11 : 13));
 	_screen->copyRegion(104, 16, 96, 8, 120, 100, 0, 2, Screen::CR_NO_P_CHECK);
 	sq.fadeText();
 
@@ -390,6 +409,8 @@ void DarkMoonEngine::seq_playIntro() {
 
 	if (_flags.platform == Common::kPlatformAmiga)
 		sq.setPlatformAnimIndexOffset(-1);
+	else if (_flags.platform == Common::kPlatformPC98)
+		sq.setPlatformAnimIndexOffset(-2);
 
 	sq.animCommand(14);
 
@@ -406,7 +427,7 @@ void DarkMoonEngine::seq_playIntro() {
 			sq.fadeText();
 			snd_playSong(14);
 		} else {
-			sq.waitForSongNotifier(5);
+			sq.waitForSongNotifier(++songCurPos);
 			sq.fadeText();
 			_screen->clearCurPage();
 			_screen->updateScreen();
@@ -430,6 +451,9 @@ void DarkMoonEngine::seq_playIntro() {
 		snd_playSong(15);
 
 	sq.animCommand(16);
+
+	// PC-98 --- SFX 2
+
 	sq.printText(7, textColor2);    // Thank you for coming so quickly
 	sq.animCommand(16);
 	sq.animCommand(17);
@@ -465,7 +489,7 @@ void DarkMoonEngine::seq_playIntro() {
 	sq.fadeText();
 	sq.loadScene(9, 2);
 
-	sq.waitForSongNotifier(6);
+	sq.waitForSongNotifier(++songCurPos);
 
 	sq.update(2);
 	sq.animCommand(34);
@@ -573,15 +597,18 @@ void DarkMoonEngine::seq_playIntro() {
 	sq.animCommand(19);
 	sq.animCommand(20);
 	sq.animCommand(18);
+
+	// PC-98 --- SFX 3
+
 	sq.fadeText();
 	sq.animCommand(29);
 
-	sq.waitForSongNotifier(7);
+	sq.waitForSongNotifier(++songCurPos);
 
 	sq.animCommand(30);
 	sq.animCommand(31);
 
-	sq.waitForSongNotifier(8, true);
+	sq.waitForSongNotifier(++songCurPos, true);
 
 	if (_flags.platform == Common::kPlatformAmiga && !skipFlag() && !shouldQuit()) {
 		static const uint8 magicHandsCol[] = { 0x15, 0x1D, 0x3A, 0x32, 0x32, 0x3F };
@@ -940,12 +967,21 @@ void DarkMoonEngine::seq_playFinale() {
 		snd_playSong(_flags.platform == Common::kPlatformFMTowns ? 16 : 1);
 
 	int temp = 0;
-	const uint8 *creditsData = (_flags.platform != Common::kPlatformDOS) ? _res->fileData("CREDITS.TXT", 0) : _staticres->loadRawData(kEoB2CreditsData, temp);
+
+	static const char *const tryFiles[2] = {
+		"CREDITS.TXT",
+		"CREDITS4.CPS"
+	};
+
+	const uint8 *creditsFileData = 0;
+	for (int i = 0; i < ARRAYSIZE(tryFiles) && !creditsFileData; ++i)
+		creditsFileData = _res->fileData(tryFiles[i], 0);
+
+	const uint8 *creditsData = creditsFileData ? creditsFileData : _staticres->loadRawData(kEoB2CreditsData, temp);
 
 	seq_playCredits(&sq, creditsData, 18, 2, 6, 2);
 
-	if (_flags.platform != Common::kPlatformDOS)
-		delete[] creditsData;
+	delete[] creditsFileData;
 
 	sq.delay(90);
 
@@ -1505,7 +1541,7 @@ void DarkmoonSequenceHelper::init(DarkmoonSequenceHelper::Mode mode) {
 			new const uint8*[16],
 			_vm->_flags.platform == Common::kPlatformAmiga ? 0 : (_vm->_configRenderMode == Common::kRenderEGA ? _palFilesIntroEGA : _palFilesIntroVGA),
 			new const DarkMoonShapeDef*[16],
-			new const DarkMoonAnimCommand *[44],
+			new const DarkMoonAnimCommand*[48],
 			false,
 			false,
 			true,
@@ -1516,7 +1552,8 @@ void DarkmoonSequenceHelper::init(DarkmoonSequenceHelper::Mode mode) {
 			2
 		);
 
-		for (int i = 0; i < 44; i++)
+
+		for (int i = 0; i < 48; i++)
 			_config->animData[i] = _vm->staticres()->loadEoB2SeqData(kEoB2IntroAnimData00 + i, size);
 
 		for (int i = 0; i < 16; i++)


Commit: cc0b6bad944d7ea2e02778b5caa04d3d3f841533
    https://github.com/scummvm/scummvm/commit/cc0b6bad944d7ea2e02778b5caa04d3d3f841533
Author: athrxx (athrxx at scummvm.org)
Date: 2022-07-14T14:59:11+02:00

Commit Message:
KYRA: (EOB II/PC98) - fix startup

- intro will run with glitchy text and without sound
- main menu will show, but also slightly glitched

Changed paths:
  A engines/kyra/sound/drivers/capcom98.cpp
  A engines/kyra/sound/drivers/capcom98.h
  A engines/kyra/sound/sound_pc98_darkmoon.cpp
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/module.mk
    engines/kyra/sequence/sequences_darkmoon.cpp
    engines/kyra/sound/sound_intern.h


diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index b3549a639b9..5a5967a8248 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -428,7 +428,7 @@ Common::Error EoBCoreEngine::init() {
 			_sound = new SoundPC98_EoB(this, _mixer);
 		} else {
 			dev = MidiDriver::detectDevice(MDT_PC98 | MDT_MIDI);
-			/**/
+			_sound = new SoundPC98_Darkmoon(this, dev, _mixer);
 		}
 		break;
 	case Common::kPlatformAmiga:
@@ -573,6 +573,9 @@ void EoBCoreEngine::loadFonts() {
 		else
 			AmigaDOSFont::errorDialog(0);
 
+	} else if (_flags.gameID == GI_EOB2 && _flags.platform == Common::kPlatformPC98) {
+		_screen->loadFont(Screen::FID_6_FNT, "FONT6B.FNT");
+		_screen->loadFont(Screen::FID_8_FNT, "FONT8B.FNT");
 	} else if (_flags.platform != Common::kPlatformSegaCD) {
 		_screen->loadFont(Screen::FID_6_FNT, "FONT6.FNT");
 		_screen->loadFont(Screen::FID_8_FNT, "FONT8.FNT");
@@ -580,7 +583,7 @@ void EoBCoreEngine::loadFonts() {
 
 	if (_flags.platform == Common::kPlatformFMTowns) {
 		_screen->loadFont(Screen::FID_SJIS_SMALL_FNT, "FONT.DMP");
-	} else if (_flags.platform == Common::kPlatformPC98) {
+	} else if (_flags.gameID == GI_EOB1 && _flags.platform == Common::kPlatformPC98) {
 		_screen->loadFont(Screen::FID_SJIS_SMALL_FNT, "FONT12.FNT");
 		_invFont1 = Screen::FID_SJIS_SMALL_FNT;
 		_conFont = _invFont3 = Screen::FID_SJIS_FNT;
@@ -601,12 +604,12 @@ Common::Error EoBCoreEngine::go() {
 
 	// Import original save game files (especially the "Quick Start Party").
 	// The SegaCD version has a "Default Party" main menu option instead.
-	if (ConfMan.getBool("importOrigSaves")) {
+	/*if (ConfMan.getBool("importOrigSaves")) {
 		if (_flags.platform != Common::kPlatformSegaCD)
 			importOriginalSaveFile(-1);
 		ConfMan.setBool("importOrigSaves", false);
 		ConfMan.flushToDisk();
-	}
+	}*/
 
 	loadItemDefs();
 	int action = 0;
diff --git a/engines/kyra/module.mk b/engines/kyra/module.mk
index 44f7e07a7a5..d4835218fa9 100644
--- a/engines/kyra/module.mk
+++ b/engines/kyra/module.mk
@@ -138,10 +138,12 @@ MODULE_OBJS += \
 	sequence/sequences_darkmoon.o \
 	sound/sound_amiga_eob.o \
 	sound/sound_pc98_eob.o \
+	sound/sound_pc98_darkmoon.o \
 	sound/sound_segacd_eob.o \
 	sound/sound_towns_darkmoon.o \
 	sound/drivers/audiomaster2.o \
 	sound/drivers/mlalf98.o \
+	sound/drivers/capcom98.o \
 	sound/drivers/pcspeaker_v1.o \
 	sound/drivers/segacd.o \
 	text/text_eob_segacd.o
diff --git a/engines/kyra/sequence/sequences_darkmoon.cpp b/engines/kyra/sequence/sequences_darkmoon.cpp
index c2424a87c72..65b3809dc39 100644
--- a/engines/kyra/sequence/sequences_darkmoon.cpp
+++ b/engines/kyra/sequence/sequences_darkmoon.cpp
@@ -345,32 +345,20 @@ void DarkMoonEngine::seq_playIntro() {
 
 	// intro scroll
 	if (!skipFlag() && !shouldQuit()) {
-		if (_configRenderMode == Common::kRenderEGA) {
-			for (int i = 0; i < 35; i++) {
-				uint32 endtime = _system->getMillis() + 2 * _tickLength;
-				_screen->copyRegion(16, 8, 8, 8, 296, 128, 0, 0, Screen::CR_NO_P_CHECK);
-				_screen->copyRegion(i << 3, 0, 304, 8, 8, 128, 2, 0, Screen::CR_NO_P_CHECK);
-				_screen->updateScreen();
-				if (i == 12)
-					sq.animCommand(_flags.platform == Common::kPlatformPC98 ? 40 : 42);
-				else if (i == 25)
-					snd_playSoundEffect(11);
-				delayUntil(endtime);
-			}
-		} else {
-			for (int i = 0; i < 280; i += 3) {
-				uint32 endtime = _system->getMillis() + _tickLength;
-				_screen->copyRegion(11, 8, 8, 8, 301, 128, 0, 0, Screen::CR_NO_P_CHECK);
-				_screen->copyRegion(i, 0, 309, 8, 3, 128, 2, 0, Screen::CR_NO_P_CHECK);
-				_screen->updateScreen();
-				if (_flags.platform == Common::kPlatformAmiga) {
-					if (i == 4 || i == 24 || i == 36)
-						sq.animCommand(39);
-				} else if (i == 96) {
-					sq.animCommand(_flags.platform == Common::kPlatformPC98 ? 40 : 42);
-				}
-				delayUntil(endtime);
+		for (int i = 0; i < 280; ++i) {
+			uint32 endtime = _system->getMillis() + 18;
+			_screen->copyRegion(9, 8, 8, 8, 303, 128, 0, 0, Screen::CR_NO_P_CHECK);
+			_screen->copyRegion(i, 0, 311, 8, 1, 128, 2, 0, Screen::CR_NO_P_CHECK);
+			_screen->updateScreen();
+			if (_flags.platform == Common::kPlatformAmiga) {
+				if (i == 4 || i == 24 || i == 36)
+					sq.animCommand(39);
+			} else if (i == 96) {
+				sq.animCommand(_flags.platform == Common::kPlatformPC98 ? 40 : 42);
+			} else if (i == 200) {
+				snd_playSoundEffect(11);
 			}
+			delayUntil(endtime);
 		}
 	}
 
@@ -1762,9 +1750,11 @@ void DarkmoonSequenceHelper::delay(uint32 ticks) {
 void DarkmoonSequenceHelper::waitForSongNotifier(int index, bool introUpdateAnim) {
 	if (_vm->gameFlags().platform == Common::kPlatformFMTowns)
 		index = _sndMarkersFMTowns[index - 1];
-	else if (_vm->sound()->getMusicType() != Sound::kAdLib)
+	else if (_vm->sound()->getMusicType() != Sound::kAdLib && _vm->gameFlags().platform != Common::kPlatformPC98)
 		return;
 
+	debug("waitForSongNotifier %d - waiting for trigger...", index);
+
 	int seq = 0;
 
 	while (_vm->sound()->musicEnabled() && _vm->sound()->checkTrigger() < index && !(_vm->skipFlag() || _vm->shouldQuit())) {
@@ -1778,6 +1768,8 @@ void DarkmoonSequenceHelper::waitForSongNotifier(int index, bool introUpdateAnim
 
 		_vm->updateInput();
 	}
+
+	debug("waitForSongNotifier %d - ...finished", index);
 }
 
 void DarkmoonSequenceHelper::updateAmigaSound() {
diff --git a/engines/kyra/sound/drivers/capcom98.cpp b/engines/kyra/sound/drivers/capcom98.cpp
new file mode 100644
index 00000000000..b8600b54916
--- /dev/null
+++ b/engines/kyra/sound/drivers/capcom98.cpp
@@ -0,0 +1,54 @@
+/* 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/>.
+ *
+ */
+
+#ifdef ENABLE_EOB
+
+#include "kyra/sound/drivers/capcom98.h"
+
+namespace Kyra {
+
+class CapcomPC98AudioDriverInternal {
+public:
+	CapcomPC98AudioDriverInternal();
+	~CapcomPC98AudioDriverInternal();
+
+private:
+};
+
+CapcomPC98AudioDriverInternal::CapcomPC98AudioDriverInternal() {
+
+}
+
+CapcomPC98AudioDriverInternal::~CapcomPC98AudioDriverInternal() {
+
+}
+
+CapcomPC98AudioDriver::CapcomPC98AudioDriver() {
+
+}
+
+CapcomPC98AudioDriver::~CapcomPC98AudioDriver() {
+
+}
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/sound/drivers/capcom98.h b/engines/kyra/sound/drivers/capcom98.h
new file mode 100644
index 00000000000..9da302435f7
--- /dev/null
+++ b/engines/kyra/sound/drivers/capcom98.h
@@ -0,0 +1,51 @@
+/* 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/>.
+ *
+ */
+
+#ifdef ENABLE_EOB
+
+#ifndef KYRA_SOUND_CAPCOM98_H
+#define KYRA_SOUND_CAPCOM98_H
+
+#include "common/scummsys.h"
+//#include "common/array.h"
+
+namespace Audio {
+	class Mixer;
+}
+
+namespace Kyra {
+
+class CapcomPC98AudioDriverInternal;
+
+class CapcomPC98AudioDriver {
+public:
+	CapcomPC98AudioDriver();
+	~CapcomPC98AudioDriver();
+
+private:
+	CapcomPC98AudioDriverInternal *_drv;
+};
+
+} // End of namespace Kyra
+
+#endif
+
+#endif
diff --git a/engines/kyra/sound/sound_intern.h b/engines/kyra/sound/sound_intern.h
index 7b8084e02cb..6948d7e5217 100644
--- a/engines/kyra/sound/sound_intern.h
+++ b/engines/kyra/sound/sound_intern.h
@@ -532,6 +532,47 @@ private:
 	bool _ready;
 };
 
+class CapcomPC98AudioDriver;
+class SoundPC98_Darkmoon : public Sound {
+public:
+	SoundPC98_Darkmoon(KyraEngine_v1 *vm, MidiDriver::DeviceHandle dev, Audio::Mixer *mixer);
+	~SoundPC98_Darkmoon() override;
+
+	kType getMusicType() const override;
+
+	bool init() override;
+
+	void initAudioResourceInfo(int set, void *info) override;
+	void selectAudioResourceSet(int set) override;
+	bool hasSoundFile(uint file) const override { return true; }
+	void loadSoundFile(uint file) override {}
+	void loadSoundFile(Common::String name) override;
+
+	void playTrack(uint8 track) override;
+	void haltTrack() override;
+	bool isPlaying() const override;
+
+	void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
+	void stopAllSoundEffects() override;
+
+	void beginFadeOut() override;
+
+	void updateVolumeSettings() override;
+
+	int checkTrigger() override;
+
+	void resetTrigger() override;
+
+private:
+	KyraEngine_v1 *_vm;
+	CapcomPC98AudioDriver *_driver;
+
+	int _currentResourceSet;
+
+	kType _drvType;
+	bool _ready;
+};
+
 class SegaAudioDriver;
 class SoundSegaCD_EoB : public Sound {
 public:
diff --git a/engines/kyra/sound/sound_pc98_darkmoon.cpp b/engines/kyra/sound/sound_pc98_darkmoon.cpp
new file mode 100644
index 00000000000..20a3206df7b
--- /dev/null
+++ b/engines/kyra/sound/sound_pc98_darkmoon.cpp
@@ -0,0 +1,136 @@
+/* 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/>.
+ *
+ */
+
+#ifdef ENABLE_EOB
+
+#include "kyra/sound/sound_intern.h"
+#include "kyra/resource/resource.h"
+#include "kyra/sound/drivers/capcom98.h"
+
+#include "common/config-manager.h"
+
+namespace Kyra {
+
+SoundPC98_Darkmoon::SoundPC98_Darkmoon(KyraEngine_v1 *vm, MidiDriver::DeviceHandle dev, Audio::Mixer *mixer) : Sound(vm, mixer),
+	_vm(vm), _driver(0), _currentResourceSet(-1), _ready(false), _drvType(kPC98) {
+	MusicType type = MidiDriver::getMusicType(dev);
+	if (type == MT_MT32) {
+		_drvType = kMidiMT32;
+	} else if (type == MT_GM) {
+		_drvType = kMidiGM;
+	} else {
+
+	}
+}
+
+SoundPC98_Darkmoon::~SoundPC98_Darkmoon() {
+	delete _driver;
+}
+
+Sound::kType SoundPC98_Darkmoon::getMusicType() const {
+	return _drvType;
+}
+
+bool SoundPC98_Darkmoon::init() {
+	_driver = new CapcomPC98AudioDriver();
+	_ready = true;
+	return true;
+}
+
+void SoundPC98_Darkmoon::initAudioResourceInfo(int set, void *info) {
+	//delete _resInfo[set];
+	//_resInfo[set] = info ? new SoundResourceInfo_PC(*(SoundResourceInfo_PC*)info) : 0;
+}
+
+void SoundPC98_Darkmoon::selectAudioResourceSet(int set) {
+	if (set == _currentResourceSet || !_ready)
+		return;
+
+	//if (!_resInfo[set])
+	//	return;
+
+	_currentResourceSet = set;
+}
+
+void SoundPC98_Darkmoon::loadSoundFile(Common::String name) {
+	if (!_ready)
+		return;
+
+	//if (file >= _resInfo[_currentResourceSet]->fileListSize)
+	//	return;
+
+	//Common::SeekableReadStream *s = _vm->resource()->createReadStream(_resInfo[_currentResourceSet]->fileList[file]);
+	//_driver->loadMusicData(s);
+	//delete s;
+}
+
+void SoundPC98_Darkmoon::playTrack(uint8 track) {
+	if (!_musicEnabled || !_ready)
+		return;
+}
+
+void SoundPC98_Darkmoon::haltTrack() {
+	if (!_musicEnabled || !_ready)
+		return;
+	//playTrack(0);
+}
+
+bool SoundPC98_Darkmoon::isPlaying() const {
+	return false;
+}
+
+void SoundPC98_Darkmoon::playSoundEffect(uint16 track, uint8) {
+	if (!_sfxEnabled || !_ready || track >= 120)
+		return;
+	//_driver->startSoundEffect(track);
+}
+
+void SoundPC98_Darkmoon::stopAllSoundEffects() {
+
+}
+
+void SoundPC98_Darkmoon::beginFadeOut() {
+
+}
+
+int SoundPC98_Darkmoon::checkTrigger() {
+	return 99;
+}
+
+void SoundPC98_Darkmoon::resetTrigger() {
+
+}
+
+void SoundPC98_Darkmoon::updateVolumeSettings() {
+	if (!_driver || !_ready)
+		return;
+
+	bool mute = false;
+	if (ConfMan.hasKey("mute"))
+		mute = ConfMan.getBool("mute");
+
+	//_driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
+	//_driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
+}
+
+} // End of namespace Kyra
+
+#endif


Commit: 2f1190a473ce4018ee81bb12282e209571dc4e56
    https://github.com/scummvm/scummvm/commit/2f1190a473ce4018ee81bb12282e209571dc4e56
Author: athrxx (athrxx at scummvm.org)
Date: 2022-07-14T14:59:11+02:00

Commit Message:
KYRA: (EOB II/PC98) - fix intro and main menu text

Changed paths:
    engines/kyra/engine/darkmoon.cpp
    engines/kyra/sequence/sequences_darkmoon.cpp


diff --git a/engines/kyra/engine/darkmoon.cpp b/engines/kyra/engine/darkmoon.cpp
index 3cd7f8fa2aa..faf7df320c3 100644
--- a/engines/kyra/engine/darkmoon.cpp
+++ b/engines/kyra/engine/darkmoon.cpp
@@ -66,11 +66,14 @@ Common::Error DarkMoonEngine::init() {
 	_screen->loadPalette(_flags.platform == Common::kPlatformFMTowns ? "MENU.PAL" : "PALETTE.COL", _screen->getPalette(0));
 	_screen->setScreenPalette(_screen->getPalette(0));
 
+	// adjust menu settings for EOB II FM-Towns/PC-98 versions
 	if (_flags.platform == Common::kPlatformFMTowns) {
-		// adjust menu settings for EOB II FM-Towns
 		_screen->modifyScreenDim(6, 10, 100, 21, 40);
 		_screen->modifyScreenDim(27, 0, 0, 21, 2);
 		_vcnFilePattern = "%s.VCC";
+	} else if (_flags.platform == Common::kPlatformPC98) {
+		_screen->modifyScreenDim(6, 10, 100, 21, 40);
+		_screen->modifyScreenDim(27, 0, 0, 21, 5);
 	}
 
 	return Common::kNoError;
diff --git a/engines/kyra/sequence/sequences_darkmoon.cpp b/engines/kyra/sequence/sequences_darkmoon.cpp
index 65b3809dc39..2bd8f7d0840 100644
--- a/engines/kyra/sequence/sequences_darkmoon.cpp
+++ b/engines/kyra/sequence/sequences_darkmoon.cpp
@@ -1450,7 +1450,8 @@ void DarkmoonSequenceHelper::printText(int index, int color) {
 	strcpy(str, _config->strings[index]);
 
 	const ScreenDim *dm = _screen->_curDim;
-	int fontHeight = _screen->getFontHeight() + 1;
+	int fontHeight = (_vm->gameFlags().platform == Common::kPlatformPC98) ? (_screen->getFontHeight() << 1) : (_screen->getFontHeight() + 1);
+	int xAlignFactor = (_vm->gameFlags().platform == Common::kPlatformPC98) ? 2 : 1;
 
 	for (int yOffs = 0; *str; yOffs += fontHeight) {
 		char *cr = strchr(str, 13);
@@ -1459,7 +1460,7 @@ void DarkmoonSequenceHelper::printText(int index, int color) {
 			*cr = 0;
 
 		uint32 len = strlen(str);
-		_screen->printText(str, (dm->sx + ((dm->w - len) >> 1)) << 3, dm->sy + yOffs, color, dm->unkA);
+		_screen->printText(str, (dm->sx * xAlignFactor + ((dm->w * xAlignFactor - len) >> 1)) << (4 - xAlignFactor), dm->sy + yOffs, color, dm->unkA);
 
 		if (cr) {
 			*cr = 13;
@@ -1654,7 +1655,7 @@ void DarkmoonSequenceHelper::init(DarkmoonSequenceHelper::Mode mode) {
 	memset(_textColor, 0, 3);
 
 	_screen->setScreenPalette(*_palettes[0]);
-	_prevFont = _screen->setFont(_vm->gameFlags().platform == Common::kPlatformFMTowns ? Screen::FID_SJIS_LARGE_FNT : Screen::FID_8_FNT);
+	_prevFont = _screen->setFont(_vm->gameFlags().platform == Common::kPlatformFMTowns ? Screen::FID_SJIS_LARGE_FNT : (_vm->gameFlags().platform == Common::kPlatformPC98 ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT));
 	_screen->hideMouse();
 
 	_vm->delay(150);
@@ -1753,8 +1754,6 @@ void DarkmoonSequenceHelper::waitForSongNotifier(int index, bool introUpdateAnim
 	else if (_vm->sound()->getMusicType() != Sound::kAdLib && _vm->gameFlags().platform != Common::kPlatformPC98)
 		return;
 
-	debug("waitForSongNotifier %d - waiting for trigger...", index);
-
 	int seq = 0;
 
 	while (_vm->sound()->musicEnabled() && _vm->sound()->checkTrigger() < index && !(_vm->skipFlag() || _vm->shouldQuit())) {
@@ -1768,8 +1767,6 @@ void DarkmoonSequenceHelper::waitForSongNotifier(int index, bool introUpdateAnim
 
 		_vm->updateInput();
 	}
-
-	debug("waitForSongNotifier %d - ...finished", index);
 }
 
 void DarkmoonSequenceHelper::updateAmigaSound() {


Commit: 13573911f5deb96c77698b2471777d6478a1d18a
    https://github.com/scummvm/scummvm/commit/13573911f5deb96c77698b2471777d6478a1d18a
Author: athrxx (athrxx at scummvm.org)
Date: 2022-07-14T14:59:12+02:00

Commit Message:
KYRA: (EOB II/PC98) - adapt file formats

(The quickstart party can now already be loaded)

Changed paths:
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/scene_eob.cpp
    engines/kyra/gui/saveload.cpp


diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 5a5967a8248..43ed57420aa 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -583,8 +583,8 @@ void EoBCoreEngine::loadFonts() {
 
 	if (_flags.platform == Common::kPlatformFMTowns) {
 		_screen->loadFont(Screen::FID_SJIS_SMALL_FNT, "FONT.DMP");
-	} else if (_flags.gameID == GI_EOB1 && _flags.platform == Common::kPlatformPC98) {
-		_screen->loadFont(Screen::FID_SJIS_SMALL_FNT, "FONT12.FNT");
+	} else if (_flags.platform == Common::kPlatformPC98) {
+		_screen->loadFont(Screen::FID_SJIS_SMALL_FNT, _flags.gameID == GI_EOB1 ? "FONT12.FNT" : "FONT1206.FNT");
 		_invFont1 = Screen::FID_SJIS_SMALL_FNT;
 		_conFont = _invFont3 = Screen::FID_SJIS_FNT;
 	} else if (_flags.platform == Common::kPlatformSegaCD) {
@@ -604,12 +604,12 @@ Common::Error EoBCoreEngine::go() {
 
 	// Import original save game files (especially the "Quick Start Party").
 	// The SegaCD version has a "Default Party" main menu option instead.
-	/*if (ConfMan.getBool("importOrigSaves")) {
+	if (ConfMan.getBool("importOrigSaves")) {
 		if (_flags.platform != Common::kPlatformSegaCD)
 			importOriginalSaveFile(-1);
 		ConfMan.setBool("importOrigSaves", false);
 		ConfMan.flushToDisk();
-	}*/
+	}
 
 	loadItemDefs();
 	int action = 0;
diff --git a/engines/kyra/engine/scene_eob.cpp b/engines/kyra/engine/scene_eob.cpp
index 14c3e6cbcfe..804046be0f9 100644
--- a/engines/kyra/engine/scene_eob.cpp
+++ b/engines/kyra/engine/scene_eob.cpp
@@ -378,9 +378,13 @@ void EoBCoreEngine::loadBlockProperties(const char *mazFile) {
 }
 
 const uint8 *EoBCoreEngine::getBlockFileData(int) {
-	Common::SeekableReadStream *s = _res->createReadStream(_curBlockFile);
-	_screen->loadFileDataToPage(s, 15, s->size());
-	delete s;
+	if (_flags.gameID == GI_EOB2 && _flags.platform == Common::kPlatformPC98) {
+		_screen->loadBitmap(_curBlockFile.c_str(), 15, 15, 0);
+	} else {
+		Common::SeekableReadStream *s = _res->createReadStream(_curBlockFile);
+		_screen->loadFileDataToPage(s, 15, s->size());
+		delete s;
+	}
 	return _screen->getCPagePtr(15);
 }
 
diff --git a/engines/kyra/gui/saveload.cpp b/engines/kyra/gui/saveload.cpp
index be322635fce..b7aa18fd89d 100644
--- a/engines/kyra/gui/saveload.cpp
+++ b/engines/kyra/gui/saveload.cpp
@@ -180,7 +180,7 @@ Common::SeekableReadStream *KyraEngine_v1::openSaveForReading(const char *filena
 
 		if (header.version < 2) {
 			warning("Make sure your savefile was from this version! (too old savefile version to detect that)");
-		} else {
+		} else if (checkID) {
 			if ((header.flags & GF_FLOPPY) && (_flags.isTalkie || _flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98)) {
 				warning("Can not load DOS Floppy savefile for this (non DOS Floppy) gameversion");
 				delete in;
@@ -189,7 +189,7 @@ Common::SeekableReadStream *KyraEngine_v1::openSaveForReading(const char *filena
 				warning("Can not load DOS CD-ROM savefile for this (non DOS CD-ROM) gameversion");
 				delete in;
 				return nullptr;
-			} else if (checkID && ((header.flags & GF_FMTOWNS) && !(_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98))) {
+			} else if ((header.flags & GF_FMTOWNS) && !(_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98)) {
 				warning("Can not load FM-TOWNS/PC98 savefile for this (non FM-TOWNS/PC98) gameversion");
 				delete in;
 				return nullptr;


Commit: 2d38105396dcfa4e6783b6fde861bb70f7ab596f
    https://github.com/scummvm/scummvm/commit/2d38105396dcfa4e6783b6fde861bb70f7ab596f
Author: athrxx (athrxx at scummvm.org)
Date: 2022-07-14T14:59:12+02:00

Commit Message:
KYRA: (EOB II/PC98) - font/layout fixes

Changed paths:
    engines/kyra/engine/chargen.cpp
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/graphics/screen_eob.cpp
    engines/kyra/graphics/screen_eob.h
    engines/kyra/graphics/screen_eob_pc98.cpp
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/gui/gui_eob.h
    engines/kyra/resource/staticres_eob.cpp


diff --git a/engines/kyra/engine/chargen.cpp b/engines/kyra/engine/chargen.cpp
index 2e7da98e3ba..00003e03319 100644
--- a/engines/kyra/engine/chargen.cpp
+++ b/engines/kyra/engine/chargen.cpp
@@ -1904,7 +1904,7 @@ bool TransferPartyWiz::selectAndLoadTransferFile() {
 
 int TransferPartyWiz::selectCharactersMenu() {
 	_screen->setCurPage(2);
-	_screen->setFont(Screen::FID_6_FNT);
+	Screen::FontId of = _screen->setFont(Screen::FID_6_FNT);
 	_screen->clearCurPage();
 
 	_vm->gui_drawBox(0, 0, 320, 163, _vm->guiSettings()->colors.frame1, _vm->guiSettings()->colors.frame2, _vm->guiSettings()->colors.fill);
@@ -1984,7 +1984,7 @@ int TransferPartyWiz::selectCharactersMenu() {
 		_screen->updateScreen();
 
 		if (highlight == 6 || _vm->shouldQuit()) {
-			_screen->setFont(Screen::FID_8_FNT);
+			_screen->setFont(of);
 			return 0;
 		}
 
@@ -2002,7 +2002,7 @@ int TransferPartyWiz::selectCharactersMenu() {
 		_screen->updateScreen();
 	}
 
-	_screen->setFont(Screen::FID_8_FNT);
+	_screen->setFont(of);
 	if (_vm->shouldQuit())
 		return 0;
 	else
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 43ed57420aa..e2efeab675c 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -198,7 +198,7 @@ EoBCoreEngine::EoBCoreEngine(OSystem *system, const GameFlags &flags) : KyraRpgE
 	_buttonList3Size = _buttonList4Size = _buttonList5Size = _buttonList6Size = 0;
 	_buttonList7Size = _buttonList8Size = 0;
 	_inventorySlotsY = _mnDef = 0;
-	_invFont1 = _invFont2 = _conFont = Screen::FID_6_FNT;
+	_invFont1 = _invFont2 = _invFont4 = _conFont = _bookFont = Screen::FID_6_FNT;
 	_invFont3 = Screen::FID_8_FNT;
 	_transferStringsScummVM = 0;
 	_buttonDefs = 0;
@@ -576,6 +576,7 @@ void EoBCoreEngine::loadFonts() {
 	} else if (_flags.gameID == GI_EOB2 && _flags.platform == Common::kPlatformPC98) {
 		_screen->loadFont(Screen::FID_6_FNT, "FONT6B.FNT");
 		_screen->loadFont(Screen::FID_8_FNT, "FONT8B.FNT");
+		_screen->loadFont(Screen::FID_SJIS_SMALL_FNT, "FONT1206.FNT");
 	} else if (_flags.platform != Common::kPlatformSegaCD) {
 		_screen->loadFont(Screen::FID_6_FNT, "FONT6.FNT");
 		_screen->loadFont(Screen::FID_8_FNT, "FONT8.FNT");
@@ -584,13 +585,16 @@ void EoBCoreEngine::loadFonts() {
 	if (_flags.platform == Common::kPlatformFMTowns) {
 		_screen->loadFont(Screen::FID_SJIS_SMALL_FNT, "FONT.DMP");
 	} else if (_flags.platform == Common::kPlatformPC98) {
-		_screen->loadFont(Screen::FID_SJIS_SMALL_FNT, _flags.gameID == GI_EOB1 ? "FONT12.FNT" : "FONT1206.FNT");
-		_invFont1 = Screen::FID_SJIS_SMALL_FNT;
+		if (_flags.gameID == GI_EOB1) {
+			_screen->loadFont(Screen::FID_SJIS_SMALL_FNT, "FONT12.FNT");
+			_invFont1 = _bookFont = Screen::FID_SJIS_SMALL_FNT;
+			_invFont4 = Screen::FID_SJIS_FNT;
+		}		
 		_conFont = _invFont3 = Screen::FID_SJIS_FNT;
 	} else if (_flags.platform == Common::kPlatformSegaCD) {
 		_screen->loadFont(Screen::FID_8_FNT, "FONTK12");
 		_screen->setFontStyles(Screen::FID_8_FNT, Font::kStyleNone);
-		_invFont1 = _invFont2 = _conFont = Screen::FID_8_FNT;
+		_invFont1 = _invFont2 = _invFont4 = _conFont = Screen::FID_8_FNT;
 	}
 }
 
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index 6cd63762bb6..9e5fcdd64e9 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -834,7 +834,9 @@ protected:
 	Screen::FontId _invFont1;
 	Screen::FontId _invFont2;
 	Screen::FontId _invFont3;
+	Screen::FontId _invFont4;
 	Screen::FontId _conFont;
+	Screen::FontId _bookFont;
 	const uint8 **_compassShapes;
 	uint8 _charExchangeSwap;
 	uint8 *_swapShape;
diff --git a/engines/kyra/graphics/screen_eob.cpp b/engines/kyra/graphics/screen_eob.cpp
index 13666ff71cd..90b98c68424 100644
--- a/engines/kyra/graphics/screen_eob.cpp
+++ b/engines/kyra/graphics/screen_eob.cpp
@@ -1536,12 +1536,16 @@ bool Screen_EoB::loadFont(FontId fontId, const char *filename) {
 	if (fnt)
 		delete fnt;
 
-	if (fontId == FID_SJIS_SMALL_FNT) {
+	if (_vm->gameFlags().platform == Common::kPlatformPC98 && _vm->game() == GI_EOB2) {
+		// We use normal VGA rendering in EOB II, since we do the complete EGA dithering in updateScreen().
+		fnt = new OldDOSFont(_useHiResEGADithering ? Common::kRenderVGA : _renderMode, 12, 256, fontId == FID_SJIS_SMALL_FNT);
+	} else if (fontId == FID_SJIS_SMALL_FNT) {
 		if (_vm->gameFlags().platform == Common::kPlatformFMTowns)
 			fnt = new SJISFont12x12(_vm->staticres()->loadRawDataBe16(kEoB2FontDmpSearchTbl, temp));
-		else if (_vm->gameFlags().platform == Common::kPlatformPC98)
+		else if (_vm->gameFlags().platform == Common::kPlatformPC98) {
 			fnt = new Font12x12PC98(12, _vm->staticres()->loadRawDataBe16(kEoB1Ascii2SjisTable1, temp),
 				_vm->staticres()->loadRawDataBe16(kEoB1Ascii2SjisTable2, temp), _vm->staticres()->loadRawData(kEoB1FontLookupTable, temp));
+		}
 	} else if (_isAmiga) {
 		fnt = new AmigaDOSFont(_vm->resource(), _vm->game() == GI_EOB2 && _vm->gameFlags().lang == Common::DE_DEU);
 	} else if (_isSegaCD) {
@@ -1549,7 +1553,7 @@ bool Screen_EoB::loadFont(FontId fontId, const char *filename) {
 			_vm->staticres()->loadRawData(kEoB1CharWidthTable1, temp), _vm->staticres()->loadRawData(kEoB1CharWidthTable2, temp), _vm->staticres()->loadRawData(kEoB1CharWidthTable3, temp));
 	} else {
 		// We use normal VGA rendering in EOB II, since we do the complete EGA dithering in updateScreen().
-		fnt = new OldDOSFont(_useHiResEGADithering ? Common::kRenderVGA : _renderMode, 12);
+		fnt = new OldDOSFont(_useHiResEGADithering ? Common::kRenderVGA : _renderMode, 12, 128);
 	}
 
 	assert(fnt);
@@ -1792,7 +1796,7 @@ const uint8 Screen_EoB::_egaMatchTable[] = {
 uint16 *OldDOSFont::_cgaDitheringTable = 0;
 int OldDOSFont::_numRef = 0;
 
-OldDOSFont::OldDOSFont(Common::RenderMode mode, uint8 shadowColor) : _renderMode(mode), _shadowColor(shadowColor), _colorMap8bit(0), _colorMap16bit(0) {
+OldDOSFont::OldDOSFont(Common::RenderMode mode, uint8 shadowColor, uint16 numGlyphMax, bool useOverlay) : _renderMode(mode), _shadowColor(shadowColor), _numGlyphMax(numGlyphMax), _useOverlay(useOverlay), _colorMap8bit(0), _colorMap16bit(0) {
 	_data = 0;
 	_width = _height = _numGlyphs = 0;
 	_bitmapOffsets = 0;
@@ -1832,11 +1836,11 @@ bool OldDOSFont::load(Common::SeekableReadStream &file) {
 	if (file.size() - 2 != READ_LE_UINT16(_data))
 		return false;
 
-	_width = _data[0x103];
-	_height = _data[0x102];
+	_width = _data[_numGlyphMax * 2 + 3];
+	_height = _data[_numGlyphMax * 2 + 2];
 	_numGlyphs = (READ_LE_UINT16(_data + 2) / 2) - 2;
 
-	_bitmapOffsets = (uint16 *)(_data + 2);
+	_bitmapOffsets = (uint16*)(_data + 2);
 
 	for (int i = 0; i < _numGlyphs; ++i)
 		_bitmapOffsets[i] = READ_LE_UINT16(&_bitmapOffsets[i]);
diff --git a/engines/kyra/graphics/screen_eob.h b/engines/kyra/graphics/screen_eob.h
index 3292558079b..a6f63522fb8 100644
--- a/engines/kyra/graphics/screen_eob.h
+++ b/engines/kyra/graphics/screen_eob.h
@@ -228,13 +228,14 @@ private:
 */
 class OldDOSFont : public Font {
 public:
-	OldDOSFont(Common::RenderMode mode, uint8 shadowColor);
+	OldDOSFont(Common::RenderMode mode, uint8 shadowColor, uint16 _numGlyphMax, bool _useOverlay = false);
 	~OldDOSFont() override;
 
 	bool load(Common::SeekableReadStream &file) override;
 	Type getType() const override { return kASCII; }
 	int getHeight() const override { return _height; }
 	int getWidth() const override { return _width; }
+	bool usesOverlay() const override { return _useOverlay; }
 	int getCharWidth(uint16 c) const override;
 	void setColorMap(const uint8 *src) override;
 	void set16bitColorMap(const uint16 *src) override { _colorMap16bit = src; }
@@ -258,6 +259,9 @@ private:
 	Common::RenderMode _renderMode;
 	const uint16 *_colorMap16bit;
 
+	const uint16 _numGlyphMax;
+	const bool _useOverlay;
+
 	static uint16 *_cgaDitheringTable;
 	static int _numRef;
 };
diff --git a/engines/kyra/graphics/screen_eob_pc98.cpp b/engines/kyra/graphics/screen_eob_pc98.cpp
index 179ad900199..9d4f441b1dc 100644
--- a/engines/kyra/graphics/screen_eob_pc98.cpp
+++ b/engines/kyra/graphics/screen_eob_pc98.cpp
@@ -209,7 +209,7 @@ uint16 SJISFontEoB1PC98::convert(uint16 c) const {
 	return c;
 }
 
-Font12x12PC98::Font12x12PC98(uint8 shadowColor, const uint16 *convTable1, const uint16 *convTable2, const uint8 *lookupTable) : OldDOSFont(Common::kRenderDefault, 12),
+Font12x12PC98::Font12x12PC98(uint8 shadowColor, const uint16 *convTable1, const uint16 *convTable2, const uint8 *lookupTable) : OldDOSFont(Common::kRenderDefault, 12, 0),
 _convTable1(convTable1), _convTable2(convTable2) {
 	assert(convTable1);
 	assert(convTable2);
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index f5641608ad7..37453dea90b 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -209,8 +209,9 @@ void EoBCoreEngine::gui_drawCharPortraitWithStats(int index, bool screenUpdt) {
 			_screen->updateScreen();
 
 		} else {
-			_screen->setFont(cf);
+			_screen->setFont(_invFont4);
 			gui_drawCharacterStatsPage();
+			_screen->setFont(cf);
 		}
 
 		_screen->_curPage = 0;
@@ -641,7 +642,7 @@ void EoBCoreEngine::gui_drawSpellbook() {
 	int numTab = (_flags.gameID == GI_EOB1) ? 5 : 6;
 	_screen->copyRegion(64, 121, 64, 121, 112, 56, 0, 2, Screen::CR_NO_P_CHECK);
 
-	Screen::FontId of = (_flags.gameID == GI_EOB1 && _flags.platform == Common::kPlatformPC98) ? _screen->setFont(Screen::FID_SJIS_SMALL_FNT) : _screen->_currentFont;
+	Screen::FontId of = _screen->setFont(_bookFont);
 
 	for (int i = 0; i < numTab; i++) {
 		int col1 = 0;
@@ -1503,9 +1504,10 @@ void EoBCoreEngine::gui_processInventorySlotClick(int slot) {
 	}
 }
 
-GUI_EoB::GUI_EoB(EoBCoreEngine *vm) : GUI(vm), _vm(vm), _screen(vm->_screen), _numSlotsVisible(vm->gameFlags().platform == Common::kPlatformSegaCD ? 5 : 6) {
-	_menuStringsPrefsTemp = new char*[4]();
+GUI_EoB::GUI_EoB(EoBCoreEngine *vm) : GUI(vm), _vm(vm), _screen(vm->_screen), _numSlotsVisible(vm->gameFlags().platform == Common::kPlatformSegaCD ? 5 : 6),
+	_menuFont(_vm->gameFlags().platform == Common::kPlatformPC98 ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT) {
 
+	_menuStringsPrefsTemp = new char*[4]();
 	_saveSlotStringsTemp = new char*[6];
 	for (int i = 0; i < 6; i++) {
 		_saveSlotStringsTemp[i] = new char[52]();
@@ -2197,7 +2199,7 @@ void GUI_EoB::simpleMenu_flashSelection(const char *str, int x, int y, int color
 }
 
 void GUI_EoB::runCampMenu() {
-	Screen::FontId of = _screen->setFont(_vm->_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT);
+	Screen::FontId of = _screen->setFont(_menuFont);
 
 	Button *highlightButton = 0;
 	Button *prevHighlightButton = 0;
@@ -2527,7 +2529,7 @@ bool GUI_EoB::runLoadMenu(int x, int y, bool fromMainMenu) {
 
 bool GUI_EoB::confirmDialogue2(int dim, int id, int deflt) {
 	int od = _screen->curDimIndex();
-	Screen::FontId of = _screen->setFont(_vm->_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT);
+	Screen::FontId of = _screen->setFont(_menuFont);
 	_screen->setScreenDim(dim);
 
 	drawTextBox(dim, id);
@@ -2596,7 +2598,7 @@ bool GUI_EoB::confirmDialogue2(int dim, int id, int deflt) {
 void GUI_EoB::messageDialogue(int dim, int id, int buttonTextCol) {
 	int od = _screen->curDimIndex();
 	_screen->setScreenDim(dim);
-	Screen::FontId of = _screen->setFont(_vm->_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT);
+	Screen::FontId of = _screen->setFont(_menuFont);
 
 	drawTextBox(dim, id);
 	const ScreenDim *dm = _screen->getScreenDim(dim);
diff --git a/engines/kyra/gui/gui_eob.h b/engines/kyra/gui/gui_eob.h
index 6533113d404..1089e47a68a 100644
--- a/engines/kyra/gui/gui_eob.h
+++ b/engines/kyra/gui/gui_eob.h
@@ -166,6 +166,8 @@ private:
 	const uint8 *_highLightColorTable;
 	uint32 _highLightBoxTimer;
 
+	const Screen::FontId _menuFont;
+
 	const EoBRect16 *_highlightFrames;
 	static const EoBRect16 _highlightFramesDefault[];
 	static const uint8 _highlightColorTableVGA[];
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index 8f44dd0b1a9..68f740617da 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -1744,7 +1744,7 @@ void DarkMoonEngine::initStaticResource() {
 	};
 
 	// ScummVM specific
-	static const char *const transferStringsScummVM[4][5] = {
+	static const char *const transferStringsScummVM[5][5] = {
 		{
 			"\r We cannot find any EOB save game\r file. Please make sure that the\r save game file with the party\r you wish to transfer is located\r in your ScummVM save game\r directory. If you have set up\r multiple save directories you\r have to copy the EOB save file\r into your EOB II save directory.\r Do you wish to try again?",
 			"Game ID",
@@ -1766,6 +1766,13 @@ void DarkMoonEngine::initStaticResource() {
 			"Escoge Fichero",
 			"\r\r   Un momento\r   por favor..."
 		},
+		{
+			"\x82""d""\x82""n""\x82""a""\x82""h""\x82\xcc\x83""f""\x81""[""\x83""^""\x81""iEOBDATA.SAV""\x81""j""\x82\xaa\x8c\xa9\x82\xc2\x82\xa9\x82\xe8\x82\xdc\x82\xb9\x82\xf1\x81""B""\x83""J""\x83\x8c\x83\x93\x83""g""\x83""f""\x83""B""\x83\x8c\x83""N""\x83""g""\x83\x8a\x82\xc9\x93""]""\x91\x97\x82\xb5\x82\xc4\x82\xa9\x82\xe7\x8e\xc0\x8d""s""\x82\xb5\x82\xc4\x89\xba\x82\xb3\x82\xa2\x81""B",
+			"\x83\x51\x81\x5B\x83\x80\x82\x68\x82\x63",
+			"\x82""d""\x82""n""\x82""a""\x82""h""\x82\xcc\x83""f""\x81""[""\x83""^""\x81""iEOBDATA.SAV""\x81""j""\x82\xaa\x8c\xa9\x82\xc2\x82\xa9\x82\xe8\x82\xdc\x82\xb9\x82\xf1\x81""B""\x83""J""\x83\x8c\x83\x93\x83""g""\x83""f""\x83""B""\x83\x8c\x83""N""\x83""g""\x83\x8a\x82\xc9\x93""]""\x91\x97\x82\xb5\x82\xc4\x82\xa9\x82\xe7\x8e\xc0\x8d""s""\x82\xb5\x82\xc4\x89\xba\x82\xb3\x82\xa2\x81""B",
+			"\x83\x51\x81\x5B\x83\x80\x82\xf0\x91\x49\x82\xf1\x82\xc5\x89\xba\x82\xb3\x82\xa2\x81\x42",
+			"\r\r   \x82\xa8\x91\xd2\x82\xbF\x89\xba\x82\xb3\x82\xa2\x81""E""\x81""E""\x81""E"
+		},
 		{
 			0, 0, 0, 0
 		}
@@ -1784,9 +1791,13 @@ void DarkMoonEngine::initStaticResource() {
 			_errorSlotNoNameString = errorSlotNoNameString[2];
 			_transferStringsScummVM = transferStringsScummVM[2];
 			break;
-		default:
+		case Common::JA_JPN:
 			_errorSlotNoNameString = errorSlotNoNameString[3];
 			_transferStringsScummVM = transferStringsScummVM[3];
+			break;
+		default:
+			_errorSlotNoNameString = errorSlotNoNameString[4];
+			_transferStringsScummVM = transferStringsScummVM[4];
 	}
 
 }


Commit: 0aafa70a5c6b8e6c524369951cd8175de168e27d
    https://github.com/scummvm/scummvm/commit/0aafa70a5c6b8e6c524369951cd8175de168e27d
Author: athrxx (athrxx at scummvm.org)
Date: 2022-07-14T14:59:13+02:00

Commit Message:
KYRA: (EOB II/PC98) - more work on the text display

Changed paths:
    engines/kyra/engine/darkmoon.cpp
    engines/kyra/engine/darkmoon.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/kyra_rpg.cpp
    engines/kyra/graphics/screen_eob.cpp
    engines/kyra/graphics/screen_eob.h
    engines/kyra/graphics/screen_eob_pc98.cpp
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/gui/gui_eob.h
    engines/kyra/resource/resource.h
    engines/kyra/resource/staticres_eob.cpp
    engines/kyra/text/text_rpg.cpp


diff --git a/engines/kyra/engine/darkmoon.cpp b/engines/kyra/engine/darkmoon.cpp
index faf7df320c3..5dc9537594f 100644
--- a/engines/kyra/engine/darkmoon.cpp
+++ b/engines/kyra/engine/darkmoon.cpp
@@ -777,6 +777,8 @@ const KyraRpgGUISettings *DarkMoonEngine::guiSettings() const {
 		return &_guiSettingsAmiga;
 	else if (_flags.platform == Common::kPlatformFMTowns)
 		return &_guiSettingsFMTowns;
+	else if (_flags.platform == Common::kPlatformPC98)
+		return &_guiSettingsPC98;
 	else
 		return &_guiSettingsDOS;
 }
diff --git a/engines/kyra/engine/darkmoon.h b/engines/kyra/engine/darkmoon.h
index ed8c2b9631b..bc271ad06a4 100644
--- a/engines/kyra/engine/darkmoon.h
+++ b/engines/kyra/engine/darkmoon.h
@@ -148,6 +148,7 @@ private:
 
 	static const KyraRpgGUISettings _guiSettingsDOS;
 	static const KyraRpgGUISettings _guiSettingsFMTowns;
+	static const KyraRpgGUISettings _guiSettingsPC98;
 	static const KyraRpgGUISettings _guiSettingsAmiga;
 	static const uint8 _egaDefaultPalette[];
 };
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index e2efeab675c..6c59bdcbae4 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -587,10 +587,11 @@ void EoBCoreEngine::loadFonts() {
 	} else if (_flags.platform == Common::kPlatformPC98) {
 		if (_flags.gameID == GI_EOB1) {
 			_screen->loadFont(Screen::FID_SJIS_SMALL_FNT, "FONT12.FNT");
-			_invFont1 = _bookFont = Screen::FID_SJIS_SMALL_FNT;
+			_bookFont = Screen::FID_SJIS_SMALL_FNT;
 			_invFont4 = Screen::FID_SJIS_FNT;
 		}		
 		_conFont = _invFont3 = Screen::FID_SJIS_FNT;
+		_invFont1 = Screen::FID_SJIS_SMALL_FNT;
 	} else if (_flags.platform == Common::kPlatformSegaCD) {
 		_screen->loadFont(Screen::FID_8_FNT, "FONTK12");
 		_screen->setFontStyles(Screen::FID_8_FNT, Font::kStyleNone);
diff --git a/engines/kyra/engine/kyra_rpg.cpp b/engines/kyra/engine/kyra_rpg.cpp
index c667f4c4233..ddf7940739b 100644
--- a/engines/kyra/engine/kyra_rpg.cpp
+++ b/engines/kyra/engine/kyra_rpg.cpp
@@ -139,7 +139,7 @@ KyraRpgEngine::KyraRpgEngine(OSystem *system, const GameFlags &flags) : KyraEngi
 	}
 
 	_buttonFont = Screen::FID_6_FNT;
-	if (_flags.use16ColorMode)
+	if (_flags.platform == Common::kPlatformPC98)
 		_buttonFont = _flags.gameID == GI_LOL ? Screen::FID_SJIS_TEXTMODE_FNT : Screen::FID_SJIS_FNT;
 	else if (_flags.gameID == GI_EOB2 && _flags.platform == Common::kPlatformFMTowns)
 		_buttonFont = Screen::FID_8_FNT;
@@ -246,6 +246,7 @@ bool KyraRpgEngine::posWithinRect(int posX, int posY, int x1, int y1, int x2, in
 void KyraRpgEngine::drawDialogueButtons() {
 	int cp = screen()->setCurPage(0);
 	Screen::FontId of = screen()->setFont(_buttonFont);
+	int cs = (_flags.platform == Common::kPlatformPC98 && !_flags.use16ColorMode) ? screen()->setFontStyles(_buttonFont, Font::kStyleFat) : -1;
 
 	for (int i = 0; i < _dialogueNumButtons; i++) {
 		int x = _dialogueButtonPosX[i];
@@ -264,6 +265,8 @@ void KyraRpgEngine::drawDialogueButtons() {
 			                    (_dialogueButtonYoffs + _dialogueButtonPosY[i]) + yOffset, _dialogueHighlightedButton == i ? _dialogueButtonLabelColor1 : _dialogueButtonLabelColor2, 0);
 		}
 	}
+	if (cs != -1)
+		screen()->setFontStyles(_buttonFont, cs);
 	screen()->setFont(of);
 	screen()->setCurPage(cp);
 }
diff --git a/engines/kyra/graphics/screen_eob.cpp b/engines/kyra/graphics/screen_eob.cpp
index 90b98c68424..4de7ca37abd 100644
--- a/engines/kyra/graphics/screen_eob.cpp
+++ b/engines/kyra/graphics/screen_eob.cpp
@@ -261,7 +261,7 @@ void Screen_EoB::loadFileDataToPage(Common::SeekableReadStream *s, int pageNum,
 void Screen_EoB::printShadedText(const char *string, int x, int y, int col1, int col2, int shadowCol, int pitch) {
 	if (_isSegaCD && shadowCol) {
 		printText(string, x + 1, y + 1, shadowCol, 0, pitch);
-	} else if (!_isSegaCD && _vm->gameFlags().lang != Common::JA_JPN) {
+	} else if (!_isSegaCD && !_use16ColorMode && !_useHiColorScreen && _fonts[_currentFont]->getType() == Font::kASCII) {
 		printText(string, x - 1, y, shadowCol, col2);
 		printText(string, x, y + 1, shadowCol, 0);
 		printText(string, x - 1, y + 1, shadowCol, 0);
@@ -1537,12 +1537,14 @@ bool Screen_EoB::loadFont(FontId fontId, const char *filename) {
 		delete fnt;
 
 	if (_vm->gameFlags().platform == Common::kPlatformPC98 && _vm->game() == GI_EOB2) {
-		// We use normal VGA rendering in EOB II, since we do the complete EGA dithering in updateScreen().
-		fnt = new OldDOSFont(_useHiResEGADithering ? Common::kRenderVGA : _renderMode, 12, 256, fontId == FID_SJIS_SMALL_FNT);
+		if (fontId == FID_SJIS_SMALL_FNT)
+			fnt = new PC98Font(12, true, 2, _vm->staticres()->loadRawData(kEoB2FontConvertTbl, temp));
+		else
+			fnt = new PC98Font(12, false, 1);
 	} else if (fontId == FID_SJIS_SMALL_FNT) {
-		if (_vm->gameFlags().platform == Common::kPlatformFMTowns)
-			fnt = new SJISFont12x12(_vm->staticres()->loadRawDataBe16(kEoB2FontDmpSearchTbl, temp));
-		else if (_vm->gameFlags().platform == Common::kPlatformPC98) {
+		if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
+			fnt = new SJISFont12x12(_vm->staticres()->loadRawDataBe16(kEoB2FontLookupTbl, temp));
+		} else if (_vm->gameFlags().platform == Common::kPlatformPC98) {
 			fnt = new Font12x12PC98(12, _vm->staticres()->loadRawDataBe16(kEoB1Ascii2SjisTable1, temp),
 				_vm->staticres()->loadRawDataBe16(kEoB1Ascii2SjisTable2, temp), _vm->staticres()->loadRawData(kEoB1FontLookupTable, temp));
 		}
@@ -1553,7 +1555,7 @@ bool Screen_EoB::loadFont(FontId fontId, const char *filename) {
 			_vm->staticres()->loadRawData(kEoB1CharWidthTable1, temp), _vm->staticres()->loadRawData(kEoB1CharWidthTable2, temp), _vm->staticres()->loadRawData(kEoB1CharWidthTable3, temp));
 	} else {
 		// We use normal VGA rendering in EOB II, since we do the complete EGA dithering in updateScreen().
-		fnt = new OldDOSFont(_useHiResEGADithering ? Common::kRenderVGA : _renderMode, 12, 128);
+		fnt = new OldDOSFont(_useHiResEGADithering ? Common::kRenderVGA : _renderMode, 12);
 	}
 
 	assert(fnt);
@@ -1796,7 +1798,7 @@ const uint8 Screen_EoB::_egaMatchTable[] = {
 uint16 *OldDOSFont::_cgaDitheringTable = 0;
 int OldDOSFont::_numRef = 0;
 
-OldDOSFont::OldDOSFont(Common::RenderMode mode, uint8 shadowColor, uint16 numGlyphMax, bool useOverlay) : _renderMode(mode), _shadowColor(shadowColor), _numGlyphMax(numGlyphMax), _useOverlay(useOverlay), _colorMap8bit(0), _colorMap16bit(0) {
+OldDOSFont::OldDOSFont(Common::RenderMode mode, uint8 shadowColor) : _renderMode(mode), _shadowColor(shadowColor), _numGlyphsMax(128), _useOverlay(false), _scaleV(1), _colorMap8bit(0), _colorMap16bit(0) {
 	_data = 0;
 	_width = _height = _numGlyphs = 0;
 	_bitmapOffsets = 0;
@@ -1836,8 +1838,8 @@ bool OldDOSFont::load(Common::SeekableReadStream &file) {
 	if (file.size() - 2 != READ_LE_UINT16(_data))
 		return false;
 
-	_width = _data[_numGlyphMax * 2 + 3];
-	_height = _data[_numGlyphMax * 2 + 2];
+	_width = _data[_numGlyphsMax * 2 + 3];
+	_height = _data[_numGlyphsMax * 2 + 2];
 	_numGlyphs = (READ_LE_UINT16(_data + 2) / 2) - 2;
 
 	_bitmapOffsets = (uint16*)(_data + 2);
@@ -1865,9 +1867,13 @@ void OldDOSFont::drawChar(uint16 c, byte *dst, int pitch, int bpp) const {
 	uint16 color2 = _colorMap8bit[0];
 
 	if (_style == kStyleLeftShadow) {
-		drawCharIntern(c, dst + pitch, pitch, 1, _shadowColor, 0);
-		drawCharIntern(c, dst - 1, pitch, 1, _shadowColor, 0);
-		drawCharIntern(c, dst - 1 + pitch, pitch, 1, _shadowColor, 0);
+		byte *dst2 = dst;
+		for (int i = 0; i < _scaleV; ++i) {
+			drawCharIntern(c, dst2 + pitch * _scaleV, pitch * _scaleV, 1, _shadowColor, 0);
+			drawCharIntern(c, dst2 - 1, pitch * _scaleV, 1, _shadowColor, 0);
+			drawCharIntern(c, dst2 - 1 + pitch * _scaleV, pitch * _scaleV, 1, _shadowColor, 0);
+			dst2 += pitch;
+		}
 	}
 
 	if (bpp == 2) {
@@ -1875,7 +1881,10 @@ void OldDOSFont::drawChar(uint16 c, byte *dst, int pitch, int bpp) const {
 		color2 = _colorMap16bit[0];
 	}
 
-	drawCharIntern(c, dst, pitch, bpp, color1, color2);
+	for (int i = 0; i < _scaleV; ++i) {
+		drawCharIntern(c, dst, pitch * _scaleV, bpp, color1, color2);
+		dst += pitch;
+	}
 }
 
 void OldDOSFont::drawCharIntern(uint16 c, byte *dst, int pitch, int bpp, int col1, int col2) const {
diff --git a/engines/kyra/graphics/screen_eob.h b/engines/kyra/graphics/screen_eob.h
index a6f63522fb8..2fa5fa42d00 100644
--- a/engines/kyra/graphics/screen_eob.h
+++ b/engines/kyra/graphics/screen_eob.h
@@ -228,7 +228,7 @@ private:
 */
 class OldDOSFont : public Font {
 public:
-	OldDOSFont(Common::RenderMode mode, uint8 shadowColor, uint16 _numGlyphMax, bool _useOverlay = false);
+	OldDOSFont(Common::RenderMode mode, uint8 shadowColor);
 	~OldDOSFont() override;
 
 	bool load(Common::SeekableReadStream &file) override;
@@ -253,15 +253,16 @@ protected:
 	int _numGlyphs;
 	uint8 _shadowColor;
 
+	uint16 _numGlyphsMax;
+	bool _useOverlay;
+	int _scaleV;
+
 private:
 	void drawCharIntern(uint16 c, byte *dst, int pitch, int bpp, int col1, int col2) const;
 	virtual uint16 convert(uint16 c) const;
 	Common::RenderMode _renderMode;
 	const uint16 *_colorMap16bit;
 
-	const uint16 _numGlyphMax;
-	const bool _useOverlay;
-
 	static uint16 *_cgaDitheringTable;
 	static int _numRef;
 };
@@ -352,7 +353,6 @@ private:
 	uint16 convert(uint16 c) const;
 	const uint16 *_convTable1, *_convTable2;
 	bool _defaultConv;
-	/*uint8 _shadowColor;*/
 };
 
 /**
@@ -376,6 +376,27 @@ private:
 	uint16 *_bmpOffs;
 };
 
+/**
+* OldDOSFont variant used in EOB II PC-98. It uses the same drawing routine, but supports weird vertical scaling, can be drawn
+* on the SJIS overlay and has some character conversion.
+*/
+class PC98Font : public OldDOSFont {
+public:
+	PC98Font(uint8 shadowColor, bool useOverlay, int scaleV, const uint8 *convTable = 0);
+	~PC98Font() override {}
+	bool load(Common::SeekableReadStream &file) override;
+	int getHeight() const override { return _outputHeight; }
+	int getWidth() const override { return _outputWidth; }
+	int getCharWidth(uint16 c) const override { return _outputWidth; };
+
+private:
+	uint16 convert(uint16 c) const override;
+	const uint8 *_convTable;
+
+	int _outputHeight;
+	int _outputWidth;
+};
+
 /**
 * SJIS Font variant used in the intro and outro of EOB II FM-Towns. It appears twice as large, since it is not rendered on the hires overlay pages.
 */
diff --git a/engines/kyra/graphics/screen_eob_pc98.cpp b/engines/kyra/graphics/screen_eob_pc98.cpp
index 9d4f441b1dc..b8eb3c388c8 100644
--- a/engines/kyra/graphics/screen_eob_pc98.cpp
+++ b/engines/kyra/graphics/screen_eob_pc98.cpp
@@ -209,8 +209,7 @@ uint16 SJISFontEoB1PC98::convert(uint16 c) const {
 	return c;
 }
 
-Font12x12PC98::Font12x12PC98(uint8 shadowColor, const uint16 *convTable1, const uint16 *convTable2, const uint8 *lookupTable) : OldDOSFont(Common::kRenderDefault, 12, 0),
-_convTable1(convTable1), _convTable2(convTable2) {
+Font12x12PC98::Font12x12PC98(uint8 shadowColor, const uint16 *convTable1, const uint16 *convTable2, const uint8 *lookupTable) : OldDOSFont(Common::kRenderDefault, 12), _convTable1(convTable1), _convTable2(convTable2) {
 	assert(convTable1);
 	assert(convTable2);
 	assert(lookupTable);
@@ -251,17 +250,15 @@ uint16 Font12x12PC98::convert(uint16 c) const {
 		c = _convTable2[l - 32];
 	} else if (l > 160 && l < 225) {
 		bool done = false;
-		if (1) {
-			if (h == 0xDE) {
-				if ((l >= 182 && l <= 196) || (l >= 202 && l <= 206)) {
-					c = _convTable1[l - 182];
-					done = true;
-				}
-			} else if (h == 0xDF) {
-				if (l >= 202 && l <= 206) {
-					c = _convTable1[l - 177];
-					done = true;
-				}
+		if (h == 0xDE) {
+			if ((l >= 182 && l <= 196) || (l >= 202 && l <= 206)) {
+				c = _convTable1[l - 182];
+				done = true;
+			}
+		} else if (h == 0xDF) {
+			if (l >= 202 && l <= 206) {
+				c = _convTable1[l - 177];
+				done = true;
 			}
 		}
 		if (!done)
@@ -283,6 +280,32 @@ uint16 Font12x12PC98::convert(uint16 c) const {
 	return c;
 }
 
+PC98Font::PC98Font(uint8 shadowColor, bool useOverlay, int scaleV, const uint8 *convTable) : OldDOSFont(Common::kRenderVGA, shadowColor), _convTable(convTable), _outputWidth(0), _outputHeight(0) {
+	_numGlyphsMax = 256;
+	_useOverlay = useOverlay;
+	_scaleV = scaleV;
+
+}
+
+bool PC98Font::load(Common::SeekableReadStream &file) {
+	bool res = OldDOSFont::load(file);
+
+	_outputWidth = _width;
+	_outputHeight = _height * _scaleV;
+
+	if (_useOverlay) {
+		_outputWidth >>= 1;
+		_outputHeight >>= 1;
+	}
+
+	return res;
+}
+
+uint16 PC98Font::convert(uint16 c) const {
+
+	return c;
+}
+
 } // End of namespace Kyra
 
 #endif // ENABLE_EOB
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 37453dea90b..275aed5143e 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -1505,7 +1505,8 @@ void EoBCoreEngine::gui_processInventorySlotClick(int slot) {
 }
 
 GUI_EoB::GUI_EoB(EoBCoreEngine *vm) : GUI(vm), _vm(vm), _screen(vm->_screen), _numSlotsVisible(vm->gameFlags().platform == Common::kPlatformSegaCD ? 5 : 6),
-	_menuFont(_vm->gameFlags().platform == Common::kPlatformPC98 ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT) {
+	_menuFont(_vm->gameFlags().platform == Common::kPlatformPC98 ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT),
+	_menuFont2(_vm->gameFlags().use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT) {
 
 	_menuStringsPrefsTemp = new char*[4]();
 	_saveSlotStringsTemp = new char*[6];
@@ -2200,6 +2201,7 @@ void GUI_EoB::simpleMenu_flashSelection(const char *str, int x, int y, int color
 
 void GUI_EoB::runCampMenu() {
 	Screen::FontId of = _screen->setFont(_menuFont);
+	int cs = (_vm->gameFlags().platform == Common::kPlatformPC98 && !_vm->gameFlags().use16ColorMode) ? _screen->setFontStyles(_menuFont, Font::kStyleFat) : -1;
 
 	Button *highlightButton = 0;
 	Button *prevHighlightButton = 0;
@@ -2483,6 +2485,9 @@ void GUI_EoB::runCampMenu() {
 		}
 	}
 
+	if (cs != -1)
+		_screen->setFontStyles(_menuFont, cs);
+
 	_screen->setFont(of);
 	releaseButtons(buttonList);
 	_vm->writeSettings();
@@ -2530,6 +2535,8 @@ bool GUI_EoB::runLoadMenu(int x, int y, bool fromMainMenu) {
 bool GUI_EoB::confirmDialogue2(int dim, int id, int deflt) {
 	int od = _screen->curDimIndex();
 	Screen::FontId of = _screen->setFont(_menuFont);
+	int cs = (_vm->gameFlags().platform == Common::kPlatformPC98 && !_vm->gameFlags().use16ColorMode) ? _screen->setFontStyles(_menuFont, Font::kStyleFat) : -1;
+
 	_screen->setScreenDim(dim);
 
 	drawTextBox(dim, id);
@@ -2589,6 +2596,10 @@ bool GUI_EoB::confirmDialogue2(int dim, int id, int deflt) {
 	_screen->updateScreen();
 
 	_screen->copyRegion(0, _screen->_curDim->h, _screen->_curDim->sx << 3, _screen->_curDim->sy, _screen->_curDim->w << 3, _screen->_curDim->h, 2, 0, Screen::CR_NO_P_CHECK);
+
+	if (cs != -1)
+		_screen->setFontStyles(_menuFont, cs);
+
 	_screen->setFont(of);
 	_screen->setScreenDim(od);
 
@@ -2599,6 +2610,7 @@ void GUI_EoB::messageDialogue(int dim, int id, int buttonTextCol) {
 	int od = _screen->curDimIndex();
 	_screen->setScreenDim(dim);
 	Screen::FontId of = _screen->setFont(_menuFont);
+	int cs = (_vm->gameFlags().platform == Common::kPlatformPC98 && !_vm->gameFlags().use16ColorMode) ? _screen->setFontStyles(_menuFont, Font::kStyleFat) : -1;
 
 	drawTextBox(dim, id);
 	const ScreenDim *dm = _screen->getScreenDim(dim);
@@ -2631,6 +2643,10 @@ void GUI_EoB::messageDialogue(int dim, int id, int buttonTextCol) {
 
 	_screen->copyRegion(0, dm->h, dm->sx << 3, dm->sy, dm->w << 3, dm->h, 2, 0, Screen::CR_NO_P_CHECK);
 	_screen->setScreenDim(od);
+
+	if (cs != -1)
+		_screen->setFontStyles(_menuFont, cs);
+
 	_screen->setFont(of);
 	dm = _screen->getScreenDim(dim);
 }
@@ -4068,7 +4084,7 @@ void GUI_EoB::printScribeScrollSpellString(const int16 *menuItems, int id, bool
 
 bool GUI_EoB::confirmDialogue(int id) {
 	int od = _screen->curDimIndex();
-	Screen::FontId of = _screen->setFont(_vm->_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT);
+	Screen::FontId of = _screen->setFont(_menuFont);
 
 	Button *buttonList = initMenu(5);
 
@@ -4291,13 +4307,18 @@ int GUI_EoB::selectCharacterDialogue(int id) {
 void GUI_EoB::displayTextBox(int id, int, bool) {
 	int op = _screen->setCurPage(2);
 	int od = _screen->curDimIndex();
-	Screen::FontId of = _screen->setFont(_vm->_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT);
+	Screen::FontId of = _screen->setFont(_menuFont);
 	_screen->setClearScreenDim(11);
 	const ScreenDim *dm = _screen->getScreenDim(11);
 
 	drawMenuButtonBox(dm->sx << 3, dm->sy, dm->w << 3, dm->h, false, false);
 	_screen->setTextMarginRight((dm->sx + dm->w) << 3);
-	_screen->printShadedText(getMenuString(id), (dm->sx << 3) + 5, dm->sy + 5, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+
+	Common::Point txtPos((dm->sx << 3) + 5, dm->sy + 5);
+	if (_vm->game() == GI_EOB2 && _vm->gameFlags().platform == Common::kPlatformPC98)
+		txtPos = Common::Point(dm->sx << 3, (dm->sy + 16) & ~7);
+
+	_screen->printShadedText(getMenuString(id), txtPos.x, txtPos.y, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
 	_screen->setTextMarginRight(Screen::SCREEN_W);
 	_screen->copyRegion(dm->sx << 3, dm->sy, dm->sx << 3, dm->sy, dm->w << 3, dm->h, 2, 0, Screen::CR_NO_P_CHECK);
 	_screen->updateScreen();
@@ -4477,10 +4498,11 @@ void GUI_EoB::memorizePrayMenuPrintString(int spellId, int bookPageIndex, int sp
 	int y = bookPageIndex * 9 + 50;
 	int col1 = (_vm->_configRenderMode == Common::kRenderCGA) ? 1 : _vm->guiSettings()->colors.guiColorWhite;
 	_screen->set16bitShadingLevel(4);
+	Screen::FontId of = _screen->setFont(_menuFont2);
 
 	if (spellId) {
 		Common::String s;
-		if (_vm->_flags.lang == Common::JA_JPN) {
+		if (_vm->_flags.use16ColorMode || _vm->_flags.useHiColorMode) {
 			s = spellType ? _vm->_clericSpellList[spellId] : _vm->_mageSpellList[spellId];
 			for (int i = s.size() >> 1; i < 17; ++i)
 				s.insertChar(' ', s.size());
@@ -4497,6 +4519,7 @@ void GUI_EoB::memorizePrayMenuPrintString(int spellId, int bookPageIndex, int sp
 		_screen->fillRect(6, y, 168, y + 8,  _vm->guiSettings()->colors.fill);
 	}
 
+	_screen->setFont(of);
 	_screen->set16bitShadingLevel(0);
 }
 
@@ -4642,7 +4665,7 @@ void GUI_EoB::sortSaveSlots() {
 }
 
 void GUI_EoB::restParty_updateRestTime(int hours, bool init) {
-	Screen::FontId of = _screen->setFont(_vm->_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT);
+	Screen::FontId of = _screen->setFont(_menuFont);
 	int od = _screen->curDimIndex();
 	_screen->setScreenDim(10);
 
diff --git a/engines/kyra/gui/gui_eob.h b/engines/kyra/gui/gui_eob.h
index 1089e47a68a..082bc4d70b3 100644
--- a/engines/kyra/gui/gui_eob.h
+++ b/engines/kyra/gui/gui_eob.h
@@ -167,6 +167,7 @@ private:
 	uint32 _highLightBoxTimer;
 
 	const Screen::FontId _menuFont;
+	const Screen::FontId _menuFont2;
 
 	const EoBRect16 *_highlightFrames;
 	static const EoBRect16 _highlightFramesDefault[];
diff --git a/engines/kyra/resource/resource.h b/engines/kyra/resource/resource.h
index 770a1d5c85e..26dc63b0a2f 100644
--- a/engines/kyra/resource/resource.h
+++ b/engines/kyra/resource/resource.h
@@ -1144,7 +1144,8 @@ enum KyraResources {
 
 	kEoB2UtilMenuStrings,
 	kEoB2Config2431Strings,
-	kEoB2FontDmpSearchTbl,
+	kEoB2FontLookupTbl,
+	kEoB2FontConvertTbl,
 	kEoB2Ascii2SjisTables,
 	kEoB2Ascii2SjisTables2,
 	kEoB2PcmSoundEffectsIngame,
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index 68f740617da..e55f711fa02 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -1833,6 +1833,17 @@ const KyraRpgGUISettings DarkMoonEngine::_guiSettingsFMTowns = {
 	}
 };
 
+const KyraRpgGUISettings DarkMoonEngine::_guiSettingsPC98 = {
+	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 9, 15, 95, 11, 2, 7, { 221, 76 }, { 189, 162 }, { 95, 95 } },
+	{ 186, 181, 183, 183, 184, 17, 23, 20, 186, 181, 183, 182, 177, 180, 15, 6, 8, 9, 2, 5, 4, 3, 12 },
+	{	{ 184, 256, -1}, { 2, 54, 106 }, 64, 50,
+		{ 8, 80, -1 }, { 11, 63, 115 }, { 181, -1, -1 }, { 3, -1, -1 },
+		{ 40, 112, -1 }, { 11, 27, 63, 79, 115, 131 },
+		{ 23, 95, -1}, { 46, 98, 150 }, 38, 3, { 250, 250, -1}, { 16, 25, -1 }, 51, 5,
+		13, 30
+	}
+};
+
 const KyraRpgGUISettings DarkMoonEngine::_guiSettingsDOS = {
 	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 9, 15, 95, 9, 2, 7, { 221, 76 }, { 189, 162 }, { 95, 95 } },
 	{ 186, 181, 183, 183, 184, 17, 23, 20, 186, 181, 183, 182, 177, 180, 15, 6, 8, 9, 2, 5, 4, 3, 12 },
diff --git a/engines/kyra/text/text_rpg.cpp b/engines/kyra/text/text_rpg.cpp
index 85e7c457995..8a39db7e46a 100644
--- a/engines/kyra/text/text_rpg.cpp
+++ b/engines/kyra/text/text_rpg.cpp
@@ -50,7 +50,7 @@ TextDisplayer_rpg::TextDisplayer_rpg(KyraRpgEngine *engine, Screen *scr) : _vm(e
 		_waitButtonFont = Screen::FID_SJIS_TEXTMODE_FNT;
 	else if ((_vm->game() == GI_EOB2 && _vm->gameFlags().platform == Common::kPlatformFMTowns))
 		_waitButtonFont = Screen::FID_8_FNT;
-	else if ((_vm->game() == GI_EOB1 && _vm->gameFlags().platform == Common::kPlatformPC98))
+	else if (_vm->gameFlags().platform == Common::kPlatformPC98)
 		_waitButtonFont = Screen::FID_SJIS_FNT;
 
 	_textDimData = new TextDimData[_screen->screenDimTableCount()];
@@ -540,7 +540,10 @@ void TextDisplayer_rpg::printDialogueText(int stringId, const char *pageBreakStr
 	Common::strlcpy(_dialogueBuffer, str, kEoBTextBufferSize);
 
 	_screen->set16bitShadingLevel(4);
+	int cs = (_vm->gameFlags().platform == Common::kPlatformPC98 && !_vm->gameFlags().use16ColorMode) ? _screen->setFontStyles(_screen->_currentFont, Font::kStyleFat) : -1;
 	displayText(_dialogueBuffer);
+	if (cs != -1)
+		_screen->setFontStyles(_screen->_currentFont, cs);
 	_screen->set16bitShadingLevel(0);
 
 	if (pageBreakString) {
@@ -559,7 +562,10 @@ void TextDisplayer_rpg::printDialogueText(const char *str, bool wait) {
 	Common::strlcpy(_dialogueBuffer, str, kEoBTextBufferSize);
 
 	strcpy(_dialogueBuffer, str);
+	int cs = (_vm->gameFlags().platform == Common::kPlatformPC98 && !_vm->gameFlags().use16ColorMode) ? _screen->setFontStyles(_screen->_currentFont, Font::kStyleFat) : -1;
 	displayText(_dialogueBuffer);
+	if (cs != -1)
+		_screen->setFontStyles(_screen->_currentFont, cs);
 	if (wait)
 		displayWaitButton();
 }
@@ -611,6 +617,7 @@ void TextDisplayer_rpg::textPageBreak() {
 
 	int cp = _screen->setCurPage(0);
 	Screen::FontId cf = _screen->setFont(_waitButtonFont);
+	int cs = (_vm->gameFlags().platform == Common::kPlatformPC98 && !_vm->gameFlags().use16ColorMode) ? _screen->setFontStyles(_waitButtonFont, Font::kStyleFat) : -1;
 
 	if (_vm->game() == GI_LOL)
 		_vm->_timer->pauseSingleTimer(11, true);
@@ -728,6 +735,8 @@ void TextDisplayer_rpg::textPageBreak() {
 		_vm->_updatePortraitSpeechAnimDuration = updatePortraitSpeechAnimDuration;
 	}
 
+	if (cs != -1)
+		_screen->setFontStyles(_waitButtonFont, cs);
 	_screen->setFont(cf);
 	_screen->setCurPage(cp);
 


Commit: ffa5a4b8fb1551ce426f229b048155be413fed22
    https://github.com/scummvm/scummvm/commit/ffa5a4b8fb1551ce426f229b048155be413fed22
Author: athrxx (athrxx at scummvm.org)
Date: 2022-07-14T14:59:13+02:00

Commit Message:
KYRA: (EOBII) - improve intro scrolling

(make it smoother)

Changed paths:
    engines/kyra/sequence/sequences_darkmoon.cpp


diff --git a/engines/kyra/sequence/sequences_darkmoon.cpp b/engines/kyra/sequence/sequences_darkmoon.cpp
index 2bd8f7d0840..aa9a21addcb 100644
--- a/engines/kyra/sequence/sequences_darkmoon.cpp
+++ b/engines/kyra/sequence/sequences_darkmoon.cpp
@@ -54,6 +54,8 @@ public:
 	void fadePalette(int index, int del);
 	void copyPalette(int srcIndex, int destIndex);
 
+	int hScroll(bool restart = false);
+
 	void initDelayedPaletteFade(int palIndex, int rate);
 	bool processDelayedPaletteFade();
 
@@ -102,6 +104,9 @@ private:
 	uint16 _sndNextTrackMarker;
 	const uint16 *_sndMarkersFMTowns;
 
+	uint32 _hScrollStartTimeStamp;
+	uint32 _hScrollResumeTimeStamp;
+
 	uint8 _textColor[3];
 
 	int _platformAnimOffset;
@@ -342,14 +347,12 @@ void DarkMoonEngine::seq_playIntro() {
 
 	sq.loadScene(1, 2);
 	sq.waitForSongNotifier(++songCurPos);
+	uint32 endtime = _system->getMillis();
 
 	// intro scroll
 	if (!skipFlag() && !shouldQuit()) {
-		for (int i = 0; i < 280; ++i) {
-			uint32 endtime = _system->getMillis() + 18;
-			_screen->copyRegion(9, 8, 8, 8, 303, 128, 0, 0, Screen::CR_NO_P_CHECK);
-			_screen->copyRegion(i, 0, 311, 8, 1, 128, 2, 0, Screen::CR_NO_P_CHECK);
-			_screen->updateScreen();
+		for (int i = sq.hScroll(true); i != 279; i = sq.hScroll()) {
+			endtime += 18;
 			if (_flags.platform == Common::kPlatformAmiga) {
 				if (i == 4 || i == 24 || i == 36)
 					sq.animCommand(39);
@@ -1522,6 +1525,7 @@ void DarkmoonSequenceHelper::init(DarkmoonSequenceHelper::Mode mode) {
 	_sndNextTrack = 1;
 	_sndNextTrackMarker = 0;
 	_sndMarkersFMTowns = soundMarkersFMTowns[mode];
+	_hScrollStartTimeStamp = _hScrollResumeTimeStamp = 0;
 
 	if (mode == kIntro) {
 		_config = new Config(
@@ -1704,6 +1708,33 @@ void DarkmoonSequenceHelper::copyPalette(int srcIndex, int destIndex) {
 	_palettes[destIndex]->copy(*_palettes[srcIndex]);
 }
 
+int DarkmoonSequenceHelper::hScroll(bool restart) {
+	if (restart)
+		_hScrollStartTimeStamp = _system->getMillis();
+	else if (!_hScrollStartTimeStamp)
+		return 0;
+
+	uint32 ct = _system->getMillis();
+	int state = (ct - _hScrollStartTimeStamp) / 18;
+	if (state < 0 || state > 279) {
+		_hScrollStartTimeStamp += (ct - _hScrollResumeTimeStamp);
+		state = (ct - _hScrollStartTimeStamp) / 18;
+		if (state < 0 || state > 279)
+			state = 279;
+	}
+
+	_hScrollResumeTimeStamp = ct;
+
+	_screen->copyRegion(9, 8, 8, 8, 303, 128, 0, 0, Screen::CR_NO_P_CHECK);
+	_screen->copyRegion(state, 0, 311, 8, 1, 128, 2, 0, Screen::CR_NO_P_CHECK);
+	_screen->updateScreen();
+
+	if (state == 279)
+		_hScrollStartTimeStamp = 0;
+
+	return state;
+}
+
 void DarkmoonSequenceHelper::initDelayedPaletteFade(int palIndex, int rate) {
 	_palettes[11]->copy(*_palettes[0]);
 
@@ -1744,7 +1775,12 @@ void DarkmoonSequenceHelper::delay(uint32 ticks) {
 		processDelayedPaletteFade();
 
 	} else {
-		_vm->delayUntil(end);
+		for (uint32 ct = 0; ct < end; ) {
+			if (ct + 18 <= end)
+				hScroll();
+			ct = _system->getMillis();
+			_vm->delay(MIN<uint32>(9, end - ct));
+		}
 	}
 }
 


Commit: 9ff6d2cf899d9599378d44909c7353bd8c82d6aa
    https://github.com/scummvm/scummvm/commit/9ff6d2cf899d9599378d44909c7353bd8c82d6aa
Author: athrxx (athrxx at scummvm.org)
Date: 2022-07-14T14:59:13+02:00

Commit Message:
temp

Changed paths:
    engines/kyra/sequence/sequences_darkmoon.cpp


diff --git a/engines/kyra/sequence/sequences_darkmoon.cpp b/engines/kyra/sequence/sequences_darkmoon.cpp
index aa9a21addcb..954da97b175 100644
--- a/engines/kyra/sequence/sequences_darkmoon.cpp
+++ b/engines/kyra/sequence/sequences_darkmoon.cpp
@@ -1790,6 +1790,8 @@ void DarkmoonSequenceHelper::waitForSongNotifier(int index, bool introUpdateAnim
 	else if (_vm->sound()->getMusicType() != Sound::kAdLib && _vm->gameFlags().platform != Common::kPlatformPC98)
 		return;
 
+	debug("waitForSongNotifier %d - waiting for trigger...", index);
+
 	int seq = 0;
 
 	while (_vm->sound()->musicEnabled() && _vm->sound()->checkTrigger() < index && !(_vm->skipFlag() || _vm->shouldQuit())) {
@@ -1803,6 +1805,8 @@ void DarkmoonSequenceHelper::waitForSongNotifier(int index, bool introUpdateAnim
 
 		_vm->updateInput();
 	}
+
+	debug("waitForSongNotifier %d - ...finished", index);
 }
 
 void DarkmoonSequenceHelper::updateAmigaSound() {


Commit: ee96d7b6ee24f7e9a2430338d819a07600bb1379
    https://github.com/scummvm/scummvm/commit/ee96d7b6ee24f7e9a2430338d819a07600bb1379
Author: athrxx (athrxx at scummvm.org)
Date: 2022-07-14T14:59:14+02:00

Commit Message:
EOBIIWIP1

Changed paths:
    engines/kyra/engine/chargen.cpp
    engines/kyra/graphics/screen_eob.h
    engines/kyra/graphics/screen_eob_pc98.cpp
    engines/kyra/gui/gui_eob.cpp


diff --git a/engines/kyra/engine/chargen.cpp b/engines/kyra/engine/chargen.cpp
index 00003e03319..a1d6b3c1702 100644
--- a/engines/kyra/engine/chargen.cpp
+++ b/engines/kyra/engine/chargen.cpp
@@ -475,8 +475,8 @@ void CharacterGenerator::checkForCompleteParty() {
 	_screen->copyRegion(0, 0, 160, 0, 160, 128, 2, 2, Screen::CR_NO_P_CHECK);
 	int cp = _screen->setCurPage(2);
 	int x = (_vm->gameFlags().platform == Common::kPlatformFMTowns) ? 184 : 168;
-	int y1 = (_vm->game() == GI_EOB2 && _vm->gameFlags().platform == Common::kPlatformPC98) ? 80 : 16;
-	int y2 = (_vm->game() == GI_EOB2 && _vm->gameFlags().platform == Common::kPlatformPC98) ? 112 : 61;
+	int y1 = (_vm->game() == GI_EOB2 && _vm->gameFlags().platform == Common::kPlatformPC98) ? 40 : 16;
+	int y2 = (_vm->game() == GI_EOB2 && _vm->gameFlags().platform == Common::kPlatformPC98) ? 56 : 61;
 	int cs = 0;
 
 	if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
diff --git a/engines/kyra/graphics/screen_eob.h b/engines/kyra/graphics/screen_eob.h
index 2fa5fa42d00..72b8faa587a 100644
--- a/engines/kyra/graphics/screen_eob.h
+++ b/engines/kyra/graphics/screen_eob.h
@@ -357,7 +357,7 @@ private:
 
 /**
 * OldDOSFont variant used in EOB I PC-98. It uses the same drawing routine, but has a different loader. It contains
-* ASCII and Katakana characters and requires several conversion tables to display these. It gets drawn on the SJIS overlay.
+* ASCII and Katakana characters and requires several conversion tables to display these. It gets drawn on the hires overlay.
 */
 class Font12x12PC98 : public OldDOSFont{
 public:
@@ -377,8 +377,8 @@ private:
 };
 
 /**
-* OldDOSFont variant used in EOB II PC-98. It uses the same drawing routine, but supports weird vertical scaling, can be drawn
-* on the SJIS overlay and has some character conversion.
+* OldDOSFont variant used in EOB II PC-98 which supports twice the number of characters. Some font files may include kana characters. The font supports
+* weird vertical scaling and can be drawn on the hires overlay.
 */
 class PC98Font : public OldDOSFont {
 public:
diff --git a/engines/kyra/graphics/screen_eob_pc98.cpp b/engines/kyra/graphics/screen_eob_pc98.cpp
index b8eb3c388c8..049f928447a 100644
--- a/engines/kyra/graphics/screen_eob_pc98.cpp
+++ b/engines/kyra/graphics/screen_eob_pc98.cpp
@@ -302,8 +302,27 @@ bool PC98Font::load(Common::SeekableReadStream &file) {
 }
 
 uint16 PC98Font::convert(uint16 c) const {
+	if (!_convTable || c < 128)
+		return c;
+
+	uint8 lo = c & 0xff;
+	uint8 hi = c >> 8;
+
+	if (lo == 0x81) {
+		if (hi >= 0x40 && hi <= 0xac)
+			return _convTable[hi - 0x40];
+	} else if (lo == 0x82) {
+		if (hi >= 0x4f && hi <= 0x58)
+			return hi + 0xe1;
+		if (hi >= 0x60 && hi <= 0x79)
+			return hi - 0x1f;
+		if (hi >= 0x81 && hi <= 0x9a)
+			return hi - 0x20;
+	} else if (lo == 0x83 && hi >= 0x40 && hi <= 0x93) {
+		return hi + 0x40;
+	}
 
-	return c;
+	return 0;
 }
 
 } // End of namespace Kyra
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 275aed5143e..1ccf8ee0eaa 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -117,7 +117,7 @@ void EoBCoreEngine::gui_drawCharPortraitWithStats(int index, bool screenUpdt) {
 			if (index == _exchangeCharacterId)
 				_screen->printText(_characterGuiStringsSt[0], x2 + 2, y2 + 2, guiSettings()->colors.guiColorDarkRed, guiSettings()->colors.fill);
 			else
-				_screen->printText(c->name, x2 + 2, y2 + (_flags.platform == Common::kPlatformFMTowns ? 1 : 2), txtCol1, _flags.use16ColorMode ? 0 : guiSettings()->colors.fill);
+				_screen->printText(c->name, x2 + 2, y2 + (_flags.platform == Common::kPlatformFMTowns ? 1 : 2), txtCol1, _flags.platform == Common::kPlatformPC98 ? 0 : guiSettings()->colors.fill);
 		}
 
 		_screen->setFont(_invFont2);


Commit: 2cf4ce91f3ea9b42adc9ae7ff4b173abf3309d59
    https://github.com/scummvm/scummvm/commit/2cf4ce91f3ea9b42adc9ae7ff4b173abf3309d59
Author: athrxx (athrxx at scummvm.org)
Date: 2022-07-14T14:59:14+02:00

Commit Message:
EOBIIWIPSTATRES

Changed paths:
    engines/kyra/resource/staticres_eob.cpp


diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index e55f711fa02..07b5a6d4751 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -1,4 +1,4 @@
-/* ScummVM - Graphic Adventure Engine
+/* 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
@@ -25,7 +25,6 @@
 
 #include "common/memstream.h"
 
-
 namespace Kyra {
 
 #ifdef ENABLE_EOB
@@ -1736,10 +1735,11 @@ void DarkMoonEngine::initStaticResource() {
 	_amigaSoundIndex2 = _staticres->loadRawData(kEoB2SoundIndex2, temp);
 	_amigaSoundPatch = _staticres->loadRawData(kEoB2MonsterSoundPatchData, _amigaSoundPatchSize);
 
-	static const char *const errorSlotNoNameString[4] = {
+	static const char *const errorSlotNoNameString[5] = {
 		" You must specify\r a name for your\r save game!",
 		" Spielst[nde m]ssen\r einen Namen haben!",
 		" Debes poner\run nombre al\rfichero!",
+		"\x83""Z""\x81""[""\x83""u""\x83""t""\x83""@""\x83""C""\x83\x8b\r\x82\xc9\x82\xcd\x96\xbc\x91""O""\x82\xaa\r\x95""K""\x97""v""\x82\xc5\x82\xb7\x81""B",
 		0
 	};
 
@@ -1748,7 +1748,7 @@ void DarkMoonEngine::initStaticResource() {
 		{
 			"\r We cannot find any EOB save game\r file. Please make sure that the\r save game file with the party\r you wish to transfer is located\r in your ScummVM save game\r directory. If you have set up\r multiple save directories you\r have to copy the EOB save file\r into your EOB II save directory.\r Do you wish to try again?",
 			"Game ID",
-			"\r It seems that you have already\r defeated Xanathar here. Do you\r wish to transfer the party that\r finished the game? If not, you\r will be able to select a save\r game from the save game\r dialogue.",
+			"\r It seems that you have already\r defeated Xanathar here. Do you\r wish to transfer the party that\r finished the game? If not, you\r will be able to select a save\r game from the save game\r dialog.",
 			"Select File",
 			"\r\r   Please wait..."
 		},
@@ -1767,9 +1767,9 @@ void DarkMoonEngine::initStaticResource() {
 			"\r\r   Un momento\r   por favor..."
 		},
 		{
-			"\x82""d""\x82""n""\x82""a""\x82""h""\x82\xcc\x83""f""\x81""[""\x83""^""\x81""iEOBDATA.SAV""\x81""j""\x82\xaa\x8c\xa9\x82\xc2\x82\xa9\x82\xe8\x82\xdc\x82\xb9\x82\xf1\x81""B""\x83""J""\x83\x8c\x83\x93\x83""g""\x83""f""\x83""B""\x83\x8c\x83""N""\x83""g""\x83\x8a\x82\xc9\x93""]""\x91\x97\x82\xb5\x82\xc4\x82\xa9\x82\xe7\x8e\xc0\x8d""s""\x82\xb5\x82\xc4\x89\xba\x82\xb3\x82\xa2\x81""B",
+			"EOBセーブゲームファイルが見つかりません。転送したいパーティのセーブゲームファイルがScummVMセーブゲームディレクトリにあることを確認してください。複数の保存ディレクトリを設定している場合は、EOB保存ファイルをEOBII保存ディレクトリにコピーする必要があります。再試行しますか?",
 			"\x83\x51\x81\x5B\x83\x80\x82\x68\x82\x63",
-			"\x82""d""\x82""n""\x82""a""\x82""h""\x82\xcc\x83""f""\x81""[""\x83""^""\x81""iEOBDATA.SAV""\x81""j""\x82\xaa\x8c\xa9\x82\xc2\x82\xa9\x82\xe8\x82\xdc\x82\xb9\x82\xf1\x81""B""\x83""J""\x83\x8c\x83\x93\x83""g""\x83""f""\x83""B""\x83\x8c\x83""N""\x83""g""\x83\x8a\x82\xc9\x93""]""\x91\x97\x82\xb5\x82\xc4\x82\xa9\x82\xe7\x8e\xc0\x8d""s""\x82\xb5\x82\xc4\x89\xba\x82\xb3\x82\xa2\x81""B",
+			"クサナターを倒したようです。ゲームを終了したパーティーを転送しますか?そうでない場合は、[ゲームの保存]ダイアログから[ゲームの保存]を選択できます。",
 			"\x83\x51\x81\x5B\x83\x80\x82\xf0\x91\x49\x82\xf1\x82\xc5\x89\xba\x82\xb3\x82\xa2\x81\x42",
 			"\r\r   \x82\xa8\x91\xd2\x82\xbF\x89\xba\x82\xb3\x82\xa2\x81""E""\x81""E""\x81""E"
 		},
@@ -1777,7 +1777,28 @@ void DarkMoonEngine::initStaticResource() {
 			0, 0, 0, 0
 		}
 	};
-
+	/*
+	EOB\xab\xbb\x3f\xab\xd6\xab\xb2\x3f\xab\xe0\xab\xd5\xab\xa1\xab\xa4\xab\xeb\xaa\xac\xcc\xb8\xaa\xc4\xaa\xab\xaa\xea\xaa
+	\xde\xaa\xbb\xaa\xf3\xa1\xa3\x3f\xe1\xea\xaa\xb7\xaa\xbf\xaa\xa4\xab\xd1\x3f\xab\xc6\xab\xa3\xaa\xce\xab\xbb\x3f\xab\xd6\xab\xb2
+	\x3f\xab\xe0\xab\xd5\xab\xa1\xab\xa4\xab\xeb\xaa\xacScummVM\xab\xbb\x3f\xab\xd6\xab\xb2\x3f\xab\xe0\xab\xc7
+	\xab\xa3\xab\xec\xab\xaf\xab\xc8\xab\xea\xaa\xcb\xaa\xa2\xaa\xeb\xaa\xb3\xaa\xc8\xaa\xf2\xfc\xac\xec\xe3\xaa\xb7\xaa\xc6\xaa\xaf
+	\xaa\xc0\xaa\xb5\xaa\xa4\xa1\xa3\xdc\xdc\x3f\xaa\xce\xdc\xc1\xf0\xed\xab\xc7\xab\xa3\xab\xec\xab\xaf\xab\xc8\xab\xea\xaa\xf2\xe0
+	\xe2\xef\xd2\xaa\xb7\xaa\xc6\xaa\xa4\xaa\xeb\xed\xde\xf9\xea\xaa\xcf\xa1\xa2\x45\x4f\x42\xdc\xc1\xf0\xed\xab\xd5\xab\xa1\xab\xa4
+	\xab\xeb\xaa\xf2\x45\x4f\x42\x49\x49\xdc\xc1\xf0\xed\xab\xc7\xab\xa3\xab\xec\xab\xaf\xab\xc8\xab\xea\xaa\xcb\xab\xb3\xab\xd4\x3f
+	\xaa\xb9\xaa\xeb\xf9\xb1\xe9\xa9\xaa\xac\xaa\xa2\xaa\xea\xaa\xde\xaa\xb9\xa1\xa3\xee\xa2\xe3\xcb\xfa\xbc\xaa\xb7\xaa\xde\xaa\xb9
+	\xaa\xab\xa3\xbf
+	*/
+	/*
+	\xab\xaf\xab\xb5\xab\xca\xab\xbf\x3f\xaa\xf2\xd3\xee\xaa\xb7\xaa
+	\xbf\xaa\xe8\xaa\xa6\xaa\xc7\xaa\xb9\xa1\xa3\xab\xb2\x3f\xab\xe0
+	\xaa\xf2\xf0\xfb\xd6\xf5\xaa\xb7\xaa\xbf\xab\xd1\x3f\xab\xc6\xab
+	\xa3\x3f\xaa\xf2\x3f\xe1\xea\xaa\xb7\xaa\xde\xaa\xb9\xaa\xab\xa3
+	\xbf\xaa\xbd\xaa\xa6\xaa\xc7\xaa\xca\xaa\xa4\xed\xde\xf9\xea\xaa
+	\xcf\xa1\xa2\x5b\xab\xb2\x3f\xab\xe0\xaa\xce\xdc\xc1\xf0\xed\x5d
+	\xab\xc0\xab\xa4\xab\xa2\xab\xed\xab\xb0\xaa\xab\xaa\xe9\x5b\xab
+	\xb2\x3f\xab\xe0\xaa\xce\xdc\xc1\xf0\xed\x5d\xaa\xf2\xe0\xd4\x3f
+	\xaa\xc7\xaa\xad\xaa\xde\xaa\xb9\xa1\xa3
+	*/
 	switch(_flags.lang) {
 		case Common::EN_ANY:
 			_errorSlotNoNameString = errorSlotNoNameString[0];


Commit: 6c370164dcb89f78f34e9a0ed4a0b136fda5fdb4
    https://github.com/scummvm/scummvm/commit/6c370164dcb89f78f34e9a0ed4a0b136fda5fdb4
Author: athrxx (athrxx at scummvm.org)
Date: 2022-07-14T14:59:14+02:00

Commit Message:
TTS: (Windows) - improve Windows text-to-speech

Change the code design a bit, to greatly reduce the lags which currently make this a bit unpleasant to use.
I think it can be improved more, but this is a good step for a commit.

Changed paths:
    backends/text-to-speech/windows/windows-text-to-speech.cpp
    backends/text-to-speech/windows/windows-text-to-speech.h


diff --git a/backends/text-to-speech/windows/windows-text-to-speech.cpp b/backends/text-to-speech/windows/windows-text-to-speech.cpp
index c17175857a9..6cf2cb3b889 100644
--- a/backends/text-to-speech/windows/windows-text-to-speech.cpp
+++ b/backends/text-to-speech/windows/windows-text-to-speech.cpp
@@ -29,44 +29,96 @@
 #include <windows.h>
 #include <servprov.h>
 
-#include <sapi.h>
-#if _SAPI_VER < 0x53
-#define SPF_PARSE_SAPI 0x80
-#endif
-
 #include "backends/platform/sdl/win32/win32_wrapper.h"
-
 #include "backends/text-to-speech/windows/windows-text-to-speech.h"
 
-
 #include "common/translation.h"
 #include "common/system.h"
 #include "common/ustr.h"
 #include "common/config-manager.h"
 
-ISpVoice *_voice;
+#define TTS_SPEECH_STR_MAXLEN	20000
+
+DWORD WINAPI speechHandler(LPVOID parameters) {
+	WindowsTextToSpeechManager::SpeechParameters *params = (WindowsTextToSpeechManager::SpeechParameters*)parameters;
+	WCHAR *speechBuff = new WCHAR[TTS_SPEECH_STR_MAXLEN];
+
+	for (bool runLoop = true; runLoop; ) {
+		WaitForMultipleObjects(ARRAYSIZE(params->handles), params->handles, true, INFINITE);
+
+		if (params->cmd > kWinTTSIsPlaying)
+			ReleaseMutex(params->mutex);
+
+		switch (params->cmd) {
+		case kWinTTSPlay: {
+			bool hasData = params->arg;
+			if (hasData) {
+				wcsncpy(speechBuff, (const WCHAR*)params->arg, TTS_SPEECH_STR_MAXLEN);
+				free(params->arg);
+				params->arg = nullptr;
+			}
+
+			ReleaseMutex(params->mutex);
+
+			if (hasData) {
+				HRESULT res = params->ttsVoice->Speak(speechBuff, SPF_ASYNC | SPF_PARSE_SAPI, nullptr);
+				if (res != S_OK)
+					warning("speechHandler(): error 0x%04x", res);
+			}
+		}	break;
+
+		case kWinTTSQuit:
+			runLoop = false;
+			// Fall through
+
+		case kWinTTSStop:
+			params->ttsVoice->Speak(nullptr, SPF_PURGEBEFORESPEAK | SPF_ASYNC, nullptr);
+			break;
+	
+		case kWinTTSPause:
+			params->ttsVoice->Pause();
+			break;
+
+		case kWinTTSResume:
+			params->ttsVoice->Resume();
+			break;
+
+		case kWinTTSIsPlaying: {
+			SPVOICESTATUS st;
+			params->ttsVoice->GetStatus(&st, nullptr);
+			params->result = (st.dwRunningState == SPRS_IS_SPEAKING);
+			SetEvent(params->sigDone);
+			ReleaseMutex(params->mutex);
+		}	break;
+
+		default:
+			error("%s(): Should never arrive here...(File '%s', Line %d", __FUNCTION__, __FILE__, __LINE__);
+		}
+	}
 
-// We need this pointer to be able to stop speech immediately.
-ISpAudio *_audio;
+	delete[] speechBuff;
+	return 0;
+}
 
 WindowsTextToSpeechManager::WindowsTextToSpeechManager()
-	: _speechState(BROKEN){
-	init();
-	_threadParams.queue = &_speechQueue;
-	_threadParams.state = &_speechState;
-	_threadParams.mutex = &_speechMutex;
-	_thread = nullptr;
-	_speechMutex = CreateMutex(nullptr, FALSE, nullptr);
-	if (_speechMutex == nullptr) {
-		_speechState = BROKEN;
-		warning("Could not create TTS mutex");
+	: _audio(nullptr), _voice(nullptr), _speechState(kSpStateBroken), _thread(nullptr), _threadParams(_voice), _mutex(_threadParams.handles[0]), _sigExec(_threadParams.handles[1]) {
+	if (!init())
+		return;
+	if ((_mutex = CreateMutex(nullptr, FALSE, nullptr)) == nullptr)
+		warning("WindowsTextToSpeechManager() : Could not create TTS mutex");
+	if ((_sigExec = CreateEvent(nullptr, FALSE, FALSE, nullptr)) == nullptr || (_threadParams.sigDone = CreateEvent(nullptr, FALSE, FALSE, nullptr)) == nullptr)
+		warning("WindowsTextToSpeechManager() : Could not create TTS signal event");
+	if (_mutex && _sigExec && _threadParams.sigDone) {
+		_thread = CreateThread(nullptr, 0, speechHandler, &_threadParams, 0, nullptr);
+		if (_thread == nullptr)
+			warning("WindowsTextToSpeechManager(): Could not create speech thread");
 	}
 }
 
-void WindowsTextToSpeechManager::init() {
+bool WindowsTextToSpeechManager::init() {
 	// init COM
 	if (FAILED(::CoInitialize(nullptr)))
-		return;
+		return false;
 
 	// init audio
 	ISpObjectTokenCategory *pTokenCategory;
@@ -91,17 +143,17 @@ void WindowsTextToSpeechManager::init() {
 	}
 	if (FAILED(hr)) {
 		warning("Could not initialize TTS audio");
-		return;
+		return false;
 	}
 
 	// init voice
 	hr = CoCreateInstance(CLSID_SpVoice, nullptr, CLSCTX_ALL, IID_ISpVoice, (void **)&_voice);
 	if (FAILED(hr)) {
 		warning("Could not initialize TTS voice");
-		return;
+		return false;
 	}
 
-	_speechState = NO_VOICE;
+	_speechState = kSpStateNoVoice;
 
 #ifdef USE_TRANSLATION
 	setLanguage(TransMan.getCurrentLanguage());
@@ -112,14 +164,11 @@ void WindowsTextToSpeechManager::init() {
 	_voice->SetOutput(_audio, FALSE);
 
 	if (!_ttsState->_availableVoices.empty())
-		_speechState = READY;
+		_speechState = kSpStateReady;
 	else
-		_speechState = NO_VOICE;
-	_lastSaid = "";
-	while (!_speechQueue.empty()) {
-		free(_speechQueue.front());
-		_speechQueue.pop_front();
-	}
+		_speechState = kSpStateNoVoice;
+
+	return true;
 }
 
 WindowsTextToSpeechManager::~WindowsTextToSpeechManager() {
@@ -128,214 +177,175 @@ WindowsTextToSpeechManager::~WindowsTextToSpeechManager() {
 	clearState();
 
 	if (_thread != nullptr) {
+		send(kWinTTSQuit);
 		WaitForSingleObject(_thread, INFINITE);
 		CloseHandle(_thread);
 	}
-	if (_speechMutex != nullptr) {
-		CloseHandle(_speechMutex);
-	}
+
+	if (_mutex != nullptr)
+		CloseHandle(_mutex);
+
+	if (_sigExec != nullptr)
+		CloseHandle(_sigExec);
+
+	if (_threadParams.sigDone != nullptr)
+		CloseHandle(_threadParams.sigDone);
+
 	if (_voice)
 		_voice->Release();
-	::CoUninitialize();
-}
 
-DWORD WINAPI startSpeech(LPVOID parameters) {
-	WindowsTextToSpeechManager::SpeechParameters *params =
-		(WindowsTextToSpeechManager::SpeechParameters *) parameters;
-	// wait for the previous speech, if the previous thread exited too early
-	_voice->WaitUntilDone(INFINITE);
-
-	while (!params->queue->empty()) {
-		WaitForSingleObject(*params->mutex, INFINITE);
-		// check again, when we have exclusive access to the queue
-		if (params->queue->empty() || *(params->state) == WindowsTextToSpeechManager::PAUSED) {
-			ReleaseMutex(*params->mutex);
-			break;
-		}
-		WCHAR *currentSpeech = params->queue->front();
-		_voice->Speak(currentSpeech, SPF_PURGEBEFORESPEAK | SPF_ASYNC | SPF_PARSE_SAPI, nullptr);
-		ReleaseMutex(*params->mutex);
-
-		while (*(params->state) != WindowsTextToSpeechManager::PAUSED)
-			if (_voice->WaitUntilDone(10) == S_OK)
-				break;
-
-		WaitForSingleObject(*params->mutex, INFINITE);
-		if (!params->queue->empty() && params->queue->front() == currentSpeech) {
-			if (currentSpeech != nullptr)
-				free(currentSpeech);
-			params->queue->pop_front();
-		}
-		ReleaseMutex(*params->mutex);
-	}
+	if (_audio)
+		_audio->Release();
 
-	WaitForSingleObject(*params->mutex, INFINITE);
-	if (*(params->state) != WindowsTextToSpeechManager::PAUSED)
-		*(params->state) = WindowsTextToSpeechManager::READY;
-	ReleaseMutex(*params->mutex);
-	return 0;
+	::CoUninitialize();
 }
 
 bool WindowsTextToSpeechManager::say(const Common::U32String &str, Action action) {
-	if (_speechState == BROKEN || _speechState == NO_VOICE) {
+	if (_speechState == kSpStateBroken || _speechState == kSpStateNoVoice) {
 		if (_ttsState->_enabled)
 			warning("The text to speech cannot speak in this state");
 		return true;
 	}
 
-	if (isSpeaking() && action == DROP)
-		return true;
-
 	// We have to set the pitch by prepending xml code at the start of the said string;
 	Common::U32String pitch = Common::U32String::format("<pitch absmiddle=\"%d\"/>%S", _ttsState->_pitch / 10, str.c_str());
-	WCHAR *strW = (WCHAR *) pitch.encodeUTF16Native();
+	WCHAR *strW = (WCHAR*)pitch.encodeUTF16Native();
 	if (strW == nullptr) {
 		warning("Cannot convert from UTF-32 encoding for text to speech");
 		return true;
 	}
 
-	WaitForSingleObject(_speechMutex, INFINITE);
-	if (isSpeaking() && !_speechQueue.empty() && action == INTERRUPT_NO_REPEAT &&
-			_speechQueue.front() != NULL && !wcscmp(_speechQueue.front(), strW)) {
-		while (_speechQueue.size() != 1) {
-			free(_speechQueue.back());
-			_speechQueue.pop_back();
-		}
-		free(strW);
-		ReleaseMutex(_speechMutex);
+	bool speaking = isSpeaking();
+	if (speaking && action == DROP)
 		return true;
-	}
 
-	if (isSpeaking() && !_speechQueue.empty() && action == QUEUE_NO_REPEAT &&
-			_speechQueue.front() != NULL &&!wcscmp(_speechQueue.back(), strW)) {
-		ReleaseMutex(_speechMutex);
+	if (WaitForSingleObject(_mutex, 32) == WAIT_TIMEOUT) {
+		warning("WindowsTextToSpeechManager::say(): Mutex wait timeout");
 		return true;
 	}
 
-	ReleaseMutex(_speechMutex);
-	if ((isPaused() || isSpeaking()) && (action == INTERRUPT || action == INTERRUPT_NO_REPEAT)) {
+	if (speaking) {
+		if (action == INTERRUPT_NO_REPEAT || action == QUEUE_NO_REPEAT) {
+			if (strW && _curSpeechStr.equals(str)) {
+				free(strW);
+				ReleaseMutex(_mutex);
+				return true;
+			}
+		}
+	}
+
+	ReleaseMutex(_mutex);
+	if ((isPaused() || speaking) && (action == INTERRUPT || action == INTERRUPT_NO_REPEAT))
 		stop();
+
+	if (WaitForSingleObject(_mutex, 32) == WAIT_TIMEOUT) {
+		warning("WindowsTextToSpeechManager::say(): Mutex wait timeout");
+		return true;
 	}
 
-	WaitForSingleObject(_speechMutex, INFINITE);
-	_speechQueue.push_back(strW);
-	ReleaseMutex(_speechMutex);
+	_curSpeechStr = str;
+	ReleaseMutex(_mutex);
 
-	if (!isSpeaking() && !isPaused()) {
-		DWORD threadId;
-		if (_thread != nullptr) {
-			WaitForSingleObject(_thread, INFINITE);
-			CloseHandle(_thread);
-		}
-		_speechState = SPEAKING;
-		_thread = CreateThread(nullptr, 0, startSpeech, &_threadParams, 0, &threadId);
-		if (_thread == nullptr) {
-			warning("Could not create speech thread");
-			_speechState = READY;
+	if (!isPaused()) {
+		if (!send(kWinTTSPlay, strW))
 			return true;
-		}
 	}
 	return false;
 }
 
 bool WindowsTextToSpeechManager::stop() {
-	if (_speechState == BROKEN || _speechState == NO_VOICE)
+	if (_speechState == kSpStateBroken || _speechState == kSpStateNoVoice)
 		return true;
 	if (isPaused())
 		resume();
+
 	_audio->SetState(SPAS_STOP, 0);
-	WaitForSingleObject(_speechMutex, INFINITE);
-	// Delete the speech queue
-	while (!_speechQueue.empty()) {
-		if (_speechQueue.front() != NULL)
-			free(_speechQueue.front());
-		_speechQueue.pop_front();
-	}
-	// Stop the current speech
-	_voice->Speak(nullptr, SPF_PURGEBEFORESPEAK | SPF_ASYNC, nullptr);
-	_speechState = READY;
-	ReleaseMutex(_speechMutex);
+	_curSpeechStr.clear();
+	send(kWinTTSStop);
 	_audio->SetState(SPAS_RUN, 0);
+
 	return false;
 }
 
 bool WindowsTextToSpeechManager::pause() {
-	if (_speechState == BROKEN || _speechState == NO_VOICE)
+	if (_speechState == kSpStateBroken || _speechState == kSpStateNoVoice)
 		return true;
 	if (isPaused())
 		return false;
-	WaitForSingleObject(_speechMutex, INFINITE);
-	_voice->Pause();
-	_speechState = PAUSED;
-	ReleaseMutex(_speechMutex);
+
+	if (!send(kWinTTSPause))
+		return true;
+
+	WaitForSingleObject(_mutex, INFINITE);
+	_speechState = kSpStatePaused;
+	ReleaseMutex(_mutex);
+
 	return false;
 }
 
 bool WindowsTextToSpeechManager::resume() {
-	if (_speechState == BROKEN || _speechState == NO_VOICE)
+	if (_speechState == kSpStateBroken || _speechState == kSpStateNoVoice)
 		return true;
 	if (!isPaused())
 		return false;
-	_voice->Resume();
-	DWORD threadId;
-	if (_thread != nullptr) {
-		WaitForSingleObject(_thread, INFINITE);
-		CloseHandle(_thread);
-	}
-	_speechState = SPEAKING;
-	_thread = CreateThread(nullptr, 0, startSpeech, &_threadParams, 0, &threadId);
-	if (_thread == nullptr) {
-		warning("Could not create speech thread");
-		_speechState = READY;
+
+	if (!send(kWinTTSResume))
 		return true;
-	}
+
+	WaitForSingleObject(_mutex, INFINITE);
+	_speechState = kSpStateReady;
+	ReleaseMutex(_mutex);
+
 	return false;
 }
 
 bool WindowsTextToSpeechManager::isSpeaking() {
-	return _speechState == SPEAKING;
+	return send(kWinTTSIsPlaying);
 }
 
 bool WindowsTextToSpeechManager::isPaused() {
-	return _speechState == PAUSED;
+	return _speechState == kSpStatePaused;
 }
 
 bool WindowsTextToSpeechManager::isReady() {
-	if (_speechState == BROKEN || _speechState == NO_VOICE)
-		return false;
-	if (_speechState != PAUSED && !isSpeaking())
-		return true;
-	else
+	if (_speechState == kSpStateBroken || _speechState == kSpStateNoVoice)
 		return false;
+	return (_speechState != kSpStatePaused && !isSpeaking());
 }
 
 void WindowsTextToSpeechManager::setVoice(unsigned index) {
-	if (_speechState == BROKEN || _speechState == NO_VOICE)
+	if (_speechState == kSpStateBroken || _speechState == kSpStateNoVoice)
 		return;
+	WaitForSingleObject(_mutex, INFINITE);
 	_voice->SetVoice((ISpObjectToken *) _ttsState->_availableVoices[index].getData());
+	ReleaseMutex(_mutex);
 	_ttsState->_activeVoice = index;
 }
 
 void WindowsTextToSpeechManager::setRate(int rate) {
-	if (_speechState == BROKEN || _speechState == NO_VOICE)
+	if (_speechState == kSpStateBroken || _speechState == kSpStateNoVoice)
 		return;
 	assert(rate >= -100 && rate <= 100);
+	WaitForSingleObject(_mutex, INFINITE);
 	_voice->SetRate(rate / 10);
+	ReleaseMutex(_mutex);
 	_ttsState->_rate = rate;
 }
 
 void WindowsTextToSpeechManager::setPitch(int pitch) {
-	if (_speechState == BROKEN || _speechState == NO_VOICE)
+	if (_speechState == kSpStateBroken || _speechState == kSpStateNoVoice)
 		return;
 	assert(pitch >= -100 && pitch <= 100);
 	_ttsState->_pitch = pitch;
 }
 
 void WindowsTextToSpeechManager::setVolume(unsigned volume) {
-	if (_speechState == BROKEN || _speechState == NO_VOICE)
+	if (_speechState == kSpStateBroken || _speechState == kSpStateNoVoice)
 		return;
 	assert(volume <= 100);
+	WaitForSingleObject(_mutex, INFINITE);
 	_voice->SetVolume(volume);
+	ReleaseMutex(_mutex);
 	_ttsState->_volume = volume;
 }
 
@@ -343,8 +353,8 @@ void WindowsTextToSpeechManager::setLanguage(Common::String language) {
 	if (_ttsState->_language != language.substr(0, 2) || _ttsState->_availableVoices.empty()) {
 		Common::TextToSpeechManager::setLanguage(language);
 		updateVoices();
-	} else if (_speechState == NO_VOICE) {
-		_speechState = READY;
+	} else if (_speechState == kSpStateNoVoice) {
+		_speechState = kSpStateReady;
 	}
 	setVoice(0);
 }
@@ -419,6 +429,39 @@ void WindowsTextToSpeechManager::createVoice(void *cpVoiceToken) {
 	_ttsState->_availableVoices.push_back(Common::TTSVoice(gender, age, (void *) voiceToken, desc));
 }
 
+bool WindowsTextToSpeechManager::send(int cmd, ...) {
+	if (WaitForSingleObject(_mutex, 32) == WAIT_TIMEOUT) {
+		warning("WindowsTextToSpeechManager::send(): Mutex wait timeout");
+		return false;
+	}
+
+	_threadParams.cmd = cmd;
+	if (cmd == kWinTTSPlay) {
+		va_list args;
+		va_start(args, cmd);
+		_threadParams.arg = va_arg(args, WCHAR*);
+		va_end(args);
+		if (_threadParams.arg == nullptr)
+			cmd = kWinTTSInvalid;
+	}
+
+	ReleaseMutex(_mutex);
+
+	if (cmd <= kWinTTSInvalid || cmd >= kWinTTSNumCommands)
+		return false;
+
+	BOOL result = SetEvent(_sigExec);
+	if (!result)
+		warning("WindowsTextToSpeechManager::send(): Failed to signal speech thread");
+
+	if (cmd != kWinTTSIsPlaying)
+		return result;
+
+	WaitForSingleObject(_threadParams.sigDone, INFINITE);
+
+	return _threadParams.result;
+}
+
 Common::String WindowsTextToSpeechManager::lcidToLocale(LCID locale) {
 	int nchars = GetLocaleInfo(locale, LOCALE_SISO639LANGNAME, nullptr, 0);
 	TCHAR *languageCode = new TCHAR[nchars];
@@ -430,11 +473,11 @@ Common::String WindowsTextToSpeechManager::lcidToLocale(LCID locale) {
 
 void WindowsTextToSpeechManager::updateVoices() {
 	if (!_ttsState->_enabled) {
-		_speechState = NO_VOICE;
+		_speechState = kSpStateNoVoice;
 		return;
 	}
 
-	if (_speechState == BROKEN)
+	if (_speechState == kSpStateBroken)
 		return;
 
 	_ttsState->_availableVoices.clear();
@@ -458,28 +501,34 @@ void WindowsTextToSpeechManager::updateVoices() {
 	if (SUCCEEDED(hr)) {
 		hr = cpEnum->GetCount(&ulCount);
 	}
+
+	WaitForSingleObject(_mutex, INFINITE);
+
 	_voice->SetVolume(0);
 	while (SUCCEEDED(hr) && ulCount--) {
 		hr = cpEnum->Next(1, &cpVoiceToken, nullptr);
 		_voice->SetVoice(cpVoiceToken);
-		if (SUCCEEDED(_voice->Speak(L"hi, this is test", SPF_PURGEBEFORESPEAK | SPF_ASYNC | SPF_IS_NOT_XML, nullptr)))
+		if (SUCCEEDED(_voice->Speak(L"hi, this is a test", SPF_PURGEBEFORESPEAK | SPF_ASYNC | SPF_IS_NOT_XML, nullptr)))
 			createVoice(cpVoiceToken);
 		else
 			cpVoiceToken->Release();
 	}
-	// stop the test speech, we don't use stop(), because we don't wan't it to set state to READY
-	// and we could easily be in NO_VOICE or BROKEN state here, in which the stop() wouldn't work
+	// stop the test speech, we don't use stop(), because we don't wan't it to set state to kSpStateReady
+	// and we could easily be in kSpStateNoVoice or kSpStateBroken state here, in which the stop() wouldn't work
 	_audio->SetState(SPAS_STOP, 0);
 	_audio->SetState(SPAS_RUN, 0);
 	_voice->Speak(nullptr, SPF_PURGEBEFORESPEAK | SPF_ASYNC | SPF_IS_NOT_XML, nullptr);
 	_voice->SetVolume(_ttsState->_volume);
+
+	ReleaseMutex(_mutex);
+
 	cpEnum->Release();
 
 	if (_ttsState->_availableVoices.empty()) {
-		_speechState = NO_VOICE;
+		_speechState = kSpStateNoVoice;
 		warning("No voice is available for language: %s", _ttsState->_language.c_str());
-	} else if (_speechState == NO_VOICE)
-		_speechState = READY;
+	} else if (_speechState == kSpStateNoVoice)
+		_speechState = kSpStateReady;
 }
 
 void WindowsTextToSpeechManager::freeVoiceData(void *data) {
diff --git a/backends/text-to-speech/windows/windows-text-to-speech.h b/backends/text-to-speech/windows/windows-text-to-speech.h
index d40f29fceb6..6cded54bcc0 100644
--- a/backends/text-to-speech/windows/windows-text-to-speech.h
+++ b/backends/text-to-speech/windows/windows-text-to-speech.h
@@ -31,21 +31,42 @@
 #include "common/ustr.h"
 #include "common/list.h"
 
+#include <sapi.h>
+#if _SAPI_VER < 0x53
+#define SPF_PARSE_SAPI 0x80
+#endif
+
+enum WinTTSCommand {
+	kWinTTSInvalid = -1,
+
+	kWinTTSPlay = 0,
+	kWinTTSIsPlaying,
+	kWinTTSStop,
+	kWinTTSPause,
+	kWinTTSResume,
+	kWinTTSQuit,
+
+	kWinTTSNumCommands
+};
 
 class WindowsTextToSpeechManager final : public Common::TextToSpeechManager {
 public:
 	enum SpeechState {
-		READY,
-		PAUSED,
-		SPEAKING,
-		BROKEN,
-		NO_VOICE
+		kSpStateReady = 0,
+		kSpStateBroken,
+		kSpStateNoVoice,
+		kSpStatePaused
 	};
 
 	struct SpeechParameters {
-		Common::List<WCHAR *> *queue;
-		SpeechState *state;
-		HANDLE *mutex;
+		SpeechParameters(ISpVoice *&voice) : ttsVoice(voice), mutex(handles[0]), handles { 0 }, cmd(kWinTTSQuit), arg(0), result(false) {}
+		int cmd;
+		void *arg;
+		bool result;
+		HANDLE &mutex;
+		HANDLE sigDone;
+		HANDLE handles[2];
+		ISpVoice *&ttsVoice;
 	};
 
 	WindowsTextToSpeechManager();
@@ -74,19 +95,22 @@ public:
 	void freeVoiceData(void *data) override;
 
 private:
-	void init();
+	bool init();
 	void updateVoices() override;
 	void createVoice(void *cpVoiceToken);
+	bool send(int cmd, ...);
 	Common::String lcidToLocale(LCID locale);
+
+	ISpAudio *_audio;
+	ISpVoice *_voice;
+
 	SpeechState _speechState;
-	Common::String _lastSaid;
 	HANDLE _thread;
-	Common::List<WCHAR *> _speechQueue;
+	Common::U32String _curSpeechStr;
 	SpeechParameters _threadParams;
-	HANDLE _speechMutex;
+	HANDLE &_mutex, &_sigExec;
 };
 
-
 #endif
 
 #endif // BACKENDS_UPDATES_WINDOWS_H


Commit: 663ab6b012e4eecf22571bd3e98c3b8b0af3833f
    https://github.com/scummvm/scummvm/commit/663ab6b012e4eecf22571bd3e98c3b8b0af3833f
Author: athrxx (athrxx at scummvm.org)
Date: 2022-07-14T14:59:15+02:00

Commit Message:
tts

Changed paths:
    backends/text-to-speech/windows/windows-text-to-speech.cpp
    backends/text-to-speech/windows/windows-text-to-speech.h


diff --git a/backends/text-to-speech/windows/windows-text-to-speech.cpp b/backends/text-to-speech/windows/windows-text-to-speech.cpp
index 6cf2cb3b889..7badb62bf31 100644
--- a/backends/text-to-speech/windows/windows-text-to-speech.cpp
+++ b/backends/text-to-speech/windows/windows-text-to-speech.cpp
@@ -32,6 +32,8 @@
 #include "backends/platform/sdl/win32/win32_wrapper.h"
 #include "backends/text-to-speech/windows/windows-text-to-speech.h"
 
+#include "audio/audiostream.h"
+
 #include "common/translation.h"
 #include "common/system.h"
 #include "common/ustr.h"
@@ -39,6 +41,20 @@
 
 #define TTS_SPEECH_STR_MAXLEN	20000
 
+/*
+class WinTTSAudioStream final : public Audio::AudioStream, public ISpStreamFormat {
+public:
+	WinTTSAudioStream();
+	~WinTTSAudioStream();
+
+	// AudioStream Interface
+
+	// IspStreamFormat Interface
+private:
+
+
+};
+*/
 DWORD WINAPI speechHandler(LPVOID parameters) {
 	WindowsTextToSpeechManager::SpeechParameters *params = (WindowsTextToSpeechManager::SpeechParameters*)parameters;
 	WCHAR *speechBuff = new WCHAR[TTS_SPEECH_STR_MAXLEN];
@@ -46,8 +62,10 @@ DWORD WINAPI speechHandler(LPVOID parameters) {
 	for (bool runLoop = true; runLoop; ) {
 		WaitForMultipleObjects(ARRAYSIZE(params->handles), params->handles, true, INFINITE);
 
-		if (params->cmd > kWinTTSIsPlaying)
+		if (params->cmd > kWinTTSIsPlaying) {
+			SetEvent(params->sigDone);
 			ReleaseMutex(params->mutex);
+		}
 
 		switch (params->cmd) {
 		case kWinTTSPlay: {
@@ -58,6 +76,7 @@ DWORD WINAPI speechHandler(LPVOID parameters) {
 				params->arg = nullptr;
 			}
 
+			SetEvent(params->sigDone);
 			ReleaseMutex(params->mutex);
 
 			if (hasData) {
@@ -86,7 +105,7 @@ DWORD WINAPI speechHandler(LPVOID parameters) {
 		case kWinTTSIsPlaying: {
 			SPVOICESTATUS st;
 			params->ttsVoice->GetStatus(&st, nullptr);
-			params->result = (st.dwRunningState == SPRS_IS_SPEAKING);
+			params->result = (st.dwRunningState != SPRS_DONE/*SPRS_IS_SPEAKING*/);
 			SetEvent(params->sigDone);
 			ReleaseMutex(params->mutex);
 		}	break;
@@ -101,7 +120,7 @@ DWORD WINAPI speechHandler(LPVOID parameters) {
 }
 
 WindowsTextToSpeechManager::WindowsTextToSpeechManager()
-	: _audio(nullptr), _voice(nullptr), _speechState(kSpStateBroken), _thread(nullptr), _threadParams(_voice), _mutex(_threadParams.handles[0]), _sigExec(_threadParams.handles[1]) {
+	: _audio(nullptr), _voice(nullptr), _speechState(kSpStateBroken), _thread(nullptr), _threadParams(_voice, _audio), _mutex(_threadParams.handles[0]), _sigExec(_threadParams.handles[1]) {
 	if (!init())
 		return;
 	if ((_mutex = CreateMutex(nullptr, FALSE, nullptr)) == nullptr)
@@ -115,6 +134,10 @@ WindowsTextToSpeechManager::WindowsTextToSpeechManager()
 	}
 }
 
+/*class WinTTSStream : public ISpStreamFormat {
+
+};*/
+
 bool WindowsTextToSpeechManager::init() {
 	// init COM
 	if (FAILED(::CoInitialize(nullptr)))
@@ -146,6 +169,8 @@ bool WindowsTextToSpeechManager::init() {
 		return false;
 	}
 
+	
+
 	// init voice
 	hr = CoCreateInstance(CLSID_SpVoice, nullptr, CLSCTX_ALL, IID_ISpVoice, (void **)&_voice);
 	if (FAILED(hr)) {
@@ -162,6 +187,10 @@ bool WindowsTextToSpeechManager::init() {
 #endif
 
 	_voice->SetOutput(_audio, FALSE);
+	//ISpStreamFormat *out;
+	//ISpStream s;
+
+	//_voice->SetOutput(out, FALSE);
 
 	if (!_ttsState->_availableVoices.empty())
 		_speechState = kSpStateReady;
@@ -264,6 +293,9 @@ bool WindowsTextToSpeechManager::stop() {
 	send(kWinTTSStop);
 	_audio->SetState(SPAS_RUN, 0);
 
+
+//	ISpTTSEngine::Speak
+
 	return false;
 }
 
@@ -454,12 +486,12 @@ bool WindowsTextToSpeechManager::send(int cmd, ...) {
 	if (!result)
 		warning("WindowsTextToSpeechManager::send(): Failed to signal speech thread");
 
-	if (cmd != kWinTTSIsPlaying)
-		return result;
-
-	WaitForSingleObject(_threadParams.sigDone, INFINITE);
+	if (WaitForSingleObject(_threadParams.sigDone, 32) == WAIT_TIMEOUT) {
+		warning("WindowsTextToSpeechManager::send(): Signal wait timeout");
+		return false;
+	}
 
-	return _threadParams.result;
+	return (cmd == kWinTTSIsPlaying) ? _threadParams.result : true;
 }
 
 Common::String WindowsTextToSpeechManager::lcidToLocale(LCID locale) {
diff --git a/backends/text-to-speech/windows/windows-text-to-speech.h b/backends/text-to-speech/windows/windows-text-to-speech.h
index 6cded54bcc0..67f650b3fd2 100644
--- a/backends/text-to-speech/windows/windows-text-to-speech.h
+++ b/backends/text-to-speech/windows/windows-text-to-speech.h
@@ -59,7 +59,7 @@ public:
 	};
 
 	struct SpeechParameters {
-		SpeechParameters(ISpVoice *&voice) : ttsVoice(voice), mutex(handles[0]), handles { 0 }, cmd(kWinTTSQuit), arg(0), result(false) {}
+		SpeechParameters(ISpVoice *&voice, ISpAudio *&audio) : ttsVoice(voice), ttsAudio(audio), mutex(handles[0]), handles { 0 }, cmd(kWinTTSQuit), arg(0), result(false) {}
 		int cmd;
 		void *arg;
 		bool result;
@@ -67,6 +67,7 @@ public:
 		HANDLE sigDone;
 		HANDLE handles[2];
 		ISpVoice *&ttsVoice;
+		ISpAudio *&ttsAudio;
 	};
 
 	WindowsTextToSpeechManager();


Commit: 4bf96009da9be01f12eb641dd9bfd406d66b615e
    https://github.com/scummvm/scummvm/commit/4bf96009da9be01f12eb641dd9bfd406d66b615e
Author: athrxx (athrxx at scummvm.org)
Date: 2022-07-14T14:59:17+02:00

Commit Message:
dfds

Changed paths:
    engines/scumm/imuse/imuse_internal.h
    engines/scumm/imuse/imuse_part.cpp
    engines/scumm/imuse/imuse_player.cpp


diff --git a/engines/scumm/imuse/imuse_internal.h b/engines/scumm/imuse/imuse_internal.h
index 9289cc546bb..a520b0537a5 100644
--- a/engines/scumm/imuse/imuse_internal.h
+++ b/engines/scumm/imuse/imuse_internal.h
@@ -218,8 +218,9 @@ protected:
 	HookDatas _hook;
 	ParameterFader _parameterFaders[4];
 
-	bool _isMT32;
 	bool _isMIDI;
+	bool _isMT32;
+	bool _isGM;
 	bool _supportsPercussion;
 
 protected:
@@ -275,6 +276,7 @@ public:
 	bool isFadingOut() const;
 	bool isMIDI() const { return _isMIDI; }
 	bool isMT32() const { return _isMT32; }
+	bool isGM() const { return _isGM; }
 	bool jump(uint track, uint beat, uint tick);
 	void onTimer();
 	void removePart(Part *part);
diff --git a/engines/scumm/imuse/imuse_part.cpp b/engines/scumm/imuse/imuse_part.cpp
index 641ddae9908..955e7acf20e 100644
--- a/engines/scumm/imuse/imuse_part.cpp
+++ b/engines/scumm/imuse/imuse_part.cpp
@@ -194,7 +194,7 @@ void Part::pitchBendFactor(byte value) {
 		return;
 	pitchBend(0);
 	_pitchbend_factor = value;
-	if (_mc)
+	if (_mc && !(_player->isGM()))
 		_mc->pitchBendFactor(value);
 }
 
@@ -339,7 +339,7 @@ void Part::sendAll() {
 	if (!clearToTransmit())
 		return;
 
-	_mc->pitchBendFactor(_pitchbend_factor);
+	//_mc->pitchBendFactor(_pitchbend_factor);
 	sendTranspose();
 	sendPitchBend();
 	_mc->volume(_vol_eff);
@@ -367,11 +367,18 @@ void Part::sendPitchBend() {
 	// so we'll do the scaling ourselves.
 	if (_player->_se->isNativeMT32())
 		bend = bend * _pitchbend_factor / 12;
+	else if (_player->isGM())
+		bend = (bend * _pitchbend_factor) >> 6;
+
+	debug("PART: %d: _pitchbend %d, _pitchbend_factor %d, _detune_eff %d, _transpose_eff %d", _mc->getNumber(), _pitchbend, _pitchbend_factor, _detune_eff, _transpose_eff);
 
 	// We send the transpose value separately for Amiga (which is more like the original handles this).
 	// Some rhythm instruments depend on this.
 	int8 transpose = _se->_isAmiga ? 0 : _transpose_eff;
-	_mc->pitchBend(clamp(bend + (_detune_eff * 64 / 12) + (transpose * 8192 / 12), -8192, 8191));
+	int16 arg1 = clamp(bend + (_detune_eff * 64 / 12) + (transpose * 8192 / 12), -8192, 8191);
+	int16 arg2 = ((clamp(bend + _detune_eff + (transpose << 7), -2048, 2047) + 0x800) << 2) - 0x2000;
+	//debug("ARG1: %d, ARG2: %d", arg1, arg2);
+	_mc->pitchBend(arg2);
 }
 
 void Part::sendTranspose() {
diff --git a/engines/scumm/imuse/imuse_player.cpp b/engines/scumm/imuse/imuse_player.cpp
index a60a3cb1065..33c1d27b7b3 100644
--- a/engines/scumm/imuse/imuse_player.cpp
+++ b/engines/scumm/imuse/imuse_player.cpp
@@ -107,6 +107,7 @@ bool Player::startSound(int sound, MidiDriver *midi) {
 	_isMT32 = _se->isMT32(sound);
 	_isMIDI = _se->isMIDI(sound);
 	_supportsPercussion = _se->supportsPercussion(sound);
+	_isGM = (_supportsPercussion && !_isMT32); // Unlike IMuseInternal::isMIDI(), IMuseInternal::supportsPercussion() really filters out all non-MIDI things...
 
 	_parts = nullptr;
 	_active = true;
@@ -1028,6 +1029,7 @@ void Player::fixAfterLoad() {
 		_isMT32 = _se->isMT32(_id);
 		_isMIDI = _se->isMIDI(_id);
 		_supportsPercussion = _se->supportsPercussion(_id);
+		_isGM = (_supportsPercussion && !_isMT32); // Unlike IMuseInternal::isMIDI(), IMuseInternal::supportsPercussion() really filters out all non-MIDI things...
 	}
 }
 


Commit: a70a7ec5a0d1427bcc286d9d52bb2f1620ff4b67
    https://github.com/scummvm/scummvm/commit/a70a7ec5a0d1427bcc286d9d52bb2f1620ff4b67
Author: athrxx (athrxx at scummvm.org)
Date: 2022-07-14T15:00:55+02:00

Commit Message:
c

Changed paths:
    engines/scumm/charset.cpp
    engines/scumm/gfx.cpp
    engines/scumm/gfx.h
    engines/scumm/input.cpp
    engines/scumm/palette.cpp
    engines/scumm/scumm.cpp
    engines/scumm/scumm.h


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index ab505f8c295..99d3c5e38c1 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -943,8 +943,8 @@ void CharsetRendererV3::drawChar(int chr, Graphics::Surface &s, int x, int y) {
 }
 
 void CharsetRenderer::translateColor() {
-	// Don't do anything for v1 here.
-	if (_vm->_game.version == 1)
+	// Don't do anything for v1 and v2 here.
+	if (_vm->_game.version == 1 || _vm->_game.version == 2)
 		return;
 
 	// Based on disassembly
diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index 5b38148c6ab..36cdd945884 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -256,13 +256,23 @@ byte GdiV1::remapColorToRenderMode(byte col) const {
 }
 
 GdiV2::GdiV2(ScummEngine *vm) : Gdi(vm) {
-	_roomStrips = nullptr;
 }
 
 GdiV2::~GdiV2() {
 	free(_roomStrips);
 }
 
+void GdiV2::setRenderModeColorMap(const byte *map) {
+	_colorMap = map;
+
+	uint8 col[256];
+	for (int i = 0; i < 256; ++i) {
+		uint8 h = map[i >> 4] & 0x0C;
+		uint8 l = map[i & 0x0f] & 0x03;
+		col[i] = (h << 4) | (l << 4) | h | l;
+	}
+}
+
 #ifdef USE_RGB_COLOR
 GdiHE16bit::GdiHE16bit(ScummEngine *vm) : GdiHE(vm) {
 }
@@ -767,8 +777,8 @@ void ScummEngine::drawStripToScreen(VirtScreen *vs, int x, int width, int top, i
 					_system->copyRectToScreen(blackbuf, 16, 0, 0, 16, 240); // Fix left strip
 				}
 			}
-		} else if (_game.version == 1) {
-			src = postProcessV1Graphics(vs, pitch, x, y, width, height);
+		} else if (_game.version == 1 || _game.version == 2) {
+			src = postProcessGraphics(vs, pitch, x, y, width, height);
 		} else if (_renderMode == Common::kRenderHercA || _renderMode == Common::kRenderHercG) {
 			ditherHerc(_compositeBuf, _hercCGAScaleBuf, width, &x, &y, &width, &height);
 
@@ -792,28 +802,42 @@ void ScummEngine::drawStripToScreen(VirtScreen *vs, int x, int width, int top, i
 	_system->copyRectToScreen(src, pitch, x, y, width, height);
 }
 
-const byte *ScummEngine::postProcessV1Graphics(VirtScreen *vs, int &pitch, int &x, int &y, int &width, int &height) const {
-	static const byte zakVrbColMap[] =	{ 0x0, 0x5, 0x5, 0x5, 0xA, 0xA, 0xA, 0xF, 0xF, 0x5, 0x5, 0x5, 0xA, 0xA, 0xF, 0xF };
-	static const byte zakTxtColMap[] =	{ 0x0, 0xF, 0xA, 0x5, 0xA, 0x5, 0x5, 0xF, 0xA, 0xA, 0xA, 0xA, 0xA, 0x5, 0x5, 0xF };
-	static const byte mmVrbColMap[] =	{ 0x0, 0x5, 0x5, 0x5, 0xA, 0xA, 0xA, 0xF, 0xA, 0x5, 0x5, 0x5, 0xA, 0xA, 0xA, 0xF };
+const byte *ScummEngine::postProcessGraphics(VirtScreen *vs, int &pitch, int &x, int &y, int &width, int &height) const {
+	static const byte v2VrbColMap[] =	{ 0x0, 0x5, 0x5, 0x5, 0xA, 0xA, 0xA, 0xF, 0xF, 0x5, 0x5, 0x5, 0xA, 0xA, 0xF, 0xF };
+	static const byte v2TxtColMap[] =	{ 0x0, 0xF, 0xA, 0x5, 0xA, 0x5, 0x5, 0xF, 0xA, 0xA, 0xA, 0xA, 0xA, 0x5, 0x5, 0xF };
+	static const byte mmv1VrbColMap[] =	{ 0x0, 0x5, 0x5, 0x5, 0xA, 0xA, 0xA, 0xF, 0xA, 0x5, 0x5, 0x5, 0xA, 0xA, 0xA, 0xF };
+
+
 
-	byte mmTxtColMap[16];
-	for (uint8 i = 0; i < ARRAYSIZE(mmTxtColMap); ++i)
-		mmTxtColMap[i] = mmVrbColMap[_gdi->remapColorToRenderMode(i)];
+
+	static const byte map[] = { 0x00, 0x04, 0x01, 0x05, 0x08, 0x0A, 0x02, 0x0F, 0x0C, 0x07, 0x0D, 0x05, 0x0E, 0x0B, 0x0D, 0x0F };
+
+	/*uint8 col[256];
+	for (int i = 0; i < 256; ++i) {
+		uint8 h = map[i >> 4] & 0x0C;
+		uint8 l = map[i & 0x0f] & 0x03;
+		col[i] = (h << 4) | (l << 4) | h | l;
+	}*/
+
+	byte tmpTxtColMap[16];
+	for (uint8 i = 0; i < ARRAYSIZE(tmpTxtColMap); ++i)
+		tmpTxtColMap[i] = mmv1VrbColMap[_gdi->remapColorToRenderMode(i)];
 
 	byte *res = _compositeBuf;
 	byte *dst = _compositeBuf;
 	const byte *src = res;
 	bool renderHerc = (_renderMode == Common::kRenderHercA || _renderMode == Common::kRenderHercG);
-
-	const byte *colMap = (_game.id == GID_ZAK) ? ((vs->number == kVerbVirtScreen || renderHerc) ? zakVrbColMap : zakTxtColMap) : (vs->number == kVerbVirtScreen ? mmVrbColMap : mmTxtColMap);
+	const byte *colMap = (_game.id == GID_ZAK || _game.version == 2) ? ((vs->number == kVerbVirtScreen || renderHerc) ? v2VrbColMap : v2TxtColMap) : (vs->number == kVerbVirtScreen ? mmv1VrbColMap : tmpTxtColMap);
 
 	if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderCGAComp) {
 		if (vs->number == kMainVirtScreen) {
 			for (int h = height; h; --h) {
 				for (int w = width >> 1; w; --w) {
-					*dst++ = (*src++ >> 2) & 3;
-					*dst++ = *src++ & 3;
+					byte c = (_game.version == 1) ? *src : (map[src[0]] & 0x0c) | (map[src[1]] & 3);
+					//byte c = (_game.version == 1) ? *src : col[(src[0] << 4) | src[1]];
+					*dst++ = (c >> 2) & 3;
+					*dst++ = c & 3;
+					src += 2;
 				}
 			}
 		} else {
@@ -851,10 +875,12 @@ const byte *ScummEngine::postProcessV1Graphics(VirtScreen *vs, int &pitch, int &
 			for (int h = height2; h; --h) {
 				for (int w = width >> 1; w; --w) {
 					const uint32 *s = (const uint32*)dst;
-					*dst++ = (*src >> 3) & 1;
-					*dst++ = (*src >> 2) & 1;
-					*dst++ = (*src >> 1) & 1;
-					*dst++ = *src & 1;
+					//byte c = (_game.version == 1) ? *src : col[(src[0] << 4) | src[1]];
+					byte c = (_game.version == 1) ? *src : (map[src[0]] & 0x0c) | (map[src[1]] & 3);
+					*dst++ = (c >> 3) & 1;
+					*dst++ = (c >> 2) & 1;
+					*dst++ = (c >> 1) & 1;
+					*dst++ = c & 1;
 					*dst2++ = renderHerc ? 0 : *s;
 					src += 2;
 				}
@@ -899,11 +925,13 @@ const byte *ScummEngine::postProcessV1Graphics(VirtScreen *vs, int &pitch, int &
 			height <<= 1;
 		}
 
-	} else if (vs->number == kTextVirtScreen) {
+	} else if (_game.version == 1 && vs->number == kTextVirtScreen) {
 		// For EGA, the only colors that need remapping are for the kTextVirtScreen.
+		for (uint8 i = 0; i < ARRAYSIZE(tmpTxtColMap); ++i)
+			tmpTxtColMap[i] = _gdi->remapColorToRenderMode(i);
 		for (int h = height; h; --h)  {
 			for (int w = width; w; --w)
-				*dst++ = _gdi->remapColorToRenderMode(*src++);
+				*dst++ = tmpTxtColMap[*src++];
 		}
 	}
 
@@ -1806,6 +1834,9 @@ void GdiV2::prepareDrawBitmap(const byte *ptr, VirtScreen *vs,
 					dither = false;
 				}
 				color = _roomPalette[data & 0x0f];
+
+				//(map[src[0]] & 0x0c) | (map[src[1]] & 3)
+
 				if (run == 0) {
 					run = *src++;
 				}
diff --git a/engines/scumm/gfx.h b/engines/scumm/gfx.h
index 79e92564f95..80a49c1ca7b 100644
--- a/engines/scumm/gfx.h
+++ b/engines/scumm/gfx.h
@@ -407,7 +407,7 @@ protected:
 		byte maskMap[4096], maskChar[4096];
 	} _V1;
 
-	const byte *_colorMap;
+	const byte *_colorMap = 0;
 
 protected:
 	void decodeV1Gfx(const byte *src, byte *dst, int size) const;
@@ -440,7 +440,8 @@ public:
 class GdiV2 : public Gdi {
 protected:
 	/** For V2 games, we cache offsets into the room graphics, to speed up things. */
-	StripTable *_roomStrips;
+	StripTable *_roomStrips = nullptr;
+	const byte *_colorMap = nullptr;
 
 protected:
 	StripTable *generateStripTable(const byte *src, int width, int height, StripTable *table) const;
@@ -461,6 +462,8 @@ public:
 	GdiV2(ScummEngine *vm);
 	~GdiV2() override;
 
+	void setRenderModeColorMap(const byte *map) override;
+
 	void roomChanged(byte *roomptr) override;
 };
 
diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp
index e4d65b4572d..41e8e395e2e 100644
--- a/engines/scumm/input.cpp
+++ b/engines/scumm/input.cpp
@@ -199,16 +199,11 @@ void ScummEngine::parseEvent(Common::Event event) {
 		if (_renderMode == Common::kRenderHercA || _renderMode == Common::kRenderHercG) {
 			_mouse.x -= (kHercWidth - _screenWidth * 2) / 2;
 			_mouse.x >>= 1;
-			if (_game.version == 1) {
-				if (_mouse.y >= _virtscr[kMainVirtScreen].topline)
-					_mouse.y = _mouse.y / 2 + _virtscr[kMainVirtScreen].topline / 2;
-				if (_mouse.y > _virtscr[kVerbVirtScreen].topline)
-					_mouse.y += (_mouse.y - _virtscr[kVerbVirtScreen].topline);
-					
-			} else {
-				_mouse.y = _mouse.y * 4 / 7;
-			}
-			
+			if (_mouse.y >= _virtscr[kMainVirtScreen].topline)
+				_mouse.y = _mouse.y / 2 + _virtscr[kMainVirtScreen].topline / 2;
+			if (_mouse.y > _virtscr[kVerbVirtScreen].topline)
+				_mouse.y += (_mouse.y - _virtscr[kVerbVirtScreen].topline);
+	
 		} else if (_macScreen || (_useCJKMode && _textSurfaceMultiplier == 2) || _renderMode == Common::kRenderCGA_BW) {
 			_mouse.x >>= 1;
 			_mouse.y >>= 1;
diff --git a/engines/scumm/palette.cpp b/engines/scumm/palette.cpp
index ed0edf2bbdd..a536fa979cd 100644
--- a/engines/scumm/palette.cpp
+++ b/engines/scumm/palette.cpp
@@ -191,7 +191,19 @@ void ScummEngine::resetPalette() {
 	int cgaPalIndex = 1;
 	int cgaPalIntensity = 1;
 
-	if (_game.version <= 1) {
+	if (_renderMode == Common::kRenderHercA) {
+		setPaletteFromTable(tableHercAPalette, sizeof(tableHercAPalette) / 3);
+	} else if (_renderMode == Common::kRenderHercG) {
+		setPaletteFromTable(tableHercGPalette, sizeof(tableHercGPalette) / 3);
+	} else if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderCGAComp) {
+		setPaletteFromTable(_cgaColors[cgaPalIndex * 2 + cgaPalIntensity], sizeof(_cgaColors[0]) / 3);
+		// Cursor palette
+		if (_game.version == 3) {
+			setPalColor( 7, 170, 170, 170);
+			setPalColor( 8,  85,  85,  85);
+			setPalColor(15, 255, 255, 255);
+		}
+	} else if (_game.version <= 1) {
 		if (_game.platform == Common::kPlatformApple2GS) {
 			setPaletteFromTable(tableApple2gsPalette, sizeof(tableApple2gsPalette) / 3);
 		} else if (_game.platform == Common::kPlatformC64) {
@@ -201,13 +213,7 @@ void ScummEngine::resetPalette() {
 				setPaletteFromTable(tableNESClassicPalette, sizeof(tableNESClassicPalette) / 3);
 			else
 				setPaletteFromTable(tableNESNTSCPalette, sizeof(tableNESNTSCPalette) / 3);
-		} else if (_renderMode == Common::kRenderHercA) {
-			setPaletteFromTable(tableHercAPalette, sizeof(tableHercAPalette) / 3);
-		} else if (_renderMode == Common::kRenderHercG) {
-			setPaletteFromTable(tableHercGPalette, sizeof(tableHercGPalette) / 3);
-		} else if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderCGAComp) {
-			setPaletteFromTable(_cgaColors[cgaPalIndex * 2 + cgaPalIntensity], sizeof(_cgaColors[0]) / 3);
-		} else if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderCGA_BW) {
+		} else if (_renderMode == Common::kRenderCGA_BW) {
 			setPalColor(0, 0x00, 0x00, 0x00);
 			setPalColor(1, 0xff, 0xff, 0xff);
 		} else {
@@ -215,7 +221,6 @@ void ScummEngine::resetPalette() {
 		}
 	} else if (_game.features & GF_16COLOR) {
 		bool setupCursor = false;
-
 		switch (_renderMode) {
 		case Common::kRenderEGA:
 		case Common::kRenderMacintoshBW:
@@ -229,21 +234,6 @@ void ScummEngine::resetPalette() {
 			setPaletteFromTable(tableAmigaPalette, sizeof(tableAmigaPalette) / 3);
 			break;
 
-		case Common::kRenderCGA:
-			setPaletteFromTable(_cgaColors[cgaPalIndex * 2 + cgaPalIntensity], sizeof(_cgaColors[0]) / 3);
-			setupCursor = true;
-			break;
-
-		case Common::kRenderHercA:
-			setPaletteFromTable(tableHercAPalette, sizeof(tableHercAPalette) / 3);
-			setupCursor = true;
-			break;
-
-		case Common::kRenderHercG:
-			setPaletteFromTable(tableHercGPalette, sizeof(tableHercGPalette) / 3);
-			setupCursor = true;
-			break;
-
 		default:
 			if ((_game.platform == Common::kPlatformAmiga) || (_game.platform == Common::kPlatformAtariST))
 				setPaletteFromTable(tableAmigaPalette, sizeof(tableAmigaPalette) / 3);
@@ -252,12 +242,6 @@ void ScummEngine::resetPalette() {
 			else
 				setPaletteFromTable(tableEGAPalette, sizeof(tableEGAPalette) / 3);
 		}
-		if (setupCursor) {
-			// Setup cursor palette
-			setPalColor( 7, 170, 170, 170);
-			setPalColor( 8,  85,  85,  85);
-			setPalColor(15, 255, 255, 255);
-		}
 
 	} else {
 		if ((_game.platform == Common::kPlatformAmiga) && _game.version == 4) {
@@ -467,26 +451,27 @@ void ScummEngine::setAmigaPaletteFromPtr(const byte *ptr) {
 	setDirtyColors(0, 255);
 }
 
-void ScummEngine::updateColorTableV1(int renderMode) {
-	static const byte v1ColorMaps[5][16] = {
-		// C-64: Just leave everything the way it is
+void ScummEngine::setColorTable(int renderMode) {
+	static const byte colorMaps[6][16] = {
+		// C-64, V2 (EGA, Tandy, MCGA): Just leave everything the way it is
 		{	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,	0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F },
-		// ZAK: EGA, Tandy, MCGA, CGA Composite
+		// ZAK V1: EGA, Tandy, MCGA, CGA Composite
 		{	0x00, 0x0F, 0x04, 0x03, 0x05, 0x02, 0x01, 0x0E,	0x0C, 0x06, 0x0D, 0x08, 0x07, 0x0A, 0x09, 0x07 },
-		// ZAK: CGA, CGA B&W, Hercules
+		// ZAK V1: CGA, CGA B&W, Hercules
 		{	0x00, 0x0F, 0x08, 0x05, 0x0A, 0x05, 0x01, 0x0D,	0x0A, 0x02, 0x0A, 0x0C, 0x0F, 0x0A, 0x05, 0x0C },
-		// MM: EGA, Tandy, MCGA, CGA Composite
+		// MM V1: EGA, Tandy, MCGA, CGA Composite
 		{	0x00, 0x0F, 0x04, 0x03, 0x05, 0x02, 0x01, 0x0E, 0x0C, 0x06, 0x0C, 0x08, 0x07, 0x0A, 0x09, 0x08 },
-		// MM: CGA, CGA B&W, Hercules
-		{	0x00, 0x0F, 0x08, 0x05, 0x0A, 0x05, 0x01, 0x0D, 0x0A, 0x02, 0x0A, 0x0C, 0x0F, 0x0A, 0x05, 0x0C }
+		// MM V1: CGA, CGA B&W, Hercules
+		{	0x00, 0x0F, 0x08, 0x05, 0x0A, 0x05, 0x01, 0x0D, 0x0A, 0x02, 0x0A, 0x0C, 0x0F, 0x0A, 0x05, 0x0C },
+		// MM/ZAK V2: CGA, Hercules
+		{	0x00, 0x04, 0x01, 0x05, 0x08, 0x0A, 0x02, 0x0F, 0x0C, 0x07, 0x0D, 0x05, 0x0E, 0x0B, 0x0D, 0x0F }
 	};
-
-	int tbl = (_game.platform == Common::kPlatformC64) ? 0 : (_game.id == GID_ZAK ? 1 : 3);
+	int tbl = (_game.platform == Common::kPlatformC64 || _game.version == 2) ? 0 : (_game.id == GID_ZAK ? 1 : 3);
 	if (renderMode == Common::kRenderHercA || renderMode == Common::kRenderHercG || renderMode == Common::kRenderCGA || renderMode == Common::kRenderCGA_BW)
-		++tbl;
+		tbl = (_game.version == 2) ? 5 : tbl + 1;
 
 	assert(_gdi);
-	_gdi->setRenderModeColorMap(v1ColorMaps[tbl]);
+	_gdi->setRenderModeColorMap(colorMaps[tbl]);
 }
 
 void ScummEngine::amigaPaletteFindFirstUsedColor() {
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 0eb268a82d2..2e2f7102567 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -327,7 +327,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
 	else if (_renderMode == Common::kRenderCGA_BW)
 		_hercCGAScaleBuf = (byte *)malloc(_screenWidth * 2 * _screenHeight * 2);
 
-	updateColorTableV1(_renderMode);
+	setColorTable(_renderMode);
 
 	_isRTL = (_language == Common::HE_ISR && _game.heversion == 0)
 			&& (_game.id == GID_MANIAC || (_game.version >= 4 && _game.version < 7));
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 09c4aa09172..99d7967e45c 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -1025,7 +1025,7 @@ protected:
 	void setPCEPaletteFromPtr(const byte *ptr);
 	void setAmigaPaletteFromPtr(const byte *ptr);
 	virtual void setPaletteFromPtr(const byte *ptr, int numcolor = -1);
-	void updateColorTableV1(int renderMode);
+	void setColorTable(int renderMode);
 
 	virtual void setPalColor(int index, int r, int g, int b);
 	void setDirtyColors(int min, int max);
@@ -1071,7 +1071,7 @@ protected:
 	void mac_undrawIndy3TextBox();
 	void mac_undrawIndy3CreditsText();
 
-	const byte *postProcessV1Graphics(VirtScreen *vs, int &pitch, int &x, int &y, int &width, int &height) const;
+	const byte *postProcessGraphics(VirtScreen *vs, int &pitch, int &x, int &y, int &width, int &height) const;
 	void ditherCGA(byte *dst, int dstPitch, int x, int y, int width, int height) const;
 
 public:




More information about the Scummvm-git-logs mailing list